/* * Copyright 2009-2012, Ingo Weinhold, ingo_weinhold@gmx.de. * Copyright 2012-2018, Rene Gollent, rene@gollent.com. * Distributed under the terms of the MIT License. */ #include "DwarfImageDebugInfo.h" #include #include #include #include #include #include #include #include "Architecture.h" #include "BasicFunctionDebugInfo.h" #include "CLanguage.h" #include "CompilationUnit.h" #include "CppLanguage.h" #include "CpuState.h" #include "DebuggerInterface.h" #include "DebugInfoEntries.h" #include "Demangler.h" #include "DisassembledCode.h" #include "Dwarf.h" #include "DwarfFile.h" #include "DwarfFunctionDebugInfo.h" #include "DwarfStackFrameDebugInfo.h" #include "DwarfTargetInterface.h" #include "DwarfTypeFactory.h" #include "DwarfTypes.h" #include "DwarfUtils.h" #include "ElfFile.h" #include "FileManager.h" #include "FileSourceCode.h" #include "FunctionID.h" #include "FunctionInstance.h" #include "GlobalTypeLookup.h" #include "Image.h" #include "ImageDebugInfo.h" #include "InstructionInfo.h" #include "LocatableFile.h" #include "Register.h" #include "RegisterMap.h" #include "SourceFile.h" #include "StackFrame.h" #include "Statement.h" #include "StringUtils.h" #include "SymbolInfo.h" #include "TargetAddressRangeList.h" #include "Team.h" #include "TeamFunctionSourceInformation.h" #include "TeamMemory.h" #include "Tracing.h" #include "TypeLookupConstraints.h" #include "UnsupportedLanguage.h" #include "Variable.h" #include "ValueLocation.h" namespace { // #pragma mark - HasTypePredicate template struct HasTypePredicate { inline bool operator()(EntryType* entry) const { return entry->GetType() != NULL; } }; } // #pragma mark - BasicTargetInterface struct DwarfImageDebugInfo::BasicTargetInterface : DwarfTargetInterface { BasicTargetInterface(const Register* registers, int32 registerCount, RegisterMap* fromDwarfMap, Architecture* architecture, TeamMemory* teamMemory) : fRegisters(registers), fRegisterCount(registerCount), fFromDwarfMap(fromDwarfMap), fArchitecture(architecture), fTeamMemory(teamMemory) { fFromDwarfMap->AcquireReference(); } ~BasicTargetInterface() { fFromDwarfMap->ReleaseReference(); } virtual uint32 CountRegisters() const { return fRegisterCount; } virtual uint32 RegisterValueType(uint32 index) const { const Register* reg = _RegisterAt(index); return reg != NULL ? reg->ValueType() : 0; } virtual bool GetRegisterValue(uint32 index, BVariant& _value) const { return false; } virtual bool SetRegisterValue(uint32 index, const BVariant& value) { return false; } virtual bool IsCalleePreservedRegister(uint32 index) const { const Register* reg = _RegisterAt(index); return reg != NULL && reg->IsCalleePreserved(); } virtual status_t InitRegisterRules(CfaContext& context) const { return fArchitecture->InitRegisterRules(context); } virtual bool ReadMemory(target_addr_t address, void* buffer, size_t size) const { ssize_t bytesRead = fTeamMemory->ReadMemory(address, buffer, size); return bytesRead >= 0 && (size_t)bytesRead == size; } virtual bool ReadValueFromMemory(target_addr_t address, uint32 valueType, BVariant& _value) const { return fArchitecture->ReadValueFromMemory(address, valueType, _value) == B_OK; } virtual bool ReadValueFromMemory(target_addr_t addressSpace, target_addr_t address, uint32 valueType, BVariant& _value) const { return fArchitecture->ReadValueFromMemory(addressSpace, address, valueType, _value) == B_OK; } protected: const Register* _RegisterAt(uint32 dwarfIndex) const { int32 index = fFromDwarfMap->MapRegisterIndex(dwarfIndex); return index >= 0 && index < fRegisterCount ? fRegisters + index : NULL; } protected: const Register* fRegisters; int32 fRegisterCount; RegisterMap* fFromDwarfMap; Architecture* fArchitecture; TeamMemory* fTeamMemory; }; // #pragma mark - UnwindTargetInterface struct DwarfImageDebugInfo::UnwindTargetInterface : BasicTargetInterface { UnwindTargetInterface(const Register* registers, int32 registerCount, RegisterMap* fromDwarfMap, RegisterMap* toDwarfMap, CpuState* cpuState, Architecture* architecture, TeamMemory* teamMemory) : BasicTargetInterface(registers, registerCount, fromDwarfMap, architecture, teamMemory), fToDwarfMap(toDwarfMap), fCpuState(cpuState) { fToDwarfMap->AcquireReference(); fCpuState->AcquireReference(); } ~UnwindTargetInterface() { fToDwarfMap->ReleaseReference(); fCpuState->ReleaseReference(); } virtual bool GetRegisterValue(uint32 index, BVariant& _value) const { const Register* reg = _RegisterAt(index); if (reg == NULL) return false; return fCpuState->GetRegisterValue(reg, _value); } virtual bool SetRegisterValue(uint32 index, const BVariant& value) { const Register* reg = _RegisterAt(index); if (reg == NULL) return false; return fCpuState->SetRegisterValue(reg, value); } private: RegisterMap* fToDwarfMap; CpuState* fCpuState; }; // #pragma mark - EntryListWrapper /*! Wraps a DebugInfoEntryList, which is a typedef and thus cannot appear in the header, since our policy disallows us to include DWARF headers there. */ struct DwarfImageDebugInfo::EntryListWrapper { const DebugInfoEntryList& list; EntryListWrapper(const DebugInfoEntryList& list) : list(list) { } }; // #pragma mark - DwarfImageDebugInfo::TypeNameKey struct DwarfImageDebugInfo::TypeNameKey { BString typeName; TypeNameKey(const BString& typeName) : typeName(typeName) { } uint32 HashValue() const { return StringUtils::HashValue(typeName); } bool operator==(const TypeNameKey& other) const { return typeName == other.typeName; } }; // #pragma mark - DwarfImageDebugInfo::TypeNameEntry struct DwarfImageDebugInfo::TypeNameEntry : TypeNameKey { TypeNameEntry* next; TypeEntryList types; TypeNameEntry(const BString& name) : TypeNameKey(name), types(10, true) { } ~TypeNameEntry() { } }; // #pragma mark - DwarfImageDebugInfo::TypeNameEntryHashDefinition struct DwarfImageDebugInfo::TypeNameEntryHashDefinition { typedef TypeNameKey KeyType; typedef TypeNameEntry ValueType; size_t HashKey(const TypeNameKey& key) const { return key.HashValue(); } size_t Hash(const TypeNameEntry* value) const { return value->HashValue(); } bool Compare(const TypeNameKey& key, const TypeNameEntry* value) const { return key == *value; } TypeNameEntry*& GetLink(TypeNameEntry* value) const { return value->next; } }; // #pragma mark - DwarfImageDebugInfo::TypeEntryInfo struct DwarfImageDebugInfo::TypeEntryInfo { DIEType* type; CompilationUnit* unit; TypeEntryInfo(DIEType* type, CompilationUnit* unit) : type(type), unit(unit) { } }; // #pragma mark - DwarfImageDebugInfo DwarfImageDebugInfo::DwarfImageDebugInfo(const ImageInfo& imageInfo, DebuggerInterface* interface, Architecture* architecture, FileManager* fileManager, GlobalTypeLookup* typeLookup, GlobalTypeCache* typeCache, TeamFunctionSourceInformation* sourceInfo, DwarfFile* file) : fLock("dwarf image debug info"), fImageInfo(imageInfo), fDebuggerInterface(interface), fArchitecture(architecture), fFileManager(fileManager), fTypeLookup(typeLookup), fTypeCache(typeCache), fSourceInfo(sourceInfo), fTypeNameTable(NULL), fFile(file), fTextSegment(NULL), fRelocationDelta(0), fTextSectionStart(0), fTextSectionEnd(0), fPLTSectionStart(0), fPLTSectionEnd(0) { fDebuggerInterface->AcquireReference(); fFile->AcquireReference(); fTypeCache->AcquireReference(); } DwarfImageDebugInfo::~DwarfImageDebugInfo() { fDebuggerInterface->ReleaseReference(); fFile->ReleaseReference(); fTypeCache->ReleaseReference(); TypeNameEntry* entry = fTypeNameTable->Clear(true); while (entry != NULL) { TypeNameEntry* next = entry->next; delete entry; entry = next; } delete fTypeNameTable; } status_t DwarfImageDebugInfo::Init() { status_t error = fLock.InitCheck(); if (error != B_OK) return error; fTextSegment = fFile->GetElfFile()->TextSegment(); if (fTextSegment == NULL) return B_ENTRY_NOT_FOUND; fRelocationDelta = fImageInfo.TextBase() - fTextSegment->LoadAddress(); ElfSection* section = fFile->GetElfFile()->FindSection(".text"); if (section != NULL) { fTextSectionStart = section->LoadAddress() + fRelocationDelta; fTextSectionEnd = fTextSectionStart + section->Size(); } section = fFile->GetElfFile()->FindSection(".plt"); if (section != NULL) { fPLTSectionStart = section->LoadAddress() + fRelocationDelta; fPLTSectionEnd = fPLTSectionStart + section->Size(); } return _BuildTypeNameTable(); } status_t DwarfImageDebugInfo::GetFunctions(const BObjectList& symbols, BObjectList& functions) { TRACE_IMAGES("DwarfImageDebugInfo::GetFunctions()\n"); TRACE_IMAGES(" %" B_PRId32 " compilation units\n", fFile->CountCompilationUnits()); status_t error = B_OK; for (int32 i = 0; CompilationUnit* unit = fFile->CompilationUnitAt(i); i++) { DIECompileUnitBase* unitEntry = unit->UnitEntry(); // printf(" %s:\n", unitEntry->Name()); // printf(" address ranges:\n"); // TargetAddressRangeList* rangeList = unitEntry->AddressRanges(); // if (rangeList != NULL) { // int32 count = rangeList->CountRanges(); // for (int32 i = 0; i < count; i++) { // TargetAddressRange range = rangeList->RangeAt(i); // printf(" %#llx - %#llx\n", range.Start(), range.End()); // } // } else { // printf(" %#llx - %#llx\n", (target_addr_t)unitEntry->LowPC(), // (target_addr_t)unitEntry->HighPC()); // } // printf(" functions:\n"); for (DebugInfoEntryList::ConstIterator it = unitEntry->OtherChildren().GetIterator(); DebugInfoEntry* entry = it.Next();) { if (entry->Tag() == DW_TAG_subprogram) { DIESubprogram* subprogramEntry = static_cast(entry); error = _AddFunction(subprogramEntry, unit, functions); if (error != B_OK) return error; } DIENamespace* nsEntry = dynamic_cast(entry); if (nsEntry != NULL) { error = _RecursiveTraverseNamespaceForFunctions(nsEntry, unit, functions); if (error != B_OK) return error; } } } if (fFile->CountCompilationUnits() != 0) return B_OK; // if we had no compilation units, fall back to providing basic // debug infos with DWARF-supported call frame unwinding, // if available. if (fFile->HasFrameInformation()) { return SpecificImageDebugInfo::GetFunctionsFromSymbols(symbols, functions, fDebuggerInterface, fImageInfo, this); } return B_OK; } status_t DwarfImageDebugInfo::GetType(GlobalTypeCache* cache, const BString& name, const TypeLookupConstraints& constraints, Type*& _type) { TypeNameEntry* entry = fTypeNameTable->Lookup(name); if (entry == NULL) return B_ENTRY_NOT_FOUND; for (int32 i = 0; TypeEntryInfo* info = entry->types.ItemAt(i); i++) { DIEType* typeEntry = info->type; if (constraints.HasTypeKind()) { if (dwarf_tag_to_type_kind(typeEntry->Tag()) != constraints.TypeKind()) { continue; } if (!_EvaluateBaseTypeConstraints(typeEntry, constraints)) continue; } if (constraints.HasSubtypeKind() && dwarf_tag_to_subtype_kind(typeEntry->Tag()) != constraints.SubtypeKind()) { continue; } int32 registerCount = fArchitecture->CountRegisters(); const Register* registers = fArchitecture->Registers(); // get the DWARF <-> architecture register maps RegisterMap* toDwarfMap; RegisterMap* fromDwarfMap; status_t error = fArchitecture->GetDwarfRegisterMaps(&toDwarfMap, &fromDwarfMap); if (error != B_OK) return error; BReference toDwarfMapReference(toDwarfMap, true); BReference fromDwarfMapReference(fromDwarfMap, true); // create the target interface BasicTargetInterface* targetInterface = new(std::nothrow) BasicTargetInterface(registers, registerCount, fromDwarfMap, fArchitecture, fDebuggerInterface); if (targetInterface == NULL) return B_NO_MEMORY; BReference targetInterfaceReference( targetInterface, true); DwarfTypeContext* typeContext = new(std::nothrow) DwarfTypeContext(fArchitecture, fImageInfo.ImageID(), fFile, info->unit, NULL, 0, 0, fRelocationDelta, targetInterface, NULL); if (typeContext == NULL) return B_NO_MEMORY; BReference typeContextReference(typeContext, true); // create the type DwarfType* type; DwarfTypeFactory typeFactory(typeContext, fTypeLookup, cache); error = typeFactory.CreateType(typeEntry, type); if (error != B_OK) continue; _type = type; return B_OK; } return B_ENTRY_NOT_FOUND; } bool DwarfImageDebugInfo::HasType(const BString& name, const TypeLookupConstraints& constraints) const { TypeNameEntry* entry = fTypeNameTable->Lookup(name); if (entry == NULL) return false; for (int32 i = 0; TypeEntryInfo* info = entry->types.ItemAt(i); i++) { DIEType* typeEntry = info->type; if (constraints.HasTypeKind()) { if (dwarf_tag_to_type_kind(typeEntry->Tag()) != constraints.TypeKind()) { continue; } if (!_EvaluateBaseTypeConstraints(typeEntry, constraints)) continue; } if (constraints.HasSubtypeKind() && dwarf_tag_to_subtype_kind(typeEntry->Tag()) != constraints.SubtypeKind()) { continue; } return true; } return false; } AddressSectionType DwarfImageDebugInfo::GetAddressSectionType(target_addr_t address) { if (address >= fTextSectionStart && address < fTextSectionEnd) return ADDRESS_SECTION_TYPE_FUNCTION; if (address >= fPLTSectionStart && address < fPLTSectionEnd) return ADDRESS_SECTION_TYPE_PLT; return ADDRESS_SECTION_TYPE_UNKNOWN; } status_t DwarfImageDebugInfo::CreateFrame(Image* image, FunctionInstance* functionInstance, CpuState* cpuState, bool getFullFrameInfo, ReturnValueInfoList* returnValueInfos, StackFrame*& _frame, CpuState*& _previousCpuState) { DwarfFunctionDebugInfo* function = dynamic_cast( functionInstance->GetFunctionDebugInfo()); FunctionID* functionID = functionInstance->GetFunctionID(); BReference functionIDReference; if (functionID != NULL) functionIDReference.SetTo(functionID, true); DIESubprogram* entry = function != NULL ? function->SubprogramEntry() : NULL; TRACE_CFI("DwarfImageDebugInfo::CreateFrame(): subprogram DIE: %p, " "function: %s\n", entry, functionID->FunctionName().String()); int32 registerCount = fArchitecture->CountRegisters(); const Register* registers = fArchitecture->Registers(); // get the DWARF <-> architecture register maps RegisterMap* toDwarfMap; RegisterMap* fromDwarfMap; status_t error = fArchitecture->GetDwarfRegisterMaps(&toDwarfMap, &fromDwarfMap); if (error != B_OK) return error; BReference toDwarfMapReference(toDwarfMap, true); BReference fromDwarfMapReference(fromDwarfMap, true); // create a clean CPU state for the previous frame CpuState* previousCpuState; error = fArchitecture->CreateCpuState(previousCpuState); if (error != B_OK) return error; BReference previousCpuStateReference(previousCpuState, true); // create the target interfaces UnwindTargetInterface* inputInterface = new(std::nothrow) UnwindTargetInterface(registers, registerCount, fromDwarfMap, toDwarfMap, cpuState, fArchitecture, fDebuggerInterface); if (inputInterface == NULL) return B_NO_MEMORY; BReference inputInterfaceReference(inputInterface, true); UnwindTargetInterface* outputInterface = new(std::nothrow) UnwindTargetInterface(registers, registerCount, fromDwarfMap, toDwarfMap, previousCpuState, fArchitecture, fDebuggerInterface); if (outputInterface == NULL) return B_NO_MEMORY; BReference outputInterfaceReference(outputInterface, true); // do the unwinding target_addr_t instructionPointer = cpuState->InstructionPointer() - fRelocationDelta; target_addr_t framePointer; CompilationUnit* unit = function != NULL ? function->GetCompilationUnit() : NULL; error = fFile->UnwindCallFrame(unit, fArchitecture->AddressSize(), fArchitecture->IsBigEndian(), entry, instructionPointer, inputInterface, outputInterface, framePointer); if (error != B_OK) { TRACE_CFI("Failed to unwind call frame: %s\n", strerror(error)); return B_UNSUPPORTED; } TRACE_CFI_ONLY( TRACE_CFI("unwound registers:\n"); for (int32 i = 0; i < registerCount; i++) { const Register* reg = registers + i; BVariant value; if (previousCpuState->GetRegisterValue(reg, value)) { TRACE_CFI(" %3s: %#" B_PRIx64 "\n", reg->Name(), value.ToUInt64()); } else TRACE_CFI(" %3s: undefined\n", reg->Name()); } ) // create the stack frame debug info DIESubprogram* subprogramEntry = function != NULL ? function->SubprogramEntry() : NULL; DwarfStackFrameDebugInfo* stackFrameDebugInfo = new(std::nothrow) DwarfStackFrameDebugInfo(fArchitecture, fImageInfo.ImageID(), fFile, unit, subprogramEntry, fTypeLookup, fTypeCache, instructionPointer, framePointer, fRelocationDelta, inputInterface, fromDwarfMap); if (stackFrameDebugInfo == NULL) return B_NO_MEMORY; BReference stackFrameDebugInfoReference( stackFrameDebugInfo, true); error = stackFrameDebugInfo->Init(); if (error != B_OK) return error; // create the stack frame StackFrame* frame = new(std::nothrow) StackFrame(STACK_FRAME_TYPE_STANDARD, cpuState, framePointer, cpuState->InstructionPointer(), stackFrameDebugInfo); if (frame == NULL) return B_NO_MEMORY; BReference frameReference(frame, true); error = frame->Init(); if (error != B_OK) return error; frame->SetReturnAddress(previousCpuState->InstructionPointer()); // Note, this is correct, since we actually retrieved the return // address. Our caller will fix the IP for us. // The subprogram entry may not be available since this may be a case // where .eh_frame was used to unwind the stack without other DWARF // info being available. if (subprogramEntry != NULL && getFullFrameInfo) { // create function parameter objects for (DebugInfoEntryList::ConstIterator it = subprogramEntry->Parameters().GetIterator(); DebugInfoEntry* entry = it.Next();) { if (entry->Tag() != DW_TAG_formal_parameter) continue; BString parameterName; DwarfUtils::GetDIEName(entry, parameterName); if (parameterName.Length() == 0) continue; DIEFormalParameter* parameterEntry = dynamic_cast(entry); Variable* parameter; if (stackFrameDebugInfo->CreateParameter(functionID, parameterEntry, parameter) != B_OK) { continue; } BReference parameterReference(parameter, true); if (!frame->AddParameter(parameter)) return B_NO_MEMORY; } // create objects for the local variables _CreateLocalVariables(unit, frame, functionID, *stackFrameDebugInfo, instructionPointer, functionInstance->Address() - fRelocationDelta, subprogramEntry->Variables(), subprogramEntry->Blocks()); if (returnValueInfos != NULL && !returnValueInfos->IsEmpty()) { _CreateReturnValues(returnValueInfos, image, frame, *stackFrameDebugInfo); } } _frame = frameReference.Detach(); _previousCpuState = previousCpuStateReference.Detach(); frame->SetPreviousCpuState(_previousCpuState); return B_OK; } status_t DwarfImageDebugInfo::GetStatement(FunctionDebugInfo* _function, target_addr_t address, Statement*& _statement) { TRACE_CODE("DwarfImageDebugInfo::GetStatement(function: %p, address: %#" B_PRIx64 ")\n", _function, address); DwarfFunctionDebugInfo* function = dynamic_cast(_function); if (function == NULL) { TRACE_LINES(" -> no dwarf function\n"); // fall back to assembly return fArchitecture->GetStatement(function, address, _statement); } AutoLocker locker(fLock); // check whether we have the source code CompilationUnit* unit = function->GetCompilationUnit(); LocatableFile* file = function->SourceFile(); if (file == NULL) { TRACE_CODE(" -> no source file\n"); // no source code -- rather return the assembly statement return fArchitecture->GetStatement(function, address, _statement); } SourceCode* sourceCode = NULL; status_t error = fSourceInfo->GetActiveSourceCode(_function, sourceCode); BReference sourceReference(sourceCode, true); if (error != B_OK || dynamic_cast(sourceCode) != NULL) { // either no source code or disassembly is currently active (i.e. // due to failing to locate the source file on disk or the user // deliberately switching to disassembly view). // return the assembly statement. return fArchitecture->GetStatement(function, address, _statement); } // get the index of the source file in the compilation unit for cheaper // comparison below int32 fileIndex = _GetSourceFileIndex(unit, file); // Get the statement by executing the line number program for the // compilation unit. LineNumberProgram& program = unit->GetLineNumberProgram(); if (!program.IsValid()) { TRACE_CODE(" -> no line number program\n"); return B_BAD_DATA; } // adjust address address -= fRelocationDelta; LineNumberProgram::State state; program.GetInitialState(state); target_addr_t statementAddress = 0; int32 statementLine = -1; int32 statementColumn = -1; while (program.GetNextRow(state)) { // skip statements of other files if (state.file != fileIndex) continue; if (statementAddress != 0 && (state.isStatement || state.isSequenceEnd)) { target_addr_t endAddress = state.address; if (address >= statementAddress && address < endAddress) { ContiguousStatement* statement = new(std::nothrow) ContiguousStatement( SourceLocation(statementLine, statementColumn), TargetAddressRange(fRelocationDelta + statementAddress, endAddress - statementAddress)); if (statement == NULL) return B_NO_MEMORY; _statement = statement; return B_OK; } statementAddress = 0; } if (state.isStatement) { statementAddress = state.address; statementLine = state.line - 1; // discard column info until proper support is implemented // statementColumn = std::max(state.column - 1, (int32)0); statementColumn = 0; } } TRACE_CODE(" -> no line number program match\n"); return B_ENTRY_NOT_FOUND; } status_t DwarfImageDebugInfo::GetStatementAtSourceLocation(FunctionDebugInfo* _function, const SourceLocation& sourceLocation, Statement*& _statement) { DwarfFunctionDebugInfo* function = dynamic_cast(_function); if (function == NULL) return B_BAD_VALUE; target_addr_t functionStartAddress = function->Address() - fRelocationDelta; target_addr_t functionEndAddress = functionStartAddress + function->Size(); TRACE_LINES2("DwarfImageDebugInfo::GetStatementAtSourceLocation(%p, " "(%" B_PRId32 ", %" B_PRId32 ")): function range: %#" B_PRIx64 " - %#" B_PRIx64 "\n", function, sourceLocation.Line(), sourceLocation.Column(), functionStartAddress, functionEndAddress); AutoLocker locker(fLock); // get the source file LocatableFile* file = function->SourceFile(); if (file == NULL) return B_ENTRY_NOT_FOUND; CompilationUnit* unit = function->GetCompilationUnit(); // get the index of the source file in the compilation unit for cheaper // comparison below int32 fileIndex = _GetSourceFileIndex(unit, file); // Get the statement by executing the line number program for the // compilation unit. LineNumberProgram& program = unit->GetLineNumberProgram(); if (!program.IsValid()) return B_BAD_DATA; LineNumberProgram::State state; program.GetInitialState(state); target_addr_t statementAddress = 0; int32 statementLine = -1; int32 statementColumn = -1; while (program.GetNextRow(state)) { bool isOurFile = state.file == fileIndex; if (statementAddress != 0 && (!isOurFile || state.isStatement || state.isSequenceEnd)) { target_addr_t endAddress = state.address; if (statementAddress < endAddress) { TRACE_LINES2(" statement: %#" B_PRIx64 " - %#" B_PRIx64 ", location: (%" B_PRId32 ", %" B_PRId32 ")\n", statementAddress, endAddress, statementLine, statementColumn); } if (statementAddress < endAddress && statementAddress >= functionStartAddress && statementAddress < functionEndAddress && statementLine == (int32)sourceLocation.Line() && statementColumn == (int32)sourceLocation.Column()) { TRACE_LINES2(" -> found statement!\n"); ContiguousStatement* statement = new(std::nothrow) ContiguousStatement( SourceLocation(statementLine, statementColumn), TargetAddressRange(fRelocationDelta + statementAddress, endAddress - statementAddress)); if (statement == NULL) return B_NO_MEMORY; _statement = statement; return B_OK; } statementAddress = 0; } // skip statements of other files if (!isOurFile) continue; if (state.isStatement) { statementAddress = state.address; statementLine = state.line - 1; // discard column info until proper support is implemented // statementColumn = std::max(state.column - 1, (int32)0); statementColumn = 0; } } return B_ENTRY_NOT_FOUND; } status_t DwarfImageDebugInfo::GetSourceLanguage(FunctionDebugInfo* _function, SourceLanguage*& _language) { DwarfFunctionDebugInfo* function = dynamic_cast(_function); if (function == NULL) return B_BAD_VALUE; SourceLanguage* language; CompilationUnit* unit = function->GetCompilationUnit(); switch (unit->UnitEntry()->Language()) { case DW_LANG_C89: case DW_LANG_C: case DW_LANG_C99: language = new(std::nothrow) CLanguage; break; case DW_LANG_C_plus_plus: language = new(std::nothrow) CppLanguage; break; case 0: default: language = new(std::nothrow) UnsupportedLanguage; break; } if (language == NULL) return B_NO_MEMORY; _language = language; return B_OK; } ssize_t DwarfImageDebugInfo::ReadCode(target_addr_t address, void* buffer, size_t size) { target_addr_t offset = address - fRelocationDelta - fTextSegment->LoadAddress() + fTextSegment->FileOffset(); ssize_t bytesRead = pread(fFile->GetElfFile()->FD(), buffer, size, offset); return bytesRead >= 0 ? bytesRead : errno; } status_t DwarfImageDebugInfo::AddSourceCodeInfo(LocatableFile* file, FileSourceCode* sourceCode) { bool addedAny = false; for (int32 i = 0; CompilationUnit* unit = fFile->CompilationUnitAt(i); i++) { int32 fileIndex = _GetSourceFileIndex(unit, file); if (fileIndex < 0) continue; status_t error = _AddSourceCodeInfo(unit, sourceCode, fileIndex); if (error == B_NO_MEMORY) return error; addedAny |= error == B_OK; } return addedAny ? B_OK : B_ENTRY_NOT_FOUND; } status_t DwarfImageDebugInfo::_AddSourceCodeInfo(CompilationUnit* unit, FileSourceCode* sourceCode, int32 fileIndex) { // Get the statements by executing the line number program for the // compilation unit and filtering the rows for our source file. LineNumberProgram& program = unit->GetLineNumberProgram(); if (!program.IsValid()) return B_BAD_DATA; LineNumberProgram::State state; program.GetInitialState(state); target_addr_t statementAddress = 0; int32 statementLine = -1; int32 statementColumn = -1; while (program.GetNextRow(state)) { TRACE_LINES2(" %#" B_PRIx64 " (%" B_PRId32 ", %" B_PRId32 ", %" B_PRId32 ") %d\n", state.address, state.file, state.line, state.column, state.isStatement); bool isOurFile = state.file == fileIndex; if (statementAddress != 0 && (!isOurFile || state.isStatement || state.isSequenceEnd)) { target_addr_t endAddress = state.address; if (endAddress > statementAddress) { // add the statement status_t error = sourceCode->AddSourceLocation( SourceLocation(statementLine, statementColumn)); if (error != B_OK) return error; TRACE_LINES2(" -> statement: %#" B_PRIx64 " - %#" B_PRIx64 ", source location: (%" B_PRId32 ", %" B_PRId32 ")\n", statementAddress, endAddress, statementLine, statementColumn); } statementAddress = 0; } // skip statements of other files if (!isOurFile) continue; if (state.isStatement) { statementAddress = state.address; statementLine = state.line - 1; // discard column info until proper support is implemented // statementColumn = std::max(state.column - 1, (int32)0); statementColumn = 0; } } return B_OK; } int32 DwarfImageDebugInfo::_GetSourceFileIndex(CompilationUnit* unit, LocatableFile* sourceFile) const { // get the index of the source file in the compilation unit for cheaper // comparison below const char* directory; for (int32 i = 0; const char* fileName = unit->FileAt(i, &directory); i++) { LocatableFile* file = fFileManager->GetSourceFile(directory, fileName); if (file != NULL) { BReference fileReference(file, true); if (file == sourceFile) { return i + 1; // indices are one-based } } } return -1; } status_t DwarfImageDebugInfo::_CreateLocalVariables(CompilationUnit* unit, StackFrame* frame, FunctionID* functionID, DwarfStackFrameDebugInfo& factory, target_addr_t instructionPointer, target_addr_t lowPC, const EntryListWrapper& variableEntries, const EntryListWrapper& blockEntries) { TRACE_LOCALS("DwarfImageDebugInfo::_CreateLocalVariables(): ip: %#" B_PRIx64 ", low PC: %#" B_PRIx64 "\n", instructionPointer, lowPC); // iterate through the variables and add the ones in scope for (DebugInfoEntryList::ConstIterator it = variableEntries.list.GetIterator(); DIEVariable* variableEntry = dynamic_cast(it.Next());) { TRACE_LOCALS(" variableEntry %p, scope start: %" B_PRIu64 "\n", variableEntry, variableEntry->StartScope()); // check the variable's scope if (instructionPointer < lowPC + variableEntry->StartScope()) continue; // add the variable Variable* variable; if (factory.CreateLocalVariable(functionID, variableEntry, variable) != B_OK) { continue; } BReference variableReference(variable, true); if (!frame->AddLocalVariable(variable)) return B_NO_MEMORY; } // iterate through the blocks and find the one we're currently in (if any) for (DebugInfoEntryList::ConstIterator it = blockEntries.list.GetIterator(); DIELexicalBlock* block = dynamic_cast(it.Next());) { TRACE_LOCALS(" lexical block: %p\n", block); // check whether the block has low/high PC attributes if (block->LowPC() != 0) { TRACE_LOCALS(" has lowPC\n"); // yep, compare with the instruction pointer if (instructionPointer < block->LowPC() || instructionPointer >= block->HighPC()) { continue; } } else { TRACE_LOCALS(" no lowPC\n"); // check the address ranges instead TargetAddressRangeList* rangeList = fFile->ResolveRangeList(unit, block->AddressRangesOffset()); if (rangeList == NULL) { TRACE_LOCALS(" failed to get ranges\n"); continue; } BReference rangeListReference(rangeList, true); if (!rangeList->Contains(instructionPointer)) { TRACE_LOCALS(" ranges don't contain IP\n"); continue; } } // found a block -- recurse return _CreateLocalVariables(unit, frame, functionID, factory, instructionPointer, lowPC, block->Variables(), block->Blocks()); } return B_OK; } status_t DwarfImageDebugInfo::_CreateReturnValues(ReturnValueInfoList* returnValueInfos, Image* image, StackFrame* frame, DwarfStackFrameDebugInfo& factory) { for (int32 i = 0; i < returnValueInfos->CountItems(); i++) { Image* targetImage = image; ReturnValueInfo* valueInfo = returnValueInfos->ItemAt(i); target_addr_t subroutineAddress = valueInfo->SubroutineAddress(); CpuState* subroutineState = valueInfo->State(); if (!targetImage->ContainsAddress(subroutineAddress)) { // our current image doesn't contain the target function, // locate the one which does. targetImage = image->GetTeam()->ImageByAddress(subroutineAddress); if (targetImage == NULL) { // nothing we can do, try the next entry (if any) continue; } } status_t result = B_OK; ImageDebugInfo* imageInfo = targetImage->GetImageDebugInfo(); if (imageInfo == NULL) { // the subroutine may have resolved to a different image // that doesn't have debug information available. continue; } FunctionInstance* targetFunction; if (imageInfo->GetAddressSectionType(subroutineAddress) == ADDRESS_SECTION_TYPE_PLT) { result = fArchitecture->ResolvePICFunctionAddress( subroutineAddress, subroutineState, subroutineAddress); if (result != B_OK) continue; if (!targetImage->ContainsAddress(subroutineAddress)) { // the PLT entry doesn't necessarily point to a function // in the same image; as such we may need to try to // resolve the target address again. targetImage = image->GetTeam()->ImageByAddress( subroutineAddress); if (targetImage == NULL) continue; imageInfo = targetImage->GetImageDebugInfo(); if (imageInfo == NULL) { // As above, since the indirection here may have // landed us in an entirely different image, there is // no guarantee that debug info is available, // depending on which image it was. continue; } } } targetFunction = imageInfo->FunctionAtAddress(subroutineAddress); if (targetFunction != NULL) { DwarfFunctionDebugInfo* targetInfo = dynamic_cast( targetFunction->GetFunctionDebugInfo()); if (targetInfo != NULL) { DIESubprogram* subProgram = targetInfo->SubprogramEntry(); DIEType* returnType = subProgram->ReturnType(); if (returnType == NULL) { // check if we have a specification, and if so, if that has // a return type subProgram = dynamic_cast( subProgram->Specification()); if (subProgram != NULL) returnType = subProgram->ReturnType(); // function doesn't return a value, we're done. if (returnType == NULL) return B_OK; } uint32 byteSize = 0; if (returnType->ByteSize() == NULL) { if (dynamic_cast(returnType) != NULL) byteSize = fArchitecture->AddressSize(); } else byteSize = returnType->ByteSize()->constant; // if we were unable to determine a size for the type, // simply default to the architecture's register width. if (byteSize == 0) byteSize = fArchitecture->AddressSize(); ValueLocation* location; result = fArchitecture->GetReturnAddressLocation(frame, byteSize, location); if (result != B_OK) return result; BReference locationReference(location, true); Variable* variable = NULL; BReference idReference( targetFunction->GetFunctionID(), true); result = factory.CreateReturnValue(idReference, returnType, location, subroutineState, variable); if (result != B_OK) return result; BReference variableReference(variable, true); if (!frame->AddLocalVariable(variable)) return B_NO_MEMORY; } } } return B_OK; } bool DwarfImageDebugInfo::_EvaluateBaseTypeConstraints(DIEType* type, const TypeLookupConstraints& constraints) const { if (constraints.HasBaseTypeName()) { BString baseEntryName; DIEType* baseTypeOwnerEntry = NULL; switch (constraints.TypeKind()) { case TYPE_ADDRESS: { DIEAddressingType* addressType = dynamic_cast(type); if (addressType != NULL) { baseTypeOwnerEntry = DwarfUtils::GetDIEByPredicate( addressType, HasTypePredicate()); } break; } case TYPE_ARRAY: { DIEArrayType* arrayType = dynamic_cast(type); if (arrayType != NULL) { baseTypeOwnerEntry = DwarfUtils::GetDIEByPredicate( arrayType, HasTypePredicate()); } break; } default: break; } if (baseTypeOwnerEntry != NULL) { DwarfUtils::GetFullyQualifiedDIEName(baseTypeOwnerEntry, baseEntryName); if (baseEntryName != constraints.BaseTypeName()) return false; } } return true; } status_t DwarfImageDebugInfo::_RecursiveTraverseNamespaceForFunctions( DIENamespace* nsEntry, CompilationUnit* unit, BObjectList& functions) { status_t error = B_OK; for (DebugInfoEntryList::ConstIterator it = nsEntry->Children().GetIterator(); DebugInfoEntry* entry = it.Next();) { if (entry->Tag() == DW_TAG_subprogram) { DIESubprogram* subprogramEntry = static_cast(entry); error = _AddFunction(subprogramEntry, unit, functions); if (error != B_OK) return error; } DIENamespace* nsEntry = dynamic_cast(entry); if (nsEntry != NULL) { error = _RecursiveTraverseNamespaceForFunctions(nsEntry, unit, functions); if (error != B_OK) return error; continue; } DIEClassBaseType* classEntry = dynamic_cast(entry); if (classEntry != NULL) { for (DebugInfoEntryList::ConstIterator it = classEntry->MemberFunctions().GetIterator(); DebugInfoEntry* memberEntry = it.Next();) { error = _AddFunction(static_cast(memberEntry), unit, functions); if (error != B_OK) return error; } } } return B_OK; } status_t DwarfImageDebugInfo::_AddFunction(DIESubprogram* subprogramEntry, CompilationUnit* unit, BObjectList& functions) { // ignore declarations and inlined functions if (subprogramEntry->IsDeclaration() || subprogramEntry->Inline() == DW_INL_inlined || subprogramEntry->Inline() == DW_INL_declared_inlined) { return B_OK; } // get the name BString name; DwarfUtils::GetFullyQualifiedDIEName(subprogramEntry, name); if (name.Length() == 0) return B_OK; // get the address ranges TargetAddressRangeList* rangeList = fFile->ResolveRangeList(unit, subprogramEntry->AddressRangesOffset()); if (rangeList == NULL) { target_addr_t lowPC = subprogramEntry->LowPC(); target_addr_t highPC = subprogramEntry->HighPC(); if (highPC <= lowPC) return B_OK; rangeList = new(std::nothrow) TargetAddressRangeList( TargetAddressRange(lowPC, highPC - lowPC)); if (rangeList == NULL) return B_NO_MEMORY; // TODO: Clean up already added functions! } BReference rangeListReference(rangeList, true); // get the source location const char* directoryPath = NULL; const char* fileName = NULL; int32 line = -1; int32 column = -1; DwarfUtils::GetDeclarationLocation(fFile, subprogramEntry, directoryPath, fileName, line, column); LocatableFile* file = NULL; if (fileName != NULL) { file = fFileManager->GetSourceFile(directoryPath, fileName); } BReference fileReference(file, true); // create and add the functions DwarfFunctionDebugInfo* function = new(std::nothrow) DwarfFunctionDebugInfo(this, unit, subprogramEntry, rangeList, name, file, SourceLocation(line, std::max(column, (int32)0))); if (function == NULL || !functions.AddItem(function)) { delete function; return B_NO_MEMORY; // TODO: Clean up already added functions! } return B_OK; } status_t DwarfImageDebugInfo::_BuildTypeNameTable() { fTypeNameTable = new(std::nothrow) TypeNameTable; if (fTypeNameTable == NULL) return B_NO_MEMORY; status_t error = fTypeNameTable->Init(); if (error != B_OK) return error; // iterate through all compilation units for (int32 i = 0; CompilationUnit* unit = fFile->CompilationUnitAt(i); i++) { // iterate through all types of the compilation unit for (DebugInfoEntryList::ConstIterator it = unit->UnitEntry()->Types().GetIterator(); DIEType* typeEntry = dynamic_cast(it.Next());) { if (_RecursiveAddTypeNames(typeEntry, unit) != B_OK) return B_NO_MEMORY; } for (DebugInfoEntryList::ConstIterator it = unit->UnitEntry()->OtherChildren().GetIterator(); DebugInfoEntry* child = it.Next();) { DIENamespace* namespaceEntry = dynamic_cast(child); if (namespaceEntry == NULL) continue; if (_RecursiveTraverseNamespaceForTypes(namespaceEntry, unit) != B_OK) { return B_NO_MEMORY; } } } return B_OK; } status_t DwarfImageDebugInfo::_RecursiveAddTypeNames(DIEType* type, CompilationUnit* unit) { if (type->IsDeclaration()) return B_OK; BString typeEntryName; DwarfUtils::GetFullyQualifiedDIEName(type, typeEntryName); status_t error = B_OK; TypeNameEntry* entry = fTypeNameTable->Lookup(typeEntryName); if (entry == NULL) { entry = new(std::nothrow) TypeNameEntry(typeEntryName); if (entry == NULL) return B_NO_MEMORY; error = fTypeNameTable->Insert(entry); if (error != B_OK) return error; } TypeEntryInfo* info = new(std::nothrow) TypeEntryInfo(type, unit); if (info == NULL) return B_NO_MEMORY; if (!entry->types.AddItem(info)) { delete info; return B_NO_MEMORY; } DIEClassBaseType* classType = dynamic_cast(type); if (classType == NULL) return B_OK; for (DebugInfoEntryList::ConstIterator it = classType->InnerTypes().GetIterator(); DIEType* innerType = dynamic_cast(it.Next());) { error = _RecursiveAddTypeNames(innerType, unit); if (error != B_OK) return error; } return B_OK; } status_t DwarfImageDebugInfo::_RecursiveTraverseNamespaceForTypes(DIENamespace* nsEntry, CompilationUnit* unit) { for (DebugInfoEntryList::ConstIterator it = nsEntry->Children().GetIterator(); DebugInfoEntry* child = it.Next();) { if (child->IsType()) { DIEType* type = dynamic_cast(child); if (_RecursiveAddTypeNames(type, unit) != B_OK) return B_NO_MEMORY; } else { DIENamespace* nameSpace = dynamic_cast(child); if (nameSpace == NULL) continue; status_t error = _RecursiveTraverseNamespaceForTypes(nameSpace, unit); if (error != B_OK) return error; continue; } } return B_OK; }