blob: ee052551b19cc70ec36e069add765fc75362bb7b [file] [log] [blame]
//===- lib/ReaderWriter/ELF/SegmentChunks.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_SEGMENT_CHUNKS_H
#define LLD_READER_WRITER_ELF_SEGMENT_CHUNKS_H
#include "Chunk.h"
#include "Layout.h"
#include "SectionChunks.h"
#include "Writer.h"
#include "lld/Core/range.h"
#include "lld/ReaderWriter/Writer.h"
#include "llvm/ADT/ArrayRef.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"
#include <memory>
namespace lld {
namespace elf {
template <typename ELFT> class DefaultLayout;
template <typename ELFT> class ScriptLayout;
/// \brief A segment can be divided into segment slices
/// depending on how the segments can be split
template<class ELFT>
class SegmentSlice {
public:
typedef typename std::vector<Chunk<ELFT> *>::iterator SectionIter;
SegmentSlice() { }
/// Set the segment slice so that it begins at the offset specified
/// by file offset and set the start of the slice to be s and the end
/// of the slice to be e
void set(uint64_t fileoffset, int32_t s, int e) {
_startSection = s;
_endSection = e + 1;
_offset = fileoffset;
}
// Set the segment slice start and end iterators. This is used to walk through
// the sections that are part of the Segment slice
inline void setSections(range<SectionIter> sections) {
_sections = sections;
}
// Return the fileOffset of the slice
inline uint64_t fileOffset() const { return _offset; }
// Return the size of the slice
inline uint64_t fileSize() const { return _size; }
// Return the start of the slice
inline int32_t startSection() const { return _startSection; }
// Return the start address of the slice
inline uint64_t virtualAddr() const { return _addr; }
// Return the memory size of the slice
inline uint64_t memSize() const { return _memSize; }
// Return the alignment of the slice
inline uint64_t align2() const { return _align2; }
inline void setSize(uint64_t sz) { _size = sz; }
inline void setMemSize(uint64_t memsz) { _memSize = memsz; }
inline void setVAddr(uint64_t addr) { _addr = addr; }
inline void setAlign(uint64_t align) { _align2 = align; }
static bool compare_slices(SegmentSlice<ELFT> *a, SegmentSlice<ELFT> *b) {
return a->startSection() < b->startSection();
}
inline range<SectionIter> sections() {
return _sections;
}
private:
int32_t _startSection;
int32_t _endSection;
range<SectionIter> _sections;
uint64_t _addr;
uint64_t _offset;
uint64_t _size;
uint64_t _align2;
uint64_t _memSize;
};
/// \brief A segment contains a set of sections, that have similar properties
// the sections are already separated based on different flags and properties
// the segment is just a way to concatenate sections to segments
template<class ELFT>
class Segment : public Chunk<ELFT> {
public:
typedef typename std::vector<SegmentSlice<ELFT> *>::iterator SliceIter;
typedef typename std::vector<Chunk<ELFT> *>::iterator SectionIter;
Segment(const ELFLinkingContext &context, StringRef name,
const Layout::SegmentType type);
/// \brief the Order of segments that appear in the output file
enum SegmentOrder {
permUnknown,
permRWX,
permRX,
permR,
permRWL,
permRW,
permNonAccess
};
/// append a section to a segment
virtual void append(Section<ELFT> *chunk);
/// append a chunk to a segment, this function
/// is used by the ProgramHeader segment
virtual void append(Chunk<ELFT> *chunk) {}
/// Sort segments depending on the property
/// If we have a Program Header segment, it should appear first
/// If we have a INTERP segment, that should appear after the Program Header
/// All Loadable segments appear next in this order
/// All Read Write Execute segments follow
/// All Read Execute segments appear next
/// All Read only segments appear first
/// All Write execute segments follow
static bool compareSegments(Segment<ELFT> *sega, Segment<ELFT> *segb);
/// \brief Start assigning file offset to the segment chunks The fileoffset
/// needs to be page at the start of the segment and in addition the
/// fileoffset needs to be aligned to the max section alignment within the
/// segment. This is required so that the ELF property p_poffset % p_align =
/// p_vaddr mod p_align holds true.
/// The algorithm starts off by assigning the startOffset thats passed in as
/// parameter to the first section in the segment, if the difference between
/// the newly computed offset is greater than a page, then we create a segment
/// slice, as it would be a waste of virtual memory just to be filled with
/// zeroes
void assignOffsets(uint64_t startOffset);
/// \brief Assign virtual addresses to the slices
void assignVirtualAddress(uint64_t &addr);
// Write the Segment
void write(ELFWriter *writer, TargetLayout<ELFT> &layout,
llvm::FileOutputBuffer &buffer);
int64_t flags() const;
/// Prepend a generic chunk to the segment.
void prepend(Chunk<ELFT> *c) {
_sections.insert(_sections.begin(), c);
}
/// Finalize the segment before assigning File Offsets / Virtual addresses
inline void doPreFlight() {}
/// Finalize the segment, before we want to write the segment header
/// information
inline void finalize() {
// We want to finalize the segment values for now only for non loadable
// segments, since those values are not set in the Layout
if (_segmentType == llvm::ELF::PT_LOAD)
return;
// The size is the difference of the
// last section to the first section, especially for TLS because
// the TLS segment contains both .tdata/.tbss
this->setFileOffset(_sections.front()->fileOffset());
this->setVAddr(_sections.front()->virtualAddr());
size_t startFileOffset = _sections.front()->fileOffset();
size_t startAddr = _sections.front()->virtualAddr();
for (auto ai : _sections) {
this->_fsize = ai->fileOffset() + ai->fileSize() - startFileOffset;
this->_msize = ai->virtualAddr() + ai->memSize() - startAddr;
}
}
// For LLVM RTTI
static inline bool classof(const Chunk<ELFT> *c) {
return c->kind() == Chunk<ELFT>::Kind::ELFSegment;
}
// Getters
inline int32_t sectionCount() const {
return _sections.size();
}
/// \brief, this function returns the type of segment (PT_*)
inline Layout::SegmentType segmentType() { return _segmentType; }
/// \brief return the segment type depending on the content,
/// If the content corresponds to Code, this will return Segment::Code
/// If the content corresponds to Data, this will return Segment::Data
/// If the content corresponds to TLS, this will return Segment::TLS
virtual int getContentType() const {
int64_t fl = flags();
switch (_segmentType) {
case llvm::ELF::PT_LOAD: {
if (fl && llvm::ELF::PF_X)
return Chunk<ELFT>::ContentType::Code;
if (fl && llvm::ELF::PF_W)
return Chunk<ELFT>::ContentType::Data;
}
case llvm::ELF::PT_TLS:
return Chunk<ELFT>::ContentType::TLS;
case llvm::ELF::PT_NOTE:
return Chunk<ELFT>::ContentType::Note;
default:
return Chunk<ELFT>::ContentType::Unknown;
}
}
inline int pageSize() const { return this->_context.getPageSize(); }
inline int rawflags() const { return _atomflags; }
inline int64_t atomflags() const {
switch (_atomflags) {
case DefinedAtom::permUnknown:
return permUnknown;
case DefinedAtom::permRWX:
return permRWX;
case DefinedAtom::permR_X:
return permRX;
case DefinedAtom::permR__:
return permR;
case DefinedAtom::permRW_L:
return permRWL;
case DefinedAtom::permRW_:
return permRW;
case DefinedAtom::perm___:
default:
return permNonAccess;
}
}
inline int64_t numSlices() const { return _segmentSlices.size(); }
inline range<SliceIter> slices() { return _segmentSlices; }
// These two accessors are still needed for a call to std::stable_sort.
// Consider adding wrappers for two iterator algorithms.
inline SliceIter slices_begin() {
return _segmentSlices.begin();
}
inline SliceIter slices_end() {
return _segmentSlices.end();
}
Chunk<ELFT> *firstSection() { return _sections[0]; }
private:
/// \brief Check if the chunk needs to be aligned
bool needAlign(Chunk<ELFT> *chunk) const {
if (chunk->getContentType() == Chunk<ELFT>::ContentType::Data &&
_outputMagic == ELFLinkingContext::OutputMagic::NMAGIC)
return true;
return false;
}
// Cached value of outputMagic
ELFLinkingContext::OutputMagic _outputMagic;
protected:
/// \brief Section or some other chunk type.
std::vector<Chunk<ELFT> *> _sections;
std::vector<SegmentSlice<ELFT> *> _segmentSlices;
Layout::SegmentType _segmentType;
uint64_t _flags;
int64_t _atomflags;
llvm::BumpPtrAllocator _segmentAllocate;
};
/// \brief A Program Header segment contains a set of chunks instead of sections
/// The segment doesn't contain any slice
template <class ELFT> class ProgramHeaderSegment : public Segment<ELFT> {
public:
ProgramHeaderSegment(const ELFLinkingContext &context)
: Segment<ELFT>(context, "PHDR", llvm::ELF::PT_PHDR) {
this->_align2 = 8;
this->_flags = (llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_EXECINSTR);
}
/// append a section to a segment
void append(Chunk<ELFT> *chunk) { _sections.push_back(chunk); }
/// Finalize the segment, before we want to write the segment header
/// information
inline void finalize() {
// If the segment is of type Program Header, then the values fileOffset
// and the fileSize need to be picked up from the last section, the first
// section points to the ELF header and the second chunk points to the
// actual program headers
this->setFileOffset(_sections.back()->fileOffset());
this->setVAddr(_sections.back()->virtualAddr());
this->_fsize = _sections.back()->fileSize();
this->_msize = _sections.back()->memSize();
}
protected:
/// \brief Section or some other chunk type.
std::vector<Chunk<ELFT> *> _sections;
};
template <class ELFT>
Segment<ELFT>::Segment(const ELFLinkingContext &context, StringRef name,
const Layout::SegmentType type)
: Chunk<ELFT>(name, Chunk<ELFT>::Kind::ELFSegment, context),
_segmentType(type), _flags(0), _atomflags(0) {
this->_align2 = 0;
this->_fsize = 0;
_outputMagic = context.getOutputMagic();
}
// This function actually is used, but not in all instantiations of Segment.
LLVM_ATTRIBUTE_UNUSED
static DefinedAtom::ContentPermissions toAtomPerms(uint64_t flags) {
switch (flags & (SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR)) {
case SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR:
return DefinedAtom::permRWX;
case SHF_ALLOC | SHF_EXECINSTR:
return DefinedAtom::permR_X;
case SHF_ALLOC:
return DefinedAtom::permR__;
case SHF_ALLOC | SHF_WRITE:
return DefinedAtom::permRW_;
default:
return DefinedAtom::permUnknown;
}
}
template <class ELFT> void Segment<ELFT>::append(Section<ELFT> *section) {
_sections.push_back(section);
if (_flags < section->getFlags())
_flags |= section->getFlags();
if (_atomflags < toAtomPerms(_flags))
_atomflags = toAtomPerms(_flags);
if (this->_align2 < section->align2())
this->_align2 = section->align2();
}
template <class ELFT>
bool Segment<ELFT>::compareSegments(Segment<ELFT> *sega, Segment<ELFT> *segb) {
int64_t type1 = sega->segmentType();
int64_t type2 = segb->segmentType();
// The single PT_PHDR segment is required to precede any loadable
// segment. We simply make it always first.
if (type1 == llvm::ELF::PT_PHDR)
return true;
if (type2 == llvm::ELF::PT_PHDR)
return false;
// The single PT_INTERP segment is required to precede any loadable
// segment. We simply make it always second.
if (type1 == llvm::ELF::PT_INTERP)
return true;
if (type2 == llvm::ELF::PT_INTERP)
return false;
// We then put PT_LOAD segments before any other segments.
if (type1 == llvm::ELF::PT_LOAD && type2 != llvm::ELF::PT_LOAD)
return true;
if (type2 == llvm::ELF::PT_LOAD && type1 != llvm::ELF::PT_LOAD)
return false;
// We put the PT_TLS segment last except for the PT_GNU_RELRO
// segment, because that is where the dynamic linker expects to find
if (type1 == llvm::ELF::PT_TLS && type2 != llvm::ELF::PT_TLS &&
type2 != llvm::ELF::PT_GNU_RELRO)
return false;
if (type2 == llvm::ELF::PT_TLS && type1 != llvm::ELF::PT_TLS &&
type1 != llvm::ELF::PT_GNU_RELRO)
return true;
// We put the PT_GNU_RELRO segment last, because that is where the
// dynamic linker expects to find it
if (type1 == llvm::ELF::PT_GNU_RELRO && type2 != llvm::ELF::PT_GNU_RELRO)
return false;
if (type2 == llvm::ELF::PT_GNU_RELRO && type1 != llvm::ELF::PT_GNU_RELRO)
return true;
if (type1 == type2)
return sega->atomflags() < segb->atomflags();
return false;
}
template <class ELFT> void Segment<ELFT>::assignOffsets(uint64_t startOffset) {
int startSection = 0;
int currSection = 0;
SectionIter startSectionIter, endSectionIter;
// slice align is set to the max alignment of the chunks that are
// contained in the slice
uint64_t sliceAlign = 0;
// Current slice size
uint64_t curSliceSize = 0;
// Current Slice File Offset
uint64_t curSliceFileOffset = 0;
startSectionIter = _sections.begin();
endSectionIter = _sections.end();
startSection = 0;
bool isFirstSection = true;
bool isDataPageAlignedForNMagic = false;
for (auto si = _sections.begin(); si != _sections.end(); ++si) {
if (isFirstSection) {
// If the linker outputmagic is set to OutputMagic::NMAGIC, align the Data
// to a page boundary
if (!isDataPageAlignedForNMagic && needAlign(*si)) {
startOffset =
llvm::RoundUpToAlignment(startOffset, this->_context.getPageSize());
isDataPageAlignedForNMagic = true;
}
// align the startOffset to the section alignment
uint64_t newOffset =
llvm::RoundUpToAlignment(startOffset, (*si)->align2());
curSliceFileOffset = newOffset;
sliceAlign = (*si)->align2();
this->setFileOffset(startOffset);
(*si)->setFileOffset(newOffset);
curSliceSize = (*si)->fileSize();
isFirstSection = false;
} else {
uint64_t curOffset = curSliceFileOffset + curSliceSize;
// If the linker outputmagic is set to OutputMagic::NMAGIC, align the Data
// to a page boundary
if (!isDataPageAlignedForNMagic && needAlign(*si)) {
curOffset =
llvm::RoundUpToAlignment(curOffset, this->_context.getPageSize());
isDataPageAlignedForNMagic = true;
}
uint64_t newOffset = llvm::RoundUpToAlignment(curOffset, (*si)->align2());
SegmentSlice<ELFT> *slice = nullptr;
// If the newOffset computed is more than a page away, let's create
// a separate segment, so that memory is not used up while running
if (((newOffset - curOffset) > this->_context.getPageSize()) &&
(_outputMagic != ELFLinkingContext::OutputMagic::NMAGIC &&
_outputMagic != ELFLinkingContext::OutputMagic::OMAGIC)) {
// TODO: use std::find here
for (auto s : slices()) {
if (s->startSection() == startSection) {
slice = s;
break;
}
}
if (!slice) {
slice = new (_segmentAllocate.Allocate<SegmentSlice<ELFT>>())
SegmentSlice<ELFT>();
_segmentSlices.push_back(slice);
}
slice->set(curSliceFileOffset, startSection, currSection);
slice->setSections(make_range(startSectionIter, endSectionIter));
slice->setSize(curSliceSize);
slice->setAlign(sliceAlign);
uint64_t newPageOffset =
llvm::RoundUpToAlignment(curOffset, this->_context.getPageSize());
newOffset = llvm::RoundUpToAlignment(newPageOffset, (*si)->align2());
curSliceFileOffset = newOffset;
startSectionIter = endSectionIter;
startSection = currSection;
(*si)->setFileOffset(curSliceFileOffset);
curSliceSize = newOffset - curSliceFileOffset + (*si)->fileSize();
sliceAlign = (*si)->align2();
} else {
if (sliceAlign < (*si)->align2())
sliceAlign = (*si)->align2();
(*si)->setFileOffset(newOffset);
curSliceSize = newOffset - curSliceFileOffset + (*si)->fileSize();
}
}
currSection++;
endSectionIter = si;
}
SegmentSlice<ELFT> *slice = nullptr;
for (auto s : slices()) {
// TODO: add std::find
if (s->startSection() == startSection) {
slice = s;
break;
}
}
if (!slice) {
slice = new (_segmentAllocate.Allocate<SegmentSlice<ELFT>>())
SegmentSlice<ELFT>();
_segmentSlices.push_back(slice);
}
slice->set(curSliceFileOffset, startSection, currSection);
slice->setSections(make_range(startSectionIter, _sections.end()));
slice->setSize(curSliceSize);
slice->setAlign(sliceAlign);
this->_fsize = curSliceFileOffset - startOffset + curSliceSize;
std::stable_sort(slices_begin(), slices_end(),
SegmentSlice<ELFT>::compare_slices);
}
/// \brief Assign virtual addresses to the slices
template <class ELFT> void Segment<ELFT>::assignVirtualAddress(uint64_t &addr) {
bool isTLSSegment = false;
bool isDataPageAlignedForNMagic = false;
uint64_t tlsStartAddr = 0;
for (auto slice : slices()) {
// Align to a page only if the output is not
// OutputMagic::NMAGIC/OutputMagic::OMAGIC
if (_outputMagic != ELFLinkingContext::OutputMagic::NMAGIC &&
_outputMagic != ELFLinkingContext::OutputMagic::OMAGIC)
addr = llvm::RoundUpToAlignment(addr, this->_context.getPageSize());
// Align to the slice alignment
addr = llvm::RoundUpToAlignment(addr, slice->align2());
bool virtualAddressSet = false;
for (auto section : slice->sections()) {
// If the linker outputmagic is set to OutputMagic::NMAGIC, align the Data
// to a page boundary
if (!isDataPageAlignedForNMagic && needAlign(section)) {
addr = llvm::RoundUpToAlignment(addr, this->_context.getPageSize());
isDataPageAlignedForNMagic = true;
}
// Align the section address
addr = llvm::RoundUpToAlignment(addr, section->align2());
// Check if the segment is of type TLS
// The sections that belong to the TLS segment have their
// virtual addresses that are relative To TP
Section<ELFT> *currentSection = dyn_cast<Section<ELFT> >(section);
if (currentSection)
isTLSSegment = (currentSection->getSegmentType() == llvm::ELF::PT_TLS);
tlsStartAddr = (isTLSSegment)
? llvm::RoundUpToAlignment(tlsStartAddr, section->align2())
: 0;
if (!virtualAddressSet) {
slice->setVAddr(addr);
virtualAddressSet = true;
}
section->setVAddr(addr);
if (auto s = dyn_cast<Section<ELFT> >(section)) {
if (isTLSSegment)
s->assignVirtualAddress(tlsStartAddr);
else
s->assignVirtualAddress(addr);
}
if (isTLSSegment)
tlsStartAddr += section->memSize();
section->setMemSize(addr + section->memSize() - section->virtualAddr());
// TBSS section is special in that it doesn't contribute to memory of any
// segment. If we see a tbss section, don't add memory size to addr
// The fileOffset is automatically taken care of since TBSS section does
// not end up using file size
if (section->order() != DefaultLayout<ELFT>::ORDER_TBSS)
addr += section->memSize();
}
slice->setMemSize(addr - slice->virtualAddr());
}
}
// Write the Segment
template <class ELFT>
void Segment<ELFT>::write(ELFWriter *writer, TargetLayout<ELFT> &layout,
llvm::FileOutputBuffer &buffer) {
for (auto slice : slices())
for (auto section : slice->sections())
section->write(writer, layout, buffer);
}
template<class ELFT>
int64_t
Segment<ELFT>::flags() const {
int64_t fl = 0;
if (_flags & llvm::ELF::SHF_ALLOC)
fl |= llvm::ELF::PF_R;
if (_flags & llvm::ELF::SHF_WRITE)
fl |= llvm::ELF::PF_W;
if (_flags & llvm::ELF::SHF_EXECINSTR)
fl |= llvm::ELF::PF_X;
return fl;
}
} // end namespace elf
} // end namespace lld
#endif