/******************************************************************************* * Copyright (c) 2007, 2011 Wind River Systems, Inc. and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Eclipse Distribution License v1.0 which accompany this distribution. * The Eclipse Public License is available at * http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * You may elect to redistribute this code under either of these licenses. * * Contributors: * Wind River Systems - initial API and implementation *******************************************************************************/ /* * Symbols service - ELF version. */ #include #if SERVICE_Symbols && !ENABLE_SymbolsProxy && ENABLE_ELF #if defined(_WRS_KERNEL) # include # include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if ENABLE_RCBP_TEST # include #endif struct Symbol { unsigned magic; ObjectInfo * obj; ObjectInfo * var; /* 'this' object if the symbol represents implicit 'this' reference */ ELF_Section * tbl; int has_size; int has_address; ContextAddress size; ContextAddress address; int sym_class; Context * ctx; int frame; unsigned index; unsigned dimension; unsigned cardinal; ContextAddress length; Symbol * base; }; #define is_cardinal_type_pseudo_symbol(s) (s->sym_class == SYM_CLASS_TYPE && s->obj == NULL && s->base == NULL) #include static Context * sym_ctx; static int sym_frame; static ContextAddress sym_ip; static int get_sym_context(Context * ctx, int frame, ContextAddress addr) { if (frame == STACK_NO_FRAME) { ctx = context_get_group(ctx, CONTEXT_GROUP_PROCESS); sym_ip = addr; } else if (frame == STACK_TOP_FRAME) { if (!ctx->stopped) { errno = ERR_IS_RUNNING; return -1; } if (ctx->exited) { errno = ERR_ALREADY_EXITED; return -1; } sym_ip = get_regs_PC(ctx); } else { U8_T ip = 0; StackFrame * info = NULL; if (get_frame_info(ctx, frame, &info) < 0) return -1; if (read_reg_value(info, get_PC_definition(ctx), &ip) < 0) return -1; sym_ip = (ContextAddress)ip; } sym_ctx = ctx; sym_frame = frame; return 0; } /* Map ELF symbol table entry value to run-time address in given context address space */ static int syminfo2address(Context * ctx, ELF_SymbolInfo * info, ContextAddress * address) { switch (info->type) { case STT_OBJECT: case STT_FUNC: { U8_T value = info->value; ELF_File * file = info->sym_section->file; ELF_Section * sec = NULL; if (info->section_index == SHN_UNDEF) { errno = ERR_INV_ADDRESS; return -1; } if (info->section_index == SHN_ABS) { *address = (ContextAddress)value; return 0; } if (info->section_index == SHN_COMMON) { errno = ERR_INV_ADDRESS; return -1; } if (file->type == ET_REL && info->section != NULL) { sec = info->section; value += sec->addr; } *address = elf_map_to_run_time_address(ctx, file, sec, (ContextAddress)value); if (*address == 0 && file->type == ET_EXEC) *address = (ContextAddress)value; return 0; } } errno = ERR_INV_ADDRESS; return -1; } static int is_frame_based_object(Symbol * sym) { int res = 0; ContextAddress addr = 0; ContextAddress size = 0; Context * org_ctx = sym_ctx; int org_frame = sym_frame; ContextAddress org_ip = sym_ip; if (sym->sym_class == SYM_CLASS_REFERENCE) { if (get_symbol_address(sym, &addr) < 0) { res = 1; } else { sym->has_address = 1; sym->address = addr; } } if (!res) { if (get_symbol_size(sym, &size) < 0) { res = 1; } else { sym->has_size = 1; sym->size = size; } } sym_ctx = org_ctx; sym_frame = org_frame; sym_ip = org_ip; return res; } static void object2symbol(ObjectInfo * obj, Symbol ** res) { Symbol * sym = alloc_symbol(); sym->obj = obj; switch (obj->mTag) { case TAG_global_subroutine: case TAG_subroutine: case TAG_subprogram: case TAG_entry_point: sym->sym_class = SYM_CLASS_FUNCTION; break; case TAG_array_type: case TAG_class_type: case TAG_enumeration_type: case TAG_pointer_type: case TAG_reference_type: case TAG_mod_pointer: case TAG_mod_reference: case TAG_string_type: case TAG_structure_type: case TAG_subroutine_type: case TAG_union_type: case TAG_ptr_to_member_type: case TAG_set_type: case TAG_subrange_type: case TAG_base_type: case TAG_fund_type: case TAG_file_type: case TAG_packed_type: case TAG_thrown_type: case TAG_const_type: case TAG_volatile_type: case TAG_restrict_type: case TAG_interface_type: case TAG_unspecified_type: case TAG_mutable_type: case TAG_shared_type: case TAG_typedef: sym->sym_class = SYM_CLASS_TYPE; break; case TAG_global_variable: case TAG_inheritance: case TAG_member: case TAG_formal_parameter: case TAG_unspecified_parameters: case TAG_local_variable: case TAG_variable: sym->sym_class = SYM_CLASS_REFERENCE; break; case TAG_constant: case TAG_enumerator: sym->sym_class = SYM_CLASS_VALUE; break; } sym->frame = STACK_NO_FRAME; sym->ctx = context_get_group(sym_ctx, CONTEXT_GROUP_PROCESS); if (sym_frame != STACK_NO_FRAME && is_frame_based_object(sym)) { sym->frame = sym_frame; sym->ctx = sym_ctx; } *res = sym; } static ObjectInfo * get_object_type(ObjectInfo * obj) { if (obj != NULL) { switch (obj->mTag) { case TAG_global_subroutine: case TAG_subroutine: case TAG_subprogram: case TAG_entry_point: case TAG_enumerator: case TAG_formal_parameter: case TAG_unspecified_parameters: case TAG_global_variable: case TAG_local_variable: case TAG_variable: case TAG_inheritance: case TAG_member: case TAG_constant: obj = obj->mType; break; } } return obj; } static int is_modified_type(ObjectInfo * obj) { if (obj != NULL && obj->mType != NULL) { switch (obj->mTag) { case TAG_subrange_type: case TAG_packed_type: case TAG_const_type: case TAG_volatile_type: case TAG_restrict_type: case TAG_shared_type: case TAG_typedef: return 1; } } return 0; } static ObjectInfo * get_original_type(ObjectInfo * obj) { obj = get_object_type(obj); while (is_modified_type(obj)) { obj = obj->mType; } return obj; } static int get_num_prop(ObjectInfo * obj, U2_T at, U8_T * res) { Trap trap; PropertyValue v; if (!set_trap(&trap)) return 0; read_and_evaluate_dwarf_object_property(sym_ctx, sym_frame, 0, obj, at, &v); *res = get_numeric_property_value(&v); clear_trap(&trap); return 1; } /* Check run-time 'addr' belongs to an object address range(s) */ static int check_in_range(ObjectInfo * obj, ContextAddress rt_offs, ContextAddress addr) { Trap trap; if (obj->u.mAddr.mHighPC > obj->u.mAddr.mLowPC) { ContextAddress lt_addr = addr - rt_offs; return lt_addr >= obj->u.mAddr.mLowPC && lt_addr < obj->u.mAddr.mHighPC; } if (set_trap(&trap)) { CompUnit * unit = obj->mCompUnit; DWARFCache * cache = get_dwarf_cache(unit->mFile); ELF_Section * debug_ranges = cache->mDebugRanges; if (debug_ranges != NULL) { ContextAddress lt_addr = addr - rt_offs; ContextAddress base = unit->mLowPC; PropertyValue v; U8_T offs = 0; int res = 0; read_and_evaluate_dwarf_object_property(sym_ctx, sym_frame, 0, obj, AT_ranges, &v); offs = get_numeric_property_value(&v); dio_EnterSection(&unit->mDesc, debug_ranges, offs); for (;;) { ELF_Section * sec = NULL; U8_T x = dio_ReadAddress(&sec); U8_T y = dio_ReadAddress(&sec); if (x == 0 && y == 0) break; if (x == ((U8_T)1 << unit->mDesc.mAddressSize * 8) - 1) { base = (ContextAddress)y; } else { x = base + x; y = base + y; if (x <= lt_addr && lt_addr < y) { res = 1; break; } } } dio_ExitSection(); clear_trap(&trap); return res; } clear_trap(&trap); } return 0; } static int find_in_object_tree(ObjectInfo * list, ContextAddress rt_offs, ContextAddress ip, const char * name, Symbol ** sym) { Symbol * sym_imp = NULL; /* Imported from a namespace */ Symbol * sym_enu = NULL; /* Enumeration constant */ Symbol * sym_cur = NULL; /* Found in current scope */ Symbol * sym_base = NULL; /* Found in base class (inherited) */ Symbol * sym_this = NULL; /* Found in 'this' reference */ ObjectInfo * obj = list; while (obj != NULL) { if (obj->mName != NULL) { U8_T v = 0; if (strcmp(obj->mName, name) == 0) { object2symbol(obj, &sym_cur); } if (sym_frame != STACK_NO_FRAME && strcmp(obj->mName, "this") == 0 && get_num_prop(obj, AT_artificial, &v) && v != 0) { ObjectInfo * type = get_original_type(obj); if ((type->mTag == TAG_pointer_type || type->mTag == TAG_mod_pointer) && type->mType != NULL) { type = get_original_type(type->mType); find_in_object_tree(type->mChildren, 0, 0, name, &sym_this); if (sym_this != NULL) { sym_this->ctx = sym_ctx; sym_this->frame = sym_frame; sym_this->var = obj; } } } } switch (obj->mTag) { case TAG_enumeration_type: find_in_object_tree(obj->mChildren, 0, 0, name, &sym_enu); break; case TAG_global_subroutine: case TAG_subroutine: case TAG_subprogram: case TAG_entry_point: case TAG_lexical_block: case TAG_inlined_subroutine: if (ip != 0 && check_in_range(obj, rt_offs, ip)) { if (find_in_object_tree(obj->mChildren, rt_offs, ip, name, sym)) return 1; } break; case TAG_inheritance: find_in_object_tree(obj->mType->mChildren, 0, 0, name, &sym_base); break; case TAG_imported_module: { PropertyValue p; ObjectInfo * module; read_and_evaluate_dwarf_object_property(sym_ctx, sym_frame, 0, obj, AT_import, &p); module = find_object(get_dwarf_cache(obj->mCompUnit->mFile), p.mValue); if (module != NULL) find_in_object_tree(module->mChildren, 0, 0, name, &sym_imp); } break; } obj = obj->mSibling; } if (*sym == NULL) *sym = sym_cur; if (*sym == NULL) *sym = sym_base; if (*sym == NULL) *sym = sym_this; if (*sym == NULL) *sym = sym_enu; if (*sym == NULL) *sym = sym_imp; return *sym != NULL; } static int find_in_dwarf(const char * name, Symbol ** sym) { ContextAddress rt_addr = 0; UnitAddressRange * range = elf_find_unit(sym_ctx, sym_ip, sym_ip, &rt_addr); *sym = NULL; if (range != NULL) { CompUnit * unit = range->mUnit; if (find_in_object_tree(unit->mObject->mChildren, rt_addr - range->mAddr, sym_ip, name, sym)) return 1; if (unit->mBaseTypes != NULL) { if (find_in_object_tree(unit->mBaseTypes->mObject->mChildren, 0, 0, name, sym)) return 1; } } return 0; } static int find_by_name_in_pub_names(DWARFCache * cache, PubNamesTable * tbl, char * name, Symbol ** sym) { unsigned n = tbl->mHash[calc_symbol_name_hash(name)]; while (n != 0) { U8_T id = tbl->mNext[n].mID; ObjectInfo * obj = find_object(cache, id); if (obj == NULL || obj->mName == NULL) str_exception(ERR_INV_DWARF, "Invalid .debug_pubnames section"); if (strcmp(obj->mName, name) == 0) { object2symbol(obj, sym); return 1; } n = tbl->mNext[n].mNext; } return 0; } static void create_symbol_names_hash(ELF_Section * tbl) { unsigned i; unsigned sym_size = tbl->file->elf64 ? sizeof(Elf64_Sym) : sizeof(Elf32_Sym); unsigned sym_cnt = (unsigned)(tbl->size / sym_size); tbl->sym_names_hash = (unsigned *)loc_alloc_zero(SYM_HASH_SIZE * sizeof(unsigned)); tbl->sym_names_next = (unsigned *)loc_alloc_zero(sym_cnt * sizeof(unsigned)); for (i = 0; i < sym_cnt; i++) { ELF_SymbolInfo sym; unpack_elf_symbol_info(tbl, i, &sym); if (sym.bind == STB_GLOBAL && sym.name != NULL && sym.section_index != SHN_UNDEF) { unsigned h = calc_symbol_name_hash(sym.name); tbl->sym_names_next[i] = tbl->sym_names_hash[h]; tbl->sym_names_hash[h] = i; } } } static int find_by_name_in_sym_table(DWARFCache * cache, char * name, Symbol ** res) { unsigned m = 0; unsigned h = calc_symbol_name_hash(name); unsigned cnt = 0; Context * prs = context_get_group(sym_ctx, CONTEXT_GROUP_PROCESS); for (m = 1; m < cache->mFile->section_cnt; m++) { unsigned n; ELF_Section * tbl = cache->mFile->sections + m; if (tbl->sym_count == 0) continue; if (tbl->sym_names_hash == NULL) create_symbol_names_hash(tbl); n = tbl->sym_names_hash[h]; while (n) { ELF_SymbolInfo sym_info; unpack_elf_symbol_info(tbl, n, &sym_info); if (cmp_symbol_names(name, sym_info.name) == 0) { int found = 0; ContextAddress addr = 0; if (sym_info.section_index != SHN_ABS && syminfo2address(prs, &sym_info, &addr) == 0) { UnitAddressRange * range = elf_find_unit(sym_ctx, addr, addr, NULL); if (range != NULL) { ObjectInfo * obj = range->mUnit->mObject->mChildren; while (obj != NULL) { switch (obj->mTag) { case TAG_global_subroutine: case TAG_global_variable: case TAG_subroutine: case TAG_subprogram: case TAG_variable: if (obj->mName != NULL && strcmp(obj->mName, name) == 0) { object2symbol(obj, res); found = 1; cnt++; } break; } obj = obj->mSibling; } } } if (!found) { Symbol * sym = alloc_symbol(); sym->frame = STACK_NO_FRAME; sym->ctx = prs; sym->tbl = tbl; sym->index = n; switch (sym_info.type) { case STT_FUNC: sym->sym_class = SYM_CLASS_FUNCTION; break; case STT_OBJECT: sym->sym_class = SYM_CLASS_REFERENCE; break; default: sym->sym_class = SYM_CLASS_VALUE; break; } *res = sym; cnt++; } } n = tbl->sym_names_next[n]; } } return cnt == 1; } int find_symbol_by_name(Context * ctx, int frame, ContextAddress ip, char * name, Symbol ** res) { int error = 0; int found = 0; assert(ctx != NULL); #if defined(_WRS_KERNEL) { char * ptr; SYM_TYPE type; if (symFindByName(sysSymTbl, name, &ptr, &type) != OK) { error = errno; assert(error != 0); if (error == S_symLib_SYMBOL_NOT_FOUND) error = 0; } else { Symbol * sym = alloc_symbol(); sym->ctx = context_get_group(ctx, CONTEXT_GROUP_PROCESS); sym->frame = STACK_NO_FRAME; sym->address = (ContextAddress)ptr; sym->has_address = 1; if (SYM_IS_TEXT(type)) { sym->sym_class = SYM_CLASS_FUNCTION; } else { sym->sym_class = SYM_CLASS_REFERENCE; } *res = sym; found = 1; } } #endif if (error == 0 && !found && get_sym_context(ctx, frame, ip) < 0) error = errno; if (error == 0 && !found && sym_ip != 0) { Trap trap; if (set_trap(&trap)) { found = find_in_dwarf(name, res); clear_trap(&trap); } else { error = trap.error; } } if (error == 0 && !found) { ELF_File * file = elf_list_first(sym_ctx, sym_ip, sym_ip ? sym_ip : ~(ContextAddress)0); if (file == NULL) error = errno; while (error == 0 && file != NULL) { Trap trap; if (set_trap(&trap)) { DWARFCache * cache = get_dwarf_cache(file); if (cache->mPubNames.mHash != NULL) { found = find_by_name_in_pub_names(cache, &cache->mPubNames, name, res); if (!found && cache->mPubTypes.mHash != NULL) { found = find_by_name_in_pub_names(cache, &cache->mPubTypes, name, res); } } if (!found) { found = find_by_name_in_sym_table(cache, name, res); } clear_trap(&trap); } else { error = trap.error; break; } if (found) break; file = elf_list_next(sym_ctx); if (file == NULL) error = errno; } elf_list_done(sym_ctx); } if (error == 0 && !found && sym_ip != 0) { Trap trap; if (set_trap(&trap)) { const char * s = NULL; if (strcmp(name, "signed") == 0) s = "int"; else if (strcmp(name, "signed int") == 0) s = "int"; else if (strcmp(name, "unsigned") == 0) s = "unsigned int"; else if (strcmp(name, "short") == 0) s = "short int"; else if (strcmp(name, "signed short") == 0) s = "short int"; else if (strcmp(name, "signed short int") == 0) s = "short int"; else if (strcmp(name, "unsigned short") == 0) s = "unsigned short int"; else if (strcmp(name, "long") == 0) s = "long int"; else if (strcmp(name, "signed long") == 0) s = "long int"; else if (strcmp(name, "signed long int") == 0) s = "long int"; else if (strcmp(name, "unsigned long") == 0) s = "unsigned long int"; else if (strcmp(name, "long long") == 0) s = "long long int"; else if (strcmp(name, "signed long long") == 0) s = "long long int"; else if (strcmp(name, "signed long long int") == 0) s = "long long int"; else if (strcmp(name, "unsigned long long") == 0) s = "unsigned long long int"; else if (strcmp(name, "char") == 0) s = "signed char"; if (s != NULL) { found = find_in_dwarf(s, res); if (!found) { s = NULL; if (strcmp(name, "char") == 0) s = "unsigned char"; if (s != NULL) found = find_in_dwarf(s, res); } } clear_trap(&trap); } else { error = trap.error; } } #if ENABLE_RCBP_TEST if (!found) { int sym_class = 0; void * address = NULL; found = find_test_symbol(ctx, name, &address, &sym_class) >= 0; if (found) { Symbol * sym = alloc_symbol(); sym->ctx = context_get_group(ctx, CONTEXT_GROUP_PROCESS); sym->frame = STACK_NO_FRAME; sym->address = (ContextAddress)address; sym->has_address = 1; sym->sym_class = sym_class; *res = sym; } } #endif if (error == 0 && !found && sym_ip == 0) { /* Search all compilation units */ ELF_File * file = elf_list_first(sym_ctx, 0, ~(ContextAddress)0); if (file == NULL) error = errno; while (error == 0 && file != NULL) { Trap trap; if (set_trap(&trap)) { unsigned i; DWARFCache * cache = get_dwarf_cache(file); for (i = 0; i < cache->mAddrRangesCnt; i++) { UnitAddressRange * range = cache->mAddrRanges + i; CompUnit * unit = range->mUnit; ContextAddress rt_addr = elf_map_to_run_time_address(sym_ctx, file, unit->mTextSection, range->mAddr); if (rt_addr != 0) { *res = NULL; sym_ip = rt_addr; if (find_in_object_tree(unit->mObject->mChildren, rt_addr - range->mAddr, sym_ip, name, res)) found = 1; if (!found && unit->mBaseTypes != NULL) { if (find_in_object_tree(unit->mBaseTypes->mObject->mChildren, 0, 0, name, res)) found = 1; } if (found) break; } } clear_trap(&trap); } else { error = trap.error; break; } if (found) break; file = elf_list_next(sym_ctx); if (file == NULL) error = errno; } elf_list_done(sym_ctx); sym_ip = 0; } if (error == 0 && !found) error = ERR_SYM_NOT_FOUND; assert(error || (*res != NULL && (*res)->ctx != NULL)); if (error) { errno = error; return -1; } return 0; } int find_symbol_in_scope(Context * ctx, int frame, ContextAddress ip, Symbol * scope, char * name, Symbol ** res) { int error = 0; int found = 0; *res = NULL; if (get_sym_context(ctx, frame, ip) < 0) error = errno; if (!error && scope == NULL && sym_ip != 0) { ELF_File * file = elf_list_first(sym_ctx, sym_ip, sym_ip); if (file == NULL) error = errno; while (error == 0 && file != NULL) { Trap trap; if (set_trap(&trap)) { DWARFCache * cache = get_dwarf_cache(file); UnitAddressRange * range = find_comp_unit_addr_range(cache, sym_ip, sym_ip); if (range != NULL) { found = find_in_object_tree(range->mUnit->mObject->mChildren, 0, 0, name, res); } if (!found) { found = find_by_name_in_sym_table(cache, name, res); } clear_trap(&trap); } else { error = trap.error; break; } if (found) break; file = elf_list_next(sym_ctx); if (file == NULL) error = errno; } elf_list_done(sym_ctx); } if (!found && !error && scope != NULL && scope->obj != NULL) { Trap trap; if (set_trap(&trap)) { found = find_in_object_tree(scope->obj->mChildren, 0, 0, name, res); clear_trap(&trap); } else { error = trap.error; } } if (error == 0 && !found) error = ERR_SYM_NOT_FOUND; assert(error || (*res != NULL && (*res)->ctx != NULL)); if (error) { errno = error; return -1; } return 0; } static int find_by_addr_in_unit(ObjectInfo * obj, int level, ContextAddress rt_offs, ContextAddress addr, Symbol ** res) { while (obj != NULL) { switch (obj->mTag) { case TAG_global_subroutine: case TAG_subroutine: case TAG_subprogram: case TAG_entry_point: case TAG_lexical_block: case TAG_inlined_subroutine: if (check_in_range(obj, rt_offs, addr)) { object2symbol(obj, res); return 1; } if (check_in_range(obj, rt_offs, sym_ip)) { return find_by_addr_in_unit(obj->mChildren, level + 1, rt_offs, addr, res); } break; case TAG_formal_parameter: case TAG_unspecified_parameters: case TAG_local_variable: if (sym_frame == STACK_NO_FRAME) break; case TAG_variable: { U8_T lc = 0; /* Ignore location evaluation errors. For example, the error can be caused by * the object not being mapped into the context memory */ if (get_num_prop(obj, AT_location, &lc) && lc <= addr) { U8_T sz = 0; if (!get_num_prop(obj, AT_byte_size, &sz)) { /* If object size unknown, continue search */ if (get_error_code(errno) == ERR_SYM_NOT_FOUND) break; exception(errno); } if (lc + sz > addr) { object2symbol(obj, res); return 1; } } } break; } obj = obj->mSibling; } return 0; } static int find_by_addr_in_sym_tables(ContextAddress addr, Symbol ** res) { ELF_File * file = NULL; ELF_Section * section = NULL; ELF_SymbolInfo sym_info; ContextAddress lt_addr = elf_map_to_link_time_address(sym_ctx, addr, &file, §ion); elf_find_symbol_by_address(section, lt_addr, &sym_info); while (sym_info.sym_section != NULL) { int sym_class = SYM_CLASS_UNKNOWN; assert(sym_info.section == section); switch (sym_info.type) { case STT_FUNC: sym_class = SYM_CLASS_FUNCTION; break; case STT_OBJECT: sym_class = SYM_CLASS_REFERENCE; break; } if (sym_class != SYM_CLASS_UNKNOWN) { ContextAddress sym_addr = sym_info.value; if (file->type == ET_REL) sym_addr += section->addr; assert(sym_addr <= lt_addr); if (sym_addr + sym_info.size > lt_addr) { Symbol * sym = alloc_symbol(); sym->frame = STACK_NO_FRAME; sym->ctx = context_get_group(sym_ctx, CONTEXT_GROUP_PROCESS); sym->tbl = sym_info.sym_section; sym->index = sym_info.sym_index; sym->sym_class = sym_class; *res = sym; return 1; } return 0; } elf_prev_symbol_by_address(&sym_info); } return 0; } int find_symbol_by_addr(Context * ctx, int frame, ContextAddress addr, Symbol ** res) { Trap trap; int found = 0; ContextAddress rt_addr = 0; UnitAddressRange * range = NULL; if (!set_trap(&trap)) return -1; if (frame == STACK_TOP_FRAME && (frame = get_top_frame(ctx)) < 0) exception(errno); if (get_sym_context(ctx, frame, addr) < 0) exception(errno); range = elf_find_unit(sym_ctx, addr, addr, &rt_addr); if (range != NULL) found = find_by_addr_in_unit(range->mUnit->mObject->mChildren, 0, rt_addr - range->mAddr, addr, res); if (!found) found = find_by_addr_in_sym_tables(addr, res); if (!found && sym_ip != 0) { /* Search in compilation unit that contains stack frame PC */ range = elf_find_unit(sym_ctx, sym_ip, sym_ip, &rt_addr); if (range != NULL) found = find_by_addr_in_unit(range->mUnit->mObject->mChildren, 0, rt_addr - range->mAddr, addr, res); } if (!found) exception(ERR_SYM_NOT_FOUND); clear_trap(&trap); return 0; } static void enumerate_local_vars(ObjectInfo * obj, int level, ContextAddress rt_offs, EnumerateSymbolsCallBack * call_back, void * args) { while (obj != NULL) { switch (obj->mTag) { case TAG_global_subroutine: case TAG_subroutine: case TAG_subprogram: case TAG_entry_point: case TAG_lexical_block: case TAG_inlined_subroutine: if (check_in_range(obj, rt_offs, sym_ip)) { enumerate_local_vars(obj->mChildren, level + 1, rt_offs, call_back, args); } break; case TAG_formal_parameter: case TAG_unspecified_parameters: case TAG_local_variable: case TAG_variable: if (level > 0) { Context * org_ctx = sym_ctx; int org_frame = sym_frame; ContextAddress org_ip = sym_ip; Symbol * sym = NULL; object2symbol(obj, &sym); call_back(args, sym); sym_ctx = org_ctx; sym_frame = org_frame; sym_ip = org_ip; } break; } obj = obj->mSibling; } } int enumerate_symbols(Context * ctx, int frame, EnumerateSymbolsCallBack * call_back, void * args) { Trap trap; if (!set_trap(&trap)) return -1; if (frame == STACK_TOP_FRAME && (frame = get_top_frame(ctx)) < 0) exception(errno); if (get_sym_context(ctx, frame, 0) < 0) exception(errno); if (sym_ip != 0) { ContextAddress rt_addr = 0; UnitAddressRange * range = elf_find_unit(sym_ctx, sym_ip, sym_ip, &rt_addr); if (range != NULL) enumerate_local_vars(range->mUnit->mObject->mChildren, 0, rt_addr - range->mAddr, call_back, args); } clear_trap(&trap); return 0; } const char * symbol2id(const Symbol * sym) { static char id[256]; assert(sym->magic == SYMBOL_MAGIC); if (sym->base) { char base[256]; assert(sym->ctx == sym->base->ctx); assert(sym->frame == STACK_NO_FRAME); assert(sym->sym_class == SYM_CLASS_TYPE); strcpy(base, symbol2id(sym->base)); snprintf(id, sizeof(id), "@P%"PRIX64".%s", (uint64_t)sym->length, base); } else { ELF_File * file = NULL; uint64_t obj_index = 0; uint64_t var_index = 0; unsigned tbl_index = 0; int frame = sym->frame; if (sym->obj != NULL) file = sym->obj->mCompUnit->mFile; if (sym->tbl != NULL) file = sym->tbl->file; if (sym->obj != NULL) obj_index = sym->obj->mID; if (sym->var != NULL) var_index = sym->var->mID; if (sym->tbl != NULL) tbl_index = sym->tbl->index; if (frame == STACK_TOP_FRAME) frame = get_top_frame(sym->ctx); assert(sym->var == NULL || sym->var->mCompUnit->mFile == file); snprintf(id, sizeof(id), "@S%X.%lX.%lX.%"PRIX64".%"PRIX64".%"PRIX64".%X.%d.%X.%X.%X.%s", sym->sym_class, file ? (unsigned long)file->dev : 0ul, file ? (unsigned long)file->ino : 0ul, file ? file->mtime : (int64_t)0, obj_index, var_index, tbl_index, frame, sym->index, sym->dimension, sym->cardinal, sym->ctx->id); } return id; } static uint64_t read_hex(const char ** s) { uint64_t res = 0; const char * p = *s; for (;;) { if (*p >= '0' && *p <= '9') res = (res << 4) | (*p - '0'); else if (*p >= 'A' && *p <= 'F') res = (res << 4) | (*p - 'A' + 10); else break; p++; } *s = p; return res; } static int read_int(const char ** s) { int neg = 0; int res = 0; const char * p = *s; if (*p == '-') { neg = 1; p++; } for (;;) { if (*p >= '0' && *p <= '9') res = res * 10 + (*p - '0'); else break; p++; } *s = p; return neg ? -res : res; } int id2symbol(const char * id, Symbol ** res) { Symbol * sym = alloc_symbol(); dev_t dev = 0; ino_t ino = 0; int64_t mtime; uint64_t obj_index = 0; uint64_t var_index = 0; unsigned tbl_index = 0; ELF_File * file = NULL; const char * p; Trap trap; *res = sym; if (id != NULL && id[0] == '@' && id[1] == 'P') { p = id + 2; sym->length = (ContextAddress)read_hex(&p); if (*p == '.') p++; if (id2symbol(p, &sym->base)) return -1; sym->ctx = sym->base->ctx; sym->frame = STACK_NO_FRAME; sym->sym_class = SYM_CLASS_TYPE; return 0; } else if (id != NULL && id[0] == '@' && id[1] == 'S') { p = id + 2; sym->sym_class = (int)read_hex(&p); if (*p == '.') p++; dev = (dev_t)read_hex(&p); if (*p == '.') p++; ino = (ino_t)read_hex(&p); if (*p == '.') p++; mtime = (int64_t)read_hex(&p); if (*p == '.') p++; obj_index = read_hex(&p); if (*p == '.') p++; var_index = read_hex(&p); if (*p == '.') p++; tbl_index = (unsigned)read_hex(&p); if (*p == '.') p++; sym->frame = read_int(&p); if (*p == '.') p++; sym->index = (unsigned)read_hex(&p); if (*p == '.') p++; sym->dimension = (unsigned)read_hex(&p); if (*p == '.') p++; sym->cardinal = (unsigned)read_hex(&p); if (*p == '.') p++; sym->ctx = id2ctx(p); if (sym->ctx == NULL) { errno = ERR_INV_CONTEXT; return -1; } if (dev == 0 && ino == 0 && mtime == 0) return 0; file = elf_open_inode(sym->ctx, dev, ino, mtime); if (file == NULL) return -1; if (set_trap(&trap)) { DWARFCache * cache = get_dwarf_cache(file); if (obj_index) { sym->obj = find_object(cache, obj_index); if (sym->obj == NULL) exception(ERR_INV_CONTEXT); } if (var_index) { sym->var = find_object(cache, var_index); if (sym->var == NULL) exception(ERR_INV_CONTEXT); } if (tbl_index) { if (tbl_index >= file->section_cnt) exception(ERR_INV_CONTEXT); sym->tbl = file->sections + tbl_index; } clear_trap(&trap); return 0; } } else { errno = ERR_INV_CONTEXT; } return -1; } ContextAddress is_plt_section(Context * ctx, ContextAddress addr) { ELF_File * file = NULL; ELF_Section * sec = NULL; ContextAddress res = elf_map_to_link_time_address(ctx, addr, &file, &sec); if (res == 0 || sec == NULL) return 0; if (sec->name == NULL) return 0; if (strcmp(sec->name, ".plt") != 0) return 0; return sec->addr + (addr - res); } int get_stack_tracing_info(Context * ctx, ContextAddress rt_addr, StackTracingInfo ** info) { /* TODO: no debug info exists for linux-gate.so, need to read stack tracing information from the kernel */ /* TODO: support for separate debug info files */ ELF_File * file = NULL; ELF_Section * sec = NULL; ContextAddress lt_addr = 0; int error = 0; Trap trap; *info = NULL; lt_addr = elf_map_to_link_time_address(ctx, rt_addr, &file, &sec); if (file != NULL) { /* This assert fails because of ambiguity in Linux memory maps: * assert(rt_addr == elf_map_to_run_time_address(ctx, file, sec, lt_addr)); */ if (set_trap(&trap)) { get_dwarf_stack_frame_info(ctx, file, sec, lt_addr); if (dwarf_stack_trace_fp->cmds_cnt > 0) { static StackTracingInfo buf; buf.addr = (ContextAddress)dwarf_stack_trace_addr - lt_addr + rt_addr; buf.size = (ContextAddress)dwarf_stack_trace_size; buf.fp = dwarf_stack_trace_fp; buf.regs = dwarf_stack_trace_regs; buf.reg_cnt = dwarf_stack_trace_regs_cnt; *info = &buf; } clear_trap(&trap); } else { error = trap.error; } } if (error) { errno = error; return -1; } return 0; } int get_next_stack_frame(StackFrame * frame, StackFrame * down) { int error = 0; uint64_t ip = 0; Context * ctx = frame->ctx; StackTracingInfo * info = NULL; if (read_reg_value(frame, get_PC_definition(ctx), &ip) < 0) { if (frame->is_top_frame) error = errno; } else if (get_stack_tracing_info(ctx, ip, &info) < 0) { error = errno; } else if (info != NULL) { Trap trap; if (set_trap(&trap)) { int i; frame->fp = (ContextAddress)evaluate_stack_trace_commands(ctx, frame, info->fp); for (i = 0; i < info->reg_cnt; i++) { int ok = 0; uint64_t v = 0; Trap trap_reg; if (set_trap(&trap_reg)) { /* If a saved register value cannot be evaluated - ignore it */ v = evaluate_stack_trace_commands(ctx, frame, info->regs[i]); clear_trap(&trap_reg); ok = 1; } if (ok && write_reg_value(down, info->regs[i]->reg, v) < 0) exception(errno); } clear_trap(&trap); } else { frame->fp = 0; } } if (error) { errno = error; return -1; } return 0; } void ini_symbols_lib(void) { } /*************** Functions for retrieving symbol properties ***************************************/ static int unpack(const Symbol * sym) { ELF_File * file = NULL; assert(sym->base == NULL); assert(!is_cardinal_type_pseudo_symbol(sym)); if (get_sym_context(sym->ctx, sym->frame, 0) < 0) return -1; if (sym->obj != NULL) file = sym->obj->mCompUnit->mFile; if (sym->tbl != NULL) file = sym->tbl->file; if (file != NULL) { DWARFCache * cache = (DWARFCache *)file->dwarf_dt_cache; if (cache == NULL || cache->magic != DWARF_CACHE_MAGIC) { errno = ERR_INV_CONTEXT; return -1; } } return 0; } static U8_T get_default_lower_bound(ObjectInfo * obj) { switch (obj->mCompUnit->mLanguage) { case LANG_FORTRAN77: case LANG_FORTRAN90: case LANG_FORTRAN95: return 1; } return 0; } static U8_T get_object_length(ObjectInfo * obj) { U8_T x, y; if (get_num_prop(obj, AT_count, &x)) return x; if (get_num_prop(obj, AT_upper_bound, &x)) { if (!get_num_prop(obj, AT_lower_bound, &y)) { y = get_default_lower_bound(obj); } return x + 1 - y; } if (obj->mTag == TAG_enumeration_type) { ObjectInfo * c = obj->mChildren; x = 0; while (c != NULL) { x++; c = c->mSibling; } return x; } return 0; } static void alloc_cardinal_type_pseudo_symbol(Context * ctx, unsigned size, Symbol ** type) { *type = alloc_symbol(); (*type)->ctx = context_get_group(ctx, CONTEXT_GROUP_PROCESS); (*type)->frame = STACK_NO_FRAME; (*type)->sym_class = SYM_CLASS_TYPE; (*type)->cardinal = size; } static int map_to_sym_table(ObjectInfo * obj, Symbol ** sym) { U8_T v = 0; int found = 0; if (get_num_prop(obj, AT_external, &v) && v != 0) { Trap trap; if (set_trap(&trap)) { PropertyValue p; DWARFCache * cache = get_dwarf_cache(obj->mCompUnit->mFile); read_and_evaluate_dwarf_object_property(sym_ctx, sym_frame, 0, obj, AT_MIPS_linkage_name, &p); if (p.mAddr != NULL) found = find_by_name_in_sym_table(cache, (char *)p.mAddr, sym); clear_trap(&trap); } } return found; } int get_symbol_class(const Symbol * sym, int * sym_class) { assert(sym->magic == SYMBOL_MAGIC); *sym_class = sym->sym_class; return 0; } int get_symbol_type(const Symbol * sym, Symbol ** type) { ObjectInfo * obj = sym->obj; assert(sym->magic == SYMBOL_MAGIC); if (sym->base || is_cardinal_type_pseudo_symbol(sym)) { *type = (Symbol *)sym; return 0; } if (sym->sym_class == SYM_CLASS_FUNCTION) { *type = alloc_symbol(); (*type)->ctx = sym->ctx; (*type)->frame = STACK_NO_FRAME; (*type)->sym_class = SYM_CLASS_TYPE; (*type)->base = (Symbol *)sym; return 0; } if (unpack(sym) < 0) return -1; obj = sym->sym_class == SYM_CLASS_TYPE ? get_original_type(obj) : get_object_type(obj); if (obj == NULL) { *type = NULL; } else if (obj == sym->obj) { *type = (Symbol *)sym; } else { object2symbol(obj, type); } return 0; } int get_symbol_type_class(const Symbol * sym, int * type_class) { U8_T x; ObjectInfo * obj = sym->obj; assert(sym->magic == SYMBOL_MAGIC); if (sym->base) { if (sym->base->sym_class == SYM_CLASS_FUNCTION) *type_class = TYPE_CLASS_FUNCTION; else if (sym->length > 0) *type_class = TYPE_CLASS_ARRAY; else *type_class = TYPE_CLASS_POINTER; return 0; } if (is_cardinal_type_pseudo_symbol(sym)) { *type_class = TYPE_CLASS_CARDINAL; return 0; } if (unpack(sym) < 0) return -1; while (obj != NULL) { switch (obj->mTag) { case TAG_global_subroutine: case TAG_subroutine: case TAG_subprogram: case TAG_entry_point: case TAG_subroutine_type: *type_class = TYPE_CLASS_FUNCTION; return 0; case TAG_array_type: case TAG_string_type: *type_class = TYPE_CLASS_ARRAY; return 0; case TAG_enumeration_type: case TAG_enumerator: *type_class = TYPE_CLASS_ENUMERATION; return 0; case TAG_pointer_type: case TAG_reference_type: case TAG_mod_pointer: case TAG_mod_reference: *type_class = TYPE_CLASS_POINTER; return 0; case TAG_class_type: case TAG_structure_type: case TAG_union_type: case TAG_interface_type: *type_class = TYPE_CLASS_COMPOSITE; return 0; case TAG_base_type: if (get_num_prop(obj, AT_encoding, &x)) { switch ((int)x) { case ATE_address: *type_class = TYPE_CLASS_POINTER; return 0; case ATE_boolean: *type_class = TYPE_CLASS_INTEGER; return 0; case ATE_float: *type_class = TYPE_CLASS_REAL; return 0; case ATE_signed: case ATE_signed_char: *type_class = TYPE_CLASS_INTEGER; return 0; case ATE_unsigned: case ATE_unsigned_char: *type_class = TYPE_CLASS_CARDINAL; return 0; } } *type_class = TYPE_CLASS_UNKNOWN; return 0; case TAG_fund_type: switch (obj->u.mFundType) { case FT_boolean: *type_class = TYPE_CLASS_INTEGER; return 0; case FT_char: *type_class = TYPE_CLASS_INTEGER; return 0; case FT_dbl_prec_float: case FT_ext_prec_float: case FT_float: *type_class = TYPE_CLASS_REAL; return 0; case FT_signed_char: case FT_signed_integer: case FT_signed_long: case FT_signed_short: case FT_short: case FT_integer: case FT_long: *type_class = TYPE_CLASS_INTEGER; return 0; case FT_unsigned_char: case FT_unsigned_integer: case FT_unsigned_long: case FT_unsigned_short: *type_class = TYPE_CLASS_CARDINAL; return 0; case FT_pointer: *type_class = TYPE_CLASS_POINTER; return 0; case FT_void: *type_class = TYPE_CLASS_CARDINAL; return 0; case FT_label: case FT_complex: case FT_dbl_prec_complex: case FT_ext_prec_complex: break; } *type_class = TYPE_CLASS_UNKNOWN; return 0; case TAG_subrange_type: case TAG_packed_type: case TAG_volatile_type: case TAG_restrict_type: case TAG_shared_type: case TAG_const_type: case TAG_typedef: case TAG_formal_parameter: case TAG_unspecified_parameters: case TAG_global_variable: case TAG_local_variable: case TAG_variable: case TAG_inheritance: case TAG_member: case TAG_constant: obj = obj->mType; break; default: obj = NULL; break; } } if (sym->tbl != NULL) { ELF_SymbolInfo info; unpack_elf_symbol_info(sym->tbl, sym->index, &info); if (info.type == STT_FUNC) { *type_class = TYPE_CLASS_FUNCTION; return 0; } } *type_class = TYPE_CLASS_UNKNOWN; return 0; } int get_symbol_update_policy(const Symbol * sym, char ** id, int * policy) { assert(sym->magic == SYMBOL_MAGIC); *id = sym->ctx->id; *policy = context_has_state(sym->ctx) ? UPDATE_ON_EXE_STATE_CHANGES : UPDATE_ON_MEMORY_MAP_CHANGES; return 0; } int get_symbol_name(const Symbol * sym, char ** name) { assert(sym->magic == SYMBOL_MAGIC); if (sym->base || is_cardinal_type_pseudo_symbol(sym)) { *name = NULL; } else if (sym->obj != NULL) { *name = sym->obj->mName; } else if (sym->tbl != NULL) { ELF_SymbolInfo info; unpack_elf_symbol_info(sym->tbl, sym->index, &info); *name = info.name; } else { *name = NULL; } return 0; } int get_symbol_size(const Symbol * sym, ContextAddress * size) { ObjectInfo * obj = sym->obj; assert(sym->magic == SYMBOL_MAGIC); if (sym->base) { if (sym->length > 0) { if (get_symbol_size(sym->base, size)) return -1; *size *= sym->length; } else { Symbol * base = sym->base; while (base->obj == NULL && base->base != NULL) base = base->base; if (base->obj != NULL) *size = base->obj->mCompUnit->mDesc.mAddressSize; else *size = context_word_size(sym->ctx); } return 0; } if (is_cardinal_type_pseudo_symbol(sym)) { *size = sym->cardinal; return 0; } if (sym->has_size != 0) { *size = sym->size; return 0; } if (unpack(sym) < 0) return -1; *size = 0; if (obj != NULL) { Trap trap; int ok = 0; U8_T sz = 0; if (!set_trap(&trap)) return -1; if (sym->dimension == 0) ok = get_num_prop(obj, AT_byte_size, &sz); if (!ok && sym->sym_class == SYM_CLASS_FUNCTION) { if (obj->u.mAddr.mHighPC > obj->u.mAddr.mLowPC) { ok = 1; sz = obj->u.mAddr.mHighPC - obj->u.mAddr.mLowPC; } } else if (!ok) { ObjectInfo * ref = NULL; if (sym->sym_class == SYM_CLASS_REFERENCE) { ref = obj; if (obj->mType != NULL) { obj = obj->mType; if (sym->dimension == 0) ok = get_num_prop(obj, AT_byte_size, &sz); } } while (!ok && obj->mType != NULL) { if (!is_modified_type(obj) && obj->mTag != TAG_enumeration_type) break; obj = obj->mType; if (sym->dimension == 0) ok = get_num_prop(obj, AT_byte_size, &sz); } if (!ok && obj->mTag == TAG_array_type) { unsigned i = 0; U8_T length = 1; ObjectInfo * elem_type = obj->mType; ObjectInfo * idx = obj->mChildren; while (idx != NULL) { if (i++ >= sym->dimension) length *= get_object_length(idx); idx = idx->mSibling; } ok = get_num_prop(obj, AT_stride_size, &sz); if (ok) { sz = (sz * length + 7) / 8; } else { if (elem_type == NULL) str_exception(ERR_OTHER, "Unknown array element type"); ok = get_num_prop(elem_type, AT_byte_size, &sz); while (!ok && elem_type->mType != NULL) { if (!is_modified_type(elem_type) && elem_type->mTag != TAG_enumeration_type) break; elem_type = elem_type->mType; ok = get_num_prop(elem_type, AT_byte_size, &sz); } if (ok) sz *= length; } } if (!ok && ref && ref->mTag != TAG_member && ref->mTag != TAG_inheritance) { Trap trap; if (set_trap(&trap)) { PropertyValue v; read_and_evaluate_dwarf_object_property(sym_ctx, sym_frame, 0, ref, AT_location, &v); if (v.mRegister) { sz = v.mRegister->size; ok = 1; } clear_trap(&trap); } } if (!ok && ref != NULL) { Symbol * elf_sym = NULL; ContextAddress elf_sym_size = 0; if (map_to_sym_table(ref, &elf_sym) && get_symbol_size(elf_sym, &elf_sym_size) == 0) { sz = elf_sym_size; ok = 1; } } } if (!ok) str_exception(ERR_INV_DWARF, "Object has no size attribute"); *size = (ContextAddress)sz; clear_trap(&trap); } else if (sym->tbl != NULL) { ELF_SymbolInfo info; unpack_elf_symbol_info(sym->tbl, sym->index, &info); switch (info.type) { case STT_OBJECT: case STT_FUNC: *size = (ContextAddress)info.size; break; default: *size = info.sym_section->file->elf64 ? 8 : 4; break; } } else { errno = set_errno(ERR_OTHER, "Debug info not available"); return -1; } return 0; } int get_symbol_base_type(const Symbol * sym, Symbol ** base_type) { ObjectInfo * obj = sym->obj; assert(sym->magic == SYMBOL_MAGIC); if (sym->base) { if (sym->base->sym_class == SYM_CLASS_FUNCTION) { if (sym->base->obj != NULL && sym->base->obj->mType != NULL) { if (unpack(sym->base) < 0) return -1; object2symbol(sym->base->obj->mType, base_type); } else { /* Function return type is 'void' */ alloc_cardinal_type_pseudo_symbol(sym->ctx, 0, base_type); } return 0; } *base_type = sym->base; return 0; } if (is_cardinal_type_pseudo_symbol(sym)) { errno = ERR_INV_CONTEXT; return -1; } if (unpack(sym) < 0) return -1; obj = get_original_type(obj); if (obj != NULL) { if (obj->mTag == TAG_array_type) { int i = sym->dimension; ObjectInfo * idx = obj->mChildren; while (i > 0 && idx != NULL) { idx = idx->mSibling; i--; } if (idx != NULL && idx->mSibling != NULL) { *base_type = alloc_symbol(); **base_type = *sym; (*base_type)->dimension++; return 0; } } if ((obj->mTag == TAG_pointer_type || obj->mTag == TAG_mod_pointer) && obj->mType == NULL) { /* pointer to void */ alloc_cardinal_type_pseudo_symbol(sym->ctx, 0, base_type); return 0; } obj = obj->mType; if (obj != NULL) { object2symbol(obj, base_type); return 0; } } errno = ERR_UNSUPPORTED; return -1; } int get_symbol_index_type(const Symbol * sym, Symbol ** index_type) { ObjectInfo * obj = sym->obj; assert(sym->magic == SYMBOL_MAGIC); if (sym->base) { if (sym->base->sym_class == SYM_CLASS_FUNCTION) { errno = ERR_INV_CONTEXT; return -1; } alloc_cardinal_type_pseudo_symbol(sym->ctx, context_word_size(sym->ctx), index_type); return 0; } if (is_cardinal_type_pseudo_symbol(sym)) { errno = ERR_INV_CONTEXT; return -1; } if (unpack(sym) < 0) return -1; obj = get_original_type(obj); if (obj != NULL && obj->mTag == TAG_array_type) { int i = sym->dimension; ObjectInfo * idx = obj->mChildren; while (i > 0 && idx != NULL) { idx = idx->mSibling; i--; } if (idx != NULL) { object2symbol(idx, index_type); return 0; } } errno = ERR_UNSUPPORTED; return -1; } int get_symbol_length(const Symbol * sym, ContextAddress * length) { ObjectInfo * obj = sym->obj; assert(sym->magic == SYMBOL_MAGIC); if (sym->base) { if (sym->base->sym_class == SYM_CLASS_FUNCTION) { errno = ERR_INV_CONTEXT; return -1; } *length = sym->length == 0 ? 1 : sym->length; return 0; } if (is_cardinal_type_pseudo_symbol(sym)) { errno = ERR_INV_CONTEXT; return -1; } if (unpack(sym) < 0) return -1; obj = get_original_type(obj); if (obj != NULL && obj->mTag == TAG_array_type) { int i = sym->dimension; ObjectInfo * idx = obj->mChildren; while (i > 0 && idx != NULL) { idx = idx->mSibling; i--; } if (idx != NULL) { Trap trap; if (!set_trap(&trap)) return -1; *length = (ContextAddress)get_object_length(idx); clear_trap(&trap); return 0; } } errno = ERR_UNSUPPORTED; return -1; } int get_symbol_lower_bound(const Symbol * sym, int64_t * value) { ObjectInfo * obj = sym->obj; assert(sym->magic == SYMBOL_MAGIC); if (sym->base) { if (sym->base->sym_class == SYM_CLASS_FUNCTION) { errno = ERR_INV_CONTEXT; return -1; } *value = 0; return 0; } if (is_cardinal_type_pseudo_symbol(sym)) { errno = ERR_INV_CONTEXT; return -1; } if (unpack(sym) < 0) return -1; obj = get_original_type(obj); if (obj != NULL && obj->mTag == TAG_array_type) { int i = sym->dimension; ObjectInfo * idx = obj->mChildren; while (i > 0 && idx != NULL) { idx = idx->mSibling; i--; } if (idx != NULL) { if (get_num_prop(obj, AT_lower_bound, (U8_T *)value)) return 0; if (get_error_code(errno) != ERR_SYM_NOT_FOUND) return -1; *value = get_default_lower_bound(obj); return 0; } } errno = ERR_UNSUPPORTED; return -1; } int get_symbol_children(const Symbol * sym, Symbol *** children, int * count) { static Symbol ** buf = NULL; static int buf_len = 0; ObjectInfo * obj = sym->obj; assert(sym->magic == SYMBOL_MAGIC); if (sym->base) { obj = sym->base->obj; if (sym->base->sym_class == SYM_CLASS_FUNCTION) { if (obj == NULL) { *children = NULL; *count = 0; errno = ERR_SYM_NOT_FOUND; return -1; } else { int n = 0; ObjectInfo * i = obj->mChildren; if (unpack(sym->base) < 0) return -1; while (i != NULL) { if (i->mTag == TAG_formal_parameter || i->mTag == TAG_unspecified_parameters) { Symbol * x = NULL; Symbol * y = NULL; object2symbol(i, &x); if (get_symbol_type(x, &y) < 0) return -1; if (buf_len <= n) { buf_len += 16; buf = (Symbol **)loc_realloc(buf, sizeof(Symbol *) * buf_len); } buf[n++] = y; } i = i->mSibling; } *children = buf; *count = n; return 0; } } *children = NULL; *count = 0; return 0; } if (is_cardinal_type_pseudo_symbol(sym)) { *children = NULL; *count = 0; return 0; } if (unpack(sym) < 0) return -1; obj = get_original_type(obj); if (obj != NULL) { int n = 0; ObjectInfo * i = obj->mChildren; while (i != NULL) { Symbol * x = NULL; object2symbol(i, &x); if (buf_len <= n) { buf_len += 16; buf = (Symbol **)loc_realloc(buf, sizeof(Symbol *) * buf_len); } buf[n++] = x; i = i->mSibling; } *children = buf; *count = n; return 0; } *children = NULL; *count = 0; return 0; } int get_symbol_offset(const Symbol * sym, ContextAddress * offset) { ObjectInfo * obj = sym->obj; assert(sym->magic == SYMBOL_MAGIC); if (sym->base || is_cardinal_type_pseudo_symbol(sym)) { errno = ERR_INV_CONTEXT; return -1; } if (unpack(sym) < 0) return -1; if (obj != NULL && (obj->mTag == TAG_member || obj->mTag == TAG_inheritance)) { U8_T v; if (!get_num_prop(obj, AT_data_member_location, &v)) return -1; *offset = (ContextAddress)v; return 0; } errno = ERR_INV_CONTEXT; return -1; } int get_symbol_value(const Symbol * sym, void ** value, size_t * size, int * big_endian) { ObjectInfo * obj = sym->obj; assert(sym->magic == SYMBOL_MAGIC); if (sym->base || is_cardinal_type_pseudo_symbol(sym) || sym->var) { errno = ERR_INV_CONTEXT; return -1; } if (unpack(sym) < 0) return -1; if (obj != NULL) { Trap trap; PropertyValue v; static U1_T * bf = NULL; static size_t bf_size = 0; if (set_trap(&trap)) { read_and_evaluate_dwarf_object_property(sym_ctx, sym_frame, 0, obj, AT_const_value, &v); if (v.mAddr != NULL) { *size = v.mSize; *value = v.mAddr; } else if (v.mRegister != NULL || v.mPieces != NULL) { str_exception(ERR_INV_CONTEXT, "Constant DWARF attribute value expected"); } else { U8_T n = v.mValue; size_t i = 0; if (bf_size < sizeof(v.mValue)) { bf_size = sizeof(v.mValue); bf = (U1_T *)loc_realloc(bf, bf_size); } for (i = 0; i < sizeof(v.mValue); i++) { bf[v.mBigEndian ? sizeof(v.mValue) - i - 1 : i] = n & 0xffu; n = n >> 8; } *size = sizeof(v.mValue); *value = bf; } *big_endian = v.mBigEndian; clear_trap(&trap); return 0; } else if (trap.error != ERR_SYM_NOT_FOUND) { return -1; } if (set_trap(&trap)) { read_and_evaluate_dwarf_object_property(sym_ctx, sym_frame, 0, obj, AT_location, &v); if (v.mPieces != NULL) { U4_T n = 0; U4_T bf_offs = 0; while (n < v.mPieceCnt) { U4_T i; U1_T pbf[32]; PropertyValuePiece * piece = v.mPieces + n++; U4_T piece_size = (piece->mBitSize + 7) / 8; if (piece_size > sizeof(pbf)) exception(ERR_BUFFER_OVERFLOW); if (bf_size < bf_offs / 8 + piece_size + 1) { bf_size = bf_offs / 8 + piece_size + 1; bf = (U1_T *)loc_realloc(bf, bf_size); } if (piece->mRegister) { StackFrame * frame = NULL; RegisterDefinition * def = piece->mRegister; if (get_frame_info(v.mContext, v.mFrame, &frame) < 0) exception(errno); if (read_reg_bytes(frame, def, 0, piece_size, pbf) < 0) exception(errno); } else { if (context_read_mem(v.mContext, piece->mAddress, pbf, piece_size) < 0) exception(errno); } if (!piece->mBigEndian != !v.mBigEndian) swap_bytes(pbf, piece_size); for (i = piece->mBitOffset; i < piece->mBitOffset + piece->mBitSize; i++) { if (pbf[i / 8] & (1u << (i % 8))) { bf[bf_offs / 8] |= (1u << (bf_offs % 8)); } else { bf[bf_offs / 8] &= ~(1u << (bf_offs % 8)); } bf_offs++; } } while (bf_offs % 8) { bf[bf_offs / 8] &= ~(1u << (bf_offs % 8)); bf_offs++; } *value = bf; *size = bf_offs / 8; *big_endian = v.mBigEndian; } else if (v.mRegister != NULL) { StackFrame * frame = NULL; RegisterDefinition * def = v.mRegister; ContextAddress sym_size = def->size; unsigned val_offs = 0; unsigned val_size = 0; if (get_symbol_size(sym, &sym_size) < 0) exception(errno); if (bf_size < sym_size) { bf_size = (size_t)sym_size; bf = (U1_T *)loc_realloc(bf, bf_size); } if (get_frame_info(v.mContext, v.mFrame, &frame) < 0) exception(errno); val_size = def->size < sym_size ? (unsigned)def->size : (unsigned)sym_size; if (def->big_endian) val_offs = (unsigned)def->size - val_size; if (read_reg_bytes(frame, def, val_offs, val_size, bf) < 0) exception(errno); *value = bf; *size = val_size; *big_endian = def->big_endian; } else { exception(ERR_INV_CONTEXT); } clear_trap(&trap); return 0; } else if (trap.error != ERR_SYM_NOT_FOUND) { return -1; } set_errno(ERR_OTHER, "Object location or value info not available"); return -1; } if (sym->tbl != NULL) { ELF_SymbolInfo info; unpack_elf_symbol_info(sym->tbl, sym->index, &info); switch (info.type) { case STT_OBJECT: case STT_FUNC: set_errno(ERR_OTHER, "Symbol represents an address"); return -1; } if (info.sym_section->file->elf64) { static U8_T buf = 0; buf = info.value; *value = &buf; *size = 8; } else { static U4_T buf = 0; buf = (U4_T)info.value; *value = &buf; *size = 4; } *big_endian = big_endian_host(); return 0; } errno = ERR_INV_CONTEXT; return -1; } static int calc_member_offset(ObjectInfo * type, ObjectInfo * member, ContextAddress * offs) { PropertyValue v; ObjectInfo * obj = NULL; if (member->mParent == type) { read_and_evaluate_dwarf_object_property(sym_ctx, sym_frame, 0, member, AT_data_member_location, &v); *offs = (ContextAddress)get_numeric_property_value(&v); return 1; } obj = type->mChildren; while (obj != NULL) { if (obj->mTag == TAG_inheritance && calc_member_offset(obj->mType, member, offs)) { read_and_evaluate_dwarf_object_property(sym_ctx, sym_frame, 0, obj, AT_data_member_location, &v); *offs += (ContextAddress)get_numeric_property_value(&v); return 1; } obj = obj->mSibling; } return 0; } int get_symbol_address(const Symbol * sym, ContextAddress * address) { ObjectInfo * obj = sym->obj; assert(sym->magic == SYMBOL_MAGIC); if (sym->base || is_cardinal_type_pseudo_symbol(sym)) { errno = ERR_INV_CONTEXT; return -1; } if (sym->has_address) { *address = sym->address; return 0; } if (unpack(sym) < 0) return -1; if (sym->var != NULL) { /* The symbol represents a member of a class instance */ Trap trap; PropertyValue v; ContextAddress base = 0; ContextAddress offs = 0; ObjectInfo * type = get_original_type(sym->var); if (!set_trap(&trap)) return -1; if ((type->mTag != TAG_pointer_type && type->mTag != TAG_mod_pointer) || type->mType == NULL) exception(ERR_INV_CONTEXT); read_and_evaluate_dwarf_object_property(sym_ctx, sym_frame, 0, sym->var, AT_location, &v); if (v.mPieces != NULL) { str_exception(ERR_UNSUPPORTED, "Unsupported location of 'this' pointer"); } else if (v.mRegister != NULL) { U8_T rv = 0; StackFrame * frame = NULL; if (get_frame_info(v.mContext, v.mFrame, &frame) < 0) exception(errno); if (read_reg_value(frame, v.mRegister, &rv) < 0) exception(errno); base = (ContextAddress)rv; } else { if (elf_read_memory_word(sym_ctx, sym->var->mCompUnit->mFile, (ContextAddress)get_numeric_property_value(&v), &base) < 0) exception(errno); } type = get_original_type(type->mType); if (!calc_member_offset(type, obj, &offs)) exception(ERR_INV_CONTEXT); clear_trap(&trap); *address = base + offs; return 0; } if (obj != NULL && obj->mTag != TAG_member && obj->mTag != TAG_inheritance) { U8_T v; Symbol * s = NULL; if (get_num_prop(obj, AT_location, &v)) { *address = (ContextAddress)v; return 0; } if (get_error_code(errno) != ERR_SYM_NOT_FOUND) return -1; if (get_num_prop(obj, AT_low_pc, &v)) { *address = (ContextAddress)v; return 0; } if (get_error_code(errno) != ERR_SYM_NOT_FOUND) return -1; if (map_to_sym_table(obj, &s)) return get_symbol_address(s, address); set_errno(ERR_OTHER, "No object location info found in DWARF data"); return -1; } if (sym->tbl != NULL) { ELF_SymbolInfo info; unpack_elf_symbol_info(sym->tbl, sym->index, &info); return syminfo2address(sym_ctx, &info, address); } errno = ERR_INV_CONTEXT; return -1; } int get_symbol_register(const Symbol * sym, Context ** ctx, int * frame, RegisterDefinition ** reg) { ObjectInfo * obj = sym->obj; assert(sym->magic == SYMBOL_MAGIC); if (sym->base || is_cardinal_type_pseudo_symbol(sym) || sym->has_address) { errno = ERR_INV_CONTEXT; return -1; } if (unpack(sym) < 0) return -1; if (obj != NULL && obj->mTag != TAG_member && obj->mTag != TAG_inheritance) { Trap trap; if (set_trap(&trap)) { PropertyValue v; read_and_evaluate_dwarf_object_property(sym_ctx, sym_frame, 0, obj, AT_location, &v); *ctx = sym_ctx; *frame = sym_frame; *reg = v.mRegister; clear_trap(&trap); return 0; } } errno = ERR_INV_CONTEXT; return -1; } int get_symbol_flags(const Symbol * sym, SYM_FLAGS * flags) { U8_T v = 0; ObjectInfo * i = NULL; ObjectInfo * obj = sym->obj; *flags = 0; assert(sym->magic == SYMBOL_MAGIC); if (sym->base || is_cardinal_type_pseudo_symbol(sym)) return 0; if (unpack(sym) < 0) return -1; i = obj; while (i != NULL) { switch (i->mTag) { case TAG_subrange_type: *flags |= SYM_FLAG_SUBRANGE_TYPE; i = i->mType; break; case TAG_packed_type: *flags |= SYM_FLAG_PACKET_TYPE; i = i->mType; break; case TAG_const_type: *flags |= SYM_FLAG_CONST_TYPE; i = i->mType; break; case TAG_volatile_type: *flags |= SYM_FLAG_VOLATILE_TYPE; i = i->mType; break; case TAG_restrict_type: *flags |= SYM_FLAG_RESTRICT_TYPE; i = i->mType; break; case TAG_shared_type: *flags |= SYM_FLAG_SHARED_TYPE; i = i->mType; break; case TAG_typedef: if (i == obj) *flags |= SYM_FLAG_TYPEDEF; i = i->mType; break; case TAG_reference_type: case TAG_mod_reference: *flags |= SYM_FLAG_REFERENCE; i = NULL; break; case TAG_union_type: *flags |= SYM_FLAG_UNION_TYPE; i = NULL; break; case TAG_class_type: *flags |= SYM_FLAG_CLASS_TYPE; i = NULL; break; case TAG_interface_type: *flags |= SYM_FLAG_INTERFACE_TYPE; i = NULL; break; case TAG_unspecified_parameters: *flags |= SYM_FLAG_PARAMETER; *flags |= SYM_FLAG_VARARG; i = NULL; break; case TAG_formal_parameter: case TAG_variable: case TAG_constant: case TAG_base_type: if (i->mTag == TAG_formal_parameter) { *flags |= SYM_FLAG_PARAMETER; if (get_num_prop(i, AT_is_optional, &v) && v != 0) *flags |= SYM_FLAG_OPTIONAL; } if (i->mTag == TAG_variable && get_num_prop(obj, AT_external, &v) && v != 0) { *flags |= SYM_FLAG_EXTERNAL; } if (get_num_prop(i, AT_endianity, &v)) { if (v == DW_END_big) *flags |= SYM_FLAG_BIG_ENDIAN; if (v == DW_END_little) *flags |= SYM_FLAG_LITTLE_ENDIAN; } i = NULL; break; default: i = NULL; break; } } if (obj != NULL && sym->sym_class == SYM_CLASS_TYPE && !(*flags & (SYM_FLAG_BIG_ENDIAN|SYM_FLAG_LITTLE_ENDIAN))) { *flags |= obj->mCompUnit->mFile->big_endian ? SYM_FLAG_BIG_ENDIAN : SYM_FLAG_LITTLE_ENDIAN; } return 0; } int get_array_symbol(const Symbol * sym, ContextAddress length, Symbol ** ptr) { assert(sym->magic == SYMBOL_MAGIC); assert(sym->sym_class == SYM_CLASS_TYPE); assert(sym->frame == STACK_NO_FRAME); assert(sym->ctx == context_get_group(sym->ctx, CONTEXT_GROUP_PROCESS)); *ptr = alloc_symbol(); (*ptr)->ctx = sym->ctx; (*ptr)->frame = STACK_NO_FRAME; (*ptr)->sym_class = SYM_CLASS_TYPE; (*ptr)->base = (Symbol *)sym; (*ptr)->length = length; return 0; } #endif /* SERVICE_Symbols && ENABLE_ELF */