llvm-project/lld/lib/ReaderWriter/ELF/SectionChunks.h

746 lines
23 KiB
C
Raw Normal View History

//===- 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/StringRef.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 lld {
namespace elf {
/// \brief A section contains a set of atoms that have similiar properties
/// The atoms that have similiar properties are merged to form a section
template<class ELFT>
class Section : public Chunk<ELFT> {
public:
// The Kind of section that the object represents
enum SectionKind {
K_Default,
K_Target, // The section is handed over to the target
K_SymbolTable,
K_StringTable,
};
// Create a section object, the section is set to the default type if the
// caller doesnot set it
Section(const ELFTargetInfo &, const StringRef sectionName,
const int32_t contentType, const int32_t contentPermissions,
const int32_t order, const SectionKind kind = K_Default);
/// return the section kind
inline SectionKind sectionKind() const {
return _sectionKind;
}
/// 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
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
inline void assignVirtualAddress(uint64_t &addr) {
for (auto &ai : _atoms) {
ai->_virtualAddr = addr + ai->_fileOffset;
}
addr += this->memSize();
}
/// \brief Set the file offset of each Atom in the section. This routine
/// gets called after the linker fixes up the section offset
inline 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
inline bool findAtomAddrByName(const 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
inline bool occupiesNoDiskSpace() const {
return _contentType == DefinedAtom::typeZeroFill;
}
/// \brief The permission of the section is the most permissive permission
/// of all atoms that the section contains
inline void setContentPermissions(int32_t perm) {
_contentPermissions = std::max(perm, _contentPermissions);
}
/// \brief Get the section flags, defined by the permissions of the section
int64_t flags();
/// \brief Return the section type, the returned value is recorded in the
/// sh_type field of the Section Header
int type();
/// \brief convert the segment type to a String for diagnostics
/// and printing purposes
StringRef segmentKindToStr() const;
/// \brief Return the raw flags, we need this to sort segments
inline int64_t atomflags() const {
return _contentPermissions;
}
/// \brief Returns the section link field, the returned value is
/// recorded in the sh_link field of the Section Header
inline int link() const {
return _link;
}
inline void setLink(int32_t link) {
_link = link;
}
/// \brief Returns the section entsize field, the returned value is
/// recorded in the sh_entsize field of the Section Header
inline int entsize() const {
return _entSize;
}
/// \brief Returns the shinfo field, the returned value is
/// recorded in the sh_info field of the Section Header
inline int shinfo() const {
return _shInfo;
}
/// \brief Records the segmentType, that this section belongs to
inline void setSegment(const Layout::SegmentType segmentType) {
_segmentType = segmentType;
}
/// \brief for LLVM style RTTI information
static inline bool classof(const Chunk<ELFT> *c) {
return c->kind() == Chunk<ELFT>::K_ELFSection;
}
/// \brief Finalize the section contents before writing
inline void finalize() { }
/// \brief Write the section and the atom contents to the buffer
void write(ELFWriter *writer, llvm::FileOutputBuffer &buffer);
/// Atom Iterators
typedef typename std::vector<AtomLayout *>::iterator atom_iter;
range<atom_iter> atoms() { return _atoms; }
protected:
int32_t _contentType;
int32_t _contentPermissions;
SectionKind _sectionKind;
std::vector<AtomLayout *> _atoms;
Layout::SegmentType _segmentType;
int64_t _entSize;
int64_t _shInfo;
int64_t _link;
llvm::BumpPtrAllocator _alloc;
};
// Create a section object, the section is set to the default type if the
// caller doesnot set it
template <class ELFT>
Section<ELFT>::Section(const ELFTargetInfo &ti, const StringRef sectionName,
const int32_t contentType,
const int32_t contentPermissions, const int32_t order,
const SectionKind kind)
: Chunk<ELFT>(sectionName, Chunk<ELFT>::K_ELFSection, ti),
_contentType(contentType), _contentPermissions(contentPermissions),
_sectionKind(kind), _entSize(0), _shInfo(0), _link(0) {
this->setOrder(order);
}
/// Align the offset to the required modulus defined by the atom alignment
template<class ELFT>
uint64_t
Section<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 &Section<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::typeData:
case DefinedAtom::typeConstant:
case DefinedAtom::typeGOT:
case DefinedAtom::typeStub:
case DefinedAtom::typeResolver:
_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::typeZeroFill:
_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 Get the section flags, defined by the permissions of the section
template<class ELFT>
int64_t
Section<ELFT>::flags() {
switch (_contentPermissions) {
case DefinedAtom::perm___:
return 0;
case DefinedAtom::permR__:
return llvm::ELF::SHF_ALLOC;
case DefinedAtom::permR_X:
return llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_EXECINSTR;
case DefinedAtom::permRW_:
case DefinedAtom::permRW_L:
return llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_WRITE;
case DefinedAtom::permRWX:
return llvm::ELF::SHF_ALLOC |
llvm::ELF::SHF_WRITE |
llvm::ELF::SHF_EXECINSTR;
default:
break;
}
return llvm::ELF::SHF_ALLOC;
}
/// \brief Return the section type, the returned value is recorded in the
/// sh_type field of the Section Header
template<class ELFT>
int
Section<ELFT>::type() {
if (_sectionKind == K_SymbolTable)
return llvm::ELF::SHT_SYMTAB;
switch (_contentType) {
case DefinedAtom::typeCode:
case DefinedAtom::typeData:
case DefinedAtom::typeConstant:
case DefinedAtom::typeGOT:
case DefinedAtom::typeStub:
case DefinedAtom::typeResolver:
return llvm::ELF::SHT_PROGBITS;
case DefinedAtom::typeZeroFill:
return llvm::ELF::SHT_NOBITS;
// Case to handle section types
// Symtab, String Table ...
default:
return _contentType;
}
}
/// \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_INTERP:
return "INTERP";
case llvm::ELF::PT_LOAD:
return "LOAD";
case llvm::ELF::PT_GNU_EH_FRAME:
return "EH_FRAME";
case llvm::ELF::PT_NOTE:
return "NOTE";
case llvm::ELF::PT_DYNAMIC:
return "DYNAMIC";
case llvm::ELF::PT_GNU_RELRO:
return "RELRO";
case llvm::ELF::PT_NULL:
return "NULL";
default:
return "UNKNOWN";
}
}
/// \brief Write the section and the atom contents to the buffer
template <class ELFT>
void Section<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)
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::copy_n(content.data(), contentSize, atomContent);
for (const auto ref : *definedAtom) {
uint32_t offset = ref->offsetInAtom();
uint64_t targetAddress = 0;
assert(ref->target() != nullptr && "Found the target to be NULL");
targetAddress = writer->addressOfAtom(ref->target());
uint64_t fixupAddress = writer->addressOfAtom(ai->_atom) + offset;
// apply the relocation
writer->kindHandler()->applyFixup(ref->kind(),
ref->addend(),
&atomContent[offset],
fixupAddress,
targetAddress);
}
}
}
/// \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;
}
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;
int64_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)) {
_link = section->link();
_shInfo = section->shinfo();
_entSize = section->entsize();
_type = section->type();
if (_flags < section->flags())
_flags = section->flags();
}
_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);
static inline bool classof(const Chunk<ELFT> *c) {
return c->kind() == Section<ELFT>::K_StringTable;
}
uint64_t addString(const StringRef symname);
void write(ELFWriter *writer, llvm::FileOutputBuffer &buffer);
inline void finalize() { }
private:
std::vector<StringRef> _strings;
};
template <class ELFT>
StringTable<ELFT>::StringTable(const ELFTargetInfo &ti, const char *str,
int32_t order)
: Section<ELFT>(ti, str, llvm::ELF::SHT_STRTAB, DefinedAtom::perm___, order,
Section<ELFT>::K_StringTable) {
// 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);
}
template<class ELFT>
uint64_t
StringTable<ELFT>::addString(const StringRef symname) {
_strings.push_back(symname);
uint64_t offset = this->_fsize;
this->_fsize += symname.size() + 1;
return offset;
}
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> {
public:
typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym;
SymbolTable(const ELFTargetInfo &ti, const char *str, int32_t order);
void addSymbol(const Atom *atom, int32_t sectionIndex, uint64_t addr = 0);
void finalize();
void write(ELFWriter *writer, llvm::FileOutputBuffer &buffer);
static inline bool classof(const Chunk<ELFT> *c) {
return c->kind() == Section<ELFT>::K_SymbolTable;
}
inline void setStringSection(StringTable<ELFT> *s) {
_stringSection = s;
}
private:
StringTable<ELFT> *_stringSection;
std::vector<Elf_Sym*> _symbolTable;
llvm::BumpPtrAllocator _symbolAllocate;
int64_t _link;
};
/// ELF Symbol Table
template <class ELFT>
SymbolTable<ELFT>::SymbolTable(const ELFTargetInfo &ti, const char *str,
int32_t order)
: Section<ELFT>(ti, str, llvm::ELF::SHT_SYMTAB, 0, order,
Section<ELFT>::K_SymbolTable) {
this->setOrder(order);
Elf_Sym *symbol = new (_symbolAllocate.Allocate<Elf_Sym>()) Elf_Sym;
memset((void *)symbol, 0, sizeof(Elf_Sym));
_symbolTable.push_back(symbol);
this->_entSize = sizeof(Elf_Sym);
this->_fsize = sizeof(Elf_Sym);
this->_align2 = sizeof(void *);
}
template<class ELFT>
void
SymbolTable<ELFT>::addSymbol(const Atom *atom,
int32_t sectionIndex,
uint64_t addr) {
Elf_Sym *symbol = new(_symbolAllocate.Allocate<Elf_Sym>()) Elf_Sym;
unsigned char binding = 0, type = 0;
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;
if (const DefinedAtom *da = dyn_cast<const DefinedAtom>(atom)){
symbol->st_size = da->size();
DefinedAtom::ContentType ct;
switch (ct = da->contentType()){
case DefinedAtom::typeCode:
case DefinedAtom::typeStub:
symbol->st_value = addr;
type = llvm::ELF::STT_FUNC;
break;
case DefinedAtom::typeResolver:
symbol->st_value = addr;
type = llvm::ELF::STT_GNU_IFUNC;
break;
case DefinedAtom::typeData:
case DefinedAtom::typeConstant:
case DefinedAtom::typeGOT:
symbol->st_value = addr;
type = llvm::ELF::STT_OBJECT;
break;
case DefinedAtom::typeZeroFill:
type = llvm::ELF::STT_OBJECT;
symbol->st_value = addr;
break;
default:
type = llvm::ELF::STT_NOTYPE;
}
if (da->scope() == DefinedAtom::scopeTranslationUnit)
binding = llvm::ELF::STB_LOCAL;
else
binding = llvm::ELF::STB_GLOBAL;
} else if (const AbsoluteAtom *aa = dyn_cast<const AbsoluteAtom>(atom)){
type = llvm::ELF::STT_OBJECT;
symbol->st_shndx = llvm::ELF::SHN_ABS;
switch (aa->scope()) {
case AbsoluteAtom::scopeLinkageUnit:
symbol->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;
}
symbol->st_value = addr;
} else {
symbol->st_value = 0;
type = llvm::ELF::STT_NOTYPE;
binding = llvm::ELF::STB_WEAK;
}
symbol->setBindingAndType(binding, type);
_symbolTable.push_back(symbol);
this->_fsize += sizeof(Elf_Sym);
}
template<class ELFT>
void
SymbolTable<ELFT>::finalize() {
// 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
std::stable_sort(_symbolTable.begin(), _symbolTable.end(),
[](const Elf_Sym *A, const Elf_Sym *B) {
return A->getBinding() < B->getBinding();
});
uint16_t shInfo = 0;
for (auto i : _symbolTable) {
if (i->getBinding() != llvm::ELF::STB_LOCAL)
break;
shInfo++;
}
this->_shInfo = shInfo;
this->setLink(_stringSection->ordinal());
}
template <class ELFT>
void SymbolTable<ELFT>::write(ELFWriter *writer,
llvm::FileOutputBuffer &buffer) {
uint8_t *chunkBuffer = buffer.getBufferStart();
uint8_t *dest = chunkBuffer + this->fileOffset();
for (auto sti : _symbolTable) {
memcpy(dest, sti, sizeof(Elf_Sym));
dest += sizeof(Elf_Sym);
}
}
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, llvm::ELF::SHT_RELA, DefinedAtom::permR__, order,
Section<ELFT>::K_Default) {
this->setOrder(order);
this->_entSize = sizeof(Elf_Rela);
this->_align2 = llvm::alignOf<Elf_Rela>();
}
void addRelocation(const DefinedAtom &da, const Reference &r) {
_relocs.emplace_back(da, r);
this->_fsize = _relocs.size() * sizeof(Elf_Rela);
this->_msize = this->_fsize;
}
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);
r->setSymbolAndType(0, rel.second.kind());
r->r_offset =
writer->addressOfAtom(&rel.first) + rel.second.offsetInAtom();
r->r_addend =
writer->addressOfAtom(rel.second.target()) + rel.second.addend();
dest += sizeof(Elf_Rela);
DEBUG_WITH_TYPE("ELFRelocationTable", llvm::dbgs()
<< "IRELATIVE 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;
};
} // end namespace elf
} // end namespace lld
#endif