Index: ELF/CMakeLists.txt =================================================================== --- ELF/CMakeLists.txt +++ ELF/CMakeLists.txt @@ -18,6 +18,7 @@ OutputSections.cpp Relocations.cpp ScriptParser.cpp + SplitDebugInfo.cpp Strings.cpp SymbolListFile.cpp SymbolTable.cpp Index: ELF/Config.h =================================================================== --- ELF/Config.h +++ ELF/Config.h @@ -98,6 +98,7 @@ bool EnableNewDtags; bool ExportDynamic; bool FatalWarnings; + bool GdbIndex; bool GcSections; bool GnuHash = false; bool ICF; Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -449,6 +449,7 @@ Config->ExportDynamic = Args.hasArg(OPT_export_dynamic); Config->FatalWarnings = Args.hasArg(OPT_fatal_warnings); Config->GcSections = getArg(Args, OPT_gc_sections, OPT_no_gc_sections, false); + Config->GdbIndex = Args.hasArg(OPT_gdb_index); Config->ICF = Args.hasArg(OPT_icf); Config->NoGnuUnique = Args.hasArg(OPT_no_gnu_unique); Config->NoUndefinedVersion = Args.hasArg(OPT_no_undefined_version); Index: ELF/InputFiles.h =================================================================== --- ELF/InputFiles.h +++ ELF/InputFiles.h @@ -142,6 +142,7 @@ void parse(llvm::DenseSet &ComdatGroups); ArrayRef *> getSections() const { return Sections; } + InputSectionBase *getSection(uint32_t Index) const; InputSectionBase *getSection(const Elf_Sym &Sym) const; SymbolBody &getSymbolBody(uint32_t SymbolIndex) const { Index: ELF/InputFiles.cpp =================================================================== --- ELF/InputFiles.cpp +++ ELF/InputFiles.cpp @@ -350,8 +350,7 @@ template InputSectionBase * -elf::ObjectFile::getSection(const Elf_Sym &Sym) const { - uint32_t Index = this->getSectionIndex(Sym); +elf::ObjectFile::getSection(uint32_t Index) const { if (Index == 0) return nullptr; if (Index >= Sections.size()) @@ -368,6 +367,12 @@ } template +InputSectionBase * +elf::ObjectFile::getSection(const Elf_Sym &Sym) const { + return getSection(this->getSectionIndex(Sym)); +} + +template SymbolBody *elf::ObjectFile::createSymbolBody(const Elf_Sym *Sym) { int Binding = Sym->getBinding(); InputSectionBase *Sec = getSection(*Sym); Index: ELF/Options.td =================================================================== --- ELF/Options.td +++ ELF/Options.td @@ -95,6 +95,9 @@ def image_base : J<"image-base=">, HelpText<"Set the base address">; +def gdb_index: F<"gdb-index">, + HelpText<"Generate .gdb_index section">; + def init: S<"init">, MetaVarName<"">, HelpText<"Specify an initializer function">; Index: ELF/OutputSections.h =================================================================== --- ELF/OutputSections.h +++ ELF/OutputSections.h @@ -12,6 +12,7 @@ #include "Config.h" #include "Relocations.h" +#include "SplitDebugInfo.h" #include "lld/Core/LLVM.h" #include "llvm/ADT/SmallPtrSet.h" @@ -119,6 +120,31 @@ Elf_Shdr Header; }; +template +class GdbIndexSection final : public OutputSectionBase { + const unsigned OffsetTypeSize = 4; + const unsigned CuListOffset = 6 * OffsetTypeSize; + const unsigned CompilationUnitSize = 16; + const unsigned AddressEntrySize = 16 + OffsetTypeSize; + const unsigned SymTabEntrySize = 2 * OffsetTypeSize; + +public: + GdbIndexSection(); + void finalize() override; + void writeTo(uint8_t *Buf) override; + + // Stores pointer to .debug_info output section. + OutputSectionBase *DebugInfoSec = nullptr; + + // Pairs of [CU Offset, CU length]. + std::vector> CompilationUnits; + +private: + bool parseDebugSections(); + + uint32_t CuTypesOffset; +}; + template class GotSection final : public OutputSectionBase { typedef OutputSectionBase Base; typedef typename ELFT::uint uintX_t; @@ -753,6 +779,7 @@ static DynamicSection *Dynamic; static EhFrameHeader *EhFrameHdr; static EhOutputSection *EhFrame; + static GdbIndexSection *GdbIndex; static GnuHashTableSection *GnuHashTab; static GotPltSection *GotPlt; static GotSection *Got; @@ -816,6 +843,7 @@ template DynamicSection *Out::Dynamic; template EhFrameHeader *Out::EhFrameHdr; template EhOutputSection *Out::EhFrame; +template GdbIndexSection *Out::GdbIndex; template GnuHashTableSection *Out::GnuHashTab; template GotPltSection *Out::GotPlt; template GotSection *Out::Got; Index: ELF/OutputSections.cpp =================================================================== --- ELF/OutputSections.cpp +++ ELF/OutputSections.cpp @@ -11,6 +11,7 @@ #include "Config.h" #include "EhFrame.h" #include "LinkerScript.h" +#include "SplitDebugInfo.h" #include "Strings.h" #include "SymbolTable.h" #include "Target.h" @@ -56,6 +57,67 @@ } template +GdbIndexSection::GdbIndexSection() + : OutputSectionBase(".gdb_index", SHT_PROGBITS, 0) {} + +template bool GdbIndexSection::parseDebugSections() { + if (!DebugInfoSec) { + error(".debug_info is required for building .gdb_index"); + return false; + } + std::vector *> &IS = + static_cast *>(DebugInfoSec)->Sections; + for (InputSection *I : IS) + DwarfInfoReader(*this, I).addToGdbIndex(); + return true; +} + +template void GdbIndexSection::finalize() { + if (!parseDebugSections()) + return; + + // GdbIndex header consist from version fields + // and 5 more fields with different kinds of offsets. + CuTypesOffset = CuListOffset + CompilationUnits.size() * CompilationUnitSize; + this->Header.sh_size = CuTypesOffset; +} + +template void GdbIndexSection::writeTo(uint8_t *Buf) { + // Write version. + write32le(Buf, 7); + Buf += 4; + + write32le(Buf, CuListOffset); + Buf += 4; + + // Offset of the types CU list. + write32le(Buf, CuTypesOffset); + Buf += 4; + + // Offset of the address area, the same as offset of the types CU list, + // as we dont support types CU lists, so it is empty. + write32le(Buf, CuTypesOffset); + Buf += 4; + + // Offset of the symbol table, the same as offset of the types CU list, + // we do not fill it yet. + write32le(Buf, CuTypesOffset); + Buf += 4; + + // Offset of the constant pool, the same as offset of the types CU list, + // we do not fill it yet. + write32le(Buf, CuTypesOffset); + Buf += 4; + + // Write the CU list. + for (std::pair CU : CompilationUnits) { + write64le(Buf, CU.first); + write64le(Buf + 8, CU.second); + Buf += 16; + } +} + +template GotPltSection::GotPltSection() : OutputSectionBase(".got.plt", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE) { this->Header.sh_addralign = Target->GotPltEntrySize; @@ -2093,6 +2155,11 @@ template class BuildIdHexstring; template class BuildIdHexstring; +template class GdbIndexSection; +template class GdbIndexSection; +template class GdbIndexSection; +template class GdbIndexSection; + template class OutputSectionFactory; template class OutputSectionFactory; template class OutputSectionFactory; Index: ELF/SplitDebugInfo.h =================================================================== --- ELF/SplitDebugInfo.h +++ ELF/SplitDebugInfo.h @@ -0,0 +1,119 @@ +//===- SplitDebugInfo.h --------------------------------------*- C++ -*-===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===-------------------------------------------------------------------===// + +#ifndef LLD_ELF_SPLIT_DEBUG_INFO_H +#define LLD_ELF_SPLIT_DEBUG_INFO_H + +#include "InputFiles.h" +#include "llvm/Object/ELF.h" + +namespace lld { +namespace elf { + +template class DwarfDieParser; +template class GdbIndexSection; +template class InputSection; + +// Helper class for extracting relocations data. +template class RelocMapper { + typedef typename ELFT::uint uintX_t; + typedef typename ELFT::Shdr Elf_Shdr; + typedef typename ELFT::Rel Elf_Rel; + typedef typename ELFT::Rela Elf_Rela; + typedef typename ELFT::Sym Elf_Sym; + + InputSection *Sec; + + const Elf_Shdr &RelSec; + const llvm::object::ELFFile &EObj; + + size_t Position = 0; + + uintX_t getNextRelocAddend(); + uintX_t getNextRelocOffset(); + uintX_t getNextRelocSymbolIndex(); + + template void advance(ArrayRef Rel, uintX_t Offset); + void advanceToOffset(uintX_t Offset); + +public: + RelocMapper(InputSection *Sec); + uintX_t lookupReloc(uintX_t RelOffset, uintX_t *TargetOffset); +}; + +struct AbbreviationCode { + AbbreviationCode(uint64_t Tag) : Tag(Tag){}; + uint64_t Tag; + std::vector> Attributes; // [Attr, Form] +}; + +// The abbreviations tables for all compilation units are contained in a +// separate object file section called ".debug_abbrev". +// Defines the abbreviation codes used by the skeleton .debug_info section. +template class DwarfAbbrevTable { + typedef typename ELFT::uint uintX_t; + typedef typename ELFT::Shdr Elf_Shdr; + +public: + void read(elf::ObjectFile *File, uintX_t Shndx, uintX_t Offset); + + AbbreviationCode *getEntry(uint32_t Code); + +protected: + ArrayRef Data; + uint32_t AbbrevOffset = (uint32_t)-1; + uintX_t AbbrevShndx = (uint32_t)-1; + llvm::DenseMap> Codes; +}; + +// Main class that is used to scan all .debug_info sections. +template class DwarfInfoReader { + typedef typename ELFT::uint uintX_t; + + ArrayRef Data; + GdbIndexSection &GdbIndex; + + elf::ObjectFile *PubObject = nullptr; + +public: + DwarfInfoReader(GdbIndexSection &GdbIndex, InputSection *Sec) + : GdbIndex(GdbIndex), DebugInfoSec(Sec) {} + void addToGdbIndex(); + + ArrayRef getDataAtOffset(uintX_t CuOffset, uintX_t Offset); + + DwarfAbbrevTable AbbrevTable; + uint8_t AddressSize; + InputSection *DebugInfoSec; +}; + +// DWARF uses a series of debugging information entries (DIEs) to define a +// low-level representation of a source program. Each debugging information +// entry consists of an identifying tag and a series of attributes. This class +// is used to parce DIEs. +template class DwarfDieParser { + typedef typename ELFT::uint uintX_t; + + const DwarfInfoReader &Reader; + RelocMapper &Mapper; + + uintX_t CuOffset; + uintX_t DieOffset; + +public: + DwarfDieParser(DwarfInfoReader &Reader, RelocMapper &Mapper, + uintX_t CuOffset, uintX_t DieOffset); + + AbbreviationCode *AbbreviationEntry; +}; + +} // namespace elf +} // namespace lld + +#endif Index: ELF/SplitDebugInfo.cpp =================================================================== --- ELF/SplitDebugInfo.cpp +++ ELF/SplitDebugInfo.cpp @@ -0,0 +1,330 @@ +//===- SplitDebugInfo.cpp -------------------------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// File contains classes for implementation of --gdb-index command line option. +// +// If that option is used, linker should emit a .gdb_index section that allows +// debugger to locate and read .dwo files, containing neccessary debug +// information. +// More information about implementation can be found in DWARF specification, +// latest version is available at http://dwarfstd.org. +// +// .gdb_index section format: +// (Information is based on/taken from +// https://sourceware.org/gdb/onlinedocs/gdb/Index-Section-Format.html (*)) +// +// A mapped index consists of several areas, laid out in order: +// 1) The file header. +// 2) "The CU (compilation unit) list. This is a sequence of pairs of 64-bit +// little-endian values, sorted by the CU offset. The first element in each +// pair is the offset of a CU in the .debug_info section. The second element +// in each pair is the length of that CU. References to a CU elsewhere in the +// map are done using a CU index, which is just the 0-based index into this +// table. Note that if there are type CUs, then conceptually CUs and type CUs +// form a single list for the purposes of CU indices."(*) +// 3) The types CU list. Depricated as .debug_types does not appear in the DWARF +// v5 specification. +// 4) The address area. The address area is a sequence of address +// entries, where each entrie contains low address, high address and CU +// index. +// 5) "The symbol table. This is an open-addressed hash table. The size of the +// hash table is always a power of 2. Each slot in the hash table consists of +// a pair of offset_type values. The first value is the offset of the +// symbol's name in the constant pool. The second value is the offset of the +// CU vector in the constant pool."(*) +// 6) "The constant pool. This is simply a bunch of bytes. It is organized so +// that alignment is correct: CU vectors are stored first, followed by +// strings." (*) +// +// For constructing the .gdb_index section following steps should be performed: +// 1) For file header nothing special should be done. It contains the offsets to +// the areas below. +// 2) Scan the compilation unit headers of the .debug_info sections to build a +// list of compilation units. +// 3) CU Types are no longer needed as DWARF skeleton type units never made it +// into the standard. lld does nothing to support parsing of .debug_types +// and generates empty types CU area in .gdb_index section. +// 4) Address area entries are extracted from DW_TAG_compile_unit DIEs of +// .debug_info sections. +// 5) For building the symbol table linker extracts the public names from the +// .debug_gnu_pubnames and .debug_gnu_pubtypes sections. Then it builds the +// hashtable in according to .gdb_index format specification. +// 6) Constant pool is populated at the same time as symbol table. +// +// Current version of implementation has 1, 2, 3 steps. So it writes .gdb_index +// header and list of compilation units. Since we so not plan to support types +// CU list area, it is also empty and so far is "implemented". +// Other data areas are not yet implemented. +//===----------------------------------------------------------------------===// + +#include "Error.h" +#include "OutputSections.h" +#include "SplitDebugInfo.h" + +#include "llvm/Object/ELF.h" +#include "llvm/Support/Dwarf.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/LEB128.h" + +using namespace llvm; +using namespace llvm::ELF; +using namespace llvm::object; +using namespace llvm::support::endian; +using namespace lld; +using namespace lld::elf; + +template +RelocMapper::RelocMapper(InputSection *Sec) + : Sec(Sec), RelSec(*Sec->RelocSections[0]), EObj(Sec->getFile()->getObj()) { +} + +template +typename ELFT::uint RelocMapper::lookupReloc(uintX_t RelOffset, + uintX_t *TargetOffset) { + advanceToOffset(RelOffset); + + if (RelOffset != getNextRelocOffset()) + return 0; + + elf::ObjectFile *File = Sec->getFile(); + const Elf_Shdr *SymTab = File->getSymbolTable(); + const Elf_Sym *Sym = + File->getObj().getSymbol(SymTab, getNextRelocSymbolIndex()); + + *TargetOffset = Sym->st_value + getNextRelocAddend(); + return Sym->st_shndx; +} + +template +typename ELFT::uint RelocMapper::getNextRelocAddend() { + if (RelSec.sh_type == SHT_RELA) + return EObj.relas(&RelSec)[Position].r_addend; + else + return 0; +} + +template +typename ELFT::uint RelocMapper::getNextRelocOffset() { + if (RelSec.sh_type == SHT_RELA) { + ArrayRef Relas = EObj.relas(&RelSec); + if (Position >= Relas.size()) + return (uintX_t)-1; + return Relas[Position].r_offset; + } + + ArrayRef Rels = EObj.rels(&RelSec); + if (Position >= Rels.size()) + return (uintX_t)-1; + return Rels[Position].r_offset; +} + +template +typename ELFT::uint RelocMapper::getNextRelocSymbolIndex() { + if (RelSec.sh_type == SHT_RELA) { + ArrayRef Relas = EObj.relas(&RelSec); + if (Position >= Relas.size()) + return (uintX_t)-1; + return Relas[Position].getSymbol(Config->Mips64EL); + } + + ArrayRef Rels = EObj.rels(&RelSec); + if (Position >= Rels.size()) + return (uintX_t)-1; + return Rels[Position].getSymbol(Config->Mips64EL); +} + +template +template +void RelocMapper::advance(ArrayRef Rel, uintX_t Offset) { + for (size_t E = Rel.size(); Position != E; ++Position) + if (Rel[Position].r_offset >= Offset) + break; +} + +template void RelocMapper::advanceToOffset(uintX_t Offset) { + if (RelSec.sh_type == SHT_RELA) + return advance(EObj.relas(&RelSec), Offset); + else + return advance(EObj.rels(&RelSec), Offset); +} + +template +void DwarfAbbrevTable::read(elf::ObjectFile *File, uintX_t Shndx, + uintX_t Offset) { + // Multiple debugging information entries may share the same abbreviation + // table entry. Each compilation unit is associated with a particular + // abbreviation table, but multiple compilation units may share the same + // table. If we already read this table then just return. + if (AbbrevOffset == Offset && this->AbbrevShndx == Shndx) + return; + AbbrevOffset = Offset; + AbbrevShndx = Shndx; + + if (InputSectionBase *Sec = File->getSection(AbbrevShndx)) + Data = Sec->Data; + else + fatal("error retriving .debug_abbrev section"); +} + +template +AbbreviationCode *DwarfAbbrevTable::getEntry(uint32_t Code) { + auto It = Codes.find(Code); + if (It != Codes.end()) + return It->second.get(); + + // Read and store abbrev code definitions until we find the + // one we're looking for. + while (true) { + // Each declaration begins with an unsigned LEB128 number representing the + // abbreviation code itself. + unsigned N; + uint64_t NextCode = decodeULEB128(Data.data(), &N); + if (NextCode == 0) + return nullptr; + Data = Data.drop_front(N); + + // The abbreviation code is followed by another + // unsigned LEB128 number that encodes the entry痴 tag. + uint64_t Tag = decodeULEB128(Data.data(), &N); + Data = Data.drop_front(N); + + // Following the tag encoding is a 1 byte value that determines whether a + // debugging information entry using this abbreviation has child entries. + Data = Data.drop_front(1); + + std::unique_ptr NewCode = + std::make_unique(Tag); + while (true) { + uint64_t Attr = decodeULEB128(Data.data(), &N); + Data = Data.drop_front(N); + uint64_t Form = decodeULEB128(Data.data(), &N); + Data = Data.drop_front(N); + + // The series of attribute specifications ends with an entry containing 0 + // for the name and 0 for the form. + if (Attr == 0 && Form == 0) + break; + NewCode->Attributes.push_back(std::make_pair(Attr, Form)); + } + + AbbreviationCode *Ret = NewCode.get(); + Codes[Tag].swap(NewCode); + if (NextCode == Code) + return Ret; + } + + return nullptr; +} + +// For each compilation unit compiled with a DWARF producer, a contribution is +// made to the .debug_info section of the object file. Each such contribution +// consists of a compilation unit header followed by a single +// DW_TAG_compile_unit. (Refer to "DWARF Debugging Information Format V4", 7.5 +// Format of Debugging Information). +// Method is used to parse .debug_info input sections to extract information +// needed to produce .gdb_index output section. +template void DwarfInfoReader::addToGdbIndex() { + const endianness E = ELFT::TargetEndianness; + if (DebugInfoSec->RelocSections.size() != 1) { + error(".debug_info should have single relocation section"); + return; + } + + RelocMapper RelocMapper(DebugInfoSec); + Data = DebugInfoSec->Data; + const uint8_t *PInfo = Data.begin(); + + while (PInfo < Data.end()) { + const uint8_t *CuStart = PInfo; + + // A 4-byte or 12-byte unsigned integer representing the length of the + // .debug_info contribution for that compilation unit, not including the + // length field itself. + uint32_t UnitLength = read(PInfo); + PInfo += sizeof(uint32_t); + if (UnitLength == (uint32_t)-1) + fatal("64-bits DWARF is not supported"); + + const uint8_t *CuEnd = PInfo + UnitLength; + + // A 2-byte unsigned integer representing the version of the DWARF + // information for the compilation unit. + uint16_t Version = read(PInfo); + if (Version != 4) + fatal("unsupported version of .debug_info"); + PInfo += sizeof(uint16_t); + + // Offset into the.debug_abbrev section.This offset associates the + // compilation unit with a particular set of debugging information entry + // abbreviations. 4 byte unsigned length for 32-bit DWARF. + uint32_t AbbrevOffset = read(PInfo); + + // Obtain a index of .debug_abbrev section. We use current offset + // in compilation unit that points to AbbrevOffset to search relocation + // and retrive target section index which is index of .debug_abbrev. + uintX_t AbbrevShndx = 0; + uintX_t RelocOffset = PInfo - Data.begin(); + AbbrevShndx = RelocMapper.lookupReloc(RelocOffset, &AbbrevShndx); + if (!AbbrevShndx) + fatal(".debug_abbrev section index not found"); + PInfo += sizeof(uint32_t); + + AddressSize = *PInfo++; + + // Retrive the .debug_abbrev table data. + AbbrevTable.read(DebugInfoSec->getFile(), AbbrevShndx, AbbrevOffset); + + uintX_t CuOffset = CuStart - Data.begin(); + + // When using -gsplit-dwarf, .debug_info section contains a single + // DW_TAG_compile_unit DIE, with no children. Here we create DIE parcer + // that reads abbreviation entry and list of atributes. + DwarfDieParser RootDie(*this, RelocMapper, CuOffset, PInfo - CuStart); + + // The abbreviation code 0 is reserved. Debugging information entries + // consisting of only the abbreviation code 0 are considered null entries, + // skip them. + if (RootDie.AbbreviationEntry && RootDie.AbbreviationEntry->Tag != 0) + GdbIndex.CompilationUnits.push_back( + std::make_pair(DebugInfoSec->OutSecOff + CuOffset, CuEnd - CuStart)); + + PInfo = CuEnd; + } +} + +template +ArrayRef DwarfInfoReader::getDataAtOffset(uintX_t CuOffset, + uintX_t Offset) { + return Data.drop_front(CuOffset + Offset); +} + +template +DwarfDieParser::DwarfDieParser(DwarfInfoReader &Reader, + RelocMapper &Mapper, + uintX_t CuOffset, uintX_t DieOffset) + : Reader(Reader), Mapper(Mapper), CuOffset(CuOffset), DieOffset(DieOffset) { + ArrayRef Data = Reader.getDataAtOffset(CuOffset, DieOffset); + + // Each debugging information entry begins with an unsigned LEB128 number + // containing the abbreviation code for the entry. + // This code represents an entry within the abbreviations table + // associated with the compilation unit containing this entry. + unsigned N; + uint64_t C = decodeULEB128(Data.data(), &N); + if (!C) + fatal("the abbreviation codes 0 are considered null entries"); + + // Now get the entry from abbreviation table. + AbbreviationEntry = Reader.AbbrevTable.getEntry(C); +} + +template class elf::DwarfInfoReader; +template class elf::DwarfInfoReader; +template class elf::DwarfInfoReader; +template class elf::DwarfInfoReader; Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -138,6 +138,7 @@ std::unique_ptr> DynStrTab; std::unique_ptr> DynSymTab; std::unique_ptr> EhFrameHdr; + std::unique_ptr> GdbIndex; std::unique_ptr> GnuHashTab; std::unique_ptr> GotPlt; std::unique_ptr> HashTab; @@ -173,6 +174,8 @@ GnuHashTab.reset(new GnuHashTableSection); if (Config->SysvHash) HashTab.reset(new HashTableSection); + if (Config->GdbIndex) + GdbIndex.reset(new GdbIndexSection); StringRef S = Config->Rela ? ".rela.plt" : ".rel.plt"; GotPlt.reset(new GotPltSection); RelaPlt.reset(new RelocationSection(S, false /*Sort*/)); @@ -200,6 +203,7 @@ Out::Dynamic = &Dynamic; Out::EhFrame = &EhFrame; Out::EhFrameHdr = EhFrameHdr.get(); + Out::GdbIndex = GdbIndex.get(); Out::GnuHashTab = GnuHashTab.get(); Out::Got = &Got; Out::GotPlt = GotPlt.get(); @@ -706,6 +710,9 @@ Out::InitArray = findSection(".init_array"); Out::FiniArray = findSection(".fini_array"); + if (Out::GdbIndex) + Out::GdbIndex->DebugInfoSec = findSection(".debug_info"); + // The linker needs to define SECNAME_start, SECNAME_end and SECNAME_stop // symbols for sections, so that the runtime can get the start and end // addresses of each section by section name. Add such symbols. @@ -839,6 +846,7 @@ // This order is not the same as the final output order // because we sort the sections using their attributes below. + Add(Out::GdbIndex); Add(Out::SymTab); Add(Out::ShStrTab); Add(Out::StrTab); Index: test/ELF/gdb-index.s =================================================================== --- test/ELF/gdb-index.s +++ test/ELF/gdb-index.s @@ -0,0 +1,48 @@ +## gdb-index-a.elf and gdb-index-b.elf are a test.o and test2.o renamed, +## were generated in a next way: +## test.cpp: +## double foo1; +## float bar1; +## void method1() {} +## int main() { return 0; } +## test2.cpp: +## double foo2; +## char method2() {} +## Compiled with: +## gcc -gsplit-dwarf -c test.cpp test2.cpp +## gcc version 5.3.1 20160413 +## Info about gdb-index: https://sourceware.org/gdb/onlinedocs/gdb/Index-Section-Format.html + +# REQUIRES: x86 +# RUN: ld.lld --gdb-index -e main %p/Inputs/gdb-index-a.elf %p/Inputs/gdb-index-b.elf -o %t +# RUN: llvm-dwarfdump -debug-dump=gdb_index %t | FileCheck %s +# RUN: llvm-objdump -d %t | FileCheck %s --check-prefix=DISASM + +# DISASM: Disassembly of section .text: +# DISASM-NEXT: _Z7method1v: +# DISASM-NEXT: 11000: +# DISASM-NEXT: 11001: +# DISASM-NEXT: 11004: +# DISASM-NEXT: 11005: +# DISASM-NEXT: 11006: +# DISASM: main: +# DISASM-NEXT: 11007: +# DISASM-NEXT: 11008: +# DISASM-NEXT: 1100b: +# DISASM-NEXT: 11010: +# DISASM-NEXT: 11011: +# DISASM: _Z7method2v: +# DISASM-NEXT: 11012: +# DISASM-NEXT: 11013: +# DISASM-NEXT: 11016: +# DISASM-NEXT: 11017: +# DISASM-NEXT: 11018: + +# CHECK: .gnu_index contents: +# CHECK-NEXT: Version = 7 +# CHECK: CU list offset = 0x18, has 2 entries: +# CHECK-NEXT: 0: Offset = 0x0, Length = 0x34 +# CHECK-NEXT: 1: Offset = 0x34, Length = 0x34 +# CHECK: Address area offset = 0x38, has 0 entries: +# CHECK: Symbol table offset = 0x38, size = 0, filled slots: +# CHECK: Constant pool offset = 0x38, has 0 CU vectors: