forked from OSchip/llvm-project
1237 lines
39 KiB
C++
1237 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);
|
|
|
|
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);
|
|
|
|
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;
|
|
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();
|
|
// FIXME: The addend is used only by IRELATIVE relocations while static
|
|
// linking executable statically, check to see how does dynamic linking
|
|
// work with IFUNC and change accordingly
|
|
if (!this->_targetInfo.isDynamic())
|
|
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
|