| /******************************************************************************* |
| * Copyright (c) 2007, 2015 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 |
| *******************************************************************************/ |
| |
| /* |
| * This module implements caching of DWARF debug information. |
| * |
| * Functions in this module use exceptions to report errors, see exceptions.h |
| */ |
| |
| #include <tcf/config.h> |
| |
| #if ENABLE_ELF && ENABLE_DebugContext |
| |
| #include <assert.h> |
| #include <tcf/framework/exceptions.h> |
| #include <tcf/framework/myalloc.h> |
| #include <tcf/framework/trace.h> |
| #include <tcf/services/dwarf.h> |
| #include <tcf/services/dwarfio.h> |
| #include <tcf/services/dwarfcache.h> |
| #include <tcf/services/dwarfexpr.h> |
| #include <tcf/services/stacktrace.h> |
| |
| #define OBJ_HASH(HashTable,ID) (((U4_T)(ID) + ((U4_T)(ID) >> 8)) % HashTable->mObjectHashSize) |
| |
| #define OBJECT_ARRAY_SIZE 128 |
| |
| /* Pseudo object IDs for fundamental types */ |
| #define OBJECT_ID_VOID(CompUnit) (~(CompUnit)->mFundTypeID - 0) |
| #define OBJECT_ID_CHAR(CompUnit) (~(CompUnit)->mFundTypeID - 1) |
| #define OBJECT_ID_LAST(Cache) (~(Cache)->mFundTypeID) |
| |
| typedef struct ObjectArray { |
| struct ObjectArray * mNext; |
| ObjectInfo mArray[OBJECT_ARRAY_SIZE]; |
| } ObjectArray; |
| |
| typedef struct ObjectReference { |
| ObjectInfo * obj; |
| ObjectInfo * org; |
| } ObjectReference; |
| |
| static DWARFCache * sCache; |
| static ELF_Section * sDebugSection; |
| static DIO_UnitDescriptor sUnitDesc; |
| static CompUnit * sCompUnit; |
| static ObjectInfo * sParentObject; |
| static ObjectInfo * sPrevSibling; |
| static ObjectReference * sObjRefs; |
| static U4_T sObjRefsCnt = 0; |
| static U4_T sObjRefsMax = 0; |
| |
| static int sCloseListenerOK = 0; |
| |
| unsigned calc_file_name_hash(const char * s) { |
| unsigned h = 0; |
| if (s != NULL) { |
| unsigned l = strlen(s); |
| while (l > 0) { |
| unsigned g; |
| unsigned char ch = s[--l]; |
| if (ch == '/') break; |
| if (ch == '\\') break; |
| h = (h << 4) + ch; |
| g = h & 0xf0000000; |
| if (g) h = (h ^ (g >> 24)) & ~g; |
| } |
| } |
| return h; |
| } |
| |
| static ObjectInfo * add_object_info(ContextAddress ID) { |
| ObjectHashTable * HashTable = sCache->mObjectHashTable + sDebugSection->index; |
| U4_T Hash = OBJ_HASH(HashTable, ID); |
| ObjectInfo * Info = HashTable->mObjectHash[Hash]; |
| while (Info != NULL) { |
| if (Info->mID == ID) return Info; |
| Info = Info->mHashNext; |
| } |
| if (ID < OBJECT_ID_LAST(sCache)) { |
| if (ID < sDebugSection->addr) str_exception(ERR_INV_DWARF, "Invalid entry reference"); |
| if (ID > sDebugSection->addr + sDebugSection->size) str_exception(ERR_INV_DWARF, "Invalid entry reference"); |
| } |
| if (sCache->mObjectArrayPos >= OBJECT_ARRAY_SIZE) { |
| ObjectArray * Buf = (ObjectArray *)loc_alloc_zero(sizeof(ObjectArray)); |
| Buf->mNext = sCache->mObjectList; |
| sCache->mObjectList = Buf; |
| sCache->mObjectArrayPos = 0; |
| } |
| Info = sCache->mObjectList->mArray + sCache->mObjectArrayPos++; |
| Info->mHashNext = HashTable->mObjectHash[Hash]; |
| HashTable->mObjectHash[Hash] = Info; |
| Info->mID = ID; |
| return Info; |
| } |
| |
| static CompUnit * add_comp_unit(ContextAddress ID) { |
| ObjectInfo * Info = add_object_info(ID); |
| if (Info->mCompUnit == NULL) { |
| CompUnit * Unit = (CompUnit *)loc_alloc_zero(sizeof(CompUnit)); |
| Unit->mFile = sCache->mFile; |
| Unit->mFundTypeID = sCache->mFundTypeID; |
| Unit->mRegIdScope.big_endian = sCache->mFile->big_endian; |
| Unit->mRegIdScope.machine = sCache->mFile->machine; |
| Unit->mRegIdScope.os_abi = sCache->mFile->os_abi; |
| Unit->mRegIdScope.elf64 = sCache->mFile->elf64; |
| Unit->mRegIdScope.id_type = REGNUM_DWARF; |
| Unit->mObject = Info; |
| Info->mCompUnit = Unit; |
| sCache->mFundTypeID += 2; |
| } |
| return Info->mCompUnit; |
| } |
| |
| static CompUnit * find_comp_unit(ELF_Section * Section, ContextAddress ObjID) { |
| ObjectHashTable * HashTable = sCache->mObjectHashTable + Section->index; |
| if (sCompUnit != NULL && sCompUnit->mDesc.mSection == Section) { |
| ContextAddress ID = sCompUnit->mDesc.mSection->addr + sCompUnit->mDesc.mUnitOffs; |
| if (ID <= ObjID && ID + sCompUnit->mDesc.mUnitSize > ObjID) return sCompUnit; |
| } |
| if (HashTable->mCompUnitsIndex != NULL) { |
| unsigned l = 0; |
| unsigned h = HashTable->mCompUnitsIndexSize; |
| while (l < h) { |
| unsigned i = (l + h) / 2; |
| CompUnit * unit = HashTable->mCompUnitsIndex[i]; |
| ContextAddress ID = unit->mDesc.mSection->addr + unit->mDesc.mUnitOffs; |
| if (ID > ObjID) { |
| h = i; |
| } |
| else if (ID + unit->mDesc.mUnitSize <= ObjID) { |
| l = i + 1; |
| } |
| else { |
| return unit; |
| } |
| } |
| } |
| return NULL; |
| } |
| |
| static void add_type_unit(CompUnit * Unit) { |
| unsigned Hash = (unsigned)(Unit->mDesc.mTypeSignature % sCache->mTypeUnitHashSize); |
| assert(Unit->mDesc.mTypeOffset != 0); |
| Unit->mNextTypeUnit = sCache->mTypeUnitHash[Hash]; |
| sCache->mTypeUnitHash[Hash] = Unit; |
| } |
| |
| static CompUnit * find_type_unit(U8_T Signature) { |
| unsigned Hash = (unsigned)(Signature % sCache->mTypeUnitHashSize); |
| CompUnit * Unit = sCache->mTypeUnitHash[Hash]; |
| while (Unit != NULL) { |
| if (Unit->mDesc.mTypeSignature == Signature) return Unit; |
| Unit = Unit->mNextTypeUnit; |
| } |
| return NULL; |
| } |
| |
| static void read_type_unit_header(U2_T Tag, U2_T Attr, U2_T Form) { |
| if (Attr == 0) { |
| if (Form) { |
| assert(sParentObject == NULL); |
| if (Tag != TAG_type_unit) str_exception(ERR_INV_DWARF, "Invalid .debug_types section"); |
| sCompUnit = add_comp_unit((ContextAddress)(sDebugSection->addr + dio_gEntryPos)); |
| } |
| else { |
| if (find_type_unit(sUnitDesc.mTypeSignature) != NULL) { |
| str_exception(ERR_INV_DWARF, "Invalid .debug_types section"); |
| } |
| sCompUnit->mDesc = sUnitDesc; |
| add_type_unit(sCompUnit); |
| add_object_info((ContextAddress)(sDebugSection->addr + sUnitDesc.mUnitOffs + sUnitDesc.mTypeOffset)); |
| sCompUnit = NULL; |
| assert(sUnitDesc.mUnitSize > 0); |
| dio_SetPos(sUnitDesc.mUnitOffs + sUnitDesc.mUnitSize); |
| } |
| } |
| } |
| |
| static void read_object_info(U2_T Tag, U2_T Attr, U2_T Form); |
| static void read_object_refs(ELF_Section * Section); |
| |
| ObjectInfo * find_object(ELF_Section * Section, ContextAddress ID) { |
| DWARFCache * Cache = get_dwarf_cache(Section->file); |
| ObjectHashTable * HashTable = Cache->mObjectHashTable + Section->index; |
| ObjectInfo * Info = HashTable->mObjectHash[OBJ_HASH(HashTable, ID)]; |
| while (Info != NULL) { |
| if (Info->mID == ID) return Info; |
| Info = Info->mHashNext; |
| } |
| #if ENABLE_DWARF_LAZY_LOAD |
| if (Cache->lazy_loaded) { |
| sCache = Cache; |
| sCompUnit = NULL; |
| sCompUnit = find_comp_unit(Section, ID); |
| if (sCompUnit != NULL) { |
| Trap trap; |
| sUnitDesc = sCompUnit->mDesc; |
| sDebugSection = sUnitDesc.mSection; |
| sParentObject = NULL; |
| sPrevSibling = NULL; |
| dio_EnterSection(&sCompUnit->mDesc, sDebugSection, ID - sDebugSection->addr); |
| if (set_trap(&trap)) { |
| dio_ReadEntry(read_object_info, 0); |
| Info = HashTable->mObjectHash[OBJ_HASH(HashTable, ID)]; |
| while (Info != NULL) { |
| if (Info->mID == ID) break; |
| Info = Info->mHashNext; |
| } |
| clear_trap(&trap); |
| } |
| dio_ExitSection(); |
| sDebugSection = NULL; |
| read_object_refs(Section); |
| } |
| sCompUnit = NULL; |
| sCache = NULL; |
| } |
| #endif |
| return Info; |
| } |
| |
| static ObjectInfo * find_loaded_object(ELF_Section * Section, ContextAddress ID) { |
| DWARFCache * Cache = (DWARFCache *)Section->file->dwarf_dt_cache; |
| if (Cache != NULL) { |
| ObjectHashTable * HashTable = Cache->mObjectHashTable + Section->index; |
| ObjectInfo * Info = HashTable->mObjectHash[OBJ_HASH(HashTable, ID)]; |
| while (Info != NULL) { |
| if (Info->mID == ID) return Info; |
| Info = Info->mHashNext; |
| } |
| } |
| return NULL; |
| } |
| |
| static ObjectInfo * find_alt_object_info(ContextAddress ID) { |
| unsigned i; |
| ObjectInfo * Info = NULL; |
| ELF_File * File = sCache->mFile; |
| if (File->dwz_file == NULL) str_exception(errno, "Cannot open DWZ file"); |
| if (get_dwarf_cache(File->dwz_file) == NULL) str_exception(errno, "Cannot read DWZ file"); |
| for (i = 0; i < File->dwz_file->section_cnt; i++) { |
| ELF_Section * section = File->dwz_file->sections + i; |
| if (section->name != NULL && strcmp(section->name, ".debug_info") == 0) { |
| if (Info != NULL) str_exception(ERR_INV_DWARF, "More then one .debug_info section in DWZ file"); |
| Info = find_loaded_object(section, ID); |
| } |
| } |
| if (Info == NULL) str_exception(errno, "Invalid DWZ file reference"); |
| return Info; |
| } |
| |
| static void add_object_reference(ObjectInfo * org, ObjectInfo * obj) { |
| #if ENABLE_DWARF_LAZY_LOAD |
| if (org->mTag != 0 && obj == NULL) return; |
| #else |
| if (obj == NULL) return; |
| #endif |
| if (org->mCompUnit == NULL) org->mCompUnit = find_comp_unit(sDebugSection, org->mID); |
| if (sObjRefsCnt >= sObjRefsMax) { |
| sObjRefsMax += sObjRefsMax ? sObjRefsMax * 2 : 256; |
| sObjRefs = (ObjectReference *)loc_realloc(sObjRefs, sizeof(ObjectReference) * sObjRefsMax); |
| } |
| if (obj != NULL) obj->mFlags |= DOIF_load_mark; |
| sObjRefs[sObjRefsCnt].org = org; |
| sObjRefs[sObjRefsCnt].obj = obj; |
| sObjRefsCnt++; |
| } |
| |
| static U4_T get_fund_type_size(CompUnit * Unit, U2_T ft) { |
| switch (ft) { |
| case FT_char : |
| case FT_signed_char : |
| case FT_unsigned_char : |
| return 1; |
| case FT_short : |
| case FT_signed_short : |
| case FT_unsigned_short: |
| return 2; |
| case FT_integer : |
| case FT_signed_integer: |
| case FT_unsigned_integer: |
| return 4; |
| case FT_long : |
| case FT_signed_long : |
| case FT_unsigned_long : |
| return Unit->mFile->elf64 ? 8 : 4; |
| case FT_pointer : |
| return Unit->mDesc.mAddressSize; |
| case FT_float : |
| return 4; |
| case FT_dbl_prec_float: |
| return 8; |
| case FT_complex : |
| return 8; |
| case FT_dbl_prec_complex: |
| return 16; |
| case FT_boolean : |
| return 4; |
| case FT_void : |
| return 0; |
| } |
| str_exception(ERR_INV_DWARF, "Invalid fundamental type code"); |
| return 0; |
| } |
| |
| static void read_mod_fund_type(U2_T Form, ObjectInfo ** Type) { |
| U1_T * Buf; |
| size_t BufSize; |
| size_t BufPos; |
| int i; |
| U2_T FT = 0; |
| dio_ChkBlock(Form, &Buf, &BufSize); |
| for (i = 0; i < 2; i++) { |
| FT |= (U2_T)Buf[BufSize - 2 + |
| (sDebugSection->file->big_endian ? 1 - i : i)] << (i * 8); |
| } |
| *Type = add_object_info((ContextAddress)(sDebugSection->addr + dio_GetPos() - 2)); |
| (*Type)->mTag = TAG_fund_type; |
| (*Type)->mCompUnit = sCompUnit; |
| (*Type)->u.mFundType = FT; |
| BufPos = BufSize - 2; |
| while (BufPos > 0) { |
| U2_T Tag = 0; |
| ObjectInfo * Mod = NULL; |
| switch (Buf[--BufPos]) { |
| case MOD_volatile: |
| case MOD_const: |
| continue; |
| case MOD_pointer_to: |
| Tag = TAG_mod_pointer; |
| break; |
| case MOD_reference_to: |
| Tag = TAG_mod_reference; |
| break; |
| default: |
| str_exception(ERR_INV_DWARF, "Invalid type modifier code"); |
| } |
| Mod = add_object_info((ContextAddress)(sDebugSection->addr + dio_GetPos() - BufSize + BufPos)); |
| Mod->mTag = Tag; |
| Mod->mCompUnit = sCompUnit; |
| Mod->mType = *Type; |
| *Type = Mod; |
| } |
| } |
| |
| static void read_mod_user_def_type(U2_T Form, ObjectInfo ** Type) { |
| U1_T * Buf; |
| size_t BufSize; |
| size_t BufPos; |
| int i; |
| U4_T Ref = 0; |
| dio_ChkBlock(Form, &Buf, &BufSize); |
| for (i = 0; i < 4; i++) { |
| Ref |= (U4_T)Buf[BufSize - 4 + |
| (sDebugSection->file->big_endian ? 3 - i : i)] << (i * 8); |
| } |
| *Type = add_object_info((ContextAddress)(sDebugSection->addr + Ref)); |
| add_object_reference(*Type, NULL); |
| BufPos = BufSize - 4; |
| while (BufPos > 0) { |
| U2_T Tag = 0; |
| ObjectInfo * Mod = NULL; |
| switch (Buf[--BufPos]) { |
| case MOD_volatile: |
| case MOD_const: |
| continue; |
| case MOD_pointer_to: |
| Tag = TAG_mod_pointer; |
| break; |
| case MOD_reference_to: |
| Tag = TAG_mod_reference; |
| break; |
| default: |
| str_exception(ERR_INV_DWARF, "Invalid type modifier code"); |
| } |
| Mod = add_object_info((ContextAddress)(sDebugSection->addr + dio_GetPos() - BufSize + BufPos)); |
| Mod->mTag = Tag; |
| Mod->mCompUnit = sCompUnit; |
| Mod->mType = *Type; |
| *Type = Mod; |
| } |
| } |
| |
| static I8_T read_long_value(void) { |
| switch (get_fund_type_size(sCompUnit, FT_long)) { |
| case 4: return (I4_T)dio_ReadU4(); |
| case 8: return (I8_T)dio_ReadU8(); |
| } |
| str_exception(ERR_OTHER, "Invalid size of long int"); |
| return 0; |
| } |
| |
| static void read_subscr_data(U2_T Form, ObjectInfo * Array) { |
| U1_T * Buf; |
| size_t BufSize; |
| U8_T BufEnd = 0; |
| U8_T OrgPos = dio_GetPos(); |
| ObjectInfo ** Children = &Array->mChildren; |
| |
| assert(Array->mChildren == NULL); |
| assert(Array->mType == NULL); |
| |
| dio_ChkBlock(Form, &Buf, &BufSize); |
| dio_SetPos(Buf - (U1_T *)sDebugSection->data); |
| BufEnd = dio_GetPos() + BufSize; |
| while (dio_GetPos() < BufEnd) { |
| ObjectInfo * Type = NULL; |
| U1_T Fmt = dio_ReadU1(); |
| switch (Fmt) { |
| case FMT_FT_C_C: |
| case FMT_FT_C_X: |
| case FMT_FT_X_C: |
| case FMT_FT_X_X: |
| Type = add_object_info((ContextAddress)(sDebugSection->addr + dio_GetPos())); |
| Type->mTag = TAG_fund_type; |
| Type->mCompUnit = sCompUnit; |
| Type->u.mFundType = dio_ReadU2(); |
| break; |
| case FMT_UT_C_C: |
| case FMT_UT_C_X: |
| case FMT_UT_X_C: |
| case FMT_UT_X_X: |
| dio_ReadAttribute(AT_subscr_data, FORM_REF); |
| Type = add_object_info((ContextAddress)dio_gFormData); |
| add_object_reference(Type, NULL); |
| break; |
| } |
| if (Type != NULL) { |
| ObjectInfo * Range = add_object_info((ContextAddress)(sDebugSection->addr + dio_GetPos())); |
| Range->mTag = TAG_index_range; |
| Range->mCompUnit = sCompUnit; |
| Range->mType = Type; |
| Range->u.mRange.mFmt = Fmt; |
| switch (Fmt) { |
| case FMT_FT_C_C: |
| case FMT_FT_C_X: |
| case FMT_UT_C_C: |
| case FMT_UT_C_X: |
| Range->u.mRange.mLow.mValue = read_long_value(); |
| break; |
| case FMT_FT_X_C: |
| case FMT_FT_X_X: |
| case FMT_UT_X_C: |
| case FMT_UT_X_X: |
| dio_ReadAttribute(0, FORM_BLOCK2); |
| Range->u.mRange.mLow.mExpr.mAddr = (U1_T *)dio_gFormDataAddr; |
| Range->u.mRange.mLow.mExpr.mSize = dio_gFormDataSize; |
| break; |
| } |
| switch (Fmt) { |
| case FMT_FT_C_C: |
| case FMT_FT_X_C: |
| case FMT_UT_C_C: |
| case FMT_UT_X_C: |
| Range->u.mRange.mHigh.mValue = read_long_value(); |
| break; |
| case FMT_FT_C_X: |
| case FMT_FT_X_X: |
| case FMT_UT_C_X: |
| case FMT_UT_X_X: |
| dio_ReadAttribute(0, FORM_BLOCK2); |
| Range->u.mRange.mHigh.mExpr.mAddr = (U1_T *)dio_gFormDataAddr; |
| Range->u.mRange.mHigh.mExpr.mSize = dio_gFormDataSize; |
| break; |
| } |
| *Children = Range; |
| Children = &Range->mSibling; |
| } |
| else if (Fmt == FMT_ET) { |
| U2_T x = dio_ReadU2(); |
| U2_T Attr = (x & 0xfff0u) >> 4; |
| U2_T Form = x & 0xfu; |
| dio_ReadAttribute(Attr, Form); |
| switch (Attr) { |
| case AT_fund_type: |
| dio_ChkData(Form); |
| Type = add_object_info((ContextAddress)(sDebugSection->addr + dio_GetPos() - dio_gFormDataSize)); |
| Type->mTag = TAG_fund_type; |
| Type->mCompUnit = sCompUnit; |
| Type->u.mFundType = (U2_T)dio_gFormData; |
| break; |
| case AT_user_def_type: |
| dio_ChkRef(Form); |
| Type = add_object_info((ContextAddress)dio_gFormData); |
| add_object_reference(Type, NULL); |
| break; |
| case AT_mod_fund_type: |
| read_mod_fund_type(Form, &Type); |
| break; |
| case AT_mod_u_d_type: |
| read_mod_user_def_type(Form, &Type); |
| break; |
| default: |
| str_exception(ERR_INV_DWARF, "Invalid array element type format"); |
| } |
| Array->mType = Type; |
| } |
| else { |
| str_exception(ERR_INV_DWARF, "Invalid array subscription format"); |
| } |
| } |
| dio_SetPos(OrgPos); |
| } |
| |
| static void read_object_info(U2_T Tag, U2_T Attr, U2_T Form) { |
| static ObjectInfo * Info; |
| static U8_T Sibling; |
| static int HasChildren; |
| static int Skip; |
| static int high_pc_offs; |
| |
| if (Skip && Attr && Attr != AT_sibling) return; |
| |
| switch (Attr) { |
| case 0: |
| if (Form) { |
| /* Initialization: executed before debug entry processing */ |
| high_pc_offs = 0; |
| if (Tag == TAG_compile_unit || Tag == TAG_partial_unit || Tag == TAG_type_unit) { |
| CompUnit * Unit = add_comp_unit((ContextAddress)(sDebugSection->addr + dio_gEntryPos)); |
| assert(sParentObject == NULL); |
| Info = Unit->mObject; |
| assert(Info->mTag == 0); |
| sCompUnit = Unit; |
| } |
| else { |
| Info = add_object_info((ContextAddress)(sDebugSection->addr + dio_gEntryPos)); |
| } |
| if (sParentObject) { |
| Info->mParent = sParentObject; |
| if (sParentObject->mFlags & DOIF_need_frame) { |
| /* Allow frame in get_symbol_container() */ |
| Info->mFlags |= DOIF_need_frame; |
| } |
| } |
| HasChildren = Form == DWARF_ENTRY_HAS_CHILDREN; |
| Sibling = 0; |
| Skip = Info->mTag != 0; |
| if (Skip) { |
| /* Object is already loaded */ |
| assert(Info->mTag == Tag); |
| assert(Tag != TAG_compile_unit); |
| assert(Tag != TAG_partial_unit); |
| assert(Tag != TAG_type_unit); |
| assert(Info->mCompUnit == sCompUnit); |
| return; |
| } |
| Info->mTag = Tag; |
| Info->mCompUnit = sCompUnit; |
| } |
| else { |
| /* Finalization: executed after debug entry processing */ |
| if (high_pc_offs && !(Info->mFlags & DOIF_ranges) && (Info->mFlags & DOIF_low_pc)) { |
| Info->u.mCode.mHighPC.mAddr += Info->u.mCode.mLowPC; |
| } |
| switch (Tag) { |
| case TAG_compile_unit: |
| case TAG_partial_unit: |
| case TAG_type_unit: |
| { |
| ObjectHashTable * HashTable = sCache->mObjectHashTable + sDebugSection->index; |
| assert(HashTable->mCompUnitsIndex == NULL); |
| if (Sibling == 0) Sibling = sUnitDesc.mUnitOffs + sUnitDesc.mUnitSize; |
| sCompUnit->mDesc = sUnitDesc; |
| HashTable->mCompUnitsIndexSize++; |
| if (Info->mFlags & DOIF_low_pc) sCompUnit->mTextSection = Info->u.mCode.mSection; |
| } |
| break; |
| case TAG_subprogram: |
| case TAG_global_subroutine: |
| case TAG_inlined_subroutine: |
| case TAG_subroutine_type: |
| case TAG_subroutine: |
| case TAG_entry_point: |
| case TAG_pointer_type: |
| case TAG_mod_pointer: |
| case TAG_const_type: |
| case TAG_volatile_type: |
| if (Info->mType == NULL) { |
| /* NULL here means "void" */ |
| Info->mType = add_object_info(OBJECT_ID_VOID(sCompUnit)); |
| if (Info->mType->mTag == 0) { |
| Info->mType->mTag = TAG_fund_type; |
| Info->mType->mCompUnit = sCompUnit; |
| Info->mType->u.mFundType = FT_void; |
| switch (Info->mCompUnit->mLanguage) { |
| case LANG_C: |
| case LANG_C89: |
| case LANG_C99: |
| case LANG_C_PLUS_PLUS: |
| Info->mType->mName = "void"; |
| break; |
| } |
| } |
| } |
| break; |
| case TAG_string_type: |
| if (Info->mType == NULL) { |
| /* NULL here means "char" */ |
| Info->mType = add_object_info(OBJECT_ID_CHAR(sCompUnit)); |
| if (Info->mType->mTag == 0) { |
| Info->mType->mTag = TAG_fund_type; |
| Info->mType->mCompUnit = sCompUnit; |
| Info->mType->u.mFundType = FT_char; |
| switch (Info->mCompUnit->mLanguage) { |
| case LANG_C: |
| case LANG_C89: |
| case LANG_C99: |
| case LANG_C_PLUS_PLUS: |
| Info->mType->mName = "char"; |
| break; |
| } |
| } |
| } |
| break; |
| } |
| if (sPrevSibling != NULL) sPrevSibling->mSibling = Info; |
| else if (sParentObject != NULL) sParentObject->mChildren = Info; |
| else if (Tag == TAG_compile_unit) sCache->mObjectHashTable[sDebugSection->index].mCompUnits = Info; |
| else if (Tag == TAG_partial_unit) sCache->mObjectHashTable[sDebugSection->index].mCompUnits = Info; |
| else if (Tag == TAG_type_unit) sCache->mObjectHashTable[sDebugSection->index].mCompUnits = Info; |
| sPrevSibling = Info; |
| if (Skip && Sibling != 0) { |
| dio_SetPos(Sibling); |
| return; |
| } |
| if (Tag == TAG_enumerator && Info->mType == NULL) Info->mType = sParentObject; |
| #if ENABLE_DWARF_LAZY_LOAD |
| if (sCache->mFile->lock_cnt == 0 && Sibling != 0 && sDebugSection->size >= 0x40000) { |
| switch (Tag) { |
| case TAG_union_type: |
| case TAG_array_type: |
| case TAG_class_type: |
| case TAG_structure_type: |
| case TAG_subroutine_type: |
| case TAG_global_subroutine: |
| case TAG_subroutine: |
| case TAG_subprogram: |
| sCache->lazy_loaded = 1; |
| dio_SetPos(Sibling); |
| return; |
| } |
| } |
| #endif |
| Info->mFlags |= DOIF_children_loaded; |
| if (Sibling != 0 || HasChildren) { |
| U8_T SiblingPos = Sibling; |
| ObjectInfo * Parent = sParentObject; |
| ObjectInfo * PrevSibling = sPrevSibling; |
| sParentObject = Info; |
| sPrevSibling = NULL; |
| for (;;) { |
| if (SiblingPos > 0 && dio_GetPos() >= SiblingPos) break; |
| if (!dio_ReadEntry(read_object_info, 0)) break; |
| } |
| if (SiblingPos > dio_GetPos()) dio_SetPos(SiblingPos); |
| sParentObject = Parent; |
| sPrevSibling = PrevSibling; |
| } |
| } |
| break; |
| case AT_sibling: |
| dio_ChkRef(Form); |
| Sibling = dio_gFormData - sDebugSection->addr; |
| break; |
| case AT_type: |
| if (Form == FORM_GNU_REF_ALT) { |
| Info->mType = find_alt_object_info((ContextAddress)dio_gFormData); |
| add_object_reference(Info->mType, NULL); |
| break; |
| } |
| if (Form == FORM_REF_SIG8) { |
| CompUnit * Unit = find_type_unit(dio_gFormData); |
| if (Unit != NULL) { |
| ContextAddress ID = (ContextAddress)(Unit->mDesc.mSection->addr + |
| Unit->mDesc.mUnitOffs + Unit->mDesc.mTypeOffset); |
| Info->mType = find_loaded_object(Unit->mDesc.mSection, ID); |
| } |
| if (Info->mType == NULL) str_exception(ERR_INV_DWARF, "Invalid type unit reference"); |
| add_object_reference(Info->mType, NULL); |
| break; |
| } |
| dio_ChkRef(Form); |
| Info->mType = add_object_info((ContextAddress)dio_gFormData); |
| add_object_reference(Info->mType, NULL); |
| break; |
| case AT_fund_type: |
| dio_ChkData(Form); |
| Info->mType = add_object_info((ContextAddress)(sDebugSection->addr + dio_GetPos() - dio_gFormDataSize)); |
| Info->mType->mTag = TAG_fund_type; |
| Info->mType->mCompUnit = sCompUnit; |
| Info->mType->u.mFundType = (U2_T)dio_gFormData; |
| break; |
| case AT_user_def_type: |
| dio_ChkRef(Form); |
| Info->mType = add_object_info((ContextAddress)dio_gFormData); |
| add_object_reference(Info->mType, NULL); |
| break; |
| case AT_mod_fund_type: |
| read_mod_fund_type(Form, &Info->mType); |
| break; |
| case AT_mod_u_d_type: |
| read_mod_user_def_type(Form, &Info->mType); |
| break; |
| case AT_encoding: |
| if (Tag == TAG_base_type) { |
| Info->u.mFundType = (U2_T)dio_gFormData; |
| } |
| break; |
| case AT_subscr_data: |
| read_subscr_data(Form, Info); |
| break; |
| case AT_name: |
| dio_ChkString(Form); |
| if (*(char *)dio_gFormDataAddr) Info->mName = (char *)dio_gFormDataAddr; |
| break; |
| case AT_specification_v2: |
| if (Form == FORM_GNU_REF_ALT) { |
| add_object_reference(find_alt_object_info((ContextAddress)dio_gFormData), Info); |
| } |
| else { |
| dio_ChkRef(Form); |
| add_object_reference(add_object_info((ContextAddress)dio_gFormData), Info); |
| } |
| Info->mFlags |= DOIF_specification; |
| break; |
| case AT_abstract_origin: |
| if (Form == FORM_GNU_REF_ALT) { |
| add_object_reference(find_alt_object_info((ContextAddress)dio_gFormData), Info); |
| } |
| else { |
| dio_ChkRef(Form); |
| add_object_reference(add_object_info((ContextAddress)dio_gFormData), Info); |
| } |
| Info->mFlags |= DOIF_abstract_origin; |
| break; |
| case AT_extension: |
| dio_ChkRef(Form); |
| add_object_reference(add_object_info((ContextAddress)dio_gFormData), Info); |
| Info->mFlags |= DOIF_extension; |
| break; |
| case AT_MIPS_linkage_name: |
| Info->mFlags |= DOIF_mips_linkage_name; |
| break; |
| case AT_linkage_name: |
| Info->mFlags |= DOIF_linkage_name; |
| break; |
| case AT_is_optional: |
| dio_ChkFlag(Form); |
| if (dio_gFormData) Info->mFlags |= DOIF_optional; |
| break; |
| case AT_low_pc: |
| dio_ChkAddr(Form); |
| Info->u.mCode.mLowPC = (ContextAddress)dio_gFormData; |
| if (dio_gFormSection) Info->u.mCode.mSection = dio_gFormSection; |
| Info->mFlags |= DOIF_low_pc; |
| break; |
| case AT_high_pc: |
| if (Info->mFlags & DOIF_ranges) break; |
| if (Form != FORM_ADDR) { |
| dio_ChkData(Form); |
| high_pc_offs = 1; |
| } |
| else if (dio_gFormSection) { |
| Info->u.mCode.mSection = dio_gFormSection; |
| } |
| Info->u.mCode.mHighPC.mAddr = (ContextAddress)dio_gFormData; |
| break; |
| case AT_ranges: |
| dio_ChkData(Form); |
| Info->u.mCode.mHighPC.mRanges = dio_gFormData; |
| Info->mFlags |= DOIF_ranges; |
| break; |
| case AT_external: |
| dio_ChkFlag(Form); |
| if (dio_gFormData) Info->mFlags |= DOIF_external; |
| break; |
| case AT_artificial: |
| dio_ChkFlag(Form); |
| if (dio_gFormData) Info->mFlags |= DOIF_artificial; |
| break; |
| case AT_declaration: |
| dio_ChkFlag(Form); |
| if (dio_gFormData) Info->mFlags |= DOIF_declaration; |
| break; |
| case AT_private: |
| if (Form == FORM_STRING) { |
| /* Diab 4.1 */ |
| Info->mFlags |= DOIF_private; |
| break; |
| } |
| dio_ChkFlag(Form); |
| if (dio_gFormData) Info->mFlags |= DOIF_private; |
| break; |
| case AT_protected: |
| if (Form == FORM_STRING) { |
| /* Diab 4.1 */ |
| Info->mFlags |= DOIF_protected; |
| break; |
| } |
| dio_ChkFlag(Form); |
| if (dio_gFormData) Info->mFlags |= DOIF_protected; |
| break; |
| case AT_public: |
| if (Form == FORM_STRING) { |
| /* Diab 4.1 */ |
| Info->mFlags |= DOIF_public; |
| break; |
| } |
| dio_ChkFlag(Form); |
| if (dio_gFormData) Info->mFlags |= DOIF_public; |
| break; |
| case AT_accessibility: |
| dio_ChkData(Form); |
| switch (dio_gFormData) { |
| case DW_ACCESS_private : Info->mFlags |= DOIF_private; break; |
| case DW_ACCESS_protected: Info->mFlags |= DOIF_protected; break; |
| case DW_ACCESS_public : Info->mFlags |= DOIF_public; break; |
| } |
| break; |
| case AT_location: |
| Info->mFlags |= DOIF_location; |
| if (Form == FORM_DATA4 || Form == FORM_DATA8 || Form == FORM_SEC_OFFSET || Tag == TAG_formal_parameter) { |
| Info->mFlags |= DOIF_need_frame; |
| } |
| break; |
| case AT_data_location: |
| if (sCompUnit->mDesc.mVersion <= 1) { |
| /* AT_mangled */ |
| Info->mFlags |= DOIF_mangled_name; |
| break; |
| } |
| Info->mFlags |= DOIF_data_location; |
| if (Form == FORM_DATA4 || Form == FORM_DATA8 || Form == FORM_SEC_OFFSET) { |
| Info->mFlags |= DOIF_need_frame; |
| } |
| break; |
| case AT_string_length: |
| case AT_return_addr: |
| case AT_data_member_location: |
| case AT_frame_base: |
| case AT_segment: |
| case AT_static_link: |
| case AT_use_location: |
| case AT_vtable_elem_location: |
| case AT_upper_bound: |
| case AT_lower_bound: |
| /* Note: FORM_DATA4, FORM_DATA8 and FORM_SEC_OFFSET are location lists. |
| * Location list needs PC, so we set DOIF_need_frame because of that */ |
| if (Form == FORM_DATA4 || Form == FORM_DATA8 || Form == FORM_SEC_OFFSET) { |
| Info->mFlags |= DOIF_need_frame; |
| } |
| break; |
| case AT_const_value: |
| Info->mFlags |= DOIF_const_value; |
| break; |
| } |
| |
| if (Tag == TAG_compile_unit || Tag == TAG_partial_unit || Tag == TAG_type_unit) { |
| CompUnit * Unit = Info->mCompUnit; |
| switch (Attr) { |
| case AT_comp_dir: |
| dio_ChkString(Form); |
| Unit->mDir = (char *)dio_gFormDataAddr; |
| break; |
| case AT_stmt_list: |
| if (Form == FORM_ADDR) { |
| /* Workaround: some compilers incorrectly use FORM_ADDR for AT_stmt_list */ |
| Unit->mLineInfoOffs = dio_gFormData; |
| if (dio_gFormSection != NULL) Unit->mLineInfoOffs -= dio_gFormSection->addr; |
| break; |
| } |
| dio_ChkData(Form); |
| Unit->mLineInfoOffs = dio_gFormData; |
| break; |
| case AT_base_types: |
| Unit->mBaseTypes = add_comp_unit((ContextAddress)dio_gFormData); |
| break; |
| case AT_language: |
| dio_ChkData(Form); |
| Unit->mLanguage = (U2_T)dio_gFormData; |
| break; |
| } |
| } |
| } |
| |
| static void read_object_refs(ELF_Section * Section) { |
| U4_T pass_cnt = 0; |
| #if ENABLE_DWARF_LAZY_LOAD |
| if (sCache->lazy_loaded) { |
| /* |
| * Build transitive closure of DWARF objects graph: |
| * load objects that are referenced by already loaded objects. |
| */ |
| U4_T pos = 0; |
| sCompUnit = NULL; |
| while (pos < sObjRefsCnt) { |
| ObjectReference ref = sObjRefs[pos++]; |
| if (ref.org->mCompUnit == NULL) ref.org->mCompUnit = find_comp_unit(Section, ref.org->mID); |
| if (ref.org->mTag == 0) { |
| Trap trap; |
| ObjectInfo * obj = ref.org; |
| sCompUnit = obj->mCompUnit; |
| sUnitDesc = sCompUnit->mDesc; |
| sDebugSection = sUnitDesc.mSection; |
| sParentObject = NULL; |
| sPrevSibling = NULL; |
| dio_EnterSection(&sCompUnit->mDesc, sDebugSection, obj->mID - sDebugSection->addr); |
| if (set_trap(&trap)) { |
| dio_ReadEntry(read_object_info, 0); |
| clear_trap(&trap); |
| } |
| dio_ExitSection(); |
| sDebugSection = NULL; |
| sCompUnit = NULL; |
| if (trap.error) exception(trap.error); |
| } |
| } |
| } |
| #endif |
| /* |
| * Propagate attributes like mName and mType along chain of references. |
| */ |
| for (;;) { |
| U4_T pos = 0; |
| int fwd = 0; |
| while (pos < sObjRefsCnt) { |
| ObjectReference ref = sObjRefs[pos++]; |
| if (ref.obj != NULL) { |
| assert(ref.org->mTag != 0); |
| if (ref.org->mFlags & DOIF_load_mark) { |
| fwd = 1; |
| } |
| else { |
| if (ref.obj->mName == NULL) ref.obj->mName = ref.org->mName; |
| if (ref.obj->mType == NULL || ref.obj->mType->mID == OBJECT_ID_VOID(ref.obj->mCompUnit)) ref.obj->mType = ref.org->mType; |
| ref.obj->mFlags |= ref.org->mFlags & ~(DOIF_children_loaded | DOIF_declaration | DOIF_specification); |
| if (ref.obj->mFlags & DOIF_specification) { |
| ref.org->mDefinition = ref.obj; |
| if ((ref.obj->mFlags & (DOIF_low_pc | DOIF_ranges | DOIF_location)) == 0) { |
| ref.obj->mFlags |= ref.org->mFlags & DOIF_declaration; |
| } |
| } |
| if (ref.obj->mFlags & DOIF_abstract_origin) { |
| if ((ref.obj->mTag == TAG_variable && (ref.obj->mFlags & DOIF_external)) || |
| ref.obj->mTag == TAG_subprogram || |
| (ref.obj->mTag == TAG_formal_parameter && ref.obj->mParent != NULL && ref.obj->mParent->mTag == TAG_subprogram)) |
| ref.org->mDefinition = ref.obj; |
| } |
| if (ref.obj->mFlags & DOIF_external) { |
| ObjectInfo * cls = ref.org; |
| while (cls->mParent != NULL && |
| (cls->mParent->mTag == TAG_class_type || cls->mParent->mTag == TAG_structure_type)) { |
| cls = cls->mParent; |
| } |
| cls->mFlags |= DOIF_external; |
| } |
| ref.obj->mFlags &= ~DOIF_load_mark; |
| ref.obj = NULL; |
| } |
| } |
| } |
| if (!fwd) break; |
| if (pass_cnt >= 8) break; |
| pass_cnt++; |
| } |
| sObjRefsCnt = 0; |
| } |
| |
| static int addr_ranges_comparator(const void * x, const void * y) { |
| UnitAddressRange * rx = (UnitAddressRange *)x; |
| UnitAddressRange * ry = (UnitAddressRange *)y; |
| if (rx->mAddr < ry->mAddr) return -1; |
| if (rx->mAddr > ry->mAddr) return +1; |
| if (rx->mSection < ry->mSection) return -1; |
| if (rx->mSection > ry->mSection) return +1; |
| if (rx->mUnit->mObject->mID < ry->mUnit->mObject->mID) return -1; |
| if (rx->mUnit->mObject->mID > ry->mUnit->mObject->mID) return +1; |
| return 0; |
| } |
| |
| static void add_addr_range(ELF_Section * sec, CompUnit * unit, ContextAddress addr, ContextAddress size) { |
| UnitAddressRange * range = NULL; |
| if (addr + size <= addr) { |
| if (size == 0) return; |
| size = 0 - addr; |
| } |
| if (size > sCache->mAddrRangesMaxSize) sCache->mAddrRangesMaxSize = size; |
| if (sCache->mAddrRangesCnt >= sCache->mAddrRangesMax) { |
| sCache->mAddrRangesMax = sCache->mAddrRangesMax == 0 ? 64 : sCache->mAddrRangesMax * 2; |
| sCache->mAddrRanges = (UnitAddressRange *)loc_realloc(sCache->mAddrRanges, sizeof(UnitAddressRange) * sCache->mAddrRangesMax); |
| } |
| range = sCache->mAddrRanges + sCache->mAddrRangesCnt++; |
| memset(range, 0, sizeof(UnitAddressRange)); |
| if (sec != NULL) { |
| assert(sec->file == sCache->mFile); |
| range->mSection = sec->index; |
| sCache->mAddrRangesRelocatable = 1; |
| } |
| range->mAddr = addr; |
| range->mSize = size; |
| range->mUnit = unit; |
| } |
| |
| static void add_object_addr_ranges(ObjectInfo * info) { |
| CompUnit * unit = info->mCompUnit; |
| ContextAddress base = info->u.mCode.mLowPC; |
| assert(info->mFlags & DOIF_low_pc); |
| if (info->mFlags & DOIF_ranges) { |
| if (sCache->mDebugRanges != NULL) { |
| dio_EnterSection(&unit->mDesc, sCache->mDebugRanges, info->u.mCode.mHighPC.mRanges); |
| for (;;) { |
| U8_T AddrMax = ~(U8_T)0; |
| ELF_Section * sec_x = NULL; |
| ELF_Section * sec_y = NULL; |
| U8_T x = dio_ReadAddress(&sec_x); |
| U8_T y = dio_ReadAddress(&sec_y); |
| if (x == 0 && y == 0) break; |
| if (unit->mDesc.mAddressSize < 8) AddrMax = ((U8_T)1 << unit->mDesc.mAddressSize * 8) - 1; |
| if (x == AddrMax) { |
| base = (ContextAddress)y; |
| } |
| else if (y > x) { |
| ELF_Section * sec = info->u.mCode.mSection; |
| if (sec == NULL) sec = unit->mTextSection; |
| x = base + x; |
| y = base + y; |
| if (sec_x != NULL) sec = sec_x; |
| else if (sec_y != NULL) sec = sec_y; |
| add_addr_range(sec, unit, (ContextAddress)x, (ContextAddress)(y - x)); |
| } |
| } |
| dio_ExitSection(); |
| } |
| } |
| else if (info->u.mCode.mHighPC.mAddr > base) { |
| ELF_Section * sec = info->u.mCode.mSection; |
| if (sec == NULL) sec = unit->mTextSection; |
| add_addr_range(sec, unit, base, info->u.mCode.mHighPC.mAddr - base); |
| } |
| } |
| |
| static void load_addr_ranges(ELF_Section * debug_info) { |
| Trap trap; |
| unsigned idx; |
| ELF_File * file = sCache->mFile; |
| |
| memset(&trap, 0, sizeof(trap)); |
| for (idx = 1; idx < file->section_cnt; idx++) { |
| ELF_Section * sec = file->sections + idx; |
| if (sec->size == 0) continue; |
| if (sec->name == NULL) continue; |
| if (strcmp(sec->name, ".debug_aranges") == 0) { |
| sCompUnit = NULL; |
| dio_EnterSection(NULL, sec, 0); |
| if (set_trap(&trap)) { |
| while (dio_GetPos() < sec->size) { |
| int dwarf64 = 0; |
| U8_T size = dio_ReadU4(); |
| U8_T next = 0; |
| if (size == 0xffffffffu) { |
| dwarf64 = 1; |
| size = dio_ReadU8(); |
| } |
| next = dio_GetPos() + size; |
| if (dio_ReadU2() == 2) { |
| ELF_Section * unit_sec = NULL; |
| U8_T unit_addr = dio_ReadAddressX(&unit_sec, dwarf64 ? 8 : 4); |
| U1_T addr_size = dio_ReadU1(); |
| U1_T segm_size = dio_ReadU1(); |
| if (segm_size != 0) str_exception(ERR_INV_DWARF, "segment descriptors are not supported"); |
| if (unit_sec == NULL) unit_sec = debug_info; |
| sCompUnit = find_comp_unit(unit_sec, (ContextAddress)unit_addr); |
| if (sCompUnit == NULL) str_exception(ERR_INV_DWARF, "invalid .debug_aranges section"); |
| sCompUnit->mObject->mFlags |= DOIF_aranges; |
| while (dio_GetPos() % (addr_size * 2) != 0) dio_Skip(1); |
| for (;;) { |
| ELF_Section * addr_sec = NULL; |
| ELF_Section * size_sec = NULL; |
| ContextAddress addr = (ContextAddress)dio_ReadAddressX(&addr_sec, addr_size); |
| ContextAddress size = (ContextAddress)dio_ReadAddressX(&size_sec, addr_size); |
| /* GCC 4.1.2 can generate 'empty entries' at the beginning of the list */ |
| if (addr == 0 && size == 0 && dio_GetPos() + addr_size * 2 > next) break; |
| if (size != 0) add_addr_range(addr_sec, sCompUnit, addr, size); |
| } |
| } |
| dio_SetPos(next); |
| } |
| clear_trap(&trap); |
| } |
| dio_ExitSection(); |
| if (trap.error) break; |
| } |
| } |
| if (trap.error) exception(trap.error); |
| for (idx = 1; idx < file->section_cnt; idx++) { |
| ObjectInfo * info = sCache->mObjectHashTable[idx].mCompUnits; |
| while (info != NULL) { |
| if (info->mFlags & DOIF_low_pc) add_object_addr_ranges(info); |
| |
| if ((info->mFlags & DOIF_aranges) == 0 && (info->mFlags & DOIF_ranges) == 0 && info->u.mCode.mHighPC.mAddr == 0) { |
| /* Unit does not have PC ranges data. As a workaround, add line info ranges */ |
| unsigned i; |
| CompUnit * unit = info->mCompUnit; |
| if (set_trap(&trap)) { |
| load_line_numbers(unit); |
| if (unit->mStatesCnt >= 2) { |
| for (i = 0; i < unit->mStatesCnt - 1; i++) { |
| LineNumbersState * x = unit->mStates + i; |
| LineNumbersState * y = unit->mStates + i + 1; |
| if (x->mSection != y->mSection) continue; |
| if (x->mAddress == y->mAddress) continue; |
| if (x->mFlags & LINE_EndSequence) continue; |
| add_addr_range(unit->mTextSection, unit, x->mAddress, y->mAddress - x->mAddress); |
| } |
| } |
| clear_trap(&trap); |
| } |
| } |
| |
| { |
| /* Workaround for GCC bug - certain ranges are missing in both ".debug_aranges" and the unit info. |
| * Add address ranges of the underlying scopes. */ |
| ObjectInfo * obj = info->mChildren; |
| assert(info->mFlags & DOIF_children_loaded); |
| while (obj != NULL) { |
| if (obj->mFlags & DOIF_low_pc) add_object_addr_ranges(obj); |
| obj = obj->mSibling; |
| } |
| } |
| |
| info = info->mSibling; |
| } |
| } |
| if (sCache->mAddrRangesCnt > 1) { |
| unsigned i, j = 0; |
| qsort(sCache->mAddrRanges, sCache->mAddrRangesCnt, sizeof(UnitAddressRange), addr_ranges_comparator); |
| for (i = 0; i < sCache->mAddrRangesCnt - 1; i++) { |
| UnitAddressRange * x = sCache->mAddrRanges + i; |
| UnitAddressRange * y = x + 1; |
| ContextAddress x_end = x->mAddr + x->mSize; |
| if (x->mSection == y->mSection && x->mUnit == y->mUnit && (x_end == 0 || x_end >= y->mAddr)) { |
| /* Skip duplicate entry */ |
| ContextAddress y_end = y->mAddr + y->mSize; |
| y->mAddr = x->mAddr; |
| if (x_end == 0 || (y_end != 0 && x_end > y_end)) { |
| y->mSize = x->mSize; |
| } |
| else { |
| y->mSize = y_end - x->mAddr; |
| } |
| if (y->mSize > sCache->mAddrRangesMaxSize) sCache->mAddrRangesMaxSize = y->mSize; |
| continue; |
| } |
| if (j < i) memcpy(sCache->mAddrRanges + j, x, sizeof(UnitAddressRange)); |
| j++; |
| } |
| /* Duplicate seen - adjust size and complete copy */ |
| if (j < sCache->mAddrRangesCnt - 1) { |
| UnitAddressRange * x = sCache->mAddrRanges + sCache->mAddrRangesCnt - 1; |
| memcpy(sCache->mAddrRanges + j++, x, sizeof(UnitAddressRange)); |
| sCache->mAddrRangesCnt = j; |
| } |
| } |
| } |
| |
| static int cmp_pub_objects(ObjectInfo * x, ObjectInfo * y) { |
| static const U4_T flags = |
| DOIF_declaration | |
| DOIF_external | |
| DOIF_artificial | |
| DOIF_specification | |
| DOIF_abstract_origin | |
| DOIF_extension | |
| DOIF_private | |
| DOIF_protected | |
| DOIF_public | |
| DOIF_ranges | |
| DOIF_low_pc | |
| DOIF_mips_linkage_name | |
| DOIF_linkage_name | |
| DOIF_mangled_name | |
| DOIF_optional | |
| DOIF_location | |
| DOIF_data_location | |
| DOIF_const_value; |
| |
| if ((x->mFlags & flags) != (y->mFlags & flags)) return 0; |
| switch (x->mTag) { |
| case TAG_base_type: |
| case TAG_fund_type: |
| if (x->u.mFundType != y->u.mFundType) return 0; |
| break; |
| } |
| if (strcmp(x->mName, y->mName) != 0) return 0; |
| return 1; |
| } |
| |
| static void add_pub_name(PubNamesTable * tbl, ObjectInfo * obj) { |
| PubNamesInfo * info = NULL; |
| unsigned h = calc_symbol_name_hash(obj->mName) % tbl->mHashSize; |
| obj->mFlags |= DOIF_pub_mark; |
| switch (obj->mTag) { |
| case TAG_base_type: |
| case TAG_typedef: |
| case TAG_class_type: |
| case TAG_structure_type: |
| case TAG_union_type: |
| case TAG_interface_type: |
| case TAG_enumeration_type: |
| case TAG_enumerator: |
| case TAG_variable: |
| { |
| /* Check for duplicates */ |
| unsigned n = tbl->mHash[h]; |
| while (n != 0) { |
| ObjectInfo * pub = tbl->mNext[n].mObject; |
| if (pub->mTag == obj->mTag && cmp_pub_objects(pub, obj)) return; |
| n = tbl->mNext[n].mNext; |
| } |
| } |
| } |
| if (tbl->mCnt >= tbl->mMax) { |
| tbl->mMax = tbl->mMax * 3 / 2; |
| tbl->mNext = (PubNamesInfo *)loc_realloc(tbl->mNext, sizeof(PubNamesInfo) * tbl->mMax); |
| } |
| info = tbl->mNext + tbl->mCnt; |
| info->mObject = obj; |
| info->mNext = tbl->mHash[h]; |
| tbl->mHash[h] = tbl->mCnt++; |
| } |
| |
| static void load_pub_names(ELF_Section * debug_info, ELF_Section * pub_names) { |
| PubNamesTable * tbl = &sCache->mPubNames; |
| dio_EnterSection(NULL, pub_names, 0); |
| while (dio_GetPos() < pub_names->size) { |
| int dwarf64 = 0; |
| U8_T size = dio_ReadU4(); |
| U8_T next = 0; |
| if (size == 0xffffffffu) { |
| dwarf64 = 1; |
| size = dio_ReadU8(); |
| } |
| next = dio_GetPos() + size; |
| if (dio_ReadU2() == 2) { |
| ELF_Section * unit_sect = NULL; |
| U8_T unit_addr = dio_ReadAddressX(&unit_sect, dwarf64 ? 8 : 4); |
| U8_T unit_offs = unit_sect == NULL ? unit_addr : unit_addr - unit_sect->addr; |
| U8_T unit_size = dwarf64 ? dio_ReadU8() : (U8_T)dio_ReadU4(); |
| if (unit_sect == NULL) unit_sect = debug_info; |
| if (unit_offs + unit_size > unit_sect->size) str_fmt_exception(ERR_INV_DWARF, |
| "Invalid unit size in %s section", pub_names->name); |
| for (;;) { |
| char * name = NULL; |
| ObjectInfo * info = NULL; |
| U8_T obj_offs = dwarf64 ? dio_ReadU8() : (U8_T)dio_ReadU4(); |
| if (obj_offs == 0) break; |
| if (obj_offs >= unit_size) str_fmt_exception(ERR_INV_DWARF, |
| "Invalid object offset in %s section", pub_names->name); |
| name = dio_ReadString(); |
| info = find_loaded_object(unit_sect, (ContextAddress)(unit_addr + obj_offs)); |
| if (info == NULL) continue; |
| if (info->mName == NULL) continue; |
| if (info->mFlags & DOIF_pub_mark) continue; |
| if (strcmp(info->mName, name) != 0) continue; |
| add_pub_name(tbl, info); |
| } |
| } |
| assert(next >= dio_GetPos()); |
| dio_SetPos(next); |
| } |
| dio_ExitSection(); |
| } |
| |
| static void add_namespace(PubNamesTable * tbl, ObjectInfo * ns) { |
| ObjectInfo * obj = get_dwarf_children(ns); |
| while (obj != NULL) { |
| if ((obj->mFlags & DOIF_pub_mark) == 0 && obj->mDefinition == NULL && obj->mName != NULL) { |
| add_pub_name(tbl, obj); |
| } |
| if (obj->mTag == TAG_enumeration_type) { |
| ObjectInfo * n = get_dwarf_children(obj); |
| while (n != NULL) { |
| if ((n->mFlags & DOIF_pub_mark) == 0 && n->mName != NULL) { |
| add_pub_name(tbl, n); |
| } |
| n = n->mSibling; |
| } |
| } |
| if (obj->mTag == TAG_namespace) { |
| add_namespace(tbl, obj); |
| } |
| obj = obj->mSibling; |
| } |
| } |
| |
| static void create_pub_names(unsigned idx) { |
| ObjectInfo * unit = sCache->mObjectHashTable[idx].mCompUnits; |
| PubNamesTable * tbl = &sCache->mPubNames; |
| while (unit != NULL) { |
| add_namespace(tbl, unit); |
| if ((unit->mFlags & DOIF_pub_mark) == 0 && unit->mName != NULL) { |
| add_pub_name(tbl, unit); |
| } |
| unit = unit->mSibling; |
| } |
| } |
| |
| static void allocate_obj_hash(ELF_Section * sec) { |
| ObjectHashTable * HashTable = sCache->mObjectHashTable + sec->index; |
| assert(HashTable->mObjectHash == NULL); |
| HashTable->mObjectHashSize = (unsigned)(sec->size / 53); |
| if (HashTable->mObjectHashSize < 251) HashTable->mObjectHashSize = 251; |
| HashTable->mObjectHash = (ObjectInfo **)loc_alloc_zero(sizeof(ObjectInfo *) * HashTable->mObjectHashSize); |
| } |
| |
| static int unit_id_comparator(const void * x1, const void * x2) { |
| ObjectInfo * u1 = (*(CompUnit **)x1)->mObject; |
| ObjectInfo * u2 = (*(CompUnit **)x2)->mObject; |
| if (u1->mID < u2->mID) return -1; |
| if (u1->mID > u2->mID) return +1; |
| return 0; |
| } |
| |
| static void load_debug_info_section(ELF_Section * sec) { |
| ObjectHashTable * HashTable = sCache->mObjectHashTable + sec->index; |
| Trap trap; |
| |
| sObjRefsCnt = 0; |
| sDebugSection = sec; |
| sParentObject = NULL; |
| sPrevSibling = NULL; |
| allocate_obj_hash(sec); |
| dio_EnterSection(NULL, sec, 0); |
| if (set_trap(&trap)) { |
| assert(HashTable->mCompUnitsIndexSize == 0); |
| assert(HashTable->mCompUnitsIndex == NULL); |
| if (strcmp(sec->name, ".debug_types") == 0) { |
| while (dio_GetPos() < sec->size) { |
| dio_ReadUnit(&sUnitDesc, read_type_unit_header); |
| } |
| dio_SetPos(0); |
| } |
| while (dio_GetPos() < sec->size) { |
| dio_ReadUnit(&sUnitDesc, read_object_info); |
| } |
| clear_trap(&trap); |
| } |
| dio_ExitSection(); |
| assert(sDebugSection == sec); |
| sDebugSection = NULL; |
| sParentObject = NULL; |
| sPrevSibling = NULL; |
| sCompUnit = NULL; |
| if (HashTable->mCompUnitsIndexSize > 0) { |
| unsigned i = 0; |
| ObjectInfo * unit = sCache->mObjectHashTable[sec->index].mCompUnits; |
| HashTable->mCompUnitsIndex = (CompUnit **)loc_alloc(sizeof(CompUnit *) * HashTable->mCompUnitsIndexSize); |
| while (unit != NULL) { |
| assert(unit->mTag == TAG_compile_unit || unit->mTag == TAG_partial_unit || unit->mTag == TAG_type_unit); |
| HashTable->mCompUnitsIndex[i++] = unit->mCompUnit; |
| unit = unit->mSibling; |
| } |
| assert(HashTable->mCompUnitsIndexSize == i); |
| qsort(HashTable->mCompUnitsIndex, HashTable->mCompUnitsIndexSize, sizeof(CompUnit *), unit_id_comparator); |
| } |
| if (trap.error) exception(trap.error); |
| read_object_refs(sec); |
| } |
| |
| static void load_debug_sections(void) { |
| unsigned idx; |
| ELF_Section * debug_info = NULL; |
| FrameInfoIndex * frame_info_d = NULL; |
| FrameInfoIndex * frame_info_e = NULL; |
| ELF_File * file = sCache->mFile; |
| U8_T debug_types_size = 0; |
| |
| for (idx = 1; idx < file->section_cnt; idx++) { |
| ELF_Section * sec = file->sections + idx; |
| if (sec->size == 0) continue; |
| if (sec->name == NULL) continue; |
| if (sec->type == SHT_NOBITS) continue; |
| if (strcmp(sec->name, ".debug_types") == 0) { |
| debug_types_size += sec->size; |
| } |
| } |
| |
| if (debug_types_size > 0) { |
| sCache->mTypeUnitHashSize = (unsigned)(debug_types_size / 101); |
| if (sCache->mTypeUnitHashSize < 239) sCache->mTypeUnitHashSize = 239; |
| sCache->mTypeUnitHash = (CompUnit **)loc_alloc_zero(sizeof(CompUnit *) * sCache->mTypeUnitHashSize); |
| } |
| |
| for (idx = 1; idx < file->section_cnt; idx++) { |
| ELF_Section * sec = file->sections + idx; |
| if (sec->size == 0) continue; |
| if (sec->name == NULL) continue; |
| if (sec->type == SHT_NOBITS) continue; |
| if (strcmp(sec->name, ".debug_types") == 0) { |
| load_debug_info_section(sec); |
| } |
| } |
| |
| for (idx = 1; idx < file->section_cnt; idx++) { |
| ELF_Section * sec = file->sections + idx; |
| if (sec->size == 0) continue; |
| if (sec->name == NULL) continue; |
| if (sec->type == SHT_NOBITS) continue; |
| if (strcmp(sec->name, ".debug_info") == 0) { |
| load_debug_info_section(sec); |
| if (debug_info == NULL) debug_info = sec; |
| } |
| } |
| |
| for (idx = 1; idx < file->section_cnt; idx++) { |
| ELF_Section * sec = file->sections + idx; |
| if (sec->size == 0) continue; |
| if (sec->name == NULL) continue; |
| if (sec->type == SHT_NOBITS) continue; |
| if (strcmp(sec->name, ".debug") == 0) { |
| load_debug_info_section(sec); |
| if (debug_info == NULL) debug_info = sec; |
| } |
| } |
| |
| for (idx = 1; idx < file->section_cnt; idx++) { |
| ELF_Section * sec = file->sections + idx; |
| if (sec->size == 0) continue; |
| if (sec->name == NULL) continue; |
| if (sec->type == SHT_NOBITS) continue; |
| if (strcmp(sec->name, ".line") == 0) { |
| sCache->mDebugLineV1 = sec; |
| } |
| else if (strcmp(sec->name, ".debug_line") == 0) { |
| sCache->mDebugLineV2 = sec; |
| } |
| else if (strcmp(sec->name, ".debug_loc") == 0) { |
| sCache->mDebugLoc = sec; |
| } |
| else if (strcmp(sec->name, ".debug_ranges") == 0) { |
| sCache->mDebugRanges = sec; |
| } |
| else if (strcmp(sec->name, ".debug_frame") == 0) { |
| FrameInfoIndex * idx = (FrameInfoIndex *)loc_alloc_zero(sizeof(FrameInfoIndex)); |
| idx->mSection = sec; |
| idx->mNext = frame_info_d; |
| frame_info_d = idx; |
| } |
| else if (strcmp(sec->name, ".eh_frame") == 0) { |
| FrameInfoIndex * idx = (FrameInfoIndex *)loc_alloc_zero(sizeof(FrameInfoIndex)); |
| idx->mSection = sec; |
| idx->mNext = frame_info_e; |
| frame_info_e = idx; |
| } |
| } |
| |
| while (frame_info_e != NULL) { |
| FrameInfoIndex * idx = frame_info_e; |
| frame_info_e = idx->mNext; |
| idx->mNext = sCache->mFrameInfo; |
| sCache->mFrameInfo = idx; |
| } |
| while (frame_info_d != NULL) { |
| FrameInfoIndex * idx = frame_info_d; |
| frame_info_d = idx->mNext; |
| idx->mNext = sCache->mFrameInfo; |
| sCache->mFrameInfo = idx; |
| } |
| |
| if (debug_info != NULL) { |
| Trap trap; |
| PubNamesTable * tbl = &sCache->mPubNames; |
| tbl->mHashSize = tbl->mMax = (unsigned)(debug_info->size / 151) + 16; |
| tbl->mHash = (unsigned *)loc_alloc_zero(sizeof(unsigned) * tbl->mHashSize); |
| tbl->mNext = (PubNamesInfo *)loc_alloc(sizeof(PubNamesInfo) * tbl->mMax); |
| memset(tbl->mNext + tbl->mCnt++, 0, sizeof(PubNamesInfo)); |
| if (set_trap(&trap)) { |
| for (idx = 1; idx < file->section_cnt; idx++) { |
| ELF_Section * sec = file->sections + idx; |
| if (sec->size == 0) continue; |
| if (sec->name == NULL) continue; |
| if (sec->type == SHT_NOBITS) continue; |
| if (strcmp(sec->name, ".debug_pubnames") == 0 || strcmp(sec->name, ".debug_pubtypes") == 0) { |
| load_pub_names(debug_info, sec); |
| } |
| } |
| clear_trap(&trap); |
| } |
| else { |
| trace(LOG_ELF, "Ignoring broken public names sections: %s.", errno_to_str(errno)); |
| memset(tbl->mHash, 0, sizeof(unsigned) * tbl->mHashSize); |
| tbl->mCnt = 1; |
| } |
| for (idx = 1; idx < file->section_cnt; idx++) { |
| create_pub_names(idx); |
| } |
| load_addr_ranges(debug_info); |
| } |
| } |
| |
| #if ENABLE_DWARF_LAZY_LOAD |
| ObjectInfo * get_dwarf_children(ObjectInfo * obj) { |
| Trap trap; |
| if (obj->mFlags & DOIF_children_loaded) return obj->mChildren; |
| sObjRefsCnt = 0; |
| sCompUnit = obj->mCompUnit; |
| sUnitDesc = sCompUnit->mDesc; |
| sDebugSection = sCompUnit->mDesc.mSection; |
| sCache = (DWARFCache *)sCompUnit->mFile->dwarf_dt_cache; |
| dio_EnterSection(&sCompUnit->mDesc, sDebugSection, obj->mID - sDebugSection->addr); |
| if (set_trap(&trap)) { |
| U8_T end_pos = sCompUnit->mDesc.mUnitOffs + sCompUnit->mDesc.mUnitSize; |
| if (obj->mSibling != NULL) end_pos = obj->mSibling->mID - sDebugSection->addr; |
| dio_ReadEntry(NULL, (U2_T)0xffffu); |
| sParentObject = obj; |
| sPrevSibling = NULL; |
| while (dio_GetPos() < end_pos) { |
| if (!dio_ReadEntry(read_object_info, 0)) break; |
| } |
| obj->mFlags |= DOIF_children_loaded; |
| clear_trap(&trap); |
| } |
| else { |
| /* TODO: dispose obj->mChildren */ |
| obj->mChildren = NULL; |
| } |
| dio_ExitSection(); |
| sDebugSection = NULL; |
| sParentObject = NULL; |
| sPrevSibling = NULL; |
| sCompUnit = NULL; |
| if (trap.error) exception(trap.error); |
| read_object_refs(obj->mCompUnit->mDesc.mSection); |
| assert(obj->mFlags & DOIF_children_loaded); |
| return obj->mChildren; |
| } |
| |
| ObjectInfo * get_dwarf_parent(ObjectInfo * obj) { |
| ObjectInfo * x; |
| if (obj->mParent != NULL) return obj->mParent; |
| if (obj->mTag == TAG_compile_unit) return NULL; |
| if (obj->mTag == TAG_partial_unit) return NULL; |
| if (obj->mTag == TAG_type_unit) return NULL; |
| x = get_dwarf_children(obj->mCompUnit->mObject); |
| while (x != NULL && x->mID < obj->mID) { |
| if (x->mSibling == NULL || x->mSibling->mID > obj->mID) { |
| x = get_dwarf_children(x); |
| } |
| else { |
| x = x->mSibling; |
| } |
| } |
| return obj->mParent; |
| } |
| #endif |
| |
| static U2_T gop_gAttr = 0; |
| static U2_T gop_gForm = 0; |
| static U8_T gop_gFormData = 0; |
| static size_t gop_gFormDataSize = 0; |
| static void * gop_gFormDataAddr = NULL; |
| static ELF_Section * gop_gFormSection = NULL; |
| static U8_T gop_gSpecification = 0; |
| static U8_T gop_gAbstractOrigin = 0; |
| static U8_T gop_gExtension = 0; |
| static U2_T gop_gSpecificationForm = 0; |
| static U2_T gop_gAbstractOriginForm = 0; |
| static U2_T gop_gExtensionForm = 0; |
| |
| static void get_object_property_callback(U2_T Tag, U2_T Attr, U2_T Form) { |
| if (Attr == AT_specification_v2) { |
| gop_gSpecification = dio_gFormData; |
| gop_gSpecificationForm = Form; |
| } |
| if (Attr == AT_abstract_origin) { |
| gop_gAbstractOrigin = dio_gFormData; |
| gop_gAbstractOriginForm = Form; |
| } |
| if (Attr == AT_extension) { |
| gop_gExtension = dio_gFormData; |
| gop_gExtensionForm = Form; |
| } |
| if (Attr != gop_gAttr) return; |
| gop_gForm = Form; |
| gop_gFormData = dio_gFormData; |
| gop_gFormDataSize = dio_gFormDataSize; |
| gop_gFormDataAddr = dio_gFormDataAddr; |
| gop_gFormSection = dio_gFormSection; |
| } |
| |
| U8_T get_numeric_property_value(PropertyValue * Value) { |
| U8_T Res = 0; |
| |
| if (Value->mPieces != NULL) { |
| str_exception(ERR_INV_DWARF, "Constant DWARF attribute value expected"); |
| } |
| else if (Value->mAddr != NULL) { |
| size_t i; |
| if (Value->mSize > 8) str_exception(ERR_INV_DWARF, "Invalid size of DWARF attribute value"); |
| for (i = 0; i < Value->mSize; i++) { |
| Res = (Res << 8) | Value->mAddr[Value->mBigEndian ? i : Value->mSize - i - 1]; |
| } |
| } |
| else { |
| Res = Value->mValue; |
| } |
| return Res; |
| } |
| |
| void read_dwarf_object_property(Context * Ctx, int Frame, ObjectInfo * Obj, U2_T Attr, PropertyValue * Value) { |
| |
| memset(Value, 0, sizeof(PropertyValue)); |
| Value->mContext = Ctx; |
| Value->mFrame = Frame; |
| Value->mObject = Obj; |
| Value->mAttr = Attr; |
| Value->mBigEndian = Obj->mCompUnit->mFile->big_endian; |
| |
| if (Obj->mTag >= TAG_fund_type && Obj->mTag < TAG_fund_type + 0x100) { |
| /* Virtual DWARF object that is created by the DWARF reader. It has no properties. */ |
| if (Obj->mTag == TAG_fund_type) { |
| if (Attr == AT_byte_size) { |
| Value->mValue = get_fund_type_size(Obj->mCompUnit, Obj->u.mFundType); |
| return; |
| } |
| } |
| else if (Obj->mTag == TAG_index_range) { |
| if (Attr == AT_lower_bound) { |
| switch (Obj->u.mRange.mFmt) { |
| case FMT_FT_C_C: |
| case FMT_FT_C_X: |
| case FMT_UT_C_C: |
| case FMT_UT_C_X: |
| Value->mValue = Obj->u.mRange.mLow.mValue; |
| return; |
| case FMT_FT_X_C: |
| case FMT_FT_X_X: |
| case FMT_UT_X_C: |
| case FMT_UT_X_X: |
| Value->mForm = FORM_BLOCK2; |
| Value->mAddr = Obj->u.mRange.mLow.mExpr.mAddr; |
| Value->mSize = Obj->u.mRange.mLow.mExpr.mSize; |
| return; |
| } |
| } |
| if (Attr == AT_upper_bound) { |
| switch (Obj->u.mRange.mFmt) { |
| case FMT_FT_C_C: |
| case FMT_FT_X_C: |
| case FMT_UT_C_C: |
| case FMT_UT_X_C: |
| Value->mValue = Obj->u.mRange.mHigh.mValue; |
| return; |
| case FMT_FT_C_X: |
| case FMT_FT_X_X: |
| case FMT_UT_C_X: |
| case FMT_UT_X_X: |
| Value->mForm = FORM_BLOCK2; |
| Value->mAddr = Obj->u.mRange.mHigh.mExpr.mAddr; |
| Value->mSize = Obj->u.mRange.mHigh.mExpr.mSize; |
| return; |
| } |
| } |
| } |
| else if (Obj->mTag == TAG_mod_pointer || Obj->mTag == TAG_mod_reference) { |
| if (Attr == AT_byte_size) { |
| Value->mValue = Obj->mCompUnit->mDesc.mAddressSize; |
| return; |
| } |
| } |
| exception(ERR_SYM_NOT_FOUND); |
| } |
| |
| sCompUnit = Obj->mCompUnit; |
| sUnitDesc = sCompUnit->mDesc; |
| sDebugSection = sCompUnit->mDesc.mSection; |
| sCache = (DWARFCache *)sCompUnit->mFile->dwarf_dt_cache; |
| dio_EnterSection(&sUnitDesc, sDebugSection, Obj->mID - sDebugSection->addr); |
| for (;;) { |
| if (sUnitDesc.mVersion == 1 && Attr == AT_data_member_location) { |
| gop_gAttr = AT_location; |
| } |
| else { |
| gop_gAttr = Attr; |
| } |
| gop_gForm = 0; |
| gop_gSpecification = 0; |
| gop_gAbstractOrigin = 0; |
| gop_gExtension = 0; |
| gop_gSpecificationForm = 0; |
| gop_gAbstractOriginForm = 0; |
| gop_gExtensionForm = 0; |
| dio_ReadEntry(get_object_property_callback, gop_gAttr); |
| dio_ExitSection(); |
| if (gop_gForm != 0) break; |
| if (gop_gSpecificationForm == FORM_GNU_REF_ALT) { |
| ObjectInfo * Ref = find_alt_object_info((ContextAddress)gop_gSpecification); |
| sCompUnit = Ref->mCompUnit; |
| sUnitDesc = sCompUnit->mDesc; |
| sDebugSection = sCompUnit->mDesc.mSection; |
| dio_EnterSection(&sUnitDesc, sDebugSection, Ref->mID - sDebugSection->addr); |
| } |
| else if (gop_gSpecification != 0) { |
| ObjectInfo * Ref = NULL; |
| dio_ChkRef(gop_gSpecificationForm); |
| Ref = find_object(sDebugSection, (ContextAddress)gop_gSpecification); |
| sCompUnit = Ref->mCompUnit; |
| sUnitDesc = sCompUnit->mDesc; |
| if (sDebugSection != sCompUnit->mDesc.mSection) str_exception(ERR_INV_DWARF, "Invalid AT_specification attribute"); |
| dio_EnterSection(&sUnitDesc, sDebugSection, Ref->mID - sDebugSection->addr); |
| } |
| else if (gop_gAbstractOriginForm == FORM_GNU_REF_ALT) { |
| ObjectInfo * Ref = find_alt_object_info((ContextAddress)gop_gAbstractOrigin); |
| sCompUnit = Ref->mCompUnit; |
| sUnitDesc = sCompUnit->mDesc; |
| sDebugSection = sCompUnit->mDesc.mSection; |
| dio_EnterSection(&sUnitDesc, sDebugSection, Ref->mID - sDebugSection->addr); |
| } |
| else if (gop_gAbstractOrigin != 0) { |
| ObjectInfo * Ref = NULL; |
| dio_ChkRef(gop_gAbstractOriginForm); |
| Ref = find_object(sDebugSection, (ContextAddress)gop_gAbstractOrigin); |
| sCompUnit = Ref->mCompUnit; |
| sUnitDesc = sCompUnit->mDesc; |
| if (sDebugSection != sCompUnit->mDesc.mSection) str_exception(ERR_INV_DWARF, "Invalid AT_abstract_origin attribute"); |
| dio_EnterSection(&sUnitDesc, sDebugSection, Ref->mID - sDebugSection->addr); |
| } |
| else if (gop_gExtension != 0) { |
| dio_ChkRef(gop_gExtensionForm); |
| dio_EnterSection(&sUnitDesc, sDebugSection, gop_gExtension - sDebugSection->addr); |
| } |
| else break; |
| } |
| |
| switch (Value->mForm = gop_gForm) { |
| case FORM_REF : |
| case FORM_REF_ADDR : |
| case FORM_REF1 : |
| case FORM_REF2 : |
| case FORM_REF4 : |
| case FORM_REF8 : |
| case FORM_REF_UDATA : |
| Value->mSection = dio_gFormSection; |
| Value->mValue = gop_gFormData; |
| break; |
| case FORM_DATA1 : |
| case FORM_DATA2 : |
| case FORM_DATA4 : |
| case FORM_DATA8 : |
| case FORM_FLAG : |
| case FORM_BLOCK1 : |
| case FORM_BLOCK2 : |
| case FORM_BLOCK4 : |
| case FORM_BLOCK : |
| case FORM_STRP : |
| case FORM_SEC_OFFSET: |
| case FORM_EXPRLOC : |
| case FORM_REF_SIG8 : |
| case FORM_STRING : |
| case FORM_GNU_STRP_ALT: |
| Value->mAddr = (U1_T *)gop_gFormDataAddr; |
| Value->mSize = gop_gFormDataSize; |
| break; |
| case FORM_SDATA : |
| case FORM_UDATA : |
| Value->mValue = gop_gFormData; |
| break; |
| case FORM_ADDR : |
| Value->mSection = dio_gFormSection; |
| Value->mValue = elf_map_to_run_time_address(Ctx, Obj->mCompUnit->mFile, gop_gFormSection, (ContextAddress)gop_gFormData); |
| if (errno) str_exception(errno, "Cannot get object run-time address"); |
| break; |
| default: |
| if (Attr == AT_data_member_location && Obj->mTag == TAG_member && Obj->mParent->mTag == TAG_union_type) { |
| Value->mForm = FORM_UDATA; |
| Value->mValue = 0; |
| break; |
| } |
| if (Attr == AT_byte_size) { |
| if (Obj->mTag == TAG_pointer_type || Obj->mTag == TAG_reference_type || Obj->mTag == TAG_rvalue_reference_type || |
| Obj->mTag == TAG_mod_pointer || Obj->mTag == TAG_mod_reference) { |
| Value->mForm = FORM_UDATA; |
| Value->mValue = sCompUnit->mDesc.mAddressSize; |
| break; |
| } |
| if (Obj->mTag == TAG_ptr_to_member_type) { |
| Value->mForm = FORM_UDATA; |
| Value->mValue = sCompUnit->mDesc.mAddressSize; |
| break; |
| } |
| if (Obj->mTag == TAG_structure_type || Obj->mTag == TAG_class_type || Obj->mTag == TAG_union_type) { |
| /* It is OK to return size 0 if the structure has no data members */ |
| int ok = 1; |
| ObjectInfo * c = get_dwarf_children(Obj); |
| while (ok && c != NULL) { |
| ObjectInfo * d = c; |
| while (d->mTag == TAG_imported_declaration) { |
| PropertyValue v; |
| read_and_evaluate_dwarf_object_property(Ctx, Frame, d, AT_import, &v); |
| d = find_object(Obj->mCompUnit->mDesc.mSection, (ContextAddress)get_numeric_property_value(&v)); |
| if (d == NULL) break; |
| } |
| if (d == NULL) { |
| ok = 0; |
| } |
| else { |
| switch (d->mTag) { |
| case TAG_typedef: |
| case TAG_subprogram: |
| case TAG_template_type_param: |
| case TAG_class_type: |
| case TAG_structure_type: |
| case TAG_union_type: |
| case TAG_enumeration_type: |
| break; |
| case TAG_member: |
| if (d->mFlags & DOIF_external) break; |
| ok = 0; |
| break; |
| default: |
| ok = 0; |
| } |
| } |
| c = c->mSibling; |
| } |
| if (ok) { |
| Value->mForm = FORM_UDATA; |
| Value->mAddr = 0; |
| Value->mValue = 0; |
| break; |
| } |
| } |
| if (Obj->mTag == TAG_label) { |
| /* By definition, label size is 0 */ |
| Value->mForm = FORM_UDATA; |
| Value->mValue = 0; |
| break; |
| } |
| } |
| exception(ERR_SYM_NOT_FOUND); |
| } |
| |
| sCache = NULL; |
| sCompUnit = NULL; |
| sDebugSection = NULL; |
| } |
| |
| void read_and_evaluate_dwarf_object_property_with_args( |
| Context * Ctx, int Frame, ObjectInfo * Obj, U2_T Attr, |
| uint64_t * Args, unsigned ArgsCnt, PropertyValue * Value) { |
| read_dwarf_object_property(Ctx, Frame, Obj, Attr, Value); |
| assert(Value->mContext == Ctx); |
| assert(Value->mFrame == Frame); |
| assert(Value->mObject == Obj); |
| assert(Value->mAttr == Attr); |
| if (Value->mForm == FORM_EXPRLOC) { |
| dwarf_evaluate_expression(Value, Args, ArgsCnt); |
| } |
| else if (Attr == AT_data_member_location) { |
| switch (Value->mForm) { |
| case FORM_DATA1 : |
| case FORM_DATA2 : |
| case FORM_DATA4 : |
| case FORM_DATA8 : |
| case FORM_SDATA : |
| case FORM_UDATA : |
| if (ArgsCnt == 0) exception(ERR_INV_CONT_OBJ); |
| Value->mValue = Args[0] + get_numeric_property_value(Value); |
| Value->mForm = FORM_UDATA; |
| Value->mAddr = NULL; |
| Value->mSize = 0; |
| break; |
| case FORM_BLOCK1 : |
| case FORM_BLOCK2 : |
| case FORM_BLOCK4 : |
| case FORM_BLOCK : |
| dwarf_evaluate_expression(Value, Args, ArgsCnt); |
| break; |
| } |
| } |
| else if (Attr == AT_location || Attr == AT_string_length || Attr == AT_frame_base || Attr == AT_use_location) { |
| switch (Value->mForm) { |
| case FORM_DATA4 : |
| case FORM_DATA8 : |
| case FORM_BLOCK1 : |
| case FORM_BLOCK2 : |
| case FORM_BLOCK4 : |
| case FORM_BLOCK : |
| case FORM_SEC_OFFSET: |
| dwarf_evaluate_expression(Value, Args, ArgsCnt); |
| break; |
| } |
| } |
| else if (Attr == AT_count || Attr == AT_byte_size || Attr == AT_lower_bound || Attr == AT_upper_bound) { |
| switch (Value->mForm) { |
| case FORM_BLOCK1 : |
| case FORM_BLOCK2 : |
| case FORM_BLOCK4 : |
| case FORM_BLOCK : |
| dwarf_evaluate_expression(Value, Args, ArgsCnt); |
| break; |
| } |
| } |
| } |
| |
| static void free_unit_cache(CompUnit * Unit) { |
| Unit->mFilesCnt = 0; |
| Unit->mFilesMax = 0; |
| loc_free(Unit->mFiles); |
| Unit->mFiles = NULL; |
| |
| Unit->mDirsCnt = 0; |
| Unit->mDirsMax = 0; |
| loc_free(Unit->mDirs); |
| Unit->mDirs = NULL; |
| |
| while (Unit->mStatesCnt > 0) { |
| loc_free(Unit->mStates[--Unit->mStatesCnt].mFileName); |
| } |
| loc_free(Unit->mStates); |
| loc_free(Unit->mStatesIndex); |
| Unit->mStates = NULL; |
| Unit->mStatesMax = 0; |
| Unit->mStatesIndex = NULL; |
| } |
| |
| static void free_dwarf_cache(ELF_File * file) { |
| DWARFCache * Cache = (DWARFCache *)file->dwarf_dt_cache; |
| if (Cache != NULL) { |
| unsigned i; |
| assert(Cache->magic == DWARF_CACHE_MAGIC); |
| Cache->magic = 0; |
| for (i = 0; i < file->section_cnt; i++) { |
| ObjectHashTable * Table = Cache->mObjectHashTable + i; |
| while (Table->mCompUnits != NULL) { |
| CompUnit * Unit = Table->mCompUnits->mCompUnit; |
| Table->mCompUnits = Table->mCompUnits->mSibling; |
| free_unit_cache(Unit); |
| loc_free(Unit); |
| } |
| loc_free(Table->mObjectHash); |
| loc_free(Table->mCompUnitsIndex); |
| } |
| while (Cache->mObjectList != NULL) { |
| ObjectArray * Buf = Cache->mObjectList; |
| Cache->mObjectList = Buf->mNext; |
| loc_free(Buf); |
| } |
| while (Cache->mFrameInfo != NULL) { |
| FrameInfoIndex * idx = Cache->mFrameInfo; |
| Cache->mFrameInfo = idx->mNext; |
| loc_free(idx->mFrameInfoRanges); |
| loc_free(idx); |
| } |
| loc_free(Cache->mObjectHashTable); |
| loc_free(Cache->mAddrRanges); |
| loc_free(Cache->mPubNames.mHash); |
| loc_free(Cache->mPubNames.mNext); |
| loc_free(Cache->mFileInfoHash); |
| loc_free(Cache->mTypeUnitHash); |
| loc_free(Cache); |
| file->dwarf_dt_cache = NULL; |
| } |
| } |
| |
| DWARFCache * get_dwarf_cache(ELF_File * file) { |
| DWARFCache * Cache = (DWARFCache *)file->dwarf_dt_cache; |
| if (Cache == NULL) { |
| Trap trap; |
| if (!sCloseListenerOK) { |
| elf_add_close_listener(free_dwarf_cache); |
| sCloseListenerOK = 1; |
| } |
| if (file->dwz_file_name != NULL) { |
| file->dwz_file = elf_open(file->dwz_file_name); |
| if (file->dwz_file == NULL) { |
| str_exception(errno, "Cannot open DWZ file"); |
| } |
| file->dwz_file->lock_cnt++; |
| get_dwarf_cache(file->dwz_file); |
| } |
| sCache = Cache = (DWARFCache *)(file->dwarf_dt_cache = loc_alloc_zero(sizeof(DWARFCache))); |
| sCache->magic = DWARF_CACHE_MAGIC; |
| sCache->mFile = file; |
| sCache->mObjectArrayPos = OBJECT_ARRAY_SIZE; |
| sCache->mObjectHashTable = (ObjectHashTable *)loc_alloc_zero(sizeof(ObjectHashTable) * file->section_cnt); |
| if (set_trap(&trap)) { |
| dio_LoadAbbrevTable(file); |
| load_debug_sections(); |
| clear_trap(&trap); |
| } |
| else { |
| sCache->mErrorReport = get_error_report(trap.error); |
| } |
| sCache = NULL; |
| } |
| if (Cache->mErrorReport) exception(set_error_report_errno(Cache->mErrorReport)); |
| return Cache; |
| } |
| |
| static void add_dir(CompUnit * Unit, char * Name) { |
| if (Unit->mDirsCnt >= Unit->mDirsMax) { |
| Unit->mDirsMax = Unit->mDirsMax == 0 ? 16 : Unit->mDirsMax * 2; |
| Unit->mDirs = (char **)loc_realloc(Unit->mDirs, sizeof(char *) * Unit->mDirsMax); |
| } |
| Unit->mDirs[Unit->mDirsCnt++] = Name; |
| } |
| |
| static void add_file(CompUnit * Unit, FileInfo * file) { |
| file->mNameHash = calc_file_name_hash(file->mName); |
| if (Unit->mFilesCnt >= Unit->mFilesMax) { |
| Unit->mFilesMax = Unit->mFilesMax == 0 ? 16 : Unit->mFilesMax * 2; |
| Unit->mFiles = (FileInfo *)loc_realloc(Unit->mFiles, sizeof(FileInfo) * Unit->mFilesMax); |
| } |
| if (file->mDir == NULL) file->mDir = Unit->mDir; |
| Unit->mFiles[Unit->mFilesCnt++] = *file; |
| } |
| |
| static void add_state(CompUnit * Unit, LineNumbersState * state) { |
| if (state->mFile >= Unit->mFilesCnt) { |
| /* Workaround: Diab compiler generates invalid file indices for an empty compilation unit */ |
| return; |
| } |
| if (Unit->mFiles[state->mFile].mAreaCnt++ == 0) { |
| /* Workaround: compilers don't produce mapping for first lines in a source file. |
| * Such mapping is needed, for example, for re-positioning of source line breakpoints. |
| * We add artificial entry for that. |
| */ |
| LineNumbersState s; |
| memset(&s, 0, sizeof(s)); |
| s.mLine = 1; |
| s.mFile = state->mFile; |
| s.mSection = state->mSection; |
| s.mAddress = state->mAddress; |
| add_state(Unit, &s); |
| } |
| if (Unit->mStatesCnt >= Unit->mStatesMax) { |
| Unit->mStatesMax = Unit->mStatesMax == 0 ? 128 : Unit->mStatesMax * 2; |
| Unit->mStates = (LineNumbersState *)loc_realloc(Unit->mStates, sizeof(LineNumbersState) * Unit->mStatesMax); |
| } |
| Unit->mStates[Unit->mStatesCnt++] = *state; |
| } |
| |
| static int state_address_comparator(const void * x1, const void * x2) { |
| LineNumbersState * s1 = (LineNumbersState *)x1; |
| LineNumbersState * s2 = (LineNumbersState *)x2; |
| if (s1->mSection < s2->mSection) return -1; |
| if (s1->mSection > s2->mSection) return +1; |
| if (s1->mAddress < s2->mAddress) return -1; |
| if (s1->mAddress > s2->mAddress) return +1; |
| if ((s1->mFlags ^ s2->mFlags) & LINE_EndSequence) { |
| return s1->mFlags & LINE_EndSequence ? -1 : +1; |
| } |
| if (s1->mFile < s2->mFile) return -1; |
| if (s1->mFile > s2->mFile) return +1; |
| if (s1->mLine < s2->mLine) return -1; |
| if (s1->mLine > s2->mLine) return +1; |
| if (s1->mColumn < s2->mColumn) return -1; |
| if (s1->mColumn > s2->mColumn) return +1; |
| return 0; |
| } |
| |
| static int state_text_pos_comparator(const void * x1, const void * x2) { |
| LineNumbersState * s1 = *(LineNumbersState **)x1; |
| LineNumbersState * s2 = *(LineNumbersState **)x2; |
| if (s1->mFile < s2->mFile) return -1; |
| if (s1->mFile > s2->mFile) return +1; |
| if (s1->mLine < s2->mLine) return -1; |
| if (s1->mLine > s2->mLine) return +1; |
| if (s1->mColumn < s2->mColumn) return -1; |
| if (s1->mColumn > s2->mColumn) return +1; |
| if (s1->mSection < s2->mSection) return -1; |
| if (s1->mSection > s2->mSection) return +1; |
| if (s1->mAddress < s2->mAddress) return -1; |
| if (s1->mAddress > s2->mAddress) return +1; |
| return 0; |
| } |
| |
| static void compute_reverse_lookup_indices(DWARFCache * Cache, CompUnit * Unit) { |
| U4_T i; |
| qsort(Unit->mStates, Unit->mStatesCnt, sizeof(LineNumbersState), state_address_comparator); |
| Unit->mStatesIndex = (LineNumbersState **)loc_alloc(sizeof(LineNumbersState *) * Unit->mStatesCnt); |
| for (i = 0; i < Unit->mStatesCnt; i++) { |
| LineNumbersState * s1 = Unit->mStates + i; |
| while (i + 1 < Unit->mStatesCnt) { |
| LineNumbersState * s2 = s1 + 1; |
| if (s1->mFile != s2->mFile || |
| s1->mLine != s2->mLine || s1->mColumn != s2->mColumn || |
| s1->mFlags != s2->mFlags || s1->mISA != s2->mISA || |
| s1->mOpIndex != s2->mOpIndex || s1->mDiscriminator != s2->mDiscriminator) break; |
| memmove(s2, s2 + 1, sizeof(LineNumbersState) * (Unit->mStatesCnt - i - 2)); |
| Unit->mStatesCnt--; |
| } |
| Unit->mStatesIndex[i] = s1; |
| } |
| qsort(Unit->mStatesIndex, Unit->mStatesCnt, sizeof(LineNumbersState *), state_text_pos_comparator); |
| for (i = 0; i < Unit->mStatesCnt; i++) Unit->mStatesIndex[i]->mStatesIndexPos = i; |
| if (Cache->mFileInfoHash == NULL) { |
| Cache->mFileInfoHashSize = 251; |
| Cache->mFileInfoHash = (FileInfo **)loc_alloc_zero(sizeof(FileInfo *) * Cache->mFileInfoHashSize); |
| } |
| for (i = 0; i < Unit->mFilesCnt; i++) { |
| FileInfo * File = Unit->mFiles + i; |
| unsigned h = File->mNameHash % Cache->mFileInfoHashSize; |
| File->mCompUnit = Unit; |
| File->mNextInHash = Cache->mFileInfoHash[h]; |
| Cache->mFileInfoHash[h] = File; |
| } |
| } |
| |
| static void load_line_numbers_v1(CompUnit * Unit, U4_T unit_size) { |
| LineNumbersState state; |
| ELF_Section * sec = NULL; |
| ContextAddress addr = 0; |
| U4_T line = 0; |
| |
| memset(&state, 0, sizeof(state)); |
| addr = (ContextAddress)dio_ReadAddress(&sec); |
| if (sec != NULL) state.mSection = sec->index; |
| while (dio_GetPos() < Unit->mLineInfoOffs + unit_size) { |
| state.mLine = dio_ReadU4(); |
| state.mColumn = dio_ReadU2(); |
| if (state.mColumn == 0xffffu) state.mColumn = 0; |
| state.mAddress = addr + dio_ReadU4(); |
| if (state.mLine == 0) { |
| state.mLine = line + 1; |
| state.mColumn = 0; |
| } |
| add_state(Unit, &state); |
| line = state.mLine; |
| } |
| } |
| |
| static void load_line_numbers_v2(CompUnit * Unit, U8_T unit_size, int dwarf64) { |
| U2_T version = 0; |
| U8_T header_pos = 0; |
| U1_T opcode_base = 0; |
| U1_T opcode_size[256]; |
| U8_T header_size = 0; |
| U1_T min_instruction_length = 0; |
| U1_T max_ops_per_instruction = 1; |
| U1_T is_stmt_default = 0; |
| I1_T line_base = 0; |
| U1_T line_range = 0; |
| LineNumbersState state; |
| |
| version = dio_ReadU2(); |
| if (version < 2 || version > 4) str_exception(ERR_INV_DWARF, "Invalid line number info version"); |
| header_size = dwarf64 ? dio_ReadU8() : (U8_T)dio_ReadU4(); |
| header_pos = dio_GetPos(); |
| min_instruction_length = dio_ReadU1(); |
| if (version >= 4) max_ops_per_instruction = dio_ReadU1(); |
| is_stmt_default = dio_ReadU1() != 0; |
| line_base = (I1_T)dio_ReadU1(); |
| line_range = dio_ReadU1(); |
| opcode_base = dio_ReadU1(); |
| memset(opcode_size, 0, sizeof(opcode_size)); |
| dio_Read(opcode_size + 1, opcode_base - 1); |
| |
| /* Read directory names */ |
| for (;;) { |
| char * Name = dio_ReadString(); |
| if (Name == NULL) break; |
| add_dir(Unit, Name); |
| } |
| |
| /* Read source files info */ |
| for (;;) { |
| U4_T dir = 0; |
| FileInfo file; |
| memset(&file, 0, sizeof(file)); |
| file.mName = dio_ReadString(); |
| if (file.mName == NULL) break; |
| dir = dio_ReadULEB128(); |
| if (dir > 0 && dir <= Unit->mDirsCnt) file.mDir = Unit->mDirs[dir - 1]; |
| file.mModTime = dio_ReadULEB128(); |
| file.mSize = dio_ReadULEB128(); |
| add_file(Unit, &file); |
| } |
| |
| if (header_pos + header_size != dio_GetPos()) { |
| if (dwarf64 && header_pos + header_size == dio_GetPos() + 12) { |
| /* OK - bug in GCC for MIPS64. */ |
| /* GCC generates prologue header_length field with a value which is exactly 12 too large. */ |
| } |
| else { |
| str_exception(ERR_INV_DWARF, "Invalid line info header"); |
| } |
| } |
| |
| /* Run the program */ |
| memset(&state, 0, sizeof(state)); |
| state.mFile = 1; |
| state.mLine = 1; |
| if (is_stmt_default) state.mFlags |= LINE_IsStmt; |
| while (dio_GetPos() < Unit->mLineInfoOffs + unit_size) { |
| U1_T opcode = dio_ReadU1(); |
| if (opcode >= opcode_base) { |
| unsigned op_advance = (opcode - opcode_base) / line_range; |
| state.mLine += (U4_T)((int)((opcode - opcode_base) % line_range) + line_base); |
| state.mAddress += (state.mOpIndex + op_advance) / max_ops_per_instruction * min_instruction_length; |
| state.mOpIndex = (state.mOpIndex + op_advance) % max_ops_per_instruction; |
| add_state(Unit, &state); |
| state.mFlags &= ~(LINE_BasicBlock | LINE_PrologueEnd | LINE_EpilogueBegin); |
| state.mDiscriminator = 0; |
| } |
| else if (opcode == 0) { |
| ELF_Section * sec = NULL; |
| U4_T op_size = dio_ReadULEB128(); |
| U8_T op_pos = dio_GetPos(); |
| switch (dio_ReadU1()) { |
| case DW_LNE_define_file: { |
| U4_T dir = 0; |
| FileInfo file; |
| memset(&file, 0, sizeof(file)); |
| file.mName = dio_ReadString(); |
| dir = dio_ReadULEB128(); |
| if (dir > 0 && dir <= Unit->mDirsCnt) file.mDir = Unit->mDirs[dir - 1]; |
| file.mModTime = dio_ReadULEB128(); |
| file.mSize = dio_ReadULEB128(); |
| add_file(Unit, &file); |
| break; |
| } |
| case DW_LNE_end_sequence: |
| state.mFlags |= LINE_EndSequence; |
| add_state(Unit, &state); |
| memset(&state, 0, sizeof(state)); |
| state.mFile = 1; |
| state.mLine = 1; |
| if (is_stmt_default) state.mFlags |= LINE_IsStmt; |
| break; |
| case DW_LNE_set_address: |
| state.mAddress = (ContextAddress)dio_ReadAddress(&sec); |
| state.mSection = sec != NULL ? sec->index : 0; |
| break; |
| case DW_LNE_set_discriminator: |
| state.mDiscriminator = (U1_T)dio_ReadULEB128(); |
| break; |
| default: |
| dio_Skip(op_size - 1); |
| break; |
| } |
| if (dio_GetPos() != op_pos + op_size) |
| str_exception(ERR_INV_DWARF, "Invalid line info op size"); |
| } |
| else { |
| switch (opcode) { |
| case DW_LNS_copy: |
| add_state(Unit, &state); |
| state.mFlags &= ~(LINE_BasicBlock | LINE_PrologueEnd | LINE_EpilogueBegin); |
| break; |
| case DW_LNS_advance_pc: |
| state.mAddress += (ContextAddress)(dio_ReadU8LEB128() * min_instruction_length); |
| break; |
| case DW_LNS_advance_line: |
| state.mLine += dio_ReadSLEB128(); |
| break; |
| case DW_LNS_set_file: |
| state.mFile = dio_ReadULEB128(); |
| break; |
| case DW_LNS_set_column: |
| state.mColumn = (U2_T)dio_ReadULEB128(); |
| break; |
| case DW_LNS_negate_stmt: |
| state.mFlags ^= LINE_IsStmt; |
| break; |
| case DW_LNS_set_basic_block: |
| state.mFlags |= LINE_BasicBlock; |
| break; |
| case DW_LNS_const_add_pc: |
| state.mAddress += (255 - opcode_base) / line_range * min_instruction_length; |
| break; |
| case DW_LNS_fixed_advance_pc: |
| state.mAddress += dio_ReadU2(); |
| break; |
| case DW_LNS_set_prologue_end: |
| state.mFlags |= LINE_PrologueEnd; |
| break; |
| case DW_LNS_set_epilogue_begin: |
| state.mFlags |= LINE_EpilogueBegin; |
| break; |
| case DW_LNS_set_isa: |
| state.mISA = (U1_T)dio_ReadULEB128(); |
| break; |
| default: |
| str_exception(ERR_INV_DWARF, "Invalid line info op code"); |
| break; |
| } |
| } |
| } |
| } |
| |
| void load_line_numbers(CompUnit * Unit) { |
| Trap trap; |
| DWARFCache * Cache = (DWARFCache *)Unit->mFile->dwarf_dt_cache; |
| ELF_Section * LineInfoSection = Unit->mDesc.mVersion <= 1 ? Cache->mDebugLineV1 : Cache->mDebugLineV2; |
| if (LineInfoSection == NULL) return; |
| if (Unit->mLineInfoLoaded) return; |
| if (elf_load(LineInfoSection)) exception(errno); |
| dio_EnterSection(&Unit->mDesc, LineInfoSection, Unit->mLineInfoOffs); |
| if (set_trap(&trap)) { |
| U8_T unit_size = 0; |
| FileInfo file; |
| memset(&file, 0, sizeof(file)); |
| file.mDir = Unit->mDir; |
| file.mName = Unit->mObject->mName; |
| add_file(Unit, &file); |
| /* Read header */ |
| unit_size = dio_ReadU4(); |
| if (Unit->mDesc.mVersion <= 1) { |
| /* DWARF 1.1 */ |
| load_line_numbers_v1(Unit, (U4_T)unit_size); |
| } |
| else { |
| /* DWARF 2+ */ |
| int dwarf64 = 0; |
| if (unit_size == 0xffffffffu) { |
| unit_size = dio_ReadU8(); |
| unit_size += 12; |
| dwarf64 = 1; |
| } |
| else { |
| unit_size += 4; |
| } |
| load_line_numbers_v2(Unit, unit_size, dwarf64); |
| } |
| dio_ExitSection(); |
| compute_reverse_lookup_indices(Cache, Unit); |
| Unit->mLineInfoLoaded = 1; |
| clear_trap(&trap); |
| } |
| else { |
| dio_ExitSection(); |
| free_unit_cache(Unit); |
| exception(trap.error); |
| } |
| } |
| |
| UnitAddressRange * find_comp_unit_addr_range(DWARFCache * cache, ELF_Section * section, |
| ContextAddress addr_min, ContextAddress addr_max) { |
| unsigned l = 0; |
| unsigned h = cache->mAddrRangesCnt; |
| U4_T s = 0; |
| |
| if (cache->mAddrRangesRelocatable && section != NULL) { |
| if (section->file == cache->mFile) { |
| s = section->index; |
| } |
| else { |
| unsigned i; |
| assert(get_dwarf_file(section->file) == cache->mFile); |
| for (i = 1; i < cache->mFile->section_cnt; i++) { |
| ELF_Section * sec = cache->mFile->sections + i; |
| if (sec->name == NULL) continue; |
| if (strcmp(sec->name, section->name) == 0) { |
| s = i; |
| break; |
| } |
| } |
| } |
| } |
| |
| while (l < h) { |
| unsigned k = (h + l) / 2; |
| UnitAddressRange * rk = cache->mAddrRanges + k; |
| if (rk->mAddr > addr_max) h = k; |
| else if (rk->mAddr + cache->mAddrRangesMaxSize <= addr_min) l = k + 1; |
| else { |
| for (; k > 0; k--) { |
| rk = cache->mAddrRanges + k - 1; |
| if (rk->mAddr + cache->mAddrRangesMaxSize <= addr_min) break; |
| } |
| for (; k < h; k++) { |
| rk = cache->mAddrRanges + k; |
| if (rk->mAddr > addr_max) break; |
| if (rk->mAddr + rk->mSize <= addr_min) continue; |
| if (rk->mSection && s && rk->mSection != s) continue; |
| return rk; |
| } |
| break; |
| } |
| } |
| return NULL; |
| } |
| |
| #endif /* ENABLE_ELF && ENABLE_DebugContext */ |