diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2022-07-24 15:03:44 +0000 |
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2022-07-24 15:03:44 +0000 |
| commit | 4b4fe385e49bd883fd183b5f21c1ea486c722e61 (patch) | |
| tree | c3d8fdb355c9c73e57723718c22103aaf7d15aa6 /llvm/tools | |
| parent | 1f917f69ff07f09b6dbb670971f57f8efe718b84 (diff) | |
Vendor import of llvm-project main llvmorg-15-init-17485-ga3e38b4a206b.vendor/llvm-project/llvmorg-15-init-17485-ga3e38b4a206b
Diffstat (limited to 'llvm/tools')
| -rw-r--r-- | llvm/tools/llc/llc.cpp | 2 | ||||
| -rw-r--r-- | llvm/tools/llvm-ar/llvm-ar.cpp | 162 | ||||
| -rw-r--r-- | llvm/tools/llvm-cov/CodeCoverage.cpp | 17 | ||||
| -rw-r--r-- | llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp | 2 | ||||
| -rw-r--r-- | llvm/tools/llvm-dwarfutil/DebugInfoLinker.cpp | 277 | ||||
| -rw-r--r-- | llvm/tools/llvm-dwarfutil/DebugInfoLinker.h | 31 | ||||
| -rw-r--r-- | llvm/tools/llvm-dwarfutil/Error.h | 44 | ||||
| -rw-r--r-- | llvm/tools/llvm-dwarfutil/Options.h | 46 | ||||
| -rw-r--r-- | llvm/tools/llvm-dwarfutil/Options.td | 65 | ||||
| -rw-r--r-- | llvm/tools/llvm-dwarfutil/llvm-dwarfutil.cpp | 527 | ||||
| -rw-r--r-- | llvm/tools/llvm-mc/llvm-mc.cpp | 4 | ||||
| -rw-r--r-- | llvm/tools/llvm-objdump/llvm-objdump.cpp | 62 | ||||
| -rw-r--r-- | llvm/tools/llvm-objdump/llvm-objdump.h | 2 | ||||
| -rw-r--r-- | llvm/tools/llvm-profdata/llvm-profdata.cpp | 7 | ||||
| -rw-r--r-- | llvm/tools/llvm-size/llvm-size.cpp | 7 | ||||
| -rw-r--r-- | llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp | 15 | ||||
| -rw-r--r-- | llvm/tools/opt/opt.cpp | 28 |
17 files changed, 1219 insertions, 79 deletions
diff --git a/llvm/tools/llc/llc.cpp b/llvm/tools/llc/llc.cpp index 853a0bd8eb54..f084ee2daa93 100644 --- a/llvm/tools/llc/llc.cpp +++ b/llvm/tools/llc/llc.cpp @@ -359,8 +359,6 @@ int main(int argc, char **argv) { initializeCodeGen(*Registry); initializeLoopStrengthReducePass(*Registry); initializeLowerIntrinsicsPass(*Registry); - initializeEntryExitInstrumenterPass(*Registry); - initializePostInlineEntryExitInstrumenterPass(*Registry); initializeUnreachableBlockElimLegacyPassPass(*Registry); initializeConstantHoistingLegacyPassPass(*Registry); initializeScalarOpts(*Registry); diff --git a/llvm/tools/llvm-ar/llvm-ar.cpp b/llvm/tools/llvm-ar/llvm-ar.cpp index e964dc8256a5..1d4a8e9cd398 100644 --- a/llvm/tools/llvm-ar/llvm-ar.cpp +++ b/llvm/tools/llvm-ar/llvm-ar.cpp @@ -18,10 +18,14 @@ #include "llvm/IR/LLVMContext.h" #include "llvm/Object/Archive.h" #include "llvm/Object/ArchiveWriter.h" +#include "llvm/Object/COFFImportFile.h" +#include "llvm/Object/ELFObjectFile.h" #include "llvm/Object/IRObjectFile.h" #include "llvm/Object/MachO.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Object/SymbolicFile.h" +#include "llvm/Object/TapiFile.h" +#include "llvm/Object/Wasm.h" #include "llvm/Object/XCOFFObjectFile.h" #include "llvm/Support/Chrono.h" #include "llvm/Support/CommandLine.h" @@ -55,6 +59,7 @@ #endif using namespace llvm; +using namespace llvm::object; // The name this program was invoked as. static StringRef ToolName; @@ -82,7 +87,7 @@ static void printArHelp(StringRef ToolName) { =gnu - gnu =darwin - darwin =bsd - bsd - =aix - aix (big archive) + =bigarchive - big archive (AIX OS) --plugin=<string> - ignored for compatibility -h --help - display this help and exit --output - the directory to extract archive members to @@ -91,6 +96,7 @@ static void printArHelp(StringRef ToolName) { =windows - windows --thin - create a thin archive --version - print the version and exit + -X{32|64|32_64|any} - object mode (only for AIX OS) @<file> - read options from <file> OPERATIONS: @@ -184,6 +190,10 @@ static void failIfError(Error E, Twine Context = "") { }); } +static void warn(Twine Message) { + WithColor::warning(errs(), ToolName) << Message << "\n"; +} + static SmallVector<const char *, 256> PositionalArgs; static bool MRI; @@ -209,6 +219,10 @@ enum ArchiveOperation { CreateSymTab ///< Create a symbol table in an existing archive }; +enum class BitModeTy { Bit32, Bit64, Bit32_64, Any, Unknown }; + +static BitModeTy BitMode = BitModeTy::Bit32; + // Modifiers to follow operation to vary behavior static bool AddAfter = false; ///< 'a' modifier static bool AddBefore = false; ///< 'b' modifier @@ -632,6 +646,71 @@ static bool shouldCreateArchive(ArchiveOperation Op) { llvm_unreachable("Missing entry in covered switch."); } +static bool is64BitSymbolicFile(SymbolicFile &Obj) { + if (auto *IRObj = dyn_cast<IRObjectFile>(&Obj)) + return Triple(IRObj->getTargetTriple()).isArch64Bit(); + if (isa<COFFObjectFile>(Obj) || isa<COFFImportFile>(Obj)) + return false; + if (XCOFFObjectFile *XCOFFObj = dyn_cast<XCOFFObjectFile>(&Obj)) + return XCOFFObj->is64Bit(); + if (isa<WasmObjectFile>(Obj)) + return false; + if (TapiFile *Tapi = dyn_cast<TapiFile>(&Obj)) + return Tapi->is64Bit(); + if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(&Obj)) + return MachO->is64Bit(); + if (ELFObjectFileBase *ElfO = dyn_cast<ELFObjectFileBase>(&Obj)) + return ElfO->getBytesInAddress() == 8; + + fail("unsupported file format"); +} + +static bool isValidInBitMode(Binary &Bin) { + if (BitMode == BitModeTy::Bit32_64 || BitMode == BitModeTy::Any) + return true; + + if (SymbolicFile *SymFile = dyn_cast<SymbolicFile>(&Bin)) { + bool Is64Bit = is64BitSymbolicFile(*SymFile); + if ((Is64Bit && (BitMode == BitModeTy::Bit32)) || + (!Is64Bit && (BitMode == BitModeTy::Bit64))) + return false; + } + // In AIX "ar", non-object files are always considered to have a valid bit + // mode. + return true; +} + +Expected<std::unique_ptr<Binary>> getAsBinary(const NewArchiveMember &NM, + LLVMContext *Context) { + auto BinaryOrErr = createBinary(NM.Buf->getMemBufferRef(), Context); + if (BinaryOrErr) + return std::move(*BinaryOrErr); + return BinaryOrErr.takeError(); +} + +Expected<std::unique_ptr<Binary>> getAsBinary(const Archive::Child &C, + LLVMContext *Context) { + return C.getAsBinary(Context); +} + +template <class A> static bool isValidInBitMode(const A &Member) { + if (object::Archive::getDefaultKindForHost() != object::Archive::K_AIXBIG) + return true; + LLVMContext Context; + Expected<std::unique_ptr<Binary>> BinOrErr = getAsBinary(Member, &Context); + // In AIX "ar", if there is a non-object file member, it is never ignored due + // to the bit mode setting. + if (!BinOrErr) { + consumeError(BinOrErr.takeError()); + return true; + } + return isValidInBitMode(*BinOrErr.get()); +} + +static void warnInvalidObjectForFileMode(Twine Name) { + warn("'" + Name + "' is not valid with the current object file mode"); +} + static void performReadOperation(ArchiveOperation Operation, object::Archive *OldArchive) { if (Operation == Extract && OldArchive->isThin()) @@ -646,6 +725,10 @@ static void performReadOperation(ArchiveOperation Operation, failIfError(NameOrErr.takeError()); StringRef Name = NameOrErr.get(); + // Check whether to ignore this object due to its bitness. + if (!isValidInBitMode(C)) + continue; + if (Filter) { auto I = find_if(Members, [Name](StringRef Path) { return comparePaths(Name, Path); @@ -722,8 +805,7 @@ static void addChildMember(std::vector<NewArchiveMember> &Members, Members.push_back(std::move(*NMOrErr)); } -static void addMember(std::vector<NewArchiveMember> &Members, - StringRef FileName, bool FlattenArchive = false) { +static NewArchiveMember getArchiveMember(StringRef FileName) { Expected<NewArchiveMember> NMOrErr = NewArchiveMember::getFile(FileName, Deterministic); failIfError(NMOrErr.takeError(), FileName); @@ -743,9 +825,24 @@ static void addMember(std::vector<NewArchiveMember> &Members, PathOrErr ? *PathOrErr : sys::path::convert_to_slash(FileName)); } } + return std::move(*NMOrErr); +} + +static void addMember(std::vector<NewArchiveMember> &Members, + NewArchiveMember &NM) { + Members.push_back(std::move(NM)); +} + +static void addMember(std::vector<NewArchiveMember> &Members, + StringRef FileName, bool FlattenArchive = false) { + NewArchiveMember NM = getArchiveMember(FileName); + if (!isValidInBitMode(NM)) { + warnInvalidObjectForFileMode(FileName); + return; + } if (FlattenArchive && - identify_magic(NMOrErr->Buf->getBuffer()) == file_magic::archive) { + identify_magic(NM.Buf->getBuffer()) == file_magic::archive) { object::Archive &Lib = readLibrary(FileName); // When creating thin archives, only flatten if the member is also thin. if (!Thin || Lib.isThin()) { @@ -757,7 +854,7 @@ static void addMember(std::vector<NewArchiveMember> &Members, return; } } - Members.push_back(std::move(*NMOrErr)); + Members.push_back(std::move(NM)); } enum InsertAction { @@ -773,6 +870,9 @@ static InsertAction computeInsertAction(ArchiveOperation Operation, StringRef Name, std::vector<StringRef>::iterator &Pos, StringMap<int> &MemberCount) { + if (!isValidInBitMode(Member)) + return IA_AddOldMember; + if (Operation == QuickAppend || Members.empty()) return IA_AddOldMember; auto MI = find_if( @@ -834,7 +934,7 @@ computeNewArchiveMembers(ArchiveOperation Operation, Expected<StringRef> NameOrErr = Child.getName(); failIfError(NameOrErr.takeError()); std::string Name = std::string(NameOrErr.get()); - if (comparePaths(Name, RelPos)) { + if (comparePaths(Name, RelPos) && isValidInBitMode(Child)) { assert(AddAfter || AddBefore); if (AddBefore) InsertPos = Pos; @@ -845,12 +945,25 @@ computeNewArchiveMembers(ArchiveOperation Operation, std::vector<StringRef>::iterator MemberI = Members.end(); InsertAction Action = computeInsertAction(Operation, Child, Name, MemberI, MemberCount); + + auto HandleNewMember = [](auto Member, auto &Members, auto &Child) { + NewArchiveMember NM = getArchiveMember(*Member); + if (isValidInBitMode(NM)) + addMember(Members, NM); + else { + // If a new member is not a valid object for the bit mode, add + // the old member back. + warnInvalidObjectForFileMode(*Member); + addChildMember(Members, Child, /*FlattenArchive=*/Thin); + } + }; + switch (Action) { case IA_AddOldMember: addChildMember(Ret, Child, /*FlattenArchive=*/Thin); break; case IA_AddNewMember: - addMember(Ret, *MemberI); + HandleNewMember(MemberI, Ret, Child); break; case IA_Delete: break; @@ -858,7 +971,7 @@ computeNewArchiveMembers(ArchiveOperation Operation, addChildMember(Moved, Child, /*FlattenArchive=*/Thin); break; case IA_MoveNewMember: - addMember(Moved, *MemberI); + HandleNewMember(MemberI, Moved, Child); break; } // When processing elements with the count param, we need to preserve the @@ -1043,8 +1156,7 @@ static int performOperation(ArchiveOperation Operation, } else { if (!Create) { // Produce a warning if we should and we're creating the archive - WithColor::warning(errs(), ToolName) - << "creating " << ArchiveName << "\n"; + warn("creating " + ArchiveName); } } @@ -1155,6 +1267,15 @@ static bool handleGenericOption(StringRef arg) { return false; } +static BitModeTy getBitMode(const char *RawBitMode) { + return StringSwitch<BitModeTy>(RawBitMode) + .Case("32", BitModeTy::Bit32) + .Case("64", BitModeTy::Bit64) + .Case("32_64", BitModeTy::Bit32_64) + .Case("any", BitModeTy::Any) + .Default(BitModeTy::Unknown); +} + static const char *matchFlagWithArg(StringRef Expected, ArrayRef<const char *>::iterator &ArgIt, ArrayRef<const char *> Args) { @@ -1204,6 +1325,14 @@ static int ar_main(int argc, char **argv) { cl::ExpandResponseFiles(Saver, getRspQuoting(makeArrayRef(argv, argc)), Argv); + // Get BitMode from enviorment variable "OBJECT_MODE" for AIX OS, if + // specified. + if (object::Archive::getDefaultKindForHost() == object::Archive::K_AIXBIG) { + BitMode = getBitMode(getenv("OBJECT_MODE")); + if (BitMode == BitModeTy::Unknown) + BitMode = BitModeTy::Bit32; + } + for (ArrayRef<const char *>::iterator ArgIt = Argv.begin(); ArgIt != Argv.end(); ++ArgIt) { const char *Match = nullptr; @@ -1258,6 +1387,19 @@ static int ar_main(int argc, char **argv) { matchFlagWithArg("rsp-quoting", ArgIt, Argv)) continue; + if (strncmp(*ArgIt, "-X", 2) == 0) { + if (object::Archive::getDefaultKindForHost() == + object::Archive::K_AIXBIG) { + Match = *(*ArgIt + 2) != '\0' ? *ArgIt + 2 : *(++ArgIt); + BitMode = getBitMode(Match); + if (BitMode == BitModeTy::Unknown) + fail(Twine("invalid bit mode: ") + Match); + continue; + } else { + fail(Twine(*ArgIt) + " option not supported on non AIX OS"); + } + } + Options += *ArgIt + 1; } diff --git a/llvm/tools/llvm-cov/CodeCoverage.cpp b/llvm/tools/llvm-cov/CodeCoverage.cpp index 6932e9b5bd31..13b6c3002216 100644 --- a/llvm/tools/llvm-cov/CodeCoverage.cpp +++ b/llvm/tools/llvm-cov/CodeCoverage.cpp @@ -436,8 +436,7 @@ std::unique_ptr<CoverageMapping> CodeCoverageTool::load() { CoverageMapping::load(ObjectFilenames, PGOFilename, CoverageArches, ViewOpts.CompilationDirectory); if (Error E = CoverageOrErr.takeError()) { - error("Failed to load coverage: " + toString(std::move(E)), - join(ObjectFilenames.begin(), ObjectFilenames.end(), ", ")); + error("Failed to load coverage: " + toString(std::move(E))); return nullptr; } auto Coverage = std::move(CoverageOrErr.get()); @@ -1053,7 +1052,7 @@ int CodeCoverageTool::doShow(int argc, const char **argv, sys::fs::file_status Status; if (std::error_code EC = sys::fs::status(PGOFilename, Status)) { - error("Could not read profile data!", EC.message()); + error("Could not read profile data!" + EC.message(), PGOFilename); return 1; } @@ -1170,6 +1169,12 @@ int CodeCoverageTool::doReport(int argc, const char **argv, return 1; } + sys::fs::file_status Status; + if (std::error_code EC = sys::fs::status(PGOFilename, Status)) { + error("Could not read profile data!" + EC.message(), PGOFilename); + return 1; + } + auto Coverage = load(); if (!Coverage) return 1; @@ -1219,6 +1224,12 @@ int CodeCoverageTool::doExport(int argc, const char **argv, return 1; } + sys::fs::file_status Status; + if (std::error_code EC = sys::fs::status(PGOFilename, Status)) { + error("Could not read profile data!" + EC.message(), PGOFilename); + return 1; + } + auto Coverage = load(); if (!Coverage) { error("Could not load coverage information"); diff --git a/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp b/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp index f7d3052c8c4d..cc7f353330b1 100644 --- a/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp +++ b/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp @@ -544,7 +544,7 @@ static bool collectObjectSources(ObjectFile &Obj, DWARFContext &DICtx, } // Dedup and order the sources. - llvm::sort(Sources.begin(), Sources.end()); + llvm::sort(Sources); Sources.erase(std::unique(Sources.begin(), Sources.end()), Sources.end()); for (StringRef Name : Sources) diff --git a/llvm/tools/llvm-dwarfutil/DebugInfoLinker.cpp b/llvm/tools/llvm-dwarfutil/DebugInfoLinker.cpp new file mode 100644 index 000000000000..458a58c12ca7 --- /dev/null +++ b/llvm/tools/llvm-dwarfutil/DebugInfoLinker.cpp @@ -0,0 +1,277 @@ +//=== DebugInfoLinker.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 "DebugInfoLinker.h" +#include "Error.h" +#include "llvm/DWARFLinker/DWARFLinker.h" +#include "llvm/DWARFLinker/DWARFStreamer.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/DWARF/DWARFExpression.h" +#include "llvm/Object/ObjectFile.h" +#include <memory> +#include <vector> + +namespace llvm { +namespace dwarfutil { + +// ObjFileAddressMap allows to check whether specified DIE referencing +// dead addresses. It uses tombstone values to determine dead addresses. +// The concrete values of tombstone constants were discussed in +// https://reviews.llvm.org/D81784 and https://reviews.llvm.org/D84825. +// So we use following values as indicators of dead addresses: +// +// bfd: (LowPC == 0) or (LowPC == 1 and HighPC == 1 and DWARF v4 (or less)) +// or ([LowPC, HighPC] is not inside address ranges of .text sections). +// +// maxpc: (LowPC == -1) or (LowPC == -2 and DWARF v4 (or less)) +// That value is assumed to be compatible with +// http://www.dwarfstd.org/ShowIssue.php?issue=200609.1 +// +// exec: [LowPC, HighPC] is not inside address ranges of .text sections +// +// universal: maxpc and bfd +class ObjFileAddressMap : public AddressesMap { +public: + ObjFileAddressMap(DWARFContext &Context, const Options &Options, + object::ObjectFile &ObjFile) + : Opts(Options) { + // Remember addresses of existing text sections. + for (const object::SectionRef &Sect : ObjFile.sections()) { + if (!Sect.isText()) + continue; + const uint64_t Size = Sect.getSize(); + if (Size == 0) + continue; + const uint64_t StartAddr = Sect.getAddress(); + TextAddressRanges.insert({StartAddr, StartAddr + Size}); + } + + // Check CU address ranges for tombstone value. + for (std::unique_ptr<DWARFUnit> &CU : Context.compile_units()) { + Expected<llvm::DWARFAddressRangesVector> ARanges = + CU->getUnitDIE().getAddressRanges(); + if (ARanges) { + for (auto &Range : *ARanges) { + if (!isDeadAddressRange(Range.LowPC, Range.HighPC, CU->getVersion(), + Options.Tombstone, CU->getAddressByteSize())) + DWARFAddressRanges.insert({Range.LowPC, Range.HighPC}, 0); + } + } + } + } + + // should be renamed into has valid address ranges + bool hasValidRelocs() override { return !DWARFAddressRanges.empty(); } + + bool isLiveSubprogram(const DWARFDie &DIE, + CompileUnit::DIEInfo &Info) override { + assert((DIE.getTag() == dwarf::DW_TAG_subprogram || + DIE.getTag() == dwarf::DW_TAG_label) && + "Wrong type of input die"); + + if (Optional<uint64_t> LowPC = + dwarf::toAddress(DIE.find(dwarf::DW_AT_low_pc))) { + if (!isDeadAddress(*LowPC, DIE.getDwarfUnit()->getVersion(), + Opts.Tombstone, + DIE.getDwarfUnit()->getAddressByteSize())) { + Info.AddrAdjust = 0; + Info.InDebugMap = true; + return true; + } + } + + return false; + } + + bool isLiveVariable(const DWARFDie &DIE, + CompileUnit::DIEInfo &Info) override { + assert((DIE.getTag() == dwarf::DW_TAG_variable || + DIE.getTag() == dwarf::DW_TAG_constant) && + "Wrong type of input die"); + + if (Expected<DWARFLocationExpressionsVector> Loc = + DIE.getLocations(dwarf::DW_AT_location)) { + DWARFUnit *U = DIE.getDwarfUnit(); + for (const auto &Entry : *Loc) { + DataExtractor Data(toStringRef(Entry.Expr), + U->getContext().isLittleEndian(), 0); + DWARFExpression Expression(Data, U->getAddressByteSize(), + U->getFormParams().Format); + bool HasLiveAddresses = + any_of(Expression, [&](const DWARFExpression::Operation &Op) { + // TODO: add handling of dwarf::DW_OP_addrx + return !Op.isError() && + (Op.getCode() == dwarf::DW_OP_addr && + !isDeadAddress(Op.getRawOperand(0), U->getVersion(), + Opts.Tombstone, + DIE.getDwarfUnit()->getAddressByteSize())); + }); + + if (HasLiveAddresses) { + Info.AddrAdjust = 0; + Info.InDebugMap = true; + return true; + } + } + } else { + // FIXME: missing DW_AT_location is OK here, but other errors should be + // reported to the user. + consumeError(Loc.takeError()); + } + + return false; + } + + bool applyValidRelocs(MutableArrayRef<char>, uint64_t, bool) override { + // no need to apply relocations to the linked binary. + return false; + } + + RangesTy &getValidAddressRanges() override { return DWARFAddressRanges; }; + + 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"); + } + +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 = + TextAddressRanges.getRangeThatContains(LowPC); + + if (HighPC) + return Range.has_value() && Range->end() >= *HighPC; + + return Range.has_value(); + } + + uint64_t isBFDDeadAddressRange(uint64_t LowPC, Optional<uint64_t> HighPC, + uint16_t Version) { + if (LowPC == 0) + return true; + + if ((Version <= 4) && HighPC && (LowPC == 1 && *HighPC == 1)) + return true; + + return !isInsideExecutableSectionsAddressRange(LowPC, HighPC); + } + + uint64_t isMAXPCDeadAddressRange(uint64_t LowPC, Optional<uint64_t> HighPC, + uint16_t Version, uint8_t AddressByteSize) { + if (Version <= 4 && HighPC) { + if (LowPC == (dwarf::computeTombstoneAddress(AddressByteSize) - 1)) + return true; + } else if (LowPC == dwarf::computeTombstoneAddress(AddressByteSize)) + return true; + + if (!isInsideExecutableSectionsAddressRange(LowPC, HighPC)) + warning("Address referencing invalid text section is not marked with " + "tombstone value"); + + return false; + } + + bool isDeadAddressRange(uint64_t LowPC, Optional<uint64_t> HighPC, + uint16_t Version, TombstoneKind Tombstone, + uint8_t AddressByteSize) { + switch (Tombstone) { + case TombstoneKind::BFD: + return isBFDDeadAddressRange(LowPC, HighPC, Version); + case TombstoneKind::MaxPC: + return isMAXPCDeadAddressRange(LowPC, HighPC, Version, AddressByteSize); + case TombstoneKind::Universal: + return isBFDDeadAddressRange(LowPC, HighPC, Version) || + isMAXPCDeadAddressRange(LowPC, HighPC, Version, AddressByteSize); + case TombstoneKind::Exec: + return !isInsideExecutableSectionsAddressRange(LowPC, HighPC); + } + + llvm_unreachable("Unknown tombstone value"); + } + + bool isDeadAddress(uint64_t LowPC, uint16_t Version, TombstoneKind Tombstone, + uint8_t AddressByteSize) { + return isDeadAddressRange(LowPC, None, Version, Tombstone, AddressByteSize); + } + +private: + RangesTy DWARFAddressRanges; + AddressRanges TextAddressRanges; + const Options &Opts; +}; + +bool linkDebugInfo(object::ObjectFile &File, const Options &Options, + raw_pwrite_stream &OutStream) { + + auto ReportWarn = [&](const Twine &Message, StringRef Context, + const DWARFDie *Die) { + warning(Message, Context); + + if (!Options.Verbose || !Die) + return; + + DIDumpOptions DumpOpts; + DumpOpts.ChildRecurseDepth = 0; + DumpOpts.Verbose = Options.Verbose; + + WithColor::note() << " in DIE:\n"; + Die->dump(errs(), /*Indent=*/6, DumpOpts); + }; + auto ReportErr = [&](const Twine &Message, StringRef Context, + const DWARFDie *) { + WithColor::error(errs(), Context) << Message << '\n'; + }; + + // Create output streamer. + DwarfStreamer OutStreamer(OutputFileType::Object, OutStream, nullptr, + ReportWarn, ReportWarn); + if (!OutStreamer.init(File.makeTriple(), "")) + return false; + + // 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); + DebugInfoLinker.setNoODR(!Options.DoODRDeduplication); + DebugInfoLinker.setVerbosity(Options.Verbose); + DebugInfoLinker.setUpdate(!Options.DoGarbageCollection); + + std::vector<std::unique_ptr<DWARFFile>> ObjectsForLinking(1); + std::vector<std::unique_ptr<AddressesMap>> AddresssMapForLinking(1); + std::vector<std::string> EmptyWarnings; + + std::unique_ptr<DWARFContext> Context = DWARFContext::create(File); + + // Add object files to the DWARFLinker. + AddresssMapForLinking[0] = + std::make_unique<ObjFileAddressMap>(*Context, Options, File); + + ObjectsForLinking[0] = std::make_unique<DWARFFile>( + File.getFileName(), &*Context, AddresssMapForLinking[0].get(), + EmptyWarnings); + + for (size_t I = 0; I < ObjectsForLinking.size(); I++) + DebugInfoLinker.addObjectFile(*ObjectsForLinking[I]); + + // Link debug info. + DebugInfoLinker.link(); + OutStreamer.finish(); + return true; +} + +} // end of namespace dwarfutil +} // end of namespace llvm diff --git a/llvm/tools/llvm-dwarfutil/DebugInfoLinker.h b/llvm/tools/llvm-dwarfutil/DebugInfoLinker.h new file mode 100644 index 000000000000..e95c83cb9609 --- /dev/null +++ b/llvm/tools/llvm-dwarfutil/DebugInfoLinker.h @@ -0,0 +1,31 @@ +//===- DebugInfoLinker.h ----------------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_DWARFUTIL_DEBUGINFOLINKER_H +#define LLVM_TOOLS_LLVM_DWARFUTIL_DEBUGINFOLINKER_H + +#include "Options.h" +#include "llvm/Object/Archive.h" +#include "llvm/Object/ELFObjectFile.h" +#include "llvm/Object/ObjectFile.h" + +namespace llvm { +namespace dwarfutil { + +inline bool isDebugSection(StringRef SecName) { + return SecName.startswith(".debug") || SecName.startswith(".zdebug") || + SecName == ".gdb_index"; +} + +bool linkDebugInfo(object::ObjectFile &file, const Options &Options, + raw_pwrite_stream &OutStream); + +} // end of namespace dwarfutil +} // end of namespace llvm + +#endif // LLVM_TOOLS_LLVM_DWARFUTIL_DEBUGINFOLINKER_H diff --git a/llvm/tools/llvm-dwarfutil/Error.h b/llvm/tools/llvm-dwarfutil/Error.h new file mode 100644 index 000000000000..9ef288d4f657 --- /dev/null +++ b/llvm/tools/llvm-dwarfutil/Error.h @@ -0,0 +1,44 @@ +//===- Error.h --------------------------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_DWARFUTIL_ERROR_H +#define LLVM_TOOLS_LLVM_DWARFUTIL_ERROR_H + +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/WithColor.h" +#include "llvm/Support/raw_ostream.h" + +namespace llvm { +namespace dwarfutil { + +inline void error(Error Err, StringRef Prefix = "") { + handleAllErrors(std::move(Err), [&](ErrorInfoBase &Info) { + WithColor::error(errs(), Prefix) << Info.message() << '\n'; + }); + std::exit(EXIT_FAILURE); +} + +inline void warning(const Twine &Message, StringRef Prefix = "") { + WithColor::warning(errs(), Prefix) << Message << '\n'; +} + +inline void verbose(const Twine &Message, bool Verbose) { + if (Verbose) + outs() << Message << '\n'; +} + +} // end of namespace dwarfutil +} // end of namespace llvm + +#endif // LLVM_TOOLS_LLVM_DWARFUTIL_ERROR_H diff --git a/llvm/tools/llvm-dwarfutil/Options.h b/llvm/tools/llvm-dwarfutil/Options.h new file mode 100644 index 000000000000..c993200ceb4b --- /dev/null +++ b/llvm/tools/llvm-dwarfutil/Options.h @@ -0,0 +1,46 @@ +//===- Options.h ------------------------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_DWARFUTIL_OPTIONS_H +#define LLVM_TOOLS_LLVM_DWARFUTIL_OPTIONS_H + +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringRef.h" + +namespace llvm { +namespace dwarfutil { + +/// The kind of tombstone value. +enum class TombstoneKind { + BFD, /// 0/[1:1]. Bfd default. + MaxPC, /// -1/-2. Assumed to match with + /// http://www.dwarfstd.org/ShowIssue.php?issue=200609.1. + Universal, /// both: BFD + MaxPC + Exec, /// match with address range of executable sections. +}; + +struct Options { + std::string InputFileName; + std::string OutputFileName; + bool DoGarbageCollection = false; + bool DoODRDeduplication = false; + bool BuildSeparateDebugFile = false; + TombstoneKind Tombstone = TombstoneKind::Universal; + bool Verbose = false; + int NumThreads = 0; + bool Verify = false; + + std::string getSeparateDebugFileName() const { + return OutputFileName + ".debug"; + } +}; + +} // namespace dwarfutil +} // namespace llvm + +#endif // LLVM_TOOLS_LLVM_DWARFUTIL_OPTIONS_H diff --git a/llvm/tools/llvm-dwarfutil/Options.td b/llvm/tools/llvm-dwarfutil/Options.td new file mode 100644 index 000000000000..4ab1b51d808d --- /dev/null +++ b/llvm/tools/llvm-dwarfutil/Options.td @@ -0,0 +1,65 @@ +include "llvm/Option/OptParser.td" + +multiclass BB<string name, string help1, string help2> { + def NAME: Flag<["--"], name>, HelpText<help1>; + def no_ # NAME: Flag<["--"], "no-" # name>, HelpText<help2>; +} + +def help : Flag<["--"], "help">, + HelpText<"Prints this help output">; + +def h : Flag<["-"], "h">, + Alias<help>, + HelpText<"Alias for --help">; + +defm odr_deduplication : BB<"odr-deduplication", + "Do ODR deduplication for debug types(default)", + "Don`t do ODR deduplication for debug types">; + +def odr : Flag<["--"], "odr">, + Alias<odr_deduplication>, + HelpText<"Alias for --odr-deduplication">; + +def no_odr : Flag<["--"], "no-odr">, + Alias<no_odr_deduplication>, + HelpText<"Alias for --no-odr-deduplication">; + +defm garbage_collection : BB<"garbage-collection", + "Do garbage collection for debug info(default)", + "Don`t do garbage collection for debug info">; + +defm separate_debug_file : BB<"separate-debug-file", + "Create two output files: file w/o debug tables and file with debug tables", + "Create single output file, containing debug tables(default)">; + +def tombstone: Separate<["--", "-"], "tombstone">, + MetaVarName<"[bfd,maxpc,exec,universal]">, + HelpText<"Tombstone value used as a marker of invalid address(default: universal)\n" + " =bfd - Zero for all addresses and [1,1] for DWARF v4 (or less) address ranges and exec\n" + " =maxpc - Minus 1 for all addresses and minus 2 for DWARF v4 (or less) address ranges\n" + " =exec - Match with address ranges of executable sections\n" + " =universal - Both: bfd and maxpc" + >; +def: Joined<["--", "-"], "tombstone=">, Alias<tombstone>; + +def threads: Separate<["--", "-"], "num-threads">, + MetaVarName<"<threads>">, + HelpText<"Number of available threads for multi-threaded execution. " + "Defaults to the number of cores on the current machine">; + +def: Separate<["-"], "j">, + Alias<threads>, + HelpText<"Alias for --num-threads">; + +def verbose : Flag<["--"], "verbose">, + HelpText<"Enable verbose logging">; + +def verify : Flag<["--"], "verify">, + HelpText<"Run the DWARF verifier on the resulting debug info">; + +def version : Flag<["--"], "version">, + HelpText<"Print the version and exit">; + +def V : Flag<["-"], "V">, + Alias<version>, + HelpText<"Alias for --version">; diff --git a/llvm/tools/llvm-dwarfutil/llvm-dwarfutil.cpp b/llvm/tools/llvm-dwarfutil/llvm-dwarfutil.cpp new file mode 100644 index 000000000000..e77c82e0fad9 --- /dev/null +++ b/llvm/tools/llvm-dwarfutil/llvm-dwarfutil.cpp @@ -0,0 +1,527 @@ +//=== llvm-dwarfutil.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 "DebugInfoLinker.h" +#include "Error.h" +#include "Options.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/DWARF/DWARFVerifier.h" +#include "llvm/MC/MCTargetOptionsCommandFlags.h" +#include "llvm/ObjCopy/CommonConfig.h" +#include "llvm/ObjCopy/ConfigManager.h" +#include "llvm/ObjCopy/ObjCopy.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/Option.h" +#include "llvm/Support/CRC.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileUtilities.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/TargetSelect.h" + +using namespace llvm; +using namespace object; + +namespace { +enum ID { + OPT_INVALID = 0, // This is not an option ID. +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + OPT_##ID, +#include "Options.inc" +#undef OPTION +}; + +#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; +#include "Options.inc" +#undef PREFIX + +const opt::OptTable::Info InfoTable[] = { +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + { \ + PREFIX, NAME, HELPTEXT, \ + METAVAR, OPT_##ID, opt::Option::KIND##Class, \ + PARAM, FLAGS, OPT_##GROUP, \ + OPT_##ALIAS, ALIASARGS, VALUES}, +#include "Options.inc" +#undef OPTION +}; + +class DwarfutilOptTable : public opt::OptTable { +public: + DwarfutilOptTable() : OptTable(InfoTable) {} +}; +} // namespace + +namespace llvm { +namespace dwarfutil { + +std::string ToolName; + +static mc::RegisterMCTargetOptionsFlags MOF; + +static Error validateAndSetOptions(opt::InputArgList &Args, Options &Options) { + auto UnknownArgs = Args.filtered(OPT_UNKNOWN); + if (!UnknownArgs.empty()) + return createStringError( + std::errc::invalid_argument, + formatv("unknown option: {0}", (*UnknownArgs.begin())->getSpelling()) + .str() + .c_str()); + + std::vector<std::string> InputFiles = Args.getAllArgValues(OPT_INPUT); + if (InputFiles.size() != 2) + return createStringError( + std::errc::invalid_argument, + formatv("exactly two positional arguments expected, {0} provided", + InputFiles.size()) + .str() + .c_str()); + + Options.InputFileName = InputFiles[0]; + Options.OutputFileName = InputFiles[1]; + + Options.BuildSeparateDebugFile = + Args.hasFlag(OPT_separate_debug_file, OPT_no_separate_debug_file, false); + Options.DoODRDeduplication = + Args.hasFlag(OPT_odr_deduplication, OPT_no_odr_deduplication, true); + Options.DoGarbageCollection = + Args.hasFlag(OPT_garbage_collection, OPT_no_garbage_collection, true); + Options.Verbose = Args.hasArg(OPT_verbose); + Options.Verify = Args.hasArg(OPT_verify); + + if (opt::Arg *NumThreads = Args.getLastArg(OPT_threads)) + Options.NumThreads = atoi(NumThreads->getValue()); + else + Options.NumThreads = 0; // Use all available hardware threads + + if (opt::Arg *Tombstone = Args.getLastArg(OPT_tombstone)) { + StringRef S = Tombstone->getValue(); + if (S == "bfd") + Options.Tombstone = TombstoneKind::BFD; + else if (S == "maxpc") + Options.Tombstone = TombstoneKind::MaxPC; + else if (S == "universal") + Options.Tombstone = TombstoneKind::Universal; + else if (S == "exec") + Options.Tombstone = TombstoneKind::Exec; + else + return createStringError( + std::errc::invalid_argument, + formatv("unknown tombstone 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"); + + Options.NumThreads = 1; + } + + if (Options.DoODRDeduplication && Args.hasArg(OPT_odr_deduplication) && + !Options.DoGarbageCollection) + return createStringError( + std::errc::invalid_argument, + "cannot use --odr-deduplication without --garbage-collection"); + + if (Options.BuildSeparateDebugFile && Options.OutputFileName == "-") + return createStringError( + std::errc::invalid_argument, + "unable to write to stdout when --separate-debug-file specified"); + + return Error::success(); +} + +static Error setConfigToAddNewDebugSections(objcopy::ConfigManager &Config, + ObjectFile &ObjFile) { + // Add new debug sections. + for (SectionRef Sec : ObjFile.sections()) { + Expected<StringRef> SecName = Sec.getName(); + if (!SecName) + return SecName.takeError(); + + if (isDebugSection(*SecName)) { + Expected<StringRef> SecData = Sec.getContents(); + if (!SecData) + return SecData.takeError(); + + Config.Common.AddSection.emplace_back(objcopy::NewSectionInfo( + *SecName, MemoryBuffer::getMemBuffer(*SecData, *SecName, false))); + } + } + + return Error::success(); +} + +static Error verifyOutput(const Options &Opts) { + if (Opts.OutputFileName == "-") { + warning("verification skipped because writing to stdout"); + return Error::success(); + } + + std::string FileName = Opts.BuildSeparateDebugFile + ? Opts.getSeparateDebugFileName() + : Opts.OutputFileName; + Expected<OwningBinary<Binary>> BinOrErr = createBinary(FileName); + if (!BinOrErr) + return createFileError(FileName, BinOrErr.takeError()); + + if (BinOrErr->getBinary()->isObject()) { + if (ObjectFile *Obj = static_cast<ObjectFile *>(BinOrErr->getBinary())) { + verbose("Verifying DWARF...", Opts.Verbose); + std::unique_ptr<DWARFContext> DICtx = DWARFContext::create(*Obj); + DIDumpOptions DumpOpts; + if (!DICtx->verify(Opts.Verbose ? outs() : nulls(), + DumpOpts.noImplicitRecursion())) + return createFileError(FileName, + createError("output verification failed")); + + return Error::success(); + } + } + + // The file "FileName" was created by this utility in the previous steps + // (i.e. it is already known that it should pass the isObject check). + // If the createBinary() function does not return an error, the isObject + // check should also be successful. + llvm_unreachable( + formatv("tool unexpectedly did not emit a supported object file: '{0}'", + FileName) + .str() + .c_str()); +} + +class raw_crc_ostream : public raw_ostream { +public: + explicit raw_crc_ostream(raw_ostream &O) : OS(O) { SetUnbuffered(); } + + void reserveExtraSpace(uint64_t ExtraSize) override { + OS.reserveExtraSpace(ExtraSize); + } + + uint32_t getCRC32() { return CRC32; } + +protected: + raw_ostream &OS; + uint32_t CRC32 = 0; + + /// See raw_ostream::write_impl. + void write_impl(const char *Ptr, size_t Size) override { + CRC32 = crc32( + CRC32, ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>(Ptr), Size)); + OS.write(Ptr, Size); + } + + /// Return the current position within the stream, not counting the bytes + /// currently in the buffer. + uint64_t current_pos() const override { return OS.tell(); } +}; + +static Expected<uint32_t> saveSeparateDebugInfo(const Options &Opts, + ObjectFile &InputFile) { + objcopy::ConfigManager Config; + std::string OutputFilename = Opts.getSeparateDebugFileName(); + Config.Common.InputFilename = Opts.InputFileName; + Config.Common.OutputFilename = OutputFilename; + Config.Common.OnlyKeepDebug = true; + uint32_t WrittenFileCRC32 = 0; + + if (Error Err = writeToOutput( + Config.Common.OutputFilename, [&](raw_ostream &OutFile) -> Error { + raw_crc_ostream CRCBuffer(OutFile); + if (Error Err = objcopy::executeObjcopyOnBinary(Config, InputFile, + CRCBuffer)) + return Err; + + WrittenFileCRC32 = CRCBuffer.getCRC32(); + return Error::success(); + })) + return std::move(Err); + + return WrittenFileCRC32; +} + +static Error saveNonDebugInfo(const Options &Opts, ObjectFile &InputFile, + uint32_t GnuDebugLinkCRC32) { + objcopy::ConfigManager Config; + Config.Common.InputFilename = Opts.InputFileName; + Config.Common.OutputFilename = Opts.OutputFileName; + Config.Common.StripDebug = true; + std::string SeparateDebugFileName = Opts.getSeparateDebugFileName(); + Config.Common.AddGnuDebugLink = sys::path::filename(SeparateDebugFileName); + Config.Common.GnuDebugLinkCRC32 = GnuDebugLinkCRC32; + + if (Error Err = writeToOutput( + Config.Common.OutputFilename, [&](raw_ostream &OutFile) -> Error { + if (Error Err = + objcopy::executeObjcopyOnBinary(Config, InputFile, OutFile)) + return Err; + + return Error::success(); + })) + return Err; + + return Error::success(); +} + +static Error splitDebugIntoSeparateFile(const Options &Opts, + ObjectFile &InputFile) { + Expected<uint32_t> SeparateDebugFileCRC32OrErr = + saveSeparateDebugInfo(Opts, InputFile); + if (!SeparateDebugFileCRC32OrErr) + return SeparateDebugFileCRC32OrErr.takeError(); + + if (Error Err = + saveNonDebugInfo(Opts, InputFile, *SeparateDebugFileCRC32OrErr)) + return Err; + + return Error::success(); +} + +using DebugInfoBits = SmallString<10000>; + +static Error addSectionsFromLinkedData(objcopy::ConfigManager &Config, + ObjectFile &InputFile, + DebugInfoBits &LinkedDebugInfoBits) { + if (dyn_cast<ELFObjectFile<ELF32LE>>(&InputFile)) { + Expected<ELFObjectFile<ELF32LE>> MemFile = ELFObjectFile<ELF32LE>::create( + MemoryBufferRef(LinkedDebugInfoBits, "")); + if (!MemFile) + return MemFile.takeError(); + + if (Error Err = setConfigToAddNewDebugSections(Config, *MemFile)) + return Err; + } else if (dyn_cast<ELFObjectFile<ELF64LE>>(&InputFile)) { + Expected<ELFObjectFile<ELF64LE>> MemFile = ELFObjectFile<ELF64LE>::create( + MemoryBufferRef(LinkedDebugInfoBits, "")); + if (!MemFile) + return MemFile.takeError(); + + if (Error Err = setConfigToAddNewDebugSections(Config, *MemFile)) + return Err; + } else if (dyn_cast<ELFObjectFile<ELF32BE>>(&InputFile)) { + Expected<ELFObjectFile<ELF32BE>> MemFile = ELFObjectFile<ELF32BE>::create( + MemoryBufferRef(LinkedDebugInfoBits, "")); + if (!MemFile) + return MemFile.takeError(); + + if (Error Err = setConfigToAddNewDebugSections(Config, *MemFile)) + return Err; + } else if (dyn_cast<ELFObjectFile<ELF64BE>>(&InputFile)) { + Expected<ELFObjectFile<ELF64BE>> MemFile = ELFObjectFile<ELF64BE>::create( + MemoryBufferRef(LinkedDebugInfoBits, "")); + if (!MemFile) + return MemFile.takeError(); + + if (Error Err = setConfigToAddNewDebugSections(Config, *MemFile)) + return Err; + } else + return createStringError(std::errc::invalid_argument, + "unsupported file format"); + + return Error::success(); +} + +static Expected<uint32_t> +saveSeparateLinkedDebugInfo(const Options &Opts, ObjectFile &InputFile, + DebugInfoBits LinkedDebugInfoBits) { + objcopy::ConfigManager Config; + std::string OutputFilename = Opts.getSeparateDebugFileName(); + Config.Common.InputFilename = Opts.InputFileName; + Config.Common.OutputFilename = OutputFilename; + Config.Common.StripDebug = true; + Config.Common.OnlyKeepDebug = true; + uint32_t WrittenFileCRC32 = 0; + + if (Error Err = + addSectionsFromLinkedData(Config, InputFile, LinkedDebugInfoBits)) + return std::move(Err); + + if (Error Err = writeToOutput( + Config.Common.OutputFilename, [&](raw_ostream &OutFile) -> Error { + raw_crc_ostream CRCBuffer(OutFile); + + if (Error Err = objcopy::executeObjcopyOnBinary(Config, InputFile, + CRCBuffer)) + return Err; + + WrittenFileCRC32 = CRCBuffer.getCRC32(); + return Error::success(); + })) + return std::move(Err); + + return WrittenFileCRC32; +} + +static Error saveSingleLinkedDebugInfo(const Options &Opts, + ObjectFile &InputFile, + DebugInfoBits LinkedDebugInfoBits) { + objcopy::ConfigManager Config; + + Config.Common.InputFilename = Opts.InputFileName; + Config.Common.OutputFilename = Opts.OutputFileName; + Config.Common.StripDebug = true; + if (Error Err = + addSectionsFromLinkedData(Config, InputFile, LinkedDebugInfoBits)) + return Err; + + if (Error Err = writeToOutput( + Config.Common.OutputFilename, [&](raw_ostream &OutFile) -> Error { + return objcopy::executeObjcopyOnBinary(Config, InputFile, OutFile); + })) + return Err; + + return Error::success(); +} + +static Error saveLinkedDebugInfo(const Options &Opts, ObjectFile &InputFile, + DebugInfoBits LinkedDebugInfoBits) { + if (Opts.BuildSeparateDebugFile) { + Expected<uint32_t> SeparateDebugFileCRC32OrErr = + saveSeparateLinkedDebugInfo(Opts, InputFile, + std::move(LinkedDebugInfoBits)); + if (!SeparateDebugFileCRC32OrErr) + return SeparateDebugFileCRC32OrErr.takeError(); + + if (Error Err = + saveNonDebugInfo(Opts, InputFile, *SeparateDebugFileCRC32OrErr)) + return Err; + } else { + if (Error Err = saveSingleLinkedDebugInfo(Opts, InputFile, + std::move(LinkedDebugInfoBits))) + return Err; + } + + return Error::success(); +} + +static Error saveCopyOfFile(const Options &Opts, ObjectFile &InputFile) { + objcopy::ConfigManager Config; + + Config.Common.InputFilename = Opts.InputFileName; + Config.Common.OutputFilename = Opts.OutputFileName; + + if (Error Err = writeToOutput( + Config.Common.OutputFilename, [&](raw_ostream &OutFile) -> Error { + return objcopy::executeObjcopyOnBinary(Config, InputFile, OutFile); + })) + return Err; + + return Error::success(); +} + +static Error applyCLOptions(const struct Options &Opts, ObjectFile &InputFile) { + if (Opts.DoGarbageCollection) { + verbose("Do garbage collection for debug info ...", Opts.Verbose); + + DebugInfoBits LinkedDebugInfo; + raw_svector_ostream OutStream(LinkedDebugInfo); + + if (linkDebugInfo(InputFile, Opts, OutStream)) { + if (Error Err = + saveLinkedDebugInfo(Opts, InputFile, std::move(LinkedDebugInfo))) + return Err; + + return Error::success(); + } + + return createStringError(std::errc::invalid_argument, + "possible broken debug info"); + } else if (Opts.BuildSeparateDebugFile) { + if (Error Err = splitDebugIntoSeparateFile(Opts, InputFile)) + return Err; + } else { + if (Error Err = saveCopyOfFile(Opts, InputFile)) + return Err; + } + + return Error::success(); +} + +} // end of namespace dwarfutil +} // end of namespace llvm + +int main(int Argc, char const *Argv[]) { + using namespace dwarfutil; + + InitLLVM X(Argc, Argv); + ToolName = Argv[0]; + + // Parse arguments. + DwarfutilOptTable T; + unsigned MAI; + unsigned MAC; + ArrayRef<const char *> ArgsArr = makeArrayRef(Argv + 1, Argc - 1); + opt::InputArgList Args = T.ParseArgs(ArgsArr, MAI, MAC); + + if (Args.hasArg(OPT_help) || Args.size() == 0) { + T.printHelp( + outs(), (ToolName + " [options] <input file> <output file>").c_str(), + "llvm-dwarfutil is a tool to copy and manipulate debug info", false); + return EXIT_SUCCESS; + } + + if (Args.hasArg(OPT_version)) { + cl::PrintVersionMessage(); + return EXIT_SUCCESS; + } + + Options Opts; + if (Error Err = validateAndSetOptions(Args, Opts)) + error(std::move(Err), dwarfutil::ToolName); + + InitializeAllTargets(); + InitializeAllTargetMCs(); + InitializeAllTargetInfos(); + InitializeAllAsmPrinters(); + InitializeAllAsmParsers(); + + ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr = + MemoryBuffer::getFileOrSTDIN(Opts.InputFileName); + if (BuffOrErr.getError()) + error(createFileError(Opts.InputFileName, BuffOrErr.getError())); + + Expected<std::unique_ptr<Binary>> BinOrErr = + object::createBinary(**BuffOrErr); + if (!BinOrErr) + error(createFileError(Opts.InputFileName, BinOrErr.takeError())); + + Expected<FilePermissionsApplier> PermsApplierOrErr = + FilePermissionsApplier::create(Opts.InputFileName); + if (!PermsApplierOrErr) + error(createFileError(Opts.InputFileName, PermsApplierOrErr.takeError())); + + if (!(*BinOrErr)->isObject()) + error(createFileError(Opts.InputFileName, + createError("unsupported input file"))); + + if (Error Err = + applyCLOptions(Opts, *static_cast<ObjectFile *>((*BinOrErr).get()))) + error(createFileError(Opts.InputFileName, std::move(Err))); + + BinOrErr->reset(); + BuffOrErr->reset(); + + if (Error Err = PermsApplierOrErr->apply(Opts.OutputFileName)) + error(std::move(Err)); + + if (Opts.BuildSeparateDebugFile) + if (Error Err = PermsApplierOrErr->apply(Opts.getSeparateDebugFileName())) + error(std::move(Err)); + + if (Opts.Verify) { + if (Error Err = verifyOutput(Opts)) + error(std::move(Err)); + } + + return EXIT_SUCCESS; +} diff --git a/llvm/tools/llvm-mc/llvm-mc.cpp b/llvm/tools/llvm-mc/llvm-mc.cpp index 3e737b9fbaa0..aa380d3fe9bc 100644 --- a/llvm/tools/llvm-mc/llvm-mc.cpp +++ b/llvm/tools/llvm-mc/llvm-mc.cpp @@ -77,9 +77,7 @@ static cl::opt<DebugCompressionType> CompressDebugSections( cl::desc("Choose DWARF debug sections compression:"), cl::values(clEnumValN(DebugCompressionType::None, "none", "No compression"), clEnumValN(DebugCompressionType::Z, "zlib", - "Use zlib compression"), - clEnumValN(DebugCompressionType::GNU, "zlib-gnu", - "Use zlib-gnu compression (deprecated)")), + "Use zlib compression")), cl::cat(MCCategory)); static cl::opt<bool> diff --git a/llvm/tools/llvm-objdump/llvm-objdump.cpp b/llvm/tools/llvm-objdump/llvm-objdump.cpp index 1245f9e18206..9e4fa7c0d9dd 100644 --- a/llvm/tools/llvm-objdump/llvm-objdump.cpp +++ b/llvm/tools/llvm-objdump/llvm-objdump.cpp @@ -1131,7 +1131,21 @@ static void emitPostInstructionInfo(formatted_raw_ostream &FOS, FOS.flush(); } -static void disassembleObject(const Target *TheTarget, const ObjectFile &Obj, +static void createFakeELFSections(ObjectFile &Obj) { + assert(Obj.isELF()); + if (auto *Elf32LEObj = dyn_cast<ELF32LEObjectFile>(&Obj)) + Elf32LEObj->createFakeSections(); + else if (auto *Elf64LEObj = dyn_cast<ELF64LEObjectFile>(&Obj)) + Elf64LEObj->createFakeSections(); + else if (auto *Elf32BEObj = dyn_cast<ELF32BEObjectFile>(&Obj)) + Elf32BEObj->createFakeSections(); + else if (auto *Elf64BEObj = cast<ELF64BEObjectFile>(&Obj)) + Elf64BEObj->createFakeSections(); + else + llvm_unreachable("Unsupported binary format"); +} + +static void disassembleObject(const Target *TheTarget, ObjectFile &Obj, MCContext &Ctx, MCDisassembler *PrimaryDisAsm, MCDisassembler *SecondaryDisAsm, const MCInstrAnalysis *MIA, MCInstPrinter *IP, @@ -1198,6 +1212,9 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile &Obj, if (Obj.isWasm()) addMissingWasmCodeSymbols(cast<WasmObjectFile>(Obj), AllSymbols); + if (Obj.isELF() && Obj.sections().empty()) + createFakeELFSections(Obj); + BumpPtrAllocator A; StringSaver Saver(A); addPltEntries(Obj, AllSymbols, Saver); @@ -1261,6 +1278,25 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile &Obj, LLVM_DEBUG(LVP.dump()); + std::unordered_map<uint64_t, BBAddrMap> AddrToBBAddrMap; + auto ReadBBAddrMap = [&](Optional<unsigned> SectionIndex = None) { + AddrToBBAddrMap.clear(); + if (const auto *Elf = dyn_cast<ELFObjectFileBase>(&Obj)) { + auto BBAddrMapsOrErr = Elf->readBBAddrMap(SectionIndex); + if (!BBAddrMapsOrErr) + reportWarning(toString(BBAddrMapsOrErr.takeError()), + Obj.getFileName()); + for (auto &FunctionBBAddrMap : *BBAddrMapsOrErr) + AddrToBBAddrMap.emplace(FunctionBBAddrMap.Addr, + std::move(FunctionBBAddrMap)); + } + }; + + // For non-relocatable objects, Read all LLVM_BB_ADDR_MAP sections into a + // single mapping, since they don't have any conflicts. + if (SymbolizeOperands && !Obj.isRelocatableObject()) + ReadBBAddrMap(); + for (const SectionRef &Section : ToolSectionFilter(Obj)) { if (FilterSections.empty() && !DisassembleAll && (!Section.isText() || Section.isVirtual())) @@ -1271,19 +1307,10 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile &Obj, if (!SectSize) continue; - std::unordered_map<uint64_t, BBAddrMap> AddrToBBAddrMap; - if (SymbolizeOperands) { - if (auto *Elf = dyn_cast<ELFObjectFileBase>(&Obj)) { - // Read the BB-address-map corresponding to this section, if present. - auto SectionBBAddrMapsOrErr = Elf->readBBAddrMap(Section.getIndex()); - if (!SectionBBAddrMapsOrErr) - reportWarning(toString(SectionBBAddrMapsOrErr.takeError()), - Obj.getFileName()); - for (auto &FunctionBBAddrMap : *SectionBBAddrMapsOrErr) - AddrToBBAddrMap.emplace(FunctionBBAddrMap.Addr, - std::move(FunctionBBAddrMap)); - } - } + // For relocatable object files, read the LLVM_BB_ADDR_MAP section + // corresponding to this section, if present. + if (SymbolizeOperands && Obj.isRelocatableObject()) + ReadBBAddrMap(Section.getIndex()); // Get the list of all the symbols in this section. SectionSymbolsTy &Symbols = AllSymbols[Section]; @@ -1688,7 +1715,7 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile &Obj, reportWarning("failed to disassemble missing symbol " + Sym, FileName); } -static void disassembleObject(const ObjectFile *Obj, bool InlineRelocs) { +static void disassembleObject(ObjectFile *Obj, bool InlineRelocs) { const Target *TheTarget = getTarget(Obj); // Package up features to be passed to target/subtarget @@ -1890,7 +1917,7 @@ static size_t getMaxSectionNameWidth(const ObjectFile &Obj) { return MaxWidth; } -void objdump::printSectionHeaders(const ObjectFile &Obj) { +void objdump::printSectionHeaders(ObjectFile &Obj) { size_t NameWidth = getMaxSectionNameWidth(Obj); size_t AddressWidth = 2 * Obj.getBytesInAddress(); bool HasLMAColumn = shouldDisplayLMA(Obj); @@ -1903,6 +1930,9 @@ void objdump::printSectionHeaders(const ObjectFile &Obj) { outs() << "Idx " << left_justify("Name", NameWidth) << " Size " << left_justify("VMA", AddressWidth) << " Type\n"; + if (Obj.isELF() && Obj.sections().empty()) + createFakeELFSections(Obj); + uint64_t Idx; for (const SectionRef &Section : ToolSectionFilter(Obj, &Idx)) { StringRef Name = unwrapOrError(Section.getName(), Obj.getFileName()); diff --git a/llvm/tools/llvm-objdump/llvm-objdump.h b/llvm/tools/llvm-objdump/llvm-objdump.h index dd9f58aa3308..c64c042d513e 100644 --- a/llvm/tools/llvm-objdump/llvm-objdump.h +++ b/llvm/tools/llvm-objdump/llvm-objdump.h @@ -124,7 +124,7 @@ SectionFilter ToolSectionFilter(llvm::object::ObjectFile const &O, bool isRelocAddressLess(object::RelocationRef A, object::RelocationRef B); void printRelocations(const object::ObjectFile *O); void printDynamicRelocations(const object::ObjectFile *O); -void printSectionHeaders(const object::ObjectFile &O); +void printSectionHeaders(object::ObjectFile &O); void printSectionContents(const object::ObjectFile *O); void printSymbolTable(const object::ObjectFile &O, StringRef ArchiveName, StringRef ArchitectureName = StringRef(), diff --git a/llvm/tools/llvm-profdata/llvm-profdata.cpp b/llvm/tools/llvm-profdata/llvm-profdata.cpp index 9c6586483ef0..0c23d7c1435f 100644 --- a/llvm/tools/llvm-profdata/llvm-profdata.cpp +++ b/llvm/tools/llvm-profdata/llvm-profdata.cpp @@ -2471,9 +2471,10 @@ static int showHotFunctionList(const sampleprof::SampleProfileMap &Profiles, (ProfileTotalSample > 0) ? (Func.getTotalSamples() * 100.0) / ProfileTotalSample : 0; - PrintValues.emplace_back(HotFuncInfo( - Func.getContext().toString(), Func.getTotalSamples(), - TotalSamplePercent, FuncPair.second.second, Func.getEntrySamples())); + PrintValues.emplace_back( + HotFuncInfo(Func.getContext().toString(), Func.getTotalSamples(), + TotalSamplePercent, FuncPair.second.second, + Func.getHeadSamplesEstimate())); } dumpHotFunctionList(ColumnTitle, ColumnOffset, PrintValues, HotFuncCount, Profiles.size(), HotFuncSample, ProfileTotalSample, diff --git a/llvm/tools/llvm-size/llvm-size.cpp b/llvm/tools/llvm-size/llvm-size.cpp index ec9a4cde56b6..1c7484ba5496 100644 --- a/llvm/tools/llvm-size/llvm-size.cpp +++ b/llvm/tools/llvm-size/llvm-size.cpp @@ -868,8 +868,11 @@ int main(int argc, char **argv) { StringSaver Saver(A); SizeOptTable Tbl; ToolName = argv[0]; - opt::InputArgList Args = Tbl.parseArgs(argc, argv, OPT_UNKNOWN, Saver, - [&](StringRef Msg) { error(Msg); }); + opt::InputArgList Args = + Tbl.parseArgs(argc, argv, OPT_UNKNOWN, Saver, [&](StringRef Msg) { + error(Msg); + exit(1); + }); if (Args.hasArg(OPT_help)) { Tbl.printHelp( outs(), diff --git a/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp b/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp index b782c7a1720a..7ec70e42f1c1 100644 --- a/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp +++ b/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp @@ -365,20 +365,15 @@ static SmallVector<uint8_t> parseBuildIDArg(const opt::InputArgList &Args, return BuildID; } -// Symbolize the markup from stdin and write the result to stdout. +// Symbolize markup from stdin and write the result to stdout. static void filterMarkup(const opt::InputArgList &Args) { - MarkupParser Parser; MarkupFilter Filter(outs(), parseColorArg(Args)); - for (std::string InputString; std::getline(std::cin, InputString);) { + std::string InputString; + while (std::getline(std::cin, InputString)) { InputString += '\n'; - Parser.parseLine(InputString); - Filter.beginLine(InputString); - while (Optional<MarkupNode> Element = Parser.nextNode()) - Filter.filter(*Element); + Filter.filter(InputString); } - Parser.flush(); - while (Optional<MarkupNode> Element = Parser.nextNode()) - Filter.filter(*Element); + Filter.finish(); } ExitOnError ExitOnErr; diff --git a/llvm/tools/opt/opt.cpp b/llvm/tools/opt/opt.cpp index 1160412e37af..a02997f82bb3 100644 --- a/llvm/tools/opt/opt.cpp +++ b/llvm/tools/opt/opt.cpp @@ -352,32 +352,6 @@ static void AddOptimizationPasses(legacy::PassManagerBase &MPM, if (TM) TM->adjustPassManager(Builder); - switch (PGOKindFlag) { - case InstrGen: - Builder.EnablePGOInstrGen = true; - Builder.PGOInstrGen = ProfileFile; - break; - case InstrUse: - Builder.PGOInstrUse = ProfileFile; - break; - case SampleUse: - Builder.PGOSampleUse = ProfileFile; - break; - default: - break; - } - - switch (CSPGOKindFlag) { - case CSInstrGen: - Builder.EnablePGOCSInstrGen = true; - break; - case CSInstrUse: - Builder.EnablePGOCSInstrUse = true; - break; - default: - break; - } - Builder.populateFunctionPassManager(FPM); Builder.populateModulePassManager(MPM); } @@ -545,8 +519,6 @@ int main(int argc, char **argv) { initializeIndirectBrExpandPassPass(Registry); initializeInterleavedLoadCombinePass(Registry); initializeInterleavedAccessPass(Registry); - initializeEntryExitInstrumenterPass(Registry); - initializePostInlineEntryExitInstrumenterPass(Registry); initializeUnreachableBlockElimLegacyPassPass(Registry); initializeExpandReductionsPass(Registry); initializeExpandVectorPredicationPass(Registry); |
