diff options
Diffstat (limited to 'lld/lib/ReaderWriter/ELF/SegmentChunks.h')
| -rw-r--r-- | lld/lib/ReaderWriter/ELF/SegmentChunks.h | 441 |
1 files changed, 6 insertions, 435 deletions
diff --git a/lld/lib/ReaderWriter/ELF/SegmentChunks.h b/lld/lib/ReaderWriter/ELF/SegmentChunks.h index cff9530b1b1..2d8c34ee8eb 100644 --- a/lld/lib/ReaderWriter/ELF/SegmentChunks.h +++ b/lld/lib/ReaderWriter/ELF/SegmentChunks.h @@ -15,11 +15,9 @@ #include "Writer.h" #include "lld/Core/range.h" #include "lld/Core/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" @@ -70,9 +68,7 @@ public: void setAlign(uint64_t align) { _alignment = align; } - static bool compare_slices(SegmentSlice<ELFT> *a, SegmentSlice<ELFT> *b) { - return a->startSection() < b->startSection(); - } + static bool compare_slices(SegmentSlice<ELFT> *a, SegmentSlice<ELFT> *b); range<SectionIter> sections() { return _sections; } @@ -148,27 +144,9 @@ public: _sections.insert(_sections.begin(), c); } - /// Finalize the segment before assigning File Offsets / Virtual addresses - /// Finalize the segment, before we want to write the segment header /// information - 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->setVirtualAddr(_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; - } - } + void finalize(); // For LLVM RTTI static bool classof(const Chunk<ELFT> *c) { @@ -187,63 +165,18 @@ public: /// 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; - } - } + virtual int getContentType() const; int pageSize() const { return this->_ctx.getPageSize(); } - int rawflags() const { return _atomflags; } - - 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; - } - } - + int64_t atomflags() const; int64_t numSlices() const { return _segmentSlices.size(); } - range<SliceIter> slices() { return _segmentSlices; } - 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; - } + bool needAlign(Chunk<ELFT> *chunk) const; // Cached value of outputMagic ELFLinkingContext::OutputMagic _outputMagic; @@ -300,371 +233,9 @@ public: /// Finalize the segment, before we want to write the segment header /// information - 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(this->_sections.back()->fileOffset()); - this->setVirtualAddr(this->_sections.back()->virtualAddr()); - this->_fsize = this->_sections.back()->fileSize(); - this->_msize = this->_sections.back()->memSize(); - } + void finalize(); }; -template <class ELFT> -Segment<ELFT>::Segment(const ELFLinkingContext &ctx, StringRef name, - const typename TargetLayout<ELFT>::SegmentType type) - : Chunk<ELFT>(name, Chunk<ELFT>::Kind::ELFSegment, ctx), _segmentType(type), - _flags(0), _atomflags(0) { - this->_alignment = 1; - this->_fsize = 0; - _outputMagic = ctx.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(Chunk<ELFT> *chunk) { - _sections.push_back(chunk); - Section<ELFT> *section = dyn_cast<Section<ELFT>>(chunk); - if (!section) - return; - if (_flags < section->getFlags()) - _flags |= section->getFlags(); - if (_atomflags < toAtomPerms(_flags)) - _atomflags = toAtomPerms(_flags); - if (this->_alignment < section->alignment()) - this->_alignment = section->alignment(); -} - -template <class ELFT> -bool Segment<ELFT>::compareSegments(Segment<ELFT> *sega, Segment<ELFT> *segb) { - int64_t type1 = sega->segmentType(); - int64_t type2 = segb->segmentType(); - - if (type1 == type2) - return sega->atomflags() < segb->atomflags(); - - // 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) - return true; - if (type2 == llvm::ELF::PT_LOAD) - return false; - - // 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) - return false; - if (type2 == llvm::ELF::PT_GNU_RELRO) - return true; - - // 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) - return false; - if (type2 == llvm::ELF::PT_TLS) - return true; - - // Otherwise compare the types to establish an arbitrary ordering. - // FIXME: Should figure out if we should just make all other types compare - // equal, but if so, we should probably do the same for atom flags and change - // users of this to use stable_sort. - return type1 < type2; -} - -template <class ELFT> -void Segment<ELFT>::assignFileOffsets(uint64_t startOffset) { - uint64_t fileOffset = startOffset; - uint64_t curSliceFileOffset = fileOffset; - bool isDataPageAlignedForNMagic = false; - bool alignSegments = this->_ctx.alignSegments(); - uint64_t p_align = this->_ctx.getPageSize(); - uint64_t lastVirtualAddress = 0; - - this->setFileOffset(startOffset); - for (auto &slice : slices()) { - bool isFirstSection = true; - for (auto section : slice->sections()) { - // Handle linker script expressions, which may change the offset - if (!isFirstSection) - if (auto expr = dyn_cast<ExpressionChunk<ELFT>>(section)) - fileOffset += expr->virtualAddr() - lastVirtualAddress; - // Align fileoffset to the alignment of the section. - fileOffset = llvm::RoundUpToAlignment(fileOffset, section->alignment()); - // If the linker outputmagic is set to OutputMagic::NMAGIC, align the Data - // to a page boundary - if (isFirstSection && - _outputMagic != ELFLinkingContext::OutputMagic::NMAGIC && - _outputMagic != ELFLinkingContext::OutputMagic::OMAGIC) { - // Align to a page only if the output is not - // OutputMagic::NMAGIC/OutputMagic::OMAGIC - if (alignSegments) - fileOffset = llvm::RoundUpToAlignment(fileOffset, p_align); - else { - // Align according to ELF spec. - // in p75, http://www.sco.com/developers/devspecs/gabi41.pdf - uint64_t virtualAddress = slice->virtualAddr(); - Section<ELFT> *sect = dyn_cast<Section<ELFT>>(section); - if (sect && sect->isLoadableSection() && - ((virtualAddress & (p_align - 1)) != - (fileOffset & (p_align - 1)))) - fileOffset = llvm::RoundUpToAlignment(fileOffset, p_align) + - (virtualAddress % p_align); - } - } else if (!isDataPageAlignedForNMagic && needAlign(section)) { - fileOffset = - llvm::RoundUpToAlignment(fileOffset, this->_ctx.getPageSize()); - isDataPageAlignedForNMagic = true; - } - if (isFirstSection) { - slice->setFileOffset(fileOffset); - isFirstSection = false; - curSliceFileOffset = fileOffset; - } - section->setFileOffset(fileOffset); - fileOffset += section->fileSize(); - lastVirtualAddress = section->virtualAddr() + section->memSize(); - } - slice->setFileSize(fileOffset - curSliceFileOffset); - } - this->setFileSize(fileOffset - startOffset); -} - -/// \brief Assign virtual addresses to the slices -template <class ELFT> void Segment<ELFT>::assignVirtualAddress(uint64_t addr) { - int startSection = 0; - int currSection = 0; - SectionIter startSectionIter; - - // 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 curSliceAddress = 0; - - startSectionIter = _sections.begin(); - startSection = 0; - bool isFirstSection = true; - bool isDataPageAlignedForNMagic = false; - uint64_t startAddr = addr; - SegmentSlice<ELFT> *slice = nullptr; - uint64_t tlsStartAddr = 0; - bool alignSegments = this->_ctx.alignSegments(); - StringRef prevOutputSectionName = StringRef(); - - for (auto si = _sections.begin(); si != _sections.end(); ++si) { - // If this is first section in the segment, page align the section start - // address. The linker needs to align the data section to a page boundary - // only if NMAGIC is set. - if (isFirstSection) { - isFirstSection = false; - if (alignSegments && - _outputMagic != ELFLinkingContext::OutputMagic::NMAGIC && - _outputMagic != ELFLinkingContext::OutputMagic::OMAGIC) - // Align to a page only if the output is not - // OutputMagic::NMAGIC/OutputMagic::OMAGIC - startAddr = - llvm::RoundUpToAlignment(startAddr, this->_ctx.getPageSize()); - else if (!isDataPageAlignedForNMagic && needAlign(*si)) { - // If the linker outputmagic is set to OutputMagic::NMAGIC, align the - // Data to a page boundary. - startAddr = - llvm::RoundUpToAlignment(startAddr, this->_ctx.getPageSize()); - isDataPageAlignedForNMagic = true; - } - // align the startOffset to the section alignment - uint64_t newAddr = llvm::RoundUpToAlignment(startAddr, (*si)->alignment()); - // Handle linker script expressions, which *may update newAddr* if the - // expression assigns to "." - if (auto expr = dyn_cast<ExpressionChunk<ELFT>>(*si)) - expr->evalExpr(newAddr); - curSliceAddress = newAddr; - sliceAlign = (*si)->alignment(); - (*si)->setVirtualAddr(curSliceAddress); - - // Handle TLS. - if (auto section = dyn_cast<Section<ELFT>>(*si)) { - if (section->getSegmentType() == llvm::ELF::PT_TLS) { - tlsStartAddr = - llvm::RoundUpToAlignment(tlsStartAddr, (*si)->alignment()); - section->assignVirtualAddress(tlsStartAddr); - tlsStartAddr += (*si)->memSize(); - } else { - section->assignVirtualAddress(newAddr); - } - } - // 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 ((*si)->order() != TargetLayout<ELFT>::ORDER_TBSS) - curSliceSize = (*si)->memSize(); - } else { - uint64_t curAddr = curSliceAddress + curSliceSize; - if (!isDataPageAlignedForNMagic && needAlign(*si)) { - // If the linker outputmagic is set to OutputMagic::NMAGIC, align the - // Data - // to a page boundary - curAddr = llvm::RoundUpToAlignment(curAddr, this->_ctx.getPageSize()); - isDataPageAlignedForNMagic = true; - } - uint64_t newAddr = llvm::RoundUpToAlignment(curAddr, (*si)->alignment()); - // Handle linker script expressions, which *may update newAddr* if the - // expression assigns to "." - if (auto expr = dyn_cast<ExpressionChunk<ELFT>>(*si)) - expr->evalExpr(newAddr); - Section<ELFT> *sec = dyn_cast<Section<ELFT>>(*si); - StringRef curOutputSectionName; - if (sec) - curOutputSectionName = sec->outputSectionName(); - else { - // If this is a linker script expression, propagate the name of the - // previous section instead - if (isa<ExpressionChunk<ELFT>>(*si)) - curOutputSectionName = prevOutputSectionName; - else - curOutputSectionName = (*si)->name(); - } - bool autoCreateSlice = true; - if (curOutputSectionName == prevOutputSectionName) - autoCreateSlice = false; - // If the newAddress computed is more than a page away, let's create - // a separate segment, so that memory is not used up while running. - // Dont create a slice, if the new section falls in the same output - // section as the previous section. - if (autoCreateSlice && ((newAddr - curAddr) > this->_ctx.getPageSize()) && - (_outputMagic != ELFLinkingContext::OutputMagic::NMAGIC && - _outputMagic != ELFLinkingContext::OutputMagic::OMAGIC)) { - auto sliceIter = - std::find_if(_segmentSlices.begin(), _segmentSlices.end(), - [startSection](SegmentSlice<ELFT> *s) -> bool { - return s->startSection() == startSection; - }); - if (sliceIter == _segmentSlices.end()) { - slice = new (_segmentAllocate.Allocate<SegmentSlice<ELFT>>()) - SegmentSlice<ELFT>(); - _segmentSlices.push_back(slice); - } else { - slice = (*sliceIter); - } - slice->setStart(startSection); - slice->setSections(make_range(startSectionIter, si)); - slice->setMemSize(curSliceSize); - slice->setAlign(sliceAlign); - slice->setVirtualAddr(curSliceAddress); - // Start new slice - curSliceAddress = newAddr; - (*si)->setVirtualAddr(curSliceAddress); - startSectionIter = si; - startSection = currSection; - if (auto section = dyn_cast<Section<ELFT>>(*si)) - section->assignVirtualAddress(newAddr); - curSliceSize = newAddr - curSliceAddress + (*si)->memSize(); - sliceAlign = (*si)->alignment(); - } else { - if (sliceAlign < (*si)->alignment()) - sliceAlign = (*si)->alignment(); - (*si)->setVirtualAddr(newAddr); - // Handle TLS. - if (auto section = dyn_cast<Section<ELFT>>(*si)) { - if (section->getSegmentType() == llvm::ELF::PT_TLS) { - tlsStartAddr = - llvm::RoundUpToAlignment(tlsStartAddr, (*si)->alignment()); - section->assignVirtualAddress(tlsStartAddr); - tlsStartAddr += (*si)->memSize(); - } else { - section->assignVirtualAddress(newAddr); - } - } - // 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 ((*si)->order() != TargetLayout<ELFT>::ORDER_TBSS) - curSliceSize = newAddr - curSliceAddress + (*si)->memSize(); - else - curSliceSize = newAddr - curSliceAddress; - } - prevOutputSectionName = curOutputSectionName; - } - currSection++; - } - auto sliceIter = std::find_if(_segmentSlices.begin(), _segmentSlices.end(), - [startSection](SegmentSlice<ELFT> *s) -> bool { - return s->startSection() == startSection; - }); - if (sliceIter == _segmentSlices.end()) { - slice = new (_segmentAllocate.Allocate<SegmentSlice<ELFT>>()) - SegmentSlice<ELFT>(); - _segmentSlices.push_back(slice); - } else { - slice = (*sliceIter); - } - slice->setStart(startSection); - slice->setVirtualAddr(curSliceAddress); - slice->setMemSize(curSliceSize); - slice->setSections(make_range(startSectionIter, _sections.end())); - slice->setAlign(sliceAlign); - - // Set the segment memory size and the virtual address. - this->setMemSize(curSliceAddress - startAddr + curSliceSize); - this->setVirtualAddr(curSliceAddress); - std::stable_sort(_segmentSlices.begin(), _segmentSlices.end(), - SegmentSlice<ELFT>::compare_slices); -} - -// 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 |

