Index: lld/ELF/Config.h =================================================================== --- lld/ELF/Config.h +++ lld/ELF/Config.h @@ -156,6 +156,7 @@ bool compressDebugSections; bool cref; std::vector> deadRelocInNonAlloc; + bool debugSections = false; bool defineCommon; bool demangle = true; bool dependentLibraries; Index: lld/ELF/Driver.cpp =================================================================== --- lld/ELF/Driver.cpp +++ lld/ELF/Driver.cpp @@ -341,6 +341,9 @@ if (config->strip == StripPolicy::All && config->emitRelocs) error("--strip-all and --emit-relocs may not be used together"); + if (config->strip == StripPolicy::All && config->debugSections) + error("--strip-all and --debug-sections may not be used together"); + if (config->zText && config->zIfuncNoplt) error("-z text and -z ifunc-noplt may not be used together"); @@ -355,6 +358,8 @@ error("-r and -pie may not be used together"); if (config->exportDynamic) error("-r and --export-dynamic may not be used together"); + if (config->debugSections) + error("-r and --debug-sections may not be used together"); } if (config->executeOnly) { @@ -1329,6 +1334,9 @@ if (args.hasArg(OPT_print_map)) config->mapFile = "-"; + if (args.hasArg(OPT_debug_sections)) + config->debugSections = true; + // Page alignment can be disabled by the -n (--nmagic) and -N (--omagic). // As PT_GNU_RELRO relies on Paging, do not create it when we have disabled // it. Index: lld/ELF/Options.td =================================================================== --- lld/ELF/Options.td +++ lld/ELF/Options.td @@ -502,6 +502,8 @@ def visual_studio_diagnostics_format : F<"vs-diagnostics">, HelpText<"Format diagnostics for Visual Studio compatibility">; +def debug_sections : F<"debug-sections">, HelpText<"Emit debug sections that describe the output">; + // Aliases def: Separate<["-"], "f">, Alias, HelpText<"Alias for --auxiliary">; def: F<"call_shared">, Alias, HelpText<"Alias for --Bdynamic">; Index: lld/ELF/OutputSections.cpp =================================================================== --- lld/ELF/OutputSections.cpp +++ lld/ELF/OutputSections.cpp @@ -155,6 +155,9 @@ // set sh_entsize to 0. if (entsize != isec->entsize) entsize = 0; + + if (config->debugSections) + in.debugLinkMap->addSection(isec); } // This function scans over the InputSectionBase list sectionBases to create Index: lld/ELF/SyntheticSections.h =================================================================== --- lld/ELF/SyntheticSections.h +++ lld/ELF/SyntheticSections.h @@ -851,6 +851,23 @@ bool isNeeded() const override; }; +class DebugLinkMapSection final : public SyntheticSection { +public: + DebugLinkMapSection(StringTableSection &strTab); + void writeTo(uint8_t *buf) override; + size_t getSize() const override; + void addSection(InputSectionBase *isec); + StringTableSection *getStrTab() { return &strTab; } + void finalizeContents() override; + +private: + std::vector sections; + StringTableSection &strTab; + + SmallVector sizeField; + SmallVector content; +}; + // For more information about .gnu.version and .gnu.version_r see: // https://www.akkadia.org/drepper/symbol-versioning @@ -1257,6 +1274,7 @@ SyntheticSection *partIndex; PltSection *plt; IpltSection *iplt; + DebugLinkMapSection *debugLinkMap; PPC32Got2Section *ppc32Got2; IBTPltSection *ibtPlt; RelocationBaseSection *relaPlt; Index: lld/ELF/SyntheticSections.cpp =================================================================== --- lld/ELF/SyntheticSections.cpp +++ lld/ELF/SyntheticSections.cpp @@ -643,6 +643,53 @@ getPartition().ehFrameHdr->write(); } + +DebugLinkMapSection::DebugLinkMapSection(StringTableSection &strTab) + : SyntheticSection(0, SHT_DEBUG_LLD_LINKMAP, 0, ".debug_lld_linkmap"), + strTab(strTab) { + this->entsize = 1; +} + +size_t DebugLinkMapSection::getSize() const { + return sizeField.size() + content.size(); +} + +void DebugLinkMapSection::writeTo(uint8_t *buf) { + memcpy(buf, sizeField.data(), sizeField.size()); + memcpy(buf + sizeField.size(), content.data(), content.size()); +} + +void DebugLinkMapSection::addSection(InputSectionBase *i) { + auto *isec = dyn_cast(i); + if (!isec || !isec->file || (isec->getSize() == 0)) + return; + sections.push_back(isec); +} + +void DebugLinkMapSection::finalizeContents() { + assert (getParent() && strTab.getParent()); + getParent()->link = strTab.getParent()->sectionIndex; + + raw_svector_ostream os(content); + auto add = [&](int64_t v) { encodeULEB128(v, os); }; + + add(0); // Version + add(sections.size()); // Entry count + + for (InputSection *isec : sections) { + StringRef secName = isec->name; + add(strTab.addString(StringRef(toString(isec->file)).copy(bAlloc), + true)); // Filename + add(strTab.addString(secName)); // Section Name + add(isec->parent->getOutputSection()->addr + isec->outSecOff); // Address + add(isec->getSize()); // Size + } + + // Finally calcuate the contents of the size field. + raw_svector_ostream sizeFieldOS(sizeField); + encodeULEB128(content.size(), sizeFieldOS); +} + GotSection::GotSection() : SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS, target->gotEntrySize, ".got") { Index: lld/ELF/Writer.cpp =================================================================== --- lld/ELF/Writer.cpp +++ lld/ELF/Writer.cpp @@ -358,6 +358,17 @@ auto add = [](SyntheticSection *sec) { inputSections.push_back(sec); }; + if (config->debugSections) { + auto *debugLinkMapStrTab = + make(".debug_lld_linkmap_str", false); + debugLinkMapStrTab->type = SHT_PROGBITS; + add(debugLinkMapStrTab); + + in.debugLinkMap = make(*debugLinkMapStrTab); + add(in.debugLinkMap); + } + + in.shStrTab = make(".shstrtab", false); Out::programHeaders = make("", 0, SHF_ALLOC); @@ -2229,6 +2240,21 @@ finalizeSynthetic(in.ppc64LongBranchTarget); } + if (config->debugSections) { + llvm::TimeTraceScope timeScope("Finalize debug sections"); + // Finalize these now. They use variable length encoding + // and refer to section by address. So, we need the layout + // to be fixed before we know the size of these sections. + auto *debugLinkMap = in.debugLinkMap; + auto *debugLinkMapStrTab = debugLinkMap ? debugLinkMap->getStrTab() : nullptr; + finalizeSynthetic(debugLinkMapStrTab); + finalizeSynthetic(in.debugLinkMap); + + // Respin the layout to account for the final sizes + // of these sections. + script->assignAddresses(); + } + // Relaxation to delete inter-basic block jumps created by basic block // sections. Run after in.symTab is finalized as optimizeBasicBlockJumps // can relax jump instructions based on symbol offset. Index: lld/test/ELF/debug_lld_linkmap.s =================================================================== --- /dev/null +++ lld/test/ELF/debug_lld_linkmap.s @@ -0,0 +1,123 @@ +## Show that the linkmap section is emitted correctly. + +# RUN: rm -rf %t.dir +# RUN: split-file %s %t.dir +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %t.dir/1.s -o %t.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %t.dir/2.s -o %t2.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %t.dir/3.s -o %t3.o +# RUN: llvm-ar rc %t.dir/archive.a %t2.o +# RUN: llvm-ar rcT %t.dir/thin.a %t3.o + +## Sanity check that the metadata section is not emitted without --debug-sections. +# RUN: ld.lld %t.o %t.dir/archive.a %t.dir/thin.a --build-id=none -o %t.default.elf +# RUN: llvm-readelf --debug_lld_linkmap %t.default.elf | FileCheck %s --check-prefix=NOLINKMAP --allow-empty + +# NOLINKMAP-NOT: DebugLLDLinkMap { + +## Show that the linkmap section headers and contents are as expected. +# RUN: ld.lld %t.o %t.dir/archive.a %t.dir/thin.a --build-id=none -o %t.elf --debug-sections +# RUN: llvm-readelf --sections --debug_lld_linkmap %t.elf | \ +# RUN: FileCheck %s --check-prefix=CONTENT --match-full-lines \ +# RUN: -DOBJECT=%t.o \ +# RUN: -DARDIR=%t.dir -DFARCHIVE='archive.a(%basename_t.tmp2.o)' \ +# RUN: -DTARCHIVE='thin.a(%/t3.o)' + +# CONTENT: Section Headers: +# CONTENT-NEXT: [Nr] Name Type Address {{.*}} ES Flg Lk Inf Al +# CONTENT-DAG: {{.*}} .text PROGBITS [[#%.16x,TEXTAD:]] {{.*}} +# CONTENT-DAG: {{.*}} .data PROGBITS [[#%.16x,DATAAD:]] {{.*}} +# CONTENT-DAG: {{.*}} .debug_lld_linkmap_str PROGBITS 0000000000000000 {{.*}} 00 0 0 1 +# CONTENT-DAG: {{.*}} .debug_lld_linkmap DEBUG_LLD_LINKMAP 0000000000000000 {{.*}} 01 4 0 1 + + +# CONTENT: DebugLLDLinkMap { +# CONTENT-NEXT: Length: 48 +# CONTENT-NEXT: Version: 0 +# CONTENT-NEXT: Entry Count: 6 +# CONTENT-NEXT: Entry { +# CONTENT-NEXT: FileName: [[OBJECT]] +# CONTENT-NEXT: Section: .text.main +# CONTENT-NEXT: Address: 0x[[#%X,TEXTAD]] +# CONTENT-NEXT: Size: 10 +# CONTENT-NEXT: } +# CONTENT-NEXT: Entry { +# CONTENT-NEXT: FileName: [[OBJECT]] +# CONTENT-NEXT: Section: .text.other +# CONTENT-NEXT: Address: 0x[[#%X,TEXTAD+10]] +# CONTENT-NEXT: Size: 10 +# CONTENT-NEXT: } +# CONTENT-NEXT: Entry { +# CONTENT-NEXT: FileName: [[ARDIR]]{{[\\/]}}[[FARCHIVE]] +# CONTENT-NEXT: Section: .text.main +# CONTENT-NEXT: Address: 0x[[#%X,TEXTAD+20]] +# CONTENT-NEXT: Size: 5 +# CONTENT-NEXT: } +# CONTENT-NEXT: Entry { +# CONTENT-NEXT: FileName: [[ARDIR]]{{[\\/]}}[[FARCHIVE]] +# CONTENT-NEXT: Section: .text.local +# CONTENT-NEXT: Address: 0x[[#%X,TEXTAD+25]] +# CONTENT-NEXT: Size: 1 +# CONTENT-NEXT: } +# CONTENT-NEXT: Entry { +# CONTENT-NEXT: FileName: [[OBJECT]] +# CONTENT-NEXT: Section: .data +# CONTENT-NEXT: Address: 0x[[#%X,DATAAD]] +# CONTENT-NEXT: Size: 4 +# CONTENT-NEXT: } +# CONTENT-NEXT: Entry { +# CONTENT-NEXT: FileName: [[ARDIR]]{{[\\/]}}[[TARCHIVE]] +# CONTENT-NEXT: Section: .data +# CONTENT-NEXT: Address: 0x[[#%X,DATAAD+4]] +# CONTENT-NEXT: Size: 8 +# CONTENT-NEXT: } +# CONTENT-NEXT: } + +## Show that the option is the help text. +# RUN: ld.lld --help | FileCheck %s --check-prefix=HELP + +# HELP: {{^}} --debug-sections{{ }} + +#--- 1.s +.section .text.main,"ax",@progbits +.global main +main: + call bar + call other + +## Different contribution to same output section gets separate entry. +.section .text.other,"ax",@progbits +.global other +other: + call foo + call thin + +## Empty section doesn't result in a linkmap entry. +.section .text.empty,"ax",@progbits + +## Different contribution in different output section gets own entry. +.data +.global foo +foo: +.long 0x42 + +#--- 2.s +## Regular archive member has correct entries. + +## Same-named contribution in different file gets separate entry. +.section .text.main,"ax",@progbits +.global bar +bar: + call local + +## Section only containing local symbols gets entry. +.section .text.local,"ax",@progbits +local: + ret + +#--- 3.s +## Thin archive member has correct entry. + +.data +.weak thin +thin: +.quad 0x24 Index: llvm/include/llvm/BinaryFormat/ELF.h =================================================================== --- llvm/include/llvm/BinaryFormat/ELF.h +++ llvm/include/llvm/BinaryFormat/ELF.h @@ -942,6 +942,7 @@ SHT_LLVM_PART_PHDR = 0x6fff4c07, // Phdrs for loadable partition. SHT_LLVM_BB_ADDR_MAP = 0x6fff4c08, // LLVM Basic Block Address Map. SHT_LLVM_CALL_GRAPH_PROFILE = 0x6fff4c09, // LLVM Call Graph Profile. + SHT_DEBUG_LLD_LINKMAP = 0x6fff4c0d, // Android's experimental support for SHT_RELR sections. // https://android.googlesource.com/platform/bionic/+/b7feec74547f84559a1467aca02708ff61346d2a/libc/include/elf.h#512 SHT_ANDROID_RELR = 0x6fffff00, // Relocation entries; only offsets. Index: llvm/lib/Object/ELF.cpp =================================================================== --- llvm/lib/Object/ELF.cpp +++ llvm/lib/Object/ELF.cpp @@ -278,6 +278,7 @@ STRINGIFY_ENUM_CASE(ELF, SHT_LLVM_ODRTAB); STRINGIFY_ENUM_CASE(ELF, SHT_LLVM_LINKER_OPTIONS); STRINGIFY_ENUM_CASE(ELF, SHT_LLVM_CALL_GRAPH_PROFILE); + STRINGIFY_ENUM_CASE(ELF, SHT_DEBUG_LLD_LINKMAP); STRINGIFY_ENUM_CASE(ELF, SHT_LLVM_ADDRSIG); STRINGIFY_ENUM_CASE(ELF, SHT_LLVM_DEPENDENT_LIBRARIES); STRINGIFY_ENUM_CASE(ELF, SHT_LLVM_SYMPART); Index: llvm/tools/llvm-readobj/ELFDumper.cpp =================================================================== --- llvm/tools/llvm-readobj/ELFDumper.cpp +++ llvm/tools/llvm-readobj/ELFDumper.cpp @@ -216,6 +216,7 @@ void printArchSpecificInfo() override; void printStackMap() const override; + void printDebugLLDLinkMap() override; const object::ELFObjectFile &getElfObject() const { return ObjF; }; std::string describe(const Elf_Shdr &Sec) const; @@ -365,6 +366,7 @@ const Elf_Shdr *DotSymtabSec = nullptr; const Elf_Shdr *DotDynsymSec = nullptr; const Elf_Shdr *DotAddrsigSec = nullptr; + const Elf_Shdr *DotDebugLLDLinkMap = nullptr; DenseMap> ShndxTables; Optional SONameOffset; Optional>> AddressToIndexMap; @@ -1853,6 +1855,10 @@ if (!DotAddrsigSec) DotAddrsigSec = &Sec; break; + case ELF::SHT_DEBUG_LLD_LINKMAP: + if (!DotDebugLLDLinkMap) + DotDebugLLDLinkMap = &Sec; + break; } } @@ -2063,6 +2069,100 @@ printVersionDependencySection(SymbolVersionNeedSection); } +template void ELFDumper::printDebugLLDLinkMap() { + const Elf_Shdr *Sec = this->DotDebugLLDLinkMap; + if (Sec == nullptr || !Sec->sh_link) + return; + + Expected StrSecOrErr = Obj.getSection(Sec->sh_link); + if (!StrSecOrErr) + return; + const Elf_Shdr *StrSec = *StrSecOrErr; + + DictScope D(W, "DebugLLDLinkMap"); + + StringRef StrTab = toStringRef( + unwrapOrError(ObjF.getFileName(), Obj.getSectionContents(*StrSec))); + + auto GetStr = [&](uint32_t Offset) -> StringRef { + if (Offset >= StrTab.size()) + return ""; + return StrTab.drop_front(Offset).take_until( + [](char C) { return C == '\0'; }); + }; + + Expected> ContentsOrErr = Obj.getSectionContents(*Sec); + if (!ContentsOrErr) { + this->reportUniqueWarning(ContentsOrErr.takeError()); + return; + } + + unsigned LengthSize; + const char *LengthErr; + const size_t Length = decodeULEB128(ContentsOrErr->begin(), &LengthSize, + ContentsOrErr->end(), &LengthErr); + if (LengthErr) { + this->reportUniqueWarning(createError("unable to read length field in " + + ::describe(Obj, *Sec) + ": " + + LengthErr)); + return; + } + const size_t PayloadSize = Length + LengthSize; + + if (ContentsOrErr->size() < PayloadSize) { + this->reportUniqueWarning(createError("unable to read " + + ::describe(Obj, *Sec) + ": " + + "section too small for payload")); + return; + } + + if (Expected> DecodedOrErr = + toULEB128Array(ArrayRef( + ContentsOrErr->begin(), ContentsOrErr->begin() + PayloadSize))) { + std::vector &Decoded = *DecodedOrErr; + + if (Decoded.size() < 3) { + this->reportUniqueWarning(createError("unable to read " + + ::describe(Obj, *Sec) + ": " + + "not enough space for header")); + return; + } + + size_t I = 0; + W.printNumber("Length", Decoded[I++]); + W.printNumber("Version", Decoded[I++]); + const size_t Entries = Decoded[I++]; + W.printNumber("Entry Count", Entries); + + size_t Entry = 0; + while (I < Decoded.size()) { + if (I + 4 > Decoded.size()) { + this->reportUniqueWarning( + createError("unable to parse " + ::describe(Obj, *Sec) + ": " + + "entry " + Twine(Entry) + " truncated")); + return; + } + + DictScope D(W, "Entry"); + W.printString("FileName", GetStr(Decoded[I++])); + W.printString("Section", GetStr(Decoded[I++])); + W.printHex("Address", Decoded[I++]); + W.printNumber("Size", Decoded[I++]); + Entry++; + } + + if (Entries != Entry + 1) + this->reportUniqueWarning( + createError("parsing " + ::describe(Obj, *Sec) + ": " + + "number of entries (" + Twine(Entry + 1) + + ") does not match entry count (" + Twine(Entries) + ")")); + + } else + this->reportUniqueWarning(createError("unable to decode " + + ::describe(Obj, *Sec) + ": " + + toString(DecodedOrErr.takeError()))); +} + #define LLVM_READOBJ_DT_FLAG_ENT(prefix, enum) \ { #enum, prefix##_##enum } Index: llvm/tools/llvm-readobj/ObjDumper.h =================================================================== --- llvm/tools/llvm-readobj/ObjDumper.h +++ llvm/tools/llvm-readobj/ObjDumper.h @@ -74,6 +74,7 @@ virtual void printCGProfile() {} virtual void printBBAddrMaps() {} virtual void printAddrsig() {} + virtual void printDebugLLDLinkMap() {} virtual void printNotes() {} virtual void printELFLinkerOptions() {} virtual void printStackSizes() {} Index: llvm/tools/llvm-readobj/Opts.td =================================================================== --- llvm/tools/llvm-readobj/Opts.td +++ llvm/tools/llvm-readobj/Opts.td @@ -49,6 +49,7 @@ def elf_linker_options : FF<"elf-linker-options", "Display the .linker-options section">, Group; defm elf_output_style : Eq<"elf-output-style", "Specify ELF dump style">, Group; def histogram : FF<"histogram", "Display bucket list histogram for hash sections">, Group; +def debug_lld_linkmap : FF<"debug_lld_linkmap", "Display contents of lld's linkmap section">, Group; def section_groups : FF<"section-groups", "Display section groups">, Group; def gnu_hash_table : FF<"gnu-hash-table", "Display .gnu.hash section">, Group; def hash_symbols : FF<"hash-symbols", "Display the dynamic symbols derived from the hash section">, Group; Index: llvm/tools/llvm-readobj/llvm-readobj.cpp =================================================================== --- llvm/tools/llvm-readobj/llvm-readobj.cpp +++ llvm/tools/llvm-readobj/llvm-readobj.cpp @@ -149,6 +149,7 @@ static bool COFFResources; static bool COFFTLSDirectory; +static bool DebugLLDLinkMap; OutputStyleTy Output = OutputStyleTy::LLVM; static std::vector InputFilenames; } // namespace opts @@ -268,6 +269,7 @@ opts::COFFResources = Args.hasArg(OPT_coff_resources); opts::COFFTLSDirectory = Args.hasArg(OPT_coff_tls_directory); + opts::DebugLLDLinkMap = Args.hasArg(OPT_debug_lld_linkmap); opts::InputFilenames = Args.getAllArgValues(OPT_INPUT); } @@ -400,6 +402,8 @@ Dumper->printBBAddrMaps(); if (opts::Addrsig) Dumper->printAddrsig(); + if (opts::DebugLLDLinkMap) + Dumper->printDebugLLDLinkMap(); if (opts::Notes) Dumper->printNotes(); }