//===- 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. // //===----------------------------------------------------------------------===// #include "SegmentChunks.h" #include "TargetLayout.h" namespace lld { namespace elf { template bool SegmentSlice::compare_slices(SegmentSlice *a, SegmentSlice *b) { return a->startSection() < b->startSection(); } template Segment::Segment(const ELFLinkingContext &ctx, StringRef name, const typename TargetLayout::SegmentType type) : Chunk(name, Chunk::Kind::ELFSegment, ctx), _segmentType(type), _flags(0), _atomflags(0), _segmentFlags(false) { 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; } } // This function actually is used, but not in all instantiations of Segment. LLVM_ATTRIBUTE_UNUSED static DefinedAtom::ContentPermissions toAtomPermsSegment(uint64_t flags) { switch (flags & (llvm::ELF::PF_R | llvm::ELF::PF_W | llvm::ELF::PF_X)) { case llvm::ELF::PF_R | llvm::ELF::PF_W | llvm::ELF::PF_X: return DefinedAtom::permRWX; case llvm::ELF::PF_R | llvm::ELF::PF_X: return DefinedAtom::permR_X; case llvm::ELF::PF_R: return DefinedAtom::permR__; case llvm::ELF::PF_R | llvm::ELF::PF_W: return DefinedAtom::permRW_; default: return DefinedAtom::permUnknown; } } template void Segment::append(Chunk *chunk) { _sections.push_back(chunk); Section *section = dyn_cast>(chunk); if (!section) return; if (this->_alignment < section->alignment()) this->_alignment = section->alignment(); if (_segmentFlags) return; if (_flags < section->getFlags()) _flags |= section->getFlags(); if (_atomflags < toAtomPerms(_flags)) _atomflags = toAtomPerms(_flags); } template bool Segment::compareSegments(Segment *sega, Segment *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 void Segment::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); bool changeOffset = false; uint64_t newOffset = 0; for (auto &slice : slices()) { bool isFirstSection = true; for (auto section : slice->sections()) { // Handle linker script expressions, which may change the offset if (auto expr = dyn_cast>(section)) { if (!isFirstSection) { changeOffset = true; newOffset = fileOffset + expr->virtualAddr() - lastVirtualAddress; } continue; } if (changeOffset) { changeOffset = false; fileOffset = newOffset; } // 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); // Align according to ELF spec. // in p75, http://www.sco.com/developers/devspecs/gabi41.pdf uint64_t virtualAddress = slice->virtualAddr(); Section *sect = dyn_cast>(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(); } changeOffset = false; slice->setFileSize(fileOffset - curSliceFileOffset); } this->setFileSize(fileOffset - startOffset); } /// \brief Assign virtual addresses to the slices template void Segment::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 isDataPageAlignedForNMagic = false; uint64_t startAddr = addr; SegmentSlice *slice = nullptr; uint64_t tlsStartAddr = 0; bool alignSegments = this->_ctx.alignSegments(); StringRef prevOutputSectionName = StringRef(); uint64_t tbssMemsize = 0; // 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. auto si = _sections.begin(); if (si != _sections.end()) { 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 (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>(*si)) expr->evalExpr(newAddr); curSliceAddress = newAddr; sliceAlign = (*si)->alignment(); (*si)->setVirtualAddr(curSliceAddress); // Handle TLS. if (auto section = dyn_cast>(*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::ORDER_TBSS) { curSliceSize = (*si)->memSize(); tbssMemsize = 0; } else { tbssMemsize = (*si)->memSize(); } ++currSection; ++si; } uint64_t scriptAddr = 0; bool forceScriptAddr = false; for (auto e = _sections.end(); si != e; ++si) { 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( forceScriptAddr ? scriptAddr : curAddr, (*si)->alignment()); forceScriptAddr = false; // Handle linker script expressions, which may force an address change if // the expression assigns to "." if (auto expr = dyn_cast>(*si)) { uint64_t oldAddr = newAddr; expr->evalExpr(newAddr); if (oldAddr != newAddr) { forceScriptAddr = true; scriptAddr = newAddr; } (*si)->setVirtualAddr(newAddr); continue; } Section *sec = dyn_cast>(*si); StringRef curOutputSectionName = sec ? sec->outputSectionName() : (*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 *s) -> bool { return s->startSection() == startSection; }); if (sliceIter == _segmentSlices.end()) { slice = new (_segmentAllocate.Allocate>()) SegmentSlice(); _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; if ((*si)->order() == TargetLayout::ORDER_TBSS) curSliceAddress += tbssMemsize; (*si)->setVirtualAddr(curSliceAddress); startSectionIter = si; startSection = currSection; if (auto section = dyn_cast>(*si)) section->assignVirtualAddress(newAddr); curSliceSize = newAddr - curSliceAddress + (*si)->memSize(); sliceAlign = (*si)->alignment(); } else { if (sliceAlign < (*si)->alignment()) sliceAlign = (*si)->alignment(); if ((*si)->order() == TargetLayout::ORDER_TBSS) newAddr += tbssMemsize; (*si)->setVirtualAddr(newAddr); // Handle TLS. if (auto section = dyn_cast>(*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::ORDER_TBSS) { curSliceSize = newAddr - curSliceAddress + (*si)->memSize(); tbssMemsize = 0; } else { // Although TBSS section does not contribute to memory of any segment, // we still need to keep track its total size to correct write it // down. Since it is done based on curSliceAddress, we need to add // add it to virtual address. tbssMemsize = (*si)->memSize(); } } prevOutputSectionName = curOutputSectionName; ++currSection; } auto sliceIter = std::find_if(_segmentSlices.begin(), _segmentSlices.end(), [startSection](SegmentSlice *s) -> bool { return s->startSection() == startSection; }); if (sliceIter == _segmentSlices.end()) { slice = new (_segmentAllocate.Allocate>()) SegmentSlice(); _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(startAddr); std::stable_sort(_segmentSlices.begin(), _segmentSlices.end(), SegmentSlice::compare_slices); } // Write the Segment template void Segment::write(ELFWriter *writer, TargetLayout &layout, llvm::FileOutputBuffer &buffer) { for (auto slice : slices()) for (auto section : slice->sections()) section->write(writer, layout, buffer); } template int64_t Segment::flags() const { if (_segmentFlags) return (int64_t)_flags; 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; } template void Segment::setSegmentFlags(uint64_t flags) { assert(!_segmentFlags && !_flags && "Flags has already been set"); _segmentFlags = true; _flags = flags; _atomflags = toAtomPermsSegment(flags); } template void Segment::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; } } template int Segment::getContentType() const { int64_t fl = flags(); switch (_segmentType) { case llvm::ELF::PT_LOAD: { if (fl && llvm::ELF::PF_X) return Chunk::ContentType::Code; if (fl && llvm::ELF::PF_W) return Chunk::ContentType::Data; } case llvm::ELF::PT_TLS: return Chunk::ContentType::TLS; case llvm::ELF::PT_NOTE: return Chunk::ContentType::Note; default: return Chunk::ContentType::Unknown; } } template int64_t Segment::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; } } /// \brief Check if the chunk needs to be aligned template bool Segment::needAlign(Chunk *chunk) const { if (chunk->getContentType() == Chunk::ContentType::Data && _outputMagic == ELFLinkingContext::OutputMagic::NMAGIC) return true; return false; } template void ProgramHeaderSegment::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(); } #define INSTANTIATE(klass) \ template class klass; \ template class klass; \ template class klass; \ template class klass INSTANTIATE(ExpressionChunk); INSTANTIATE(ProgramHeaderSegment); INSTANTIATE(Segment); INSTANTIATE(SegmentSlice); } // end namespace elf } // end namespace lld