/*
* Copyright 2009-2012, Ingo Weinhold, ingo_weinhold@gmx.de.
* Copyright 2012-2014, Rene Gollent, rene@gollent.com.
* Distributed under the terms of the MIT License.
*/
#include "DwarfFile.h"
#include <algorithm>
#include <new>
#include <AutoDeleter.h>
#include <Entry.h>
#include <FindDirectory.h>
#include <Path.h>
#include <PathFinder.h>
#include "AttributeClasses.h"
#include "AttributeValue.h"
#include "AbbreviationTable.h"
#include "CfaContext.h"
#include "CompilationUnit.h"
#include "DataReader.h"
#include "DwarfExpressionEvaluator.h"
#include "DwarfTargetInterface.h"
#include "ElfFile.h"
#include "TagNames.h"
#include "TargetAddressRangeList.h"
#include "Tracing.h"
#include "Variant.h"
// #pragma mark - AutoSectionPutter
class AutoSectionPutter {
public:
AutoSectionPutter(ElfFile* elfFile, ElfSection* elfSection)
:
fElfFile(elfFile),
fElfSection(elfSection)
{
}
~AutoSectionPutter()
{
if (fElfSection != NULL)
fElfFile->PutSection(fElfSection);
}
private:
ElfFile* fElfFile;
ElfSection* fElfSection;
};
// #pragma mark - ExpressionEvaluationContext
struct DwarfFile::ExpressionEvaluationContext
: DwarfExpressionEvaluationContext {
public:
ExpressionEvaluationContext(DwarfFile* file, CompilationUnit* unit,
uint8 addressSize, DIESubprogram* subprogramEntry,
const DwarfTargetInterface* targetInterface,
target_addr_t instructionPointer, target_addr_t objectPointer,
bool hasObjectPointer, target_addr_t framePointer,
target_addr_t relocationDelta)
:
DwarfExpressionEvaluationContext(targetInterface, addressSize,
relocationDelta),
fFile(file),
fUnit(unit),
fSubprogramEntry(subprogramEntry),
fInstructionPointer(instructionPointer),
fObjectPointer(objectPointer),
fHasObjectPointer(hasObjectPointer),
fFramePointer(framePointer),
fFrameBasePointer(0),
fFrameBaseEvaluated(false)
{
}
virtual bool GetObjectAddress(target_addr_t& _address)
{
if (!fHasObjectPointer)
return false;
_address = fObjectPointer;
return true;
}
virtual bool GetFrameAddress(target_addr_t& _address)
{
if (fFramePointer == 0)
return false;
_address = fFramePointer;
return true;
}
virtual bool GetFrameBaseAddress(target_addr_t& _address)
{
if (fFrameBaseEvaluated) {
if (fFrameBasePointer == 0)
return false;
_address = fFrameBasePointer;
return true;
}
// set flag already to prevent recursion for a buggy expression
fFrameBaseEvaluated = true;
// get the subprogram's frame base location
if (fSubprogramEntry == NULL)
return false;
const LocationDescription* location = fSubprogramEntry->FrameBase();
if (!location->IsValid())
return false;
// get the expression
const void* expression;
off_t expressionLength;
status_t error = fFile->_GetLocationExpression(fUnit, location,
fInstructionPointer, expression, expressionLength);
if (error != B_OK)
return false;
// evaluate the expression
DwarfExpressionEvaluator evaluator(this);
error = evaluator.Evaluate(expression, expressionLength,
fFrameBasePointer);
if (error != B_OK)
return false;
TRACE_EXPR(" -> frame base: %" B_PRIx64 "\n", fFrameBasePointer);
_address = fFrameBasePointer;
return true;
}
virtual bool GetTLSAddress(target_addr_t localAddress,
target_addr_t& _address)
{
// TODO:...
return false;
}
virtual status_t GetCallTarget(uint64 offset, uint8 refType,
const void*& _block, off_t& _size)
{
// resolve the entry
DebugInfoEntry* entry = fFile->_ResolveReference(fUnit, offset, refType);
if (entry == NULL)
return B_ENTRY_NOT_FOUND;
// get the location description
LocationDescription* location = entry->GetLocationDescription();
if (location == NULL || !location->IsValid()) {
_block = NULL;
_size = 0;
return B_OK;
}
// get the expression
return fFile->_GetLocationExpression(fUnit, location,
fInstructionPointer, _block, _size);
}
private:
DwarfFile* fFile;
CompilationUnit* fUnit;
DIESubprogram* fSubprogramEntry;
target_addr_t fInstructionPointer;
target_addr_t fObjectPointer;
bool fHasObjectPointer;
target_addr_t fFramePointer;
target_addr_t fFrameBasePointer;
bool fFrameBaseEvaluated;
};
// #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,
};
// encodings for CFI_AUGMENTATION_ADDRESS_POINTER_FORMAT
enum {
CFI_ADDRESS_FORMAT_ABSOLUTE = 0x00,
CFI_ADDRESS_FORMAT_UNSIGNED_LEB128 = 0x01,
CFI_ADDRESS_FORMAT_UNSIGNED_16 = 0x02,
CFI_ADDRESS_FORMAT_UNSIGNED_32 = 0x03,
CFI_ADDRESS_FORMAT_UNSIGNED_64 = 0x04,
CFI_ADDRESS_FORMAT_SIGNED = 0x08,
CFI_ADDRESS_FORMAT_SIGNED_LEB128 =
CFI_ADDRESS_FORMAT_UNSIGNED_LEB128 | CFI_ADDRESS_FORMAT_SIGNED,
CFI_ADDRESS_FORMAT_SIGNED_16 =
CFI_ADDRESS_FORMAT_UNSIGNED_16 | CFI_ADDRESS_FORMAT_SIGNED,
CFI_ADDRESS_FORMAT_SIGNED_32 =
CFI_ADDRESS_FORMAT_UNSIGNED_32 | CFI_ADDRESS_FORMAT_SIGNED,
CFI_ADDRESS_FORMAT_SIGNED_64 =
CFI_ADDRESS_FORMAT_UNSIGNED_64 | CFI_ADDRESS_FORMAT_SIGNED
};
enum {
CFI_ADDRESS_TYPE_PC_RELATIVE = 0x10,
CFI_ADDRESS_TYPE_TEXT_RELATIVE = 0x20,
CFI_ADDRESS_TYPE_DATA_RELATIVE = 0x30,
CFI_ADDRESS_TYPE_FUNCTION_RELATIVE = 0x40,
CFI_ADDRESS_TYPE_ALIGNED = 0x50,
CFI_ADDRESS_TYPE_INDIRECT = 0x80
};
struct DwarfFile::CIEAugmentation {
CIEAugmentation()
:
fString(NULL),
fFlags(0),
fAddressEncoding(CFI_ADDRESS_FORMAT_ABSOLUTE)
{
// we default to absolute address format since that corresponds
// to the DWARF standard for .debug_frame. In gcc's case, however,
// .eh_frame will generally override that via augmentation 'R'
}
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;
// read the augmentation data block -- it is preceeded by an
// LEB128 indicating the length of the data block
uint64 length = dataReader.ReadUnsignedLEB128(0);
uint64 remaining = length;
// let's see what data we have to expect
TRACE_CFI(" %" B_PRIu64 " bytes of augmentation data\n", length);
while (*string != '\0') {
switch (*string) {
case 'L':
fFlags |= CFI_AUGMENTATION_LANGUAGE_SPECIFIC_DATA;
dataReader.Read<char>(0);
--remaining;
break;
case 'P':
{
char tempEncoding = fAddressEncoding;
fAddressEncoding = dataReader.Read<char>(0);
off_t offset = dataReader.Offset();
ReadEncodedAddress(dataReader, NULL, NULL, true);
fAddressEncoding = tempEncoding;
remaining -= dataReader.Offset() - offset + 1;
break;
}
case 'R':
fFlags |= CFI_AUGMENTATION_ADDRESS_POINTER_FORMAT;
fAddressEncoding = dataReader.Read<char>(0);
--remaining;
break;
default:
WARNING("Encountered unsupported augmentation '%c' "
" while parsing CIE augmentation string %s\n",
*string, fString);
return B_UNSUPPORTED;
}
string++;
}
// we should have read through all of the augmentation data
// at this point, if not, something is wrong.
if (remaining != 0 || dataReader.HasOverflow()) {
WARNING("Error while reading CIE Augmentation, expected "
"%" B_PRIu64 " bytes of augmentation data, but read "
"%" B_PRIu64 " bytes.\n", length, length - remaining);
return B_BAD_DATA;
}
return B_OK;
}
// nothing to do
if (strcmp(fString, "eh") == 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;
}
bool HasFDEAddressFormat() const
{
return (fFlags & CFI_AUGMENTATION_ADDRESS_POINTER_FORMAT) != 0;
}
target_addr_t FDEAddressOffset(ElfFile* file,
ElfSection* debugFrameSection) const
{
switch (FDEAddressType()) {
case CFI_ADDRESS_FORMAT_ABSOLUTE:
TRACE_CFI("FDE address format: absolute, ");
return 0;
case CFI_ADDRESS_TYPE_PC_RELATIVE:
TRACE_CFI("FDE address format: PC relative, ");
return debugFrameSection->LoadAddress();
case CFI_ADDRESS_TYPE_FUNCTION_RELATIVE:
TRACE_CFI("FDE address format: function relative, ");
return 0;
case CFI_ADDRESS_TYPE_TEXT_RELATIVE:
TRACE_CFI("FDE address format: text relative, ");
return file->TextSegment()->LoadAddress();
case CFI_ADDRESS_TYPE_DATA_RELATIVE:
TRACE_CFI("FDE address format: data relative, ");
return file->DataSegment()->LoadAddress();
case CFI_ADDRESS_TYPE_ALIGNED:
case CFI_ADDRESS_TYPE_INDIRECT:
TRACE_CFI("FDE address format: UNIMPLEMENTED, ");
// TODO: implement
// -- note: type indirect is currently not generated
return 0;
}
return 0;
}
uint8 FDEAddressType() const
{
return fAddressEncoding & 0x70;
}
target_addr_t ReadEncodedAddress(DataReader &reader,
ElfFile* file, ElfSection* debugFrameSection,
bool valueOnly = false) const
{
target_addr_t address = valueOnly ? 0 : FDEAddressOffset(file,
debugFrameSection);
switch (fAddressEncoding & 0x0f) {
case CFI_ADDRESS_FORMAT_ABSOLUTE:
address += reader.ReadAddress(0);
TRACE_CFI(" target address: %" B_PRId64 "\n", address);
break;
case CFI_ADDRESS_FORMAT_UNSIGNED_LEB128:
address += reader.ReadUnsignedLEB128(0);
TRACE_CFI(" unsigned LEB128: %" B_PRId64 "\n", address);
break;
case CFI_ADDRESS_FORMAT_SIGNED_LEB128:
address += reader.ReadSignedLEB128(0);
TRACE_CFI(" signed LEB128: %" B_PRId64 "\n", address);
break;
case CFI_ADDRESS_FORMAT_UNSIGNED_16:
address += reader.Read<uint16>(0);
TRACE_CFI(" unsigned 16-bit: %" B_PRId64 "\n", address);
break;
case CFI_ADDRESS_FORMAT_SIGNED_16:
address += reader.Read<int16>(0);
TRACE_CFI(" signed 16-bit: %" B_PRId64 "\n", address);
break;
case CFI_ADDRESS_FORMAT_UNSIGNED_32:
address += reader.Read<uint32>(0);
TRACE_CFI(" unsigned 32-bit: %" B_PRId64 "\n", address);
break;
case CFI_ADDRESS_FORMAT_SIGNED_32:
address += reader.Read<int32>(0);
TRACE_CFI(" signed 32-bit: %" B_PRId64 "\n", address);
break;
case CFI_ADDRESS_FORMAT_UNSIGNED_64:
address += reader.Read<uint64>(0);
TRACE_CFI(" unsigned 64-bit: %" B_PRId64 "\n", address);
break;
case CFI_ADDRESS_FORMAT_SIGNED_64:
address += reader.Read<int64>(0);
TRACE_CFI(" signed 64-bit: %" B_PRId64 "\n", address);
break;
}
return address;
}
private:
const char* fString;
uint32 fFlags;
int8 fAddressEncoding;
};
// #pragma mark - FDELookupInfo
struct DwarfFile::FDELookupInfo {
public:
FDELookupInfo(target_addr_t start, target_addr_t end,
uint64 fdeOffset, uint64 cieOffset, bool ehFrame)
:
start(start),
end(end),
fdeOffset(fdeOffset),
cieOffset(cieOffset),
ehFrame(ehFrame)
{
}
static int CompareFDEInfos(const FDELookupInfo* a, const FDELookupInfo* b)
{
if (a->start < b->start)
return -1;
else if (a->start > b->start)
return 1;
return 0;
}
inline bool ContainsAddress(target_addr_t address) const
{
return address >= start && address < end;
}
target_addr_t start;
target_addr_t end;
uint64 fdeOffset;
uint64 cieOffset;
bool ehFrame;
};
// #pragma mark - DwarfFile
DwarfFile::DwarfFile()
:
fName(NULL),
fAlternateName(NULL),
fElfFile(NULL),
fAlternateElfFile(NULL),
fDebugInfoSection(NULL),
fDebugAbbrevSection(NULL),
fDebugStringSection(NULL),
fDebugRangesSection(NULL),
fDebugLineSection(NULL),
fDebugFrameSection(NULL),
fEHFrameSection(NULL),
fDebugLocationSection(NULL),
fDebugPublicTypesSection(NULL),
fDebugTypesSection(NULL),
fCompilationUnits(20, true),
fTypeUnits(),
fDebugFrameInfos(100, true),
fEHFrameInfos(100, true),
fTypesSectionRequired(false),
fFinished(false),
fItaniumEHFrameFormat(false),
fFinishError(B_OK)
{
}
DwarfFile::~DwarfFile()
{
while (AbbreviationTable* table = fAbbreviationTables.RemoveHead())
delete table;
if (fElfFile != NULL) {
ElfFile* debugInfoFile = fAlternateElfFile != NULL
? fAlternateElfFile : fElfFile;
debugInfoFile->PutSection(fDebugInfoSection);
debugInfoFile->PutSection(fDebugAbbrevSection);
debugInfoFile->PutSection(fDebugStringSection);
debugInfoFile->PutSection(fDebugRangesSection);
debugInfoFile->PutSection(fDebugLineSection);
debugInfoFile->PutSection(fDebugFrameSection);
fElfFile->PutSection(fEHFrameSection);
debugInfoFile->PutSection(fDebugLocationSection);
debugInfoFile->PutSection(fDebugPublicTypesSection);
delete fElfFile;
delete fAlternateElfFile;
}
TypeUnitTableEntry* entry = fTypeUnits.Clear(true);
while (entry != NULL) {
TypeUnitTableEntry* nextEntry = entry->next;
delete entry;
entry = nextEntry;
}
free(fName);
free(fAlternateName);
}
status_t
DwarfFile::StartLoading(const char* fileName, BString& _requiredExternalFile)
{
fName = strdup(fileName);
if (fName == NULL)
return B_NO_MEMORY;
status_t error = fTypeUnits.Init();
if (error != B_OK)
return error;
// load the ELF file
fElfFile = new(std::nothrow) ElfFile;
if (fElfFile == NULL)
return B_NO_MEMORY;
error = fElfFile->Init(fileName);
if (error != B_OK)
return error;
return _LocateDebugInfo(_requiredExternalFile);
}
status_t
DwarfFile::Load(uint8 addressSize, const BString& externalInfoFilePath)
{
status_t error = B_OK;
if (fDebugInfoSection == NULL) {
BString path;
error = _LocateDebugInfo(path, externalInfoFilePath.IsEmpty()
? NULL : externalInfoFilePath.String());
if (error != B_OK)
return error;
}
ElfFile* debugInfoFile = fAlternateElfFile != NULL
? fAlternateElfFile : fElfFile;
// non mandatory sections
fDebugStringSection = debugInfoFile->GetSection(".debug_str");
fDebugRangesSection = debugInfoFile->GetSection(".debug_ranges");
fDebugLineSection = debugInfoFile->GetSection(".debug_line");
fDebugFrameSection = debugInfoFile->GetSection(".debug_frame");
if (fDebugFrameSection != NULL) {
error = _ParseFrameSection(fDebugFrameSection, addressSize, false,
fDebugFrameInfos);
if (error != B_OK)
return error;
}
// .eh_frame doesn't appear to get copied into separate debug
// info files properly, therefore always use it off the main
// executable image
if (fEHFrameSection == NULL)
fEHFrameSection = fElfFile->GetSection(".eh_frame");
if (fEHFrameSection != NULL) {
error = _ParseFrameSection(fEHFrameSection, addressSize, true,
fEHFrameInfos);
if (error != B_OK)
return error;
}
fDebugLocationSection = debugInfoFile->GetSection(".debug_loc");
fDebugPublicTypesSection = debugInfoFile->GetSection(".debug_pubtypes");
if (fDebugInfoSection == NULL) {
fFinished = true;
return B_OK;
}
error = _ParseDebugInfoSection();
if (error != B_OK)
return error;
if (fTypesSectionRequired) {
fDebugTypesSection = debugInfoFile->GetSection(".debug_types");
if (fDebugTypesSection == NULL) {
WARNING(".debug_types section required but missing.\n");
return B_BAD_DATA;
}
error = _ParseTypesSection();
if (error != B_OK)
return error;
}
return B_OK;
}
status_t
DwarfFile::FinishLoading()
{
if (fFinished)
return B_OK;
if (fFinishError != B_OK)
return fFinishError;
status_t error;
for (TypeUnitTable::Iterator it = fTypeUnits.GetIterator();
TypeUnitTableEntry* entry = it.Next();) {
error = _FinishUnit(entry->unit);
if (error != B_OK)
return fFinishError = error;
}
for (int32 i = 0; CompilationUnit* unit = fCompilationUnits.ItemAt(i);
i++) {
error = _FinishUnit(unit);
if (error != B_OK)
return fFinishError = error;
}
_ParsePublicTypesInfo();
fFinished = true;
return B_OK;
}
int32
DwarfFile::CountCompilationUnits() const
{
return fCompilationUnits.CountItems();
}
CompilationUnit*
DwarfFile::CompilationUnitAt(int32 index) const
{
return fCompilationUnits.ItemAt(index);
}
CompilationUnit*
DwarfFile::CompilationUnitForDIE(const DebugInfoEntry* entry) const
{
// find the root of the tree the entry lives in
while (entry != NULL && entry->Parent() != NULL)
entry = entry->Parent();
// that should be the compilation unit entry
const DIECompileUnitBase* unitEntry
= dynamic_cast<const DIECompileUnitBase*>(entry);
if (unitEntry == NULL)
return NULL;
// find the compilation unit
for (int32 i = 0; CompilationUnit* unit = fCompilationUnits.ItemAt(i);
i++) {
if (unit->UnitEntry() == unitEntry)
return unit;
}
return NULL;
}
TargetAddressRangeList*
DwarfFile::ResolveRangeList(CompilationUnit* unit, uint64 offset) const
{
if (unit == NULL || fDebugRangesSection == NULL)
return NULL;
if (offset >= (uint64)fDebugRangesSection->Size())
return NULL;
TargetAddressRangeList* ranges = new(std::nothrow) TargetAddressRangeList;
if (ranges == NULL) {
ERROR("Out of memory.\n");
return NULL;
}
BReference<TargetAddressRangeList> rangesReference(ranges, true);
target_addr_t baseAddress = unit->AddressRangeBase();
target_addr_t maxAddress = unit->MaxAddress();
DataReader dataReader((uint8*)fDebugRangesSection->Data() + offset,
fDebugRangesSection->Size() - offset, unit->AddressSize());
while (true) {
target_addr_t start = dataReader.ReadAddress(0);
target_addr_t end = dataReader.ReadAddress(0);
if (dataReader.HasOverflow())
return NULL;
if (start == 0 && end == 0)
break;
if (start == maxAddress) {
baseAddress = end;
continue;
}
if (start == end)
continue;
if (!ranges->AddRange(baseAddress + start, end - start)) {
ERROR("Out of memory.\n");
return NULL;
}
}
return rangesReference.Detach();
}
status_t
DwarfFile::UnwindCallFrame(CompilationUnit* unit, uint8 addressSize,
DIESubprogram* subprogramEntry, target_addr_t location,
const DwarfTargetInterface* inputInterface,
DwarfTargetInterface* outputInterface, target_addr_t& _framePointer)
{
FDELookupInfo* info = _GetContainingFDEInfo(location);
if (info == NULL)
return B_ENTRY_NOT_FOUND;
return _UnwindCallFrame(unit, addressSize, subprogramEntry, location, info,
inputInterface, outputInterface, _framePointer);
}
status_t
DwarfFile::EvaluateExpression(CompilationUnit* unit, uint8 addressSize,
DIESubprogram* subprogramEntry, const void* expression,
off_t expressionLength, const DwarfTargetInterface* targetInterface,
target_addr_t instructionPointer, target_addr_t framePointer,
target_addr_t valueToPush, bool pushValue, target_addr_t& _result)
{
ExpressionEvaluationContext context(this, unit, addressSize,
subprogramEntry, targetInterface, instructionPointer, 0, false,
framePointer, 0);
DwarfExpressionEvaluator evaluator(&context);
if (pushValue && evaluator.Push(valueToPush) != B_OK)
return B_NO_MEMORY;
return evaluator.Evaluate(expression, expressionLength, _result);
}
status_t
DwarfFile::ResolveLocation(CompilationUnit* unit, uint8 addressSize,
DIESubprogram* subprogramEntry, const LocationDescription* location,
const DwarfTargetInterface* targetInterface,
target_addr_t instructionPointer, target_addr_t objectPointer,
bool hasObjectPointer, target_addr_t framePointer,
target_addr_t relocationDelta, ValueLocation& _result)
{
// get the expression
const void* expression;
off_t expressionLength;
status_t error = _GetLocationExpression(unit, location, instructionPointer,
expression, expressionLength);
if (error != B_OK)
return error;
// evaluate it
ExpressionEvaluationContext context(this, unit, addressSize,
subprogramEntry, targetInterface, instructionPointer, objectPointer,
hasObjectPointer, framePointer, relocationDelta);
DwarfExpressionEvaluator evaluator(&context);
return evaluator.EvaluateLocation(expression, expressionLength,
_result);
}
status_t
DwarfFile::EvaluateConstantValue(CompilationUnit* unit, uint8 addressSize,
DIESubprogram* subprogramEntry, const ConstantAttributeValue* value,
const DwarfTargetInterface* targetInterface,
target_addr_t instructionPointer, target_addr_t framePointer,
BVariant& _result)
{
if (!value->IsValid())
return B_BAD_VALUE;
switch (value->attributeClass) {
case ATTRIBUTE_CLASS_CONSTANT:
_result.SetTo(value->constant);
return B_OK;
case ATTRIBUTE_CLASS_STRING:
_result.SetTo(value->string);
return B_OK;
case ATTRIBUTE_CLASS_BLOCK:
{
target_addr_t result;
status_t error = EvaluateExpression(unit, addressSize,
subprogramEntry, value->block.data, value->block.length,
targetInterface, instructionPointer, framePointer, 0, false,
result);
if (error != B_OK)
return error;
_result.SetTo(result);
return B_OK;
}
default:
return B_BAD_VALUE;
}
}
status_t
DwarfFile::EvaluateDynamicValue(CompilationUnit* unit, uint8 addressSize,
DIESubprogram* subprogramEntry, const DynamicAttributeValue* value,
const DwarfTargetInterface* targetInterface,
target_addr_t instructionPointer, target_addr_t framePointer,
BVariant& _result, DIEType** _type)
{
if (!value->IsValid())
return B_BAD_VALUE;
DIEType* dummyType;
if (_type == NULL)
_type = &dummyType;
switch (value->attributeClass) {
case ATTRIBUTE_CLASS_CONSTANT:
_result.SetTo(value->constant);
*_type = NULL;
return B_OK;
case ATTRIBUTE_CLASS_REFERENCE:
{
// TODO: The specs are a bit fuzzy on this one: "the value is a
// reference to another entity whose value is the value of the
// attribute". Supposedly that also means e.g. if the referenced
// entity is a variable, we should read the value of that variable.
// ATM we only check for the types that can have a DW_AT_const_value
// attribute and evaluate it, if present.
DebugInfoEntry* entry = value->reference;
if (entry == NULL)
return B_BAD_VALUE;
const ConstantAttributeValue* constantValue = NULL;
DIEType* type = NULL;
switch (entry->Tag()) {
case DW_TAG_constant:
{
DIEConstant* constantEntry
= dynamic_cast<DIEConstant*>(entry);
constantValue = constantEntry->ConstValue();
type = constantEntry->GetType();
break;
}
case DW_TAG_enumerator:
constantValue = dynamic_cast<DIEEnumerator*>(entry)
->ConstValue();
if (DIEEnumerationType* enumerationType
= dynamic_cast<DIEEnumerationType*>(
entry->Parent())) {
type = enumerationType->GetType();
}
break;
case DW_TAG_formal_parameter:
{
DIEFormalParameter* parameterEntry
= dynamic_cast<DIEFormalParameter*>(entry);
constantValue = parameterEntry->ConstValue();
type = parameterEntry->GetType();
break;
}
case DW_TAG_template_value_parameter:
{
DIETemplateValueParameter* parameterEntry
= dynamic_cast<DIETemplateValueParameter*>(entry);
constantValue = parameterEntry->ConstValue();
type = parameterEntry->GetType();
break;
}
case DW_TAG_variable:
{
DIEVariable* variableEntry
= dynamic_cast<DIEVariable*>(entry);
constantValue = variableEntry->ConstValue();
type = variableEntry->GetType();
break;
}
default:
return B_BAD_VALUE;
}
if (constantValue == NULL || !constantValue->IsValid())
return B_BAD_VALUE;
status_t error = EvaluateConstantValue(unit, addressSize,
subprogramEntry, constantValue, targetInterface,
instructionPointer, framePointer, _result);
if (error != B_OK)
return error;
*_type = type;
return B_OK;
}
case ATTRIBUTE_CLASS_BLOCK:
{
target_addr_t result;
status_t error = EvaluateExpression(unit, addressSize,
subprogramEntry, value->block.data, value->block.length,
targetInterface, instructionPointer, framePointer, 0, false,
result);
if (error != B_OK)
return error;
_result.SetTo(result);
*_type = NULL;
return B_OK;
}
default:
return B_BAD_VALUE;
}
}
status_t
DwarfFile::_ParseDebugInfoSection()
{
// iterate through the debug info section
DataReader dataReader(fDebugInfoSection->Data(),
fDebugInfoSection->Size(), 4);
// address size doesn't matter here
while (dataReader.HasData()) {
off_t unitHeaderOffset = dataReader.Offset();
bool dwarf64;
uint64 unitLength = dataReader.ReadInitialLength(dwarf64);
off_t unitLengthOffset = dataReader.Offset();
// the unitLength starts here
if (unitLengthOffset + unitLength
> (uint64)fDebugInfoSection->Size()) {
WARNING("\"%s\": Invalid compilation unit length.\n", fName);
break;
}
int version = dataReader.Read<uint16>(0);
off_t abbrevOffset = dwarf64
? dataReader.Read<uint64>(0)
: dataReader.Read<uint32>(0);
uint8 addressSize = dataReader.Read<uint8>(0);
if (dataReader.HasOverflow()) {
WARNING("\"%s\": Unexpected end of data in compilation unit "
"header.\n", fName);
break;
}
TRACE_DIE("DWARF%d compilation unit: version %d, length: %" B_PRIu64
", abbrevOffset: %" B_PRIdOFF ", address size: %d\n",
dwarf64 ? 64 : 32, version, unitLength, abbrevOffset, addressSize);
if (version < 2 || version > 4) {
WARNING("\"%s\": Unsupported compilation unit version: %d\n",
fName, version);
break;
}
if (addressSize != 4 && addressSize != 8) {
WARNING("\"%s\": Unsupported address size: %d\n", fName,
addressSize);
break;
}
dataReader.SetAddressSize(addressSize);
off_t unitContentOffset = dataReader.Offset();
// create a compilation unit object
CompilationUnit* unit = new(std::nothrow) CompilationUnit(
unitHeaderOffset, unitContentOffset,
unitLength + (unitLengthOffset - unitHeaderOffset),
abbrevOffset, addressSize, dwarf64);
if (unit == NULL || !fCompilationUnits.AddItem(unit)) {
delete unit;
return B_NO_MEMORY;
}
// parse the debug info for the unit
status_t error = _ParseCompilationUnit(unit);
if (error != B_OK)
return error;
dataReader.SeekAbsolute(unitLengthOffset + unitLength);
}
return B_OK;
}
status_t
DwarfFile::_ParseTypesSection()
{
DataReader dataReader(fDebugTypesSection->Data(),
fDebugTypesSection->Size(), 4);
while (dataReader.HasData()) {
off_t unitHeaderOffset = dataReader.Offset();
bool dwarf64;
uint64 unitLength = dataReader.ReadInitialLength(dwarf64);
off_t unitLengthOffset = dataReader.Offset();
// the unitLength starts here
if (unitLengthOffset + unitLength
> (uint64)fDebugTypesSection->Size()) {
WARNING("Invalid type unit length, offset %#" B_PRIx64 ".\n",
unitHeaderOffset);
break;
}
int version = dataReader.Read<uint16>(0);
off_t abbrevOffset = dwarf64
? dataReader.Read<uint64>(0)
: dataReader.Read<uint32>(0);
uint8 addressSize = dataReader.Read<uint8>(0);
if (dataReader.HasOverflow()) {
WARNING("Unexpected end of data in type unit header at %#"
B_PRIx64 ".\n", unitHeaderOffset);
break;
}
dataReader.SetAddressSize(addressSize);
uint64 signature = dataReader.Read<uint64>(0);
off_t typeOffset = dwarf64
? dataReader.Read<uint64>(0)
: dataReader.Read<uint32>(0);
off_t unitContentOffset = dataReader.Offset();
TRACE_DIE("DWARF%d type unit: version %d, length: %" B_PRIu64
", abbrevOffset: %" B_PRIdOFF ", address size: %d, "
"signature: %#" B_PRIx64 ", type offset: %" B_PRIu64 "\n",
dwarf64 ? 64 : 32, version, unitLength, abbrevOffset, addressSize,
signature, typeOffset);
if (version > 4) {
WARNING("\"%s\": Unsupported type unit version: %d\n",
fName, version);
break;
}
if (addressSize != 4 && addressSize != 8) {
WARNING("\"%s\": Unsupported address size: %d\n", fName,
addressSize);
break;
}
// create a type unit object
TypeUnit* unit = new(std::nothrow) TypeUnit(
unitHeaderOffset, unitContentOffset,
unitLength + (unitLengthOffset - unitHeaderOffset),
abbrevOffset, typeOffset, addressSize, signature, dwarf64);
if (unit == NULL)
return B_NO_MEMORY;
// parse the debug info for the unit
status_t error = _ParseTypeUnit(unit);
if (error != B_OK)
return error;
// TODO: it should theoretically never happen that we get a duplicate,
// but it wouldn't hurt to check since that situation would potentially
// be problematic.
if (fTypeUnits.Lookup(signature) == NULL) {
TypeUnitTableEntry* entry = new(std::nothrow)
TypeUnitTableEntry(signature, unit);
if (entry == NULL)
return B_NO_MEMORY;
fTypeUnits.Insert(entry);
}
dataReader.SeekAbsolute(unitLengthOffset + unitLength);
}
return B_OK;
}
status_t
DwarfFile::_ParseFrameSection(ElfSection* section, uint8 addressSize,
bool ehFrame, FDEInfoList& infos)
{
if (ehFrame) {
fItaniumEHFrameFormat = section->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.
}
DataReader dataReader((uint8*)section->Data(),
section->Size(), addressSize);
while (dataReader.BytesRemaining() > 0) {
// length
bool dwarf64;
off_t entryOffset = dataReader.Offset();
uint64 length = dataReader.ReadInitialLength(dwarf64);
TRACE_CFI("DwarfFile::_ParseFrameSection(): offset: %" B_PRIdOFF
", length: %" B_PRId64 "\n", entryOffset, length);
if (length > (uint64)dataReader.BytesRemaining())
return B_BAD_DATA;
off_t lengthOffset = dataReader.Offset();
// CIE ID/CIE pointer
uint64 cieID = dwarf64
? dataReader.Read<uint64>(0) : dataReader.Read<uint32>(0);
// In .debug_frame ~0 indicates a CIE, in .eh_frame 0 does.
if (ehFrame
? cieID == 0
: (dwarf64
? cieID == 0xffffffffffffffffULL
: cieID == 0xffffffff)) {
// this is a CIE -- skip it
} else {
// this is a FDE
uint64 initialLocationOffset = dataReader.Offset();
// In .eh_frame the CIE offset is a relative back offset.
if (ehFrame) {
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;
}
CfaContext context;
CIEAugmentation cieAugmentation;
// when using .eh_frame format, we need to parse the CIE's
// augmentation up front in order to know how the FDE's addresses
// will be represented
DataReader cieReader;
off_t cieRemaining;
status_t error = _ParseCIEHeader(section, ehFrame, NULL,
addressSize, context, cieID, cieAugmentation, cieReader,
cieRemaining);
if (error != B_OK)
return error;
if (cieReader.HasOverflow())
return B_BAD_DATA;
if (cieRemaining < 0)
return B_BAD_DATA;
target_addr_t initialLocation = cieAugmentation.ReadEncodedAddress(
dataReader, fElfFile, section);
target_addr_t addressRange = cieAugmentation.ReadEncodedAddress(
dataReader, fElfFile, section, true);
if (dataReader.HasOverflow())
return B_BAD_DATA;
if ((cieAugmentation.FDEAddressType()
& CFI_ADDRESS_TYPE_PC_RELATIVE) != 0) {
initialLocation += initialLocationOffset;
}
// for unknown reasons, the debug frame sections generated by gcc
// sometimes contain duplicates at different offsets within the
// section. In such a case, simply skip the duplicates.
FDELookupInfo* temp = _GetContainingFDEInfo(initialLocation,
infos);
if (temp == NULL) {
FDELookupInfo* info = new(std::nothrow)FDELookupInfo(
initialLocation, initialLocation + addressRange - 1,
entryOffset, cieID, ehFrame);
if (info == NULL)
return B_NO_MEMORY;
ObjectDeleter<FDELookupInfo> infoDeleter(info);
if (!infos.BinaryInsert(info, FDELookupInfo::CompareFDEInfos))
return B_NO_MEMORY;
infoDeleter.Detach();
}
}
dataReader.SeekAbsolute(lengthOffset + length);
}
return B_OK;
}
status_t
DwarfFile::_ParseCompilationUnit(CompilationUnit* unit)
{
AbbreviationTable* abbreviationTable;
status_t error = _GetAbbreviationTable(unit->AbbreviationOffset(),
abbreviationTable);
if (error != B_OK)
return error;
unit->SetAbbreviationTable(abbreviationTable);
DataReader dataReader(
(const uint8*)fDebugInfoSection->Data() + unit->ContentOffset(),
unit->ContentSize(), unit->AddressSize());
DebugInfoEntry* entry;
bool endOfEntryList;
error = _ParseDebugInfoEntry(dataReader, unit, abbreviationTable, entry,
endOfEntryList);
if (error != B_OK)
return error;
DIECompileUnitBase* unitEntry = dynamic_cast<DIECompileUnitBase*>(entry);
if (unitEntry == NULL) {
WARNING("No compilation unit entry in .debug_info section.\n");
return B_BAD_DATA;
}
unit->SetUnitEntry(unitEntry);
TRACE_DIE_ONLY(
TRACE_DIE("remaining bytes in unit: %" B_PRIdOFF "\n",
dataReader.BytesRemaining());
if (dataReader.HasData()) {
TRACE_DIE(" ");
while (dataReader.HasData())
TRACE_DIE("%02x", dataReader.Read<uint8>(0));
TRACE_DIE("\n");
}
)
return B_OK;
}
status_t
DwarfFile::_ParseTypeUnit(TypeUnit* unit)
{
AbbreviationTable* abbreviationTable;
status_t error = _GetAbbreviationTable(unit->AbbreviationOffset(),
abbreviationTable);
if (error != B_OK)
return error;
unit->SetAbbreviationTable(abbreviationTable);
DataReader dataReader(
(const uint8*)fDebugTypesSection->Data() + unit->ContentOffset(),
unit->ContentSize(), unit->AddressSize());
DebugInfoEntry* entry;
bool endOfEntryList;
error = _ParseDebugInfoEntry(dataReader, unit, abbreviationTable, entry,
endOfEntryList);
if (error != B_OK)
return error;
DIETypeUnit* unitEntry = dynamic_cast<DIETypeUnit*>(entry);
if (unitEntry == NULL) {
WARNING("No type unit entry in .debug_types section.\n");
return B_BAD_DATA;
}
unit->SetUnitEntry(unitEntry);
DebugInfoEntry* typeEntry = unit->EntryForOffset(unit->TypeOffset());
if (typeEntry == NULL) {
WARNING("No type found for type unit %p at specified offset %"
B_PRId64 ".\n", unit, unit->TypeOffset());
return B_BAD_DATA;
}
unit->SetTypeEntry(typeEntry);
TRACE_DIE_ONLY(
TRACE_DIE("remaining bytes in unit: %" B_PRIdOFF "\n",
dataReader.BytesRemaining());
if (dataReader.HasData()) {
TRACE_DIE(" ");
while (dataReader.HasData())
TRACE_DIE("%02x", dataReader.Read<uint8>(0));
TRACE_DIE("\n");
}
)
return B_OK;
}
status_t
DwarfFile::_ParseDebugInfoEntry(DataReader& dataReader,
BaseUnit* unit, AbbreviationTable* abbreviationTable,
DebugInfoEntry*& _entry, bool& _endOfEntryList, int level)
{
off_t entryOffset = dataReader.Offset()
+ unit->RelativeContentOffset();
uint32 code = dataReader.ReadUnsignedLEB128(0);
if (code == 0) {
if (dataReader.HasOverflow()) {
WARNING("Unexpected end of .debug_info section.\n");
return B_BAD_DATA;
}
_entry = NULL;
_endOfEntryList = true;
return B_OK;
}
// get the corresponding abbreviation entry
AbbreviationEntry abbreviationEntry;
if (!abbreviationTable->GetAbbreviationEntry(code, abbreviationEntry)) {
WARNING("No abbreviation entry for code %" B_PRIx32 "\n", code);
return B_BAD_DATA;
}
DebugInfoEntry* entry;
status_t error = fDebugInfoFactory.CreateDebugInfoEntry(
abbreviationEntry.Tag(), entry);
if (error != B_OK) {
WARNING("Failed to generate entry for tag %" B_PRIu32 ", code %"
B_PRIu32 "\n", abbreviationEntry.Tag(), code);
return error;
}
ObjectDeleter<DebugInfoEntry> entryDeleter(entry);
TRACE_DIE("%*sentry %p at %" B_PRIdOFF ": %" B_PRIu32 ", tag: %s (%"
B_PRIu32 "), children: %d\n", level * 2, "", entry, entryOffset,
abbreviationEntry.Code(), get_entry_tag_name(abbreviationEntry.Tag()),
abbreviationEntry.Tag(), abbreviationEntry.HasChildren());
error = unit->AddDebugInfoEntry(entry, entryOffset);
if (error != B_OK)
return error;
// parse the attributes (supply NULL entry to avoid adding them yet)
error = _ParseEntryAttributes(dataReader, unit, NULL, abbreviationEntry);
if (error != B_OK)
return error;
// parse children, if the entry has any
if (abbreviationEntry.HasChildren()) {
while (true) {
DebugInfoEntry* childEntry;
bool endOfEntryList;
status_t error = _ParseDebugInfoEntry(dataReader,
unit, abbreviationTable, childEntry, endOfEntryList, level + 1);
if (error != B_OK)
return error;
// add the child to our entry
if (childEntry != NULL) {
if (entry != NULL) {
error = entry->AddChild(childEntry);
if (error == B_OK) {
childEntry->SetParent(entry);
} else if (error == ENTRY_NOT_HANDLED) {
error = B_OK;
TRACE_DIE("%*s -> child unhandled\n", level * 2, "");
}
if (error != B_OK) {
delete childEntry;
return error;
}
} else
delete childEntry;
}
if (endOfEntryList)
break;
}
}
entryDeleter.Detach();
_entry = entry;
_endOfEntryList = false;
return B_OK;
}
status_t
DwarfFile::_FinishUnit(BaseUnit* unit)
{
CompilationUnit* compilationUnit = dynamic_cast<CompilationUnit*>(unit);
bool isTypeUnit = compilationUnit == NULL;
TRACE_DIE("\nfinishing %s unit %p\n",
isTypeUnit ? "type" : "compilation", unit);
AbbreviationTable* abbreviationTable = unit->GetAbbreviationTable();
ElfSection* section = isTypeUnit
? fDebugTypesSection : fDebugInfoSection;
DataReader dataReader(
(const uint8*)section->Data() + unit->HeaderOffset(),
unit->TotalSize(), unit->AddressSize());
DebugInfoEntryInitInfo entryInitInfo;
int entryCount = unit->CountEntries();
for (int i = 0; i < entryCount; i++) {
// get the entry
DebugInfoEntry* entry;
off_t offset;
unit->GetEntryAt(i, entry, offset);
TRACE_DIE("entry %p at %" B_PRIdOFF "\n", entry, offset);
// seek the reader to the entry
dataReader.SeekAbsolute(offset);
// read the entry code
uint32 code = dataReader.ReadUnsignedLEB128(0);
// get the respective abbreviation entry
AbbreviationEntry abbreviationEntry;
abbreviationTable->GetAbbreviationEntry(code, abbreviationEntry);
// initialization before setting the attributes
status_t error = entry->InitAfterHierarchy(entryInitInfo);
if (error != B_OK) {
WARNING("Init after hierarchy failed!\n");
return error;
}
// parse the attributes -- this time pass the entry, so that the
// attribute get set on it
error = _ParseEntryAttributes(dataReader, unit, entry,
abbreviationEntry);
if (error != B_OK)
return error;
// initialization after setting the attributes
error = entry->InitAfterAttributes(entryInitInfo);
if (error != B_OK) {
WARNING("Init after attributes failed!\n");
return error;
}
}
// set the compilation unit's source language
unit->SetSourceLanguage(entryInitInfo.languageInfo);
if (isTypeUnit)
return B_OK;
// resolve the compilation unit's address range list
if (TargetAddressRangeList* ranges = ResolveRangeList(compilationUnit,
compilationUnit->UnitEntry()->AddressRangesOffset())) {
compilationUnit->SetAddressRanges(ranges);
ranges->ReleaseReference();
}
// add compilation dir to directory list
const char* compilationDir = compilationUnit->UnitEntry()
->CompilationDir();
if (!compilationUnit->AddDirectory(compilationDir != NULL
? compilationDir : ".")) {
return B_NO_MEMORY;
}
// parse line info header
if (fDebugLineSection != NULL)
_ParseLineInfo(compilationUnit);
return B_OK;
}
status_t
DwarfFile::_ParseEntryAttributes(DataReader& dataReader,
BaseUnit* unit, DebugInfoEntry* entry, AbbreviationEntry& abbreviationEntry)
{
uint32 attributeName;
uint32 attributeForm;
while (abbreviationEntry.GetNextAttribute(attributeName,
attributeForm)) {
// resolve attribute form indirection
if (attributeForm == DW_FORM_indirect)
attributeForm = dataReader.ReadUnsignedLEB128(0);
// prepare an AttributeValue
AttributeValue attributeValue;
attributeValue.attributeForm = attributeForm;
bool isSigned = false;
// Read the attribute value according to the attribute's form. For
// the forms that don't map to a single attribute class only or
// those that need additional processing, we read a temporary value
// first.
uint64 value = 0;
off_t blockLength = 0;
off_t valueOffset = dataReader.Offset() + unit->ContentOffset();
uint8 refType = dwarf_reference_type_local;
switch (attributeForm) {
case DW_FORM_addr:
value = dataReader.ReadAddress(0);
break;
case DW_FORM_block2:
blockLength = dataReader.Read<uint16>(0);
break;
case DW_FORM_block4:
blockLength = dataReader.Read<uint32>(0);
break;
case DW_FORM_data2:
value = dataReader.Read<uint16>(0);
break;
case DW_FORM_data4:
value = dataReader.Read<uint32>(0);
break;
case DW_FORM_data8:
value = dataReader.Read<uint64>(0);
break;
case DW_FORM_string:
attributeValue.SetToString(dataReader.ReadString());
break;
case DW_FORM_block:
case DW_FORM_exprloc:
blockLength = dataReader.ReadUnsignedLEB128(0);
break;
case DW_FORM_block1:
blockLength = dataReader.Read<uint8>(0);
break;
case DW_FORM_data1:
value = dataReader.Read<uint8>(0);
break;
case DW_FORM_flag:
attributeValue.SetToFlag(dataReader.Read<uint8>(0) != 0);
break;
case DW_FORM_sdata:
value = dataReader.ReadSignedLEB128(0);
isSigned = true;
break;
case DW_FORM_strp:
{
if (fDebugStringSection != NULL) {
uint64 offset = unit->IsDwarf64()
? dataReader.Read<uint64>(0)
: dataReader.Read<uint32>(0);
if (offset >= fDebugStringSection->Size()) {
WARNING("Invalid DW_FORM_strp offset: %" B_PRIu64 "\n",
offset);
return B_BAD_DATA;
}
attributeValue.SetToString(
(const char*)fDebugStringSection->Data() + offset);
} else {
WARNING("Invalid DW_FORM_strp: no string section!\n");
return B_BAD_DATA;
}
break;
}
case DW_FORM_udata:
value = dataReader.ReadUnsignedLEB128(0);
break;
case DW_FORM_ref_addr:
value = unit->IsDwarf64()
? dataReader.Read<uint64>(0)
: (uint64)dataReader.Read<uint32>(0);
refType = dwarf_reference_type_global;
break;
case DW_FORM_ref1:
value = dataReader.Read<uint8>(0);
break;
case DW_FORM_ref2:
value = dataReader.Read<uint16>(0);
break;
case DW_FORM_ref4:
value = dataReader.Read<uint32>(0);
break;
case DW_FORM_ref8:
value = dataReader.Read<uint64>(0);
break;
case DW_FORM_ref_udata:
value = dataReader.ReadUnsignedLEB128(0);
break;
case DW_FORM_flag_present:
attributeValue.SetToFlag(true);
break;
case DW_FORM_ref_sig8:
fTypesSectionRequired = true;
value = dataReader.Read<uint64>(0);
refType = dwarf_reference_type_signature;
break;
case DW_FORM_sec_offset:
value = unit->IsDwarf64()
? dataReader.Read<uint64>(0)
: (uint64)dataReader.Read<uint32>(0);
break;
case DW_FORM_indirect:
default:
WARNING("Unsupported attribute form: %" B_PRIu32 "\n",
attributeForm);
return B_BAD_DATA;
}
// get the attribute class -- skip the attribute, if we can't handle
// it
uint8 attributeClass = get_attribute_class(attributeName,
attributeForm);
if (attributeClass == ATTRIBUTE_CLASS_UNKNOWN) {
TRACE_DIE("skipping attribute with unrecognized class: %s (%#"
B_PRIx32 ") %s (%#" B_PRIx32 ")\n",
get_attribute_name_name(attributeName), attributeName,
get_attribute_form_name(attributeForm), attributeForm);
continue;
}
// set the attribute value according to the attribute's class
switch (attributeClass) {
case ATTRIBUTE_CLASS_ADDRESS:
attributeValue.SetToAddress(value);
break;
case ATTRIBUTE_CLASS_BLOCK:
attributeValue.SetToBlock(dataReader.Data(), blockLength);
dataReader.Skip(blockLength);
break;
case ATTRIBUTE_CLASS_CONSTANT:
attributeValue.SetToConstant(value, isSigned);
break;
case ATTRIBUTE_CLASS_LINEPTR:
attributeValue.SetToLinePointer(value);
break;
case ATTRIBUTE_CLASS_LOCLISTPTR:
attributeValue.SetToLocationListPointer(value);
break;
case ATTRIBUTE_CLASS_MACPTR:
attributeValue.SetToMacroPointer(value);
break;
case ATTRIBUTE_CLASS_RANGELISTPTR:
attributeValue.SetToRangeListPointer(value);
break;
case ATTRIBUTE_CLASS_REFERENCE:
if (entry != NULL) {
attributeValue.SetToReference(_ResolveReference(
unit, value, refType));
if (attributeValue.reference == NULL) {
// gcc 2 apparently somtimes produces DW_AT_sibling
// attributes pointing to the end of the sibling list.
// Just ignore those.
if (attributeName == DW_AT_sibling)
continue;
WARNING("Failed to resolve reference on entry %p: "
"(%#" B_PRIx64 ") %s (%#" B_PRIx32 ") %s "
"(%#" B_PRIx32 "): value: %#" B_PRIx64 "\n",
entry,
valueOffset,
get_attribute_name_name(attributeName),
attributeName,
get_attribute_form_name(attributeForm),
attributeForm, value);
return B_ENTRY_NOT_FOUND;
}
}
break;
case ATTRIBUTE_CLASS_FLAG:
case ATTRIBUTE_CLASS_STRING:
// already set
break;
}
if (dataReader.HasOverflow()) {
WARNING("Unexpected end of .debug_info section.\n");
return B_BAD_DATA;
}
TRACE_DIE_ONLY(
char buffer[1024];
TRACE_DIE(" attr (%#" B_PRIx64 ") %s %s (%d): %s\n",
valueOffset,
get_attribute_name_name(attributeName),
get_attribute_form_name(attributeForm), attributeClass,
attributeValue.ToString(buffer, sizeof(buffer)));
)
// add the attribute
if (entry != NULL) {
DebugInfoEntrySetter attributeSetter
= get_attribute_name_setter(attributeName);
if (attributeSetter != 0) {
status_t error = (entry->*attributeSetter)(attributeName,
attributeValue);
if (error == ATTRIBUTE_NOT_HANDLED) {
error = B_OK;
TRACE_DIE(" -> unhandled\n");
}
if (error != B_OK) {
WARNING("Failed to set attribute: name: %s, form: %s: %s\n",
get_attribute_name_name(attributeName),
get_attribute_form_name(attributeForm),
strerror(error));
}
} else
TRACE_DIE(" -> no attribute setter!\n");
}
}
return B_OK;
}
status_t
DwarfFile::_ParseLineInfo(CompilationUnit* unit)
{
off_t offset = unit->UnitEntry()->StatementListOffset();
TRACE_LINES("DwarfFile::_ParseLineInfo(%p), offset: %" B_PRIdOFF "\n", unit,
offset);
DataReader dataReader((uint8*)fDebugLineSection->Data() + offset,
fDebugLineSection->Size() - offset, unit->AddressSize());
// unit length
bool dwarf64;
uint64 unitLength = dataReader.ReadInitialLength(dwarf64);
if (unitLength > (uint64)dataReader.BytesRemaining())
return B_BAD_DATA;
off_t unitOffset = dataReader.Offset();
// version (uhalf)
uint16 version = dataReader.Read<uint16>(0);
// header_length (4/8)
uint64 headerLength = dwarf64
? dataReader.Read<uint64>(0) : (uint64)dataReader.Read<uint32>(0);
off_t headerOffset = dataReader.Offset();
if ((uint64)dataReader.BytesRemaining() < headerLength)
return B_BAD_DATA;
// minimum instruction length
uint8 minInstructionLength = dataReader.Read<uint8>(0);
// default is statement
bool defaultIsStatement = dataReader.Read<uint8>(0) != 0;
// line_base (sbyte)
int8 lineBase = (int8)dataReader.Read<uint8>(0);
// line_range (ubyte)
uint8 lineRange = dataReader.Read<uint8>(0);
// opcode_base (ubyte)
uint8 opcodeBase = dataReader.Read<uint8>(0);
// standard_opcode_lengths (ubyte[])
const uint8* standardOpcodeLengths = (const uint8*)dataReader.Data();
dataReader.Skip(opcodeBase - 1);
if (dataReader.HasOverflow())
return B_BAD_DATA;
if (version != 2 && version != 3)
return B_UNSUPPORTED;
TRACE_LINES(" unitLength: %" B_PRIu64 "\n", unitLength);
TRACE_LINES(" version: %u\n", version);
TRACE_LINES(" headerLength: %" B_PRIu64 "\n", headerLength);
TRACE_LINES(" minInstructionLength: %u\n", minInstructionLength);
TRACE_LINES(" defaultIsStatement: %d\n", defaultIsStatement);
TRACE_LINES(" lineBase: %d\n", lineBase);
TRACE_LINES(" lineRange: %u\n", lineRange);
TRACE_LINES(" opcodeBase: %u\n", opcodeBase);
// include directories
TRACE_LINES(" include directories:\n");
while (const char* directory = dataReader.ReadString()) {
if (*directory == '\0')
break;
TRACE_LINES(" \"%s\"\n", directory);
if (!unit->AddDirectory(directory))
return B_NO_MEMORY;
}
// file names
TRACE_LINES(" files:\n");
while (const char* file = dataReader.ReadString()) {
if (*file == '\0')
break;
uint64 dirIndex = dataReader.ReadUnsignedLEB128(0);
TRACE_LINES_ONLY(uint64 modificationTime =)
dataReader.ReadUnsignedLEB128(0);
TRACE_LINES_ONLY(uint64 fileLength =)
dataReader.ReadUnsignedLEB128(0);
if (dataReader.HasOverflow())
return B_BAD_DATA;
TRACE_LINES(" \"%s\", dir index: %" B_PRIu64 ", mtime: %" B_PRIu64
", length: %" B_PRIu64 "\n", file, dirIndex, modificationTime,
fileLength);
if (!unit->AddFile(file, dirIndex))
return B_NO_MEMORY;
}
off_t readerOffset = dataReader.Offset();
if ((uint64)readerOffset > readerOffset + headerLength)
return B_BAD_DATA;
off_t offsetToProgram = headerOffset + headerLength - readerOffset;
const uint8* program = (uint8*)dataReader.Data() + offsetToProgram;
size_t programSize = unitLength - (readerOffset - unitOffset);
return unit->GetLineNumberProgram().Init(program, programSize,
minInstructionLength, defaultIsStatement, lineBase, lineRange,
opcodeBase, standardOpcodeLengths);
}
status_t
DwarfFile::_UnwindCallFrame(CompilationUnit* unit, uint8 addressSize,
DIESubprogram* subprogramEntry, target_addr_t location,
const FDELookupInfo* info, const DwarfTargetInterface* inputInterface,
DwarfTargetInterface* outputInterface, target_addr_t& _framePointer)
{
ElfSection* currentFrameSection = (info->ehFrame)
? fEHFrameSection : fDebugFrameSection;
TRACE_CFI("DwarfFile::_UnwindCallFrame(%#" B_PRIx64 ")\n", location);
DataReader dataReader((uint8*)currentFrameSection->Data(),
currentFrameSection->Size(), unit != NULL
? unit->AddressSize() : addressSize);
dataReader.SeekAbsolute(info->fdeOffset);
bool dwarf64;
uint64 length = dataReader.ReadInitialLength(dwarf64);
uint64 lengthOffset = dataReader.Offset();
CfaContext context;
CIEAugmentation cieAugmentation;
// when using .eh_frame format, we need to parse the CIE's
// augmentation up front in order to know how the FDE's addresses
// will be represented
DataReader cieReader;
off_t cieRemaining;
status_t error = _ParseCIEHeader(currentFrameSection,
info->ehFrame, unit, addressSize, context, info->cieOffset,
cieAugmentation, cieReader, cieRemaining);
if (error != B_OK)
return error;
if (cieReader.HasOverflow())
return B_BAD_DATA;
if (cieRemaining < 0)
return B_BAD_DATA;
// skip CIE ID, initial offset and range, since we already know those
// from FDELookupInfo.
dwarf64 ? dataReader.Read<uint64>(0) : dataReader.Read<uint32>(0);
cieAugmentation.ReadEncodedAddress(dataReader, fElfFile,
currentFrameSection);
cieAugmentation.ReadEncodedAddress(dataReader, fElfFile,
currentFrameSection, true);
TRACE_CFI(" found fde: length: %" B_PRIu64 " (%" B_PRIdOFF
"), CIE offset: %#" B_PRIx64 ", location: %#" B_PRIx64 ", "
"range: %#" B_PRIx64 "\n", length, dataReader.BytesRemaining(),
info->cieOffset, info->start, info->end - info->start);
context.SetLocation(location, info->start);
uint32 registerCount = outputInterface->CountRegisters();
error = context.Init(registerCount);
if (error != B_OK)
return error;
error = outputInterface->InitRegisterRules(context);
if (error != B_OK)
return error;
// process the CIE's frame info instructions
cieReader = cieReader.RestrictedReader(cieRemaining);
error = _ParseFrameInfoInstructions(unit, context,
cieReader, 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;
uint64 remaining = lengthOffset + length - dataReader.Offset();
if (remaining < 0)
return B_BAD_DATA;
DataReader restrictedReader =
dataReader.RestrictedReader(remaining);
error = _ParseFrameInfoInstructions(unit, context,
restrictedReader, cieAugmentation);
if (error != B_OK)
return error;
TRACE_CFI(" found row!\n");
// apply the rules of the final row
// get the frameAddress first
target_addr_t frameAddress;
CfaCfaRule* cfaCfaRule = context.GetCfaCfaRule();
switch (cfaCfaRule->Type()) {
case CFA_CFA_RULE_REGISTER_OFFSET:
{
BVariant value;
if (!inputInterface->GetRegisterValue(
cfaCfaRule->Register(), value)
|| !value.IsNumber()) {
return B_UNSUPPORTED;
}
frameAddress = value.ToUInt64() + cfaCfaRule->Offset();
break;
}
case CFA_CFA_RULE_EXPRESSION:
{
error = EvaluateExpression(unit, addressSize,
subprogramEntry,
cfaCfaRule->Expression().block,
cfaCfaRule->Expression().size,
inputInterface, location, 0, 0, false,
frameAddress);
if (error != B_OK)
return error;
break;
}
case CFA_CFA_RULE_UNDEFINED:
default:
return B_BAD_VALUE;
}
TRACE_CFI(" frame address: %#" B_PRIx64 "\n", frameAddress);
// apply the register rules
for (uint32 i = 0; i < registerCount; i++) {
TRACE_CFI(" reg %" B_PRIu32 "\n", i);
uint32 valueType = outputInterface->RegisterValueType(i);
if (valueType == 0)
continue;
CfaRule* rule = context.RegisterRule(i);
if (rule == NULL)
continue;
// apply the rule
switch (rule->Type()) {
case CFA_RULE_SAME_VALUE:
{
TRACE_CFI(" -> CFA_RULE_SAME_VALUE\n");
BVariant value;
if (inputInterface->GetRegisterValue(i, value))
outputInterface->SetRegisterValue(i, value);
break;
}
case CFA_RULE_LOCATION_OFFSET:
{
TRACE_CFI(" -> CFA_RULE_LOCATION_OFFSET: %"
B_PRId64 "\n", rule->Offset());
BVariant value;
if (inputInterface->ReadValueFromMemory(
frameAddress + rule->Offset(), valueType,
value)) {
outputInterface->SetRegisterValue(i, value);
}
break;
}
case CFA_RULE_VALUE_OFFSET:
TRACE_CFI(" -> CFA_RULE_VALUE_OFFSET\n");
outputInterface->SetRegisterValue(i,
frameAddress + rule->Offset());
break;
case CFA_RULE_REGISTER:
{
TRACE_CFI(" -> CFA_RULE_REGISTER\n");
BVariant value;
if (inputInterface->GetRegisterValue(
rule->Register(), value)) {
outputInterface->SetRegisterValue(i, value);
}
break;
}
case CFA_RULE_LOCATION_EXPRESSION:
{
TRACE_CFI(" -> CFA_RULE_LOCATION_EXPRESSION\n");
target_addr_t address;
error = EvaluateExpression(unit, addressSize,
subprogramEntry,
rule->Expression().block,
rule->Expression().size,
inputInterface, location, frameAddress,
frameAddress, true, address);
BVariant value;
if (error == B_OK
&& inputInterface->ReadValueFromMemory(address,
valueType, value)) {
outputInterface->SetRegisterValue(i, value);
}
break;
}
case CFA_RULE_VALUE_EXPRESSION:
{
TRACE_CFI(" -> CFA_RULE_VALUE_EXPRESSION\n");
target_addr_t value;
error = EvaluateExpression(unit, addressSize,
subprogramEntry,
rule->Expression().block,
rule->Expression().size,
inputInterface, location, frameAddress,
frameAddress, true, value);
if (error == B_OK)
outputInterface->SetRegisterValue(i, value);
break;
}
case CFA_RULE_UNDEFINED:
TRACE_CFI(" -> CFA_RULE_UNDEFINED\n");
default:
break;
}
}
_framePointer = frameAddress;
return B_OK;
}
status_t
DwarfFile::_ParseCIEHeader(ElfSection* debugFrameSection,
bool usingEHFrameSection, CompilationUnit* unit, uint8 addressSize,
CfaContext& context, off_t cieOffset, CIEAugmentation& cieAugmentation,
DataReader& dataReader, off_t& _cieRemaining)
{
if (cieOffset < 0 || (uint64)cieOffset >= debugFrameSection->Size())
return B_BAD_DATA;
dataReader.SetTo((uint8*)debugFrameSection->Data() + cieOffset,
debugFrameSection->Size() - cieOffset, unit != NULL
? unit->AddressSize() : addressSize);
// length
bool dwarf64;
uint64 length = dataReader.ReadInitialLength(dwarf64);
if (length > (uint64)dataReader.BytesRemaining())
return B_BAD_DATA;
off_t lengthOffset = dataReader.Offset();
// CIE ID/CIE pointer
uint64 cieID = dwarf64
? dataReader.Read<uint64>(0) : dataReader.Read<uint32>(0);
if (usingEHFrameSection) {
if (cieID != 0)
return B_BAD_DATA;
} else {
if (dwarf64 ? cieID != 0xffffffffffffffffULL : cieID != 0xffffffff)
return B_BAD_DATA;
}
uint8 version = dataReader.Read<uint8>(0);
if (version != 1) {
TRACE_CFI(" cie: length: %" B_PRIu64 ", offset: %#" B_PRIx64 ", "
"version: %u -- unsupported\n", length, (uint64)cieOffset, version);
return B_UNSUPPORTED;
}
// read the augmentation string
cieAugmentation.Init(dataReader);
// in the cause of augmentation string "eh",
// the exception table pointer is located immediately before the
// code/data alignment values. We have no use for it so simply skip.
if (strcmp(cieAugmentation.String(), "eh") == 0)
dataReader.Skip(dwarf64 ? sizeof(uint64) : sizeof(uint32));
context.SetCodeAlignment(dataReader.ReadUnsignedLEB128(0));
context.SetDataAlignment(dataReader.ReadSignedLEB128(0));
context.SetReturnAddressRegister(dataReader.ReadUnsignedLEB128(0));
TRACE_CFI(" cie: length: %" B_PRIu64 ", offset: %#" B_PRIx64 ", version: "
"%u, augmentation: \"%s\", aligment: code: %" B_PRIu32 ", data: %"
B_PRId32 ", return address reg: %" B_PRIu32 "\n", length,
(uint64)cieOffset, version, cieAugmentation.String(),
context.CodeAlignment(), context.DataAlignment(),
context.ReturnAddressRegister());
status_t error = cieAugmentation.Read(dataReader);
if (error != B_OK) {
TRACE_CFI(" cie: length: %" B_PRIu64 ", version: %u, augmentation: "
"\"%s\" -- unsupported\n", length, version,
cieAugmentation.String());
return error;
}
if (dataReader.HasOverflow())
return B_BAD_DATA;
_cieRemaining = length -(dataReader.Offset() - lengthOffset);
if (_cieRemaining < 0)
return B_BAD_DATA;
return B_OK;
}
status_t
DwarfFile::_ParseFrameInfoInstructions(CompilationUnit* unit,
CfaContext& context, DataReader& dataReader, CIEAugmentation& augmentation)
{
while (dataReader.BytesRemaining() > 0) {
TRACE_CFI(" [%2" B_PRId64 "]", dataReader.BytesRemaining());
uint8 opcode = dataReader.Read<uint8>(0);
if ((opcode >> 6) != 0) {
uint32 operand = opcode & 0x3f;
switch (opcode >> 6) {
case DW_CFA_advance_loc:
{
TRACE_CFI(" DW_CFA_advance_loc: %#" B_PRIx32 "\n",
operand);
target_addr_t location = context.Location()
+ operand * context.CodeAlignment();
if (location > context.TargetLocation())
return B_OK;
context.SetLocation(location);
break;
}
case DW_CFA_offset:
{
uint64 offset = dataReader.ReadUnsignedLEB128(0);
TRACE_CFI(" DW_CFA_offset: reg: %" B_PRIu32 ", offset: "
"%" B_PRIu64 "\n", operand, offset);
if (CfaRule* rule = context.RegisterRule(operand)) {
rule->SetToLocationOffset(
offset * context.DataAlignment());
}
break;
}
case DW_CFA_restore:
{
TRACE_CFI(" DW_CFA_restore: %#" B_PRIx32 "\n", operand);
context.RestoreRegisterRule(operand);
break;
}
}
} else {
switch (opcode) {
case DW_CFA_nop:
{
TRACE_CFI(" DW_CFA_nop\n");
break;
}
case DW_CFA_set_loc:
{
target_addr_t location = augmentation.ReadEncodedAddress(
dataReader, fElfFile, fDebugFrameSection);
TRACE_CFI(" DW_CFA_set_loc: %#" B_PRIx64 "\n", location);
if (location < context.Location())
return B_BAD_VALUE;
if (location > context.TargetLocation())
return B_OK;
context.SetLocation(location);
break;
}
case DW_CFA_advance_loc1:
{
uint32 delta = dataReader.Read<uint8>(0);
TRACE_CFI(" DW_CFA_advance_loc1: %#" B_PRIx32 "\n",
delta);
target_addr_t location = context.Location()
+ delta * context.CodeAlignment();
if (location > context.TargetLocation())
return B_OK;
context.SetLocation(location);
break;
}
case DW_CFA_advance_loc2:
{
uint32 delta = dataReader.Read<uint16>(0);
TRACE_CFI(" DW_CFA_advance_loc2: %#" B_PRIx32 "\n",
delta);
target_addr_t location = context.Location()
+ delta * context.CodeAlignment();
if (location > context.TargetLocation())
return B_OK;
context.SetLocation(location);
break;
}
case DW_CFA_advance_loc4:
{
uint32 delta = dataReader.Read<uint32>(0);
TRACE_CFI(" DW_CFA_advance_loc4: %#" B_PRIx32 "\n",
delta);
target_addr_t location = context.Location()
+ delta * context.CodeAlignment();
if (location > context.TargetLocation())
return B_OK;
context.SetLocation(location);
break;
}
case DW_CFA_offset_extended:
{
uint32 reg = dataReader.ReadUnsignedLEB128(0);
uint64 offset = dataReader.ReadUnsignedLEB128(0);
TRACE_CFI(" DW_CFA_offset_extended: reg: %" B_PRIu32 ", "
"offset: %" B_PRIu64 "\n", reg, offset);
if (CfaRule* rule = context.RegisterRule(reg)) {
rule->SetToLocationOffset(
offset * context.DataAlignment());
}
break;
}
case DW_CFA_restore_extended:
{
uint32 reg = dataReader.ReadUnsignedLEB128(0);
TRACE_CFI(" DW_CFA_restore_extended: %#" B_PRIx32 "\n",
reg);
context.RestoreRegisterRule(reg);
break;
}
case DW_CFA_undefined:
{
uint32 reg = dataReader.ReadUnsignedLEB128(0);
TRACE_CFI(" DW_CFA_undefined: %" B_PRIu32 "\n", reg);
if (CfaRule* rule = context.RegisterRule(reg))
rule->SetToUndefined();
break;
}
case DW_CFA_same_value:
{
uint32 reg = dataReader.ReadUnsignedLEB128(0);
TRACE_CFI(" DW_CFA_same_value: %" B_PRIu32 "\n", reg);
if (CfaRule* rule = context.RegisterRule(reg))
rule->SetToSameValue();
break;
}
case DW_CFA_register:
{
uint32 reg1 = dataReader.ReadUnsignedLEB128(0);
uint32 reg2 = dataReader.ReadUnsignedLEB128(0);
TRACE_CFI(" DW_CFA_register: reg1: %" B_PRIu32 ", reg2: "
"%" B_PRIu32 "\n", reg1, reg2);
if (CfaRule* rule = context.RegisterRule(reg1))
rule->SetToValueOffset(reg2);
break;
}
case DW_CFA_remember_state:
{
TRACE_CFI(" DW_CFA_remember_state\n");
status_t error = context.PushRuleSet();
if (error != B_OK)
return error;
break;
}
case DW_CFA_restore_state:
{
TRACE_CFI(" DW_CFA_restore_state\n");
status_t error = context.PopRuleSet();
if (error != B_OK)
return error;
break;
}
case DW_CFA_def_cfa:
{
uint32 reg = dataReader.ReadUnsignedLEB128(0);
uint64 offset = dataReader.ReadUnsignedLEB128(0);
TRACE_CFI(" DW_CFA_def_cfa: reg: %" B_PRIu32 ", offset: "
"%" B_PRIu64 "\n", reg, offset);
context.GetCfaCfaRule()->SetToRegisterOffset(reg, offset);
break;
}
case DW_CFA_def_cfa_register:
{
uint32 reg = dataReader.ReadUnsignedLEB128(0);
TRACE_CFI(" DW_CFA_def_cfa_register: %" B_PRIu32 "\n",
reg);
if (context.GetCfaCfaRule()->Type()
!= CFA_CFA_RULE_REGISTER_OFFSET) {
return B_BAD_DATA;
}
context.GetCfaCfaRule()->SetRegister(reg);
break;
}
case DW_CFA_def_cfa_offset:
{
uint64 offset = dataReader.ReadUnsignedLEB128(0);
TRACE_CFI(" DW_CFA_def_cfa_offset: %" B_PRIu64 "\n",
offset);
if (context.GetCfaCfaRule()->Type()
!= CFA_CFA_RULE_REGISTER_OFFSET) {
return B_BAD_DATA;
}
context.GetCfaCfaRule()->SetOffset(offset);
break;
}
case DW_CFA_def_cfa_expression:
{
uint64 blockLength = dataReader.ReadUnsignedLEB128(0);
uint8* block = (uint8*)dataReader.Data();
dataReader.Skip(blockLength);
TRACE_CFI(" DW_CFA_def_cfa_expression: %p, %" B_PRIu64
"\n", block, blockLength);
context.GetCfaCfaRule()->SetToExpression(block,
blockLength);
break;
}
case DW_CFA_expression:
{
uint32 reg = dataReader.ReadUnsignedLEB128(0);
uint64 blockLength = dataReader.ReadUnsignedLEB128(0);
uint8* block = (uint8*)dataReader.Data();
dataReader.Skip(blockLength);
TRACE_CFI(" DW_CFA_expression: reg: %" B_PRIu32 ", "
"block: %p, %" B_PRIu64 "\n", reg, block, blockLength);
if (CfaRule* rule = context.RegisterRule(reg))
rule->SetToLocationExpression(block, blockLength);
break;
}
case DW_CFA_offset_extended_sf:
{
uint32 reg = dataReader.ReadUnsignedLEB128(0);
int64 offset = dataReader.ReadSignedLEB128(0);
TRACE_CFI(" DW_CFA_offset_extended: reg: %" B_PRIu32 ", "
"offset: %" B_PRId64 "\n", reg, offset);
if (CfaRule* rule = context.RegisterRule(reg)) {
rule->SetToLocationOffset(
offset * (int32)context.DataAlignment());
}
break;
}
case DW_CFA_def_cfa_sf:
{
uint32 reg = dataReader.ReadUnsignedLEB128(0);
int64 offset = dataReader.ReadSignedLEB128(0);
TRACE_CFI(" DW_CFA_def_cfa_sf: reg: %" B_PRIu32 ", "
"offset: %" B_PRId64 "\n", reg, offset);
context.GetCfaCfaRule()->SetToRegisterOffset(reg,
offset * (int32)context.DataAlignment());
break;
}
case DW_CFA_def_cfa_offset_sf:
{
int64 offset = dataReader.ReadSignedLEB128(0);
TRACE_CFI(" DW_CFA_def_cfa_offset: %" B_PRId64 "\n",
offset);
if (context.GetCfaCfaRule()->Type()
!= CFA_CFA_RULE_REGISTER_OFFSET) {
return B_BAD_DATA;
}
context.GetCfaCfaRule()->SetOffset(
offset * (int32)context.DataAlignment());
break;
}
case DW_CFA_val_offset:
{
uint32 reg = dataReader.ReadUnsignedLEB128(0);
uint64 offset = dataReader.ReadUnsignedLEB128(0);
TRACE_CFI(" DW_CFA_val_offset: reg: %" B_PRIu32 ", "
"offset: %" B_PRIu64 "\n", reg, offset);
if (CfaRule* rule = context.RegisterRule(reg)) {
rule->SetToValueOffset(
offset * context.DataAlignment());
}
break;
}
case DW_CFA_val_offset_sf:
{
uint32 reg = dataReader.ReadUnsignedLEB128(0);
int64 offset = dataReader.ReadSignedLEB128(0);
TRACE_CFI(" DW_CFA_val_offset_sf: reg: %" B_PRIu32 ", "
"offset: %" B_PRId64 "\n", reg, offset);
if (CfaRule* rule = context.RegisterRule(reg)) {
rule->SetToValueOffset(
offset * (int32)context.DataAlignment());
}
break;
}
case DW_CFA_val_expression:
{
uint32 reg = dataReader.ReadUnsignedLEB128(0);
uint64 blockLength = dataReader.ReadUnsignedLEB128(0);
uint8* block = (uint8*)dataReader.Data();
dataReader.Skip(blockLength);
TRACE_CFI(" DW_CFA_val_expression: reg: %" B_PRIu32 ", "
"block: %p, %" B_PRIu64 "\n", reg, block, blockLength);
if (CfaRule* rule = context.RegisterRule(reg))
rule->SetToValueExpression(block, blockLength);
break;
}
// extensions
case DW_CFA_MIPS_advance_loc8:
{
uint64 delta = dataReader.Read<uint64>(0);
TRACE_CFI(" DW_CFA_MIPS_advance_loc8: %#" B_PRIx64 "\n",
delta);
target_addr_t location = context.Location()
+ delta * context.CodeAlignment();
if (location > context.TargetLocation())
return B_OK;
context.SetLocation(location);
break;
}
case DW_CFA_GNU_window_save:
{
// SPARC specific, no args
TRACE_CFI(" DW_CFA_GNU_window_save\n");
// TODO: Implement once we have SPARC support!
break;
}
case DW_CFA_GNU_args_size:
{
// Updates the total size of arguments on the stack.
TRACE_CFI_ONLY(uint64 size =)
dataReader.ReadUnsignedLEB128(0);
TRACE_CFI(" DW_CFA_GNU_args_size: %" B_PRIu64 "\n",
size);
// TODO: Implement!
break;
}
case DW_CFA_GNU_negative_offset_extended:
{
// obsolete
uint32 reg = dataReader.ReadUnsignedLEB128(0);
int64 offset = dataReader.ReadSignedLEB128(0);
TRACE_CFI(" DW_CFA_GNU_negative_offset_extended: "
"reg: %" B_PRIu32 ", offset: %" B_PRId64 "\n", reg,
offset);
if (CfaRule* rule = context.RegisterRule(reg)) {
rule->SetToLocationOffset(
offset * (int32)context.DataAlignment());
}
break;
}
default:
TRACE_CFI(" unknown opcode %u!\n", opcode);
return B_BAD_DATA;
}
}
}
return B_OK;
}
status_t
DwarfFile::_ParsePublicTypesInfo()
{
TRACE_PUBTYPES("DwarfFile::_ParsePublicTypesInfo()\n");
if (fDebugPublicTypesSection == NULL) {
TRACE_PUBTYPES(" -> no public types section\n");
return B_ENTRY_NOT_FOUND;
}
DataReader dataReader((uint8*)fDebugPublicTypesSection->Data(),
fDebugPublicTypesSection->Size(), 4);
// address size doesn't matter at this point
while (dataReader.BytesRemaining() > 0) {
bool dwarf64;
uint64 unitLength = dataReader.ReadInitialLength(dwarf64);
off_t unitLengthOffset = dataReader.Offset();
// the unitLength starts here
if (dataReader.HasOverflow())
return B_BAD_DATA;
if (unitLengthOffset + unitLength
> (uint64)fDebugPublicTypesSection->Size()) {
WARNING("Invalid public types set unit length.\n");
break;
}
DataReader unitDataReader(dataReader.Data(), unitLength, 4);
// address size doesn't matter
_ParsePublicTypesInfo(unitDataReader, dwarf64);
dataReader.SeekAbsolute(unitLengthOffset + unitLength);
}
return B_OK;
}
status_t
DwarfFile::_ParsePublicTypesInfo(DataReader& dataReader, bool dwarf64)
{
int version = dataReader.Read<uint16>(0);
if (version != 2) {
TRACE_PUBTYPES(" pubtypes version %d unsupported\n", version);
return B_UNSUPPORTED;
}
TRACE_PUBTYPES_ONLY(off_t debugInfoOffset =) dwarf64
? dataReader.Read<uint64>(0)
: (uint64)dataReader.Read<uint32>(0);
TRACE_PUBTYPES_ONLY(off_t debugInfoSize =) dwarf64
? dataReader.Read<uint64>(0)
: (uint64)dataReader.Read<uint32>(0);
if (dataReader.HasOverflow())
return B_BAD_DATA;
TRACE_PUBTYPES("DwarfFile::_ParsePublicTypesInfo(): compilation unit debug "
"info: (%" B_PRIdOFF ", %" B_PRIdOFF ")\n", debugInfoOffset,
debugInfoSize);
while (dataReader.BytesRemaining() > 0) {
off_t entryOffset = dwarf64
? dataReader.Read<uint64>(0)
: (uint64)dataReader.Read<uint32>(0);
if (entryOffset == 0)
return B_OK;
TRACE_PUBTYPES_ONLY(const char* name =) dataReader.ReadString();
TRACE_PUBTYPES(" \"%s\" -> %" B_PRIdOFF "\n", name, entryOffset);
}
return B_OK;
}
status_t
DwarfFile::_GetAbbreviationTable(off_t offset, AbbreviationTable*& _table)
{
// check, whether we've already loaded it
for (AbbreviationTableList::Iterator it
= fAbbreviationTables.GetIterator();
AbbreviationTable* table = it.Next();) {
if (offset == table->Offset()) {
_table = table;
return B_OK;
}
}
// create a new table
AbbreviationTable* table = new(std::nothrow) AbbreviationTable(offset);
if (table == NULL)
return B_NO_MEMORY;
status_t error = table->Init(fDebugAbbrevSection->Data(),
fDebugAbbrevSection->Size());
if (error != B_OK) {
delete table;
return error;
}
fAbbreviationTables.Add(table);
_table = table;
return B_OK;
}
DebugInfoEntry*
DwarfFile::_ResolveReference(BaseUnit* unit, uint64 offset,
uint8 refType) const
{
switch (refType) {
case dwarf_reference_type_local:
return unit->EntryForOffset(offset);
break;
case dwarf_reference_type_global:
{
CompilationUnit* unit = _GetContainingCompilationUnit(offset);
if (unit == NULL)
break;
offset -= unit->HeaderOffset();
DebugInfoEntry* entry = unit->EntryForOffset(offset);
if (entry != NULL)
return entry;
break;
}
case dwarf_reference_type_signature:
{
TRACE_DIE("Resolving signature %#" B_PRIx64 "\n", offset);
TypeUnitTableEntry* entry = fTypeUnits.Lookup(offset);
if (entry != NULL && entry->unit != NULL)
return entry->unit->TypeEntry();
break;
}
}
return NULL;
}
status_t
DwarfFile::_GetLocationExpression(CompilationUnit* unit,
const LocationDescription* location, target_addr_t instructionPointer,
const void*& _expression, off_t& _length) const
{
if (!location->IsValid())
return B_BAD_VALUE;
if (location->IsExpression()) {
_expression = location->expression.data;
_length = location->expression.length;
return B_OK;
}
if (location->IsLocationList() && instructionPointer != 0) {
return _FindLocationExpression(unit, location->listOffset,
instructionPointer, _expression, _length);
}
return B_BAD_VALUE;
}
status_t
DwarfFile::_FindLocationExpression(CompilationUnit* unit, uint64 offset,
target_addr_t address, const void*& _expression, off_t& _length) const
{
if (unit == NULL)
return B_BAD_VALUE;
if (fDebugLocationSection == NULL)
return B_ENTRY_NOT_FOUND;
if (offset < 0 || offset >= (uint64)fDebugLocationSection->Size())
return B_BAD_DATA;
target_addr_t baseAddress = unit->AddressRangeBase();
target_addr_t maxAddress = unit->MaxAddress();
DataReader dataReader((uint8*)fDebugLocationSection->Data() + offset,
fDebugLocationSection->Size() - offset, unit->AddressSize());
while (true) {
target_addr_t start = dataReader.ReadAddress(0);
target_addr_t end = dataReader.ReadAddress(0);
if (dataReader.HasOverflow())
return B_BAD_DATA;
if (start == 0 && end == 0)
return B_ENTRY_NOT_FOUND;
if (start == maxAddress) {
baseAddress = end;
continue;
}
uint16 expressionLength = dataReader.Read<uint16>(0);
const void* expression = dataReader.Data();
if (!dataReader.Skip(expressionLength))
return B_BAD_DATA;
if (start == end)
continue;
start += baseAddress;
end += baseAddress;
if (address >= start && address < end) {
_expression = expression;
_length = expressionLength;
return B_OK;
}
}
}
status_t
DwarfFile::_LocateDebugInfo(BString& _requiredExternalFileName,
const char* locatedFilePath)
{
ElfFile* debugInfoFile = fElfFile;
ElfSection* debugLinkSection = fElfFile->GetSection(".gnu_debuglink");
if (debugLinkSection != NULL) {
AutoSectionPutter putter(fElfFile, debugLinkSection);
// the file specifies a debug link, look at its target instead
// for debug information.
// Format: null-terminated filename, as many 0 padding bytes as
// needed to reach the next 32-bit address boundary, followed
// by a 32-bit CRC
BString debugPath;
if (locatedFilePath)
debugPath = locatedFilePath;
else {
status_t result = _GetDebugInfoPath(
(const char*)debugLinkSection->Data(),
_requiredExternalFileName);
if (result != B_OK)
return result;
debugPath = _requiredExternalFileName;
}
if (fAlternateName != NULL)
free(fAlternateName);
fAlternateName = strdup(debugPath.String());
if (fAlternateName == NULL)
return B_NO_MEMORY;
/*
// TODO: validate CRC
int32 debugCRC = *(int32*)((char*)debugLinkSection->Data()
+ debugLinkSection->Size() - sizeof(int32));
*/
if (fAlternateElfFile == NULL) {
fAlternateElfFile = new(std::nothrow) ElfFile;
if (fAlternateElfFile == NULL)
return B_NO_MEMORY;
}
status_t result = fAlternateElfFile->Init(fAlternateName);
if (result != B_OK)
return result;
debugInfoFile = fAlternateElfFile;
}
// get the interesting sections
fDebugInfoSection = debugInfoFile->GetSection(".debug_info");
fDebugAbbrevSection = debugInfoFile->GetSection(".debug_abbrev");
if (fDebugInfoSection == NULL || fDebugAbbrevSection == NULL) {
WARNING("DwarfManager::File::Load(\"%s\"): no "
".debug_info or .debug_abbrev.\n", fName);
// if we at least have an EH frame, use that for stack unwinding
// if nothing else.
fEHFrameSection = fElfFile->GetSection(".eh_frame");
if (fEHFrameSection == NULL)
return B_ERROR;
}
return B_OK;
}
status_t
DwarfFile::_GetDebugInfoPath(const char* debugFileName,
BString& _infoPath) const
{
// first, see if we have a relative match to our local directory
BPath basePath;
status_t result = basePath.SetTo(fName);
if (result != B_OK)
return result;
basePath.GetParent(&basePath);
if (strcmp(basePath.Leaf(), "lib") == 0 || strcmp(basePath.Leaf(),
"add-ons") == 0) {
_infoPath.SetToFormat("%s/../debug/%s", basePath.Path(),
debugFileName);
} else
_infoPath.SetToFormat("%s/debug/%s", basePath.Path(), debugFileName);
BEntry entry(_infoPath.String());
result = entry.InitCheck();
if (result != B_OK && result != B_ENTRY_NOT_FOUND)
return result;
if (entry.Exists())
return B_OK;
// If the above search failed, check if our image is located in any
// of the system installation paths, and attempt to locate the debug info
// file in the corresponding well-known location
BString pathSuffix;
pathSuffix.SetToFormat("debug/%s", debugFileName);
BPathFinder finder(fName);
result = finder.FindPath(B_FIND_PATH_DEVELOP_DIRECTORY,
pathSuffix.String(), B_FIND_PATH_EXISTING_ONLY, basePath);
if (result == B_OK) {
_infoPath = basePath.Path();
return B_OK;
} else {
// if we failed to find a match, then it's up to the user to
// locate it. As such, return the external info file name
// for user interface purposes.
_infoPath.SetTo(debugFileName);
}
return B_ENTRY_NOT_FOUND;
}
TypeUnitTableEntry*
DwarfFile::_GetTypeUnit(uint64 signature) const
{
return fTypeUnits.Lookup(signature);
}
CompilationUnit*
DwarfFile::_GetContainingCompilationUnit(off_t refAddr) const
{
if (fCompilationUnits.IsEmpty())
return NULL;
// binary search
int lower = 0;
int upper = fCompilationUnits.CountItems() - 1;
while (lower < upper) {
int mid = (lower + upper + 1) / 2;
if (fCompilationUnits.ItemAt(mid)->HeaderOffset() > refAddr)
upper = mid - 1;
else
lower = mid;
}
CompilationUnit* unit = fCompilationUnits.ItemAt(lower);
return unit->ContainsAbsoluteOffset(refAddr) ? unit : NULL;
}
DwarfFile::FDELookupInfo*
DwarfFile::_GetContainingFDEInfo(target_addr_t offset) const
{
FDELookupInfo* info = NULL;
if (fDebugFrameSection != NULL) {
info = _GetContainingFDEInfo(offset, fDebugFrameInfos);
if (info != NULL)
return info;
}
return _GetContainingFDEInfo(offset, fEHFrameInfos);
}
DwarfFile::FDELookupInfo*
DwarfFile::_GetContainingFDEInfo(target_addr_t offset,
const FDEInfoList& infoList) const
{
// binary search
int lower = 0;
int upper = infoList.CountItems() - 1;
if (upper < 0)
return NULL;
while (lower < upper) {
int mid = (lower + upper + 1) / 2;
if (offset < infoList.ItemAt(mid)->start)
upper = mid - 1;
else
lower = mid;
}
FDELookupInfo* info = infoList.ItemAt(lower);
return info->ContainsAddress(offset) ? info : NULL;
}
↑ V547 Expression 'remaining < 0' is always false. Unsigned type value is never < 0.