/* * 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 #include #include #include #include #include #include #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, bool isBigEndian, 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, isBigEndian, 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(0); --remaining; break; case 'P': { char tempEncoding = fAddressEncoding; fAddressEncoding = dataReader.Read(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(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(0); TRACE_CFI(" unsigned 16-bit: %" B_PRId64 "\n", address); break; case CFI_ADDRESS_FORMAT_SIGNED_16: address += reader.Read(0); TRACE_CFI(" signed 16-bit: %" B_PRId64 "\n", address); break; case CFI_ADDRESS_FORMAT_UNSIGNED_32: address += reader.Read(0); TRACE_CFI(" unsigned 32-bit: %" B_PRId64 "\n", address); break; case CFI_ADDRESS_FORMAT_SIGNED_32: address += reader.Read(0); TRACE_CFI(" signed 32-bit: %" B_PRId64 "\n", address); break; case CFI_ADDRESS_FORMAT_UNSIGNED_64: address += reader.Read(0); TRACE_CFI(" unsigned 64-bit: %" B_PRId64 "\n", address); break; case CFI_ADDRESS_FORMAT_SIGNED_64: address += reader.Read(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), fDebugAddressSection(NULL), fDebugStringSection(NULL), fDebugStrOffsetsSection(NULL), fDebugRangesSection(NULL), fDebugLineSection(NULL), fDebugLineStrSection(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(fDebugAddressSection); debugInfoFile->PutSection(fDebugStringSection); debugInfoFile->PutSection(fDebugStrOffsetsSection); debugInfoFile->PutSection(fDebugRangesSection); debugInfoFile->PutSection(fDebugLineSection); debugInfoFile->PutSection(fDebugLineStrSection); 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, bool isBigEndian, 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 fDebugAddressSection = debugInfoFile->GetSection(".debug_addr"); fDebugStringSection = debugInfoFile->GetSection(".debug_str"); fDebugStrOffsetsSection = debugInfoFile->GetSection(".debug_str_offsets"); fDebugRangesSection = debugInfoFile->GetSection(".debug_ranges"); fDebugLineSection = debugInfoFile->GetSection(".debug_line"); fDebugLineStrSection = debugInfoFile->GetSection(".debug_line_str"); fDebugFrameSection = debugInfoFile->GetSection(".debug_frame"); if (fDebugFrameSection != NULL) { error = _ParseFrameSection(fDebugFrameSection, addressSize, isBigEndian, 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, isBigEndian, 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(addressSize, isBigEndian); 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(addressSize, isBigEndian); if (error != B_OK) return error; } return B_OK; } status_t DwarfFile::FinishLoading(uint8 addressSize, bool isBigEndian) { 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(addressSize, isBigEndian); 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(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 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(), unit->IsBigEndian()); 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, bool isBigEndian, 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, isBigEndian, subprogramEntry, location, info, inputInterface, outputInterface, _framePointer); } status_t DwarfFile::EvaluateExpression(CompilationUnit* unit, uint8 addressSize, bool isBigEndian, 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, isBigEndian, 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, bool isBigEndian, 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, isBigEndian, subprogramEntry, targetInterface, instructionPointer, objectPointer, hasObjectPointer, framePointer, relocationDelta); DwarfExpressionEvaluator evaluator(&context); return evaluator.EvaluateLocation(expression, expressionLength, _result); } status_t DwarfFile::EvaluateConstantValue(CompilationUnit* unit, uint8 addressSize, bool isBigEndian, 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, isBigEndian, 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, bool isBigEndian, 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(entry); constantValue = constantEntry->ConstValue(); type = constantEntry->GetType(); break; } case DW_TAG_enumerator: constantValue = dynamic_cast(entry) ->ConstValue(); if (DIEEnumerationType* enumerationType = dynamic_cast( entry->Parent())) { type = enumerationType->GetType(); } break; case DW_TAG_formal_parameter: { DIEFormalParameter* parameterEntry = dynamic_cast(entry); constantValue = parameterEntry->ConstValue(); type = parameterEntry->GetType(); break; } case DW_TAG_template_value_parameter: { DIETemplateValueParameter* parameterEntry = dynamic_cast(entry); constantValue = parameterEntry->ConstValue(); type = parameterEntry->GetType(); break; } case DW_TAG_variable: { DIEVariable* variableEntry = dynamic_cast(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, isBigEndian, 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, isBigEndian, 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(uint8 _addressSize, bool isBigEndian) { // iterate through the debug info section DataReader dataReader(fDebugInfoSection->Data(), fDebugInfoSection->Size(), _addressSize, isBigEndian); 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(0); if (version >= 5) { uint8 unitType = dataReader.Read(0); if (unitType != DW_UT_compile) { WARNING("\"%s\": Unsupported unit type %d\n", fName, unitType); return B_UNSUPPORTED; } } off_t abbrevOffset; uint8 addressSize; if (version >= 5) { addressSize = dataReader.Read(0); abbrevOffset = dwarf64 ? dataReader.Read(0) : dataReader.Read(0); } else { abbrevOffset = dwarf64 ? dataReader.Read(0) : dataReader.Read(0); addressSize = dataReader.Read(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, isBigEndian, 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(uint8 _addressSize, bool isBigEndian) { DataReader dataReader(fDebugTypesSection->Data(), fDebugTypesSection->Size(), _addressSize, isBigEndian); 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(0); off_t abbrevOffset = dwarf64 ? dataReader.Read(0) : dataReader.Read(0); uint8 addressSize = dataReader.Read(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(0); off_t typeOffset = dwarf64 ? dataReader.Read(0) : dataReader.Read(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, isBigEndian, 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 isBigEndian, 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, isBigEndian); 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(); // If the length is 0, it means a terminator of the CIE. // Then just skip this .debug_frame/.eh_frame section. if (length == 0) return B_OK; // CIE ID/CIE pointer uint64 cieID = dwarf64 ? dataReader.Read(0) : dataReader.Read(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, isBigEndian, 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 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(), unit->IsBigEndian()); DebugInfoEntry* entry; bool endOfEntryList; error = _ParseDebugInfoEntry(dataReader, unit, abbreviationTable, entry, endOfEntryList); if (error != B_OK) return error; DIECompileUnitBase* unitEntry = dynamic_cast(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(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(), unit->IsBigEndian()); DebugInfoEntry* entry; bool endOfEntryList; error = _ParseDebugInfoEntry(dataReader, unit, abbreviationTable, entry, endOfEntryList); if (error != B_OK) return error; DIETypeUnit* unitEntry = dynamic_cast(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(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 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(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(), unit->IsBigEndian()); 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::_ReadStringIndirect(BaseUnit* unit, uint64 index, const char*& value) const { if (fDebugStrOffsetsSection == NULL) { WARNING("Invalid DW_FORM_strx*: no debug_str_offsets section!\n"); return B_BAD_DATA; } uint64 strOffsetsBase = unit->IsDwarf64() ? 16 : 8; uint64 offsetSize = unit->IsDwarf64() ? 8 : 4; if (strOffsetsBase + index * offsetSize >= fDebugStrOffsetsSection->Size()) { WARNING("Invalid DW_FORM_strx* index: %" B_PRIu64 "\n", index); return B_BAD_DATA; } const char *strOffsets = (const char*)fDebugStrOffsetsSection->Data() + strOffsetsBase; uint64 offset = unit->IsDwarf64() ? ((uint64*)strOffsets)[index] : ((uint32*)strOffsets)[index]; if (offset >= fDebugStringSection->Size()) { WARNING("Invalid DW_FORM_strx* offset: %" B_PRIu64 "\n", offset); return B_BAD_DATA; } value = (const char*)fDebugStringSection->Data() + offset; return B_OK; } status_t DwarfFile::_ReadAddressIndirect(BaseUnit* unit, uint64 index, uint64& value) const { if (fDebugAddressSection == NULL) { WARNING("Invalid DW_FORM_addrx*: no debug_addr section!\n"); return B_BAD_DATA; } uint64 addrBase = unit->IsDwarf64() ? 16 : 8; if (addrBase + index * unit->AddressSize() >= fDebugAddressSection->Size()) { WARNING("Invalid DW_FORM_addrx* index: %" B_PRIu64 "\n", index); return B_BAD_DATA; } const char *addrPtr = (const char*)fDebugAddressSection->Data() + addrBase + index * unit->AddressSize(); if (unit->AddressSize() == 8) value = *(uint64*)addrPtr; else value = *(uint32*)addrPtr; return B_OK; } status_t DwarfFile::_ParseEntryAttributes(DataReader& dataReader, BaseUnit* unit, DebugInfoEntry* entry, AbbreviationEntry& abbreviationEntry) { uint32 attributeName; uint32 attributeForm; int32 attributeImplicitConst = 0; while (abbreviationEntry.GetNextAttribute(attributeName, attributeForm, attributeImplicitConst)) { // 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(0); break; case DW_FORM_block4: blockLength = dataReader.Read(0); break; case DW_FORM_data2: value = dataReader.Read(0); break; case DW_FORM_data4: value = dataReader.Read(0); break; case DW_FORM_data8: value = dataReader.Read(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(0); break; case DW_FORM_data1: value = dataReader.Read(0); break; case DW_FORM_flag: attributeValue.SetToFlag(dataReader.Read(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(0) : dataReader.Read(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(0) : (uint64)dataReader.Read(0); refType = dwarf_reference_type_global; break; case DW_FORM_ref1: value = dataReader.Read(0); break; case DW_FORM_ref2: value = dataReader.Read(0); break; case DW_FORM_ref4: value = dataReader.Read(0); break; case DW_FORM_ref8: value = dataReader.Read(0); break; case DW_FORM_ref_udata: value = dataReader.ReadUnsignedLEB128(0); break; case DW_FORM_flag_present: attributeValue.SetToFlag(true); break; case DW_FORM_strx: { uint64 index = dataReader.ReadUnsignedLEB128(0); const char* strValue; status_t res = _ReadStringIndirect(unit, index, strValue); if (res != B_OK) return res; attributeValue.SetToString(strValue); break; } case DW_FORM_addrx: { uint64 index = dataReader.ReadUnsignedLEB128(0); status_t res = _ReadAddressIndirect(unit, index, value); if (res != B_OK) return res; break; } case DW_FORM_line_strp: { if (fDebugLineStrSection != NULL) { uint64 offset = unit->IsDwarf64() ? dataReader.Read(0) : dataReader.Read(0); if (offset >= fDebugLineStrSection->Size()) { WARNING("Invalid DW_FORM_line_strp offset: %" B_PRIu64 "\n", offset); return B_BAD_DATA; } attributeValue.SetToString( (const char*)fDebugLineStrSection->Data() + offset); } else { WARNING("Invalid DW_FORM_line_strp: no debug_line_str section!\n"); return B_BAD_DATA; } break; } case DW_FORM_ref_sig8: fTypesSectionRequired = true; value = dataReader.Read(0); refType = dwarf_reference_type_signature; break; case DW_FORM_implicit_const: value = attributeImplicitConst; break; case DW_FORM_sec_offset: value = unit->IsDwarf64() ? dataReader.Read(0) : (uint64)dataReader.Read(0); break; case DW_FORM_strx1: case DW_FORM_strx2: case DW_FORM_strx3: case DW_FORM_strx4: { size_t numBytes = attributeForm - DW_FORM_strx1 + 1; uint64 index = dataReader.ReadUInt(numBytes, 0); const char* strValue; status_t res = _ReadStringIndirect(unit, index, strValue); if (res != B_OK) return res; attributeValue.SetToString(strValue); break; } case DW_FORM_addrx1: case DW_FORM_addrx2: case DW_FORM_addrx3: case DW_FORM_addrx4: { size_t numBytes = attributeForm - DW_FORM_addrx1 + 1; uint64 index = dataReader.ReadUInt(numBytes, 0); status_t res = _ReadAddressIndirect(unit, index, value); if (res != B_OK) return res; 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_ADDRPTR: attributeValue.SetToAddrPtr(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_LOCLIST: attributeValue.SetToLocationList(value); break; case ATTRIBUTE_CLASS_LOCLISTPTR: attributeValue.SetToLocationListPointer(value); break; case ATTRIBUTE_CLASS_MACPTR: attributeValue.SetToMacroPointer(value); break; case ATTRIBUTE_CLASS_RANGELIST: attributeValue.SetToRangeList(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; case ATTRIBUTE_CLASS_STROFFSETSPTR: attributeValue.SetToStrOffsetsPtr(value); 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::_ParseLineInfoFormatString(CompilationUnit* unit, DataReader &dataReader, uint64 format, const char*& value) { switch (format) { case DW_FORM_string: value = dataReader.ReadString(); break; case DW_FORM_line_strp: { if (fDebugLineStrSection == NULL) { WARNING("Invalid DW_FORM_line_strp: no line_str section!\n"); return B_BAD_DATA; } target_addr_t offset = unit->IsDwarf64() ? dataReader.Read(0) : dataReader.Read(0); if (offset > fDebugLineStrSection->Size()) { WARNING("Invalid DW_FORM_line_strp offset: %" B_PRIu64 "\n", offset); return B_BAD_DATA; } value = (const char*)fDebugLineStrSection->Data() + offset; break; } case DW_FORM_strp: { if (fDebugStringSection == NULL) { WARNING("Invalid DW_FORM_strp: no string section!\n"); return B_BAD_DATA; } target_addr_t offset = unit->IsDwarf64() ? dataReader.Read(0) : dataReader.Read(0); if (offset > fDebugStringSection->Size()) { WARNING("Invalid DW_FORM_strp offset: %" B_PRIu64 "\n", offset); return B_BAD_DATA; } value = (const char*)fDebugStringSection->Data() + offset; break; } case DW_FORM_strp_sup: return B_UNSUPPORTED; break; default: WARNING("DwarfFile::_ParseLineInfoFormatString(\"%s\"): unsupported " "field type %" PRIu64 "\n", fName, format); return B_BAD_DATA; } return B_OK; } status_t DwarfFile::_ParseLineInfoFormatUint(CompilationUnit* unit, DataReader &dataReader, uint64 format, uint64 &value) { switch (format) { case DW_FORM_data1: value = dataReader.Read(0); break; case DW_FORM_data2: value = dataReader.Read(0); break; case DW_FORM_data4: value = dataReader.Read(0); break; case DW_FORM_data8: value = dataReader.Read(0); break; case DW_FORM_udata: value = dataReader.ReadUnsignedLEB128(0); break; default: WARNING("DwarfFile::_ParseLineInfoFormatUint(\"%s\"): unsupported " "field type %" PRIu64 "\n", fName, format); return B_BAD_DATA; } 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->IsBigEndian()); // 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(0); if (version < 2 || version > 5) { WARNING("DwarfFile::_ParseLineInfo(\"%s\"): unsupported " "version %d\n", fName, version); return B_UNSUPPORTED; } uint8 addressSize = unit->AddressSize(); uint8 segmentSelectorSize = 0; if (version >= 5) { addressSize = dataReader.Read(0); if (addressSize != 4 && addressSize != 8) { WARNING("DwarfFile::_ParseLineInfo(\"%s\"): unsupported " "addressSize %d\n", fName, addressSize); return B_BAD_DATA; } segmentSelectorSize = dataReader.Read(0); if (segmentSelectorSize != 0) { WARNING("DwarfFile::_ParseLineInfo(\"%s\"): unsupported " "segmentSelectorSize %d\n", fName, segmentSelectorSize); return B_BAD_DATA; } } // header_length (4/8) uint64 headerLength = dwarf64 ? dataReader.Read(0) : (uint64)dataReader.Read(0); off_t headerOffset = dataReader.Offset(); if ((uint64)dataReader.BytesRemaining() < headerLength) return B_BAD_DATA; // minimum instruction length uint8 minInstructionLength = dataReader.Read(0); uint8 maxOpsPerInstruction; if (version >= 4) maxOpsPerInstruction = dataReader.Read(0); else maxOpsPerInstruction = 1; if (maxOpsPerInstruction != 1) { WARNING("DwarfFile::_ParseLineInfo(\"%s\"): unsupported " "maxOpsPerInstruction %u\n", fName, maxOpsPerInstruction); return B_UNSUPPORTED; } // default is statement bool defaultIsStatement = dataReader.Read(0) != 0; // line_base (sbyte) int8 lineBase = (int8)dataReader.Read(0); // line_range (ubyte) uint8 lineRange = dataReader.Read(0); // opcode_base (ubyte) uint8 opcodeBase = dataReader.Read(0); // standard_opcode_lengths (ubyte[]) const uint8* standardOpcodeLengths = (const uint8*)dataReader.Data(); dataReader.Skip(opcodeBase - 1); if (dataReader.HasOverflow()) return B_BAD_DATA; TRACE_LINES(" unitLength: %" B_PRIu64 "\n", unitLength); TRACE_LINES(" version: %u\n", version); if (version >= 5) { TRACE_LINES(" addressSize: %u\n", addressSize); TRACE_LINES(" segmentSelectorSize: %u\n", segmentSelectorSize); } TRACE_LINES(" headerLength: %" B_PRIu64 "\n", headerLength); TRACE_LINES(" minInstructionLength: %u\n", minInstructionLength); if (version >= 4) TRACE_LINES(" maxOpsPerInstruction: %u\n", maxOpsPerInstruction); TRACE_LINES(" defaultIsStatement: %d\n", defaultIsStatement); TRACE_LINES(" lineBase: %d\n", lineBase); TRACE_LINES(" lineRange: %u\n", lineRange); TRACE_LINES(" opcodeBase: %u\n", opcodeBase); if (version >= 5) { uint8 dirEntryFormatCount = dataReader.Read(0); TRACE_LINES(" dirEntryFormatCount: %u\n", dirEntryFormatCount); off_t dirEntryFormatOffset = dataReader.Offset(); for (unsigned int i = 0; i < dirEntryFormatCount; i++) { TRACE_LINES_ONLY(uint64 content =) dataReader.ReadUnsignedLEB128(0); TRACE_LINES_ONLY(uint64 format =) dataReader.ReadUnsignedLEB128(0); TRACE_LINES(" content: %" B_PRIu64 "\n", content); TRACE_LINES(" format: %" B_PRIu64 "\n", format); } off_t dirEntryFormatLength = dataReader.Offset() - dirEntryFormatOffset; DataReader dirEntryFormatReader = dataReader.RestrictedReader(-dirEntryFormatLength, dirEntryFormatLength); uint8 dirCount = dataReader.Read(0); TRACE_LINES(" dirCount: %u\n", dirCount); for (unsigned int i = 0; i < dirCount; i++) { dirEntryFormatReader.SeekAbsolute(0); for (unsigned int j = 0; j < dirEntryFormatCount; j++) { uint64 content = dirEntryFormatReader.ReadUnsignedLEB128(0); uint64 format = dirEntryFormatReader.ReadUnsignedLEB128(0); if (content != DW_LNCT_path) { WARNING("DwarfFile::_ParseLineInfo(\"%s\"): unsupported " "field in dirs %" PRIu64 "\n", fName, content); return B_UNSUPPORTED; } const char* directory; status_t res = _ParseLineInfoFormatString(unit, dataReader, format, directory); if (res != B_OK) return res; TRACE_LINES(" \"%s\"\n", directory); if (!unit->AddDirectory(directory)) return B_NO_MEMORY; } } uint8 fileNameEntryFormatCount = dataReader.Read(0); TRACE_LINES(" fileNameFormatCount: %u\n", fileNameEntryFormatCount); off_t fileNameEntryFormatOffset = dataReader.Offset(); for (unsigned int i = 0; i < fileNameEntryFormatCount; i++) { TRACE_LINES_ONLY(uint64 content =) dataReader.ReadUnsignedLEB128(0); TRACE_LINES_ONLY(uint64 format =) dataReader.ReadUnsignedLEB128(0); TRACE_LINES(" content: %" B_PRIu64 "\n", content); TRACE_LINES(" format: %" B_PRIu64 "\n", format); } off_t fileNameEntryFormatLength = dataReader.Offset() - fileNameEntryFormatOffset; DataReader fileNameEntryFormatReader = dataReader.RestrictedReader(-fileNameEntryFormatLength, fileNameEntryFormatLength); uint8 fileNameCount = dataReader.Read(0); TRACE_LINES(" fileNameCount: %u\n", dirCount); for (unsigned int i = 0; i < fileNameCount; i++) { const char* fileName = NULL; uint64 dirIndex = 0xffffffffffffffffull; uint64 modificationTime = 0; uint64 fileLength = 0; fileNameEntryFormatReader.SeekAbsolute(0); for (unsigned int j = 0; j < fileNameEntryFormatCount; j++) { uint64 content = fileNameEntryFormatReader.ReadUnsignedLEB128(0); uint64 format = fileNameEntryFormatReader.ReadUnsignedLEB128(0); status_t res; switch (content) { case DW_LNCT_path: res = _ParseLineInfoFormatString(unit, dataReader, format, fileName); if (res != B_OK) return res; break; case DW_LNCT_directory_index: res = _ParseLineInfoFormatUint(unit, dataReader, format, dirIndex); if (res != B_OK) return res; break; case DW_LNCT_timestamp: res = _ParseLineInfoFormatUint(unit, dataReader, format, modificationTime); if (res != B_OK) return res; break; case DW_LNCT_size: res = _ParseLineInfoFormatUint(unit, dataReader, format, fileLength); if (res != B_OK) return res; break; case DW_LNCT_MD5: if (format != DW_FORM_data16) return B_BAD_DATA; dataReader.Skip(16); break; default: WARNING("DwarfFile::_ParseLineInfo(\"%s\"): unsupported " "field in files %" PRIu64 "\n", fName, content); return B_UNSUPPORTED; } } if ((fileName != NULL) && (dirIndex != 0xffffffffffffffffull)) { TRACE_LINES(" \"%s\", dir index: %" B_PRIu64 "\n", fileName, dirIndex); if (!unit->AddFile(fileName, dirIndex)) return B_NO_MEMORY; } } } else { // 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, bool isBigEndian, 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, unit != NULL ? unit->IsBigEndian() : isBigEndian); 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, isBigEndian, 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(0) : dataReader.Read(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, isBigEndian, 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, isBigEndian, 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, isBigEndian, 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, bool isBigEndian, 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, unit != NULL ? unit->IsBigEndian() : isBigEndian); // 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(0) : dataReader.Read(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(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(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(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(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(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(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(uint8 _addressSize, bool isBigEndian) { 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(), _addressSize, isBigEndian); 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, _addressSize, isBigEndian); _ParsePublicTypesInfo(unitDataReader, dwarf64); dataReader.SeekAbsolute(unitLengthOffset + unitLength); } return B_OK; } status_t DwarfFile::_ParsePublicTypesInfo(DataReader& dataReader, bool dwarf64) { int version = dataReader.Read(0); if (version != 2) { TRACE_PUBTYPES(" pubtypes version %d unsupported\n", version); return B_UNSUPPORTED; } TRACE_PUBTYPES_ONLY(off_t debugInfoOffset =) dwarf64 ? dataReader.Read(0) : (uint64)dataReader.Read(0); TRACE_PUBTYPES_ONLY(off_t debugInfoSize =) dwarf64 ? dataReader.Read(0) : (uint64)dataReader.Read(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(0) : (uint64)dataReader.Read(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(), unit->IsBigEndian()); 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(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) { TRACE_DIE("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; }