* 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
This commit is contained in:
Ingo Weinhold 2010-11-02 20:21:03 +00:00
parent e2ea5a6646
commit be649db06f
2 changed files with 210 additions and 44 deletions

View File

@ -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. * 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 // #pragma mark - DwarfFile
@ -167,6 +291,7 @@ DwarfFile::DwarfFile()
fCompilationUnits(20, true), fCompilationUnits(20, true),
fCurrentCompilationUnit(NULL), fCurrentCompilationUnit(NULL),
fUsingEHFrameSection(false), fUsingEHFrameSection(false),
fGCC4EHFrameSection(false),
fFinished(false), fFinished(false),
fFinishError(B_OK) fFinishError(B_OK)
{ {
@ -227,6 +352,12 @@ DwarfFile::Load(const char* fileName)
if (fDebugFrameSection == NULL) { if (fDebugFrameSection == NULL) {
fDebugFrameSection = fElfFile->GetSection(".eh_frame"); fDebugFrameSection = fElfFile->GetSection(".eh_frame");
fUsingEHFrameSection = fDebugFrameSection != NULL; 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"); fDebugLocationSection = fElfFile->GetSection(".debug_loc");
fDebugPublicTypesSection = fElfFile->GetSection(".debug_pubtypes"); fDebugPublicTypesSection = fElfFile->GetSection(".debug_pubtypes");
@ -424,14 +555,15 @@ DwarfFile::UnwindCallFrame(CompilationUnit* unit,
DataReader dataReader((uint8*)fDebugFrameSection->Data(), DataReader dataReader((uint8*)fDebugFrameSection->Data(),
fDebugFrameSection->Size(), unit->AddressSize()); fDebugFrameSection->Size(), unit->AddressSize());
uint64 previousCIE = 0;
while (dataReader.BytesRemaining() > 0) { while (dataReader.BytesRemaining() > 0) {
// length // length
bool dwarf64; bool dwarf64;
off_t entryOffset = dataReader.Offset(); TRACE_CFI_ONLY(off_t entryOffset = dataReader.Offset();)
uint64 length = dataReader.ReadInitialLength(dwarf64); 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()) if (length > (uint64)dataReader.BytesRemaining())
return B_BAD_DATA; return B_BAD_DATA;
off_t lengthOffset = dataReader.Offset(); off_t lengthOffset = dataReader.Offset();
@ -440,28 +572,48 @@ DwarfFile::UnwindCallFrame(CompilationUnit* unit,
uint64 cieID = dwarf64 uint64 cieID = dwarf64
? dataReader.Read<uint64>(0) : dataReader.Read<uint32>(0); ? dataReader.Read<uint64>(0) : dataReader.Read<uint32>(0);
// TODO: gcc4.4+ only specifies an eh_frame by default on // In .debug_frame ~0 indicates a CIE, in .eh_frame 0 does.
// 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
if (fUsingEHFrameSection if (fUsingEHFrameSection
? cieID == 0 ? cieID == 0
: (dwarf64 : (dwarf64
? cieID == 0xffffffffffffffffULL ? cieID == 0xffffffffffffffffULL
: cieID == 0xffffffff)) { : cieID == 0xffffffff)) {
// this is a CIE -- skip it // this is a CIE -- skip it
previousCIE = entryOffset;
} else { } else {
// this is a FDE // this is a FDE
uint64 initialLocationOffset = dataReader.Offset();
target_addr_t initialLocation = dataReader.ReadAddress(0); target_addr_t initialLocation = dataReader.ReadAddress(0);
target_size_t addressRange = dataReader.ReadAddress(0); target_size_t addressRange = dataReader.ReadAddress(0);
if (dataReader.HasOverflow()) if (dataReader.HasOverflow())
return B_BAD_DATA; return B_BAD_DATA;
TRACE_CFI("location: %Lx, initial location: %Lx, address range: %Lx\n", // In the GCC 4 .eh_frame initialLocation is relative to the offset
location, initialLocation, addressRange); // 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 if (location >= initialLocation
&& location < initialLocation + addressRange) { && location < initialLocation + addressRange) {
// This is the FDE we're looking for. // This is the FDE we're looking for.
@ -470,13 +622,16 @@ DwarfFile::UnwindCallFrame(CompilationUnit* unit,
if (remaining < 0) if (remaining < 0)
return B_BAD_DATA; return B_BAD_DATA;
// For some reason gcc 2.95.3 doesn't write the CIE offset, but // In .eh_frame the CIE offset is a relative back offset.
// always the offset of this entry's CIE pointer field if (fUsingEHFrameSection) {
// (probably even relative to the start of the data for the if (cieID > (uint64)lengthOffset) {
// respective compilation unit). We simply assume the previous TRACE_CFI("Invalid CIE offset: %" B_PRIu64 ", max "
// CIE we encountered is the right one. "possible: %" B_PRIu64 "\n", cieID, lengthOffset);
if (fUsingEHFrameSection) break;
cieID = previousCIE; }
// convert to a section relative offset
cieID = lengthOffset - cieID;
}
TRACE_CFI(" found fde: length: %llu (%lld), CIE offset: %llu, " TRACE_CFI(" found fde: length: %llu (%lld), CIE offset: %llu, "
"location: %#llx, range: %#llx\n", length, remaining, cieID, "location: %#llx, range: %#llx\n", length, remaining, cieID,
@ -502,10 +657,20 @@ DwarfFile::UnwindCallFrame(CompilationUnit* unit,
} }
// process the CIE // process the CIE
error = _ParseCIE(unit, context, cieID); CIEAugmentation cieAugmentation;
error = _ParseCIE(unit, context, cieID, cieAugmentation);
if (error != B_OK) if (error != B_OK)
return error; 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(); error = context.SaveInitialRuleSet();
if (error != B_OK) if (error != B_OK)
return error; return error;
@ -1379,7 +1544,7 @@ DwarfFile::_ParseLineInfo(CompilationUnit* unit)
status_t status_t
DwarfFile::_ParseCIE(CompilationUnit* unit, CfaContext& context, DwarfFile::_ParseCIE(CompilationUnit* unit, CfaContext& context,
off_t cieOffset) off_t cieOffset, CIEAugmentation& cieAugmentation)
{ {
if (cieOffset < 0 || cieOffset >= fDebugFrameSection->Size()) if (cieOffset < 0 || cieOffset >= fDebugFrameSection->Size())
return B_BAD_DATA; return B_BAD_DATA;
@ -1406,24 +1571,14 @@ DwarfFile::_ParseCIE(CompilationUnit* unit, CfaContext& context,
} }
uint8 version = dataReader.Read<uint8>(0); uint8 version = dataReader.Read<uint8>(0);
const char* augmentation = dataReader.ReadString();
if (version != 1) { if (version != 1) {
TRACE_CFI(" cie: length: %llu, version: %u -- unsupported\n", TRACE_CFI(" cie: length: %llu, version: %u -- unsupported\n",
length, version); length, version);
return B_UNSUPPORTED; return B_UNSUPPORTED;
} }
if (augmentation != NULL && *augmentation != '\0') { // read the augmentation string
if (strcmp(augmentation, "eh") == 0) { cieAugmentation.Init(dataReader);
// 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;
}
}
context.SetCodeAlignment(dataReader.ReadUnsignedLEB128(0)); context.SetCodeAlignment(dataReader.ReadUnsignedLEB128(0));
context.SetDataAlignment(dataReader.ReadSignedLEB128(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\", " TRACE_CFI(" cie: length: %llu, version: %u, augmentation: \"%s\", "
"aligment: code: %lu, data: %ld, return address reg: %lu\n", length, "aligment: code: %lu, data: %ld, return address reg: %lu\n", length,
version, augmentation, context.CodeAlignment(), context.DataAlignment(), version, cieAugmentation.String(), context.CodeAlignment(),
context.ReturnAddressRegister()); 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()) if (dataReader.HasOverflow())
return B_BAD_DATA; return B_BAD_DATA;

View File

@ -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. * Distributed under the terms of the MIT License.
*/ */
#ifndef DWARF_FILE_H #ifndef DWARF_FILE_H
@ -91,13 +91,15 @@ public:
private: private:
struct ExpressionEvaluationContext; struct ExpressionEvaluationContext;
struct FDEAugmentation;
struct CIEAugmentation;
typedef DoublyLinkedList<AbbreviationTable> AbbreviationTableList; typedef DoublyLinkedList<AbbreviationTable> AbbreviationTableList;
typedef BObjectList<CompilationUnit> CompilationUnitList; typedef BObjectList<CompilationUnit> CompilationUnitList;
private: private:
status_t _ParseCompilationUnit(CompilationUnit* unit); status_t _ParseCompilationUnit(CompilationUnit* unit);
status_t _ParseDebugInfoEntry(DataReader& dataReader, status_t _ParseDebugInfoEntry(DataReader& dataReader,
AbbreviationTable* abbreviationTable, AbbreviationTable* abbreviationTable,
DebugInfoEntry*& _entry, DebugInfoEntry*& _entry,
bool& _endOfEntryList, int level = 0); bool& _endOfEntryList, int level = 0);
@ -109,7 +111,8 @@ private:
status_t _ParseLineInfo(CompilationUnit* unit); status_t _ParseLineInfo(CompilationUnit* unit);
status_t _ParseCIE(CompilationUnit* unit, status_t _ParseCIE(CompilationUnit* unit,
CfaContext& context, off_t cieOffset); CfaContext& context, off_t cieOffset,
CIEAugmentation& cieAugmentation);
status_t _ParseFrameInfoInstructions( status_t _ParseFrameInfoInstructions(
CompilationUnit* unit, CfaContext& context, CompilationUnit* unit, CfaContext& context,
off_t instructionOffset, off_t instructionOffset,
@ -154,6 +157,7 @@ private:
CompilationUnitList fCompilationUnits; CompilationUnitList fCompilationUnits;
CompilationUnit* fCurrentCompilationUnit; CompilationUnit* fCurrentCompilationUnit;
bool fUsingEHFrameSection; bool fUsingEHFrameSection;
bool fGCC4EHFrameSection;
bool fFinished; bool fFinished;
status_t fFinishError; status_t fFinishError;
}; };