| /******************************************************************************* |
| * Copyright (c) 2007, 2010 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 <config.h> |
| |
| #if ENABLE_ELF && ENABLE_DebugContext |
| |
| #include <assert.h> |
| #include <framework/exceptions.h> |
| #include <framework/myalloc.h> |
| #include <services/dwarf.h> |
| #include <services/dwarfio.h> |
| #include <services/dwarfcache.h> |
| #include <services/dwarfexpr.h> |
| #include <services/stacktrace.h> |
| |
| #define OBJ_HASH(Cache,ID) (((U4_T)(ID) + ((U4_T)(ID) >> 8)) % Cache->mObjectHashSize) |
| |
| static DWARFCache * sCache; |
| static ELF_Section * sDebugSection; |
| static DIO_UnitDescriptor sUnitDesc; |
| static CompUnit * sCompUnit; |
| static ObjectInfo * sParentObject; |
| static ObjectInfo * sPrevSibling; |
| |
| static int sCloseListenerOK = 0; |
| |
| unsigned calc_file_name_hash(const char * s) { |
| unsigned l = strlen(s); |
| unsigned h = 0; |
| 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 ^= g >> 24; |
| h &= ~g; |
| } |
| return h; |
| } |
| |
| ObjectInfo * find_object(DWARFCache * Cache, U8_T ID) { |
| if (Cache->mObjectHash != NULL) { |
| ObjectInfo * Info = Cache->mObjectHash[OBJ_HASH(Cache, ID)]; |
| |
| while (Info != NULL) { |
| if (Info->mID == ID) return Info; |
| Info = Info->mHashNext; |
| } |
| } |
| return NULL; |
| } |
| |
| static ObjectInfo * add_object_info(U8_T ID) { |
| ObjectInfo * Info = find_object(sCache, ID); |
| if (Info == NULL) { |
| U4_T Hash; |
| if (ID < sDebugSection->addr + dio_gEntryPos) str_exception(ERR_INV_DWARF, "Invalid entry reference"); |
| if (ID > sDebugSection->addr + sDebugSection->size) str_exception(ERR_INV_DWARF, "Invalid entry reference"); |
| if (sCache->mObjectHash == NULL) { |
| sCache->mObjectHashSize = (unsigned)(sDebugSection->size / 251); |
| if (sCache->mObjectHashSize < 101) sCache->mObjectHashSize = 101; |
| sCache->mObjectHash = (ObjectInfo **)loc_alloc_zero(sizeof(ObjectInfo *) * sCache->mObjectHashSize); |
| } |
| Hash = OBJ_HASH(sCache, ID); |
| 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 = sCache->mObjectHash[Hash]; |
| sCache->mObjectHash[Hash] = Info; |
| Info->mID = ID; |
| } |
| return Info; |
| } |
| |
| static CompUnit * add_comp_unit(U8_T ID) { |
| ObjectInfo * Info = add_object_info(ID); |
| if (Info->mCompUnit == NULL) { |
| CompUnit * Unit = (CompUnit *)loc_alloc_zero(sizeof(CompUnit)); |
| Unit->mObject = Info; |
| Info->mCompUnit = Unit; |
| } |
| return Info->mCompUnit; |
| } |
| |
| 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(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(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(sDebugSection->addr + Ref); |
| 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(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(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(dio_gFormData); |
| break; |
| } |
| if (Type != NULL) { |
| ObjectInfo * Range = add_object_info(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(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(dio_gFormData); |
| 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 ObjectInfo * Spec; |
| static ObjectInfo * AOrg; |
| static U8_T Sibling; |
| static int HasChildren; |
| |
| switch (Attr) { |
| case 0: |
| if (Form) { |
| assert((sParentObject == NULL) == (Tag == TAG_compile_unit)); |
| if (Tag == TAG_compile_unit) { |
| CompUnit * Unit = add_comp_unit(sDebugSection->addr + dio_gEntryPos); |
| Unit->mFile = sCache->mFile; |
| Unit->mDebugRangesOffs = ~(U8_T)0; |
| Unit->mRegIdScope.big_endian = sCache->mFile->big_endian; |
| Unit->mRegIdScope.machine = sCache->mFile->machine; |
| Unit->mRegIdScope.os_abi = sCache->mFile->os_abi; |
| Unit->mRegIdScope.id_type = REGNUM_DWARF; |
| Info = Unit->mObject; |
| sCompUnit = Unit; |
| } |
| else { |
| Info = add_object_info(sDebugSection->addr + dio_gEntryPos); |
| Info->mCompUnit = sCompUnit; |
| } |
| assert(Info->mTag == 0); |
| Info->mTag = Tag; |
| Info->mParent = sParentObject; |
| HasChildren = Form == DWARF_ENTRY_HAS_CHILDREN; |
| Sibling = 0; |
| Spec = NULL; |
| AOrg = NULL; |
| } |
| else { |
| if (Spec != NULL) { |
| if (Info->mName == NULL) Info->mName = Spec->mName; |
| if (Info->mType == NULL) Info->mType = Spec->mType; |
| } |
| if (AOrg != NULL) { |
| if (Info->mName == NULL) Info->mName = AOrg->mName; |
| if (Info->mType == NULL) Info->mType = AOrg->mType; |
| } |
| if (Tag == TAG_compile_unit && Sibling == 0) Sibling = sUnitDesc.mUnitOffs + sUnitDesc.mUnitSize; |
| if (Tag == TAG_enumerator && Info->mType == NULL) Info->mType = sParentObject; |
| if (sPrevSibling != NULL) sPrevSibling->mSibling = Info; |
| else if (sParentObject != NULL) sParentObject->mChildren = Info; |
| else sCache->mCompUnits = Info; |
| sPrevSibling = Info; |
| 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: |
| dio_ChkRef(Form); |
| Info->mType = add_object_info(dio_gFormData); |
| break; |
| case AT_fund_type: |
| dio_ChkData(Form); |
| Info->mType = add_object_info(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(dio_gFormData); |
| 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_subscr_data: |
| read_subscr_data(Form, Info); |
| break; |
| case AT_name: |
| dio_ChkString(Form); |
| Info->mName = (char *)dio_gFormDataAddr; |
| break; |
| case AT_specification_v2: |
| dio_ChkRef(Form); |
| Spec = add_object_info(dio_gFormData); |
| break; |
| case AT_abstract_origin: |
| dio_ChkRef(Form); |
| AOrg = add_object_info(dio_gFormData); |
| break; |
| case AT_low_pc: |
| dio_ChkAddr(Form); |
| Info->u.mAddr.mLowPC = (ContextAddress)dio_gFormData; |
| break; |
| case AT_high_pc: |
| dio_ChkAddr(Form); |
| Info->u.mAddr.mHighPC = (ContextAddress)dio_gFormData; |
| break; |
| } |
| if (Tag == TAG_compile_unit) { |
| CompUnit * Unit = Info->mCompUnit; |
| switch (Attr) { |
| case AT_low_pc: |
| dio_ChkAddr(Form); |
| Unit->mLowPC = (ContextAddress)dio_gFormData; |
| Unit->mTextSection = dio_gFormSection; |
| break; |
| case AT_high_pc: |
| dio_ChkAddr(Form); |
| Unit->mHighPC = (ContextAddress)dio_gFormData; |
| break; |
| case AT_ranges: |
| dio_ChkData(Form); |
| Unit->mDebugRangesOffs = dio_gFormData; |
| break; |
| case AT_comp_dir: |
| dio_ChkString(Form); |
| Unit->mDir = (char *)dio_gFormDataAddr; |
| break; |
| case AT_stmt_list: |
| dio_ChkData(Form); |
| Unit->mLineInfoOffs = dio_gFormData; |
| break; |
| case AT_base_types: |
| Unit->mBaseTypes = add_comp_unit(dio_gFormData); |
| break; |
| case AT_language: |
| dio_ChkData(Form); |
| Unit->mLanguage = (U2_T)dio_gFormData; |
| break; |
| } |
| } |
| } |
| |
| static int cmp_addr_ranges(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; |
| return 0; |
| } |
| |
| static void add_addr_range(ELF_Section * sec, CompUnit * unit, ContextAddress addr, ContextAddress size) { |
| UnitAddressRange * range = NULL; |
| 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)); |
| range->mSection = sec; |
| range->mAddr = addr; |
| range->mSize = size; |
| range->mUnit = unit; |
| } |
| |
| static void load_addr_ranges(void) { |
| Trap trap; |
| unsigned idx; |
| ELF_File * file = sCache->mFile; |
| ELF_Section * debug_ranges = NULL; |
| |
| 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_ranges") == 0) { |
| debug_ranges = sec; |
| } |
| else if (strcmp(sec->name, ".debug_aranges") == 0) { |
| ObjectInfo * info = sCache->mCompUnits; |
| 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) { |
| dio_SetPos(next); |
| } |
| else { |
| U8_T offs = dwarf64 ? dio_ReadU8() : (U8_T)dio_ReadU4(); |
| 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"); |
| while (info != NULL && info->mCompUnit->mDesc.mUnitOffs != offs) info = info->mSibling; |
| if (info == NULL) { |
| info = sCache->mCompUnits; |
| while (info != NULL && info->mCompUnit->mDesc.mUnitOffs != offs) info = info->mSibling; |
| } |
| if (info == NULL) str_exception(ERR_INV_DWARF, "invalid .debug_aranges section"); |
| info->mCompUnit->mARangesFound = 1; |
| while (dio_GetPos() % (addr_size * 2) != 0) dio_Skip(1); |
| for (;;) { |
| ELF_Section * range_sec = NULL; |
| ContextAddress addr = dio_ReadAddressX(&range_sec, addr_size); |
| ContextAddress size = dio_ReadUX(addr_size); |
| if (addr == 0 && size == 0) break; |
| if (size == 0) continue; |
| add_addr_range(range_sec, info->mCompUnit, addr, size); |
| } |
| } |
| } |
| clear_trap(&trap); |
| } |
| dio_ExitSection(); |
| if (trap.error) break; |
| } |
| } |
| if (trap.error) exception(trap.error); |
| if (sCache->mCompUnits != NULL) { |
| ObjectInfo * info = sCache->mCompUnits; |
| while (info != NULL) { |
| CompUnit * unit = info->mCompUnit; |
| ContextAddress base = unit->mLowPC; |
| ContextAddress size = unit->mHighPC - unit->mLowPC; |
| info = info->mSibling; |
| if (unit->mARangesFound) continue; |
| if (size == 0) continue; |
| if (unit->mDebugRangesOffs != ~(U8_T)0 && debug_ranges != NULL) { |
| dio_EnterSection(&unit->mDesc, debug_ranges, unit->mDebugRangesOffs); |
| for (;;) { |
| ELF_Section * sec = NULL; |
| U8_T x = dio_ReadAddress(&sec); |
| U8_T y = dio_ReadAddress(&sec); |
| if (x == 0 && y == 0) break; |
| if (sec != unit->mTextSection) exception(ERR_INV_DWARF); |
| if (x == ((U8_T)1 << unit->mDesc.mAddressSize * 8) - 1) { |
| base = (ContextAddress)y; |
| } |
| else { |
| x = base + x; |
| y = base + y; |
| add_addr_range(sec, unit, x, y - x); |
| } |
| } |
| dio_ExitSection(); |
| } |
| else { |
| add_addr_range(unit->mTextSection, unit, base, size); |
| } |
| } |
| } |
| if (sCache->mAddrRangesCnt > 1) { |
| qsort(sCache->mAddrRanges, sCache->mAddrRangesCnt, sizeof(UnitAddressRange), cmp_addr_ranges); |
| } |
| } |
| |
| static void load_pub_names(ELF_Section * debug_info, ELF_Section * pub_names, PubNamesTable * tbl) { |
| tbl->mMax = (unsigned)(pub_names->size / 16) + 16; |
| tbl->mHash = (unsigned *)loc_alloc_zero(sizeof(unsigned) * SYM_HASH_SIZE); |
| tbl->mNext = (PubNamesInfo *)loc_alloc(sizeof(PubNamesInfo) * tbl->mMax); |
| memset(tbl->mNext + tbl->mCnt++, 0, sizeof(PubNamesInfo)); |
| 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_offs + unit_size > debug_info->size) str_fmt_exception(ERR_INV_DWARF, |
| "Invalid unit size in %s section", pub_names->name); |
| for (;;) { |
| unsigned h; |
| PubNamesInfo * 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); |
| 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; |
| h = calc_symbol_name_hash(dio_ReadString()); |
| info->mID = debug_info->addr + unit_offs + obj_offs; |
| info->mNext = tbl->mHash[h]; |
| tbl->mHash[h] = tbl->mCnt++; |
| } |
| } |
| assert(next >= dio_GetPos()); |
| dio_SetPos(next); |
| } |
| dio_ExitSection(); |
| } |
| |
| static void load_debug_sections(void) { |
| Trap trap; |
| unsigned idx; |
| ELF_Section * pub_names = NULL; |
| ELF_Section * pub_types = NULL; |
| ELF_Section * debug_info = NULL; |
| 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") == 0 || strcmp(sec->name, ".debug_info") == 0) { |
| if (strcmp(sec->name, ".debug_info") == 0) debug_info = sec; |
| sDebugSection = sec; |
| sParentObject = NULL; |
| sPrevSibling = NULL; |
| dio_EnterSection(NULL, sec, 0); |
| if (set_trap(&trap)) { |
| while (dio_GetPos() < sec->size) { |
| dio_ReadUnit(&sUnitDesc, read_object_info); |
| sCompUnit->mDesc = sUnitDesc; |
| } |
| clear_trap(&trap); |
| } |
| dio_ExitSection(); |
| sParentObject = NULL; |
| sPrevSibling = NULL; |
| sCompUnit = NULL; |
| sDebugSection = NULL; |
| if (trap.error) break; |
| } |
| else if (strcmp(sec->name, ".line") == 0) { |
| sCache->mDebugLineV1 = sec; |
| } |
| else if (strcmp(sec->name, ".debug_line") == 0) { |
| sCache->mDebugLine = 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) { |
| sCache->mDebugFrame = sec; |
| } |
| else if (strcmp(sec->name, ".eh_frame") == 0) { |
| sCache->mEHFrame = sec; |
| } |
| else if (strcmp(sec->name, ".debug_pubnames") == 0) { |
| pub_names = sec; |
| } |
| else if (strcmp(sec->name, ".debug_pubtypes") == 0) { |
| pub_types = sec; |
| } |
| } |
| |
| if (debug_info) { |
| if (pub_names) load_pub_names(debug_info, pub_names, &sCache->mPubNames); |
| if (pub_types) load_pub_names(debug_info, pub_types, &sCache->mPubTypes); |
| } |
| |
| if (trap.error) exception(trap.error); |
| } |
| |
| 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 void get_object_property_callback(U2_T Tag, U2_T Attr, U2_T Form) { |
| if (Attr == AT_specification_v2) gop_gSpecification = dio_gFormData; |
| if (Attr == AT_abstract_origin) gop_gAbstractOrigin = dio_gFormData; |
| 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 || Value->mRegister != NULL) { |
| str_exception(ERR_INV_CONTEXT, "Constant DWARF attribute value expected"); |
| } |
| else if (Value->mAddr != NULL) { |
| size_t i; |
| 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; |
| } |
| |
| static 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 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) { |
| /* TAG_index_range is virtual DWARF object that is created by DWARF reader. It has no properties. */ |
| 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; |
| sCache = (DWARFCache *)sCompUnit->mFile->dwarf_dt_cache; |
| sDebugSection = sCompUnit->mDesc.mSection; |
| dio_EnterSection(&sCompUnit->mDesc, sDebugSection, Obj->mID - sDebugSection->addr); |
| for (;;) { |
| gop_gAttr = Attr; |
| gop_gForm = 0; |
| gop_gSpecification = 0; |
| gop_gAbstractOrigin = 0; |
| dio_ReadEntry(get_object_property_callback, Attr); |
| dio_ExitSection(); |
| if (gop_gForm != 0) break; |
| if (gop_gSpecification != 0) dio_EnterSection(&sCompUnit->mDesc, sDebugSection, gop_gSpecification - sDebugSection->addr); |
| else if (gop_gAbstractOrigin != 0) dio_EnterSection(&sCompUnit->mDesc, sDebugSection, gop_gAbstractOrigin - 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 : |
| if (Attr == AT_import) { |
| Value->mValue = gop_gFormData; |
| } |
| else { |
| PropertyValue ValueAddr; |
| ObjectInfo * RefObj = find_object(sCache, gop_gFormData); |
| |
| if (RefObj == NULL) exception(ERR_INV_DWARF); |
| read_and_evaluate_dwarf_object_property(Ctx, Frame, 0, RefObj, AT_location, &ValueAddr); |
| if (ValueAddr.mRegister != NULL) { |
| static U1_T Buf[8]; |
| StackFrame * Frame = NULL; |
| if (get_frame_info(ValueAddr.mContext, ValueAddr.mFrame, &Frame) < 0) exception(errno); |
| if (read_reg_bytes(Frame, ValueAddr.mRegister, 0, ValueAddr.mRegister->size, Buf) < 0) exception(errno); |
| Value->mAddr = Buf; |
| Value->mSize = ValueAddr.mSize; |
| Value->mBigEndian = ValueAddr.mBigEndian; |
| } |
| else { |
| static U1_T Buf[8]; |
| PropertyValue ValueSize; |
| ContextAddress Addr; |
| size_t Size; |
| |
| Addr = (ContextAddress)get_numeric_property_value(&ValueAddr); |
| read_and_evaluate_dwarf_object_property(Ctx, Frame, Addr, RefObj, AT_byte_size, &ValueSize); |
| Size = (size_t)get_numeric_property_value(&ValueSize); |
| if (Size < 1 || Size > sizeof(Buf)) exception(ERR_INV_DATA_TYPE); |
| if (context_read_mem(Ctx, Addr, Buf, Size) < 0) exception(errno); |
| Value->mAddr = Buf; |
| Value->mSize = Size; |
| } |
| } |
| 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 : |
| 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->mValue = elf_map_to_run_time_address(Ctx, Obj->mCompUnit->mFile, gop_gFormSection, (ContextAddress)gop_gFormData); |
| 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_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 * 2; |
| break; |
| } |
| } |
| exception(ERR_SYM_NOT_FOUND); |
| } |
| |
| sCompUnit = NULL; |
| sCache = NULL; |
| sDebugSection = NULL; |
| } |
| |
| void read_and_evaluate_dwarf_object_property(Context * Ctx, int Frame, U8_T Base, ObjectInfo * Obj, U2_T Attr, 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 (Attr == AT_location || Attr == AT_data_member_location || Attr == AT_frame_base) { |
| switch (Value->mForm) { |
| case FORM_DATA4 : |
| case FORM_DATA8 : |
| case FORM_BLOCK1 : |
| case FORM_BLOCK2 : |
| case FORM_BLOCK4 : |
| case FORM_BLOCK : |
| dwarf_evaluate_expression(Base, Value); |
| 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(Base, Value); |
| 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) { |
| assert(Cache->magic == DWARF_CACHE_MAGIC); |
| Cache->magic = 0; |
| while (Cache->mCompUnits != NULL) { |
| CompUnit * Unit = Cache->mCompUnits->mCompUnit; |
| Cache->mCompUnits = Cache->mCompUnits->mSibling; |
| free_unit_cache(Unit); |
| loc_free(Unit); |
| } |
| while (Cache->mObjectList != NULL) { |
| ObjectArray * Buf = Cache->mObjectList; |
| Cache->mObjectList = Buf->mNext; |
| loc_free(Buf); |
| } |
| loc_free(Cache->mObjectHash); |
| loc_free(Cache->mAddrRanges); |
| loc_free(Cache->mFrameInfoRanges); |
| loc_free(Cache->mPubNames.mHash); |
| loc_free(Cache->mPubNames.mNext); |
| loc_free(Cache->mPubTypes.mHash); |
| loc_free(Cache->mPubTypes.mNext); |
| loc_free(Cache->mFileInfoHash); |
| 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; |
| } |
| 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; |
| if (set_trap(&trap)) { |
| dio_LoadAbbrevTable(file); |
| load_debug_sections(); |
| load_addr_ranges(); |
| 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 (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->mAddress < s2->mAddress) return -1; |
| if (s1->mAddress > s2->mAddress) 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->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++) Unit->mStatesIndex[i] = Unit->mStates + i; |
| qsort(Unit->mStatesIndex, Unit->mStatesCnt, sizeof(LineNumbersState *), state_text_pos_comparator); |
| for (i = 1; i < Unit->mStatesCnt; i++) { |
| LineNumbersState * s = Unit->mStatesIndex[i - 1]; |
| LineNumbersState * n = Unit->mStatesIndex[i]; |
| s->mNext = n - Unit->mStates; |
| } |
| 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 * s = NULL; |
| ContextAddress addr = 0; |
| U4_T line = 0; |
| |
| memset(&state, 0, sizeof(state)); |
| addr = (ContextAddress)dio_ReadAddress(&s); |
| 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) { |
| 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 is_stmt_default = 0; |
| I1_T line_base = 0; |
| U1_T line_range = 0; |
| LineNumbersState state; |
| |
| dio_ReadU2(); /* line info version */ |
| header_size = dwarf64 ? dio_ReadU8() : (U8_T)dio_ReadU4(); |
| header_pos = dio_GetPos(); |
| min_instruction_length = 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); |
| } |
| |
| /* Run the program */ |
| if (header_pos + header_size != dio_GetPos()) |
| str_exception(ERR_INV_DWARF, "Invalid line info header"); |
| 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) { |
| state.mLine += (unsigned)((int)((opcode - opcode_base) % line_range) + line_base); |
| state.mAddress += (opcode - opcode_base) / line_range * min_instruction_length; |
| add_state(Unit, &state); |
| state.mFlags &= ~(LINE_BasicBlock | LINE_PrologueEnd | LINE_EpilogueBegin); |
| } |
| else if (opcode == 0) { |
| 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; |
| else state.mFlags &= ~LINE_IsStmt; |
| break; |
| case DW_LNE_set_address: |
| { |
| ELF_Section * s = NULL; |
| state.mAddress = (ContextAddress)dio_ReadAddress(&s); |
| if (s != Unit->mTextSection) state.mAddress = 0; |
| } |
| 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->mDebugLine; |
| 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, ContextAddress addr_min, ContextAddress addr_max) { |
| unsigned l = 0; |
| unsigned h = cache->mAddrRangesCnt; |
| while (l < h) { |
| unsigned k = (h + l) / 2; |
| UnitAddressRange * rk = cache->mAddrRanges + k; |
| if (rk->mAddr <= addr_max && rk->mAddr + rk->mSize > addr_min) { |
| int first = 1; |
| if (k > 0) { |
| UnitAddressRange * rp = rk - 1; |
| first = rp->mAddr + rp->mSize <= addr_min; |
| } |
| if (first) return rk; |
| } |
| if (rk->mAddr >= addr_min) h = k; |
| else l = k + 1; |
| } |
| return NULL; |
| } |
| |
| #endif /* ENABLE_ELF && ENABLE_DebugContext */ |