blob: 38ff5b7b7814b25e93076c896af7f6d92527b2df [file] [log] [blame]
/*******************************************************************************
* 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 <config.h>
#if SERVICE_Symbols && !ENABLE_SymbolsProxy && ENABLE_ELF
#if defined(_WRS_KERNEL)
# include <symLib.h>
# include <sysSymTbl.h>
#endif
#include <errno.h>
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <framework/errors.h>
#include <framework/myalloc.h>
#include <framework/events.h>
#include <framework/exceptions.h>
#include <services/tcf_elf.h>
#include <services/dwarf.h>
#include <services/dwarfcache.h>
#include <services/dwarfexpr.h>
#include <services/dwarfframe.h>
#include <services/stacktrace.h>
#include <services/symbols.h>
#if ENABLE_RCBP_TEST
# include <main/test.h>
#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 <services/symbols_alloc.h>
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, int at, U8_T * res) {
Trap trap;
PropertyValue v;
if (!set_trap(&trap)) return 0;
/* XXX : fle : check if cast is not an error */
read_and_evaluate_dwarf_object_property(sym_ctx, sym_frame, 0, obj, (U2_T) 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) {
/* 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, &section);
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) {
int n = 0;
static Symbol ** buf = NULL;
static int buf_len = 0;
ObjectInfo * obj = sym->obj;
assert(sym->magic == SYMBOL_MAGIC);
if (sym->base) {
if (sym->base->sym_class == SYM_CLASS_FUNCTION && sym->base->obj == NULL) {
*children = NULL;
*count = 0;
errno = ERR_SYM_NOT_FOUND;
return -1;
}
if (sym->base->sym_class == SYM_CLASS_FUNCTION) {
ObjectInfo * i = sym->base->obj->mChildren;
if (unpack(sym->base) < 0) return -1;
while (i != NULL) {
if (i->mTag == TAG_formal_parameter || i->mTag == TAG_unspecified_parameters) n++;
i = i->mSibling;
}
if (buf_len < n) {
buf = (Symbol **)loc_realloc(buf, sizeof(Symbol *) * n);
buf_len = n;
}
n = 0;
i = sym->base->obj->mChildren;
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;
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) {
ObjectInfo * i = obj->mChildren;
while (i != NULL) {
i = i->mSibling;
n++;
}
if (buf_len < n) {
buf = (Symbol **)loc_realloc(buf, sizeof(Symbol *) * n);
buf_len = n;
}
n = 0;
i = obj->mChildren;
while (i != NULL) {
Symbol * x = NULL;
object2symbol(i, &x);
buf[n++] = x;
i = i->mSibling;
}
}
*children = buf;
*count = n;
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 */