From be649db06f135822fc89b2d3f22ecda51c3a4171 Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Tue, 2 Nov 2010 20:21:03 +0000 Subject: [PATCH] * Added support for reading gcc 4 generated .eh_frame sections. The main difference to gcc 2 .eh_frame sections is the initial location field in the FDEs, which is a relocated absolute address with gcc 2, but a relative address with gcc 4. * Recognize and skip the additional augmentations gcc 4 puts in the .eh_frame sections contain. We might want/need to interpret some of them, but, since I haven't found actual documentation for them yet, currently they are just ignored. * DwarfFile::UnwindCallFrame(): - Correctly support CIE offsets in .eh_frame sections. Unlike in .debug_frame they are current position relative back offsets. - Cleaned up some debug output. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@39276 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/apps/debugger/dwarf/DwarfFile.cpp | 244 +++++++++++++++++++++----- src/apps/debugger/dwarf/DwarfFile.h | 10 +- 2 files changed, 210 insertions(+), 44 deletions(-) diff --git a/src/apps/debugger/dwarf/DwarfFile.cpp b/src/apps/debugger/dwarf/DwarfFile.cpp index 8103e76af8..b1c9e7258f 100644 --- a/src/apps/debugger/dwarf/DwarfFile.cpp +++ b/src/apps/debugger/dwarf/DwarfFile.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Copyright 2009-2010, Ingo Weinhold, ingo_weinhold@gmx.de. * Distributed under the terms of the MIT License. */ @@ -149,6 +149,130 @@ private: }; +// #pragma mark - FDEAugmentation + + +struct DwarfFile::FDEAugmentation { + // Currently we're ignoring all augmentation data. +}; + + +// #pragma mark - CIEAugmentation + + +enum { + CFI_AUGMENTATION_DATA = 0x01, + CFI_AUGMENTATION_LANGUAGE_SPECIFIC_DATA = 0x02, + CFI_AUGMENTATION_PERSONALITY = 0x04, + CFI_AUGMENTATION_ADDRESS_POINTER_FORMAT = 0x08, +}; + + +struct DwarfFile::CIEAugmentation { + CIEAugmentation() + : + fString(NULL), + fFlags(0) + { + } + + void Init(DataReader& dataReader) + { + fFlags = 0; + fString = dataReader.ReadString(); + } + + status_t Read(DataReader& dataReader) + { + if (fString == NULL || *fString == '\0') + return B_OK; + + if (*fString == 'z') { + // There are augmentation data. + fFlags |= CFI_AUGMENTATION_DATA; + const char* string = fString + 1; + + // let's see what data we have to expect + while (*string != '\0') { + switch (*string) { + case 'L': + fFlags |= CFI_AUGMENTATION_LANGUAGE_SPECIFIC_DATA; + break; + case 'P': + fFlags |= CFI_AUGMENTATION_PERSONALITY; + break; + case 'R': + fFlags |= CFI_AUGMENTATION_ADDRESS_POINTER_FORMAT; + break; + default: + return B_UNSUPPORTED; + } + string++; + } + + // read the augmentation data block -- it is preceeded by an + // LEB128 indicating the length of the data block + uint64 length = dataReader.ReadUnsignedLEB128(0); + dataReader.Skip(length); + // TODO: Actually read what is interesting for us! The + // CFI_AUGMENTATION_ADDRESS_POINTER_FORMAT might be. The + // specs are not saying much about it. + + TRACE_CFI(" %" B_PRIu64 " bytes of augmentation data\n", length); + + if (dataReader.HasOverflow()) + return B_BAD_DATA; + + return B_OK; + } + + if (strcmp(fString, "eh") == 0) { + // the augmentation consists of the exception table pointer + // -- just ignore it + dataReader.ReadAddress(0); + return B_OK; + } + + // something we can't handle + return B_UNSUPPORTED; + } + + status_t ReadFDEData(DataReader& dataReader, + FDEAugmentation& fdeAugmentation) + { + if (!HasData()) + return B_OK; + + // read the augmentation data block -- it is preceeded by an LEB128 + // indicating the length of the data block + uint64 length = dataReader.ReadUnsignedLEB128(0); + dataReader.Skip(length); + // TODO: Actually read what is interesting for us! + + TRACE_CFI(" %" B_PRIu64 " bytes of augmentation data\n", length); + + if (dataReader.HasOverflow()) + return B_BAD_DATA; + + return B_OK; + } + + const char* String() const + { + return fString; + } + + bool HasData() const + { + return (fFlags & CFI_AUGMENTATION_DATA) != 0; + } + +private: + const char* fString; + uint32 fFlags; +}; + + // #pragma mark - DwarfFile @@ -167,6 +291,7 @@ DwarfFile::DwarfFile() fCompilationUnits(20, true), fCurrentCompilationUnit(NULL), fUsingEHFrameSection(false), + fGCC4EHFrameSection(false), fFinished(false), fFinishError(B_OK) { @@ -227,6 +352,12 @@ DwarfFile::Load(const char* fileName) if (fDebugFrameSection == NULL) { fDebugFrameSection = fElfFile->GetSection(".eh_frame"); fUsingEHFrameSection = fDebugFrameSection != NULL; + if (fUsingEHFrameSection) { + fGCC4EHFrameSection = !fDebugFrameSection->IsWritable(); + // Crude heuristic for recognizing GCC 4 (Itanium ABI) style + // .eh_frame sections. The ones generated by GCC 2 are writable, + // the ones generated by GCC 4 aren't. + } } fDebugLocationSection = fElfFile->GetSection(".debug_loc"); fDebugPublicTypesSection = fElfFile->GetSection(".debug_pubtypes"); @@ -424,14 +555,15 @@ DwarfFile::UnwindCallFrame(CompilationUnit* unit, DataReader dataReader((uint8*)fDebugFrameSection->Data(), fDebugFrameSection->Size(), unit->AddressSize()); - uint64 previousCIE = 0; while (dataReader.BytesRemaining() > 0) { // length bool dwarf64; - off_t entryOffset = dataReader.Offset(); + TRACE_CFI_ONLY(off_t entryOffset = dataReader.Offset();) uint64 length = dataReader.ReadInitialLength(dwarf64); - TRACE_CFI("DwarfFile::UnwindCallFrame(): offset: %Lx, length: %Lx\n", - entryOffset, length); + + TRACE_CFI("DwarfFile::UnwindCallFrame(): offset: %" B_PRIdOFF + ", length: %" B_PRId64 "\n", entryOffset, length); + if (length > (uint64)dataReader.BytesRemaining()) return B_BAD_DATA; off_t lengthOffset = dataReader.Offset(); @@ -439,29 +571,49 @@ DwarfFile::UnwindCallFrame(CompilationUnit* unit, // CIE ID/CIE pointer uint64 cieID = dwarf64 ? dataReader.Read(0) : dataReader.Read(0); - - // TODO: gcc4.4+ only specifies an eh_frame by default on - // Haiku, and its format differs from the 2.x format - // we currently handle. Support for detecting and parsing - // the appropriate format needs to be added, though this can - // currently be worked around using -fno-dwarf2-cfi-asm + + // In .debug_frame ~0 indicates a CIE, in .eh_frame 0 does. if (fUsingEHFrameSection - ? cieID == 0 - : (dwarf64 - ? cieID == 0xffffffffffffffffULL - : cieID == 0xffffffff)) { + ? cieID == 0 + : (dwarf64 + ? cieID == 0xffffffffffffffffULL + : cieID == 0xffffffff)) { // this is a CIE -- skip it - previousCIE = entryOffset; } else { // this is a FDE + uint64 initialLocationOffset = dataReader.Offset(); target_addr_t initialLocation = dataReader.ReadAddress(0); target_size_t addressRange = dataReader.ReadAddress(0); if (dataReader.HasOverflow()) return B_BAD_DATA; - TRACE_CFI("location: %Lx, initial location: %Lx, address range: %Lx\n", - location, initialLocation, addressRange); + // In the GCC 4 .eh_frame initialLocation is relative to the offset + // of the address. + if (fUsingEHFrameSection && fGCC4EHFrameSection) { + // Note: We need to cast to the exact address width, since the + // initialLocation value can be (and likely is) negative. + if (dwarf64) { + initialLocation = (uint64)fDebugFrameSection->LoadAddress() + + (uint64)initialLocationOffset + + (uint64)initialLocation; + } else { + initialLocation = (uint32)fDebugFrameSection->LoadAddress() + + (uint32)initialLocationOffset + + (uint32)initialLocation; + } + } + // TODO: For GCC 2 .eh_frame sections things work differently: The + // initial locations are relocated by the runtime loader and + // afterwards point to the absolute addresses. Fortunately the + // relocations that always seem to be used are R_386_RELATIVE, so + // that the value we read from the file is already absolute + // (assuming an unchanged segment load address). + + TRACE_CFI("location: %" B_PRIx64 ", initial location: %" B_PRIx64 + ", address range: %" B_PRIx64 "\n", location, initialLocation, + addressRange); + if (location >= initialLocation && location < initialLocation + addressRange) { // This is the FDE we're looking for. @@ -470,13 +622,16 @@ DwarfFile::UnwindCallFrame(CompilationUnit* unit, if (remaining < 0) return B_BAD_DATA; - // For some reason gcc 2.95.3 doesn't write the CIE offset, but - // always the offset of this entry's CIE pointer field - // (probably even relative to the start of the data for the - // respective compilation unit). We simply assume the previous - // CIE we encountered is the right one. - if (fUsingEHFrameSection) - cieID = previousCIE; + // In .eh_frame the CIE offset is a relative back offset. + if (fUsingEHFrameSection) { + if (cieID > (uint64)lengthOffset) { + TRACE_CFI("Invalid CIE offset: %" B_PRIu64 ", max " + "possible: %" B_PRIu64 "\n", cieID, lengthOffset); + break; + } + // convert to a section relative offset + cieID = lengthOffset - cieID; + } TRACE_CFI(" found fde: length: %llu (%lld), CIE offset: %llu, " "location: %#llx, range: %#llx\n", length, remaining, cieID, @@ -502,10 +657,20 @@ DwarfFile::UnwindCallFrame(CompilationUnit* unit, } // process the CIE - error = _ParseCIE(unit, context, cieID); + CIEAugmentation cieAugmentation; + error = _ParseCIE(unit, context, cieID, cieAugmentation); if (error != B_OK) return error; + // read the FDE augmentation data (if any) + FDEAugmentation fdeAugmentation; + error = cieAugmentation.ReadFDEData(dataReader, + fdeAugmentation); + if (error != B_OK) { + TRACE_CFI(" failed to read FDE augmentation data!\n"); + return error; + } + error = context.SaveInitialRuleSet(); if (error != B_OK) return error; @@ -1379,7 +1544,7 @@ DwarfFile::_ParseLineInfo(CompilationUnit* unit) status_t DwarfFile::_ParseCIE(CompilationUnit* unit, CfaContext& context, - off_t cieOffset) + off_t cieOffset, CIEAugmentation& cieAugmentation) { if (cieOffset < 0 || cieOffset >= fDebugFrameSection->Size()) return B_BAD_DATA; @@ -1406,24 +1571,14 @@ DwarfFile::_ParseCIE(CompilationUnit* unit, CfaContext& context, } uint8 version = dataReader.Read(0); - const char* augmentation = dataReader.ReadString(); if (version != 1) { TRACE_CFI(" cie: length: %llu, version: %u -- unsupported\n", length, version); return B_UNSUPPORTED; } - if (augmentation != NULL && *augmentation != '\0') { - if (strcmp(augmentation, "eh") == 0) { - // the augmentation consists of the exception table pointer -- just - // ignore it - dataReader.ReadAddress(0); - } else { - TRACE_CFI(" cie: length: %llu, version: %u, augmentation: \"%s\" " - "-- unsupported\n", length, version, augmentation); - return B_UNSUPPORTED; - } - } + // read the augmentation string + cieAugmentation.Init(dataReader); context.SetCodeAlignment(dataReader.ReadUnsignedLEB128(0)); context.SetDataAlignment(dataReader.ReadSignedLEB128(0)); @@ -1431,8 +1586,15 @@ DwarfFile::_ParseCIE(CompilationUnit* unit, CfaContext& context, TRACE_CFI(" cie: length: %llu, version: %u, augmentation: \"%s\", " "aligment: code: %lu, data: %ld, return address reg: %lu\n", length, - version, augmentation, context.CodeAlignment(), context.DataAlignment(), - context.ReturnAddressRegister()); + version, cieAugmentation.String(), context.CodeAlignment(), + context.DataAlignment(), context.ReturnAddressRegister()); + + status_t error = cieAugmentation.Read(dataReader); + if (error != B_OK) { + TRACE_CFI(" cie: length: %llu, version: %u, augmentation: \"%s\" " + "-- unsupported\n", length, version, cieAugmentation.String()); + return error; + } if (dataReader.HasOverflow()) return B_BAD_DATA; diff --git a/src/apps/debugger/dwarf/DwarfFile.h b/src/apps/debugger/dwarf/DwarfFile.h index dd5fd73ac8..2d213809cb 100644 --- a/src/apps/debugger/dwarf/DwarfFile.h +++ b/src/apps/debugger/dwarf/DwarfFile.h @@ -1,5 +1,5 @@ /* - * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Copyright 2009-2010, Ingo Weinhold, ingo_weinhold@gmx.de. * Distributed under the terms of the MIT License. */ #ifndef DWARF_FILE_H @@ -91,13 +91,15 @@ public: private: struct ExpressionEvaluationContext; + struct FDEAugmentation; + struct CIEAugmentation; typedef DoublyLinkedList AbbreviationTableList; typedef BObjectList CompilationUnitList; private: status_t _ParseCompilationUnit(CompilationUnit* unit); - status_t _ParseDebugInfoEntry(DataReader& dataReader, + status_t _ParseDebugInfoEntry(DataReader& dataReader, AbbreviationTable* abbreviationTable, DebugInfoEntry*& _entry, bool& _endOfEntryList, int level = 0); @@ -109,7 +111,8 @@ private: status_t _ParseLineInfo(CompilationUnit* unit); status_t _ParseCIE(CompilationUnit* unit, - CfaContext& context, off_t cieOffset); + CfaContext& context, off_t cieOffset, + CIEAugmentation& cieAugmentation); status_t _ParseFrameInfoInstructions( CompilationUnit* unit, CfaContext& context, off_t instructionOffset, @@ -154,6 +157,7 @@ private: CompilationUnitList fCompilationUnits; CompilationUnit* fCurrentCompilationUnit; bool fUsingEHFrameSection; + bool fGCC4EHFrameSection; bool fFinished; status_t fFinishError; };