diff --git a/lld/MachO/CMakeLists.txt b/lld/MachO/CMakeLists.txt --- a/lld/MachO/CMakeLists.txt +++ b/lld/MachO/CMakeLists.txt @@ -11,6 +11,7 @@ Arch/ARM64_32.cpp Arch/X86_64.cpp UnwindInfoSection.cpp + ConcatOutputSection.cpp Driver.cpp DriverUtils.cpp Dwarf.cpp @@ -18,7 +19,6 @@ InputFiles.cpp InputSection.cpp LTO.cpp - MergedOutputSection.cpp ObjC.cpp OutputSection.cpp OutputSegment.cpp diff --git a/lld/MachO/MergedOutputSection.h b/lld/MachO/ConcatOutputSection.h rename from lld/MachO/MergedOutputSection.h rename to lld/MachO/ConcatOutputSection.h --- a/lld/MachO/MergedOutputSection.h +++ b/lld/MachO/ConcatOutputSection.h @@ -1,4 +1,4 @@ -//===- OutputSection.h ------------------------------------------*- C++ -*-===// +//===- ConcatOutputSection.h ------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -24,26 +24,26 @@ // files that are labeled with the same segment and section name. This class // contains all such sections and writes the data from each section sequentially // in the final binary. -class MergedOutputSection : public OutputSection { +class ConcatOutputSection : public OutputSection { public: - MergedOutputSection(StringRef name) : OutputSection(MergedKind, name) {} + ConcatOutputSection(StringRef name) : OutputSection(MergedKind, name) {} - const InputSection *firstSection() const { return inputs.front(); } - const InputSection *lastSection() const { return inputs.back(); } + const ConcatInputSection *firstSection() const { return inputs.front(); } + const ConcatInputSection *lastSection() const { return inputs.back(); } // These accessors will only be valid after finalizing the section uint64_t getSize() const override { return size; } uint64_t getFileSize() const override { return fileSize; } - void mergeInput(InputSection *input); + void addInput(ConcatInputSection *input); void finalize() override; bool needsThunks() const; uint64_t estimateStubsInRangeVA(size_t callIdx) const; void writeTo(uint8_t *buf) const override; - std::vector inputs; - std::vector thunks; + std::vector inputs; + std::vector thunks; static bool classof(const OutputSection *sec) { return sec->kind() == MergedKind; @@ -68,8 +68,8 @@ struct ThunkInfo { // These denote the active thunk: - Defined *sym = nullptr; // private-extern symbol for active thunk - InputSection *isec = nullptr; // input section for active thunk + Defined *sym = nullptr; // private-extern symbol for active thunk + ConcatInputSection *isec = nullptr; // input section for active thunk // The following values are cumulative across all thunks on this function uint32_t callSiteCount = 0; // how many calls to the real function? diff --git a/lld/MachO/MergedOutputSection.cpp b/lld/MachO/ConcatOutputSection.cpp rename from lld/MachO/MergedOutputSection.cpp rename to lld/MachO/ConcatOutputSection.cpp --- a/lld/MachO/MergedOutputSection.cpp +++ b/lld/MachO/ConcatOutputSection.cpp @@ -1,4 +1,4 @@ -//===- OutputSection.cpp --------------------------------------------------===// +//===- ConcatOutputSection.cpp --------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -#include "MergedOutputSection.h" +#include "ConcatOutputSection.h" #include "Config.h" #include "OutputSegment.h" #include "SymbolTable.h" @@ -25,7 +25,7 @@ using namespace lld; using namespace lld::macho; -void MergedOutputSection::mergeInput(InputSection *input) { +void ConcatOutputSection::addInput(ConcatInputSection *input) { if (inputs.empty()) { align = input->align; flags = input->flags; @@ -119,7 +119,7 @@ // instructions, whereas CISC (i.e., x86) generally doesn't. RISC only needs // thunks for programs so large that branch source & destination addresses // might differ more than the range of branch instruction(s). -bool MergedOutputSection::needsThunks() const { +bool ConcatOutputSection::needsThunks() const { if (!target->usesThunks()) return false; uint64_t isecAddr = addr; @@ -135,7 +135,7 @@ auto *sym = r.referent.get(); // Pre-populate the thunkMap and memoize call site counts for every // InputSection and ThunkInfo. We do this for the benefit of - // MergedOutputSection::estimateStubsInRangeVA() + // ConcatOutputSection::estimateStubsInRangeVA() ThunkInfo &thunkInfo = thunkMap[sym]; // Knowing ThunkInfo call site count will help us know whether or not we // might need to create more for this referent at the time we are @@ -151,10 +151,10 @@ // Since __stubs is placed after __text, we must estimate the address // beyond which stubs are within range of a simple forward branch. -uint64_t MergedOutputSection::estimateStubsInRangeVA(size_t callIdx) const { +uint64_t ConcatOutputSection::estimateStubsInRangeVA(size_t callIdx) const { uint64_t branchRange = target->branchRange; size_t endIdx = inputs.size(); - InputSection *isec = inputs[callIdx]; + ConcatInputSection *isec = inputs[callIdx]; uint64_t isecVA = isec->getVA(); // Tally the non-stub functions which still have call sites // remaining to process, which yields the maximum number @@ -185,10 +185,10 @@ return stubsInRangeVA; } -void MergedOutputSection::finalize() { +void ConcatOutputSection::finalize() { uint64_t isecAddr = addr; uint64_t isecFileOff = fileOff; - auto finalizeOne = [&](InputSection *isec) { + auto finalizeOne = [&](ConcatInputSection *isec) { isecAddr = alignTo(isecAddr, isec->align); isecFileOff = alignTo(isecFileOff, isec->align); isec->outSecOff = isecAddr - addr; @@ -199,7 +199,7 @@ }; if (!needsThunks()) { - for (InputSection *isec : inputs) + for (ConcatInputSection *isec : inputs) finalizeOne(isec); size = isecAddr - addr; fileSize = isecFileOff - fileOff; @@ -221,7 +221,7 @@ ++callIdx) { if (finalIdx == callIdx) finalizeOne(inputs[finalIdx++]); - InputSection *isec = inputs[callIdx]; + ConcatInputSection *isec = inputs[callIdx]; assert(isec->isFinal); uint64_t isecVA = isec->getVA(); // Assign addresses up-to the forward branch-range limit @@ -290,7 +290,7 @@ // unfinalized inputs[finalIdx]. fatal(Twine(__FUNCTION__) + ": FIXME: thunk range overrun"); } - thunkInfo.isec = make(); + thunkInfo.isec = make(); thunkInfo.isec->name = isec->name; thunkInfo.isec->segname = isec->segname; thunkInfo.isec->parent = this; @@ -317,7 +317,7 @@ ", thunks = " + std::to_string(thunkCount)); } -void MergedOutputSection::writeTo(uint8_t *buf) const { +void ConcatOutputSection::writeTo(uint8_t *buf) const { // Merge input sections from thunk & ordinary vectors size_t i = 0, ie = inputs.size(); size_t t = 0, te = thunks.size(); @@ -337,7 +337,7 @@ // TODO: this is most likely wrong; reconsider how section flags // are actually merged. The logic presented here was written without // any form of informed research. -void MergedOutputSection::mergeFlags(InputSection *input) { +void ConcatOutputSection::mergeFlags(InputSection *input) { uint8_t baseType = flags & SECTION_TYPE; uint8_t inputType = input->flags & SECTION_TYPE; if (baseType != inputType) diff --git a/lld/MachO/Config.h b/lld/MachO/Config.h --- a/lld/MachO/Config.h +++ b/lld/MachO/Config.h @@ -99,6 +99,7 @@ bool emitEncryptionInfo = false; bool timeTraceEnabled = false; bool dataConst = false; + bool mergeLiterals = true; uint32_t headerPad; uint32_t dylibCompatibilityVersion = 0; uint32_t dylibCurrentVersion = 0; diff --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp --- a/lld/MachO/Driver.cpp +++ b/lld/MachO/Driver.cpp @@ -505,7 +505,7 @@ if (common == nullptr) continue; - auto *isec = make(); + auto *isec = make(); isec->file = common->getFile(); isec->name = section_names::common; isec->segname = segment_names::data; @@ -985,6 +985,7 @@ config->implicitDylibs = !args.hasArg(OPT_no_implicit_dylibs); config->emitFunctionStarts = !args.hasArg(OPT_no_function_starts); config->emitBitcodeBundle = args.hasArg(OPT_bitcode_bundle); + config->mergeLiterals = !args.hasArg(OPT_no_literal_merge); std::array encryptablePlatforms{ PlatformKind::iOS, PlatformKind::watchOS, PlatformKind::tvOS}; diff --git a/lld/MachO/InputFiles.h b/lld/MachO/InputFiles.h --- a/lld/MachO/InputFiles.h +++ b/lld/MachO/InputFiles.h @@ -39,7 +39,8 @@ namespace macho { struct PlatformInfo; -class InputSection; +class ConcatInputSection; +class CStringInputSection; class Symbol; struct Reloc; enum class RefState : uint8_t; @@ -103,7 +104,7 @@ llvm::DWARFUnit *compileUnit = nullptr; const uint32_t modTime; - std::vector debugSections; + std::vector debugSections; private: template void parse(); diff --git a/lld/MachO/InputFiles.cpp b/lld/MachO/InputFiles.cpp --- a/lld/MachO/InputFiles.cpp +++ b/lld/MachO/InputFiles.cpp @@ -53,6 +53,7 @@ #include "OutputSegment.h" #include "SymbolTable.h" #include "Symbols.h" +#include "SyntheticSections.h" #include "Target.h" #include "lld/Common/DWARF.h" @@ -240,37 +241,55 @@ InputFile::InputFile(Kind kind, const InterfaceFile &interface) : id(idCount++), fileKind(kind), name(saver.save(interface.getPath())) {} +template +static void parseSection(ObjFile *file, const uint8_t *buf, const Section &sec, + InputSection *isec) { + isec->file = file; + isec->name = + StringRef(sec.sectname, strnlen(sec.sectname, sizeof(sec.sectname))); + isec->segname = + StringRef(sec.segname, strnlen(sec.segname, sizeof(sec.segname))); + isec->data = {isZeroFill(sec.flags) ? nullptr : buf + sec.offset, + static_cast(sec.size)}; + if (sec.align >= 32) + error("alignment " + std::to_string(sec.align) + " of section " + + isec->name + " is too large"); + else + isec->align = 1 << sec.align; + isec->flags = sec.flags; +} + template void ObjFile::parseSections(ArrayRef
sections) { subsections.reserve(sections.size()); auto *buf = reinterpret_cast(mb.getBufferStart()); for (const Section &sec : sections) { - InputSection *isec = make(); - isec->file = this; - isec->name = - StringRef(sec.sectname, strnlen(sec.sectname, sizeof(sec.sectname))); - isec->segname = - StringRef(sec.segname, strnlen(sec.segname, sizeof(sec.segname))); - isec->data = {isZeroFill(sec.flags) ? nullptr : buf + sec.offset, - static_cast(sec.size)}; - if (sec.align >= 32) - error("alignment " + std::to_string(sec.align) + " of section " + - isec->name + " is too large"); - else - isec->align = 1 << sec.align; - isec->flags = sec.flags; - - if (!(isDebugSection(isec->flags) && - isec->segname == segment_names::dwarf)) { + if (config->mergeLiterals && sectionType(sec.flags) == S_CSTRING_LITERALS) { + if (sec.nreloc) + fatal(toString(this) + + " contains relocations in a cstring_literals section, so LLD " + "cannot do string merging. Try re-running with " + "--no-literal-merge."); + + auto *isec = make(); + parseSection(this, buf, sec, isec); + isec->splitIntoPieces(); // FIXME: parallelize this? subsections.push_back({{0, isec}}); } else { - // Instead of emitting DWARF sections, we emit STABS symbols to the - // object files that contain them. We filter them out early to avoid - // parsing their relocations unnecessarily. But we must still push an - // empty map to ensure the indices line up for the remaining sections. - subsections.push_back({}); - debugSections.push_back(isec); + auto *isec = make(); + parseSection(this, buf, sec, isec); + if (!(isDebugSection(isec->flags) && + isec->segname == segment_names::dwarf)) { + subsections.push_back({{0, isec}}); + } else { + // Instead of emitting DWARF sections, we emit STABS symbols to the + // object files that contain them. We filter them out early to avoid + // parsing their relocations unnecessarily. But we must still push an + // empty map to ensure the indices line up for the remaining sections. + subsections.push_back({}); + debugSections.push_back(isec); + } } } } @@ -586,6 +605,8 @@ uint64_t sectionAddr = sectionHeaders[i].addr; uint32_t sectionAlign = 1u << sectionHeaders[i].align; + // FIXME: handle __cstring + // We populate subsecMap by repeatedly splitting the last (highest address) // subsection. SubsectionEntry subsecEntry = subsecMap.back(); @@ -601,20 +622,22 @@ j + 1 < symbolIndices.size() ? nList[symbolIndices[j + 1]].n_value - sym.n_value : isec->data.size() - symbolOffset; - // There are 3 cases where we do not need to create a new subsection: + // There are 4 cases where we do not need to create a new subsection: // 1. If the input file does not use subsections-via-symbols. // 2. Multiple symbols at the same address only induce one subsection. // (The symbolOffset == 0 check covers both this case as well as // the first loop iteration.) // 3. Alternative entry points do not induce new subsections. + // 4. If we have a literal section (e.g. __cstring and __literal4). if (!subsectionsViaSymbols || symbolOffset == 0 || - sym.n_desc & N_ALT_ENTRY) { + sym.n_desc & N_ALT_ENTRY || !isa(isec)) { symbols[symIndex] = createDefined(sym, name, isec, symbolOffset, symbolSize); continue; } + auto *concatIsec = cast(isec); - auto *nextIsec = make(*isec); + auto *nextIsec = make(*concatIsec); nextIsec->data = isec->data.slice(symbolOffset); nextIsec->numRefs = 0; nextIsec->canOmitFromOutput = false; @@ -637,7 +660,7 @@ OpaqueFile::OpaqueFile(MemoryBufferRef mb, StringRef segName, StringRef sectName) : InputFile(OpaqueKind, mb) { - InputSection *isec = make(); + ConcatInputSection *isec = make(); isec->file = this; isec->name = sectName.take_front(16); isec->segname = segName.take_front(16); diff --git a/lld/MachO/InputSection.h b/lld/MachO/InputSection.h --- a/lld/MachO/InputSection.h +++ b/lld/MachO/InputSection.h @@ -13,6 +13,7 @@ #include "lld/Common/LLVM.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/CachedHashString.h" #include "llvm/BinaryFormat/MachO.h" namespace lld { @@ -23,11 +24,21 @@ class InputSection { public: + enum Kind { + ConcatKind, + CStringLiteralKind, + }; + + Kind kind() const { return sectionKind; } virtual ~InputSection() = default; virtual uint64_t getSize() const { return data.size(); } uint64_t getFileSize() const; - uint64_t getFileOffset() const; - uint64_t getVA() const; + // Translates \p off -- an offset relative to this InputSection -- into an + // offset from the beginning of its parent OutputSection. + virtual uint64_t getOffset(uint64_t off) const = 0; + // The offset from the beginning of the file. + virtual uint64_t getFileOffset(uint64_t off) const = 0; + uint64_t getVA(uint64_t off) const; void writeTo(uint8_t *buf); @@ -36,8 +47,6 @@ StringRef segname; OutputSection *parent = nullptr; - uint64_t outSecOff = 0; - uint64_t outSecFileOff = 0; uint32_t align = 1; uint32_t flags = 0; @@ -61,6 +70,75 @@ ArrayRef data; std::vector relocs; + +protected: + InputSection(Kind kind) : sectionKind(kind) {} + +private: + Kind sectionKind; +}; + +// ConcatInputSections are combined into (Concat)OutputSections through simple +// concatentation, in contrast with literal sections which may have their +// contents merged before output. +class ConcatInputSection : public InputSection { +public: + ConcatInputSection() : InputSection(ConcatKind) {} + uint64_t getFileOffset(uint64_t off) const override; + uint64_t getOffset(uint64_t off) const override { return outSecOff + off; } + uint64_t getVA() const { return InputSection::getVA(0); } + + static bool classof(const InputSection *isec) { + return isec->kind() == ConcatKind; + } + + uint64_t outSecOff = 0; + uint64_t outSecFileOff = 0; +}; + +struct StringPiece { + uint32_t inSecOff; + uint32_t hash; + uint64_t outSecOff; + + StringPiece(uint64_t off, uint32_t hash) : inSecOff(off), hash(hash) {} +}; + +// CStringInputSections are composed of multiple null-terminated string +// literals, which we represent using StringPieces. These literals can be +// deduplicated and tail-merged, so translating offsets between the input and +// outputs sections is more complicated. +// +// NOTE: One significant difference between LLD and ld64 is that we merge all +// cstring literals, even those referenced directly by non-private symbols. +// ld64 is more conservative and does not do that. This was mostly done for +// implementation simplicity; if we find programs that need the more +// conservative behavior we can certainly implement that. +class CStringInputSection : public InputSection { +public: + CStringInputSection() : InputSection(CStringLiteralKind) {} + uint64_t getFileOffset(uint64_t off) const override; + uint64_t getOffset(uint64_t off) const override; + // Find the StringPiece that contains this offset. + const StringPiece *getStringPiece(uint64_t off) const; + // Split at each null byte. + void splitIntoPieces(); + + // Returns i'th piece as a CachedHashStringRef. This function is very hot when + // string merging is enabled, so we want to inline. + LLVM_ATTRIBUTE_ALWAYS_INLINE + llvm::CachedHashStringRef getCachedHashStringRef(size_t i) const { + size_t begin = pieces[i].inSecOff; + size_t end = + (pieces.size() - 1 == i) ? data.size() : pieces[i + 1].inSecOff; + return {toStringRef(data.slice(begin, end - begin)), pieces[i].hash}; + } + + static bool classof(const InputSection *isec) { + return isec->kind() == CStringLiteralKind; + } + + std::vector pieces; }; inline uint8_t sectionType(uint32_t flags) { @@ -96,6 +174,7 @@ constexpr const char authPtr[] = "__auth_ptr"; constexpr const char binding[] = "__binding"; constexpr const char bitcodeBundle[] = "__bundle"; +constexpr const char cString[] = "__cstring"; constexpr const char cfString[] = "__cfstring"; constexpr const char codeSignature[] = "__code_signature"; constexpr const char common[] = "__common"; diff --git a/lld/MachO/InputSection.cpp b/lld/MachO/InputSection.cpp --- a/lld/MachO/InputSection.cpp +++ b/lld/MachO/InputSection.cpp @@ -15,6 +15,7 @@ #include "Writer.h" #include "lld/Common/Memory.h" #include "llvm/Support/Endian.h" +#include "llvm/Support/xxhash.h" using namespace llvm; using namespace llvm::MachO; @@ -24,15 +25,17 @@ std::vector macho::inputSections; -uint64_t InputSection::getFileOffset() const { - return parent->fileOff + outSecFileOff; +uint64_t ConcatInputSection::getFileOffset(uint64_t off) const { + return parent->fileOff + outSecFileOff + off; } uint64_t InputSection::getFileSize() const { return isZeroFill(flags) ? 0 : getSize(); } -uint64_t InputSection::getVA() const { return parent->addr + outSecOff; } +uint64_t InputSection::getVA(uint64_t off) const { + return parent->addr + getOffset(off); +} static uint64_t resolveSymbolVA(const Symbol *sym, uint8_t type) { const RelocAttrs &relocAttrs = target->getRelocAttrs(type); @@ -62,18 +65,18 @@ const Reloc &minuend = relocs[++i]; uint64_t minuendVA; if (const Symbol *toSym = minuend.referent.dyn_cast()) - minuendVA = toSym->getVA(); + minuendVA = toSym->getVA() + minuend.addend; else { auto *referentIsec = minuend.referent.get(); assert(!referentIsec->shouldOmitFromOutput()); - minuendVA = referentIsec->getVA(); + minuendVA = referentIsec->getVA(minuend.addend); } - referentVA = minuendVA - fromSym->getVA() + minuend.addend; + referentVA = minuendVA - fromSym->getVA(); } else if (auto *referentSym = r.referent.dyn_cast()) { if (target->hasAttr(r.type, RelocAttrBits::LOAD) && !referentSym->isInGot()) target->relaxGotLoad(loc, r.type); - referentVA = resolveSymbolVA(referentSym, r.type); + referentVA = resolveSymbolVA(referentSym, r.type) + r.addend; if (isThreadLocalVariables(flags)) { // References from thread-local variable sections are treated as offsets @@ -85,12 +88,45 @@ } } else if (auto *referentIsec = r.referent.dyn_cast()) { assert(!referentIsec->shouldOmitFromOutput()); - referentVA = referentIsec->getVA(); + referentVA = referentIsec->getVA(r.addend); } - target->relocateOne(loc, r, referentVA + r.addend, getVA() + r.offset); + target->relocateOne(loc, r, referentVA, getVA(r.offset)); + } +} + +void CStringInputSection::splitIntoPieces() { + size_t off = 0; + StringRef s = toStringRef(data); + while (!s.empty()) { + size_t end = s.find(0); + if (end == StringRef::npos) + fatal(toString(this) + ": string is not null terminated"); + size_t size = end + 1; + pieces.emplace_back(off, xxHash64(s.substr(0, size))); + s = s.substr(size); + off += size; } } +const StringPiece *CStringInputSection::getStringPiece(uint64_t off) const { + if (off >= data.size()) + fatal(toString(this) + ": offset is outside the section"); + + auto it = + partition_point(pieces, [=](StringPiece p) { return p.inSecOff <= off; }); + return &it[-1]; +} + +uint64_t CStringInputSection::getFileOffset(uint64_t off) const { + return parent->fileOff + getOffset(off); +} + +uint64_t CStringInputSection::getOffset(uint64_t off) const { + const StringPiece &piece = *getStringPiece(off); + uint64_t addend = off - piece.inSecOff; + return piece.outSecOff + addend; +} + bool macho::isCodeSection(const InputSection *isec) { uint32_t type = isec->flags & SECTION_TYPE; if (type != S_REGULAR && type != S_COALESCED) diff --git a/lld/MachO/Options.td b/lld/MachO/Options.td --- a/lld/MachO/Options.td +++ b/lld/MachO/Options.td @@ -50,6 +50,7 @@ def time_trace_granularity_eq: Joined<["--"], "time-trace-granularity=">, HelpText<"Minimum time granularity (in microseconds) traced by time profiler">; def time_trace_file_eq: Joined<["--"], "time-trace-file=">, HelpText<"Specify time trace output file">; +def no_literal_merge: Flag<["--"], "no-literal-merge">, HelpText<"Disable literal merging">; // This is a complete Options.td compiled from Apple's ld(1) manpage // dated 2018-03-07 and cross checked with ld64 source code in repo diff --git a/lld/MachO/OutputSection.h b/lld/MachO/OutputSection.h --- a/lld/MachO/OutputSection.h +++ b/lld/MachO/OutputSection.h @@ -56,6 +56,10 @@ StringRef name; OutputSegment *parent = nullptr; + // For output sections that don't have explicit ordering requirements, their + // output order should be based on the relative ordering of the input sections + // they contain. + size_t inputOrder = 0; uint32_t index = 0; uint64_t addr = 0; diff --git a/lld/MachO/OutputSegment.h b/lld/MachO/OutputSegment.h --- a/lld/MachO/OutputSegment.h +++ b/lld/MachO/OutputSegment.h @@ -42,7 +42,7 @@ void addOutputSection(OutputSection *os); void sortOutputSections( llvm::function_ref comparator) { - llvm::stable_sort(sections, comparator); + llvm::sort(sections, comparator); } const std::vector &getSections() const { return sections; } @@ -51,6 +51,7 @@ uint64_t fileOff = 0; uint64_t fileSize = 0; uint64_t vmSize = 0; + size_t inputOrder = 0; StringRef name; uint32_t maxProt = 0; uint32_t initProt = 0; diff --git a/lld/MachO/OutputSegment.cpp b/lld/MachO/OutputSegment.cpp --- a/lld/MachO/OutputSegment.cpp +++ b/lld/MachO/OutputSegment.cpp @@ -7,8 +7,8 @@ //===----------------------------------------------------------------------===// #include "OutputSegment.h" +#include "ConcatOutputSection.h" #include "InputSection.h" -#include "MergedOutputSection.h" #include "SyntheticSections.h" #include "lld/Common/ErrorHandler.h" @@ -50,6 +50,9 @@ } void OutputSegment::addOutputSection(OutputSection *osec) { + if (sections.empty()) + inputOrder = osec->inputOrder; + osec->parent = this; sections.push_back(osec); diff --git a/lld/MachO/Symbols.cpp b/lld/MachO/Symbols.cpp --- a/lld/MachO/Symbols.cpp +++ b/lld/MachO/Symbols.cpp @@ -40,13 +40,13 @@ // the address of a function that has not yet been finalized. assert(target->usesThunks()); - // MergedOutputSection::finalize() can seek the address of a + // ConcatOutputSection::finalize() can seek the address of a // function before its address is assigned. The thunking algorithm // knows that unfinalized functions will be out of range, so it is // expedient to return a contrived out-of-range address. return TargetInfo::outOfRangeVA; } - return isec->getVA() + value; + return isec->getVA(value); } uint64_t Defined::getFileOffset() const { @@ -55,7 +55,7 @@ " does not have a file offset"); return 0; } - return isec->getFileOffset() + value; + return isec->getFileOffset(value); } uint64_t DylibSymbol::getVA() const { diff --git a/lld/MachO/SyntheticSections.h b/lld/MachO/SyntheticSections.h --- a/lld/MachO/SyntheticSections.h +++ b/lld/MachO/SyntheticSections.h @@ -18,6 +18,7 @@ #include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/SetVector.h" +#include "llvm/MC/StringTableBuilder.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/raw_ostream.h" @@ -38,6 +39,7 @@ public: SyntheticSection(const char *segname, const char *name); virtual ~SyntheticSection() = default; + virtual void finalizeContents() {} static bool classof(const OutputSection *sec) { return sec->kind() == SyntheticKind; @@ -57,8 +59,6 @@ align = target->wordSize; } - virtual void finalizeContents() {} - // Sections in __LINKEDIT are special: their offsets are recorded in the // load commands like LC_DYLD_INFO_ONLY and LC_SYMTAB, instead of in section // headers. @@ -154,7 +154,7 @@ Location(const InputSection *isec, uint64_t offset) : isec(isec), offset(offset) {} - uint64_t getVA() const { return isec->getVA() + offset; } + uint64_t getVA() const { return isec->getVA(offset); } }; // Stores rebase opcodes, which tell dyld where absolute addresses have been @@ -296,7 +296,7 @@ // have a corresponding entry in the LazyPointerSection. bool addEntry(Symbol *); uint64_t getVA(uint32_t stubsIndex) const { - // MergedOutputSection::finalize() can seek the address of a + // ConcatOutputSection::finalize() can seek the address of a // stub before its address is assigned. Before __stubs is // finalized, return a contrived out-of-range address. return isFinal ? addr + stubsIndex * target->stubSize @@ -326,7 +326,7 @@ // to cache an address to the image loader it uses. Note that unlike the other // synthetic sections, which are OutputSections, the ImageLoaderCacheSection is // an InputSection that gets merged into the __data OutputSection. -class ImageLoaderCacheSection : public InputSection { +class ImageLoaderCacheSection : public ConcatInputSection { public: ImageLoaderCacheSection(); uint64_t getSize() const override { return target->wordSize; } @@ -515,11 +515,24 @@ uint64_t xarSize; }; -static_assert((CodeSignatureSection::blobHeadersSize % 8) == 0, ""); -static_assert((CodeSignatureSection::fixedHeadersSize % 8) == 0, ""); +class CStringSection : public SyntheticSection { +public: + CStringSection(); + void addInput(CStringInputSection *); + uint64_t getSize() const override { return builder.getSize(); } + void finalize() override; + bool isNeeded() const override { return !inputs.empty(); } + void writeTo(uint8_t *buf) const override { builder.write(buf); } + + std::vector inputs; + +private: + llvm::StringTableBuilder builder; +}; struct InStruct { MachHeaderSection *header = nullptr; + CStringSection *cStringSection = nullptr; RebaseSection *rebase = nullptr; BindingSection *binding = nullptr; WeakBindingSection *weakBinding = nullptr; diff --git a/lld/MachO/SyntheticSections.cpp b/lld/MachO/SyntheticSections.cpp --- a/lld/MachO/SyntheticSections.cpp +++ b/lld/MachO/SyntheticSections.cpp @@ -7,11 +7,11 @@ //===----------------------------------------------------------------------===// #include "SyntheticSections.h" +#include "ConcatOutputSection.h" #include "Config.h" #include "ExportTrie.h" #include "InputFiles.h" #include "MachOStructs.h" -#include "MergedOutputSection.h" #include "OutputSegment.h" #include "SymbolTable.h" #include "Symbols.h" @@ -48,11 +48,10 @@ SyntheticSection::SyntheticSection(const char *segname, const char *name) : OutputSection(SyntheticKind, name), segname(segname) { - isec = make(); + isec = make(); isec->segname = segname; isec->name = name; isec->parent = this; - isec->outSecOff = 0; syntheticSections.push_back(this); } @@ -202,10 +201,10 @@ os << static_cast(REBASE_OPCODE_SET_TYPE_IMM | REBASE_TYPE_POINTER); llvm::sort(locations, [](const Location &a, const Location &b) { - return a.isec->getVA() < b.isec->getVA(); + return a.isec->getVA(a.offset) < b.isec->getVA(b.offset); }); for (const Location &loc : locations) - encodeRebase(loc.isec->parent, loc.isec->outSecOff + loc.offset, lastRebase, + encodeRebase(loc.isec->parent, loc.isec->getOffset(loc.offset), lastRebase, os); if (lastRebase.consecutiveCount != 0) encodeDoRebase(lastRebase, os); @@ -366,7 +365,7 @@ lastBinding.ordinal = ordinal; } encodeBinding(b.dysym, b.target.isec->parent, - b.target.isec->outSecOff + b.target.offset, b.addend, + b.target.isec->getOffset(b.target.offset), b.addend, /*isWeakBinding=*/false, lastBinding, os); } if (!bindings.empty()) @@ -395,7 +394,7 @@ }); for (const WeakBindingEntry &b : bindings) encodeBinding(b.symbol, b.target.isec->parent, - b.target.isec->outSecOff + b.target.offset, b.addend, + b.target.isec->getOffset(b.target.offset), b.addend, /*isWeakBinding=*/true, lastBinding, os); if (!bindings.empty() || !definitions.empty()) os << static_cast(BIND_OPCODE_DONE); @@ -922,6 +921,9 @@ } } +static_assert((CodeSignatureSection::blobHeadersSize % 8) == 0, ""); +static_assert((CodeSignatureSection::fixedHeadersSize % 8) == 0, ""); + CodeSignatureSection::CodeSignatureSection() : LinkEditSection(segment_names::linkEdit, section_names::codeSignature) { align = 16; // required by libstuff @@ -1064,6 +1066,40 @@ remove(xarPath); } +CStringSection::CStringSection() + : SyntheticSection(segment_names::text, section_names::cString), + builder(StringTableBuilder::RAW) { + flags = S_CSTRING_LITERALS; // FIXME merge flags properly +} + +void CStringSection::addInput(CStringInputSection *isec) { + align = std::max(align, isec->align); + isec->parent = this; + inputs.push_back(isec); +} + +void CStringSection::finalize() { + // Add all string pieces to the string table builder to create section + // contents. + for (const CStringInputSection *isec : inputs) + for (size_t i = 0, e = isec->pieces.size(); i != e; ++i) + builder.add(isec->getCachedHashStringRef(i)); + + // Fix the string table content. After this, the contents will never change. + builder.finalize(); + + // finalize() fixed tail-optimized strings, so we can now get + // offsets of strings. Get an offset for each string and save it + // to a corresponding SectionPiece for easy access. + for (CStringInputSection *isec : inputs) { + for (size_t i = 0, e = isec->pieces.size(); i != e; ++i) { + isec->pieces[i].outSecOff = + builder.getOffset(isec->getCachedHashStringRef(i)); + isec->isFinal = true; + } + } +} + void macho::createSyntheticSymbols() { auto addHeaderSymbol = [](const char *name) { symtab->addSynthetic(name, in.header->isec, /*value=*/0, diff --git a/lld/MachO/UnwindInfoSection.h b/lld/MachO/UnwindInfoSection.h --- a/lld/MachO/UnwindInfoSection.h +++ b/lld/MachO/UnwindInfoSection.h @@ -9,7 +9,7 @@ #ifndef LLD_MACHO_UNWIND_INFO_H #define LLD_MACHO_UNWIND_INFO_H -#include "MergedOutputSection.h" +#include "ConcatOutputSection.h" #include "SyntheticSections.h" #include "mach-o/compact_unwind_encoding.h" @@ -26,7 +26,7 @@ uint64_t getSize() const override { return unwindInfoSize; } virtual void prepareRelocations(InputSection *) = 0; - void setCompactUnwindSection(MergedOutputSection *cuSection) { + void setCompactUnwindSection(ConcatOutputSection *cuSection) { compactUnwindSection = cuSection; } @@ -36,7 +36,7 @@ align = 4; } - MergedOutputSection *compactUnwindSection = nullptr; + ConcatOutputSection *compactUnwindSection = nullptr; uint64_t unwindInfoSize = 0; }; diff --git a/lld/MachO/UnwindInfoSection.cpp b/lld/MachO/UnwindInfoSection.cpp --- a/lld/MachO/UnwindInfoSection.cpp +++ b/lld/MachO/UnwindInfoSection.cpp @@ -7,9 +7,9 @@ //===----------------------------------------------------------------------===// #include "UnwindInfoSection.h" +#include "ConcatOutputSection.h" #include "Config.h" #include "InputSection.h" -#include "MergedOutputSection.h" #include "OutputSection.h" #include "OutputSegment.h" #include "SymbolTable.h" @@ -212,9 +212,9 @@ // is no source address to make a relative location meaningful. template static void -relocateCompactUnwind(MergedOutputSection *compactUnwindSection, +relocateCompactUnwind(ConcatOutputSection *compactUnwindSection, std::vector> &cuVector) { - for (const InputSection *isec : compactUnwindSection->inputs) { + for (const ConcatInputSection *isec : compactUnwindSection->inputs) { assert(isec->parent == compactUnwindSection); uint8_t *buf = @@ -238,7 +238,7 @@ if (referentIsec->shouldOmitFromOutput()) referentVA = UINT64_MAX; // Tombstone value else - referentVA = referentIsec->getVA() + r.addend; + referentVA = referentIsec->getVA(r.addend); } writeAddress(buf + r.offset, referentVA, r.length); diff --git a/lld/MachO/Writer.cpp b/lld/MachO/Writer.cpp --- a/lld/MachO/Writer.cpp +++ b/lld/MachO/Writer.cpp @@ -7,11 +7,11 @@ //===----------------------------------------------------------------------===// #include "Writer.h" +#include "ConcatOutputSection.h" #include "Config.h" #include "InputFiles.h" #include "InputSection.h" #include "MapFile.h" -#include "MergedOutputSection.h" #include "OutputSection.h" #include "OutputSegment.h" #include "SymbolTable.h" @@ -765,7 +765,7 @@ // 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::max()) - .Default(0); + .Default(seg->inputOrder); } static int sectionOrder(OutputSection *osec) { @@ -779,7 +779,7 @@ .Case(section_names::stubHelper, -1) .Case(section_names::unwindInfo, std::numeric_limits::max() - 1) .Case(section_names::ehFrame, std::numeric_limits::max()) - .Default(0); + .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 @@ -798,9 +798,10 @@ return std::numeric_limits::max(); default: return StringSwitch(osec->name) + .Case(section_names::got, -3) .Case(section_names::lazySymbolPtr, -2) - .Case(section_names::data, -1) - .Default(0); + .Case(section_names::const_, -1) + .Default(osec->inputOrder); } } else if (segname == segment_names::linkEdit) { return StringSwitch(osec->name) @@ -814,13 +815,13 @@ .Case(section_names::indirectSymbolTable, -2) .Case(section_names::stringTable, -1) .Case(section_names::codeSignature, std::numeric_limits::max()) - .Default(0); + .Default(osec->inputOrder); } // ZeroFill sections must always be the at the end of their segments, // otherwise subsequent sections may get overwritten with zeroes at runtime. if (sectionType(osec->flags) == S_ZEROFILL) return std::numeric_limits::max(); - return 0; + return osec->inputOrder; } template @@ -834,8 +835,7 @@ static void sortSegmentsAndSections() { TimeTraceScope timeScope("Sort segments and sections"); - llvm::stable_sort(outputSegments, - compareByOrder(segmentOrder)); + llvm::sort(outputSegments, compareByOrder(segmentOrder)); DenseMap isecPriorities = buildInputSectionPriorities(); @@ -852,7 +852,7 @@ firstTLVDataSection = osec; if (!isecPriorities.empty()) { - if (auto *merged = dyn_cast(osec)) { + if (auto *merged = dyn_cast(osec)) { llvm::stable_sort(merged->inputs, [&](InputSection *a, InputSection *b) { return isecPriorities[a] > isecPriorities[b]; @@ -897,21 +897,32 @@ llvm_unreachable("unhandled output file type"); } - // Then merge input sections into output sections. - MapVector mergedOutputSections; - for (InputSection *isec : inputSections) { + // Then add input sections to output sections. + DenseMap mergedOutputSections; + for (const auto &p : enumerate(inputSections)) { + InputSection *isec = p.value(); if (isec->shouldOmitFromOutput()) continue; - NamePair names = maybeRenameSection({isec->segname, isec->name}); - MergedOutputSection *&osec = mergedOutputSections[names]; - if (osec == nullptr) - osec = make(names.second); - osec->mergeInput(isec); + if (auto *concatIsec = dyn_cast(isec)) { + NamePair names = maybeRenameSection({isec->segname, isec->name}); + ConcatOutputSection *&osec = mergedOutputSections[names]; + if (osec == nullptr) { + osec = make(names.second); + osec->inputOrder = p.index(); + } + osec->addInput(concatIsec); + } else if (auto *cStringIsec = dyn_cast(isec)) { + if (in.cStringSection->inputs.empty()) + in.cStringSection->inputOrder = p.index(); + in.cStringSection->addInput(cStringIsec); + } } + // Once all the inputs are added, we can finalize the output section + // properties and create the corresponding output segments. for (const auto &it : mergedOutputSections) { StringRef segname = it.first.first; - MergedOutputSection *osec = it.second; + ConcatOutputSection *osec = it.second; if (segname == segment_names::ld) { assert(osec->name == section_names::compactUnwind); in.unwindInfo->setCompactUnwindSection(osec); @@ -922,13 +933,14 @@ for (SyntheticSection *ssec : syntheticSections) { auto it = mergedOutputSections.find({ssec->segname, ssec->name}); - if (it == mergedOutputSections.end()) { - if (ssec->isNeeded()) + if (ssec->isNeeded()) { + if (it == mergedOutputSections.end()) { getOrCreateOutputSegment(ssec->segname)->addOutputSection(ssec); - } else { - error("section from " + toString(it->second->firstSection()->file) + - " conflicts with synthetic section " + ssec->segname + "," + - ssec->name); + } else { + fatal("section from " + toString(it->second->firstSection()->file) + + " conflicts with synthetic section " + ssec->segname + "," + + ssec->name); + } } } @@ -1077,6 +1089,7 @@ void macho::createSyntheticSections() { in.header = make(); + in.cStringSection = config->mergeLiterals ? make() : nullptr; in.rebase = make(); in.binding = make(); in.weakBinding = make(); diff --git a/lld/test/MachO/cstring-merging.s b/lld/test/MachO/cstring-merging.s new file mode 100644 --- /dev/null +++ b/lld/test/MachO/cstring-merging.s @@ -0,0 +1,96 @@ +# REQUIRES: x86 +# RUN: rm -rf %t; split-file %s %t +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/test.s -o %t/test.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/more-foo.s -o %t/more-foo.o +# RUN: %lld -dylib %t/test.o %t/more-foo.o -o %t/test +# RUN: llvm-objdump --macho --section="__TEXT,__cstring" --section="__DATA,ptrs" --syms %t/test | FileCheck %s +# RUN: llvm-readobj --section-headers %t/test | FileCheck %s --check-prefix=HEADER + +## Make sure we only have two deduplicated strings in __cstring. +# CHECK: Contents of (__TEXT,__cstring) section +# CHECK-NEXT: bar +# CHECK-NEXT: foo + +## Make sure both symbol and section relocations point to the right thing. +# CHECK-NEXT: Contents of (__DATA,ptrs) section +# CHECK-NEXT: __TEXT:__cstring:foo +# CHECK-NEXT: __TEXT:__cstring:foo +# CHECK-NEXT: __TEXT:__cstring:foo +# CHECK-NEXT: __TEXT:__cstring:foo +# CHECK-NEXT: __TEXT:__cstring:foo +# CHECK-NEXT: __TEXT:__cstring:foo +# CHECK-NEXT: __TEXT:__cstring:bar +# CHECK-NEXT: __TEXT:__cstring:bar +# CHECK-NEXT: __TEXT:__cstring:ar +# CHECK-NEXT: __TEXT:__cstring:r + +## Make sure the symbol addresses are correct too. +# CHECK: SYMBOL TABLE: +# CHECK-DAG: [[#%.16x,FOO:]] l O __TEXT,__cstring _local_foo1 +# CHECK-DAG: [[#FOO]] l O __TEXT,__cstring _local_foo2 +# CHECK-DAG: [[#FOO]] g O __TEXT,__cstring _globl_foo1 +# CHECK-DAG: [[#FOO]] g O __TEXT,__cstring _globl_foo2 +# CHECK-DAG: [[#%.16x,BAR:]] l O __TEXT,__cstring _bar1 +# CHECK-DAG: [[#BAR]] l O __TEXT,__cstring _bar2 +# CHECK-DAG: [[#BAR+1]] l O __TEXT,__cstring _ar +# CHECK-DAG: [[#BAR+2]] l O __TEXT,__cstring _r + +## Make sure we set the right alignment and flags. +# HEADER: Name: __cstring +# HEADER-NEXT: Segment: __TEXT +# HEADER-NEXT: Address: +# HEADER-NEXT: Size: +# HEADER-NEXT: Offset: +# HEADER-NEXT: Alignment: 4 +# HEADER-NEXT: RelocationOffset: +# HEADER-NEXT: RelocationCount: 0 +# HEADER-NEXT: Type: CStringLiterals +# HEADER-NEXT: Attributes [ (0x0) +# HEADER-NEXT: ] +# HEADER-NEXT: Reserved1: 0x0 +# HEADER-NEXT: Reserved2: 0x0 +# HEADER-NEXT: Reserved3: 0x0 + +#--- test.s +.cstring +.p2align 2 +_local_foo1: + .asciz "foo" +_local_foo2: + .asciz "foo" +L._foo1: + .asciz "foo" +L._foo2: + .asciz "foo" +_bar1: + .asciz "bar" +_bar2: + .asciz "bar" +_ar: + .asciz "ar" +_r: + .asciz "r" + +.text +movq _r@GOTPCREL(%rip), %rax + +.section __DATA,ptrs,literal_pointers +.quad L._foo1 +.quad L._foo2 +.quad _local_foo1 +.quad _local_foo2 +.quad _globl_foo1 +.quad _globl_foo2 +.quad _bar1 +.quad _bar2 +.quad _ar +.quad _r + +#--- more-foo.s +.globl _globl_foo1, _globl_foo2 +.cstring +.p2align 4 +_globl_foo1: + .asciz "foo" +_globl_foo2: + .asciz "foo" diff --git a/lld/test/MachO/invalid/cstring-merging.s b/lld/test/MachO/invalid/cstring-merging.s new file mode 100644 --- /dev/null +++ b/lld/test/MachO/invalid/cstring-merging.s @@ -0,0 +1,31 @@ +# REQUIRES: x86 +# RUN: rm -rf %t; split-file %s %t +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/not-terminated.s -o %t/not-terminated.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/relocs.s -o %t/relocs.o + +# RUN: not %lld -dylib %t/not-terminated.o 2>&1 | FileCheck %s --check-prefix=TERM +# RUN: not %lld -dylib %t/relocs.o 2>&1 | FileCheck %s --check-prefix=RELOCS + +# TERM: not-terminated.o:(__cstring): string is not null terminated +# RELOCS: relocs.o contains relocations in a cstring_literals section, so LLD cannot do string merging. Try re-running with --no-literal-merge. + +## Make sure things still work reasonably if literal merging is disabled +# RUN: %lld -dylib --no-literal-merge %t/relocs.o -o %t/relocs +# RUN: llvm-objdump --macho --syms --full-contents %t/relocs | FileCheck %s + +# CHECK: Contents of section __TEXT,__cstring: +# CHECK: 02d8 666f6f00 d8020000 00000000 + +# CHECK: SYMBOL TABLE: +# CHECK: 00000000000002d8 l O __TEXT,__cstring _str + +#--- not-terminated.s +.cstring +.asciz "foo" +.ascii "oh no" + +#--- relocs.s +.cstring +_str: +.asciz "foo" +.quad _str diff --git a/lld/test/MachO/invalid/reserved-section-name.s b/lld/test/MachO/invalid/reserved-section-name.s --- a/lld/test/MachO/invalid/reserved-section-name.s +++ b/lld/test/MachO/invalid/reserved-section-name.s @@ -8,7 +8,12 @@ .section __DATA_CONST,__got .space 1 +.data +_foo: +.space 1 + .text _main: - mov $0, %rax +## make sure the GOT will be needed + pushq _foo@GOTPCREL(%rip) ret diff --git a/lld/test/MachO/load-command-sequence.s b/lld/test/MachO/load-command-sequence.s --- a/lld/test/MachO/load-command-sequence.s +++ b/lld/test/MachO/load-command-sequence.s @@ -28,10 +28,10 @@ # COMMON: segname __TEXT # COMMON: cmd LC_SEGMENT_64 # COMMON: segname __DATA_CONST -# COMMON: sectname __const -# COMMON: segname __DATA_CONST # COMMON: sectname __got # COMMON: segname __DATA_CONST +# COMMON: sectname __const +# COMMON: segname __DATA_CONST # COMMON: cmd LC_SEGMENT_64 # COMMON: segname __DATA # COMMON: sectname __data diff --git a/lld/test/MachO/section-order.s b/lld/test/MachO/section-order.s new file mode 100644 --- /dev/null +++ b/lld/test/MachO/section-order.s @@ -0,0 +1,35 @@ +# REQUIRES: x86 +## Check that section ordering follows from input file ordering. +# RUN: rm -rf %t; split-file %s %t +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/1.s -o %t/1.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/2.s -o %t/2.o +# RUN: %lld -dylib %t/1.o %t/2.o -o %t/12 +# RUN: %lld -dylib %t/2.o %t/1.o -o %t/21 +# RUN: llvm-objdump --macho --section-headers %t/12 | FileCheck %s --check-prefix=CHECK-12 +# RUN: llvm-objdump --macho --section-headers %t/21 | FileCheck %s --check-prefix=CHECK-21 + +# CHECK-12: __text +# CHECK-12-NEXT: foo +# CHECK-12-NEXT: bar +# CHECK-12-NEXT: __cstring + +# CHECK-21: __text +# CHECK-21-NEXT: __cstring +# CHECK-21-NEXT: bar +# CHECK-21-NEXT: foo + +#--- 1.s +.section __TEXT,foo + .space 1 +.section __TEXT,bar + .space 1 +.cstring + .asciz "" + +#--- 2.s +.cstring + .asciz "" +.section __TEXT,bar + .space 1 +.section __TEXT,foo + .space 1 diff --git a/lld/test/MachO/subsections-section-relocs.s b/lld/test/MachO/subsections-section-relocs.s --- a/lld/test/MachO/subsections-section-relocs.s +++ b/lld/test/MachO/subsections-section-relocs.s @@ -2,7 +2,7 @@ # RUN: rm -rf %t; split-file %s %t # RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/test.s -o %t/test.o -# RUN: %lld -o %t/test %t/test.o -order_file %t/order-file +# RUN: %lld --no-literal-merge -o %t/test %t/test.o -order_file %t/order-file # RUN: llvm-objdump --section-headers -d --no-show-raw-insn %t/test | FileCheck %s # CHECK-LABEL: Sections: # CHECK: __cstring {{[^ ]*}} {{0*}}[[#%x, CSTRING_ADDR:]] @@ -39,10 +39,13 @@ ## References to this generate a section relocation ## N.B.: ld64 doesn't actually reorder symbols in __cstring based on the order -## file. Only our implementation does. However, I'm not sure how else to -## test section relocations that target an address inside a relocated -## symbol: using a non-__cstring section would cause llvm-mc to emit a -## symbol relocation instead using the nearest symbol. +## file. Our implementation only does does so if --no-literal-merge is +## specified. I'm not sure how else to test section relocations that +## target an address inside a relocated symbol: using a non-__cstring +## section would cause llvm-mc to emit a symbol relocation instead using +## the nearest symbol. It might be more consistent for LLD to disable +## symbol-based cstring reordering altogether and leave this functionality +## untested, at least until we find a real-world use case... L_.str: .asciz "Private symbol" diff --git a/lld/test/MachO/weak-binding.s b/lld/test/MachO/weak-binding.s --- a/lld/test/MachO/weak-binding.s +++ b/lld/test/MachO/weak-binding.s @@ -37,8 +37,11 @@ # CHECK-DAG: __DATA __la_symbol_ptr 0x[[#%x,WEAK_DY_FN:]] pointer 0 libfoo _weak_dysym_fn # CHECK-DAG: __DATA __data 0x[[#%x,WEAK_DY:]] pointer 0 libfoo _weak_dysym # CHECK-DAG: __DATA __thread_vars 0x{{[0-9a-f]*}} pointer 0 libSystem __tlv_bootstrap +# CHECK-DAG: __DATA __thread_vars 0x{{[0-9a-f]*}} pointer 0 libSystem __tlv_bootstrap # CHECK-DAG: __DATA __thread_ptrs 0x[[#WEAK_DY_TLV_ADDR]] pointer 0 libfoo _weak_dysym_tlv -## Check that we don't have any other bindings +## FIXME: This was supposed to check that we don't have any other bindings in +## the bind table, but in practice it just checks that we don't have a binding +## immediately after the last -DAG match... # CHECK-NOT: pointer # CHECK-LABEL: Lazy bind table: diff --git a/lld/test/MachO/x86-64-relocs.s b/lld/test/MachO/x86-64-relocs.s --- a/lld/test/MachO/x86-64-relocs.s +++ b/lld/test/MachO/x86-64-relocs.s @@ -4,7 +4,7 @@ # RUN: llvm-objdump --section-headers --syms -d %t | FileCheck %s # CHECK-LABEL: Sections: -# CHECK: __cstring {{[0-9a-z]+}} [[#%x, CSTRING_ADDR:]] +# CHECK: __data {{[0-9a-z]+}} [[#%x, DATA_ADDR:]] # CHECK-LABEL: SYMBOL TABLE: # CHECK: [[#%x, F_ADDR:]] {{.*}} _f @@ -13,15 +13,15 @@ ## Test X86_64_RELOC_BRANCH # CHECK: callq 0x[[#%x, F_ADDR]] <_f> ## Test extern (symbol) X86_64_RELOC_SIGNED -# CHECK: leaq [[#%u, STR_OFF:]](%rip), %rsi -# CHECK-NEXT: [[#%x, CSTRING_ADDR - STR_OFF]] +# CHECK: leaq [[#%u, LOCAL_OFF:]](%rip), %rsi +# CHECK-NEXT: [[#%x, DATA_ADDR - LOCAL_OFF]] ## Test non-extern (section) X86_64_RELOC_SIGNED -# CHECK: leaq [[#%u, LSTR_OFF:]](%rip), %rsi -# CHECK-NEXT: [[#%x, CSTRING_ADDR + 22 - LSTR_OFF]] +# CHECK: leaq [[#%u, PRIVATE_OFF:]](%rip), %rsi +# CHECK-NEXT: [[#%x, DATA_ADDR + 8 - PRIVATE_OFF]] # RUN: llvm-objdump --section=__const --full-contents %t | FileCheck %s --check-prefix=NONPCREL # NONPCREL: Contents of section __DATA_CONST,__const: -# NONPCREL-NEXT: 100001000 18040000 01000000 18040000 01000000 +# NONPCREL-NEXT: 100001000 08200000 01000000 08200000 01000000 .section __TEXT,__text .globl _main, _f @@ -31,36 +31,22 @@ ret _f: - movl $0x2000004, %eax # write() syscall - mov $1, %rdi # stdout - leaq _str(%rip), %rsi # Generates a X86_64_RELOC_SIGNED pcrel symbol relocation - mov $21, %rdx # length of str - syscall - - movl $0x2000004, %eax # write() syscall - mov $1, %rdi # stdout - leaq L_.str(%rip), %rsi # Generates a X86_64_RELOC_SIGNED pcrel section relocation - mov $15, %rdx # length of str - syscall - - movl $0x2000004, %eax # write() syscall - mov $1, %rdi # stdout - movq L_.ptr_1_to_str(%rip), %rsi - mov $15, %rdx # length of str - syscall + leaq _local(%rip), %rsi # Generates a X86_64_RELOC_SIGNED pcrel symbol relocation + leaq L_.private(%rip), %rsi # Generates a X86_64_RELOC_SIGNED pcrel section relocation + movq L_.ptr_1(%rip), %rsi ret -.section __TEXT,__cstring +.data ## References to this generate a symbol relocation -_str: - .asciz "Local defined symbol\n" +_local: + .quad 123 ## References to this generate a section relocation -L_.str: - .asciz "Private symbol\n" +L_.private: + .quad 123 .section __DATA,__const ## These generate X86_64_RELOC_UNSIGNED non-pcrel section relocations -L_.ptr_1_to_str: - .quad L_.str -L_.ptr_2_to_str: - .quad L_.str +L_.ptr_1: + .quad L_.private +L_.ptr_2: + .quad L_.private