//===- 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) { 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 void Segment::append(Chunk *chunk) { _sections.push_back(chunk); Section *section = dyn_cast>(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 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); 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>(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 *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(); } 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 isFirstSection = true; bool isDataPageAlignedForNMagic = false; uint64_t startAddr = addr; SegmentSlice *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>(*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(); } 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>(*si)) expr->evalExpr(newAddr); Section *sec = dyn_cast>(*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>(*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 *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; (*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(); (*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(); else curSliceSize = newAddr - curSliceAddress; } 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(curSliceAddress); 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 { 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::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