Files
clang-p2996/lld/lib/ReaderWriter/ELF/SectionChunks.h
Shankar Easwaran 9af77a2cab [lld][ELF][All Archs] Addend is used by dynamic relocations
only if they are relative. This removes the FIXME when the
relocations are being emitted and checks if the relocation
is relative and only then populates the addend information.

I couldnt add a testcase for this as llvm-readobj lacks     
functionality of printing dynamic relocations.              

When the functionality is added, remove the commented lines
from elf/ifunc.test to test functionality.

llvm-svn: 182077
2013-05-17 05:10:30 +00:00

1251 lines
39 KiB
C++

//===- lib/ReaderWriter/ELF/SectionChunks.h -------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_READER_WRITER_ELF_SECTION_CHUNKS_H
#define LLD_READER_WRITER_ELF_SECTION_CHUNKS_H
#include "Chunk.h"
#include "Layout.h"
#include "TargetHandler.h"
#include "Writer.h"
#include "lld/Core/DefinedAtom.h"
#include "lld/Core/range.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/OwningPtr.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/Object/ELF.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ELF.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FileOutputBuffer.h"
namespace {
LLVM_ATTRIBUTE_UNUSED std::string kindOrUnknown(llvm::ErrorOr<std::string> k) {
if (k)
return *k;
return "<unknown>";
}
}
namespace lld {
namespace elf {
template <class> class MergedSections;
using namespace llvm::ELF;
template <class ELFT> class Segment;
/// \brief An ELF section.
template <class ELFT> class Section : public Chunk<ELFT> {
public:
Section(const ELFTargetInfo &ti, StringRef name,
typename Chunk<ELFT>::Kind k = Chunk<ELFT>::K_ELFSection)
: Chunk<ELFT>(name, k, ti), _parent(nullptr), _flags(0), _entSize(0),
_type(0), _link(0), _info(0), _segmentType(SHT_NULL) {}
/// \brief Modify the section contents before assigning virtual addresses
// or assigning file offsets
virtual void doPreFlight() {}
/// \brief Finalize the section contents before writing
virtual void finalize() {}
/// \brief Does this section have an output segment.
virtual bool hasOutputSegment() {
return false;
}
/// \brief Assign file offsets starting at offset.
virtual void assignOffsets(uint64_t offset) {}
/// \brief Assign virtual addresses starting at addr. Addr is modified to be
/// the next available virtual address.
virtual void assignVirtualAddress(uint64_t &addr) {}
uint64_t getFlags() const { return _flags; }
uint64_t getEntSize() const { return _entSize; }
uint32_t getType() const { return _type; }
uint32_t getLink() const { return _link; }
uint32_t getInfo() const { return _info; }
Layout::SegmentType getSegmentType() const { return _segmentType; }
/// \brief convert the segment type to a String for diagnostics and printing
/// purposes
StringRef segmentKindToStr() const;
// TODO: Move this down to AtomSection.
virtual bool findAtomAddrByName(StringRef name, uint64_t &addr) {
return false;
}
/// \brief Records the segmentType, that this section belongs to
void setSegmentType(const Layout::SegmentType segmentType) {
this->_segmentType = segmentType;
}
void setMergedSection(MergedSections<ELFT> *ms) {
_parent = ms;
}
static bool classof(const Chunk<ELFT> *c) {
return c->kind() == Chunk<ELFT>::K_ELFSection ||
c->kind() == Chunk<ELFT>::K_AtomSection;
}
protected:
/// \brief MergedSections this Section is a member of, or nullptr.
MergedSections<ELFT> *_parent;
/// \brief ELF SHF_* flags.
uint64_t _flags;
/// \brief The size of each entity.
uint64_t _entSize;
/// \brief ELF SHT_* type.
uint32_t _type;
/// \brief sh_link field.
uint32_t _link;
/// \brief the sh_info field.
uint32_t _info;
/// \brief the output ELF segment type of this section.
Layout::SegmentType _segmentType;
};
/// \brief A section containing atoms.
template <class ELFT> class AtomSection : public Section<ELFT> {
public:
AtomSection(const ELFTargetInfo &ti, StringRef name, int32_t contentType,
int32_t permissions, int32_t order)
: Section<ELFT>(ti, name, Chunk<ELFT>::K_AtomSection),
_contentType(contentType), _contentPermissions(permissions) {
this->setOrder(order);
switch (contentType) {
case DefinedAtom::typeCode:
case DefinedAtom::typeDataFast:
case DefinedAtom::typeData:
case DefinedAtom::typeConstant:
case DefinedAtom::typeGOT:
case DefinedAtom::typeStub:
case DefinedAtom::typeResolver:
case DefinedAtom::typeTLVInitialData:
this->_type = SHT_PROGBITS;
break;
case DefinedAtom::typeTLVInitialZeroFill:
case DefinedAtom::typeZeroFillFast:
case DefinedAtom::typeZeroFill:
this->_type = SHT_NOBITS;
break;
}
switch (permissions) {
case DefinedAtom::permR__:
this->_flags = SHF_ALLOC;
break;
case DefinedAtom::permR_X:
this->_flags = SHF_ALLOC | SHF_EXECINSTR;
break;
case DefinedAtom::permRW_:
case DefinedAtom::permRW_L:
this->_flags = SHF_ALLOC | SHF_WRITE;
if (_contentType == DefinedAtom::typeTLVInitialData ||
_contentType == DefinedAtom::typeTLVInitialZeroFill)
this->_flags |= SHF_TLS;
break;
case DefinedAtom::permRWX:
this->_flags = SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR;
break;
}
}
/// Align the offset to the required modulus defined by the atom alignment
uint64_t alignOffset(uint64_t offset, DefinedAtom::Alignment &atomAlign);
// \brief Append an atom to a Section. The atom gets pushed into a vector
// contains the atom, the atom file offset, the atom virtual address
// the atom file offset is aligned appropriately as set by the Reader
virtual const AtomLayout &appendAtom(const Atom *atom);
/// \brief Set the virtual address of each Atom in the Section. This
/// routine gets called after the linker fixes up the virtual address
/// of the section
virtual void assignVirtualAddress(uint64_t &addr) {
for (auto &ai : _atoms) {
ai->_virtualAddr = addr + ai->_fileOffset;
}
}
/// \brief Set the file offset of each Atom in the section. This routine
/// gets called after the linker fixes up the section offset
virtual void assignOffsets(uint64_t offset) {
for (auto &ai : _atoms) {
ai->_fileOffset = offset + ai->_fileOffset;
}
}
/// \brief Find the Atom address given a name, this is needed to to properly
/// apply relocation. The section class calls this to find the atom address
/// to fix the relocation
virtual bool findAtomAddrByName(StringRef name, uint64_t &addr) {
for (auto ai : _atoms) {
if (ai->_atom->name() == name) {
addr = ai->_virtualAddr;
return true;
}
}
return false;
}
/// \brief Does the Atom occupy any disk space
bool occupiesNoDiskSpace() const {
return ((_contentType == DefinedAtom::typeZeroFill) ||
(_contentType == DefinedAtom::typeZeroFillFast));
}
/// \brief The permission of the section is the most permissive permission
/// of all atoms that the section contains
void setContentPermissions(int32_t perm) {
_contentPermissions = std::max(perm, _contentPermissions);
}
/// \brief Return the raw flags, we need this to sort segments
inline int64_t atomflags() const {
return _contentPermissions;
}
/// Atom Iterators
typedef typename std::vector<AtomLayout *>::iterator atom_iter;
range<atom_iter> atoms() { return _atoms; }
virtual void write(ELFWriter *writer, llvm::FileOutputBuffer &buffer);
static bool classof(const Chunk<ELFT> *c) {
return c->kind() == Chunk<ELFT>::K_AtomSection;
}
protected:
llvm::BumpPtrAllocator _alloc;
int32_t _contentType;
int32_t _contentPermissions;
std::vector<AtomLayout *> _atoms;
};
/// Align the offset to the required modulus defined by the atom alignment
template <class ELFT>
uint64_t AtomSection<ELFT>::alignOffset(uint64_t offset,
DefinedAtom::Alignment &atomAlign) {
uint64_t requiredModulus = atomAlign.modulus;
uint64_t align2 = 1u << atomAlign.powerOf2;
uint64_t currentModulus = (offset % align2);
uint64_t retOffset = offset;
if (currentModulus != requiredModulus) {
if (requiredModulus > currentModulus)
retOffset += requiredModulus - currentModulus;
else
retOffset += align2 + requiredModulus - currentModulus;
}
return retOffset;
}
// \brief Append an atom to a Section. The atom gets pushed into a vector
// contains the atom, the atom file offset, the atom virtual address
// the atom file offset is aligned appropriately as set by the Reader
template <class ELFT>
const AtomLayout &AtomSection<ELFT>::appendAtom(const Atom *atom) {
Atom::Definition atomType = atom->definition();
const DefinedAtom *definedAtom = cast<DefinedAtom>(atom);
DefinedAtom::Alignment atomAlign = definedAtom->alignment();
uint64_t align2 = 1u << atomAlign.powerOf2;
// Align the atom to the required modulus/ align the file offset and the
// memory offset seperately this is required so that BSS symbols are handled
// properly as the BSS symbols only occupy memory size and not file size
uint64_t fOffset = alignOffset(this->fileSize(), atomAlign);
uint64_t mOffset = alignOffset(this->memSize(), atomAlign);
switch (atomType) {
case Atom::definitionRegular:
switch(definedAtom->contentType()) {
case DefinedAtom::typeCode:
case DefinedAtom::typeConstant:
case DefinedAtom::typeData:
case DefinedAtom::typeDataFast:
case DefinedAtom::typeGOT:
case DefinedAtom::typeStub:
case DefinedAtom::typeResolver:
case DefinedAtom::typeTLVInitialData:
_atoms.push_back(new (_alloc) AtomLayout(atom, fOffset, 0));
this->_fsize = fOffset + definedAtom->size();
this->_msize = mOffset + definedAtom->size();
DEBUG_WITH_TYPE("Section",
llvm::dbgs() << "[" << this->name() << " " << this << "] "
<< "Adding atom: " << atom->name() << "@"
<< fOffset << "\n");
break;
case DefinedAtom::typeTLVInitialZeroFill:
case DefinedAtom::typeZeroFill:
case DefinedAtom::typeZeroFillFast:
_atoms.push_back(new (_alloc) AtomLayout(atom, mOffset, 0));
this->_msize = mOffset + definedAtom->size();
break;
default:
llvm::dbgs() << definedAtom->contentType() << "\n";
llvm_unreachable("Uexpected content type.");
}
break;
default:
llvm_unreachable("Expecting only definedAtoms being passed here");
break;
}
// Set the section alignment to the largest alignment
// std::max doesnot support uint64_t
if (this->_align2 < align2)
this->_align2 = align2;
return *_atoms.back();
}
/// \brief convert the segment type to a String for diagnostics
/// and printing purposes
template <class ELFT> StringRef Section<ELFT>::segmentKindToStr() const {
switch(_segmentType) {
case llvm::ELF::PT_DYNAMIC:
return "DYNAMIC";
case llvm::ELF::PT_INTERP:
return "INTERP";
case llvm::ELF::PT_LOAD:
return "LOAD";
case llvm::ELF::PT_GNU_EH_FRAME:
return "EH_FRAME";
case llvm::ELF::PT_GNU_RELRO:
return "RELRO";
case llvm::ELF::PT_NOTE:
return "NOTE";
case llvm::ELF::PT_NULL:
return "NULL";
case llvm::ELF::PT_TLS:
return "TLS";
default:
return "UNKNOWN";
}
}
/// \brief Write the section and the atom contents to the buffer
template <class ELFT>
void AtomSection<ELFT>::write(ELFWriter *writer,
llvm::FileOutputBuffer &buffer) {
uint8_t *chunkBuffer = buffer.getBufferStart();
for (auto &ai : _atoms) {
DEBUG_WITH_TYPE("Section",
llvm::dbgs() << "Writing atom: " << ai->_atom->name()
<< " | " << ai->_fileOffset << "\n");
const DefinedAtom *definedAtom = cast<DefinedAtom>(ai->_atom);
if ((definedAtom->contentType() == DefinedAtom::typeZeroFill) ||
(definedAtom->contentType() == DefinedAtom::typeZeroFillFast))
continue;
// Copy raw content of atom to file buffer.
llvm::ArrayRef<uint8_t> content = definedAtom->rawContent();
uint64_t contentSize = content.size();
if (contentSize == 0)
continue;
uint8_t *atomContent = chunkBuffer + ai->_fileOffset;
std::memcpy(atomContent, content.data(), contentSize);
const TargetRelocationHandler<ELFT> &relHandler =
this->_targetInfo.template getTargetHandler<ELFT>()
.getRelocationHandler();
for (const auto ref : *definedAtom)
relHandler.applyRelocation(*writer, buffer, *ai, *ref);
}
}
/// \brief A MergedSections represents a set of sections grouped by the same
/// name. The output file that gets written by the linker has sections grouped
/// by similiar names
template<class ELFT>
class MergedSections {
public:
// Iterators
typedef typename std::vector<Chunk<ELFT> *>::iterator ChunkIter;
MergedSections(StringRef name);
// Appends a section into the list of sections that are part of this Merged
// Section
void appendSection(Chunk<ELFT> *c);
// Set the MergedSections is associated with a segment
inline void setHasSegment() { _hasSegment = true; }
/// Sets the ordinal
inline void setOrdinal(uint64_t ordinal) {
_ordinal = ordinal;
}
/// Sets the Memory size
inline void setMemSize(uint64_t memsz) {
_memSize = memsz;
}
/// Sets the size fo the merged Section
inline void setSize(uint64_t fsiz) {
_size = fsiz;
}
// The offset of the first section contained in the merged section is
// contained here
inline void setFileOffset(uint64_t foffset) {
_fileOffset = foffset;
}
// Sets the starting address of the section
inline void setAddr(uint64_t addr) {
_virtualAddr = addr;
}
void setLink(uint64_t link) { _link = link; }
void setInfo(uint64_t info) { _shInfo = info; }
inline range<ChunkIter> sections() { return _sections; }
// The below functions returns the properties of the MergeSection
inline bool hasSegment() const { return _hasSegment; }
inline StringRef name() const { return _name; }
inline int64_t shinfo() const { return _shInfo; }
inline uint64_t align2() const { return _align2; }
inline int64_t link() const { return _link; }
inline int64_t type() const { return _type; }
inline uint64_t virtualAddr() const { return _virtualAddr; }
inline int64_t ordinal() const { return _ordinal; }
inline int64_t kind() const { return _kind; }
inline uint64_t fileSize() const { return _size; }
inline int64_t entsize() const { return _entSize; }
inline uint64_t fileOffset() const { return _fileOffset; }
inline int64_t flags() const { return _flags; }
inline uint64_t memSize() { return _memSize; }
private:
StringRef _name;
bool _hasSegment;
uint64_t _ordinal;
uint64_t _flags;
uint64_t _size;
uint64_t _memSize;
uint64_t _fileOffset;
uint64_t _virtualAddr;
int64_t _shInfo;
int64_t _entSize;
int64_t _link;
uint64_t _align2;
int64_t _kind;
int64_t _type;
std::vector<Chunk<ELFT> *> _sections;
};
/// MergedSections
template<class ELFT>
MergedSections<ELFT>::MergedSections(StringRef name)
: _name(name)
,_hasSegment(false)
,_ordinal(0)
,_flags(0)
,_size(0)
,_memSize(0)
,_fileOffset(0)
,_virtualAddr(0)
,_shInfo(0)
,_entSize(0)
,_link(0)
,_align2(0)
,_kind(0)
,_type(0) { }
template<class ELFT>
void
MergedSections<ELFT>::appendSection(Chunk<ELFT> *c) {
if (c->align2() > _align2)
_align2 = c->align2();
if (const auto section = dyn_cast<Section<ELFT>>(c)) {
assert(!_link && "Section already has a link!");
_link = section->getLink();
_shInfo = section->getInfo();
_entSize = section->getEntSize();
_type = section->getType();
if (_flags < section->getFlags())
_flags = section->getFlags();
section->setMergedSection(this);
}
_kind = c->kind();
_sections.push_back(c);
}
/// \brief The class represents the ELF String Table
template<class ELFT>
class StringTable : public Section<ELFT> {
public:
StringTable(const ELFTargetInfo &, const char *str, int32_t order,
bool dynamic = false);
uint64_t addString(StringRef symname);
virtual void write(ELFWriter *writer, llvm::FileOutputBuffer &buffer);
inline void setNumEntries(int64_t numEntries) {
_stringMap.resize(numEntries);
}
private:
std::vector<StringRef> _strings;
struct StringRefMappingInfo {
static StringRef getEmptyKey() { return StringRef(); }
static StringRef getTombstoneKey() { return StringRef(" ", 0); }
static unsigned getHashValue(StringRef const val) {
return llvm::HashString(val);
}
static bool isEqual(StringRef const lhs, StringRef const rhs) {
return lhs.equals(rhs);
}
};
typedef typename llvm::DenseMap<StringRef, uint64_t,
StringRefMappingInfo> StringMapT;
typedef typename StringMapT::iterator StringMapTIter;
StringMapT _stringMap;
};
template <class ELFT>
StringTable<ELFT>::StringTable(const ELFTargetInfo &ti, const char *str,
int32_t order, bool dynamic)
: Section<ELFT>(ti, str) {
// the string table has a NULL entry for which
// add an empty string
_strings.push_back("");
this->_fsize = 1;
this->_align2 = 1;
this->setOrder(order);
this->_type = SHT_STRTAB;
if (dynamic) {
this->_flags = SHF_ALLOC;
this->_msize = this->_fsize;
}
}
template <class ELFT> uint64_t StringTable<ELFT>::addString(StringRef symname) {
if (symname.size() == 0)
return 0;
StringMapTIter stringIter = _stringMap.find(symname);
if (stringIter == _stringMap.end()) {
_strings.push_back(symname);
uint64_t offset = this->_fsize;
this->_fsize += symname.size() + 1;
if (this->_flags & SHF_ALLOC)
this->_msize = this->_fsize;
_stringMap[symname] = offset;
return offset;
}
return stringIter->second;
}
template <class ELFT>
void StringTable<ELFT>::write(ELFWriter *writer,
llvm::FileOutputBuffer &buffer) {
uint8_t *chunkBuffer = buffer.getBufferStart();
uint8_t *dest = chunkBuffer + this->fileOffset();
for (auto si : _strings) {
memcpy(dest, si.data(), si.size());
dest += si.size();
memcpy(dest, "", 1);
dest += 1;
}
}
/// \brief The SymbolTable class represents the symbol table in a ELF file
template<class ELFT>
class SymbolTable : public Section<ELFT> {
typedef typename llvm::object::ELFDataTypeTypedefHelper<ELFT>::Elf_Addr
Elf_Addr;
typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym;
struct SymbolEntry {
SymbolEntry(const Atom *a, const Elf_Sym &sym,
const AtomLayout *layout) : _atom(a), _symbol(sym),
_atomLayout(layout) {}
SymbolEntry() : _atom(nullptr) {}
const Atom *_atom;
Elf_Sym _symbol;
const AtomLayout *_atomLayout;
};
public:
SymbolTable(const ELFTargetInfo &ti, const char *str, int32_t order);
/// \brief set the number of entries that would exist in the symbol
/// table for the current link
void setNumEntries(int64_t numEntries) const {
if (_stringSection)
_stringSection->setNumEntries(numEntries);
}
void addSymbol(const Atom *atom, int32_t sectionIndex, uint64_t addr = 0,
const AtomLayout *layout = nullptr);
/// \brief Get the symbol table index for an Atom. If it's not in the symbol
/// table, return STN_UNDEF.
uint32_t getSymbolTableIndex(const Atom *a) const {
auto entry = std::find_if(_symbolTable.begin(), _symbolTable.end(),
[=](const SymbolEntry &se) {
return se._atom == a;
});
if (entry == _symbolTable.end())
return STN_UNDEF;
return std::distance(_symbolTable.begin(), entry);
}
virtual void finalize() { finalize(true); }
virtual void sortSymbols() {
std::stable_sort(_symbolTable.begin(), _symbolTable.end(),
[](const SymbolEntry & A, const SymbolEntry & B) {
return A._symbol.getBinding() < B._symbol.getBinding();
});
}
virtual void addAbsoluteAtom(Elf_Sym &sym, const AbsoluteAtom *aa,
int64_t addr);
virtual void addDefinedAtom(Elf_Sym &sym, const DefinedAtom *da,
int64_t addr);
virtual void addUndefinedAtom(Elf_Sym &sym, const UndefinedAtom *ua);
virtual void addSharedLibAtom(Elf_Sym &sym, const SharedLibraryAtom *sla);
virtual void finalize(bool sort = true);
virtual void write(ELFWriter *writer, llvm::FileOutputBuffer &buffer);
void setStringSection(StringTable<ELFT> *s) { _stringSection = s; }
StringTable<ELFT> *getStringTable() const { return _stringSection; }
protected:
llvm::BumpPtrAllocator _symbolAllocate;
StringTable<ELFT> *_stringSection;
std::vector<SymbolEntry> _symbolTable;
};
/// ELF Symbol Table
template <class ELFT>
SymbolTable<ELFT>::SymbolTable(const ELFTargetInfo &ti, const char *str,
int32_t order)
: Section<ELFT>(ti, str) {
this->setOrder(order);
Elf_Sym symbol;
std::memset(&symbol, 0, sizeof(Elf_Sym));
_symbolTable.push_back(SymbolEntry(nullptr, symbol, nullptr));
this->_entSize = sizeof(Elf_Sym);
this->_fsize = sizeof(Elf_Sym);
this->_align2 = sizeof(Elf_Addr);
this->_type = SHT_SYMTAB;
}
template <class ELFT>
void SymbolTable<ELFT>::addDefinedAtom(Elf_Sym &sym, const DefinedAtom *da,
int64_t addr) {
unsigned char binding = 0, type = 0;
sym.st_size = da->size();
DefinedAtom::ContentType ct;
switch (ct = da->contentType()) {
case DefinedAtom::typeCode:
case DefinedAtom::typeStub:
sym.st_value = addr;
type = llvm::ELF::STT_FUNC;
break;
case DefinedAtom::typeResolver:
sym.st_value = addr;
type = llvm::ELF::STT_GNU_IFUNC;
break;
case DefinedAtom::typeDataFast:
case DefinedAtom::typeData:
case DefinedAtom::typeConstant:
sym.st_value = addr;
type = llvm::ELF::STT_OBJECT;
break;
case DefinedAtom::typeGOT:
sym.st_value = addr;
type = llvm::ELF::STT_NOTYPE;
break;
case DefinedAtom::typeZeroFill:
case DefinedAtom::typeZeroFillFast:
type = llvm::ELF::STT_OBJECT;
sym.st_value = addr;
break;
case DefinedAtom::typeTLVInitialData:
case DefinedAtom::typeTLVInitialZeroFill:
type = llvm::ELF::STT_TLS;
sym.st_value = addr;
break;
default:
type = llvm::ELF::STT_NOTYPE;
}
if (da->customSectionName() == da->name())
type = llvm::ELF::STT_SECTION;
if (da->scope() == DefinedAtom::scopeTranslationUnit)
binding = llvm::ELF::STB_LOCAL;
else
binding = llvm::ELF::STB_GLOBAL;
sym.setBindingAndType(binding, type);
}
template <class ELFT>
void SymbolTable<ELFT>::addAbsoluteAtom(Elf_Sym &sym, const AbsoluteAtom *aa,
int64_t addr) {
unsigned char binding = 0, type = 0;
type = llvm::ELF::STT_OBJECT;
sym.st_shndx = llvm::ELF::SHN_ABS;
switch (aa->scope()) {
case AbsoluteAtom::scopeLinkageUnit:
sym.st_other = llvm::ELF::STV_HIDDEN;
binding = llvm::ELF::STB_LOCAL;
break;
case AbsoluteAtom::scopeTranslationUnit:
binding = llvm::ELF::STB_LOCAL;
break;
case AbsoluteAtom::scopeGlobal:
binding = llvm::ELF::STB_GLOBAL;
break;
}
sym.st_value = addr;
sym.setBindingAndType(binding, type);
}
template <class ELFT>
void SymbolTable<ELFT>::addSharedLibAtom(Elf_Sym &sym,
const SharedLibraryAtom *aa) {
unsigned char binding = 0, type = 0;
type = llvm::ELF::STT_FUNC;
sym.st_shndx = llvm::ELF::SHN_UNDEF;
binding = llvm::ELF::STB_GLOBAL;
sym.setBindingAndType(binding, type);
}
template <class ELFT>
void SymbolTable<ELFT>::addUndefinedAtom(Elf_Sym &sym,
const UndefinedAtom *ua) {
unsigned char binding = 0, type = 0;
sym.st_value = 0;
type = llvm::ELF::STT_NOTYPE;
if (ua->canBeNull())
binding = llvm::ELF::STB_WEAK;
else
binding = llvm::ELF::STB_GLOBAL;
sym.setBindingAndType(binding, type);
}
/// Add a symbol to the symbol Table, definedAtoms which get added to the symbol
/// section dont have their virtual addresses set at the time of adding the
/// symbol to the symbol table(Example: dynamic symbols), the addresses needs
/// to be updated in the table before writing the dynamic symbol table
/// information
template <class ELFT>
void SymbolTable<ELFT>::addSymbol(const Atom *atom, int32_t sectionIndex,
uint64_t addr, const AtomLayout *atomLayout) {
Elf_Sym symbol;
if (atom->name().empty())
return;
symbol.st_name = _stringSection->addString(atom->name());
symbol.st_size = 0;
symbol.st_shndx = sectionIndex;
symbol.st_value = 0;
symbol.st_other = llvm::ELF::STV_DEFAULT;
// Add all the atoms
if (const DefinedAtom *da = dyn_cast<const DefinedAtom>(atom))
addDefinedAtom(symbol, da, addr);
else if (const AbsoluteAtom *aa = dyn_cast<const AbsoluteAtom>(atom))
addAbsoluteAtom(symbol, aa, addr);
else if (isa<const SharedLibraryAtom>(atom))
addSharedLibAtom(symbol, llvm::dyn_cast<SharedLibraryAtom>(atom));
else
addUndefinedAtom(symbol, llvm::dyn_cast<UndefinedAtom>(atom));
_symbolTable.push_back(SymbolEntry(atom, symbol, atomLayout));
this->_fsize += sizeof(Elf_Sym);
if (this->_flags & SHF_ALLOC)
this->_msize = this->_fsize;
}
template <class ELFT> void SymbolTable<ELFT>::finalize(bool sort) {
// sh_info should be one greater than last symbol with STB_LOCAL binding
// we sort the symbol table to keep all local symbols at the beginning
if (sort)
sortSymbols();
uint16_t shInfo = 0;
for (const auto &i : _symbolTable) {
if (i._symbol.getBinding() != llvm::ELF::STB_LOCAL)
break;
shInfo++;
}
this->_info = shInfo;
this->_link = _stringSection->ordinal();
if (this->_parent) {
this->_parent->setInfo(this->_info);
this->_parent->setLink(this->_link);
}
}
template <class ELFT>
void SymbolTable<ELFT>::write(ELFWriter *writer,
llvm::FileOutputBuffer &buffer) {
uint8_t *chunkBuffer = buffer.getBufferStart();
uint8_t *dest = chunkBuffer + this->fileOffset();
for (const auto &sti : _symbolTable) {
memcpy(dest, &sti._symbol, sizeof(Elf_Sym));
dest += sizeof(Elf_Sym);
}
}
template <class ELFT> class HashSection;
template <class ELFT> class DynamicSymbolTable : public SymbolTable<ELFT> {
public:
DynamicSymbolTable(const ELFTargetInfo &ti, const char *str, int32_t order)
: SymbolTable<ELFT>(ti, str, order), _hashTable(nullptr) {
this->_type = SHT_DYNSYM;
this->_flags = SHF_ALLOC;
this->_msize = this->_fsize;
}
// Set the dynamic hash table for symbols to be added into
void setHashTable(HashSection<ELFT> *hashTable) { _hashTable = hashTable; }
// Add all the dynamic symbos to the hash table
void addSymbolsToHashTable() {
int index = 0;
for (auto &ste : this->_symbolTable) {
if (!ste._atom)
_hashTable->addSymbol("", index);
else
_hashTable->addSymbol(ste._atom->name(), index);
++index;
}
}
virtual void finalize() {
// Defined symbols which have been added into the dynamic symbol table
// dont have their addresses known until addresses have been assigned
// so lets update the symbol values after they have got assigned
for (auto &ste: this->_symbolTable) {
const AtomLayout *atomLayout = ste._atomLayout;
if (!atomLayout)
continue;
ste._symbol.st_value = atomLayout->_virtualAddr;
}
// Dont sort the symbols
SymbolTable<ELFT>::finalize(false);
}
private:
HashSection<ELFT> *_hashTable;
};
template <class ELFT> class RelocationTable : public Section<ELFT> {
public:
typedef llvm::object::Elf_Rel_Impl<ELFT, true> Elf_Rela;
RelocationTable(const ELFTargetInfo &ti, StringRef str, int32_t order)
: Section<ELFT>(ti, str), _symbolTable(nullptr) {
this->setOrder(order);
this->_entSize = sizeof(Elf_Rela);
this->_align2 = llvm::alignOf<Elf_Rela>();
this->_type = SHT_RELA;
this->_flags = SHF_ALLOC;
}
/// \returns the index of the relocation added.
uint32_t addRelocation(const DefinedAtom &da, const Reference &r) {
_relocs.emplace_back(&da, &r);
this->_fsize = _relocs.size() * sizeof(Elf_Rela);
this->_msize = this->_fsize;
return _relocs.size() - 1;
}
bool getRelocationIndex(const Reference &r, uint32_t &res) {
auto rel = std::find_if(
_relocs.begin(), _relocs.end(),
[&](const std::pair<const DefinedAtom *, const Reference *> &p) {
if (p.second == &r)
return true;
return false;
});
if (rel == _relocs.end())
return false;
res = std::distance(_relocs.begin(), rel);
return true;
}
void setSymbolTable(const DynamicSymbolTable<ELFT> *symbolTable) {
_symbolTable = symbolTable;
}
virtual void finalize() {
this->_link = _symbolTable ? _symbolTable->ordinal() : 0;
if (this->_parent)
this->_parent->setLink(this->_link);
}
virtual void write(ELFWriter *writer, llvm::FileOutputBuffer &buffer) {
uint8_t *chunkBuffer = buffer.getBufferStart();
uint8_t *dest = chunkBuffer + this->fileOffset();
for (const auto &rel : _relocs) {
Elf_Rela *r = reinterpret_cast<Elf_Rela *>(dest);
uint32_t index =
_symbolTable ? _symbolTable->getSymbolTableIndex(rel.second->target())
: (uint32_t) STN_UNDEF;
r->setSymbolAndType(index, rel.second->kind());
r->r_offset =
writer->addressOfAtom(rel.first) + rel.second->offsetInAtom();
r->r_addend = 0;
// The addend is used only by relative relocations
if (this->_targetInfo.isRelativeReloc(*rel.second))
r->r_addend =
writer->addressOfAtom(rel.second->target()) + rel.second->addend();
dest += sizeof(Elf_Rela);
DEBUG_WITH_TYPE(
"ELFRelocationTable",
llvm::dbgs() << kindOrUnknown(this->_targetInfo.stringFromRelocKind(
rel.second->kind())) << " relocation at "
<< rel.first->name() << "@" << r->r_offset << " to "
<< rel.second->target()->name() << "@" << r->r_addend
<< "\n");
}
}
private:
std::vector<std::pair<const DefinedAtom *, const Reference *> > _relocs;
const DynamicSymbolTable<ELFT> *_symbolTable;
};
template <class ELFT> class HashSection;
template <class ELFT> class DynamicTable : public Section<ELFT> {
typedef llvm::object::Elf_Dyn_Impl<ELFT> Elf_Dyn;
typedef std::vector<Elf_Dyn> EntriesT;
public:
DynamicTable(const ELFTargetInfo &ti, StringRef str, int32_t order)
: Section<ELFT>(ti, str) {
this->setOrder(order);
this->_entSize = sizeof(Elf_Dyn);
this->_align2 = llvm::alignOf<Elf_Dyn>();
// Reserve space for the DT_NULL entry.
this->_fsize = sizeof(Elf_Dyn);
this->_msize = sizeof(Elf_Dyn);
this->_type = SHT_DYNAMIC;
this->_flags = SHF_ALLOC;
_layout = &ti.getTargetHandler<ELFT>().targetLayout();
}
range<typename EntriesT::iterator> entries() { return _entries; }
/// \returns the index of the entry.
std::size_t addEntry(Elf_Dyn e) {
_entries.push_back(e);
this->_fsize = (_entries.size() * sizeof(Elf_Dyn)) + sizeof(Elf_Dyn);
this->_msize = this->_fsize;
return _entries.size() - 1;
}
void write(ELFWriter *writer, llvm::FileOutputBuffer &buffer) {
uint8_t *chunkBuffer = buffer.getBufferStart();
uint8_t *dest = chunkBuffer + this->fileOffset();
// Add the null entry.
Elf_Dyn d;
d.d_tag = 0;
d.d_un.d_val = 0;
_entries.push_back(d);
std::memcpy(dest, _entries.data(), this->_fsize);
}
void createDefaultEntries() {
Elf_Dyn dyn;
dyn.d_un.d_val = 0;
dyn.d_tag = DT_HASH;
_dt_hash = addEntry(dyn);
dyn.d_tag = DT_STRTAB;
_dt_strtab = addEntry(dyn);
dyn.d_tag = DT_SYMTAB;
_dt_symtab = addEntry(dyn);
dyn.d_tag = DT_STRSZ;
_dt_strsz = addEntry(dyn);
dyn.d_tag = DT_SYMENT;
_dt_syment = addEntry(dyn);
if (_layout->hasDynamicRelocationTable()) {
dyn.d_tag = DT_RELA;
_dt_rela = addEntry(dyn);
dyn.d_tag = DT_RELASZ;
_dt_relasz = addEntry(dyn);
dyn.d_tag = DT_RELAENT;
_dt_relaent = addEntry(dyn);
}
if (_layout->hasPLTRelocationTable()) {
dyn.d_tag = DT_PLTRELSZ;
_dt_pltrelsz = addEntry(dyn);
dyn.d_tag = DT_PLTGOT;
_dt_pltgot = addEntry(dyn);
dyn.d_tag = DT_PLTREL;
dyn.d_un.d_val = DT_RELA;
_dt_pltrel = addEntry(dyn);
dyn.d_un.d_val = 0;
dyn.d_tag = DT_JMPREL;
_dt_jmprel = addEntry(dyn);
}
}
virtual void finalize() {
StringTable<ELFT> *dynamicStringTable =
_dynamicSymbolTable->getStringTable();
this->_link = dynamicStringTable->ordinal();
if (this->_parent) {
this->_parent->setInfo(this->_info);
this->_parent->setLink(this->_link);
}
}
void setSymbolTable(DynamicSymbolTable<ELFT> *dynsym) {
_dynamicSymbolTable = dynsym;
}
void setHashTable(HashSection<ELFT> *hsh) { _hashTable = hsh; }
void updateDynamicTable() {
StringTable<ELFT> *dynamicStringTable =
_dynamicSymbolTable->getStringTable();
_entries[_dt_hash].d_un.d_val = _hashTable->virtualAddr();
_entries[_dt_strtab].d_un.d_val = dynamicStringTable->virtualAddr();
_entries[_dt_symtab].d_un.d_val = _dynamicSymbolTable->virtualAddr();
_entries[_dt_strsz].d_un.d_val = dynamicStringTable->memSize();
_entries[_dt_syment].d_un.d_val = _dynamicSymbolTable->getEntSize();
if (_layout->hasDynamicRelocationTable()) {
auto relaTbl = _layout->getDynamicRelocationTable();
_entries[_dt_rela].d_un.d_val = relaTbl->virtualAddr();
_entries[_dt_relasz].d_un.d_val = relaTbl->memSize();
_entries[_dt_relaent].d_un.d_val = relaTbl->getEntSize();
}
if (_layout->hasPLTRelocationTable()) {
auto relaTbl = _layout->getPLTRelocationTable();
_entries[_dt_jmprel].d_un.d_val = relaTbl->virtualAddr();
_entries[_dt_pltrelsz].d_un.d_val = relaTbl->memSize();
auto gotplt = _layout->findOutputSection(".got.plt");
_entries[_dt_pltgot].d_un.d_val = gotplt->virtualAddr();
}
}
private:
EntriesT _entries;
std::size_t _dt_hash;
std::size_t _dt_strtab;
std::size_t _dt_symtab;
std::size_t _dt_rela;
std::size_t _dt_relasz;
std::size_t _dt_relaent;
std::size_t _dt_strsz;
std::size_t _dt_syment;
std::size_t _dt_pltrelsz;
std::size_t _dt_pltgot;
std::size_t _dt_pltrel;
std::size_t _dt_jmprel;
TargetLayout<ELFT> *_layout;
DynamicSymbolTable<ELFT> *_dynamicSymbolTable;
HashSection<ELFT> *_hashTable;
};
template <class ELFT> class InterpSection : public Section<ELFT> {
public:
InterpSection(const ELFTargetInfo &ti, StringRef str, int32_t order,
StringRef interp)
: Section<ELFT>(ti, str),
_interp(interp){
this->setOrder(order);
this->_align2 = 1;
// + 1 for null term.
this->_fsize = interp.size() + 1;
this->_msize = this->_fsize;
this->_type = SHT_PROGBITS;
this->_flags = SHF_ALLOC;
}
void write(ELFWriter *writer, llvm::FileOutputBuffer &buffer) {
uint8_t *chunkBuffer = buffer.getBufferStart();
uint8_t *dest = chunkBuffer + this->fileOffset();
std::memcpy(dest, _interp.data(), _interp.size());
}
private:
StringRef _interp;
};
/// The hash table in the dynamic linker is organized into
///
/// [ nbuckets ]
/// [ nchains ]
/// [ buckets[0] ]
/// .........................
/// [ buckets[nbuckets-1] ]
/// [ chains[0] ]
/// .........................
/// [ chains[nchains - 1] ]
///
/// nbuckets - total number of hash buckets
/// nchains is equal to the number of dynamic symbols.
///
/// The symbol is searched by the dynamic linker using the below approach.
/// * Calculate the hash of the symbol that needs to be searched
/// * Take the value from the buckets[hash % nbuckets] as the index of symbol
/// * Compare the symbol's name, if true return, if false, look through the
/// * array since there was a collision
template <class ELFT> class HashSection : public Section<ELFT> {
struct SymbolTableEntry {
StringRef _name;
uint32_t _index;
};
public:
HashSection(const ELFTargetInfo &ti, StringRef name, int32_t order)
: Section<ELFT>(ti, name), _symbolTable(nullptr) {
this->setOrder(order);
this->_entSize = 4;
this->_type = SHT_HASH;
this->_flags = SHF_ALLOC;
// Set the alignment properly depending on the target architecture
if (ti.is64Bits())
this->_align2 = 8;
else
this->_align2 = 4;
this->_fsize = 0;
this->_msize = 0;
}
/// \brief add the dynamic symbol into the table so that the
/// hash could be calculated
void addSymbol(StringRef name, uint32_t index) {
SymbolTableEntry ste;
ste._name = name;
ste._index = index;
_entries.push_back(ste);
}
/// \brief Set the dynamic symbol table
void setSymbolTable(const DynamicSymbolTable<ELFT> *symbolTable) {
_symbolTable = symbolTable;
}
// The size of the section has to be determined so that fileoffsets
// may be properly assigned. Lets calculate the buckets and the chains
// and fill the chains and the buckets hash table used by the dynamic
// linker and update the filesize and memory size accordingly
virtual void doPreFlight() {
// The number of buckets to use for a certain number of symbols.
// If there are less than 3 symbols, 1 bucket will be used. If
// there are less than 17 symbols, 3 buckets will be used, and so
// forth. The bucket numbers are defined by GNU ld. We use the
// same rules here so we generate hash sections with the same
// size as those generated by GNU ld.
uint32_t hashBuckets[] = { 1, 3, 17, 37, 67, 97, 131, 197, 263, 521, 1031,
2053, 4099, 8209, 16411, 32771, 65537, 131101,
262147 };
int hashBucketsCount = sizeof(hashBuckets) / sizeof(uint32_t);
unsigned int bucketsCount = 0;
unsigned int dynSymCount = _entries.size();
// Get the number of buckes that we want to use
for (int i = 0; i < hashBucketsCount; ++i) {
if (dynSymCount < hashBuckets[i])
break;
bucketsCount = hashBuckets[i];
}
_buckets.resize(bucketsCount);
_chains.resize(_entries.size());
// Create the hash table for the dynamic linker
for (auto ai : _entries) {
unsigned int dynsymIndex = ai._index;
unsigned int bucketpos = llvm::object::elf_hash(ai._name) % bucketsCount;
_chains[dynsymIndex] = _buckets[bucketpos];
_buckets[bucketpos] = dynsymIndex;
}
this->_fsize = (2 + _chains.size() + _buckets.size()) * sizeof(uint32_t);
this->_msize = this->_fsize;
}
virtual void finalize() {
this->_link = _symbolTable ? _symbolTable->ordinal() : 0;
if (this->_parent)
this->_parent->setLink(this->_link);
}
virtual void write(ELFWriter *writer, llvm::FileOutputBuffer &buffer) {
uint8_t *chunkBuffer = buffer.getBufferStart();
uint8_t *dest = chunkBuffer + this->fileOffset();
uint32_t bucketChainCounts[2];
bucketChainCounts[0] = _buckets.size();
bucketChainCounts[1] = _chains.size();
std::memcpy(dest, (char *)bucketChainCounts, sizeof(bucketChainCounts));
dest += sizeof(bucketChainCounts);
// write bucket values
for (auto bi : _buckets) {
uint32_t val = (bi);
std::memcpy(dest, &val, sizeof(uint32_t));
dest += sizeof(uint32_t);
}
// write chain values
for (auto ci : _chains) {
uint32_t val = (ci);
std::memcpy(dest, &val, sizeof(uint32_t));
dest += sizeof(uint32_t);
}
}
private:
std::vector<SymbolTableEntry> _entries;
std::vector<uint32_t> _buckets;
std::vector<uint32_t> _chains;
const DynamicSymbolTable<ELFT> *_symbolTable;
};
} // end namespace elf
} // end namespace lld
#endif