diff options
Diffstat (limited to 'llvm/tools/llvm-dwarfutil')
| -rw-r--r-- | llvm/tools/llvm-dwarfutil/DebugInfoLinker.cpp | 185 | ||||
| -rw-r--r-- | llvm/tools/llvm-dwarfutil/Options.h | 7 | ||||
| -rw-r--r-- | llvm/tools/llvm-dwarfutil/Options.td | 8 | ||||
| -rw-r--r-- | llvm/tools/llvm-dwarfutil/llvm-dwarfutil.cpp | 32 |
4 files changed, 199 insertions, 33 deletions
diff --git a/llvm/tools/llvm-dwarfutil/DebugInfoLinker.cpp b/llvm/tools/llvm-dwarfutil/DebugInfoLinker.cpp index 3e70f460bc58..ef222f8cc1a4 100644 --- a/llvm/tools/llvm-dwarfutil/DebugInfoLinker.cpp +++ b/llvm/tools/llvm-dwarfutil/DebugInfoLinker.cpp @@ -14,6 +14,7 @@ #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/DebugInfo/DWARF/DWARFExpression.h" #include "llvm/Object/ObjectFile.h" +#include "llvm/Support/Endian.h" #include <memory> #include <vector> @@ -40,7 +41,7 @@ class ObjFileAddressMap : public AddressesMap { public: ObjFileAddressMap(DWARFContext &Context, const Options &Options, object::ObjectFile &ObjFile) - : Opts(Options) { + : Opts(Options), Context(Context) { // Remember addresses of existing text sections. for (const object::SectionRef &Sect : ObjFile.sections()) { if (!Sect.isText()) @@ -75,7 +76,7 @@ public: DIE.getTag() == dwarf::DW_TAG_label) && "Wrong type of input die"); - if (Optional<uint64_t> LowPC = + if (std::optional<uint64_t> LowPC = dwarf::toAddress(DIE.find(dwarf::DW_AT_low_pc))) { if (!isDeadAddress(*LowPC, DIE.getDwarfUnit()->getVersion(), Opts.Tombstone, @@ -137,17 +138,36 @@ public: void clear() override { DWARFAddressRanges.clear(); } - llvm::Expected<uint64_t> relocateIndexedAddr(uint64_t, uint64_t) override { - // should not be called. - return object::createError("no relocations in linked binary"); + llvm::Expected<uint64_t> relocateIndexedAddr(uint64_t StartOffset, + uint64_t EndOffset) override { + // No relocations in linked binary. Return just address value. + + const char *AddrPtr = + Context.getDWARFObj().getAddrSection().Data.data() + StartOffset; + support::endianness Endianess = + Context.getDWARFObj().isLittleEndian() ? support::little : support::big; + + assert(EndOffset > StartOffset); + switch (EndOffset - StartOffset) { + case 1: + return *AddrPtr; + case 2: + return support::endian::read16(AddrPtr, Endianess); + case 4: + return support::endian::read32(AddrPtr, Endianess); + case 8: + return support::endian::read64(AddrPtr, Endianess); + } + + llvm_unreachable("relocateIndexedAddr unhandled case!"); } protected: // returns true if specified address range is inside address ranges // of executable sections. bool isInsideExecutableSectionsAddressRange(uint64_t LowPC, - Optional<uint64_t> HighPC) { - Optional<AddressRange> Range = + std::optional<uint64_t> HighPC) { + std::optional<AddressRange> Range = TextAddressRanges.getRangeThatContains(LowPC); if (HighPC) @@ -156,7 +176,7 @@ protected: return Range.has_value(); } - uint64_t isBFDDeadAddressRange(uint64_t LowPC, Optional<uint64_t> HighPC, + uint64_t isBFDDeadAddressRange(uint64_t LowPC, std::optional<uint64_t> HighPC, uint16_t Version) { if (LowPC == 0) return true; @@ -167,7 +187,8 @@ protected: return !isInsideExecutableSectionsAddressRange(LowPC, HighPC); } - uint64_t isMAXPCDeadAddressRange(uint64_t LowPC, Optional<uint64_t> HighPC, + uint64_t isMAXPCDeadAddressRange(uint64_t LowPC, + std::optional<uint64_t> HighPC, uint16_t Version, uint8_t AddressByteSize) { if (Version <= 4 && HighPC) { if (LowPC == (dwarf::computeTombstoneAddress(AddressByteSize) - 1)) @@ -182,7 +203,7 @@ protected: return false; } - bool isDeadAddressRange(uint64_t LowPC, Optional<uint64_t> HighPC, + bool isDeadAddressRange(uint64_t LowPC, std::optional<uint64_t> HighPC, uint16_t Version, TombstoneKind Tombstone, uint8_t AddressByteSize) { switch (Tombstone) { @@ -202,13 +223,15 @@ protected: bool isDeadAddress(uint64_t LowPC, uint16_t Version, TombstoneKind Tombstone, uint8_t AddressByteSize) { - return isDeadAddressRange(LowPC, None, Version, Tombstone, AddressByteSize); + return isDeadAddressRange(LowPC, std::nullopt, Version, Tombstone, + AddressByteSize); } private: RangesTy DWARFAddressRanges; AddressRanges TextAddressRanges; const Options &Opts; + DWARFContext &Context; }; static bool knownByDWARFUtil(StringRef SecName) { @@ -229,9 +252,63 @@ static bool knownByDWARFUtil(StringRef SecName) { .Case(".debug_macinfo", true) .Case(".debug_str", true) .Case(".debug_str_offsets", true) + .Case(".debug_pubnames", true) + .Case(".debug_pubtypes", true) + .Case(".debug_names", true) .Default(false); } +static std::optional<DwarfLinkerAccelTableKind> +getAcceleratorTableKind(StringRef SecName) { + return llvm::StringSwitch<std::optional<DwarfLinkerAccelTableKind>>(SecName) + .Case(".debug_pubnames", DwarfLinkerAccelTableKind::Pub) + .Case(".debug_pubtypes", DwarfLinkerAccelTableKind::Pub) + .Case(".debug_names", DwarfLinkerAccelTableKind::DebugNames) + .Default(std::nullopt); +} + +static std::string getMessageForReplacedAcceleratorTables( + SmallVector<StringRef> &AccelTableNamesToReplace, + DwarfUtilAccelKind TargetTable) { + std::string Message; + + Message += "'"; + for (StringRef Name : AccelTableNamesToReplace) { + if (Message.size() > 1) + Message += ", "; + Message += Name; + } + + Message += "' will be replaced with requested "; + + switch (TargetTable) { + case DwarfUtilAccelKind::DWARF: + Message += ".debug_names table"; + break; + + default: + assert(false); + } + + return Message; +} + +static std::string getMessageForDeletedAcceleratorTables( + SmallVector<StringRef> &AccelTableNamesToReplace) { + std::string Message; + + Message += "'"; + for (StringRef Name : AccelTableNamesToReplace) { + if (Message.size() > 1) + Message += ", "; + Message += Name; + } + + Message += "' will be deleted as no accelerator tables are requested"; + + return Message; +} + Error linkDebugInfo(object::ObjectFile &File, const Options &Options, raw_pwrite_stream &OutStream) { @@ -263,11 +340,12 @@ Error linkDebugInfo(object::ObjectFile &File, const Options &Options, .str())) return createStringError(std::errc::invalid_argument, ""); + std::unique_ptr<DWARFContext> Context = DWARFContext::create(File); + // Create DWARF linker. DWARFLinker DebugInfoLinker(&OutStreamer, DwarfLinkerClient::LLD); DebugInfoLinker.setEstimatedObjfilesAmount(1); - DebugInfoLinker.setAccelTableKind(DwarfLinkerAccelTableKind::None); DebugInfoLinker.setErrorHandler(ReportErr); DebugInfoLinker.setWarningHandler(ReportWarn); DebugInfoLinker.setNumThreads(Options.NumThreads); @@ -279,18 +357,6 @@ Error linkDebugInfo(object::ObjectFile &File, const Options &Options, std::vector<std::unique_ptr<AddressesMap>> AddresssMapForLinking(1); std::vector<std::string> EmptyWarnings; - std::unique_ptr<DWARFContext> Context = DWARFContext::create(File); - - // Unknown debug sections would be removed. Display warning - // for such sections. - for (SectionName Sec : Context->getDWARFObj().getSectionNames()) { - if (isDebugSection(Sec.Name) && !knownByDWARFUtil(Sec.Name)) - warning( - formatv("'{0}' is not currently supported: section will be skipped", - Sec.Name), - Options.InputFileName); - } - // Add object files to the DWARFLinker. AddresssMapForLinking[0] = std::make_unique<ObjFileAddressMap>(*Context, Options, File); @@ -299,8 +365,77 @@ Error linkDebugInfo(object::ObjectFile &File, const Options &Options, File.getFileName(), &*Context, AddresssMapForLinking[0].get(), EmptyWarnings); + uint16_t MaxDWARFVersion = 0; + std::function<void(const DWARFUnit &Unit)> OnCUDieLoaded = + [&MaxDWARFVersion](const DWARFUnit &Unit) { + MaxDWARFVersion = std::max(Unit.getVersion(), MaxDWARFVersion); + }; + for (size_t I = 0; I < ObjectsForLinking.size(); I++) - DebugInfoLinker.addObjectFile(*ObjectsForLinking[I]); + DebugInfoLinker.addObjectFile(*ObjectsForLinking[I], nullptr, + OnCUDieLoaded); + + // If we haven't seen any CUs, pick an arbitrary valid Dwarf version anyway. + if (MaxDWARFVersion == 0) + MaxDWARFVersion = 3; + + if (Error Err = DebugInfoLinker.setTargetDWARFVersion(MaxDWARFVersion)) + return Err; + + SmallVector<DwarfLinkerAccelTableKind> AccelTables; + + switch (Options.AccelTableKind) { + case DwarfUtilAccelKind::None: + // Nothing to do. + break; + case DwarfUtilAccelKind::DWARF: + // use .debug_names for all DWARF versions. + AccelTables.push_back(DwarfLinkerAccelTableKind::DebugNames); + break; + } + + // Add accelerator tables to DWARFLinker. + for (DwarfLinkerAccelTableKind Table : AccelTables) + DebugInfoLinker.addAccelTableKind(Table); + + SmallVector<StringRef> AccelTableNamesToReplace; + SmallVector<StringRef> AccelTableNamesToDelete; + + // Unknown debug sections or non-requested accelerator sections would be + // removed. Display warning for such sections. + for (SectionName Sec : Context->getDWARFObj().getSectionNames()) { + if (isDebugSection(Sec.Name)) { + std::optional<DwarfLinkerAccelTableKind> SrcAccelTableKind = + getAcceleratorTableKind(Sec.Name); + + if (SrcAccelTableKind) { + assert(knownByDWARFUtil(Sec.Name)); + + if (Options.AccelTableKind == DwarfUtilAccelKind::None) + AccelTableNamesToDelete.push_back(Sec.Name); + else if (std::find(AccelTables.begin(), AccelTables.end(), + *SrcAccelTableKind) == AccelTables.end()) + AccelTableNamesToReplace.push_back(Sec.Name); + } else if (!knownByDWARFUtil(Sec.Name)) { + assert(!SrcAccelTableKind); + warning( + formatv("'{0}' is not currently supported: section will be skipped", + Sec.Name), + Options.InputFileName); + } + } + } + + // Display message for the replaced accelerator tables. + if (!AccelTableNamesToReplace.empty()) + warning(getMessageForReplacedAcceleratorTables(AccelTableNamesToReplace, + Options.AccelTableKind), + Options.InputFileName); + + // Display message for the removed accelerator tables. + if (!AccelTableNamesToDelete.empty()) + warning(getMessageForDeletedAcceleratorTables(AccelTableNamesToDelete), + Options.InputFileName); // Link debug info. if (Error Err = DebugInfoLinker.link()) diff --git a/llvm/tools/llvm-dwarfutil/Options.h b/llvm/tools/llvm-dwarfutil/Options.h index c993200ceb4b..38fa2b9eda63 100644 --- a/llvm/tools/llvm-dwarfutil/Options.h +++ b/llvm/tools/llvm-dwarfutil/Options.h @@ -24,6 +24,12 @@ enum class TombstoneKind { Exec, /// match with address range of executable sections. }; +/// The kind of accelerator table. +enum class DwarfUtilAccelKind : uint8_t { + None, + DWARF // DWARFv5: .debug_names +}; + struct Options { std::string InputFileName; std::string OutputFileName; @@ -34,6 +40,7 @@ struct Options { bool Verbose = false; int NumThreads = 0; bool Verify = false; + DwarfUtilAccelKind AccelTableKind = DwarfUtilAccelKind::None; std::string getSeparateDebugFileName() const { return OutputFileName + ".debug"; diff --git a/llvm/tools/llvm-dwarfutil/Options.td b/llvm/tools/llvm-dwarfutil/Options.td index 4ab1b51d808d..d4541188c0c2 100644 --- a/llvm/tools/llvm-dwarfutil/Options.td +++ b/llvm/tools/llvm-dwarfutil/Options.td @@ -5,6 +5,14 @@ multiclass BB<string name, string help1, string help2> { def no_ # NAME: Flag<["--"], "no-" # name>, HelpText<help2>; } +def build_accelerator: Separate<["--", "-"], "build-accelerator">, + MetaVarName<"[none,DWARF]">, + HelpText<"Build accelerator tables(default: none)\n" + " =none - Do not build accelerators\n" + " =DWARF - .debug_names are generated for all DWARF versions\n" + >; +def: Joined<["--", "-"], "build-accelerator=">, Alias<build_accelerator>; + def help : Flag<["--"], "help">, HelpText<"Prints this help output">; diff --git a/llvm/tools/llvm-dwarfutil/llvm-dwarfutil.cpp b/llvm/tools/llvm-dwarfutil/llvm-dwarfutil.cpp index a6466be37513..74b6104bc668 100644 --- a/llvm/tools/llvm-dwarfutil/llvm-dwarfutil.cpp +++ b/llvm/tools/llvm-dwarfutil/llvm-dwarfutil.cpp @@ -40,11 +40,14 @@ enum ID { #undef OPTION }; -#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; +#define PREFIX(NAME, VALUE) \ + static constexpr StringLiteral NAME##_init[] = VALUE; \ + static constexpr ArrayRef<StringLiteral> NAME(NAME##_init, \ + std::size(NAME##_init) - 1); #include "Options.inc" #undef PREFIX -const opt::OptTable::Info InfoTable[] = { +static constexpr opt::OptTable::Info InfoTable[] = { #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ HELPTEXT, METAVAR, VALUES) \ { \ @@ -56,9 +59,9 @@ const opt::OptTable::Info InfoTable[] = { #undef OPTION }; -class DwarfutilOptTable : public opt::OptTable { +class DwarfutilOptTable : public opt::GenericOptTable { public: - DwarfutilOptTable() : OptTable(InfoTable) {} + DwarfutilOptTable() : opt::GenericOptTable(InfoTable) {} }; } // namespace @@ -120,6 +123,19 @@ static Error validateAndSetOptions(opt::InputArgList &Args, Options &Options) { formatv("unknown tombstone value: '{0}'", S).str().c_str()); } + if (opt::Arg *BuildAccelerator = Args.getLastArg(OPT_build_accelerator)) { + StringRef S = BuildAccelerator->getValue(); + + if (S == "none") + Options.AccelTableKind = DwarfUtilAccelKind::None; + else if (S == "DWARF") + Options.AccelTableKind = DwarfUtilAccelKind::DWARF; + else + return createStringError( + std::errc::invalid_argument, + formatv("unknown build-accelerator value: '{0}'", S).str().c_str()); + } + if (Options.Verbose) { if (Options.NumThreads != 1 && Args.hasArg(OPT_threads)) warning("--num-threads set to 1 because verbose mode is specified"); @@ -420,8 +436,9 @@ static Error saveCopyOfFile(const Options &Opts, ObjectFile &InputFile) { } static Error applyCLOptions(const struct Options &Opts, ObjectFile &InputFile) { - if (Opts.DoGarbageCollection) { - verbose("Do garbage collection for debug info ...", Opts.Verbose); + if (Opts.DoGarbageCollection || + Opts.AccelTableKind != DwarfUtilAccelKind::None) { + verbose("Do debug info linking...", Opts.Verbose); DebugInfoBits LinkedDebugInfo; raw_svector_ostream OutStream(LinkedDebugInfo); @@ -458,7 +475,7 @@ int main(int Argc, char const *Argv[]) { DwarfutilOptTable T; unsigned MAI; unsigned MAC; - ArrayRef<const char *> ArgsArr = makeArrayRef(Argv + 1, Argc - 1); + ArrayRef<const char *> ArgsArr = ArrayRef(Argv + 1, Argc - 1); opt::InputArgList Args = T.ParseArgs(ArgsArr, MAI, MAC); if (Args.hasArg(OPT_help) || Args.size() == 0) { @@ -481,7 +498,6 @@ int main(int Argc, char const *Argv[]) { InitializeAllTargetMCs(); InitializeAllTargetInfos(); InitializeAllAsmPrinters(); - InitializeAllAsmParsers(); ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr = MemoryBuffer::getFileOrSTDIN(Opts.InputFileName); |
