Path: blob/main/contrib/llvm-project/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp
35231 views
//===-- llvm-dwarfdump.cpp - Debug info dumping utility for llvm ----------===//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//===----------------------------------------------------------------------===//7//8// This program is a utility that works like "dwarfdump".9//10//===----------------------------------------------------------------------===//1112#include "llvm-dwarfdump.h"13#include "llvm/ADT/MapVector.h"14#include "llvm/ADT/STLExtras.h"15#include "llvm/ADT/SmallSet.h"16#include "llvm/ADT/StringSet.h"17#include "llvm/DebugInfo/DIContext.h"18#include "llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h"19#include "llvm/DebugInfo/DWARF/DWARFCompileUnit.h"20#include "llvm/DebugInfo/DWARF/DWARFContext.h"21#include "llvm/MC/MCRegisterInfo.h"22#include "llvm/MC/TargetRegistry.h"23#include "llvm/Object/Archive.h"24#include "llvm/Object/MachOUniversal.h"25#include "llvm/Object/ObjectFile.h"26#include "llvm/Support/CommandLine.h"27#include "llvm/Support/Debug.h"28#include "llvm/Support/Format.h"29#include "llvm/Support/FormatVariadic.h"30#include "llvm/Support/InitLLVM.h"31#include "llvm/Support/MemoryBuffer.h"32#include "llvm/Support/Path.h"33#include "llvm/Support/Regex.h"34#include "llvm/Support/TargetSelect.h"35#include "llvm/Support/ToolOutputFile.h"36#include "llvm/Support/WithColor.h"37#include "llvm/Support/raw_ostream.h"38#include "llvm/TargetParser/Triple.h"39#include <cstdlib>4041using namespace llvm;42using namespace llvm::dwarfdump;43using namespace llvm::object;4445namespace {46/// Parser for options that take an optional offest argument.47/// @{48struct OffsetOption {49uint64_t Val = 0;50bool HasValue = false;51bool IsRequested = false;52};53struct BoolOption : public OffsetOption {};54} // namespace5556namespace llvm {57namespace cl {58template <>59class parser<OffsetOption> final : public basic_parser<OffsetOption> {60public:61parser(Option &O) : basic_parser(O) {}6263/// Return true on error.64bool parse(Option &O, StringRef ArgName, StringRef Arg, OffsetOption &Val) {65if (Arg == "") {66Val.Val = 0;67Val.HasValue = false;68Val.IsRequested = true;69return false;70}71if (Arg.getAsInteger(0, Val.Val))72return O.error("'" + Arg + "' value invalid for integer argument");73Val.HasValue = true;74Val.IsRequested = true;75return false;76}7778enum ValueExpected getValueExpectedFlagDefault() const {79return ValueOptional;80}8182StringRef getValueName() const override { return StringRef("offset"); }8384void printOptionDiff(const Option &O, OffsetOption V, OptVal Default,85size_t GlobalWidth) const {86printOptionName(O, GlobalWidth);87outs() << "[=offset]";88}89};9091template <> class parser<BoolOption> final : public basic_parser<BoolOption> {92public:93parser(Option &O) : basic_parser(O) {}9495/// Return true on error.96bool parse(Option &O, StringRef ArgName, StringRef Arg, BoolOption &Val) {97if (Arg != "")98return O.error("this is a flag and does not take a value");99Val.Val = 0;100Val.HasValue = false;101Val.IsRequested = true;102return false;103}104105enum ValueExpected getValueExpectedFlagDefault() const {106return ValueOptional;107}108109StringRef getValueName() const override { return StringRef(); }110111void printOptionDiff(const Option &O, OffsetOption V, OptVal Default,112size_t GlobalWidth) const {113printOptionName(O, GlobalWidth);114}115};116} // namespace cl117} // namespace llvm118119/// @}120/// Command line options.121/// @{122123namespace {124using namespace cl;125126enum ErrorDetailLevel {127OnlyDetailsNoSummary,128NoDetailsOnlySummary,129NoDetailsOrSummary,130BothDetailsAndSummary,131Unspecified132};133134OptionCategory DwarfDumpCategory("Specific Options");135static list<std::string>136InputFilenames(Positional, desc("<input object files or .dSYM bundles>"),137cat(DwarfDumpCategory));138139cl::OptionCategory SectionCategory("Section-specific Dump Options",140"These control which sections are dumped. "141"Where applicable these parameters take an "142"optional =<offset> argument to dump only "143"the entry at the specified offset.");144145static opt<bool> DumpAll("all", desc("Dump all debug info sections"),146cat(SectionCategory));147static alias DumpAllAlias("a", desc("Alias for --all"), aliasopt(DumpAll),148cl::NotHidden);149150// Options for dumping specific sections.151static unsigned DumpType = DIDT_Null;152static std::array<std::optional<uint64_t>, (unsigned)DIDT_ID_Count> DumpOffsets;153#define HANDLE_DWARF_SECTION(ENUM_NAME, ELF_NAME, CMDLINE_NAME, OPTION) \154static opt<OPTION> Dump##ENUM_NAME(CMDLINE_NAME, \155desc("Dump the " ELF_NAME " section"), \156cat(SectionCategory));157#include "llvm/BinaryFormat/Dwarf.def"158#undef HANDLE_DWARF_SECTION159160// The aliased DumpDebugFrame is created by the Dwarf.def x-macro just above.161static alias DumpDebugFrameAlias("eh-frame", desc("Alias for --debug-frame"),162NotHidden, cat(SectionCategory),163aliasopt(DumpDebugFrame));164static list<std::string>165ArchFilters("arch",166desc("Dump debug information for the specified CPU "167"architecture only. Architectures may be specified by "168"name or by number. This option can be specified "169"multiple times, once for each desired architecture."),170cat(DwarfDumpCategory));171static opt<bool>172Diff("diff",173desc("Emit diff-friendly output by omitting offsets and addresses."),174cat(DwarfDumpCategory));175static list<std::string>176Find("find",177desc("Search for the exact match for <name> in the accelerator tables "178"and print the matching debug information entries. When no "179"accelerator tables are available, the slower but more complete "180"-name option can be used instead."),181value_desc("name"), cat(DwarfDumpCategory));182static alias FindAlias("f", desc("Alias for --find."), aliasopt(Find),183cl::NotHidden);184static opt<bool> FindAllApple(185"find-all-apple",186desc("Print every debug information entry in the accelerator tables."),187cat(DwarfDumpCategory));188static opt<bool> IgnoreCase("ignore-case",189desc("Ignore case distinctions when using --name."),190value_desc("i"), cat(DwarfDumpCategory));191static opt<bool> DumpNonSkeleton(192"dwo",193desc("Dump the non skeleton DIE in the .dwo or .dwp file after dumping the "194"skeleton DIE from the main executable. This allows dumping the .dwo "195"files with resolved addresses."),196value_desc("d"), cat(DwarfDumpCategory));197198static alias IgnoreCaseAlias("i", desc("Alias for --ignore-case."),199aliasopt(IgnoreCase), cl::NotHidden);200static list<std::string> Name(201"name",202desc("Find and print all debug info entries whose name (DW_AT_name "203"attribute) matches the exact text in <pattern>. When used with the "204"the -regex option <pattern> is interpreted as a regular expression."),205value_desc("pattern"), cat(DwarfDumpCategory));206static alias NameAlias("n", desc("Alias for --name"), aliasopt(Name),207cl::NotHidden);208static opt<uint64_t>209Lookup("lookup",210desc("Lookup <address> in the debug information and print out any "211"available file, function, block and line table details."),212value_desc("address"), cat(DwarfDumpCategory));213static opt<std::string>214OutputFilename("o", cl::init("-"),215cl::desc("Redirect output to the specified file."),216cl::value_desc("filename"), cat(DwarfDumpCategory));217static alias OutputFilenameAlias("out-file", desc("Alias for -o."),218aliasopt(OutputFilename));219static opt<bool> UseRegex(220"regex",221desc("Treat any <pattern> strings as regular "222"expressions when searching with --name. If --ignore-case is also "223"specified, the regular expression becomes case-insensitive."),224cat(DwarfDumpCategory));225static alias RegexAlias("x", desc("Alias for --regex"), aliasopt(UseRegex),226cl::NotHidden);227static opt<bool>228ShowChildren("show-children",229desc("Show a debug info entry's children when selectively "230"printing entries."),231cat(DwarfDumpCategory));232static alias ShowChildrenAlias("c", desc("Alias for --show-children."),233aliasopt(ShowChildren), cl::NotHidden);234static opt<bool>235ShowParents("show-parents",236desc("Show a debug info entry's parents when selectively "237"printing entries."),238cat(DwarfDumpCategory));239static alias ShowParentsAlias("p", desc("Alias for --show-parents."),240aliasopt(ShowParents), cl::NotHidden);241static opt<bool>242ShowForm("show-form",243desc("Show DWARF form types after the DWARF attribute types."),244cat(DwarfDumpCategory));245static alias ShowFormAlias("F", desc("Alias for --show-form."),246aliasopt(ShowForm), cat(DwarfDumpCategory),247cl::NotHidden);248static opt<unsigned>249ChildRecurseDepth("recurse-depth",250desc("Only recurse to a depth of N when displaying "251"children of debug info entries."),252cat(DwarfDumpCategory), init(-1U), value_desc("N"));253static alias ChildRecurseDepthAlias("r", desc("Alias for --recurse-depth."),254aliasopt(ChildRecurseDepth), cl::NotHidden);255static opt<unsigned>256ParentRecurseDepth("parent-recurse-depth",257desc("Only recurse to a depth of N when displaying "258"parents of debug info entries."),259cat(DwarfDumpCategory), init(-1U), value_desc("N"));260static opt<bool>261SummarizeTypes("summarize-types",262desc("Abbreviate the description of type unit entries."),263cat(DwarfDumpCategory));264static cl::opt<bool>265Statistics("statistics",266cl::desc("Emit JSON-formatted debug info quality metrics."),267cat(DwarfDumpCategory));268static cl::opt<bool>269ShowSectionSizes("show-section-sizes",270cl::desc("Show the sizes of all debug sections, "271"expressed in bytes."),272cat(DwarfDumpCategory));273static cl::opt<bool> ManuallyGenerateUnitIndex(274"manaully-generate-unit-index",275cl::desc("if the input is dwp file, parse .debug_info "276"section and use it to populate "277"DW_SECT_INFO contributions in cu-index. "278"For DWARF5 it also populated TU Index."),279cl::init(false), cl::Hidden, cl::cat(DwarfDumpCategory));280static cl::opt<bool>281ShowSources("show-sources",282cl::desc("Show the sources across all compilation units."),283cat(DwarfDumpCategory));284static opt<bool> Verify("verify", desc("Verify the DWARF debug info."),285cat(DwarfDumpCategory));286static opt<ErrorDetailLevel> ErrorDetails(287"error-display", init(Unspecified),288desc("Set the level of detail and summary to display when verifying "289"(implies --verify)"),290values(clEnumValN(NoDetailsOrSummary, "quiet",291"Only display whether errors occurred."),292clEnumValN(NoDetailsOnlySummary, "summary",293"Display only a summary of the errors found."),294clEnumValN(OnlyDetailsNoSummary, "details",295"Display each error in detail but no summary."),296clEnumValN(BothDetailsAndSummary, "full",297"Display each error as well as a summary. [default]")),298cat(DwarfDumpCategory));299static opt<std::string> JsonErrSummaryFile(300"verify-json", init(""),301desc("Output JSON-formatted error summary to the specified file. "302"(Implies --verify)"),303value_desc("filename.json"), cat(DwarfDumpCategory));304static opt<bool> Quiet("quiet", desc("Use with -verify to not emit to STDOUT."),305cat(DwarfDumpCategory));306static opt<bool> DumpUUID("uuid", desc("Show the UUID for each architecture."),307cat(DwarfDumpCategory));308static alias DumpUUIDAlias("u", desc("Alias for --uuid."), aliasopt(DumpUUID),309cl::NotHidden);310static opt<bool> Verbose("verbose",311desc("Print more low-level encoding details."),312cat(DwarfDumpCategory));313static alias VerboseAlias("v", desc("Alias for --verbose."), aliasopt(Verbose),314cat(DwarfDumpCategory), cl::NotHidden);315static cl::extrahelp316HelpResponse("\nPass @FILE as argument to read options from FILE.\n");317} // namespace318/// @}319//===----------------------------------------------------------------------===//320321static void error(Error Err) {322if (!Err)323return;324WithColor::error() << toString(std::move(Err)) << "\n";325exit(1);326}327328static void error(StringRef Prefix, Error Err) {329if (!Err)330return;331WithColor::error() << Prefix << ": " << toString(std::move(Err)) << "\n";332exit(1);333}334335static void error(StringRef Prefix, std::error_code EC) {336error(Prefix, errorCodeToError(EC));337}338339static DIDumpOptions getDumpOpts(DWARFContext &C) {340DIDumpOptions DumpOpts;341DumpOpts.DumpType = DumpType;342DumpOpts.ChildRecurseDepth = ChildRecurseDepth;343DumpOpts.ParentRecurseDepth = ParentRecurseDepth;344DumpOpts.ShowAddresses = !Diff;345DumpOpts.ShowChildren = ShowChildren;346DumpOpts.ShowParents = ShowParents;347DumpOpts.ShowForm = ShowForm;348DumpOpts.SummarizeTypes = SummarizeTypes;349DumpOpts.Verbose = Verbose;350DumpOpts.DumpNonSkeleton = DumpNonSkeleton;351DumpOpts.RecoverableErrorHandler = C.getRecoverableErrorHandler();352// In -verify mode, print DIEs without children in error messages.353if (Verify) {354DumpOpts.Verbose = ErrorDetails != NoDetailsOnlySummary &&355ErrorDetails != NoDetailsOrSummary;356DumpOpts.ShowAggregateErrors = ErrorDetails != OnlyDetailsNoSummary &&357ErrorDetails != NoDetailsOnlySummary;358DumpOpts.JsonErrSummaryFile = JsonErrSummaryFile;359return DumpOpts.noImplicitRecursion();360}361return DumpOpts;362}363364static uint32_t getCPUType(MachOObjectFile &MachO) {365if (MachO.is64Bit())366return MachO.getHeader64().cputype;367else368return MachO.getHeader().cputype;369}370371/// Return true if the object file has not been filtered by an --arch option.372static bool filterArch(ObjectFile &Obj) {373if (ArchFilters.empty())374return true;375376if (auto *MachO = dyn_cast<MachOObjectFile>(&Obj)) {377for (const StringRef Arch : ArchFilters) {378// Match architecture number.379unsigned Value;380if (!Arch.getAsInteger(0, Value))381if (Value == getCPUType(*MachO))382return true;383384// Match as name.385if (MachO->getArchTriple().getArchName() == Triple(Arch).getArchName())386return true;387}388}389return false;390}391392using HandlerFn = std::function<bool(ObjectFile &, DWARFContext &DICtx,393const Twine &, raw_ostream &)>;394395/// Print only DIEs that have a certain name.396static bool filterByName(397const StringSet<> &Names, DWARFDie Die, StringRef NameRef, raw_ostream &OS,398std::function<StringRef(uint64_t RegNum, bool IsEH)> GetNameForDWARFReg) {399DIDumpOptions DumpOpts = getDumpOpts(Die.getDwarfUnit()->getContext());400DumpOpts.GetNameForDWARFReg = GetNameForDWARFReg;401std::string Name =402(IgnoreCase && !UseRegex) ? NameRef.lower() : NameRef.str();403if (UseRegex) {404// Match regular expression.405for (auto Pattern : Names.keys()) {406Regex RE(Pattern, IgnoreCase ? Regex::IgnoreCase : Regex::NoFlags);407std::string Error;408if (!RE.isValid(Error)) {409errs() << "error in regular expression: " << Error << "\n";410exit(1);411}412if (RE.match(Name)) {413Die.dump(OS, 0, DumpOpts);414return true;415}416}417} else if (Names.count(Name)) {418// Match full text.419Die.dump(OS, 0, DumpOpts);420return true;421}422return false;423}424425/// Print only DIEs that have a certain name.426static void filterByName(427const StringSet<> &Names, DWARFContext::unit_iterator_range CUs,428raw_ostream &OS,429std::function<StringRef(uint64_t RegNum, bool IsEH)> GetNameForDWARFReg) {430auto filterDieNames = [&](DWARFUnit *Unit) {431for (const auto &Entry : Unit->dies()) {432DWARFDie Die = {Unit, &Entry};433if (const char *Name = Die.getName(DINameKind::ShortName))434if (filterByName(Names, Die, Name, OS, GetNameForDWARFReg))435continue;436if (const char *Name = Die.getName(DINameKind::LinkageName))437filterByName(Names, Die, Name, OS, GetNameForDWARFReg);438}439};440for (const auto &CU : CUs) {441filterDieNames(CU.get());442if (DumpNonSkeleton) {443// If we have split DWARF, then recurse down into the .dwo files as well.444DWARFDie CUDie = CU->getUnitDIE(false);445DWARFDie CUNonSkeletonDie = CU->getNonSkeletonUnitDIE(false);446// If we have a DWO file, we need to search it as well447if (CUNonSkeletonDie && CUDie != CUNonSkeletonDie)448filterDieNames(CUNonSkeletonDie.getDwarfUnit());449}450}451}452453static void getDies(DWARFContext &DICtx, const AppleAcceleratorTable &Accel,454StringRef Name, SmallVectorImpl<DWARFDie> &Dies) {455for (const auto &Entry : Accel.equal_range(Name)) {456if (std::optional<uint64_t> Off = Entry.getDIESectionOffset()) {457if (DWARFDie Die = DICtx.getDIEForOffset(*Off))458Dies.push_back(Die);459}460}461}462463static DWARFDie toDie(const DWARFDebugNames::Entry &Entry,464DWARFContext &DICtx) {465std::optional<uint64_t> CUOff = Entry.getCUOffset();466std::optional<uint64_t> Off = Entry.getDIEUnitOffset();467if (!CUOff || !Off)468return DWARFDie();469470DWARFCompileUnit *CU = DICtx.getCompileUnitForOffset(*CUOff);471if (!CU)472return DWARFDie();473474if (std::optional<uint64_t> DWOId = CU->getDWOId()) {475// This is a skeleton unit. Look up the DIE in the DWO unit.476CU = DICtx.getDWOCompileUnitForHash(*DWOId);477if (!CU)478return DWARFDie();479}480481return CU->getDIEForOffset(CU->getOffset() + *Off);482}483484static void getDies(DWARFContext &DICtx, const DWARFDebugNames &Accel,485StringRef Name, SmallVectorImpl<DWARFDie> &Dies) {486for (const auto &Entry : Accel.equal_range(Name)) {487if (DWARFDie Die = toDie(Entry, DICtx))488Dies.push_back(Die);489}490}491492/// Print only DIEs that have a certain name.493static void filterByAccelName(494ArrayRef<std::string> Names, DWARFContext &DICtx, raw_ostream &OS,495std::function<StringRef(uint64_t RegNum, bool IsEH)> GetNameForDWARFReg) {496SmallVector<DWARFDie, 4> Dies;497for (const auto &Name : Names) {498getDies(DICtx, DICtx.getAppleNames(), Name, Dies);499getDies(DICtx, DICtx.getAppleTypes(), Name, Dies);500getDies(DICtx, DICtx.getAppleNamespaces(), Name, Dies);501getDies(DICtx, DICtx.getDebugNames(), Name, Dies);502}503llvm::sort(Dies);504Dies.erase(llvm::unique(Dies), Dies.end());505506DIDumpOptions DumpOpts = getDumpOpts(DICtx);507DumpOpts.GetNameForDWARFReg = GetNameForDWARFReg;508for (DWARFDie Die : Dies)509Die.dump(OS, 0, DumpOpts);510}511512/// Print all DIEs in apple accelerator tables513static void findAllApple(514DWARFContext &DICtx, raw_ostream &OS,515std::function<StringRef(uint64_t RegNum, bool IsEH)> GetNameForDWARFReg) {516MapVector<StringRef, llvm::SmallSet<DWARFDie, 2>> NameToDies;517518auto PushDIEs = [&](const AppleAcceleratorTable &Accel) {519for (const auto &Entry : Accel.entries()) {520if (std::optional<uint64_t> Off = Entry.BaseEntry.getDIESectionOffset()) {521std::optional<StringRef> MaybeName = Entry.readName();522DWARFDie Die = DICtx.getDIEForOffset(*Off);523if (Die && MaybeName)524NameToDies[*MaybeName].insert(Die);525}526}527};528529PushDIEs(DICtx.getAppleNames());530PushDIEs(DICtx.getAppleNamespaces());531PushDIEs(DICtx.getAppleTypes());532533DIDumpOptions DumpOpts = getDumpOpts(DICtx);534DumpOpts.GetNameForDWARFReg = GetNameForDWARFReg;535for (const auto &[Name, Dies] : NameToDies) {536OS << llvm::formatv("\nApple accelerator entries with name = \"{0}\":\n",537Name);538for (DWARFDie Die : Dies)539Die.dump(OS, 0, DumpOpts);540}541}542543/// Handle the --lookup option and dump the DIEs and line info for the given544/// address.545/// TODO: specified Address for --lookup option could relate for several546/// different sections(in case not-linked object file). llvm-dwarfdump547/// need to do something with this: extend lookup option with section548/// information or probably display all matched entries, or something else...549static bool lookup(ObjectFile &Obj, DWARFContext &DICtx, uint64_t Address,550raw_ostream &OS) {551auto DIEsForAddr = DICtx.getDIEsForAddress(Lookup, DumpNonSkeleton);552553if (!DIEsForAddr)554return false;555556DIDumpOptions DumpOpts = getDumpOpts(DICtx);557DumpOpts.ChildRecurseDepth = 0;558DIEsForAddr.CompileUnit->dump(OS, DumpOpts);559if (DIEsForAddr.FunctionDIE) {560DIEsForAddr.FunctionDIE.dump(OS, 2, DumpOpts);561if (DIEsForAddr.BlockDIE)562DIEsForAddr.BlockDIE.dump(OS, 4, DumpOpts);563}564565// TODO: it is neccessary to set proper SectionIndex here.566// object::SectionedAddress::UndefSection works for only absolute addresses.567if (DILineInfo LineInfo = DICtx.getLineInfoForAddress(568{Lookup, object::SectionedAddress::UndefSection}))569LineInfo.dump(OS);570571return true;572}573574// Collect all sources referenced from the given line table, scoped to the given575// CU compilation directory.576static bool collectLineTableSources(const DWARFDebugLine::LineTable <,577StringRef CompDir,578std::vector<std::string> &Sources) {579bool Result = true;580std::optional<uint64_t> LastIndex = LT.getLastValidFileIndex();581for (uint64_t I = LT.hasFileAtIndex(0) ? 0 : 1,582E = LastIndex ? *LastIndex + 1 : 0;583I < E; ++I) {584std::string Path;585Result &= LT.getFileNameByIndex(586I, CompDir, DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath,587Path);588Sources.push_back(std::move(Path));589}590return Result;591}592593static bool collectObjectSources(ObjectFile &Obj, DWARFContext &DICtx,594const Twine &Filename, raw_ostream &OS) {595bool Result = true;596std::vector<std::string> Sources;597598bool HasCompileUnits = false;599for (const auto &CU : DICtx.compile_units()) {600HasCompileUnits = true;601// Extract paths from the line table for this CU. This allows combining the602// compilation directory with the line information, in case both the include603// directory and file names in the line table are relative.604const DWARFDebugLine::LineTable *LT = DICtx.getLineTableForUnit(CU.get());605StringRef CompDir = CU->getCompilationDir();606if (LT) {607Result &= collectLineTableSources(*LT, CompDir, Sources);608} else {609// Since there's no line table for this CU, collect the name from the CU610// itself.611const char *Name = CU->getUnitDIE().getShortName();612if (!Name) {613WithColor::warning()614<< Filename << ": missing name for compilation unit\n";615continue;616}617SmallString<64> AbsName;618if (sys::path::is_relative(Name, sys::path::Style::posix) &&619sys::path::is_relative(Name, sys::path::Style::windows))620AbsName = CompDir;621sys::path::append(AbsName, Name);622Sources.push_back(std::string(AbsName));623}624}625626if (!HasCompileUnits) {627// Since there's no compile units available, walk the line tables and628// extract out any referenced paths.629DWARFDataExtractor LineData(DICtx.getDWARFObj(),630DICtx.getDWARFObj().getLineSection(),631DICtx.isLittleEndian(), 0);632DWARFDebugLine::SectionParser Parser(LineData, DICtx, DICtx.normal_units());633while (!Parser.done()) {634const auto RecoverableErrorHandler = [&](Error Err) {635Result = false;636WithColor::defaultErrorHandler(std::move(Err));637};638void (*UnrecoverableErrorHandler)(Error Err) = error;639640DWARFDebugLine::LineTable LT =641Parser.parseNext(RecoverableErrorHandler, UnrecoverableErrorHandler);642Result &= collectLineTableSources(LT, /*CompDir=*/"", Sources);643}644}645646// Dedup and order the sources.647llvm::sort(Sources);648Sources.erase(llvm::unique(Sources), Sources.end());649650for (StringRef Name : Sources)651OS << Name << "\n";652return Result;653}654655static std::unique_ptr<MCRegisterInfo>656createRegInfo(const object::ObjectFile &Obj) {657std::unique_ptr<MCRegisterInfo> MCRegInfo;658Triple TT;659TT.setArch(Triple::ArchType(Obj.getArch()));660TT.setVendor(Triple::UnknownVendor);661TT.setOS(Triple::UnknownOS);662std::string TargetLookupError;663const Target *TheTarget =664TargetRegistry::lookupTarget(TT.str(), TargetLookupError);665if (!TargetLookupError.empty())666return nullptr;667MCRegInfo.reset(TheTarget->createMCRegInfo(TT.str()));668return MCRegInfo;669}670671static bool dumpObjectFile(ObjectFile &Obj, DWARFContext &DICtx,672const Twine &Filename, raw_ostream &OS) {673674auto MCRegInfo = createRegInfo(Obj);675if (!MCRegInfo)676logAllUnhandledErrors(createStringError(inconvertibleErrorCode(),677"Error in creating MCRegInfo"),678errs(), Filename.str() + ": ");679680auto GetRegName = [&MCRegInfo](uint64_t DwarfRegNum, bool IsEH) -> StringRef {681if (!MCRegInfo)682return {};683if (std::optional<unsigned> LLVMRegNum =684MCRegInfo->getLLVMRegNum(DwarfRegNum, IsEH))685if (const char *RegName = MCRegInfo->getName(*LLVMRegNum))686return StringRef(RegName);687return {};688};689690// The UUID dump already contains all the same information.691if (!(DumpType & DIDT_UUID) || DumpType == DIDT_All)692OS << Filename << ":\tfile format " << Obj.getFileFormatName() << '\n';693694// Handle the --lookup option.695if (Lookup)696return lookup(Obj, DICtx, Lookup, OS);697698// Handle the --name option.699if (!Name.empty()) {700StringSet<> Names;701for (const auto &name : Name)702Names.insert((IgnoreCase && !UseRegex) ? StringRef(name).lower() : name);703704filterByName(Names, DICtx.normal_units(), OS, GetRegName);705filterByName(Names, DICtx.dwo_units(), OS, GetRegName);706return true;707}708709// Handle the --find option and lower it to --debug-info=<offset>.710if (!Find.empty()) {711filterByAccelName(Find, DICtx, OS, GetRegName);712return true;713}714715// Handle the --find-all-apple option and lower it to --debug-info=<offset>.716if (FindAllApple) {717findAllApple(DICtx, OS, GetRegName);718return true;719}720721// Dump the complete DWARF structure.722auto DumpOpts = getDumpOpts(DICtx);723DumpOpts.GetNameForDWARFReg = GetRegName;724DICtx.dump(OS, DumpOpts, DumpOffsets);725return true;726}727728static bool verifyObjectFile(ObjectFile &Obj, DWARFContext &DICtx,729const Twine &Filename, raw_ostream &OS) {730// Verify the DWARF and exit with non-zero exit status if verification731// fails.732raw_ostream &stream = Quiet ? nulls() : OS;733stream << "Verifying " << Filename.str() << ":\tfile format "734<< Obj.getFileFormatName() << "\n";735bool Result = DICtx.verify(stream, getDumpOpts(DICtx));736if (Result)737stream << "No errors.\n";738else739stream << "Errors detected.\n";740return Result;741}742743static bool handleBuffer(StringRef Filename, MemoryBufferRef Buffer,744HandlerFn HandleObj, raw_ostream &OS);745746static bool handleArchive(StringRef Filename, Archive &Arch,747HandlerFn HandleObj, raw_ostream &OS) {748bool Result = true;749Error Err = Error::success();750for (const auto &Child : Arch.children(Err)) {751auto BuffOrErr = Child.getMemoryBufferRef();752error(Filename, BuffOrErr.takeError());753auto NameOrErr = Child.getName();754error(Filename, NameOrErr.takeError());755std::string Name = (Filename + "(" + NameOrErr.get() + ")").str();756Result &= handleBuffer(Name, BuffOrErr.get(), HandleObj, OS);757}758error(Filename, std::move(Err));759760return Result;761}762763static bool handleBuffer(StringRef Filename, MemoryBufferRef Buffer,764HandlerFn HandleObj, raw_ostream &OS) {765Expected<std::unique_ptr<Binary>> BinOrErr = object::createBinary(Buffer);766error(Filename, BinOrErr.takeError());767768bool Result = true;769auto RecoverableErrorHandler = [&](Error E) {770Result = false;771WithColor::defaultErrorHandler(std::move(E));772};773if (auto *Obj = dyn_cast<ObjectFile>(BinOrErr->get())) {774if (filterArch(*Obj)) {775std::unique_ptr<DWARFContext> DICtx = DWARFContext::create(776*Obj, DWARFContext::ProcessDebugRelocations::Process, nullptr, "",777RecoverableErrorHandler);778DICtx->setParseCUTUIndexManually(ManuallyGenerateUnitIndex);779if (!HandleObj(*Obj, *DICtx, Filename, OS))780Result = false;781}782} else if (auto *Fat = dyn_cast<MachOUniversalBinary>(BinOrErr->get()))783for (auto &ObjForArch : Fat->objects()) {784std::string ObjName =785(Filename + "(" + ObjForArch.getArchFlagName() + ")").str();786if (auto MachOOrErr = ObjForArch.getAsObjectFile()) {787auto &Obj = **MachOOrErr;788if (filterArch(Obj)) {789std::unique_ptr<DWARFContext> DICtx = DWARFContext::create(790Obj, DWARFContext::ProcessDebugRelocations::Process, nullptr, "",791RecoverableErrorHandler);792if (!HandleObj(Obj, *DICtx, ObjName, OS))793Result = false;794}795continue;796} else797consumeError(MachOOrErr.takeError());798if (auto ArchiveOrErr = ObjForArch.getAsArchive()) {799error(ObjName, ArchiveOrErr.takeError());800if (!handleArchive(ObjName, *ArchiveOrErr.get(), HandleObj, OS))801Result = false;802continue;803} else804consumeError(ArchiveOrErr.takeError());805}806else if (auto *Arch = dyn_cast<Archive>(BinOrErr->get()))807Result = handleArchive(Filename, *Arch, HandleObj, OS);808return Result;809}810811static bool handleFile(StringRef Filename, HandlerFn HandleObj,812raw_ostream &OS) {813ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr =814MemoryBuffer::getFileOrSTDIN(Filename);815error(Filename, BuffOrErr.getError());816std::unique_ptr<MemoryBuffer> Buffer = std::move(BuffOrErr.get());817return handleBuffer(Filename, *Buffer, HandleObj, OS);818}819820int main(int argc, char **argv) {821InitLLVM X(argc, argv);822823// Flush outs() when printing to errs(). This avoids interleaving output824// between the two.825errs().tie(&outs());826827llvm::InitializeAllTargetInfos();828llvm::InitializeAllTargetMCs();829830HideUnrelatedOptions(831{&DwarfDumpCategory, &SectionCategory, &getColorCategory()});832cl::ParseCommandLineOptions(833argc, argv,834"pretty-print DWARF debug information in object files"835" and debug info archives.\n");836837// FIXME: Audit interactions between these two options and make them838// compatible.839if (Diff && Verbose) {840WithColor::error() << "incompatible arguments: specifying both -diff and "841"-verbose is currently not supported";842return 1;843}844// -error-detail and -json-summary-file both imply -verify845if (ErrorDetails != Unspecified || !JsonErrSummaryFile.empty()) {846Verify = true;847}848849std::error_code EC;850ToolOutputFile OutputFile(OutputFilename, EC, sys::fs::OF_TextWithCRLF);851error("unable to open output file " + OutputFilename, EC);852// Don't remove output file if we exit with an error.853OutputFile.keep();854855bool OffsetRequested = false;856857// Defaults to dumping only debug_info, unless: A) verbose mode is specified,858// in which case all sections are dumped, or B) a specific section is859// requested.860#define HANDLE_DWARF_SECTION(ENUM_NAME, ELF_NAME, CMDLINE_NAME, OPTION) \861if (Dump##ENUM_NAME.IsRequested) { \862DumpType |= DIDT_##ENUM_NAME; \863if (Dump##ENUM_NAME.HasValue) { \864DumpOffsets[DIDT_ID_##ENUM_NAME] = Dump##ENUM_NAME.Val; \865OffsetRequested = true; \866} \867}868#include "llvm/BinaryFormat/Dwarf.def"869#undef HANDLE_DWARF_SECTION870if (DumpUUID)871DumpType |= DIDT_UUID;872if (DumpAll)873DumpType = DIDT_All;874if (DumpType == DIDT_Null) {875if (Verbose || Verify)876DumpType = DIDT_All;877else878DumpType = DIDT_DebugInfo;879}880881// Unless dumping a specific DIE, default to --show-children.882if (!ShowChildren && !Verify && !OffsetRequested && Name.empty() &&883Find.empty() && !FindAllApple)884ShowChildren = true;885886// Defaults to a.out if no filenames specified.887if (InputFilenames.empty())888InputFilenames.push_back("a.out");889890// Expand any .dSYM bundles to the individual object files contained therein.891std::vector<std::string> Objects;892for (const auto &F : InputFilenames) {893if (auto DsymObjectsOrErr = MachOObjectFile::findDsymObjectMembers(F)) {894if (DsymObjectsOrErr->empty())895Objects.push_back(F);896else897llvm::append_range(Objects, *DsymObjectsOrErr);898} else {899error(DsymObjectsOrErr.takeError());900}901}902903bool Success = true;904if (Verify) {905for (StringRef Object : Objects)906Success &= handleFile(Object, verifyObjectFile, OutputFile.os());907} else if (Statistics) {908for (StringRef Object : Objects)909Success &= handleFile(Object, collectStatsForObjectFile, OutputFile.os());910} else if (ShowSectionSizes) {911for (StringRef Object : Objects)912Success &= handleFile(Object, collectObjectSectionSizes, OutputFile.os());913} else if (ShowSources) {914for (StringRef Object : Objects)915Success &= handleFile(Object, collectObjectSources, OutputFile.os());916} else {917for (StringRef Object : Objects)918Success &= handleFile(Object, dumpObjectFile, OutputFile.os());919}920921return Success ? EXIT_SUCCESS : EXIT_FAILURE;922}923924925