* 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:
parent
e2ea5a6646
commit
be649db06f
@ -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;
|
||||
|
@ -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;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user