* 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.
*/
@ -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<uint64>(0) : dataReader.Read<uint32>(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<uint8>(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;

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.
*/
#ifndef DWARF_FILE_H
@ -91,13 +91,15 @@ public:
private:
struct ExpressionEvaluationContext;
struct FDEAugmentation;
struct CIEAugmentation;
typedef DoublyLinkedList<AbbreviationTable> AbbreviationTableList;
typedef BObjectList<CompilationUnit> 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;
};