| //===- bolt/Rewrite/DWARFRewriter.cpp -------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "bolt/Rewrite/DWARFRewriter.h" |
| #include "bolt/Core/BinaryContext.h" |
| #include "bolt/Core/BinaryFunction.h" |
| #include "bolt/Core/DebugData.h" |
| #include "bolt/Core/ParallelUtilities.h" |
| #include "bolt/Rewrite/RewriteInstance.h" |
| #include "bolt/Utils/Utils.h" |
| #include "llvm/ADT/STLExtras.h" |
| #include "llvm/BinaryFormat/Dwarf.h" |
| #include "llvm/DWP/DWP.h" |
| #include "llvm/DebugInfo/DWARF/DWARFContext.h" |
| #include "llvm/DebugInfo/DWARF/DWARFFormValue.h" |
| #include "llvm/MC/MCAsmBackend.h" |
| #include "llvm/MC/MCAsmLayout.h" |
| #include "llvm/MC/MCContext.h" |
| #include "llvm/MC/MCObjectWriter.h" |
| #include "llvm/MC/MCStreamer.h" |
| #include "llvm/Object/ObjectFile.h" |
| #include "llvm/Support/Casting.h" |
| #include "llvm/Support/CommandLine.h" |
| #include "llvm/Support/Debug.h" |
| #include "llvm/Support/Endian.h" |
| #include "llvm/Support/FileSystem.h" |
| #include "llvm/Support/ThreadPool.h" |
| #include "llvm/Support/ToolOutputFile.h" |
| #include <algorithm> |
| #include <cstdint> |
| #include <string> |
| #include <unordered_map> |
| |
| #undef DEBUG_TYPE |
| #define DEBUG_TYPE "bolt" |
| |
| LLVM_ATTRIBUTE_UNUSED |
| static void printDie(const DWARFDie &DIE) { |
| DIDumpOptions DumpOpts; |
| DumpOpts.ShowForm = true; |
| DumpOpts.Verbose = true; |
| DumpOpts.ChildRecurseDepth = 0; |
| DumpOpts.ShowChildren = 0; |
| DIE.dump(dbgs(), 0, DumpOpts); |
| } |
| |
| struct AttrInfo { |
| DWARFFormValue V; |
| uint64_t Offset; |
| uint32_t Size; // Size of the attribute. |
| }; |
| |
| /// Finds attributes FormValue and Offset. |
| /// |
| /// \param DIE die to look up in. |
| /// \param Index the attribute index to extract. |
| /// \return an optional AttrInfo with DWARFFormValue and Offset. |
| static Optional<AttrInfo> |
| findAttributeInfo(const DWARFDie DIE, |
| const DWARFAbbreviationDeclaration *AbbrevDecl, |
| uint32_t Index) { |
| const DWARFUnit &U = *DIE.getDwarfUnit(); |
| uint64_t Offset = |
| AbbrevDecl->getAttributeOffsetFromIndex(Index, DIE.getOffset(), U); |
| Optional<DWARFFormValue> Value = |
| AbbrevDecl->getAttributeValueFromOffset(Index, Offset, U); |
| if (!Value) |
| return None; |
| // AttributeSpec |
| const DWARFAbbreviationDeclaration::AttributeSpec *AttrVal = |
| AbbrevDecl->attributes().begin() + Index; |
| uint32_t ValSize = 0; |
| Optional<int64_t> ValSizeOpt = AttrVal->getByteSize(U); |
| if (ValSizeOpt) { |
| ValSize = static_cast<uint32_t>(*ValSizeOpt); |
| } else { |
| DWARFDataExtractor DebugInfoData = U.getDebugInfoExtractor(); |
| uint64_t NewOffset = Offset; |
| DWARFFormValue::skipValue(Value->getForm(), DebugInfoData, &NewOffset, |
| U.getFormParams()); |
| // This includes entire size of the entry, which might not be just the |
| // encoding part. For example for DW_AT_loc it will include expression |
| // location. |
| ValSize = NewOffset - Offset; |
| } |
| |
| return AttrInfo{*Value, Offset, ValSize}; |
| } |
| |
| /// Finds attributes FormValue and Offset. |
| /// |
| /// \param DIE die to look up in. |
| /// \param Attr the attribute to extract. |
| /// \return an optional AttrInfo with DWARFFormValue and Offset. |
| static Optional<AttrInfo> findAttributeInfo(const DWARFDie DIE, |
| dwarf::Attribute Attr) { |
| if (!DIE.isValid()) |
| return None; |
| const DWARFAbbreviationDeclaration *AbbrevDecl = |
| DIE.getAbbreviationDeclarationPtr(); |
| if (!AbbrevDecl) |
| return None; |
| Optional<uint32_t> Index = AbbrevDecl->findAttributeIndex(Attr); |
| if (!Index) |
| return None; |
| return findAttributeInfo(DIE, AbbrevDecl, *Index); |
| } |
| |
| using namespace llvm; |
| using namespace llvm::support::endian; |
| using namespace object; |
| using namespace bolt; |
| |
| namespace opts { |
| |
| extern cl::OptionCategory BoltCategory; |
| extern cl::opt<unsigned> Verbosity; |
| extern cl::opt<std::string> OutputFilename; |
| |
| static cl::opt<bool> |
| KeepARanges("keep-aranges", |
| cl::desc("keep or generate .debug_aranges section if .gdb_index is written"), |
| cl::ZeroOrMore, |
| cl::Hidden, |
| cl::cat(BoltCategory)); |
| |
| static cl::opt<bool> |
| DeterministicDebugInfo("deterministic-debuginfo", |
| cl::desc("disables parallel execution of tasks that may produce" |
| "nondeterministic debug info"), |
| cl::init(true), |
| cl::cat(BoltCategory)); |
| |
| static cl::opt<std::string> DwarfOutputPath( |
| "dwarf-output-path", |
| cl::desc("Path to where .dwo files or dwp file will be written out to."), |
| cl::init(""), cl::cat(BoltCategory)); |
| |
| static cl::opt<bool> |
| WriteDWP("write-dwp", |
| cl::desc("output a single dwarf package file (dwp) instead of " |
| "multiple non-relocatable dwarf object files (dwo)."), |
| cl::init(false), cl::cat(BoltCategory)); |
| |
| static cl::opt<bool> |
| DebugSkeletonCu("debug-skeleton-cu", |
| cl::desc("prints out offsetrs for abbrev and debu_info of " |
| "Skeleton CUs that get patched."), |
| cl::ZeroOrMore, cl::Hidden, cl::init(false), |
| cl::cat(BoltCategory)); |
| } // namespace opts |
| |
| /// Returns DWO Name to be used. Handles case where user specifies output DWO |
| /// directory, and there are duplicate names. Assumes DWO ID is unique. |
| static std::string |
| getDWOName(llvm::DWARFUnit &CU, |
| std::unordered_map<std::string, uint32_t> *NameToIndexMap, |
| std::unordered_map<uint64_t, std::string> &DWOIdToName) { |
| llvm::Optional<uint64_t> DWOId = CU.getDWOId(); |
| assert(DWOId && "DWO ID not found."); |
| (void)DWOId; |
| auto NameIter = DWOIdToName.find(*DWOId); |
| if (NameIter != DWOIdToName.end()) |
| return NameIter->second; |
| |
| std::string DWOName = dwarf::toString( |
| CU.getUnitDIE().find({dwarf::DW_AT_dwo_name, dwarf::DW_AT_GNU_dwo_name}), |
| ""); |
| assert(!DWOName.empty() && |
| "DW_AT_dwo_name/DW_AT_GNU_dwo_name does not exists."); |
| if (NameToIndexMap && !opts::DwarfOutputPath.empty()) { |
| auto Iter = NameToIndexMap->find(DWOName); |
| if (Iter == NameToIndexMap->end()) |
| Iter = NameToIndexMap->insert({DWOName, 0}).first; |
| DWOName.append(std::to_string(Iter->second)); |
| ++Iter->second; |
| } |
| DWOName.append(".dwo"); |
| DWOIdToName[*DWOId] = DWOName; |
| return DWOName; |
| } |
| |
| static bool isHighPcFormEightBytes(dwarf::Form DwarfForm) { |
| return DwarfForm == dwarf::DW_FORM_addr || DwarfForm == dwarf::DW_FORM_data8; |
| } |
| |
| void DWARFRewriter::updateDebugInfo() { |
| ErrorOr<BinarySection &> DebugInfo = BC.getUniqueSectionByName(".debug_info"); |
| if (!DebugInfo) |
| return; |
| |
| auto *DebugInfoPatcher = |
| static_cast<DebugInfoBinaryPatcher *>(DebugInfo->getPatcher()); |
| |
| ARangesSectionWriter = std::make_unique<DebugARangesSectionWriter>(); |
| RangesSectionWriter = std::make_unique<DebugRangesSectionWriter>(); |
| StrWriter = std::make_unique<DebugStrWriter>(&BC); |
| AbbrevWriter = std::make_unique<DebugAbbrevWriter>(*BC.DwCtx); |
| |
| AddrWriter = std::make_unique<DebugAddrWriter>(&BC); |
| DebugLoclistWriter::setAddressWriter(AddrWriter.get()); |
| |
| uint64_t NumCUs = BC.DwCtx->getNumCompileUnits(); |
| if ((opts::NoThreads || opts::DeterministicDebugInfo) && |
| BC.getNumDWOCUs() == 0) { |
| // Use single entry for efficiency when running single-threaded |
| NumCUs = 1; |
| } |
| |
| LocListWritersByCU.reserve(NumCUs); |
| |
| for (size_t CUIndex = 0; CUIndex < NumCUs; ++CUIndex) |
| LocListWritersByCU[CUIndex] = std::make_unique<DebugLocWriter>(&BC); |
| |
| // Unordered maps to handle name collision if output DWO directory is |
| // specified. |
| std::unordered_map<std::string, uint32_t> NameToIndexMap; |
| std::unordered_map<uint64_t, std::string> DWOIdToName; |
| std::mutex AccessMutex; |
| |
| auto updateDWONameCompDir = [&](DWARFUnit &Unit) -> void { |
| const DWARFDie &DIE = Unit.getUnitDIE(); |
| Optional<AttrInfo> AttrInfoVal = |
| findAttributeInfo(DIE, dwarf::DW_AT_GNU_dwo_name); |
| (void)AttrInfoVal; |
| assert(AttrInfoVal && "Skeleton CU doesn't have dwo_name."); |
| |
| std::string ObjectName = ""; |
| |
| { |
| std::lock_guard<std::mutex> Lock(AccessMutex); |
| ObjectName = getDWOName(Unit, &NameToIndexMap, DWOIdToName); |
| } |
| |
| uint32_t NewOffset = StrWriter->addString(ObjectName.c_str()); |
| DebugInfoPatcher->addLE32Patch(AttrInfoVal->Offset, NewOffset, |
| AttrInfoVal->Size); |
| |
| AttrInfoVal = findAttributeInfo(DIE, dwarf::DW_AT_comp_dir); |
| (void)AttrInfoVal; |
| assert(AttrInfoVal && "DW_AT_comp_dir is not in Skeleton CU."); |
| |
| if (!opts::DwarfOutputPath.empty()) { |
| uint32_t NewOffset = StrWriter->addString(opts::DwarfOutputPath.c_str()); |
| DebugInfoPatcher->addLE32Patch(AttrInfoVal->Offset, NewOffset, |
| AttrInfoVal->Size); |
| } |
| }; |
| |
| auto processUnitDIE = [&](size_t CUIndex, DWARFUnit *Unit) { |
| // Check if the unit is a skeleton and we need special updates for it and |
| // its matching split/DWO CU. |
| Optional<DWARFUnit *> SplitCU; |
| Optional<uint64_t> RangesBase; |
| llvm::Optional<uint64_t> DWOId = Unit->getDWOId(); |
| if (DWOId) |
| SplitCU = BC.getDWOCU(*DWOId); |
| |
| DebugLocWriter *DebugLocWriter = nullptr; |
| // Skipping CUs that failed to load. |
| if (SplitCU) { |
| updateDWONameCompDir(*Unit); |
| |
| // Assuming there is unique DWOID per binary. i.e. two or more CUs don't |
| // have same DWO ID. |
| assert(LocListWritersByCU.count(*DWOId) == 0 && |
| "LocList writer for DWO unit already exists."); |
| { |
| std::lock_guard<std::mutex> Lock(AccessMutex); |
| DebugLocWriter = |
| LocListWritersByCU |
| .insert( |
| {*DWOId, std::make_unique<DebugLoclistWriter>(&BC, *DWOId)}) |
| .first->second.get(); |
| } |
| DebugInfoBinaryPatcher *DwoDebugInfoPatcher = |
| llvm::cast<DebugInfoBinaryPatcher>( |
| getBinaryDWODebugInfoPatcher(*DWOId)); |
| RangesBase = RangesSectionWriter->getSectionOffset(); |
| DWARFContext *DWOCtx = BC.getDWOContext(); |
| // Setting this CU offset with DWP to normalize DIE offsets to uint32_t |
| if (DWOCtx && !DWOCtx->getCUIndex().getRows().empty()) |
| DwoDebugInfoPatcher->setDWPOffset((*SplitCU)->getOffset()); |
| DwoDebugInfoPatcher->setRangeBase(*RangesBase); |
| DwoDebugInfoPatcher->addUnitBaseOffsetLabel((*SplitCU)->getOffset()); |
| DebugAbbrevWriter *DWOAbbrevWriter = |
| createBinaryDWOAbbrevWriter((*SplitCU)->getContext(), *DWOId); |
| updateUnitDebugInfo(*(*SplitCU), *DwoDebugInfoPatcher, *DWOAbbrevWriter, |
| *DebugLocWriter); |
| DwoDebugInfoPatcher->clearDestinationLabels(); |
| if (!DwoDebugInfoPatcher->getWasRangBasedUsed()) |
| RangesBase = None; |
| } |
| |
| { |
| std::lock_guard<std::mutex> Lock(AccessMutex); |
| DebugLocWriter = LocListWritersByCU[CUIndex].get(); |
| } |
| DebugInfoPatcher->addUnitBaseOffsetLabel(Unit->getOffset()); |
| updateUnitDebugInfo(*Unit, *DebugInfoPatcher, *AbbrevWriter, |
| *DebugLocWriter, RangesBase); |
| }; |
| |
| if (opts::NoThreads || opts::DeterministicDebugInfo) { |
| for (std::unique_ptr<DWARFUnit> &CU : BC.DwCtx->compile_units()) |
| processUnitDIE(0, CU.get()); |
| } else { |
| // Update unit debug info in parallel |
| ThreadPool &ThreadPool = ParallelUtilities::getThreadPool(); |
| size_t CUIndex = 0; |
| for (std::unique_ptr<DWARFUnit> &CU : BC.DwCtx->compile_units()) { |
| ThreadPool.async(processUnitDIE, CUIndex, CU.get()); |
| CUIndex++; |
| } |
| ThreadPool.wait(); |
| } |
| |
| DebugInfoPatcher->clearDestinationLabels(); |
| flushPendingRanges(*DebugInfoPatcher); |
| |
| CUOffsetMap OffsetMap = finalizeDebugSections(*DebugInfoPatcher); |
| |
| if (opts::WriteDWP) |
| writeDWP(DWOIdToName); |
| else |
| writeDWOFiles(DWOIdToName); |
| |
| updateGdbIndexSection(OffsetMap); |
| } |
| |
| void DWARFRewriter::updateUnitDebugInfo( |
| DWARFUnit &Unit, DebugInfoBinaryPatcher &DebugInfoPatcher, |
| DebugAbbrevWriter &AbbrevWriter, DebugLocWriter &DebugLocWriter, |
| Optional<uint64_t> RangesBase) { |
| // Cache debug ranges so that the offset for identical ranges could be reused. |
| std::map<DebugAddressRangesVector, uint64_t> CachedRanges; |
| |
| uint64_t DIEOffset = Unit.getOffset() + Unit.getHeaderSize(); |
| uint64_t NextCUOffset = Unit.getNextUnitOffset(); |
| DWARFDebugInfoEntry Die; |
| DWARFDataExtractor DebugInfoData = Unit.getDebugInfoExtractor(); |
| uint32_t Depth = 0; |
| |
| while ( |
| DIEOffset < NextCUOffset && |
| Die.extractFast(Unit, &DIEOffset, DebugInfoData, NextCUOffset, Depth)) { |
| if (const DWARFAbbreviationDeclaration *AbbrDecl = |
| Die.getAbbreviationDeclarationPtr()) { |
| if (AbbrDecl->hasChildren()) |
| ++Depth; |
| } else { |
| // NULL entry. |
| if (Depth > 0) |
| --Depth; |
| if (Depth == 0) |
| break; |
| } |
| |
| DWARFDie DIE(&Unit, &Die); |
| |
| switch (DIE.getTag()) { |
| case dwarf::DW_TAG_compile_unit: { |
| auto ModuleRangesOrError = DIE.getAddressRanges(); |
| if (!ModuleRangesOrError) { |
| consumeError(ModuleRangesOrError.takeError()); |
| break; |
| } |
| DWARFAddressRangesVector &ModuleRanges = *ModuleRangesOrError; |
| DebugAddressRangesVector OutputRanges = |
| BC.translateModuleAddressRanges(ModuleRanges); |
| const uint64_t RangesSectionOffset = |
| RangesSectionWriter->addRanges(OutputRanges); |
| if (!Unit.isDWOUnit()) |
| ARangesSectionWriter->addCURanges(Unit.getOffset(), |
| std::move(OutputRanges)); |
| updateDWARFObjectAddressRanges(DIE, RangesSectionOffset, DebugInfoPatcher, |
| AbbrevWriter, RangesBase); |
| break; |
| } |
| case dwarf::DW_TAG_subprogram: { |
| // Get function address either from ranges or [LowPC, HighPC) pair. |
| bool UsesRanges = false; |
| uint64_t Address; |
| uint64_t SectionIndex, HighPC; |
| if (!DIE.getLowAndHighPC(Address, HighPC, SectionIndex)) { |
| Expected<DWARFAddressRangesVector> RangesOrError = |
| DIE.getAddressRanges(); |
| if (!RangesOrError) { |
| consumeError(RangesOrError.takeError()); |
| break; |
| } |
| DWARFAddressRangesVector Ranges = *RangesOrError; |
| // Not a function definition. |
| if (Ranges.empty()) |
| break; |
| |
| Address = Ranges.front().LowPC; |
| UsesRanges = true; |
| } |
| |
| // Clear cached ranges as the new function will have its own set. |
| CachedRanges.clear(); |
| |
| DebugAddressRangesVector FunctionRanges; |
| if (const BinaryFunction *Function = |
| BC.getBinaryFunctionAtAddress(Address)) |
| FunctionRanges = Function->getOutputAddressRanges(); |
| // Update ranges. |
| if (UsesRanges) { |
| updateDWARFObjectAddressRanges( |
| DIE, RangesSectionWriter->addRanges(FunctionRanges), |
| DebugInfoPatcher, AbbrevWriter); |
| } else { |
| // Delay conversion of [LowPC, HighPC) into DW_AT_ranges if possible. |
| const DWARFAbbreviationDeclaration *Abbrev = |
| DIE.getAbbreviationDeclarationPtr(); |
| assert(Abbrev && "abbrev expected"); |
| |
| // Create a critical section. |
| static std::shared_timed_mutex CriticalSectionMutex; |
| std::unique_lock<std::shared_timed_mutex> Lock(CriticalSectionMutex); |
| |
| if (FunctionRanges.size() > 1) { |
| convertPending(Unit, Abbrev, DebugInfoPatcher, AbbrevWriter); |
| // Exit critical section early. |
| Lock.unlock(); |
| convertToRanges(DIE, FunctionRanges, DebugInfoPatcher); |
| } else if (ConvertedRangesAbbrevs.find(Abbrev) != |
| ConvertedRangesAbbrevs.end()) { |
| // Exit critical section early. |
| Lock.unlock(); |
| convertToRanges(DIE, FunctionRanges, DebugInfoPatcher); |
| } else { |
| if (FunctionRanges.empty()) |
| FunctionRanges.emplace_back(DebugAddressRange()); |
| addToPendingRanges(Abbrev, DIE, FunctionRanges, Unit.getDWOId()); |
| } |
| } |
| break; |
| } |
| case dwarf::DW_TAG_lexical_block: |
| case dwarf::DW_TAG_inlined_subroutine: |
| case dwarf::DW_TAG_try_block: |
| case dwarf::DW_TAG_catch_block: { |
| uint64_t RangesSectionOffset = |
| RangesSectionWriter->getEmptyRangesOffset(); |
| Expected<DWARFAddressRangesVector> RangesOrError = DIE.getAddressRanges(); |
| const BinaryFunction *Function = |
| RangesOrError && !RangesOrError->empty() |
| ? BC.getBinaryFunctionContainingAddress( |
| RangesOrError->front().LowPC) |
| : nullptr; |
| if (Function) { |
| DebugAddressRangesVector OutputRanges = |
| Function->translateInputToOutputRanges(*RangesOrError); |
| LLVM_DEBUG(if (OutputRanges.empty() != RangesOrError->empty()) { |
| dbgs() << "BOLT-DEBUG: problem with DIE at 0x" |
| << Twine::utohexstr(DIE.getOffset()) << " in CU at 0x" |
| << Twine::utohexstr(Unit.getOffset()) << '\n'; |
| }); |
| RangesSectionOffset = RangesSectionWriter->addRanges( |
| std::move(OutputRanges), CachedRanges); |
| } else if (!RangesOrError) { |
| consumeError(RangesOrError.takeError()); |
| } |
| updateDWARFObjectAddressRanges(DIE, RangesSectionOffset, DebugInfoPatcher, |
| AbbrevWriter); |
| break; |
| } |
| default: { |
| // Handle any tag that can have DW_AT_location attribute. |
| DWARFFormValue Value; |
| uint64_t AttrOffset; |
| if (Optional<AttrInfo> AttrVal = |
| findAttributeInfo(DIE, dwarf::DW_AT_location)) { |
| AttrOffset = AttrVal->Offset; |
| Value = AttrVal->V; |
| if (Value.isFormClass(DWARFFormValue::FC_Constant) || |
| Value.isFormClass(DWARFFormValue::FC_SectionOffset)) { |
| uint64_t Offset = Value.isFormClass(DWARFFormValue::FC_Constant) |
| ? Value.getAsUnsignedConstant().getValue() |
| : Value.getAsSectionOffset().getValue(); |
| DebugLocationsVector InputLL; |
| |
| Optional<object::SectionedAddress> SectionAddress = |
| Unit.getBaseAddress(); |
| uint64_t BaseAddress = 0; |
| if (SectionAddress) |
| BaseAddress = SectionAddress->Address; |
| |
| Error E = Unit.getLocationTable().visitLocationList( |
| &Offset, [&](const DWARFLocationEntry &Entry) { |
| switch (Entry.Kind) { |
| default: |
| llvm_unreachable("Unsupported DWARFLocationEntry Kind."); |
| case dwarf::DW_LLE_end_of_list: |
| return false; |
| case dwarf::DW_LLE_base_address: |
| assert(Entry.SectionIndex == SectionedAddress::UndefSection && |
| "absolute address expected"); |
| BaseAddress = Entry.Value0; |
| break; |
| case dwarf::DW_LLE_offset_pair: |
| assert( |
| (Entry.SectionIndex == SectionedAddress::UndefSection && |
| !Unit.isDWOUnit()) && |
| "absolute address expected"); |
| InputLL.emplace_back(DebugLocationEntry{ |
| BaseAddress + Entry.Value0, BaseAddress + Entry.Value1, |
| Entry.Loc}); |
| break; |
| case dwarf::DW_LLE_startx_length: |
| assert(Unit.isDWOUnit() && |
| "None DWO Unit with DW_LLE_startx_length encoding."); |
| Optional<object::SectionedAddress> EntryAddress = |
| Unit.getAddrOffsetSectionItem(Entry.Value0); |
| assert(EntryAddress && "Address does not exist."); |
| InputLL.emplace_back(DebugLocationEntry{ |
| EntryAddress->Address, |
| EntryAddress->Address + Entry.Value1, Entry.Loc}); |
| break; |
| } |
| return true; |
| }); |
| |
| if (E || InputLL.empty()) { |
| errs() << "BOLT-WARNING: empty location list detected at 0x" |
| << Twine::utohexstr(Offset) << " for DIE at 0x" |
| << Twine::utohexstr(DIE.getOffset()) << " in CU at 0x" |
| << Twine::utohexstr(Unit.getOffset()) << '\n'; |
| } else { |
| const uint64_t Address = InputLL.front().LowPC; |
| if (const BinaryFunction *Function = |
| BC.getBinaryFunctionContainingAddress(Address)) { |
| DebugLocationsVector OutputLL = |
| Function->translateInputToOutputLocationList(InputLL); |
| LLVM_DEBUG(if (OutputLL.empty()) { |
| dbgs() << "BOLT-DEBUG: location list translated to an empty " |
| "one at 0x" |
| << Twine::utohexstr(DIE.getOffset()) << " in CU at 0x" |
| << Twine::utohexstr(Unit.getOffset()) << '\n'; |
| }); |
| DebugLocWriter.addList(AttrOffset, std::move(OutputLL)); |
| } |
| } |
| } else { |
| assert((Value.isFormClass(DWARFFormValue::FC_Exprloc) || |
| Value.isFormClass(DWARFFormValue::FC_Block)) && |
| "unexpected DW_AT_location form"); |
| if (Unit.isDWOUnit()) { |
| ArrayRef<uint8_t> Expr = *Value.getAsBlock(); |
| DataExtractor Data( |
| StringRef((const char *)Expr.data(), Expr.size()), |
| Unit.getContext().isLittleEndian(), 0); |
| DWARFExpression LocExpr(Data, Unit.getAddressByteSize(), |
| Unit.getFormParams().Format); |
| for (auto &Expr : LocExpr) { |
| if (Expr.getCode() != dwarf::DW_OP_GNU_addr_index) |
| continue; |
| uint64_t Index = Expr.getRawOperand(0); |
| Optional<object::SectionedAddress> EntryAddress = |
| Unit.getAddrOffsetSectionItem(Index); |
| assert(EntryAddress && "Address is not found."); |
| assert(Index <= std::numeric_limits<uint32_t>::max() && |
| "Invalid Operand Index."); |
| AddrWriter->addIndexAddress(EntryAddress->Address, |
| static_cast<uint32_t>(Index), |
| *Unit.getDWOId()); |
| } |
| } |
| } |
| } else if (Optional<AttrInfo> AttrVal = |
| findAttributeInfo(DIE, dwarf::DW_AT_low_pc)) { |
| AttrOffset = AttrVal->Offset; |
| Value = AttrVal->V; |
| const Optional<uint64_t> Result = Value.getAsAddress(); |
| if (Result.hasValue()) { |
| const uint64_t Address = Result.getValue(); |
| uint64_t NewAddress = 0; |
| if (const BinaryFunction *Function = |
| BC.getBinaryFunctionContainingAddress(Address)) { |
| NewAddress = Function->translateInputToOutputAddress(Address); |
| LLVM_DEBUG(dbgs() |
| << "BOLT-DEBUG: Fixing low_pc 0x" |
| << Twine::utohexstr(Address) << " for DIE with tag " |
| << DIE.getTag() << " to 0x" |
| << Twine::utohexstr(NewAddress) << '\n'); |
| } |
| |
| dwarf::Form Form = Value.getForm(); |
| assert(Form != dwarf::DW_FORM_LLVM_addrx_offset && |
| "DW_FORM_LLVM_addrx_offset is not supported"); |
| std::lock_guard<std::mutex> Lock(DebugInfoPatcherMutex); |
| if (Form == dwarf::DW_FORM_GNU_addr_index) { |
| assert(Unit.isDWOUnit() && |
| "DW_FORM_GNU_addr_index in Non DWO unit."); |
| uint64_t Index = Value.getRawUValue(); |
| // If there is no new address, storing old address. |
| // Re-using Index to make implementation easier. |
| // DW_FORM_GNU_addr_index is variable lenght encoding so we either |
| // have to create indices of same sizes, or use same index. |
| AddrWriter->addIndexAddress(NewAddress ? NewAddress : Address, |
| Index, *Unit.getDWOId()); |
| } else { |
| DebugInfoPatcher.addLE64Patch(AttrOffset, NewAddress); |
| } |
| } else if (opts::Verbosity >= 1) { |
| errs() << "BOLT-WARNING: unexpected form value for attribute at 0x" |
| << Twine::utohexstr(AttrOffset); |
| } |
| } |
| } |
| } |
| |
| // Handling references. |
| assert(DIE.isValid() && "Invalid DIE."); |
| const DWARFAbbreviationDeclaration *AbbrevDecl = |
| DIE.getAbbreviationDeclarationPtr(); |
| if (!AbbrevDecl) |
| continue; |
| uint32_t Index = 0; |
| for (const DWARFAbbreviationDeclaration::AttributeSpec &Decl : |
| AbbrevDecl->attributes()) { |
| switch (Decl.Form) { |
| default: |
| break; |
| case dwarf::DW_FORM_ref1: |
| case dwarf::DW_FORM_ref2: |
| case dwarf::DW_FORM_ref4: |
| case dwarf::DW_FORM_ref8: |
| case dwarf::DW_FORM_ref_udata: |
| case dwarf::DW_FORM_ref_addr: { |
| Optional<AttrInfo> AttrVal = findAttributeInfo(DIE, AbbrevDecl, Index); |
| uint32_t DestinationAddress = |
| AttrVal->V.getRawUValue() + |
| (Decl.Form == dwarf::DW_FORM_ref_addr ? 0 : Unit.getOffset()); |
| DebugInfoPatcher.addReferenceToPatch( |
| AttrVal->Offset, DestinationAddress, AttrVal->Size, Decl.Form); |
| // We can have only one reference, and it can be backward one. |
| DebugInfoPatcher.addDestinationReferenceLabel(DestinationAddress); |
| break; |
| } |
| } |
| ++Index; |
| } |
| } |
| if (DIEOffset > NextCUOffset) |
| errs() << "BOLT-WARNING: corrupt DWARF detected at 0x" |
| << Twine::utohexstr(Unit.getOffset()) << '\n'; |
| } |
| |
| void DWARFRewriter::updateDWARFObjectAddressRanges( |
| const DWARFDie DIE, uint64_t DebugRangesOffset, |
| SimpleBinaryPatcher &DebugInfoPatcher, DebugAbbrevWriter &AbbrevWriter, |
| Optional<uint64_t> RangesBase) { |
| |
| // Some objects don't have an associated DIE and cannot be updated (such as |
| // compiler-generated functions). |
| if (!DIE) |
| return; |
| |
| const DWARFAbbreviationDeclaration *AbbreviationDecl = |
| DIE.getAbbreviationDeclarationPtr(); |
| if (!AbbreviationDecl) { |
| if (opts::Verbosity >= 1) |
| errs() << "BOLT-WARNING: object's DIE doesn't have an abbreviation: " |
| << "skipping update. DIE at offset 0x" |
| << Twine::utohexstr(DIE.getOffset()) << '\n'; |
| return; |
| } |
| |
| if (RangesBase) { |
| // If DW_AT_GNU_ranges_base is present, update it. No further modifications |
| // are needed for ranges base. |
| Optional<AttrInfo> RangesBaseAttrInfo = |
| findAttributeInfo(DIE, dwarf::DW_AT_GNU_ranges_base); |
| if (RangesBaseAttrInfo) { |
| DebugInfoPatcher.addLE32Patch(RangesBaseAttrInfo->Offset, |
| static_cast<uint32_t>(*RangesBase), |
| RangesBaseAttrInfo->Size); |
| RangesBase = None; |
| } |
| } |
| |
| Optional<AttrInfo> LowPCAttrInfo = |
| findAttributeInfo(DIE, dwarf::DW_AT_low_pc); |
| if (AbbreviationDecl->findAttributeIndex(dwarf::DW_AT_ranges)) { |
| // Case 1: The object was already non-contiguous and had DW_AT_ranges. |
| // In this case we simply need to update the value of DW_AT_ranges |
| // and introduce DW_AT_GNU_ranges_base if required. |
| Optional<AttrInfo> AttrVal = findAttributeInfo(DIE, dwarf::DW_AT_ranges); |
| std::lock_guard<std::mutex> Lock(DebugInfoPatcherMutex); |
| DebugInfoPatcher.addLE32Patch( |
| AttrVal->Offset, DebugRangesOffset - DebugInfoPatcher.getRangeBase(), |
| AttrVal->Size); |
| |
| if (!RangesBase) { |
| if (LowPCAttrInfo && |
| LowPCAttrInfo->V.getForm() != dwarf::DW_FORM_GNU_addr_index && |
| LowPCAttrInfo->V.getForm() != dwarf::DW_FORM_addrx) |
| DebugInfoPatcher.addLE64Patch(LowPCAttrInfo->Offset, 0); |
| return; |
| } |
| |
| // Convert DW_AT_low_pc into DW_AT_GNU_ranges_base. |
| if (!LowPCAttrInfo) { |
| errs() << "BOLT-ERROR: skeleton CU at 0x" |
| << Twine::utohexstr(DIE.getOffset()) |
| << " does not have DW_AT_GNU_ranges_base or DW_AT_low_pc to" |
| " convert to update ranges base\n"; |
| return; |
| } |
| |
| AbbrevWriter.addAttributePatch( |
| *DIE.getDwarfUnit(), AbbreviationDecl, dwarf::DW_AT_low_pc, |
| dwarf::DW_AT_GNU_ranges_base, dwarf::DW_FORM_indirect); |
| DebugInfoPatcher.addUDataPatch(LowPCAttrInfo->Offset, dwarf::DW_FORM_udata, |
| 1); |
| DebugInfoPatcher.addUDataPatch(LowPCAttrInfo->Offset + 1, *RangesBase, 7); |
| |
| return; |
| } |
| |
| // Case 2: The object has both DW_AT_low_pc and DW_AT_high_pc emitted back |
| // to back. Replace with new attributes and patch the DIE. |
| Optional<AttrInfo> HighPCAttrInfo = |
| findAttributeInfo(DIE, dwarf::DW_AT_high_pc); |
| if (LowPCAttrInfo && HighPCAttrInfo) { |
| convertToRangesPatchAbbrev(*DIE.getDwarfUnit(), AbbreviationDecl, |
| AbbrevWriter, RangesBase); |
| convertToRangesPatchDebugInfo(DIE, DebugRangesOffset, DebugInfoPatcher, |
| RangesBase); |
| } else { |
| if (opts::Verbosity >= 1) |
| errs() << "BOLT-ERROR: cannot update ranges for DIE at offset 0x" |
| << Twine::utohexstr(DIE.getOffset()) << '\n'; |
| } |
| } |
| |
| void DWARFRewriter::updateLineTableOffsets(const MCAsmLayout &Layout) { |
| ErrorOr<BinarySection &> DbgInfoSection = |
| BC.getUniqueSectionByName(".debug_info"); |
| ErrorOr<BinarySection &> TypeInfoSection = |
| BC.getUniqueSectionByName(".debug_types"); |
| assert(((BC.DwCtx->getNumTypeUnits() > 0 && TypeInfoSection) || |
| BC.DwCtx->getNumTypeUnits() == 0) && |
| "Was not able to retrieve Debug Types section."); |
| |
| // We will be re-writing .debug_info so relocation mechanism doesn't work for |
| // Debug Info Patcher. |
| DebugInfoBinaryPatcher *DebugInfoPatcher = nullptr; |
| if (BC.DwCtx->getNumCompileUnits()) { |
| DbgInfoSection->registerPatcher(std::make_unique<DebugInfoBinaryPatcher>()); |
| DebugInfoPatcher = |
| static_cast<DebugInfoBinaryPatcher *>(DbgInfoSection->getPatcher()); |
| } |
| |
| // There is no direct connection between CU and TU, but same offsets, |
| // encoded in DW_AT_stmt_list, into .debug_line get modified. |
| // We take advantage of that to map original CU line table offsets to new |
| // ones. |
| std::unordered_map<uint64_t, uint64_t> DebugLineOffsetMap; |
| |
| auto GetStatementListValue = [](DWARFUnit *Unit) { |
| Optional<DWARFFormValue> StmtList = |
| Unit->getUnitDIE().find(dwarf::DW_AT_stmt_list); |
| Optional<uint64_t> Offset = dwarf::toSectionOffset(StmtList); |
| assert(Offset && "Was not able to retreive value of DW_AT_stmt_list."); |
| return *Offset; |
| }; |
| |
| const uint64_t Reloc32Type = BC.isAArch64() |
| ? static_cast<uint64_t>(ELF::R_AARCH64_ABS32) |
| : static_cast<uint64_t>(ELF::R_X86_64_32); |
| |
| for (const std::unique_ptr<DWARFUnit> &CU : BC.DwCtx->compile_units()) { |
| const unsigned CUID = CU->getOffset(); |
| MCSymbol *Label = BC.getDwarfLineTable(CUID).getLabel(); |
| if (!Label) |
| continue; |
| |
| Optional<AttrInfo> AttrVal = |
| findAttributeInfo(CU.get()->getUnitDIE(), dwarf::DW_AT_stmt_list); |
| if (!AttrVal) |
| continue; |
| |
| const uint64_t AttributeOffset = AttrVal->Offset; |
| const uint64_t LineTableOffset = Layout.getSymbolOffset(*Label); |
| DebugLineOffsetMap[GetStatementListValue(CU.get())] = LineTableOffset; |
| assert(DbgInfoSection && ".debug_info section must exist"); |
| DebugInfoPatcher->addLE32Patch(AttributeOffset, LineTableOffset); |
| } |
| |
| for (const std::unique_ptr<DWARFUnit> &TU : BC.DwCtx->types_section_units()) { |
| DWARFUnit *Unit = TU.get(); |
| Optional<AttrInfo> AttrVal = |
| findAttributeInfo(TU.get()->getUnitDIE(), dwarf::DW_AT_stmt_list); |
| if (!AttrVal) |
| continue; |
| const uint64_t AttributeOffset = AttrVal->Offset; |
| auto Iter = DebugLineOffsetMap.find(GetStatementListValue(Unit)); |
| assert(Iter != DebugLineOffsetMap.end() && |
| "Type Unit Updated Line Number Entry does not exist."); |
| TypeInfoSection->addRelocation(AttributeOffset, nullptr, Reloc32Type, |
| Iter->second, 0, /*Pending=*/true); |
| } |
| |
| // Set .debug_info as finalized so it won't be skipped over when |
| // we process sections while writing out the new binary. This ensures |
| // that the pending relocations will be processed and not ignored. |
| if (DbgInfoSection) |
| DbgInfoSection->setIsFinalized(); |
| |
| if (TypeInfoSection) |
| TypeInfoSection->setIsFinalized(); |
| } |
| |
| CUOffsetMap |
| DWARFRewriter::finalizeDebugSections(DebugInfoBinaryPatcher &DebugInfoPatcher) { |
| if (StrWriter->isInitialized()) { |
| RewriteInstance::addToDebugSectionsToOverwrite(".debug_str"); |
| std::unique_ptr<DebugStrBufferVector> DebugStrSectionContents = |
| StrWriter->finalize(); |
| BC.registerOrUpdateNoteSection(".debug_str", |
| copyByteArray(*DebugStrSectionContents), |
| DebugStrSectionContents->size()); |
| } |
| |
| std::unique_ptr<DebugBufferVector> RangesSectionContents = |
| RangesSectionWriter->finalize(); |
| BC.registerOrUpdateNoteSection(".debug_ranges", |
| copyByteArray(*RangesSectionContents), |
| RangesSectionContents->size()); |
| |
| std::unique_ptr<DebugBufferVector> LocationListSectionContents = |
| makeFinalLocListsSection(DebugInfoPatcher); |
| BC.registerOrUpdateNoteSection(".debug_loc", |
| copyByteArray(*LocationListSectionContents), |
| LocationListSectionContents->size()); |
| |
| // AddrWriter should be finalized after debug_loc since more addresses can be |
| // added there. |
| if (AddrWriter->isInitialized()) { |
| AddressSectionBuffer AddressSectionContents = AddrWriter->finalize(); |
| BC.registerOrUpdateNoteSection(".debug_addr", |
| copyByteArray(AddressSectionContents), |
| AddressSectionContents.size()); |
| for (auto &CU : BC.DwCtx->compile_units()) { |
| DWARFDie DIE = CU->getUnitDIE(); |
| if (Optional<AttrInfo> AttrVal = |
| findAttributeInfo(DIE, dwarf::DW_AT_GNU_addr_base)) { |
| uint64_t Offset = AddrWriter->getOffset(*CU->getDWOId()); |
| DebugInfoPatcher.addLE32Patch( |
| AttrVal->Offset, static_cast<int32_t>(Offset), AttrVal->Size); |
| } |
| } |
| } |
| |
| std::unique_ptr<DebugBufferVector> AbbrevSectionContents = |
| AbbrevWriter->finalize(); |
| BC.registerOrUpdateNoteSection(".debug_abbrev", |
| copyByteArray(*AbbrevSectionContents), |
| AbbrevSectionContents->size()); |
| |
| // Update abbreviation offsets for CUs/TUs if they were changed. |
| SimpleBinaryPatcher *DebugTypesPatcher = nullptr; |
| for (auto &Unit : BC.DwCtx->normal_units()) { |
| const uint64_t NewAbbrevOffset = |
| AbbrevWriter->getAbbreviationsOffsetForUnit(*Unit); |
| if (Unit->getAbbreviationsOffset() == NewAbbrevOffset) |
| continue; |
| |
| // DWARFv4 |
| // unit_length - 4 bytes |
| // version - 2 bytes |
| // So + 6 to patch debug_abbrev_offset |
| constexpr uint64_t AbbrevFieldOffset = 6; |
| if (!Unit->isTypeUnit()) { |
| DebugInfoPatcher.addLE32Patch(Unit->getOffset() + AbbrevFieldOffset, |
| static_cast<uint32_t>(NewAbbrevOffset)); |
| continue; |
| } |
| |
| if (!DebugTypesPatcher) { |
| ErrorOr<BinarySection &> DebugTypes = |
| BC.getUniqueSectionByName(".debug_types"); |
| DebugTypes->registerPatcher(std::make_unique<SimpleBinaryPatcher>()); |
| DebugTypesPatcher = |
| static_cast<SimpleBinaryPatcher *>(DebugTypes->getPatcher()); |
| } |
| DebugTypesPatcher->addLE32Patch(Unit->getOffset() + AbbrevFieldOffset, |
| static_cast<uint32_t>(NewAbbrevOffset)); |
| } |
| |
| // No more creating new DebugInfoPatches. |
| CUOffsetMap CUMap = |
| DebugInfoPatcher.computeNewOffsets(*BC.DwCtx.get(), false); |
| |
| // Skip .debug_aranges if we are re-generating .gdb_index. |
| if (opts::KeepARanges || !BC.getGdbIndexSection()) { |
| SmallVector<char, 16> ARangesBuffer; |
| raw_svector_ostream OS(ARangesBuffer); |
| |
| auto MAB = std::unique_ptr<MCAsmBackend>( |
| BC.TheTarget->createMCAsmBackend(*BC.STI, *BC.MRI, MCTargetOptions())); |
| |
| ARangesSectionWriter->writeARangesSection(OS, CUMap); |
| const StringRef &ARangesContents = OS.str(); |
| |
| BC.registerOrUpdateNoteSection(".debug_aranges", |
| copyByteArray(ARangesContents), |
| ARangesContents.size()); |
| } |
| return CUMap; |
| } |
| |
| // Creates all the data structures necessary for creating MCStreamer. |
| // They are passed by reference because they need to be kept around. |
| // Also creates known debug sections. These are sections handled by |
| // handleDebugDataPatching. |
| using KnownSectionsEntry = std::pair<MCSection *, DWARFSectionKind>; |
| namespace { |
| |
| std::unique_ptr<BinaryContext> |
| createDwarfOnlyBC(const object::ObjectFile &File) { |
| return BinaryContext::createBinaryContext( |
| &File, false, |
| DWARFContext::create(File, DWARFContext::ProcessDebugRelocations::Ignore, |
| nullptr, "", WithColor::defaultErrorHandler, |
| WithColor::defaultWarningHandler)); |
| } |
| |
| StringMap<KnownSectionsEntry> |
| createKnownSectionsMap(const MCObjectFileInfo &MCOFI) { |
| StringMap<KnownSectionsEntry> KnownSectionsTemp = { |
| {"debug_info.dwo", {MCOFI.getDwarfInfoDWOSection(), DW_SECT_INFO}}, |
| {"debug_types.dwo", {MCOFI.getDwarfTypesDWOSection(), DW_SECT_EXT_TYPES}}, |
| {"debug_str_offsets.dwo", |
| {MCOFI.getDwarfStrOffDWOSection(), DW_SECT_STR_OFFSETS}}, |
| {"debug_str.dwo", {MCOFI.getDwarfStrDWOSection(), DW_SECT_EXT_unknown}}, |
| {"debug_loc.dwo", {MCOFI.getDwarfLocDWOSection(), DW_SECT_EXT_LOC}}, |
| {"debug_abbrev.dwo", {MCOFI.getDwarfAbbrevDWOSection(), DW_SECT_ABBREV}}, |
| {"debug_line.dwo", {MCOFI.getDwarfLineDWOSection(), DW_SECT_LINE}}}; |
| return KnownSectionsTemp; |
| } |
| |
| StringRef getSectionName(const SectionRef &Section) { |
| Expected<StringRef> SectionName = Section.getName(); |
| assert(SectionName && "Invalid section name."); |
| StringRef Name = *SectionName; |
| Name = Name.substr(Name.find_first_not_of("._")); |
| return Name; |
| } |
| |
| // Exctracts an appropriate slice if input is DWP. |
| // Applies patches or overwrites the section. |
| Optional<StringRef> updateDebugData( |
| DWARFContext &DWCtx, std::string &Storage, const SectionRef &Section, |
| const StringMap<KnownSectionsEntry> &KnownSections, MCStreamer &Streamer, |
| DWARFRewriter &Writer, const DWARFUnitIndex::Entry *DWOEntry, |
| uint64_t DWOId, std::unique_ptr<DebugBufferVector> &OutputBuffer) { |
| auto applyPatch = [&](DebugInfoBinaryPatcher *Patcher, |
| StringRef Data) -> StringRef { |
| Patcher->computeNewOffsets(DWCtx, true); |
| Storage = Patcher->patchBinary(Data); |
| return StringRef(Storage.c_str(), Storage.size()); |
| }; |
| |
| using DWOSectionContribution = |
| const DWARFUnitIndex::Entry::SectionContribution; |
| auto getSliceData = [&](const DWARFUnitIndex::Entry *DWOEntry, |
| StringRef OutData, DWARFSectionKind Sec, |
| uint32_t &DWPOffset) -> StringRef { |
| if (DWOEntry) { |
| DWOSectionContribution *DWOContrubution = DWOEntry->getContribution(Sec); |
| DWPOffset = DWOContrubution->Offset; |
| OutData = OutData.substr(DWPOffset, DWOContrubution->Length); |
| } |
| return OutData; |
| }; |
| |
| StringRef Name = getSectionName(Section); |
| auto SectionIter = KnownSections.find(Name); |
| if (SectionIter == KnownSections.end()) |
| return None; |
| Streamer.SwitchSection(SectionIter->second.first); |
| Expected<StringRef> Contents = Section.getContents(); |
| assert(Contents && "Invalid contents."); |
| StringRef OutData = *Contents; |
| uint32_t DWPOffset = 0; |
| |
| switch (SectionIter->second.second) { |
| default: { |
| if (!Name.equals("debug_str.dwo")) |
| errs() << "BOLT-WARNING: Unsupported Debug section: " << Name << "\n"; |
| return OutData; |
| } |
| case DWARFSectionKind::DW_SECT_INFO: { |
| OutData = getSliceData(DWOEntry, OutData, DWARFSectionKind::DW_SECT_INFO, |
| DWPOffset); |
| DebugInfoBinaryPatcher *Patcher = llvm::cast<DebugInfoBinaryPatcher>( |
| Writer.getBinaryDWODebugInfoPatcher(DWOId)); |
| return applyPatch(Patcher, OutData); |
| } |
| case DWARFSectionKind::DW_SECT_EXT_TYPES: { |
| return getSliceData(DWOEntry, OutData, DWARFSectionKind::DW_SECT_EXT_TYPES, |
| DWPOffset); |
| } |
| case DWARFSectionKind::DW_SECT_STR_OFFSETS: { |
| return getSliceData(DWOEntry, OutData, |
| DWARFSectionKind::DW_SECT_STR_OFFSETS, DWPOffset); |
| } |
| case DWARFSectionKind::DW_SECT_ABBREV: { |
| DebugAbbrevWriter *AbbrevWriter = Writer.getBinaryDWOAbbrevWriter(DWOId); |
| OutputBuffer = AbbrevWriter->finalize(); |
| // Creating explicit StringRef here, otherwise |
| // with impicit conversion it will take null byte as end of |
| // string. |
| return StringRef(reinterpret_cast<const char *>(OutputBuffer->data()), |
| OutputBuffer->size()); |
| } |
| case DWARFSectionKind::DW_SECT_EXT_LOC: { |
| DebugLocWriter *LocWriter = Writer.getDebugLocWriter(DWOId); |
| OutputBuffer = LocWriter->getBuffer(); |
| // Creating explicit StringRef here, otherwise |
| // with impicit conversion it will take null byte as end of |
| // string. |
| return StringRef(reinterpret_cast<const char *>(OutputBuffer->data()), |
| OutputBuffer->size()); |
| } |
| case DWARFSectionKind::DW_SECT_LINE: { |
| return getSliceData(DWOEntry, OutData, DWARFSectionKind::DW_SECT_LINE, |
| DWPOffset); |
| } |
| } |
| } |
| |
| } // namespace |
| |
| void DWARFRewriter::writeDWP( |
| std::unordered_map<uint64_t, std::string> &DWOIdToName) { |
| SmallString<0> OutputNameStr; |
| StringRef OutputName; |
| if (opts::DwarfOutputPath.empty()) { |
| OutputName = |
| Twine(opts::OutputFilename).concat(".dwp").toStringRef(OutputNameStr); |
| } else { |
| StringRef ExeFileName = llvm::sys::path::filename(opts::OutputFilename); |
| OutputName = Twine(opts::DwarfOutputPath) |
| .concat("/") |
| .concat(ExeFileName) |
| .concat(".dwp") |
| .toStringRef(OutputNameStr); |
| errs() << "BOLT-WARNING: dwarf-output-path is in effect and .dwp file will " |
| "possibly be written to another location that is not the same as " |
| "the executable\n"; |
| } |
| std::error_code EC; |
| std::unique_ptr<ToolOutputFile> Out = |
| std::make_unique<ToolOutputFile>(OutputName, EC, sys::fs::OF_None); |
| |
| const object::ObjectFile *File = BC.DwCtx->getDWARFObj().getFile(); |
| std::unique_ptr<BinaryContext> TmpBC = createDwarfOnlyBC(*File); |
| std::unique_ptr<MCStreamer> Streamer = TmpBC->createStreamer(Out->os()); |
| const MCObjectFileInfo &MCOFI = *Streamer->getContext().getObjectFileInfo(); |
| StringMap<KnownSectionsEntry> KnownSections = createKnownSectionsMap(MCOFI); |
| MCSection *const StrSection = MCOFI.getDwarfStrDWOSection(); |
| MCSection *const StrOffsetSection = MCOFI.getDwarfStrOffDWOSection(); |
| |
| // Data Structures for DWP book keeping |
| // Size of array corresponds to the number of sections supported by DWO format |
| // in DWARF4/5. |
| uint32_t ContributionOffsets[8] = {}; |
| std::deque<SmallString<32>> UncompressedSections; |
| DWPStringPool Strings(*Streamer, StrSection); |
| MapVector<uint64_t, UnitIndexEntry> IndexEntries; |
| constexpr uint32_t IndexVersion = 2; |
| |
| // Setup DWP code once. |
| DWARFContext *DWOCtx = BC.getDWOContext(); |
| const DWARFUnitIndex *CUIndex = nullptr; |
| bool IsDWP = false; |
| if (DWOCtx) { |
| CUIndex = &DWOCtx->getCUIndex(); |
| IsDWP = !CUIndex->getRows().empty(); |
| } |
| |
| for (const std::unique_ptr<DWARFUnit> &CU : BC.DwCtx->compile_units()) { |
| Optional<uint64_t> DWOId = CU->getDWOId(); |
| if (!DWOId) |
| continue; |
| |
| // Skipping CUs that we failed to load. |
| Optional<DWARFUnit *> DWOCU = BC.getDWOCU(*DWOId); |
| if (!DWOCU) |
| continue; |
| |
| assert(CU->getVersion() == 4 && "For DWP output only DWARF4 is supported"); |
| UnitIndexEntry CurEntry = {}; |
| CurEntry.DWOName = |
| dwarf::toString(CU->getUnitDIE().find( |
| {dwarf::DW_AT_dwo_name, dwarf::DW_AT_GNU_dwo_name}), |
| ""); |
| const char *Name = CU->getUnitDIE().getShortName(); |
| if (Name) |
| CurEntry.Name = Name; |
| StringRef CurStrSection; |
| StringRef CurStrOffsetSection; |
| |
| // This maps each section contained in this file to its length. |
| // This information is later on used to calculate the contributions, |
| // i.e. offset and length, of each compile/type unit to a section. |
| std::vector<std::pair<DWARFSectionKind, uint32_t>> SectionLength; |
| |
| const DWARFUnitIndex::Entry *DWOEntry = nullptr; |
| if (IsDWP) |
| DWOEntry = CUIndex->getFromHash(*DWOId); |
| |
| bool StrSectionWrittenOut = false; |
| const object::ObjectFile *DWOFile = |
| (*DWOCU)->getContext().getDWARFObj().getFile(); |
| for (const SectionRef &Section : DWOFile->sections()) { |
| std::string Storage = ""; |
| std::unique_ptr<DebugBufferVector> OutputData; |
| Optional<StringRef> TOutData = updateDebugData( |
| (*DWOCU)->getContext(), Storage, Section, KnownSections, *Streamer, |
| *this, DWOEntry, *DWOId, OutputData); |
| if (!TOutData) |
| continue; |
| |
| StringRef OutData = *TOutData; |
| StringRef Name = getSectionName(Section); |
| if (Name.equals("debug_str.dwo")) { |
| CurStrSection = OutData; |
| } else { |
| // Since handleDebugDataPatching returned true, we already know this is |
| // a known section. |
| auto SectionIter = KnownSections.find(Name); |
| if (SectionIter->second.second == DWARFSectionKind::DW_SECT_STR_OFFSETS) |
| CurStrOffsetSection = OutData; |
| else |
| Streamer->emitBytes(OutData); |
| auto Index = |
| getContributionIndex(SectionIter->second.second, IndexVersion); |
| CurEntry.Contributions[Index].Offset = ContributionOffsets[Index]; |
| CurEntry.Contributions[Index].Length = OutData.size(); |
| ContributionOffsets[Index] += CurEntry.Contributions[Index].Length; |
| } |
| |
| // Strings are combined in to a new string section, and de-duplicated |
| // based on hash. |
| if (!StrSectionWrittenOut && !CurStrOffsetSection.empty() && |
| !CurStrSection.empty()) { |
| writeStringsAndOffsets(*Streamer.get(), Strings, StrOffsetSection, |
| CurStrSection, CurStrOffsetSection, |
| CU->getVersion()); |
| StrSectionWrittenOut = true; |
| } |
| } |
| CompileUnitIdentifiers CUI{*DWOId, CurEntry.Name.c_str(), |
| CurEntry.DWOName.c_str()}; |
| auto P = IndexEntries.insert(std::make_pair(CUI.Signature, CurEntry)); |
| if (!P.second) { |
| Error Err = buildDuplicateError(*P.first, CUI, ""); |
| errs() << "BOLT-ERROR: " << toString(std::move(Err)) << "\n"; |
| return; |
| } |
| } |
| |
| // Lie about the type contribution for DWARF < 5. In DWARFv5 the type |
| // section does not exist, so no need to do anything about this. |
| ContributionOffsets[getContributionIndex(DW_SECT_EXT_TYPES, 2)] = 0; |
| writeIndex(*Streamer.get(), MCOFI.getDwarfCUIndexSection(), |
| ContributionOffsets, IndexEntries, IndexVersion); |
| |
| Streamer->Finish(); |
| Out->keep(); |
| } |
| |
| void DWARFRewriter::writeDWOFiles( |
| std::unordered_map<uint64_t, std::string> &DWOIdToName) { |
| // Setup DWP code once. |
| DWARFContext *DWOCtx = BC.getDWOContext(); |
| const DWARFUnitIndex *CUIndex = nullptr; |
| bool IsDWP = false; |
| if (DWOCtx) { |
| CUIndex = &DWOCtx->getCUIndex(); |
| IsDWP = !CUIndex->getRows().empty(); |
| } |
| |
| for (const std::unique_ptr<DWARFUnit> &CU : BC.DwCtx->compile_units()) { |
| Optional<uint64_t> DWOId = CU->getDWOId(); |
| if (!DWOId) |
| continue; |
| |
| // Skipping CUs that we failed to load. |
| Optional<DWARFUnit *> DWOCU = BC.getDWOCU(*DWOId); |
| if (!DWOCU) |
| continue; |
| |
| std::string CompDir = opts::DwarfOutputPath.empty() |
| ? CU->getCompilationDir() |
| : opts::DwarfOutputPath.c_str(); |
| std::string ObjectName = getDWOName(*CU.get(), nullptr, DWOIdToName); |
| auto FullPath = CompDir.append("/").append(ObjectName); |
| |
| std::error_code EC; |
| std::unique_ptr<ToolOutputFile> TempOut = |
| std::make_unique<ToolOutputFile>(FullPath, EC, sys::fs::OF_None); |
| |
| const DWARFUnitIndex::Entry *DWOEntry = nullptr; |
| if (IsDWP) |
| DWOEntry = CUIndex->getFromHash(*DWOId); |
| |
| const object::ObjectFile *File = |
| (*DWOCU)->getContext().getDWARFObj().getFile(); |
| std::unique_ptr<BinaryContext> TmpBC = createDwarfOnlyBC(*File); |
| std::unique_ptr<MCStreamer> Streamer = TmpBC->createStreamer(TempOut->os()); |
| StringMap<KnownSectionsEntry> KnownSections = |
| createKnownSectionsMap(*Streamer->getContext().getObjectFileInfo()); |
| |
| for (const SectionRef &Section : File->sections()) { |
| std::string Storage = ""; |
| std::unique_ptr<DebugBufferVector> OutputData; |
| if (Optional<StringRef> OutData = updateDebugData( |
| (*DWOCU)->getContext(), Storage, Section, KnownSections, |
| *Streamer, *this, DWOEntry, *DWOId, OutputData)) |
| Streamer->emitBytes(*OutData); |
| } |
| Streamer->Finish(); |
| TempOut->keep(); |
| } |
| } |
| |
| void DWARFRewriter::updateGdbIndexSection(CUOffsetMap &CUMap) { |
| if (!BC.getGdbIndexSection()) |
| return; |
| |
| // See https://sourceware.org/gdb/onlinedocs/gdb/Index-Section-Format.html |
| // for .gdb_index section format. |
| |
| StringRef GdbIndexContents = BC.getGdbIndexSection()->getContents(); |
| |
| const char *Data = GdbIndexContents.data(); |
| |
| // Parse the header. |
| const uint32_t Version = read32le(Data); |
| if (Version != 7 && Version != 8) { |
| errs() << "BOLT-ERROR: can only process .gdb_index versions 7 and 8\n"; |
| exit(1); |
| } |
| |
| // Some .gdb_index generators use file offsets while others use section |
| // offsets. Hence we can only rely on offsets relative to each other, |
| // and ignore their absolute values. |
| const uint32_t CUListOffset = read32le(Data + 4); |
| const uint32_t CUTypesOffset = read32le(Data + 8); |
| const uint32_t AddressTableOffset = read32le(Data + 12); |
| const uint32_t SymbolTableOffset = read32le(Data + 16); |
| const uint32_t ConstantPoolOffset = read32le(Data + 20); |
| Data += 24; |
| |
| // Map CUs offsets to indices and verify existing index table. |
| std::map<uint32_t, uint32_t> OffsetToIndexMap; |
| const uint32_t CUListSize = CUTypesOffset - CUListOffset; |
| const unsigned NumCUs = BC.DwCtx->getNumCompileUnits(); |
| if (CUListSize != NumCUs * 16) { |
| errs() << "BOLT-ERROR: .gdb_index: CU count mismatch\n"; |
| exit(1); |
| } |
| for (unsigned Index = 0; Index < NumCUs; ++Index, Data += 16) { |
| const DWARFUnit *CU = BC.DwCtx->getUnitAtIndex(Index); |
| const uint64_t Offset = read64le(Data); |
| if (CU->getOffset() != Offset) { |
| errs() << "BOLT-ERROR: .gdb_index CU offset mismatch\n"; |
| exit(1); |
| } |
| |
| OffsetToIndexMap[Offset] = Index; |
| } |
| |
| // Ignore old address table. |
| const uint32_t OldAddressTableSize = SymbolTableOffset - AddressTableOffset; |
| // Move Data to the beginning of symbol table. |
| Data += SymbolTableOffset - CUTypesOffset; |
| |
| // Calculate the size of the new address table. |
| uint32_t NewAddressTableSize = 0; |
| for (const auto &CURangesPair : ARangesSectionWriter->getCUAddressRanges()) { |
| const SmallVector<DebugAddressRange, 2> &Ranges = CURangesPair.second; |
| NewAddressTableSize += Ranges.size() * 20; |
| } |
| |
| // Difference between old and new table (and section) sizes. |
| // Could be negative. |
| int32_t Delta = NewAddressTableSize - OldAddressTableSize; |
| |
| size_t NewGdbIndexSize = GdbIndexContents.size() + Delta; |
| |
| // Free'd by ExecutableFileMemoryManager. |
| auto *NewGdbIndexContents = new uint8_t[NewGdbIndexSize]; |
| uint8_t *Buffer = NewGdbIndexContents; |
| |
| write32le(Buffer, Version); |
| write32le(Buffer + 4, CUListOffset); |
| write32le(Buffer + 8, CUTypesOffset); |
| write32le(Buffer + 12, AddressTableOffset); |
| write32le(Buffer + 16, SymbolTableOffset + Delta); |
| write32le(Buffer + 20, ConstantPoolOffset + Delta); |
| Buffer += 24; |
| |
| // Writing out CU List <Offset, Size> |
| for (auto &CUInfo : CUMap) { |
| write64le(Buffer, CUInfo.second.Offset); |
| // Length encoded in CU doesn't contain first 4 bytes that encode length. |
| write64le(Buffer + 8, CUInfo.second.Length + 4); |
| Buffer += 16; |
| } |
| |
| // Copy over types CU list |
| // Spec says " triplet, the first value is the CU offset, the second value is |
| // the type offset in the CU, and the third value is the type signature" |
| // Looking at what is being generated by gdb-add-index. The first entry is TU |
| // offset, second entry is offset from it, and third entry is the type |
| // signature. |
| memcpy(Buffer, GdbIndexContents.data() + CUTypesOffset, |
| AddressTableOffset - CUTypesOffset); |
| Buffer += AddressTableOffset - CUTypesOffset; |
| |
| // Generate new address table. |
| for (const std::pair<const uint64_t, DebugAddressRangesVector> &CURangesPair : |
| ARangesSectionWriter->getCUAddressRanges()) { |
| const uint32_t CUIndex = OffsetToIndexMap[CURangesPair.first]; |
| const DebugAddressRangesVector &Ranges = CURangesPair.second; |
| for (const DebugAddressRange &Range : Ranges) { |
| write64le(Buffer, Range.LowPC); |
| write64le(Buffer + 8, Range.HighPC); |
| write32le(Buffer + 16, CUIndex); |
| Buffer += 20; |
| } |
| } |
| |
| const size_t TrailingSize = |
| GdbIndexContents.data() + GdbIndexContents.size() - Data; |
| assert(Buffer + TrailingSize == NewGdbIndexContents + NewGdbIndexSize && |
| "size calculation error"); |
| |
| // Copy over the rest of the original data. |
| memcpy(Buffer, Data, TrailingSize); |
| |
| // Register the new section. |
| BC.registerOrUpdateNoteSection(".gdb_index", NewGdbIndexContents, |
| NewGdbIndexSize); |
| } |
| |
| void DWARFRewriter::convertToRanges(DWARFDie DIE, |
| const DebugAddressRangesVector &Ranges, |
| SimpleBinaryPatcher &DebugInfoPatcher) { |
| uint64_t RangesSectionOffset; |
| if (Ranges.empty()) |
| RangesSectionOffset = RangesSectionWriter->getEmptyRangesOffset(); |
| else |
| RangesSectionOffset = RangesSectionWriter->addRanges(Ranges); |
| |
| convertToRangesPatchDebugInfo(DIE, RangesSectionOffset, DebugInfoPatcher); |
| } |
| |
| void DWARFRewriter::convertPending(const DWARFUnit &Unit, |
| const DWARFAbbreviationDeclaration *Abbrev, |
| SimpleBinaryPatcher &DebugInfoPatcher, |
| DebugAbbrevWriter &AbbrevWriter) { |
| if (ConvertedRangesAbbrevs.count(Abbrev)) |
| return; |
| |
| convertToRangesPatchAbbrev(Unit, Abbrev, AbbrevWriter); |
| |
| auto I = PendingRanges.find(Abbrev); |
| if (I != PendingRanges.end()) { |
| for (std::pair<DWARFDieWrapper, DebugAddressRange> &Pair : I->second) |
| convertToRanges(Pair.first, {Pair.second}, DebugInfoPatcher); |
| PendingRanges.erase(I); |
| } |
| |
| ConvertedRangesAbbrevs.emplace(Abbrev); |
| } |
| |
| void DWARFRewriter::addToPendingRanges( |
| const DWARFAbbreviationDeclaration *Abbrev, DWARFDie DIE, |
| DebugAddressRangesVector &FunctionRanges, Optional<uint64_t> DWOId) { |
| Optional<DWARFFormValue> LowPcValue = DIE.find(dwarf::DW_AT_low_pc); |
| Optional<DWARFFormValue> HighPcValue = DIE.find(dwarf::DW_AT_high_pc); |
| if (LowPcValue && |
| LowPcValue->getForm() == dwarf::Form::DW_FORM_GNU_addr_index) { |
| assert(DWOId && "Invalid DWO ID."); |
| (void)DWOId; |
| assert(HighPcValue && "Low PC exists, but not High PC."); |
| (void)HighPcValue; |
| uint64_t IndexL = LowPcValue->getRawUValue(); |
| uint64_t IndexH = HighPcValue->getRawUValue(); |
| for (auto Address : FunctionRanges) { |
| AddrWriter->addIndexAddress(Address.LowPC, IndexL, *DWOId); |
| // 2.17.2 |
| // If the value of the DW_AT_high_pc is of class address, it is the |
| // relocated address of the first location past the last instruction |
| // associated with the entity; if it is of class constant, the value is |
| // an unsigned integer offset which when added to the low PC gives the |
| // address of the first location past the last instruction associated |
| // with the entity. |
| if (!HighPcValue->isFormClass(DWARFFormValue::FC_Constant)) |
| AddrWriter->addIndexAddress(Address.HighPC, IndexH, *DWOId); |
| } |
| } |
| PendingRanges[Abbrev].emplace_back( |
| std::make_pair(DWARFDieWrapper(DIE), FunctionRanges.front())); |
| } |
| |
| std::unique_ptr<DebugBufferVector> |
| DWARFRewriter::makeFinalLocListsSection(SimpleBinaryPatcher &DebugInfoPatcher) { |
| auto LocBuffer = std::make_unique<DebugBufferVector>(); |
| auto LocStream = std::make_unique<raw_svector_ostream>(*LocBuffer); |
| auto Writer = |
| std::unique_ptr<MCObjectWriter>(BC.createObjectWriter(*LocStream)); |
| |
| uint64_t SectionOffset = 0; |
| |
| // Add an empty list as the first entry; |
| const char Zeroes[16] = {0}; |
| *LocStream << StringRef(Zeroes, 16); |
| SectionOffset += 2 * 8; |
| |
| for (std::pair<const uint64_t, std::unique_ptr<DebugLocWriter>> &Loc : |
| LocListWritersByCU) { |
| DebugLocWriter *LocWriter = Loc.second.get(); |
| if (auto *LocListWriter = llvm::dyn_cast<DebugLoclistWriter>(LocWriter)) { |
| SimpleBinaryPatcher *Patcher = |
| getBinaryDWODebugInfoPatcher(LocListWriter->getDWOID()); |
| LocListWriter->finalize(0, *Patcher); |
| continue; |
| } |
| LocWriter->finalize(SectionOffset, DebugInfoPatcher); |
| std::unique_ptr<DebugBufferVector> CurrCULocationLists = |
| LocWriter->getBuffer(); |
| *LocStream << *CurrCULocationLists; |
| SectionOffset += CurrCULocationLists->size(); |
| } |
| |
| return LocBuffer; |
| } |
| |
| void DWARFRewriter::flushPendingRanges(SimpleBinaryPatcher &DebugInfoPatcher) { |
| for (std::pair<const DWARFAbbreviationDeclaration *const, |
| std::vector<std::pair<DWARFDieWrapper, DebugAddressRange>>> |
| &I : PendingRanges) |
| for (std::pair<DWARFDieWrapper, DebugAddressRange> &RangePair : I.second) |
| patchLowHigh(RangePair.first, RangePair.second, DebugInfoPatcher); |
| clearList(PendingRanges); |
| } |
| |
| namespace { |
| |
| void getRangeAttrData(DWARFDie DIE, Optional<AttrInfo> &LowPCVal, |
| Optional<AttrInfo> &HighPCVal) { |
| LowPCVal = findAttributeInfo(DIE, dwarf::DW_AT_low_pc); |
| HighPCVal = findAttributeInfo(DIE, dwarf::DW_AT_high_pc); |
| uint64_t LowPCOffset = LowPCVal->Offset; |
| uint64_t HighPCOffset = HighPCVal->Offset; |
| dwarf::Form LowPCForm = LowPCVal->V.getForm(); |
| dwarf::Form HighPCForm = HighPCVal->V.getForm(); |
| |
| if (LowPCForm != dwarf::DW_FORM_addr && |
| LowPCForm != dwarf::DW_FORM_GNU_addr_index) { |
| errs() << "BOLT-WARNING: unexpected low_pc form value. Cannot update DIE " |
| << "at offset 0x" << Twine::utohexstr(DIE.getOffset()) << "\n"; |
| return; |
| } |
| if (HighPCForm != dwarf::DW_FORM_addr && HighPCForm != dwarf::DW_FORM_data8 && |
| HighPCForm != dwarf::DW_FORM_data4 && |
| HighPCForm != dwarf::DW_FORM_data2 && |
| HighPCForm != dwarf::DW_FORM_data1 && |
| HighPCForm != dwarf::DW_FORM_udata) { |
| errs() << "BOLT-WARNING: unexpected high_pc form value. Cannot update DIE " |
| << "at offset 0x" << Twine::utohexstr(DIE.getOffset()) << "\n"; |
| return; |
| } |
| if ((LowPCOffset == -1U || (LowPCOffset + 8 != HighPCOffset)) && |
| LowPCForm != dwarf::DW_FORM_GNU_addr_index) { |
| errs() << "BOLT-WARNING: high_pc expected immediately after low_pc. " |
| << "Cannot update DIE at offset 0x" |
| << Twine::utohexstr(DIE.getOffset()) << '\n'; |
| return; |
| } |
| } |
| |
| } // namespace |
| |
| void DWARFRewriter::patchLowHigh(DWARFDie DIE, DebugAddressRange Range, |
| SimpleBinaryPatcher &DebugInfoPatcher) { |
| Optional<AttrInfo> LowPCVal = None; |
| Optional<AttrInfo> HighPCVal = None; |
| getRangeAttrData(DIE, LowPCVal, HighPCVal); |
| uint64_t LowPCOffset = LowPCVal->Offset; |
| uint64_t HighPCOffset = HighPCVal->Offset; |
| auto *TempDebugPatcher = &DebugInfoPatcher; |
| if (LowPCVal->V.getForm() == dwarf::DW_FORM_GNU_addr_index) { |
| DWARFUnit *Unit = DIE.getDwarfUnit(); |
| assert(Unit->isDWOUnit() && "DW_FORM_GNU_addr_index not part of DWO."); |
| uint32_t AddressIndex = |
| AddrWriter->getIndexFromAddress(Range.LowPC, *Unit->getDWOId()); |
| TempDebugPatcher = getBinaryDWODebugInfoPatcher(*Unit->getDWOId()); |
| TempDebugPatcher->addUDataPatch(LowPCOffset, AddressIndex, |
| std::abs(int(HighPCOffset - LowPCOffset))); |
| // TODO: In DWARF5 support ULEB128 for high_pc |
| } else { |
| TempDebugPatcher->addLE64Patch(LowPCOffset, Range.LowPC); |
| } |
| |
| uint64_t HighPC = Range.HighPC; |
| // The DW_FORM_data* is delta between high and low pc |
| if (HighPCVal->V.getForm() != dwarf::Form::DW_FORM_addr) |
| HighPC -= Range.LowPC; |
| |
| if (isHighPcFormEightBytes(HighPCVal->V.getForm())) |
| TempDebugPatcher->addLE64Patch(HighPCOffset, HighPC); |
| else |
| TempDebugPatcher->addLE32Patch(HighPCOffset, HighPC); |
| } |
| |
| void DWARFRewriter::convertToRangesPatchAbbrev( |
| const DWARFUnit &Unit, const DWARFAbbreviationDeclaration *Abbrev, |
| DebugAbbrevWriter &AbbrevWriter, Optional<uint64_t> RangesBase) { |
| auto getAttributeForm = [&Abbrev](const dwarf::Attribute Attr) { |
| Optional<uint32_t> Index = Abbrev->findAttributeIndex(Attr); |
| assert(Index && "attribute not found"); |
| return Abbrev->getFormByIndex(*Index); |
| }; |
| dwarf::Form LowPCForm = getAttributeForm(dwarf::DW_AT_low_pc); |
| |
| // DW_FORM_GNU_addr_index is already variable encoding so nothing to do |
| // there. |
| if (RangesBase) { |
| assert(LowPCForm != dwarf::DW_FORM_GNU_addr_index); |
| AbbrevWriter.addAttributePatch(Unit, Abbrev, dwarf::DW_AT_low_pc, |
| dwarf::DW_AT_GNU_ranges_base, |
| dwarf::DW_FORM_sec_offset); |
| } |
| |
| AbbrevWriter.addAttributePatch(Unit, Abbrev, dwarf::DW_AT_high_pc, |
| dwarf::DW_AT_ranges, |
| dwarf::DW_FORM_sec_offset); |
| } |
| |
| void DWARFRewriter::convertToRangesPatchDebugInfo( |
| DWARFDie DIE, uint64_t RangesSectionOffset, |
| SimpleBinaryPatcher &DebugInfoPatcher, Optional<uint64_t> RangesBase) { |
| Optional<AttrInfo> LowPCVal = None; |
| Optional<AttrInfo> HighPCVal = None; |
| getRangeAttrData(DIE, LowPCVal, HighPCVal); |
| uint64_t LowPCOffset = LowPCVal->Offset; |
| uint64_t HighPCOffset = HighPCVal->Offset; |
| |
| std::lock_guard<std::mutex> Lock(DebugInfoPatcherMutex); |
| uint32_t BaseOffset = 0; |
| if (LowPCVal->V.getForm() == dwarf::DW_FORM_GNU_addr_index) { |
| // Use ULEB128 for the value. |
| DebugInfoPatcher.addUDataPatch(LowPCOffset, 0, |
| std::abs(int(HighPCOffset - LowPCOffset))); |
| // Ranges are relative to DW_AT_GNU_ranges_base. |
| BaseOffset = DebugInfoPatcher.getRangeBase(); |
| } else { |
| // If case DW_AT_low_pc was converted into DW_AT_GNU_ranges_base |
| if (RangesBase) |
| DebugInfoPatcher.addLE32Patch(LowPCOffset, *RangesBase, 8); |
| else |
| DebugInfoPatcher.addLE64Patch(LowPCOffset, 0); |
| } |
| DebugInfoPatcher.addLE32Patch(HighPCOffset, RangesSectionOffset - BaseOffset, |
| HighPCVal->Size); |
| } |