Index: ELF/CMakeLists.txt =================================================================== --- ELF/CMakeLists.txt +++ ELF/CMakeLists.txt @@ -11,6 +11,7 @@ ICF.cpp InputFiles.cpp InputSection.cpp + GdbIndex.cpp LTO.cpp LinkerScript.cpp MarkLive.cpp Index: ELF/Config.h =================================================================== --- ELF/Config.h +++ ELF/Config.h @@ -99,6 +99,7 @@ bool ExportDynamic; bool FatalWarnings; bool GcSections; + bool GdbIndex; bool GnuHash = false; bool ICF; bool Mips64EL = false; Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -452,6 +452,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/GdbIndex.h =================================================================== --- ELF/GdbIndex.h +++ ELF/GdbIndex.h @@ -0,0 +1,29 @@ +//===- GdbIndex.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_GDB_INDEX_H +#define LLD_ELF_GDB_INDEX_H + +#include "InputFiles.h" +#include "llvm/Object/ELF.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" + +namespace lld { +namespace elf { + +template class InputSection; + +template +std::vector> +readCuList(InputSection *Sec); + +} // namespace elf +} // namespace lld + +#endif Index: ELF/GdbIndex.cpp =================================================================== --- ELF/GdbIndex.cpp +++ ELF/GdbIndex.cpp @@ -0,0 +1,101 @@ +//===- GdbIndex.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 "GdbIndex.h" + +using namespace llvm; +using namespace llvm::object; + +template +std::vector> +lld::elf::readCuList(InputSection *DebugInfoSec) { + typedef typename ELFT::uint uintX_t; + + std::unique_ptr Dwarf; + if (Expected> Obj = + object::ObjectFile::createObjectFile(DebugInfoSec->getFile()->MB)) + Dwarf.reset(new DWARFContextInMemory(*Obj.get())); + + if (!Dwarf) { + error(getFilename(DebugInfoSec->getFile()) + + ": error creating DWARF context"); + return {}; + } + + std::vector> Ret; + for (const auto &CU : Dwarf->compile_units()) + Ret.push_back( + {DebugInfoSec->OutSecOff + CU->getOffset(), CU->getLength() + 4}); + return Ret; +} + +template std::vector> +lld::elf::readCuList(InputSection *Sec); +template std::vector> +lld::elf::readCuList(InputSection *Sec); +template std::vector> +lld::elf::readCuList(InputSection *Sec); +template std::vector> +lld::elf::readCuList(InputSection *Sec); 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 @@ -11,6 +11,7 @@ #define LLD_ELF_OUTPUT_SECTIONS_H #include "Config.h" +#include "GdbIndex.h" #include "Relocations.h" #include "lld/Core/LLVM.h" @@ -131,6 +132,31 @@ uintX_t LMAOffset = 0; }; +template +class GdbIndexSection final : public OutputSectionBase { + typedef typename ELFT::uint uintX_t; + + 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; + + // Pairs of [CU Offset, CU length]. + std::vector> CompilationUnits; + +private: + void parseDebugSections(); + void readDwarf(InputSection *I); + + uint32_t CuTypesOffset; +}; + template class GotSection final : public OutputSectionBase { typedef OutputSectionBase Base; typedef typename ELFT::uint uintX_t; @@ -765,6 +791,7 @@ static DynamicSection *Dynamic; static EhFrameHeader *EhFrameHdr; static EhOutputSection *EhFrame; + static GdbIndexSection *GdbIndex; static GnuHashTableSection *GnuHashTab; static GotPltSection *GotPlt; static GotSection *Got; @@ -786,6 +813,7 @@ static VersionTableSection *VerSym; static VersionNeedSection *VerNeed; static Elf_Phdr *TlsPhdr; + static OutputSectionBase *DebugInfo; static OutputSectionBase *ElfHeader; static OutputSectionBase *ProgramHeaders; @@ -834,6 +862,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; @@ -855,6 +884,7 @@ template VersionTableSection *Out::VerSym; template VersionNeedSection *Out::VerNeed; template typename ELFT::Phdr *Out::TlsPhdr; +template OutputSectionBase *Out::DebugInfo; template OutputSectionBase *Out::ElfHeader; template OutputSectionBase *Out::ProgramHeaders; template OutputSectionBase *Out::PreinitArray; Index: ELF/OutputSections.cpp =================================================================== --- ELF/OutputSections.cpp +++ ELF/OutputSections.cpp @@ -11,6 +11,7 @@ #include "Config.h" #include "EhFrame.h" #include "LinkerScript.h" +#include "GdbIndex.h" #include "Strings.h" #include "SymbolTable.h" #include "Target.h" @@ -57,6 +58,50 @@ } template +GdbIndexSection::GdbIndexSection() + : OutputSectionBase(".gdb_index", SHT_PROGBITS, 0) {} + +template void GdbIndexSection::parseDebugSections() { + std::vector *> &IS = + static_cast *>(Out::DebugInfo)->Sections; + + for (InputSection *I : IS) + readDwarf(I); +} + +template +void GdbIndexSection::readDwarf(InputSection *I) { + std::vector> CuList = readCuList(I); + CompilationUnits.insert(CompilationUnits.end(), CuList.begin(), CuList.end()); +} + +template void GdbIndexSection::finalize() { + parseDebugSections(); + + // 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) { + write32le(Buf, 7); // Write Version + write32le(Buf + 4, CuListOffset); // CU list offset + write32le(Buf + 8, CuTypesOffset); // Types CU list offset + write32le(Buf + 12, CuTypesOffset); // Address area offset + write32le(Buf + 16, CuTypesOffset); // Symbol table offset + write32le(Buf + 20, CuTypesOffset); // Constant pool offset + Buf += 24; + + // 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; @@ -2067,6 +2112,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/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -147,6 +147,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; @@ -182,6 +183,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*/)); @@ -209,6 +212,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(); @@ -767,6 +771,8 @@ // Create output section objects and add them to OutputSections. template void Writer::finalizeSections() { + Out::DebugInfo = findSection(".debug_info"); + Out::PreinitArray = findSection(".preinit_array"); Out::InitArray = findSection(".init_array"); Out::FiniArray = findSection(".fini_array"); @@ -898,6 +904,8 @@ // This order is not the same as the final output order // because we sort the sections using their attributes below. + if (Out::GdbIndex && Out::DebugInfo) + 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,38 @@ +## gdb-index-a.elf and gdb-index-b.elf are a test.o and test2.o renamed, +## were generated in this way: +## test.cpp: +## int main() { return 0; } +## test2.cpp: +## int main2() { return 0; } +## 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: main: +# DISASM-CHECK: 11000: 55 pushq %rbp +# DISASM-CHECK: 11001: 48 89 e5 movq %rsp, %rbp +# DISASM-CHECK: 11004: b8 00 00 00 00 movl $0, %eax +# DISASM-CHECK: 11009: 5d popq %rbp +# DISASM-CHECK: 1100a: c3 retq +# DISASM: _Z5main2v: +# DISASM-CHECK: 1100b: 55 pushq %rbp +# DISASM-CHECK: 1100c: 48 89 e5 movq %rsp, %rbp +# DISASM-CHECK: 1100f: b8 00 00 00 00 movl $0, %eax +# DISASM-CHECK: 11014: 5d popq %rbp +# DISASM-CHECK: 11015: c3 retq + +# 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: