| //===- 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 |