Put sections to segments according to linker scripts if available. Rework the code of TargetLayout::assignSectionsToSegments so it operates on the given list of segments, which can be either read from linker scripts or constructed as before. Handle NONE segments defined in linker scripts by putting corresponding sections to PT_NULL segment. Consider flags set for segments through linker scripts. Differential Revision: http://reviews.llvm.org/D10918 llvm-svn: 243002
742 lines
24 KiB
C++
742 lines
24 KiB
C++
//===- lib/ReaderWriter/ELF/TargetLayout.cpp ------------------------------===//
|
|
//
|
|
// The LLVM Linker
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "TargetLayout.h"
|
|
#include "llvm/ADT/ArrayRef.h"
|
|
#include "llvm/ADT/StringSwitch.h"
|
|
#include "llvm/Support/Errc.h"
|
|
|
|
namespace lld {
|
|
namespace elf {
|
|
|
|
template <class ELFT>
|
|
typename TargetLayout<ELFT>::SectionOrder
|
|
TargetLayout<ELFT>::getSectionOrder(StringRef name, int32_t contentType,
|
|
int32_t contentPermissions) {
|
|
switch (contentType) {
|
|
case DefinedAtom::typeResolver:
|
|
case DefinedAtom::typeCode:
|
|
return llvm::StringSwitch<typename TargetLayout<ELFT>::SectionOrder>(name)
|
|
.StartsWith(".eh_frame_hdr", ORDER_EH_FRAMEHDR)
|
|
.StartsWith(".eh_frame", ORDER_EH_FRAME)
|
|
.StartsWith(".init", ORDER_INIT)
|
|
.StartsWith(".fini", ORDER_FINI)
|
|
.StartsWith(".hash", ORDER_HASH)
|
|
.Default(ORDER_TEXT);
|
|
|
|
case DefinedAtom::typeConstant:
|
|
return ORDER_RODATA;
|
|
|
|
case DefinedAtom::typeData:
|
|
case DefinedAtom::typeDataFast:
|
|
return llvm::StringSwitch<typename TargetLayout<ELFT>::SectionOrder>(name)
|
|
.StartsWith(".init_array", ORDER_INIT_ARRAY)
|
|
.StartsWith(".fini_array", ORDER_FINI_ARRAY)
|
|
.StartsWith(".dynamic", ORDER_DYNAMIC)
|
|
.StartsWith(".ctors", ORDER_CTORS)
|
|
.StartsWith(".dtors", ORDER_DTORS)
|
|
.Default(ORDER_DATA);
|
|
|
|
case DefinedAtom::typeZeroFill:
|
|
case DefinedAtom::typeZeroFillFast:
|
|
return ORDER_BSS;
|
|
|
|
case DefinedAtom::typeGOT:
|
|
return llvm::StringSwitch<typename TargetLayout<ELFT>::SectionOrder>(name)
|
|
.StartsWith(".got.plt", ORDER_GOT_PLT)
|
|
.Default(ORDER_GOT);
|
|
|
|
case DefinedAtom::typeStub:
|
|
return ORDER_PLT;
|
|
|
|
case DefinedAtom::typeRONote:
|
|
return ORDER_RO_NOTE;
|
|
|
|
case DefinedAtom::typeRWNote:
|
|
return ORDER_RW_NOTE;
|
|
|
|
case DefinedAtom::typeNoAlloc:
|
|
return ORDER_NOALLOC;
|
|
|
|
case DefinedAtom::typeThreadData:
|
|
return ORDER_TDATA;
|
|
case DefinedAtom::typeThreadZeroFill:
|
|
return ORDER_TBSS;
|
|
default:
|
|
// If we get passed in a section push it to OTHER
|
|
if (contentPermissions == DefinedAtom::perm___)
|
|
return ORDER_OTHER;
|
|
|
|
return ORDER_NOT_DEFINED;
|
|
}
|
|
}
|
|
|
|
/// \brief This maps the input sections to the output section names
|
|
template <class ELFT>
|
|
StringRef TargetLayout<ELFT>::getInputSectionName(const DefinedAtom *da) const {
|
|
if (da->sectionChoice() == DefinedAtom::sectionBasedOnContent) {
|
|
switch (da->contentType()) {
|
|
case DefinedAtom::typeCode:
|
|
return ".text";
|
|
case DefinedAtom::typeData:
|
|
return ".data";
|
|
case DefinedAtom::typeConstant:
|
|
return ".rodata";
|
|
case DefinedAtom::typeZeroFill:
|
|
return ".bss";
|
|
case DefinedAtom::typeThreadData:
|
|
return ".tdata";
|
|
case DefinedAtom::typeThreadZeroFill:
|
|
return ".tbss";
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return da->customSectionName();
|
|
}
|
|
|
|
/// \brief This maps the input sections to the output section names.
|
|
template <class ELFT>
|
|
StringRef
|
|
TargetLayout<ELFT>::getOutputSectionName(StringRef archivePath,
|
|
StringRef memberPath,
|
|
StringRef inputSectionName) const {
|
|
StringRef outputSectionName;
|
|
if (_linkerScriptSema.hasLayoutCommands()) {
|
|
script::Sema::SectionKey key = {archivePath, memberPath, inputSectionName};
|
|
outputSectionName = _linkerScriptSema.getOutputSection(key);
|
|
if (!outputSectionName.empty())
|
|
return outputSectionName;
|
|
}
|
|
return llvm::StringSwitch<StringRef>(inputSectionName)
|
|
.StartsWith(".text", ".text")
|
|
.StartsWith(".ctors", ".ctors")
|
|
.StartsWith(".dtors", ".dtors")
|
|
.StartsWith(".rodata", ".rodata")
|
|
.StartsWith(".gcc_except_table", ".gcc_except_table")
|
|
.StartsWith(".data.rel.ro", ".data.rel.ro")
|
|
.StartsWith(".data.rel.local", ".data.rel.local")
|
|
.StartsWith(".data", ".data")
|
|
.StartsWith(".tdata", ".tdata")
|
|
.StartsWith(".tbss", ".tbss")
|
|
.StartsWith(".init_array", ".init_array")
|
|
.StartsWith(".fini_array", ".fini_array")
|
|
.Default(inputSectionName);
|
|
}
|
|
|
|
/// \brief Gets the segment for a output section
|
|
template <class ELFT>
|
|
typename TargetLayout<ELFT>::SegmentType
|
|
TargetLayout<ELFT>::getSegmentType(const Section<ELFT> *section) const {
|
|
switch (section->order()) {
|
|
case ORDER_INTERP:
|
|
return llvm::ELF::PT_INTERP;
|
|
|
|
case ORDER_TEXT:
|
|
case ORDER_HASH:
|
|
case ORDER_DYNAMIC_SYMBOLS:
|
|
case ORDER_DYNAMIC_STRINGS:
|
|
case ORDER_DYNAMIC_RELOCS:
|
|
case ORDER_DYNAMIC_PLT_RELOCS:
|
|
case ORDER_REL:
|
|
case ORDER_INIT:
|
|
case ORDER_PLT:
|
|
case ORDER_FINI:
|
|
case ORDER_RODATA:
|
|
case ORDER_EH_FRAME:
|
|
case ORDER_CTORS:
|
|
case ORDER_DTORS:
|
|
return llvm::ELF::PT_LOAD;
|
|
|
|
case ORDER_RO_NOTE:
|
|
case ORDER_RW_NOTE:
|
|
return llvm::ELF::PT_NOTE;
|
|
|
|
case ORDER_DYNAMIC:
|
|
return llvm::ELF::PT_DYNAMIC;
|
|
|
|
case ORDER_EH_FRAMEHDR:
|
|
return llvm::ELF::PT_GNU_EH_FRAME;
|
|
|
|
case ORDER_GOT:
|
|
case ORDER_GOT_PLT:
|
|
case ORDER_DATA:
|
|
case ORDER_BSS:
|
|
case ORDER_INIT_ARRAY:
|
|
case ORDER_FINI_ARRAY:
|
|
return llvm::ELF::PT_LOAD;
|
|
|
|
case ORDER_TDATA:
|
|
case ORDER_TBSS:
|
|
return llvm::ELF::PT_TLS;
|
|
|
|
default:
|
|
return llvm::ELF::PT_NULL;
|
|
}
|
|
}
|
|
|
|
template <class ELFT>
|
|
bool TargetLayout<ELFT>::hasOutputSegment(Section<ELFT> *section) {
|
|
switch (section->order()) {
|
|
case ORDER_INTERP:
|
|
case ORDER_HASH:
|
|
case ORDER_DYNAMIC_SYMBOLS:
|
|
case ORDER_DYNAMIC_STRINGS:
|
|
case ORDER_DYNAMIC_RELOCS:
|
|
case ORDER_DYNAMIC_PLT_RELOCS:
|
|
case ORDER_REL:
|
|
case ORDER_INIT:
|
|
case ORDER_PLT:
|
|
case ORDER_TEXT:
|
|
case ORDER_FINI:
|
|
case ORDER_RODATA:
|
|
case ORDER_EH_FRAME:
|
|
case ORDER_EH_FRAMEHDR:
|
|
case ORDER_TDATA:
|
|
case ORDER_TBSS:
|
|
case ORDER_RO_NOTE:
|
|
case ORDER_RW_NOTE:
|
|
case ORDER_DYNAMIC:
|
|
case ORDER_CTORS:
|
|
case ORDER_DTORS:
|
|
case ORDER_GOT:
|
|
case ORDER_GOT_PLT:
|
|
case ORDER_DATA:
|
|
case ORDER_INIT_ARRAY:
|
|
case ORDER_FINI_ARRAY:
|
|
case ORDER_BSS:
|
|
case ORDER_NOALLOC:
|
|
return true;
|
|
default:
|
|
return section->hasOutputSegment();
|
|
}
|
|
}
|
|
|
|
template <class ELFT>
|
|
AtomSection<ELFT> *
|
|
TargetLayout<ELFT>::createSection(StringRef sectionName, int32_t contentType,
|
|
DefinedAtom::ContentPermissions permissions,
|
|
SectionOrder sectionOrder) {
|
|
return new (_allocator) AtomSection<ELFT>(_ctx, sectionName, contentType,
|
|
permissions, sectionOrder);
|
|
}
|
|
|
|
template <class ELFT>
|
|
AtomSection<ELFT> *
|
|
TargetLayout<ELFT>::getSection(StringRef sectionName, int32_t contentType,
|
|
DefinedAtom::ContentPermissions permissions,
|
|
const DefinedAtom *da) {
|
|
const SectionKey sectionKey(sectionName, permissions, da->file().path());
|
|
SectionOrder sectionOrder =
|
|
getSectionOrder(sectionName, contentType, permissions);
|
|
auto sec = _sectionMap.find(sectionKey);
|
|
if (sec != _sectionMap.end())
|
|
return sec->second;
|
|
AtomSection<ELFT> *newSec =
|
|
createSection(sectionName, contentType, permissions, sectionOrder);
|
|
|
|
newSec->setOutputSectionName(getOutputSectionName(
|
|
da->file().archivePath(), da->file().memberPath(), sectionName));
|
|
newSec->setOrder(sectionOrder);
|
|
newSec->setArchiveNameOrPath(da->file().archivePath());
|
|
newSec->setMemberNameOrPath(da->file().memberPath());
|
|
_sections.push_back(newSec);
|
|
_sectionMap.insert(std::make_pair(sectionKey, newSec));
|
|
return newSec;
|
|
}
|
|
|
|
template <class ELFT>
|
|
ErrorOr<const AtomLayout *> TargetLayout<ELFT>::addAtom(const Atom *atom) {
|
|
if (const DefinedAtom *definedAtom = dyn_cast<DefinedAtom>(atom)) {
|
|
// HACK: Ignore undefined atoms. We need to adjust the interface so that
|
|
// undefined atoms can still be included in the output symbol table for
|
|
// -noinhibit-exec.
|
|
if (definedAtom->contentType() == DefinedAtom::typeUnknown)
|
|
return make_error_code(llvm::errc::invalid_argument);
|
|
const DefinedAtom::ContentPermissions permissions =
|
|
definedAtom->permissions();
|
|
const DefinedAtom::ContentType contentType = definedAtom->contentType();
|
|
|
|
StringRef sectionName = getInputSectionName(definedAtom);
|
|
AtomSection<ELFT> *section =
|
|
getSection(sectionName, contentType, permissions, definedAtom);
|
|
|
|
// Add runtime relocations to the .rela section.
|
|
for (const auto &reloc : *definedAtom) {
|
|
bool isLocalReloc = true;
|
|
if (_ctx.isDynamicRelocation(*reloc)) {
|
|
getDynamicRelocationTable()->addRelocation(*definedAtom, *reloc);
|
|
isLocalReloc = false;
|
|
} else if (_ctx.isPLTRelocation(*reloc)) {
|
|
getPLTRelocationTable()->addRelocation(*definedAtom, *reloc);
|
|
isLocalReloc = false;
|
|
}
|
|
|
|
if (!reloc->target())
|
|
continue;
|
|
|
|
// Ignore undefined atoms that are not target of dynamic relocations
|
|
if (isa<UndefinedAtom>(reloc->target()) && isLocalReloc)
|
|
continue;
|
|
|
|
if (_ctx.isCopyRelocation(*reloc)) {
|
|
_copiedDynSymNames.insert(definedAtom->name());
|
|
continue;
|
|
}
|
|
|
|
_referencedDynAtoms.insert(reloc->target());
|
|
}
|
|
return section->appendAtom(atom);
|
|
}
|
|
|
|
const AbsoluteAtom *absoluteAtom = cast<AbsoluteAtom>(atom);
|
|
// Absolute atoms are not part of any section, they are global for the whole
|
|
// link
|
|
_absoluteAtoms.push_back(
|
|
new (_allocator) AtomLayout(absoluteAtom, 0, absoluteAtom->value()));
|
|
return _absoluteAtoms.back();
|
|
}
|
|
|
|
/// Output sections with the same name into a OutputSection
|
|
template <class ELFT> void TargetLayout<ELFT>::createOutputSections() {
|
|
OutputSection<ELFT> *outputSection;
|
|
|
|
for (auto &si : _sections) {
|
|
Section<ELFT> *section = dyn_cast<Section<ELFT>>(si);
|
|
if (!section)
|
|
continue;
|
|
const std::pair<StringRef, OutputSection<ELFT> *> currentOutputSection(
|
|
section->outputSectionName(), nullptr);
|
|
std::pair<typename OutputSectionMapT::iterator, bool> outputSectionInsert(
|
|
_outputSectionMap.insert(currentOutputSection));
|
|
if (!outputSectionInsert.second) {
|
|
outputSection = outputSectionInsert.first->second;
|
|
} else {
|
|
outputSection = new (_allocator.Allocate<OutputSection<ELFT>>())
|
|
OutputSection<ELFT>(section->outputSectionName());
|
|
_outputSections.push_back(outputSection);
|
|
outputSectionInsert.first->second = outputSection;
|
|
}
|
|
outputSection->appendSection(section);
|
|
}
|
|
}
|
|
|
|
template <class ELFT>
|
|
std::vector<const script::PHDR *>
|
|
TargetLayout<ELFT>::getCustomSegments(const OutputSection<ELFT> *sec) const {
|
|
std::vector<const script::PHDR *> phdrs;
|
|
if (_linkerScriptSema.getPHDRsForOutputSection(sec->name(), phdrs)) {
|
|
llvm::report_fatal_error(
|
|
"Linker script has wrong segments set for output sections");
|
|
}
|
|
return phdrs;
|
|
}
|
|
|
|
template <class ELFT>
|
|
std::vector<const script::PHDR *>
|
|
TargetLayout<ELFT>::getCustomSegments(const Section<ELFT> *section) const {
|
|
auto sec = section->getOutputSection();
|
|
assert(sec && "Output section should be already set for input section");
|
|
return getCustomSegments(sec);
|
|
}
|
|
|
|
template <class ELFT>
|
|
std::vector<typename TargetLayout<ELFT>::SegmentKey>
|
|
TargetLayout<ELFT>::getSegmentsForSection(const OutputSection<ELFT> *os,
|
|
const Section<ELFT> *sec) const {
|
|
std::vector<SegmentKey> segKeys;
|
|
auto phdrs = getCustomSegments(os);
|
|
if (!phdrs.empty()) {
|
|
if (phdrs.size() == 1 && phdrs[0]->isNone()) {
|
|
segKeys.emplace_back("NONE", llvm::ELF::PT_NULL, 0, false);
|
|
return segKeys;
|
|
}
|
|
|
|
for (auto phdr : phdrs) {
|
|
segKeys.emplace_back(phdr->name(), phdr->type(), phdr->flags(), true);
|
|
}
|
|
return segKeys;
|
|
}
|
|
|
|
uint64_t flags = getLookupSectionFlags(os);
|
|
int64_t segmentType = getSegmentType(sec);
|
|
StringRef segmentName = sec->segmentKindToStr();
|
|
|
|
// We need a separate segment for sections that don't have
|
|
// the segment type to be PT_LOAD
|
|
if (segmentType != llvm::ELF::PT_LOAD)
|
|
segKeys.emplace_back(segmentName, segmentType, flags, false);
|
|
|
|
if (segmentType == llvm::ELF::PT_NULL)
|
|
return segKeys;
|
|
|
|
// If the output magic is set to OutputMagic::NMAGIC or
|
|
// OutputMagic::OMAGIC, Place the data alongside text in one single
|
|
// segment
|
|
ELFLinkingContext::OutputMagic outputMagic = _ctx.getOutputMagic();
|
|
if (outputMagic == ELFLinkingContext::OutputMagic::NMAGIC ||
|
|
outputMagic == ELFLinkingContext::OutputMagic::OMAGIC)
|
|
flags =
|
|
llvm::ELF::SHF_EXECINSTR | llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_WRITE;
|
|
|
|
segKeys.emplace_back("LOAD", llvm::ELF::PT_LOAD, flags, false);
|
|
return segKeys;
|
|
}
|
|
|
|
template <class ELFT>
|
|
uint64_t
|
|
TargetLayout<ELFT>::getLookupSectionFlags(const OutputSection<ELFT> *os) const {
|
|
uint64_t flags = os->flags();
|
|
if (!(flags & llvm::ELF::SHF_WRITE) && _ctx.mergeRODataToTextSegment())
|
|
flags &= ~llvm::ELF::SHF_EXECINSTR;
|
|
|
|
// Merge string sections into Data segment itself
|
|
flags &= ~(llvm::ELF::SHF_STRINGS | llvm::ELF::SHF_MERGE);
|
|
|
|
// Merge the TLS section into the DATA segment itself
|
|
flags &= ~(llvm::ELF::SHF_TLS);
|
|
return flags;
|
|
}
|
|
|
|
template <class ELFT> void TargetLayout<ELFT>::assignSectionsToSegments() {
|
|
ScopedTask task(getDefaultDomain(), "assignSectionsToSegments");
|
|
// sort the sections by their order as defined by the layout
|
|
sortInputSections();
|
|
|
|
// Create output sections.
|
|
createOutputSections();
|
|
|
|
// Finalize output section layout.
|
|
finalizeOutputSectionLayout();
|
|
|
|
// Set the ordinal after sorting the sections
|
|
int ordinal = 1;
|
|
for (auto osi : _outputSections) {
|
|
osi->setOrdinal(ordinal);
|
|
for (auto ai : osi->sections()) {
|
|
ai->setOrdinal(ordinal);
|
|
}
|
|
++ordinal;
|
|
}
|
|
for (auto osi : _outputSections) {
|
|
for (auto section : osi->sections()) {
|
|
if (!hasOutputSegment(section))
|
|
continue;
|
|
|
|
osi->setLoadableSection(section->isLoadableSection());
|
|
osi->setHasSegment();
|
|
|
|
auto segKeys = getSegmentsForSection(osi, section);
|
|
assert(!segKeys.empty() && "Must always be at least one segment");
|
|
section->setSegmentType(segKeys[0]._type);
|
|
|
|
for (auto key : segKeys) {
|
|
// Try to find non-load (real) segment type if possible
|
|
if (key._type != llvm::ELF::PT_LOAD)
|
|
section->setSegmentType(key._type);
|
|
|
|
const std::pair<SegmentKey, Segment<ELFT> *> currentSegment(key,
|
|
nullptr);
|
|
std::pair<typename SegmentMapT::iterator, bool> segmentInsert(
|
|
_segmentMap.insert(currentSegment));
|
|
Segment<ELFT> *segment;
|
|
if (!segmentInsert.second) {
|
|
segment = segmentInsert.first->second;
|
|
} else {
|
|
segment = new (_allocator) Segment<ELFT>(_ctx, key._name, key._type);
|
|
if (key._segmentFlags)
|
|
segment->setSegmentFlags(key._flags);
|
|
segmentInsert.first->second = segment;
|
|
_segments.push_back(segment);
|
|
}
|
|
if (key._type == llvm::ELF::PT_LOAD) {
|
|
// Insert chunks with linker script expressions that occur at this
|
|
// point, just before appending a new input section
|
|
addExtraChunksToSegment(segment, section->archivePath(),
|
|
section->memberPath(),
|
|
section->inputSectionName());
|
|
}
|
|
segment->append(section);
|
|
}
|
|
}
|
|
}
|
|
if (_ctx.isDynamic() && !_ctx.isDynamicLibrary()) {
|
|
Segment<ELFT> *segment = new (_allocator) ProgramHeaderSegment<ELFT>(_ctx);
|
|
_segments.push_back(segment);
|
|
segment->append(_elfHeader);
|
|
segment->append(_programHeader);
|
|
}
|
|
}
|
|
|
|
template <class ELFT> void TargetLayout<ELFT>::sortSegments() {
|
|
std::sort(_segments.begin(), _segments.end(), Segment<ELFT>::compareSegments);
|
|
}
|
|
|
|
template <class ELFT> void TargetLayout<ELFT>::assignVirtualAddress() {
|
|
if (_segments.empty())
|
|
return;
|
|
|
|
sortSegments();
|
|
|
|
uint64_t baseAddress = _ctx.getBaseAddress();
|
|
|
|
// HACK: This is a super dirty hack. The elf header and program header are
|
|
// not part of a section, but we need them to be loaded at the base address
|
|
// so that AT_PHDR is set correctly by the loader and so they are accessible
|
|
// at runtime. To do this we simply prepend them to the first loadable Segment
|
|
// and let the layout logic take care of it.
|
|
Segment<ELFT> *firstLoadSegment = nullptr;
|
|
for (auto si : _segments) {
|
|
if (si->segmentType() == llvm::ELF::PT_LOAD) {
|
|
firstLoadSegment = si;
|
|
si->firstSection()->setAlign(si->alignment());
|
|
break;
|
|
}
|
|
}
|
|
assert(firstLoadSegment != nullptr && "No loadable segment!");
|
|
firstLoadSegment->prepend(_programHeader);
|
|
firstLoadSegment->prepend(_elfHeader);
|
|
bool newSegmentHeaderAdded = true;
|
|
bool virtualAddressAssigned = false;
|
|
bool fileOffsetAssigned = false;
|
|
while (true) {
|
|
for (auto si : _segments) {
|
|
si->finalize();
|
|
// Don't add PT_NULL segments into the program header
|
|
if (si->segmentType() != llvm::ELF::PT_NULL)
|
|
newSegmentHeaderAdded = _programHeader->addSegment(si);
|
|
}
|
|
if (!newSegmentHeaderAdded && virtualAddressAssigned)
|
|
break;
|
|
uint64_t address = baseAddress;
|
|
// start assigning virtual addresses
|
|
for (auto &si : _segments) {
|
|
if ((si->segmentType() != llvm::ELF::PT_LOAD) &&
|
|
(si->segmentType() != llvm::ELF::PT_NULL))
|
|
continue;
|
|
|
|
if (si->segmentType() == llvm::ELF::PT_NULL) {
|
|
si->assignVirtualAddress(0 /*non loadable*/);
|
|
} else {
|
|
if (virtualAddressAssigned && (address != baseAddress) &&
|
|
(address == si->virtualAddr()))
|
|
break;
|
|
si->assignVirtualAddress(address);
|
|
}
|
|
address = si->virtualAddr() + si->memSize();
|
|
}
|
|
uint64_t baseFileOffset = 0;
|
|
uint64_t fileoffset = baseFileOffset;
|
|
for (auto &si : _segments) {
|
|
if ((si->segmentType() != llvm::ELF::PT_LOAD) &&
|
|
(si->segmentType() != llvm::ELF::PT_NULL))
|
|
continue;
|
|
if (fileOffsetAssigned && (fileoffset != baseFileOffset) &&
|
|
(fileoffset == si->fileOffset()))
|
|
break;
|
|
si->assignFileOffsets(fileoffset);
|
|
fileoffset = si->fileOffset() + si->fileSize();
|
|
}
|
|
virtualAddressAssigned = true;
|
|
fileOffsetAssigned = true;
|
|
_programHeader->resetProgramHeaders();
|
|
}
|
|
Section<ELFT> *section;
|
|
// Fix the offsets of all the atoms within a section
|
|
for (auto &si : _sections) {
|
|
section = dyn_cast<Section<ELFT>>(si);
|
|
if (section && TargetLayout<ELFT>::hasOutputSegment(section))
|
|
section->assignFileOffsets(section->fileOffset());
|
|
}
|
|
// Set the size of the merged Sections
|
|
for (auto osi : _outputSections) {
|
|
uint64_t sectionfileoffset = 0;
|
|
uint64_t startFileOffset = 0;
|
|
uint64_t sectionsize = 0;
|
|
bool isFirstSection = true;
|
|
for (auto si : osi->sections()) {
|
|
if (isFirstSection) {
|
|
startFileOffset = si->fileOffset();
|
|
isFirstSection = false;
|
|
}
|
|
sectionfileoffset = si->fileOffset();
|
|
sectionsize = si->fileSize();
|
|
}
|
|
sectionsize = (sectionfileoffset - startFileOffset) + sectionsize;
|
|
osi->setFileOffset(startFileOffset);
|
|
osi->setSize(sectionsize);
|
|
}
|
|
// Set the virtual addr of the merged Sections
|
|
for (auto osi : _outputSections) {
|
|
uint64_t sectionstartaddr = 0;
|
|
uint64_t startaddr = 0;
|
|
uint64_t sectionsize = 0;
|
|
bool isFirstSection = true;
|
|
for (auto si : osi->sections()) {
|
|
if (isFirstSection) {
|
|
startaddr = si->virtualAddr();
|
|
isFirstSection = false;
|
|
}
|
|
sectionstartaddr = si->virtualAddr();
|
|
sectionsize = si->memSize();
|
|
}
|
|
sectionsize = (sectionstartaddr - startaddr) + sectionsize;
|
|
osi->setMemSize(sectionsize);
|
|
osi->setAddr(startaddr);
|
|
}
|
|
}
|
|
|
|
template <class ELFT>
|
|
void TargetLayout<ELFT>::assignFileOffsetsForMiscSections() {
|
|
uint64_t fileoffset = 0;
|
|
uint64_t size = 0;
|
|
for (auto si : _segments) {
|
|
// Don't calculate offsets from non loadable segments
|
|
if ((si->segmentType() != llvm::ELF::PT_LOAD) &&
|
|
(si->segmentType() != llvm::ELF::PT_NULL))
|
|
continue;
|
|
fileoffset = si->fileOffset();
|
|
size = si->fileSize();
|
|
}
|
|
fileoffset = fileoffset + size;
|
|
Section<ELFT> *section;
|
|
for (auto si : _sections) {
|
|
section = dyn_cast<Section<ELFT>>(si);
|
|
if (section && TargetLayout<ELFT>::hasOutputSegment(section))
|
|
continue;
|
|
fileoffset = llvm::RoundUpToAlignment(fileoffset, si->alignment());
|
|
si->setFileOffset(fileoffset);
|
|
si->setVirtualAddr(0);
|
|
fileoffset += si->fileSize();
|
|
}
|
|
}
|
|
|
|
template <class ELFT> void TargetLayout<ELFT>::sortInputSections() {
|
|
// First, sort according to default layout's order
|
|
std::stable_sort(
|
|
_sections.begin(), _sections.end(),
|
|
[](Chunk<ELFT> *A, Chunk<ELFT> *B) { return A->order() < B->order(); });
|
|
|
|
if (!_linkerScriptSema.hasLayoutCommands())
|
|
return;
|
|
|
|
// Sort the sections by their order as defined by the linker script
|
|
std::stable_sort(
|
|
this->_sections.begin(), this->_sections.end(),
|
|
[this](Chunk<ELFT> *A, Chunk<ELFT> *B) {
|
|
auto *a = dyn_cast<Section<ELFT>>(A);
|
|
auto *b = dyn_cast<Section<ELFT>>(B);
|
|
|
|
if (a == nullptr)
|
|
return false;
|
|
if (b == nullptr)
|
|
return true;
|
|
|
|
return _linkerScriptSema.less(
|
|
{a->archivePath(), a->memberPath(), a->inputSectionName()},
|
|
{b->archivePath(), b->memberPath(), b->inputSectionName()});
|
|
});
|
|
// Now try to arrange sections with no mapping rules to sections with
|
|
// similar content
|
|
auto p = this->_sections.begin();
|
|
// Find first section that has no assigned rule id
|
|
while (p != this->_sections.end()) {
|
|
auto *sect = dyn_cast<AtomSection<ELFT>>(*p);
|
|
if (!sect)
|
|
break;
|
|
|
|
if (!_linkerScriptSema.hasMapping({sect->archivePath(), sect->memberPath(),
|
|
sect->inputSectionName()}))
|
|
break;
|
|
|
|
++p;
|
|
}
|
|
// For all sections that have no assigned rule id, try to move them near a
|
|
// section with similar contents
|
|
if (p != this->_sections.begin()) {
|
|
for (; p != this->_sections.end(); ++p) {
|
|
auto q = p;
|
|
--q;
|
|
while (q != this->_sections.begin() &&
|
|
(*q)->getContentType() != (*p)->getContentType())
|
|
--q;
|
|
if ((*q)->getContentType() != (*p)->getContentType())
|
|
continue;
|
|
++q;
|
|
for (auto i = p; i != q;) {
|
|
auto next = i--;
|
|
std::iter_swap(i, next);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
template <class ELFT>
|
|
const AtomLayout *
|
|
TargetLayout<ELFT>::findAtomLayoutByName(StringRef name) const {
|
|
for (auto sec : _sections)
|
|
if (auto section = dyn_cast<Section<ELFT>>(sec))
|
|
if (auto *al = section->findAtomLayoutByName(name))
|
|
return al;
|
|
return nullptr;
|
|
}
|
|
|
|
template <class ELFT>
|
|
void TargetLayout<ELFT>::addExtraChunksToSegment(Segment<ELFT> *segment,
|
|
StringRef archivePath,
|
|
StringRef memberPath,
|
|
StringRef sectionName) {
|
|
if (!_linkerScriptSema.hasLayoutCommands())
|
|
return;
|
|
std::vector<const script::SymbolAssignment *> exprs =
|
|
_linkerScriptSema.getExprs({archivePath, memberPath, sectionName});
|
|
for (auto expr : exprs) {
|
|
auto expChunk =
|
|
new (this->_allocator) ExpressionChunk<ELFT>(this->_ctx, expr);
|
|
segment->append(expChunk);
|
|
}
|
|
}
|
|
|
|
template <class ELFT>
|
|
RelocationTable<ELFT> *TargetLayout<ELFT>::getDynamicRelocationTable() {
|
|
if (!_dynamicRelocationTable) {
|
|
_dynamicRelocationTable = createRelocationTable(
|
|
_ctx.isRelaOutputFormat() ? ".rela.dyn" : ".rel.dyn",
|
|
ORDER_DYNAMIC_RELOCS);
|
|
addSection(_dynamicRelocationTable.get());
|
|
}
|
|
return _dynamicRelocationTable.get();
|
|
}
|
|
|
|
template <class ELFT>
|
|
RelocationTable<ELFT> *TargetLayout<ELFT>::getPLTRelocationTable() {
|
|
if (!_pltRelocationTable) {
|
|
_pltRelocationTable = createRelocationTable(
|
|
_ctx.isRelaOutputFormat() ? ".rela.plt" : ".rel.plt",
|
|
ORDER_DYNAMIC_PLT_RELOCS);
|
|
addSection(_pltRelocationTable.get());
|
|
}
|
|
return _pltRelocationTable.get();
|
|
}
|
|
|
|
template <class ELFT> uint64_t TargetLayout<ELFT>::getTLSSize() const {
|
|
for (const auto &phdr : *_programHeader)
|
|
if (phdr->p_type == llvm::ELF::PT_TLS)
|
|
return phdr->p_memsz;
|
|
return 0;
|
|
}
|
|
|
|
template class TargetLayout<ELF32LE>;
|
|
template class TargetLayout<ELF32BE>;
|
|
template class TargetLayout<ELF64LE>;
|
|
template class TargetLayout<ELF64BE>;
|
|
|
|
} // end namespace elf
|
|
} // end namespace lld
|