Skip to content

Commit

Permalink
[ELF] - Partial support of --gdb-index command line option (Part 1).
Browse files Browse the repository at this point in the history
In this patch partial gdb_index section is created. 
For costructing the .gdb_index section 6 steps should be performed (details are in
SplitDebugInfo.cpp file header), this patch do first 3:

Creates proper section header.
Fills list of compilation units.
Types CU list area is not supposed to be supported, so it is ignored and therefore
can be treated as implemented either.

Differential revision: https://reviews.llvm.org/D24706

llvm-svn: 284708
  • Loading branch information
George Rimar committed Oct 20, 2016
1 parent 656d821 commit 58fa524
Show file tree
Hide file tree
Showing 12 changed files with 262 additions and 0 deletions.
1 change: 1 addition & 0 deletions lld/ELF/CMakeLists.txt
Expand Up @@ -8,6 +8,7 @@ add_lld_library(lldELF
EhFrame.cpp
ELFCreator.cpp
Error.cpp
GdbIndex.cpp
ICF.cpp
InputFiles.cpp
InputSection.cpp
Expand Down
1 change: 1 addition & 0 deletions lld/ELF/Config.h
Expand Up @@ -100,6 +100,7 @@ struct Configuration {
bool ExportDynamic;
bool FatalWarnings;
bool GcSections;
bool GdbIndex;
bool GnuHash = false;
bool ICF;
bool Mips64EL = false;
Expand Down
1 change: 1 addition & 0 deletions lld/ELF/Driver.cpp
Expand Up @@ -468,6 +468,7 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
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);
Expand Down
103 changes: 103 additions & 0 deletions lld/ELF/GdbIndex.cpp
@@ -0,0 +1,103 @@
//===- 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"

#include "llvm/DebugInfo/DWARF/DWARFContext.h"

using namespace llvm;
using namespace llvm::object;

template <class ELFT>
std::vector<std::pair<typename ELFT::uint, typename ELFT::uint>>
lld::elf::readCuList(InputSection<ELFT> *DebugInfoSec) {
typedef typename ELFT::uint uintX_t;

std::unique_ptr<DWARFContext> Dwarf;
if (Expected<std::unique_ptr<object::ObjectFile>> 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<std::pair<uintX_t, uintX_t>> Ret;
for (std::unique_ptr<DWARFCompileUnit> &CU : Dwarf->compile_units())
Ret.push_back(
{DebugInfoSec->OutSecOff + CU->getOffset(), CU->getLength() + 4});
return Ret;
}

template std::vector<std::pair<uint32_t, uint32_t>>
lld::elf::readCuList<ELF32LE>(InputSection<ELF32LE> *);
template std::vector<std::pair<uint32_t, uint32_t>>
lld::elf::readCuList<ELF32BE>(InputSection<ELF32BE> *);
template std::vector<std::pair<uint64_t, uint64_t>>
lld::elf::readCuList<ELF64LE>(InputSection<ELF64LE> *);
template std::vector<std::pair<uint64_t, uint64_t>>
lld::elf::readCuList<ELF64BE>(InputSection<ELF64BE> *);
28 changes: 28 additions & 0 deletions lld/ELF/GdbIndex.h
@@ -0,0 +1,28 @@
//===- 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"

namespace lld {
namespace elf {

template <class ELFT> class InputSection;

template <class ELFT>
std::vector<std::pair<typename ELFT::uint, typename ELFT::uint>>
readCuList(InputSection<ELFT> *Sec);

} // namespace elf
} // namespace lld

#endif
3 changes: 3 additions & 0 deletions lld/ELF/Options.td
Expand Up @@ -86,6 +86,9 @@ def format: J<"format=">, MetaVarName<"<input-format>">,
def gc_sections: F<"gc-sections">,
HelpText<"Enable garbage collection of unused sections">;

def gdb_index: F<"gdb-index">,
HelpText<"Generate .gdb_index section">;

def hash_style: S<"hash-style">,
HelpText<"Specify hash style (sysv, gnu or both)">;

Expand Down
50 changes: 50 additions & 0 deletions lld/ELF/OutputSections.cpp
Expand Up @@ -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"
Expand Down Expand Up @@ -56,6 +57,50 @@ void OutputSectionBase<ELFT>::writeHeaderTo(Elf_Shdr *Shdr) {
*Shdr = Header;
}

template <class ELFT>
GdbIndexSection<ELFT>::GdbIndexSection()
: OutputSectionBase<ELFT>(".gdb_index", SHT_PROGBITS, 0) {}

template <class ELFT> void GdbIndexSection<ELFT>::parseDebugSections() {
std::vector<InputSection<ELFT> *> &IS =
static_cast<OutputSection<ELFT> *>(Out<ELFT>::DebugInfo)->Sections;

for (InputSection<ELFT> *I : IS)
readDwarf(I);
}

template <class ELFT>
void GdbIndexSection<ELFT>::readDwarf(InputSection<ELFT> *I) {
std::vector<std::pair<uintX_t, uintX_t>> CuList = readCuList(I);
CompilationUnits.insert(CompilationUnits.end(), CuList.begin(), CuList.end());
}

template <class ELFT> void GdbIndexSection<ELFT>::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 <class ELFT> void GdbIndexSection<ELFT>::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<uintX_t, uintX_t> CU : CompilationUnits) {
write64le(Buf, CU.first);
write64le(Buf + 8, CU.second);
Buf += 16;
}
}

