Path: blob/main/contrib/llvm-project/llvm/tools/llvm-dwarfutil/DebugInfoLinker.cpp
35231 views
//=== DebugInfoLinker.cpp -------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "DebugInfoLinker.h"9#include "Error.h"10#include "llvm/ADT/StringSwitch.h"11#include "llvm/DWARFLinker/Classic/DWARFLinker.h"12#include "llvm/DWARFLinker/Classic/DWARFStreamer.h"13#include "llvm/DWARFLinker/Parallel/DWARFLinker.h"14#include "llvm/DebugInfo/DWARF/DWARFContext.h"15#include "llvm/DebugInfo/DWARF/DWARFExpression.h"16#include "llvm/Object/ObjectFile.h"17#include <memory>18#include <vector>1920namespace llvm {21using namespace dwarf_linker;2223namespace dwarfutil {2425// ObjFileAddressMap allows to check whether specified DIE referencing26// dead addresses. It uses tombstone values to determine dead addresses.27// The concrete values of tombstone constants were discussed in28// https://reviews.llvm.org/D81784 and https://reviews.llvm.org/D84825.29// So we use following values as indicators of dead addresses:30//31// bfd: (LowPC == 0) or (LowPC == 1 and HighPC == 1 and DWARF v4 (or less))32// or ([LowPC, HighPC] is not inside address ranges of .text sections).33//34// maxpc: (LowPC == -1) or (LowPC == -2 and DWARF v4 (or less))35// That value is assumed to be compatible with36// http://www.dwarfstd.org/ShowIssue.php?issue=200609.137//38// exec: [LowPC, HighPC] is not inside address ranges of .text sections39//40// universal: maxpc and bfd41class ObjFileAddressMap : public AddressesMap {42public:43ObjFileAddressMap(DWARFContext &Context, const Options &Options,44object::ObjectFile &ObjFile)45: Opts(Options) {46// Remember addresses of existing text sections.47for (const object::SectionRef &Sect : ObjFile.sections()) {48if (!Sect.isText())49continue;50const uint64_t Size = Sect.getSize();51if (Size == 0)52continue;53const uint64_t StartAddr = Sect.getAddress();54TextAddressRanges.insert({StartAddr, StartAddr + Size});55}5657// Check CU address ranges for tombstone value.58for (std::unique_ptr<DWARFUnit> &CU : Context.compile_units()) {59Expected<llvm::DWARFAddressRangesVector> ARanges =60CU->getUnitDIE().getAddressRanges();61if (!ARanges) {62llvm::consumeError(ARanges.takeError());63continue;64}6566for (auto &Range : *ARanges) {67if (!isDeadAddressRange(Range.LowPC, Range.HighPC, CU->getVersion(),68Options.Tombstone, CU->getAddressByteSize())) {69HasValidAddressRanges = true;70break;71}72}7374if (HasValidAddressRanges)75break;76}77}7879// should be renamed into has valid address ranges80bool hasValidRelocs() override { return HasValidAddressRanges; }8182std::optional<int64_t> getSubprogramRelocAdjustment(const DWARFDie &DIE,83bool Verbose) override {84assert((DIE.getTag() == dwarf::DW_TAG_subprogram ||85DIE.getTag() == dwarf::DW_TAG_label) &&86"Wrong type of input die");8788if (std::optional<uint64_t> LowPC =89dwarf::toAddress(DIE.find(dwarf::DW_AT_low_pc))) {90if (!isDeadAddress(*LowPC, DIE.getDwarfUnit()->getVersion(),91Opts.Tombstone,92DIE.getDwarfUnit()->getAddressByteSize()))93// Relocation value for the linked binary is 0.94return 0;95}9697return std::nullopt;98}99100std::optional<int64_t>101getExprOpAddressRelocAdjustment(DWARFUnit &U,102const DWARFExpression::Operation &Op,103uint64_t, uint64_t, bool Verbose) override {104switch (Op.getCode()) {105default: {106assert(false && "Specified operation does not have address operand");107} break;108case dwarf::DW_OP_const2u:109case dwarf::DW_OP_const4u:110case dwarf::DW_OP_const8u:111case dwarf::DW_OP_const2s:112case dwarf::DW_OP_const4s:113case dwarf::DW_OP_const8s:114case dwarf::DW_OP_addr: {115if (!isDeadAddress(Op.getRawOperand(0), U.getVersion(), Opts.Tombstone,116U.getAddressByteSize()))117// Relocation value for the linked binary is 0.118return 0;119} break;120case dwarf::DW_OP_constx:121case dwarf::DW_OP_addrx: {122if (std::optional<object::SectionedAddress> Address =123U.getAddrOffsetSectionItem(Op.getRawOperand(0))) {124if (!isDeadAddress(Address->Address, U.getVersion(), Opts.Tombstone,125U.getAddressByteSize()))126// Relocation value for the linked binary is 0.127return 0;128}129} break;130}131132return std::nullopt;133}134135std::optional<StringRef> getLibraryInstallName() override {136return std::nullopt;137}138139bool applyValidRelocs(MutableArrayRef<char>, uint64_t, bool) override {140// no need to apply relocations to the linked binary.141return false;142}143144bool needToSaveValidRelocs() override { return false; }145146void updateAndSaveValidRelocs(bool, uint64_t, int64_t, uint64_t,147uint64_t) override {}148149void updateRelocationsWithUnitOffset(uint64_t OriginalUnitOffset,150uint64_t OutputUnitOffset) override {}151152void clear() override {}153154protected:155// returns true if specified address range is inside address ranges156// of executable sections.157bool isInsideExecutableSectionsAddressRange(uint64_t LowPC,158std::optional<uint64_t> HighPC) {159std::optional<AddressRange> Range =160TextAddressRanges.getRangeThatContains(LowPC);161162if (HighPC)163return Range.has_value() && Range->end() >= *HighPC;164165return Range.has_value();166}167168uint64_t isBFDDeadAddressRange(uint64_t LowPC, std::optional<uint64_t> HighPC,169uint16_t Version) {170if (LowPC == 0)171return true;172173if ((Version <= 4) && HighPC && (LowPC == 1 && *HighPC == 1))174return true;175176return !isInsideExecutableSectionsAddressRange(LowPC, HighPC);177}178179uint64_t isMAXPCDeadAddressRange(uint64_t LowPC,180std::optional<uint64_t> HighPC,181uint16_t Version, uint8_t AddressByteSize) {182if (Version <= 4 && HighPC) {183if (LowPC == (dwarf::computeTombstoneAddress(AddressByteSize) - 1))184return true;185} else if (LowPC == dwarf::computeTombstoneAddress(AddressByteSize))186return true;187188if (!isInsideExecutableSectionsAddressRange(LowPC, HighPC))189warning("Address referencing invalid text section is not marked with "190"tombstone value");191192return false;193}194195bool isDeadAddressRange(uint64_t LowPC, std::optional<uint64_t> HighPC,196uint16_t Version, TombstoneKind Tombstone,197uint8_t AddressByteSize) {198switch (Tombstone) {199case TombstoneKind::BFD:200return isBFDDeadAddressRange(LowPC, HighPC, Version);201case TombstoneKind::MaxPC:202return isMAXPCDeadAddressRange(LowPC, HighPC, Version, AddressByteSize);203case TombstoneKind::Universal:204return isBFDDeadAddressRange(LowPC, HighPC, Version) ||205isMAXPCDeadAddressRange(LowPC, HighPC, Version, AddressByteSize);206case TombstoneKind::Exec:207return !isInsideExecutableSectionsAddressRange(LowPC, HighPC);208}209210llvm_unreachable("Unknown tombstone value");211}212213bool isDeadAddress(uint64_t LowPC, uint16_t Version, TombstoneKind Tombstone,214uint8_t AddressByteSize) {215return isDeadAddressRange(LowPC, std::nullopt, Version, Tombstone,216AddressByteSize);217}218219private:220AddressRanges TextAddressRanges;221const Options &Opts;222bool HasValidAddressRanges = false;223};224225static bool knownByDWARFUtil(StringRef SecName) {226return llvm::StringSwitch<bool>(SecName)227.Case(".debug_info", true)228.Case(".debug_types", true)229.Case(".debug_abbrev", true)230.Case(".debug_loc", true)231.Case(".debug_loclists", true)232.Case(".debug_frame", true)233.Case(".debug_aranges", true)234.Case(".debug_ranges", true)235.Case(".debug_rnglists", true)236.Case(".debug_line", true)237.Case(".debug_line_str", true)238.Case(".debug_addr", true)239.Case(".debug_macro", true)240.Case(".debug_macinfo", true)241.Case(".debug_str", true)242.Case(".debug_str_offsets", true)243.Case(".debug_pubnames", true)244.Case(".debug_pubtypes", true)245.Case(".debug_names", true)246.Default(false);247}248249template <typename AccelTableKind>250static std::optional<AccelTableKind>251getAcceleratorTableKind(StringRef SecName) {252return llvm::StringSwitch<std::optional<AccelTableKind>>(SecName)253.Case(".debug_pubnames", AccelTableKind::Pub)254.Case(".debug_pubtypes", AccelTableKind::Pub)255.Case(".debug_names", AccelTableKind::DebugNames)256.Default(std::nullopt);257}258259static std::string getMessageForReplacedAcceleratorTables(260SmallVector<StringRef> &AccelTableNamesToReplace,261DwarfUtilAccelKind TargetTable) {262std::string Message;263264Message += "'";265for (StringRef Name : AccelTableNamesToReplace) {266if (Message.size() > 1)267Message += ", ";268Message += Name;269}270271Message += "' will be replaced with requested ";272273switch (TargetTable) {274case DwarfUtilAccelKind::DWARF:275Message += ".debug_names table";276break;277278default:279assert(false);280}281282return Message;283}284285static std::string getMessageForDeletedAcceleratorTables(286SmallVector<StringRef> &AccelTableNamesToReplace) {287std::string Message;288289Message += "'";290for (StringRef Name : AccelTableNamesToReplace) {291if (Message.size() > 1)292Message += ", ";293Message += Name;294}295296Message += "' will be deleted as no accelerator tables are requested";297298return Message;299}300301template <typename Linker>302Error linkDebugInfoImpl(object::ObjectFile &File, const Options &Options,303raw_pwrite_stream &OutStream) {304std::mutex ErrorHandlerMutex;305306auto ReportWarn = [&](const Twine &Message, StringRef Context,307const DWARFDie *Die) {308// FIXME: implement warning logging which does not block other threads.309if (!ErrorHandlerMutex.try_lock())310return;311312warning(Message, Context);313if (Options.Verbose && Die) {314DIDumpOptions DumpOpts;315DumpOpts.ChildRecurseDepth = 0;316DumpOpts.Verbose = Options.Verbose;317318WithColor::note() << " in DIE:\n";319Die->dump(errs(), /*Indent=*/6, DumpOpts);320}321ErrorHandlerMutex.unlock();322};323auto ReportErr = [&](const Twine &Message, StringRef Context,324const DWARFDie *) {325// FIXME: implement error logging which does not block other threads.326if (!ErrorHandlerMutex.try_lock())327return;328329WithColor::error(errs(), Context) << Message << '\n';330ErrorHandlerMutex.unlock();331};332333// Create DWARF linker.334std::unique_ptr<Linker> DebugInfoLinker =335Linker::createLinker(ReportErr, ReportWarn);336337Triple TargetTriple = File.makeTriple();338std::unique_ptr<classic::DwarfStreamer> Streamer;339if (Expected<std::unique_ptr<classic::DwarfStreamer>> StreamerOrErr =340classic::DwarfStreamer::createStreamer(TargetTriple,341Linker::OutputFileType::Object,342OutStream, ReportWarn))343Streamer = std::move(*StreamerOrErr);344else345return StreamerOrErr.takeError();346347if constexpr (std::is_same<Linker,348dwarf_linker::parallel::DWARFLinker>::value) {349DebugInfoLinker->setOutputDWARFHandler(350TargetTriple,351[&](std::shared_ptr<dwarf_linker::parallel::SectionDescriptorBase>352Section) {353Streamer->emitSectionContents(Section->getContents(),354Section->getKind());355});356} else357DebugInfoLinker->setOutputDWARFEmitter(Streamer.get());358359DebugInfoLinker->setEstimatedObjfilesAmount(1);360DebugInfoLinker->setNumThreads(Options.NumThreads);361DebugInfoLinker->setNoODR(!Options.DoODRDeduplication);362DebugInfoLinker->setVerbosity(Options.Verbose);363DebugInfoLinker->setUpdateIndexTablesOnly(!Options.DoGarbageCollection);364365std::vector<std::unique_ptr<DWARFFile>> ObjectsForLinking(1);366367// Add object files to the DWARFLinker.368std::unique_ptr<DWARFContext> Context = DWARFContext::create(369File, DWARFContext::ProcessDebugRelocations::Process, nullptr, "",370[&](Error Err) {371handleAllErrors(std::move(Err), [&](ErrorInfoBase &Info) {372ReportErr(Info.message(), "", nullptr);373});374},375[&](Error Warning) {376handleAllErrors(std::move(Warning), [&](ErrorInfoBase &Info) {377ReportWarn(Info.message(), "", nullptr);378});379});380std::unique_ptr<ObjFileAddressMap> AddressesMap(381std::make_unique<ObjFileAddressMap>(*Context, Options, File));382383ObjectsForLinking[0] = std::make_unique<DWARFFile>(384File.getFileName(), std::move(Context), std::move(AddressesMap));385386uint16_t MaxDWARFVersion = 0;387std::function<void(const DWARFUnit &Unit)> OnCUDieLoaded =388[&MaxDWARFVersion](const DWARFUnit &Unit) {389MaxDWARFVersion = std::max(Unit.getVersion(), MaxDWARFVersion);390};391392for (size_t I = 0; I < ObjectsForLinking.size(); I++)393DebugInfoLinker->addObjectFile(*ObjectsForLinking[I], nullptr,394OnCUDieLoaded);395396// If we haven't seen any CUs, pick an arbitrary valid Dwarf version anyway.397if (MaxDWARFVersion == 0)398MaxDWARFVersion = 3;399400if (Error Err = DebugInfoLinker->setTargetDWARFVersion(MaxDWARFVersion))401return Err;402403SmallVector<typename Linker::AccelTableKind> AccelTables;404405switch (Options.AccelTableKind) {406case DwarfUtilAccelKind::None:407// Nothing to do.408break;409case DwarfUtilAccelKind::DWARF:410// use .debug_names for all DWARF versions.411AccelTables.push_back(Linker::AccelTableKind::DebugNames);412break;413}414415// Add accelerator tables to DWARFLinker.416for (typename Linker::AccelTableKind Table : AccelTables)417DebugInfoLinker->addAccelTableKind(Table);418419for (std::unique_ptr<DWARFFile> &CurFile : ObjectsForLinking) {420SmallVector<StringRef> AccelTableNamesToReplace;421SmallVector<StringRef> AccelTableNamesToDelete;422423// Unknown debug sections or non-requested accelerator sections would be424// removed. Display warning for such sections.425for (SectionName Sec : CurFile->Dwarf->getDWARFObj().getSectionNames()) {426if (isDebugSection(Sec.Name)) {427std::optional<typename Linker::AccelTableKind> SrcAccelTableKind =428getAcceleratorTableKind<typename Linker::AccelTableKind>(Sec.Name);429430if (SrcAccelTableKind) {431assert(knownByDWARFUtil(Sec.Name));432433if (Options.AccelTableKind == DwarfUtilAccelKind::None)434AccelTableNamesToDelete.push_back(Sec.Name);435else if (!llvm::is_contained(AccelTables, *SrcAccelTableKind))436AccelTableNamesToReplace.push_back(Sec.Name);437} else if (!knownByDWARFUtil(Sec.Name)) {438assert(!SrcAccelTableKind);439warning(440formatv(441"'{0}' is not currently supported: section will be skipped",442Sec.Name),443Options.InputFileName);444}445}446}447448// Display message for the replaced accelerator tables.449if (!AccelTableNamesToReplace.empty())450warning(getMessageForReplacedAcceleratorTables(AccelTableNamesToReplace,451Options.AccelTableKind),452Options.InputFileName);453454// Display message for the removed accelerator tables.455if (!AccelTableNamesToDelete.empty())456warning(getMessageForDeletedAcceleratorTables(AccelTableNamesToDelete),457Options.InputFileName);458}459460// Link debug info.461if (Error Err = DebugInfoLinker->link())462return Err;463464Streamer->finish();465return Error::success();466}467468Error linkDebugInfo(object::ObjectFile &File, const Options &Options,469raw_pwrite_stream &OutStream) {470if (Options.UseDWARFLinkerParallel)471return linkDebugInfoImpl<parallel::DWARFLinker>(File, Options, OutStream);472else473return linkDebugInfoImpl<classic::DWARFLinker>(File, Options, OutStream);474}475476} // end of namespace dwarfutil477} // end of namespace llvm478479480