* 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.
|
* 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;
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user