template <class ELFT>
GotPltSection<ELFT>::GotPltSection()
: OutputSectionBase<ELFT>(".got.plt", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE) {
Expand Down Expand Up @@ -2058,6 +2103,11 @@ template class BuildIdHexstring<ELF32BE>;
template class BuildIdHexstring<ELF64LE>;
template class BuildIdHexstring<ELF64BE>;

template class GdbIndexSection<ELF32LE>;
template class GdbIndexSection<ELF32BE>;
template class GdbIndexSection<ELF64LE>;
template class GdbIndexSection<ELF64BE>;

template class OutputSectionFactory<ELF32LE>;
template class OutputSectionFactory<ELF32BE>;
template class OutputSectionFactory<ELF64LE>;
Expand Down
30 changes: 30 additions & 0 deletions lld/ELF/OutputSections.h
Expand Up @@ -11,6 +11,7 @@
#define LLD_ELF_OUTPUT_SECTIONS_H

#include "Config.h"
#include "GdbIndex.h"
#include "Relocations.h"

#include "lld/Core/LLVM.h"
Expand Down Expand Up @@ -131,6 +132,31 @@ template <class ELFT> class OutputSectionBase {
uintX_t LMAOffset = 0;
};

template <class ELFT>
class GdbIndexSection final : public OutputSectionBase<ELFT> {
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<std::pair<uintX_t, uintX_t>> CompilationUnits;

private:
void parseDebugSections();
void readDwarf(InputSection<ELFT> *I);

uint32_t CuTypesOffset;
};

template <class ELFT> class GotSection final : public OutputSectionBase<ELFT> {
typedef OutputSectionBase<ELFT> Base;
typedef typename ELFT::uint uintX_t;
Expand Down Expand Up @@ -768,6 +794,7 @@ template <class ELFT> struct Out {
static DynamicSection<ELFT> *Dynamic;
static EhFrameHeader<ELFT> *EhFrameHdr;
static EhOutputSection<ELFT> *EhFrame;
static GdbIndexSection<ELFT> *GdbIndex;
static GnuHashTableSection<ELFT> *GnuHashTab;
static GotPltSection<ELFT> *GotPlt;
static GotSection<ELFT> *Got;
Expand All @@ -789,6 +816,7 @@ template <class ELFT> struct Out {
static VersionTableSection<ELFT> *VerSym;
static VersionNeedSection<ELFT> *VerNeed;
static Elf_Phdr *TlsPhdr;
static OutputSectionBase<ELFT> *DebugInfo;
static OutputSectionBase<ELFT> *ElfHeader;
static OutputSectionBase<ELFT> *ProgramHeaders;

Expand Down Expand Up @@ -837,6 +865,7 @@ template <class ELFT> BuildIdSection<ELFT> *Out<ELFT>::BuildId;
template <class ELFT> DynamicSection<ELFT> *Out<ELFT>::Dynamic;
template <class ELFT> EhFrameHeader<ELFT> *Out<ELFT>::EhFrameHdr;
template <class ELFT> EhOutputSection<ELFT> *Out<ELFT>::EhFrame;
template <class ELFT> GdbIndexSection<ELFT> *Out<ELFT>::GdbIndex;
template <class ELFT> GnuHashTableSection<ELFT> *Out<ELFT>::GnuHashTab;
template <class ELFT> GotPltSection<ELFT> *Out<ELFT>::GotPlt;
template <class ELFT> GotSection<ELFT> *Out<ELFT>::Got;
Expand All @@ -858,6 +887,7 @@ template <class ELFT> VersionDefinitionSection<ELFT> *Out<ELFT>::VerDef;
template <class ELFT> VersionTableSection<ELFT> *Out<ELFT>::VerSym;
template <class ELFT> VersionNeedSection<ELFT> *Out<ELFT>::VerNeed;
template <class ELFT> typename ELFT::Phdr *Out<ELFT>::TlsPhdr;
template <class ELFT> OutputSectionBase<ELFT> *Out<ELFT>::DebugInfo;
template <class ELFT> OutputSectionBase<ELFT> *Out<ELFT>::ElfHeader;
template <class ELFT> OutputSectionBase<ELFT> *Out<ELFT>::ProgramHeaders;
template <class ELFT> OutputSectionBase<ELFT> *Out<ELFT>::PreinitArray;
Expand Down
7 changes: 7 additions & 0 deletions lld/ELF/Writer.cpp
Expand Up @@ -151,6 +151,7 @@ template <class ELFT> void elf::writeResult() {
std::unique_ptr<StringTableSection<ELFT>> DynStrTab;
std::unique_ptr<SymbolTableSection<ELFT>> DynSymTab;
std::unique_ptr<EhFrameHeader<ELFT>> EhFrameHdr;
std::unique_ptr<GdbIndexSection<ELFT>> GdbIndex;
std::unique_ptr<GnuHashTableSection<ELFT>> GnuHashTab;
std::unique_ptr<GotPltSection<ELFT>> GotPlt;
std::unique_ptr<HashTableSection<ELFT>> HashTab;
Expand Down Expand Up @@ -186,6 +187,8 @@ template <class ELFT> void elf::writeResult() {
GnuHashTab.reset(new GnuHashTableSection<ELFT>);
if (Config->SysvHash)
HashTab.reset(new HashTableSection<ELFT>);
if (Config->GdbIndex)
GdbIndex.reset(new GdbIndexSection<ELFT>);
StringRef S = Config->Rela ? ".rela.plt" : ".rel.plt";
GotPlt.reset(new GotPltSection<ELFT>);
RelaPlt.reset(new RelocationSection<ELFT>(S, false /*Sort*/));
Expand Down Expand Up @@ -213,6 +216,7 @@ template <class ELFT> void elf::writeResult() {
Out<ELFT>::Dynamic = &Dynamic;
Out<ELFT>::EhFrame = &EhFrame;
Out<ELFT>::EhFrameHdr = EhFrameHdr.get();
Out<ELFT>::GdbIndex = GdbIndex.get();
Out<ELFT>::GnuHashTab = GnuHashTab.get();
Out<ELFT>::Got = &Got;
Out<ELFT>::GotPlt = GotPlt.get();
Expand Down Expand Up @@ -771,6 +775,7 @@ template <class ELFT> void Writer<ELFT>::sortSections() {

// Create output section objects and add them to OutputSections.
template <class ELFT> void Writer<ELFT>::finalizeSections() {
Out<ELFT>::DebugInfo = findSection(".debug_info");
Out<ELFT>::PreinitArray = findSection(".preinit_array");
Out<ELFT>::InitArray = findSection(".init_array");
Out<ELFT>::FiniArray = findSection(".fini_array");
Expand Down Expand Up @@ -902,6 +907,8 @@ template <class ELFT> void Writer<ELFT>::addPredefinedSections() {

// This order is not the same as the final output order
// because we sort the sections using their attributes below.
if (Out<ELFT>::GdbIndex && Out<ELFT>::DebugInfo)
Add(Out<ELFT>::GdbIndex);
Add(Out<ELFT>::SymTab);
Add(Out<ELFT>::ShStrTab);
Add(Out<ELFT>::StrTab);
Expand Down
Binary file added lld/test/ELF/Inputs/gdb-index-a.elf
Binary file not shown.
Binary file added lld/test/ELF/Inputs/gdb-index-b.elf
Binary file not shown.
38 changes: 38 additions & 0 deletions lld/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:

0 comments on commit 58fa524

Please sign in to comment.