Index: ELF/Chunks.h =================================================================== --- ELF/Chunks.h +++ ELF/Chunks.h @@ -16,7 +16,6 @@ namespace lld { namespace elf2 { -class Defined; template class ObjectFile; class OutputSection; @@ -79,10 +78,11 @@ typedef llvm::object::Elf_Rel_Impl Elf_Rel; public: - SectionChunk(llvm::object::ELFFile *Obj, const Elf_Shdr *Header); + SectionChunk(ObjectFile *F, const Elf_Shdr *Header); size_t getSize() const override { return Header->sh_size; } void writeTo(uint8_t *Buf) override; StringRef getSectionName() const override { return SectionName; } + ObjectFile *getFile() { return File; } const Elf_Shdr *getSectionHdr() const { return Header; } static bool classof(const Chunk *C) { @@ -91,7 +91,7 @@ private: // A file this chunk was created from. - llvm::object::ELFFile *Obj; + ObjectFile *File; const Elf_Shdr *Header; StringRef SectionName; Index: ELF/Chunks.cpp =================================================================== --- ELF/Chunks.cpp +++ ELF/Chunks.cpp @@ -9,6 +9,7 @@ #include "Chunks.h" #include "Driver.h" +#include "InputFiles.h" using namespace llvm; using namespace llvm::ELF; @@ -17,11 +18,11 @@ using namespace lld::elf2; template -SectionChunk::SectionChunk(object::ELFFile *Obj, +SectionChunk::SectionChunk(ObjectFile *F, const Elf_Shdr *Header) - : Chunk(SectionKind), Obj(Obj), Header(Header) { + : Chunk(SectionKind), File(F), Header(Header) { // Initialize SectionName. - ErrorOr Name = Obj->getSectionName(Header); + ErrorOr Name = F->getObj()->getSectionName(Header); error(Name); SectionName = *Name; @@ -32,7 +33,7 @@ if (Header->sh_type == SHT_NOBITS) return; // Copy section contents from source object file to output file. - ArrayRef Data = *Obj->getSectionContents(Header); + ArrayRef Data = *File->getObj()->getSectionContents(Header); memcpy(Buf + FileOff, Data.data(), Data.size()); // FIXME: Relocations Index: ELF/InputFiles.h =================================================================== --- ELF/InputFiles.h +++ ELF/InputFiles.h @@ -17,6 +17,7 @@ namespace elf2 { class SymbolBody; class Chunk; +template class SectionChunk; // The root class of input files. class InputFile { @@ -46,11 +47,32 @@ typedef typename llvm::object::ELFFile::Elf_Sym_Range Elf_Sym_Range; public: + struct RelocMapInfo { + RelocMapInfo(const Elf_Shdr *S, const Elf_Shdr *RS) + : Section(S), RelocSection(RS) {} + + const Elf_Shdr *Section; + const Elf_Shdr *RelocSection; + + bool operator<(const RelocMapInfo &Other) const { + return Section < Other.Section; + } + }; + explicit ObjectFile(MemoryBufferRef M) : InputFile(ObjectKind, M) {} static bool classof(const InputFile *F) { return F->kind() == ObjectKind; } void parse() override; ArrayRef getChunks() { return Chunks; } ArrayRef getSymbols() override { return SymbolBodies; } + const std::vector &getRelocations() const { return RelocMap; } + + SymbolBody *getSymbolBody(uint32_t SymbolIndex) { + // Skip null symbol. + SymbolIndex -= 1; + if (SymbolIndex >= SymbolBodies.size()) + llvm::report_fatal_error("Invalid symbol table index."); + return SymbolBodies[SymbolIndex]->getReplacement(); + } // Returns the underying ELF file. llvm::object::ELFFile *getObj() { return ELFObj.get(); } @@ -68,8 +90,16 @@ // chunks and non-section chunks for common symbols. std::vector Chunks; + // Section index to SectionChunk map. Unmapped sections are nullptr. + std::vector *> SparseChunks; + // List of all symbols referenced or defined by this file. std::vector SymbolBodies; + + // This vector contains a sorted list of pairs. This is used to quickly find and iterate over + // the relocations for a given section. + std::vector RelocMap; }; } // namespace elf2 Index: ELF/InputFiles.cpp =================================================================== --- ELF/InputFiles.cpp +++ ELF/InputFiles.cpp @@ -32,12 +32,20 @@ template void elf2::ObjectFile::initializeChunks() { uint64_t Size = ELFObj->getNumSections(); Chunks.reserve(Size); + SparseChunks.reserve(Size); for (const Elf_Shdr &Sec : ELFObj->sections()) { if (Sec.sh_flags & SHF_ALLOC) { - auto *C = new (Alloc) SectionChunk(this->getObj(), &Sec); + auto *C = new (Alloc) SectionChunk(this, &Sec); Chunks.push_back(C); + SparseChunks.push_back(C); + } else if (Sec.sh_type == SHT_RELA || Sec.sh_type == SHT_REL) { + RelocMap.emplace_back(*ELFObj->getSection(Sec.sh_info), &Sec); + SparseChunks.push_back(nullptr); + } else { + SparseChunks.push_back(nullptr); } } + std::stable_sort(RelocMap.begin(), RelocMap.end()); } template void elf2::ObjectFile::initializeSymbols() { @@ -65,7 +73,8 @@ StringRef Name = *NameOrErr; if (Sym->isUndefined()) return new (Alloc) Undefined(Name); - return new (Alloc) DefinedRegular(this, Sym); + return new (Alloc) + DefinedRegular(this, Sym, SparseChunks[Sym->st_shndx]); } namespace lld { Index: ELF/SymbolTable.h =================================================================== --- ELF/SymbolTable.h +++ ELF/SymbolTable.h @@ -16,7 +16,6 @@ namespace lld { namespace elf2 { -class Defined; struct Symbol; // SymbolTable is a bucket of all known symbols, including defined, Index: ELF/Symbols.h =================================================================== --- ELF/Symbols.h +++ ELF/Symbols.h @@ -10,6 +10,8 @@ #ifndef LLD_ELF_SYMBOLS_H #define LLD_ELF_SYMBOLS_H +#include "Chunks.h" + #include "lld/Core/LLVM.h" #include "llvm/Object/ELF.h" @@ -76,6 +78,7 @@ // The base class for any defined symbols, including absolute symbols, // etc. +template class Defined : public SymbolBody { public: Defined(Kind K, StringRef N = "") : SymbolBody(K, N) {} @@ -84,22 +87,29 @@ Kind K = S->kind(); return DefinedFirst <= K && K <= DefinedLast; } + // Returns the Virtual Address of this symbol. + uint64_t getVA() const; }; // Regular defined symbols read from object file symbol tables. -template class DefinedRegular : public Defined { +template class DefinedRegular : public Defined { typedef typename llvm::object::ELFFile::Elf_Sym Elf_Sym; public: - DefinedRegular(ObjectFile *F, const Elf_Sym *S); + DefinedRegular(ObjectFile *F, const Elf_Sym *S, SectionChunk *C); static bool classof(const SymbolBody *S) { return S->kind() == DefinedRegularKind; } + const Elf_Sym *getELFSymbol() const { return Sym; } + + uint64_t getVA() const { return Chunk->getVA() + Sym->st_value; } + SectionChunk *getChunk() const { return Chunk; } private: ObjectFile *File; const Elf_Sym *Sym; + SectionChunk *Chunk; }; // Undefined symbols. @@ -112,6 +122,17 @@ } }; +template +inline uint64_t Defined::getVA() const { + switch(kind()) { + case DefinedRegularKind: + return cast>(this)->getVA(); + case UndefinedKind: + llvm_unreachable("Cannot get the address for an undefined symbol."); + } + llvm_unreachable("unknown symbol kind"); +} + } // namespace elf2 } // namespace lld Index: ELF/Symbols.cpp =================================================================== --- ELF/Symbols.cpp +++ ELF/Symbols.cpp @@ -27,9 +27,10 @@ } template -DefinedRegular::DefinedRegular(ObjectFile *F, const Elf_Sym *S) +DefinedRegular::DefinedRegular(ObjectFile *F, const Elf_Sym *S, + SectionChunk *C) : Defined(DefinedRegularKind, getSymbolName(F->getObj(), S)), File(F), - Sym(S) { + Sym(S), Chunk(C) { IsExternal = S->isExternal(); } Index: ELF/Writer.h =================================================================== --- ELF/Writer.h +++ ELF/Writer.h @@ -51,6 +51,7 @@ private: void createSections(); void assignAddresses(); + void applyRelocations(); void openFile(StringRef OutputPath); void writeHeader(); void writeSections(); Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -10,6 +10,7 @@ #include "Writer.h" #include "Chunks.h" #include "Driver.h" +#include "Symbols.h" #include "llvm/ADT/DenseMap.h" using namespace llvm; @@ -29,8 +30,9 @@ createSections(); assignAddresses(); openFile(OutputPath); - writeHeader(); writeSections(); + applyRelocations(); + writeHeader(); error(Buffer->commit()); } @@ -166,6 +168,50 @@ } } +template void Writer::applyRelocations() { + uint8_t *Buf = Buffer->getBufferStart(); + for (OutputSection *Sec : OutputSections) { + for (Chunk *C : Sec->getChunks()) { + if (C->kind() != Chunk::SectionKind) + continue; + SectionChunk *SC = static_cast *>(C); + const std::vector::RelocMapInfo> &Relocs = + SC->getFile()->getRelocations(); + auto Hdr = SC->getSectionHdr(); + auto EObj = SC->getFile()->getObj(); + uint8_t *Base = Buf + SC->getFileOff(); + // Iterate over all relocation sections that apply to this section. + for (auto I = std::lower_bound( + Relocs.begin(), Relocs.end(), + typename ObjectFile::RelocMapInfo(Hdr, nullptr)), + E = Relocs.end(); + I != E && I->Section == Hdr; ++I) { + for (auto RI = EObj->rela_begin(I->RelocSection), + RE = EObj->rela_end(I->RelocSection); + RI != RE; ++RI) { + SymbolBody *Body = + SC->getFile() + ->getSymbolBody(RI->getSymbol(EObj->isMips64EL())) + ->getReplacement(); + uintX_t Offset = RI->r_offset; + uint32_t Type = RI->getType(EObj->isMips64EL()); + uintX_t P = SC->getVA(); + switch (Type) { + case llvm::ELF::R_X86_64_PC32: + support::endian::write32le(Base + Offset, + cast>(Body)->getVA() + + (RI->r_addend - (P + RI->r_offset))); + break; + default: + llvm::errs() << "unrecognized reloc " << Type << "\n"; + llvm::report_fatal_error("failed linking"); + } + } + } + } + } +} + namespace lld { namespace elf2 { template class Writer; Index: test/elf2/relocation.test =================================================================== --- /dev/null +++ test/elf2/relocation.test @@ -0,0 +1,74 @@ +# RUN: yaml2obj -format elf %s -docnum 1 -o %t1obj +# RUN: yaml2obj -format elf %s -docnum 2 -o %t2obj +# RUN: lld -flavor gnu2 %t1obj %t2obj -o %tout +# RUN: llvm-objdump -d %tout | FileCheck %s +# REQUIRES: x86 + +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: E800000000 + - Name: .rela.text + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .text + Relocations: + - Offset: 0x0000000000000001 + Symbol: lulz + Type: R_X86_64_PC32 + Addend: -4 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 +Symbols: + Global: + - Name: _start + Type: STT_FUNC + Section: .text + - Name: lulz +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: 5548C7C03C00000048C7C7010000000F05 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 +Symbols: + Global: + - Name: lulz + Type: STT_FUNC + Section: .text +... + +# CHECK: e8 03 00 00 00 callq 3