diff options
Diffstat (limited to 'lld/MachO/OutputSegment.cpp')
| -rw-r--r-- | lld/MachO/OutputSegment.cpp | 126 |
1 files changed, 122 insertions, 4 deletions
diff --git a/lld/MachO/OutputSegment.cpp b/lld/MachO/OutputSegment.cpp index c00e819ea3a4..3bbaf7f0304e 100644 --- a/lld/MachO/OutputSegment.cpp +++ b/lld/MachO/OutputSegment.cpp @@ -7,12 +7,14 @@ //===----------------------------------------------------------------------===// #include "OutputSegment.h" +#include "ConcatOutputSection.h" #include "InputSection.h" -#include "MergedOutputSection.h" +#include "Symbols.h" #include "SyntheticSections.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Memory.h" +#include "llvm/ADT/StringSwitch.h" #include "llvm/BinaryFormat/MachO.h" using namespace llvm; @@ -21,6 +23,12 @@ using namespace lld; using namespace lld::macho; static uint32_t initProt(StringRef name) { + auto it = find_if( + config->segmentProtections, + [&](const SegmentProtection &segprot) { return segprot.name == name; }); + if (it != config->segmentProtections.end()) + return it->initProt; + if (name == segment_names::text) return VM_PROT_READ | VM_PROT_EXECUTE; if (name == segment_names::pageZero) @@ -31,28 +39,138 @@ static uint32_t initProt(StringRef name) { } static uint32_t maxProt(StringRef name) { - assert(config->arch != AK_i386 && + assert(config->arch() != AK_i386 && "TODO: i386 has different maxProt requirements"); return initProt(name); } size_t OutputSegment::numNonHiddenSections() const { size_t count = 0; - for (const OutputSection *osec : sections) { + for (const OutputSection *osec : sections) count += (!osec->isHidden() ? 1 : 0); - } return count; } void OutputSegment::addOutputSection(OutputSection *osec) { + inputOrder = std::min(inputOrder, osec->inputOrder); + osec->parent = this; sections.push_back(osec); + + for (const SectionAlign §Align : config->sectionAlignments) + if (sectAlign.segName == name && sectAlign.sectName == osec->name) + osec->align = sectAlign.align; +} + +template <typename T, typename F> static auto compareByOrder(F ord) { + return [=](T a, T b) { return ord(a) < ord(b); }; +} + +static int segmentOrder(OutputSegment *seg) { + return StringSwitch<int>(seg->name) + .Case(segment_names::pageZero, -4) + .Case(segment_names::text, -3) + .Case(segment_names::dataConst, -2) + .Case(segment_names::data, -1) + .Case(segment_names::llvm, std::numeric_limits<int>::max() - 1) + // Make sure __LINKEDIT is the last segment (i.e. all its hidden + // sections must be ordered after other sections). + .Case(segment_names::linkEdit, std::numeric_limits<int>::max()) + .Default(seg->inputOrder); +} + +static int sectionOrder(OutputSection *osec) { + StringRef segname = osec->parent->name; + // Sections are uniquely identified by their segment + section name. + if (segname == segment_names::text) { + return StringSwitch<int>(osec->name) + .Case(section_names::header, -4) + .Case(section_names::text, -3) + .Case(section_names::stubs, -2) + .Case(section_names::stubHelper, -1) + .Case(section_names::unwindInfo, std::numeric_limits<int>::max() - 1) + .Case(section_names::ehFrame, std::numeric_limits<int>::max()) + .Default(osec->inputOrder); + } else if (segname == segment_names::data || + segname == segment_names::dataConst) { + // For each thread spawned, dyld will initialize its TLVs by copying the + // address range from the start of the first thread-local data section to + // the end of the last one. We therefore arrange these sections contiguously + // to minimize the amount of memory used. Additionally, since zerofill + // sections must be at the end of their segments, and since TLV data + // sections can be zerofills, we end up putting all TLV data sections at the + // end of the segment. + switch (sectionType(osec->flags)) { + case S_THREAD_LOCAL_VARIABLE_POINTERS: + return std::numeric_limits<int>::max() - 3; + case S_THREAD_LOCAL_REGULAR: + return std::numeric_limits<int>::max() - 2; + case S_THREAD_LOCAL_ZEROFILL: + return std::numeric_limits<int>::max() - 1; + case S_ZEROFILL: + return std::numeric_limits<int>::max(); + default: + return StringSwitch<int>(osec->name) + .Case(section_names::got, -3) + .Case(section_names::lazySymbolPtr, -2) + .Case(section_names::const_, -1) + .Default(osec->inputOrder); + } + } else if (segname == segment_names::linkEdit) { + return StringSwitch<int>(osec->name) + .Case(section_names::rebase, -10) + .Case(section_names::binding, -9) + .Case(section_names::weakBinding, -8) + .Case(section_names::lazyBinding, -7) + .Case(section_names::export_, -6) + .Case(section_names::functionStarts, -5) + .Case(section_names::dataInCode, -4) + .Case(section_names::symbolTable, -3) + .Case(section_names::indirectSymbolTable, -2) + .Case(section_names::stringTable, -1) + .Case(section_names::codeSignature, std::numeric_limits<int>::max()) + .Default(osec->inputOrder); + } + // ZeroFill sections must always be the at the end of their segments: + // dyld checks if a segment's file size is smaller than its in-memory + // size to detect if a segment has zerofill sections, and if so it maps + // the missing tail as zerofill. + if (sectionType(osec->flags) == S_ZEROFILL) + return std::numeric_limits<int>::max(); + return osec->inputOrder; +} + +void OutputSegment::sortOutputSections() { + // Must be stable_sort() to keep special sections such as + // S_THREAD_LOCAL_REGULAR in input order. + llvm::stable_sort(sections, compareByOrder<OutputSection *>(sectionOrder)); +} + +void OutputSegment::assignAddressesToStartEndSymbols() { + for (Defined *d : segmentStartSymbols) + d->value = addr; + for (Defined *d : segmentEndSymbols) + d->value = addr + vmSize; +} + +void macho::sortOutputSegments() { + llvm::stable_sort(outputSegments, + compareByOrder<OutputSegment *>(segmentOrder)); } static DenseMap<StringRef, OutputSegment *> nameToOutputSegment; std::vector<OutputSegment *> macho::outputSegments; +static StringRef maybeRenameSegment(StringRef name) { + auto newName = config->segmentRenameMap.find(name); + if (newName != config->segmentRenameMap.end()) + return newName->second; + return name; +} + OutputSegment *macho::getOrCreateOutputSegment(StringRef name) { + name = maybeRenameSegment(name); + OutputSegment *&segRef = nameToOutputSegment[name]; if (segRef) return segRef; |
