[PATCH] D11612: [lld][ELF2] Apply relocations.
Rafael Espíndola via llvm-commits
llvm-commits at lists.llvm.org
Wed Aug 26 13:49:13 PDT 2015
Hacked to pass all tests.
Trying to merge now.
On 26 August 2015 at 16:35, Rafael Espíndola <rafael.espindola at gmail.com> wrote:
> elf2/basic64be.s is still failing, looking.
>
> On 26 August 2015 at 16:26, Rafael Espíndola <rafael.espindola at gmail.com> wrote:
>> The attached patch fixes the build and runs git-clang-format.
>>
>> On 26 August 2015 at 16:23, Rafael Espíndola <rafael.espindola at gmail.com> wrote:
>>> This doesn't even compile:
>>>
>>> /home/espindola/llvm/llvm/tools/lld/ELF/Symbols.h:135:12: error: use
>>> of undeclared identifier 'Sym'
>>> return Sym.st_value;
>>>
>>> On 26 August 2015 at 02:09, Rui Ueyama <ruiu at google.com> wrote:
>>>> ruiu added a comment.
>>>>
>>>> Rafael should be the person who signs off.
>>>>
>>>>
>>>> http://reviews.llvm.org/D11612
>>>>
>>>>
>>>>
-------------- next part --------------
diff --git a/ELF/Chunks.cpp b/ELF/Chunks.cpp
index 77b8fa1..c3e4849 100644
--- a/ELF/Chunks.cpp
+++ b/ELF/Chunks.cpp
@@ -9,6 +9,7 @@
#include "Chunks.h"
#include "Error.h"
+#include "InputFiles.h"
using namespace llvm;
using namespace llvm::ELF;
@@ -17,23 +18,21 @@ using namespace lld;
using namespace lld::elf2;
template <class ELFT>
-SectionChunk<ELFT>::SectionChunk(object::ELFFile<ELFT> *Obj,
- const Elf_Shdr *Header)
- : Obj(Obj), Header(Header) {
-}
+SectionChunk<ELFT>::SectionChunk(ObjectFile<ELFT> *F, const Elf_Shdr *Header)
+ : File(F), OS(nullptr), Header(Header) {}
template <class ELFT> void SectionChunk<ELFT>::writeTo(uint8_t *Buf) {
if (Header->sh_type == SHT_NOBITS)
return;
// Copy section contents from source object file to output file.
- ArrayRef<uint8_t> Data = *Obj->getSectionContents(Header);
+ ArrayRef<uint8_t> Data = *File->getObj()->getSectionContents(Header);
memcpy(Buf + OutputSectionOff, Data.data(), Data.size());
// FIXME: Relocations
}
template <class ELFT> StringRef SectionChunk<ELFT>::getSectionName() const {
- ErrorOr<StringRef> Name = Obj->getSectionName(Header);
+ ErrorOr<StringRef> Name = File->getObj()->getSectionName(Header);
error(Name);
return *Name;
}
diff --git a/ELF/Chunks.h b/ELF/Chunks.h
index 3b9274c..f428a72 100644
--- a/ELF/Chunks.h
+++ b/ELF/Chunks.h
@@ -17,6 +17,7 @@ namespace lld {
namespace elf2 {
template <class ELFT> class ObjectFile;
+template <class ELFT> class OutputSection;
// A chunk corresponding a section of an input file.
template <class ELFT> class SectionChunk {
@@ -26,7 +27,7 @@ template <class ELFT> class SectionChunk {
typedef typename llvm::object::ELFFile<ELFT>::uintX_t uintX_t;
public:
- SectionChunk(llvm::object::ELFFile<ELFT> *Obj, const Elf_Shdr *Header);
+ SectionChunk(ObjectFile<ELFT> *F, const Elf_Shdr *Header);
// Returns the size of this chunk (even if this is a common or BSS.)
size_t getSize() const { return Header->sh_size; }
@@ -37,6 +38,9 @@ public:
StringRef getSectionName() const;
const Elf_Shdr *getSectionHdr() const { return Header; }
+ ObjectFile<ELFT> *getFile() { return File; }
+ void setOutputSection(OutputSection<ELFT> *S) { OS = S; }
+ OutputSection<ELFT> *getOutputSection() { return OS; }
// The writer sets and uses the addresses.
uint64_t getOutputSectionOff() { return OutputSectionOff; }
@@ -48,8 +52,11 @@ private:
// to. The writer sets a value.
uint64_t OutputSectionOff = 0;
- // A file this chunk was created from.
- llvm::object::ELFFile<ELFT> *Obj;
+ // The file this chunk was created from.
+ ObjectFile<ELFT> *File;
+
+ // The OutputSection this is assigned to.
+ OutputSection<ELFT> *OS;
const Elf_Shdr *Header;
};
diff --git a/ELF/InputFiles.cpp b/ELF/InputFiles.cpp
index 5e7d4d1..3541ab7 100644
--- a/ELF/InputFiles.cpp
+++ b/ELF/InputFiles.cpp
@@ -40,22 +40,33 @@ template <class ELFT> void elf2::ObjectFile<ELFT>::parse() {
template <class ELFT> void elf2::ObjectFile<ELFT>::initializeChunks() {
uint64_t Size = ELFObj->getNumSections();
Chunks.reserve(Size);
+ SparseChunks.reserve(Size);
for (const Elf_Shdr &Sec : ELFObj->sections()) {
+ SectionChunk<ELFT> *SC = nullptr;
switch (Sec.sh_type) {
case SHT_SYMTAB:
Symtab = &Sec;
break;
case SHT_STRTAB:
case SHT_NULL:
+ break;
case SHT_RELA:
- case SHT_REL:
+ case SHT_REL: {
+ auto RelocatedSec = ELFObj->getSection(Sec.sh_info);
+ if (!RelocatedSec)
+ error(RelocatedSec);
+ RelocMap.emplace_back(*RelocatedSec, &Sec);
break;
+ }
default:
- auto *C = new (Alloc) SectionChunk<ELFT>(this->getObj(), &Sec);
- Chunks.push_back(C);
+ SC = new (Alloc) SectionChunk<ELFT>(this, &Sec);
+ Chunks.push_back(SC);
break;
}
+ SparseChunks.push_back(SC);
}
+ assert(SparseChunks.size() == Size && "Not all chunks added to SparseChunks");
+ std::sort(RelocMap.begin(), RelocMap.end());
}
template <class ELFT> void elf2::ObjectFile<ELFT>::initializeSymbols() {
@@ -69,10 +80,19 @@ template <class ELFT> void elf2::ObjectFile<ELFT>::initializeSymbols() {
uint32_t FirstNonLocal = Symtab->sh_info;
if (FirstNonLocal > NumSymbols)
error("Invalid sh_info in symbol table");
+ Elf_Sym_Range LocalSyms =
+ llvm::make_range(Syms.begin() + 1, Syms.begin() + FirstNonLocal);
Syms = llvm::make_range(Syms.begin() + FirstNonLocal, Syms.end());
SymbolBodies.reserve(NumSymbols);
- for (const Elf_Sym &Sym : Syms)
- SymbolBodies.push_back(createSymbolBody(StringTable, &Sym));
+ IndexedSymbolBodies.reserve(NumSymbols);
+ IndexedSymbolBodies.push_back(nullptr); // Skip the null entry.
+ for (const Elf_Sym &Sym : LocalSyms)
+ IndexedSymbolBodies.push_back(createSymbolBody(StringTable, &Sym));
+ for (const Elf_Sym &Sym : Syms) {
+ SymbolBody *S = createSymbolBody(StringTable, &Sym);
+ SymbolBodies.push_back(S);
+ IndexedSymbolBodies.push_back(S);
+ }
}
template <class ELFT>
@@ -84,10 +104,14 @@ SymbolBody *elf2::ObjectFile<ELFT>::createSymbolBody(StringRef StringTable,
switch (Sym->getBinding()) {
default:
error("unexpected binding");
+ case STB_LOCAL:
+ return new (Alloc)
+ DefinedRegular<ELFT>(Name, *Sym, SparseChunks[Sym->st_shndx]);
case STB_GLOBAL:
if (Sym->isUndefined())
return new (Alloc) Undefined<ELFT>(Name, *Sym);
- return new (Alloc) DefinedRegular<ELFT>(Name, *Sym);
+ return new (Alloc)
+ DefinedRegular<ELFT>(Name, *Sym, SparseChunks[Sym->st_shndx]);
case STB_WEAK:
if (Sym->isUndefined())
return new (Alloc) UndefinedWeak<ELFT>(Name, *Sym);
diff --git a/ELF/InputFiles.h b/ELF/InputFiles.h
index b916789..9bdfe96 100644
--- a/ELF/InputFiles.h
+++ b/ELF/InputFiles.h
@@ -11,6 +11,9 @@
#define LLD_ELF_INPUT_FILES_H
#include "Chunks.h"
+#include "Error.h"
+#include "Symbols.h"
+
#include "lld/Core/LLVM.h"
#include "llvm/Object/ELF.h"
@@ -67,6 +70,21 @@ template <class ELFT> class ObjectFile : public ObjectFileBase {
typedef typename llvm::object::ELFFile<ELFT>::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 {
+ // Pointer comparison is stable here as all pointers in a given RelocMap
+ // point to a single memory mapped file.
+ return std::tie(Section, RelocSection) <
+ std::tie(Other.Section, Other.RelocSection);
+ }
+ };
+
bool isCompatibleWith(const ObjectFileBase &Other) const override;
static Kind getKind() {
@@ -84,12 +102,24 @@ public:
explicit ObjectFile(MemoryBufferRef M) : ObjectFileBase(getKind(), M) {}
void parse() override;
+ const std::vector<RelocMapInfo> &getRelocations() const { return RelocMap; }
+
+ SymbolBody *getSymbolBody(uint32_t SymbolIndex) {
+ if (SymbolIndex >= IndexedSymbolBodies.size())
+ error("Invalid symbol table index.");
+ SymbolBody *S = IndexedSymbolBodies[SymbolIndex];
+ if (S)
+ return S->getReplacement();
+ return nullptr;
+ }
// Returns the underying ELF file.
llvm::object::ELFFile<ELFT> *getObj() const { return ELFObj.get(); }
ArrayRef<SectionChunk<ELFT> *> getChunks() { return Chunks; }
+ const Elf_Shdr *getSymtab() const { return Symtab; }
+
private:
void initializeChunks();
void initializeSymbols();
@@ -102,6 +132,16 @@ private:
std::vector<SectionChunk<ELFT> *> Chunks;
const Elf_Shdr *Symtab = nullptr;
+ // Section index to SectionChunk map. Unmapped sections are nullptr.
+ std::vector<SectionChunk<ELFT> *> SparseChunks;
+
+ // Symbol bodies indexed the same as the symbol table.
+ std::vector<SymbolBody *> IndexedSymbolBodies;
+
+ // This vector contains a sorted list of <section, relocation section that
+ // applies to section> pairs. This is used to quickly find and iterate over
+ // the relocations for a given section.
+ std::vector<RelocMapInfo> RelocMap;
};
} // namespace elf2
diff --git a/ELF/Symbols.cpp b/ELF/Symbols.cpp
index 095940c..bb89a3f 100644
--- a/ELF/Symbols.cpp
+++ b/ELF/Symbols.cpp
@@ -43,3 +43,12 @@ int SymbolBody::compare(SymbolBody *Other) {
}
llvm_unreachable("unknown symbol kind");
}
+
+namespace lld {
+namespace elf2 {
+template class DefinedRegular<llvm::object::ELF32LE>;
+template class DefinedRegular<llvm::object::ELF32BE>;
+template class DefinedRegular<llvm::object::ELF64LE>;
+template class DefinedRegular<llvm::object::ELF64BE>;
+}
+}
diff --git a/ELF/Symbols.h b/ELF/Symbols.h
index 82bf354..7a8fb3f 100644
--- a/ELF/Symbols.h
+++ b/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"
@@ -60,6 +62,7 @@ public:
// has chosen the object among other objects having the same name,
// you can access P->Backref->Body to get the resolver's result.
void setBackref(Symbol *P) { Backref = P; }
+ SymbolBody *getReplacement() { return Backref ? Backref->Body : this; }
// Decides which symbol should "win" in the symbol table, this or
// the Other. Returns 1 if this wins, -1 if the Other wins, or 0 if
@@ -121,12 +124,21 @@ template <class ELFT> class DefinedRegular : public Defined<ELFT> {
typedef typename Base::Elf_Sym Elf_Sym;
public:
- explicit DefinedRegular(StringRef N, const Elf_Sym &Sym)
- : Defined<ELFT>(Base::DefinedRegularKind, N, Sym) {}
+ DefinedRegular(StringRef N, const Elf_Sym &Sym, SectionChunk<ELFT> *C)
+ : Defined<ELFT>(Base::DefinedRegularKind, N, Sym), Chunk(C) {}
static bool classof(const SymbolBody *S) {
return S->kind() == Base::DefinedRegularKind;
}
+
+ typename llvm::object::ELFFile<ELFT>::uintX_t getValue() const {
+ return this->Sym.st_value;
+ }
+
+ SectionChunk<ELFT> *getChunk() const { return Chunk; }
+
+private:
+ SectionChunk<ELFT> *Chunk;
};
template <class ELFT> class DefinedWeak : public Defined<ELFT> {
diff --git a/ELF/Writer.cpp b/ELF/Writer.cpp
index 9fe3270..42dbcc2 100644
--- a/ELF/Writer.cpp
+++ b/ELF/Writer.cpp
@@ -10,12 +10,14 @@
#include "Chunks.h"
#include "Config.h"
#include "Error.h"
+#include "Symbols.h"
#include "SymbolTable.h"
#include "Writer.h"
#include "Symbols.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/Support/FileOutputBuffer.h"
+#include "llvm/Support/raw_ostream.h"
using namespace llvm;
using namespace llvm::ELF;
@@ -26,7 +28,8 @@ using namespace lld::elf2;
static const int PageSize = 4096;
-namespace {
+namespace lld {
+namespace elf2 {
// OutputSection represents a section in an output file. It's a
// container of chunks. OutputSection and Chunk are 1:N relationship.
// Chunks cannot belong to more than one OutputSections. The writer
@@ -45,6 +48,7 @@ public:
Header.sh_flags = sh_flags;
}
void setVA(uintX_t VA) { Header.sh_addr = VA; }
+ uintX_t getVA() { return Header.sh_addr; }
void setFileOffset(uintX_t Off) { Header.sh_offset = Off; }
template <endianness E>
void writeHeaderTo(typename ELFFile<ELFType<E, Is64Bits>>::Elf_Shdr *SHdr);
@@ -79,7 +83,10 @@ public:
private:
std::vector<SectionChunk<ELFT> *> Chunks;
};
+} // end namespace elf2
+} // end namespace lld
+namespace {
template <bool Is64Bits>
class StringTableSection final : public OutputSectionBase<Is64Bits> {
llvm::StringTableBuilder &StrTabBuilder;
@@ -200,9 +207,57 @@ void OutputSection<ELFT>::addChunk(SectionChunk<ELFT> *C) {
this->Header.sh_size = Off;
}
+template <class ELFT>
+typename llvm::object::ELFFile<ELFT>::uintX_t
+getSymVA(DefinedRegular<ELFT> *DR) {
+ SectionChunk<ELFT> *SC = DR->getChunk();
+ OutputSection<ELFT> *OS = SC->getOutputSection();
+ return OS->getVA() + SC->getOutputSectionOff() + DR->getValue();
+}
+
template <class ELFT> void OutputSection<ELFT>::writeTo(uint8_t *Buf) {
- for (SectionChunk<ELFT> *C : Chunks)
+ for (SectionChunk<ELFT> *C : Chunks) {
C->writeTo(Buf);
+ const std::vector<typename ObjectFile<ELFT>::RelocMapInfo> &Relocs =
+ C->getFile()->getRelocations();
+ auto Hdr = C->getSectionHdr();
+ auto EObj = C->getFile()->getObj();
+ uint8_t *Base = Buf + C->getOutputSectionOff();
+ // Iterate over all relocation sections that apply to this section.
+ for (auto I = std::lower_bound(
+ Relocs.begin(), Relocs.end(),
+ typename ObjectFile<ELFT>::RelocMapInfo(Hdr, nullptr)),
+ E = Relocs.end();
+ I != E && I->Section == Hdr; ++I) {
+ // Only support RELA for now.
+ if (I->RelocSection->sh_type != SHT_RELA)
+ continue;
+ for (auto &RI : EObj->relas(I->RelocSection)) {
+ SymbolBody *Body =
+ C->getFile()->getSymbolBody(RI.getSymbol(EObj->isMips64EL()));
+ if (!Body)
+ continue;
+ // Skip undefined weak for now.
+ if (isa<UndefinedWeak<ELFT>>(Body))
+ continue;
+ if (!isa<DefinedRegular<ELFT>>(Body))
+ error(Twine("Can't relocate symbol ") + Body->getName());
+ uintX_t Offset = RI.r_offset;
+ uint32_t Type = RI.getType(EObj->isMips64EL());
+ uintX_t P = this->getVA() + C->getOutputSectionOff();
+ uintX_t SymVA = getSymVA<ELFT>(cast<DefinedRegular<ELFT>>(Body));
+ switch (Type) {
+ case llvm::ELF::R_X86_64_PC32:
+ support::endian::write32le(Base + Offset,
+ SymVA + (RI.r_addend - (P + Offset)));
+ break;
+ default:
+ llvm::errs() << Twine("unrecognized reloc ") + Twine(Type) << '\n';
+ break;
+ }
+ }
+ }
+ }
}
template <bool Is64Bits>
@@ -304,6 +359,7 @@ template <class ELFT> void Writer<ELFT>::createSections() {
OutputSections.push_back(Sec);
}
Sec->addChunk(C);
+ C->setOutputSection(Sec);
}
}
}
diff --git a/test/elf2/relocation.s b/test/elf2/relocation.s
new file mode 100644
index 0000000..37c4431
--- /dev/null
+++ b/test/elf2/relocation.s
@@ -0,0 +1,15 @@
+// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t
+// RUN: lld -flavor gnu2 %t -o %t2
+// RUN: llvm-objdump -d %t2 | FileCheck %s
+// REQUIRES: x86
+
+
+.section .text,"ax", at progbits,unique,1
+.global _start
+_start:
+ call lulz
+
+.section .text,"ax", at progbits,unique,2
+lulz:
+
+// CHECK: e8 00 00 00 00 callq 0
More information about the llvm-commits
mailing list