diff --git a/lld/MachO/ConcatOutputSection.cpp b/lld/MachO/ConcatOutputSection.cpp --- a/lld/MachO/ConcatOutputSection.cpp +++ b/lld/MachO/ConcatOutputSection.cpp @@ -27,7 +27,7 @@ void ConcatOutputSection::addInput(ConcatInputSection *input) { if (inputs.empty()) { align = input->align; - flags = input->flags; + flags = input->getFlags(); } else { align = std::max(align, input->align); finalizeFlags(input); @@ -288,7 +288,8 @@ // unfinalized inputs[finalIdx]. fatal(Twine(__FUNCTION__) + ": FIXME: thunk range overrun"); } - thunkInfo.isec = make(isec->segname, isec->name); + thunkInfo.isec = + make(isec->getSegName(), isec->getName()); thunkInfo.isec->parent = this; StringRef thunkName = saver.save(funcSym->getName() + ".thunk." + std::to_string(thunkInfo.sequence++)); @@ -332,8 +333,7 @@ } void ConcatOutputSection::finalizeFlags(InputSection *input) { - uint8_t inputType = input->flags & SECTION_TYPE; - switch (inputType) { + switch (sectionType(input->getFlags())) { default /*type-unspec'ed*/: // FIXME: Add additional logics here when supporting emitting obj files. break; @@ -351,7 +351,7 @@ case S_THREAD_LOCAL_VARIABLE_POINTERS: case S_NON_LAZY_SYMBOL_POINTERS: case S_SYMBOL_STUBS: - flags |= input->flags; + flags |= input->getFlags(); break; } } diff --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp --- a/lld/MachO/Driver.cpp +++ b/lld/MachO/Driver.cpp @@ -546,20 +546,19 @@ if (common == nullptr) continue; - auto *isec = - make(segment_names::data, section_names::common); - isec->file = common->getFile(); - isec->align = common->align; // Casting to size_t will truncate large values on 32-bit architectures, // but it's not really worth supporting the linking of 64-bit programs on // 32-bit archs. - isec->data = {nullptr, static_cast(common->size)}; - isec->flags = S_ZEROFILL; + ArrayRef data = {nullptr, static_cast(common->size)}; + auto *isec = make( + segment_names::data, section_names::common, common->getFile(), data, + common->align, S_ZEROFILL); inputSections.push_back(isec); // FIXME: CommonSymbol should store isReferencedDynamically, noDeadStrip // and pass them on here. - replaceSymbol(sym, sym->getName(), isec->file, isec, /*value=*/0, + replaceSymbol(sym, sym->getName(), isec->getFile(), isec, + /*value=*/0, /*size=*/0, /*isWeakDef=*/false, /*isExternal=*/true, common->privateExtern, @@ -994,8 +993,8 @@ if (auto *isec = dyn_cast(entry.isec)) { if (isec->isCoalescedWeak()) continue; - if (isec->segname == segment_names::ld) { - assert(isec->name == section_names::compactUnwind); + if (isec->getSegName() == segment_names::ld) { + assert(isec->getName() == section_names::compactUnwind); in.unwindInfo->addInput(isec); continue; } diff --git a/lld/MachO/Dwarf.cpp b/lld/MachO/Dwarf.cpp --- a/lld/MachO/Dwarf.cpp +++ b/lld/MachO/Dwarf.cpp @@ -27,7 +27,7 @@ // ourselves. for (const InputSection *isec : obj->debugSections) { if (StringRef *s = - StringSwitch(isec->name) + StringSwitch(isec->getName()) .Case(section_names::debugInfo, &dObj->infoSection.Data) .Case(section_names::debugAbbrev, &dObj->abbrevSection) .Case(section_names::debugStr, &dObj->strSection) diff --git a/lld/MachO/ICF.cpp b/lld/MachO/ICF.cpp --- a/lld/MachO/ICF.cpp +++ b/lld/MachO/ICF.cpp @@ -88,7 +88,7 @@ return false; if (ia->data != ib->data) return false; - if (ia->flags != ib->flags) + if (ia->getFlags() != ib->getFlags()) return false; if (ia->relocs.size() != ib->relocs.size()) return false; diff --git a/lld/MachO/InputFiles.cpp b/lld/MachO/InputFiles.cpp --- a/lld/MachO/InputFiles.cpp +++ b/lld/MachO/InputFiles.cpp @@ -295,8 +295,8 @@ } else { auto *isec = make(segname, name, this, data, align, flags); - if (!(isDebugSection(isec->flags) && - isec->segname == segment_names::dwarf)) { + if (!(isDebugSection(isec->getFlags()) && + isec->getSegName() == segment_names::dwarf)) { subsections.push_back({{0, isec}}); } else { // Instead of emitting DWARF sections, we emit STABS symbols to the @@ -524,7 +524,7 @@ isPrivateExtern = true; return symtab->addDefined( - name, isec->file, isec, value, size, sym.n_desc & N_WEAK_DEF, + name, isec->getFile(), isec, value, size, sym.n_desc & N_WEAK_DEF, isPrivateExtern, sym.n_desc & N_ARM_THUMB_DEF, sym.n_desc & REFERENCED_DYNAMICALLY, sym.n_desc & N_NO_DEAD_STRIP); } @@ -532,7 +532,7 @@ assert(!isWeakDefCanBeHidden && "weak_def_can_be_hidden on already-hidden symbol?"); return make( - name, isec->file, isec, value, size, sym.n_desc & N_WEAK_DEF, + name, isec->getFile(), isec, value, size, sym.n_desc & N_WEAK_DEF, /*isExternal=*/false, /*isPrivateExtern=*/false, sym.n_desc & N_ARM_THUMB_DEF, sym.n_desc & REFERENCED_DYNAMICALLY, sym.n_desc & N_NO_DEAD_STRIP); @@ -674,7 +674,7 @@ auto *nextIsec = make(*concatIsec); nextIsec->numRefs = 0; nextIsec->wasCoalesced = false; - if (isZeroFill(isec->flags)) { + if (isZeroFill(isec->getFlags())) { // Zero-fill sections have NULL data.data() non-zero data.size() nextIsec->data = {nullptr, isec->data.size() - symbolOffset}; isec->data = {nullptr, symbolOffset}; @@ -700,11 +700,11 @@ OpaqueFile::OpaqueFile(MemoryBufferRef mb, StringRef segName, StringRef sectName) : InputFile(OpaqueKind, mb) { - ConcatInputSection *isec = - make(segName.take_front(16), sectName.take_front(16)); - isec->file = this; const auto *buf = reinterpret_cast(mb.getBufferStart()); - isec->data = {buf, mb.getBufferSize()}; + ArrayRef data = {buf, mb.getBufferSize()}; + ConcatInputSection *isec = + make(segName.take_front(16), sectName.take_front(16), + /*file=*/this, data); isec->live = true; subsections.push_back({{0, isec}}); } 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 "Relocations.h" #include "lld/Common/LLVM.h" +#include "lld/Common/Memory.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/BitVector.h" #include "llvm/ADT/CachedHashString.h" @@ -33,9 +34,13 @@ WordLiteralKind, }; - Kind kind() const { return sectionKind; } + Kind kind() const { return shared->sectionKind; } virtual ~InputSection() = default; virtual uint64_t getSize() const { return data.size(); } + InputFile *getFile() const { return shared->file; } + StringRef getName() const { return shared->name; } + StringRef getSegName() const { return shared->segname; } + uint32_t getFlags() const { return shared->flags; } uint64_t getFileSize() const; // Translates \p off -- an offset relative to this InputSection -- into an // offset from the beginning of its parent OutputSection. @@ -47,33 +52,43 @@ virtual void markLive(uint64_t off) = 0; virtual InputSection *canonical() { return this; } - InputFile *file = nullptr; - StringRef name; - StringRef segname; - OutputSection *parent = nullptr; uint32_t align = 1; - uint32_t flags = 0; - uint32_t callSiteCount = 0; - + uint32_t callSiteCount : 31; // is address assigned? - bool isFinal = false; + uint32_t isFinal : 1; ArrayRef data; std::vector relocs; protected: + // The fields in this struct are immutable. Since we create a lot of + // InputSections with identical values for them (due to + // .subsections_via_symbols), factoring them out into a shared struct reduces + // memory consumption and makes copying cheaper. + struct Shared { + InputFile *file; + StringRef name; + StringRef segname; + uint32_t flags; + Kind sectionKind; + Shared(InputFile *file, StringRef name, StringRef segname, uint32_t flags, + Kind kind) + : file(file), name(name), segname(segname), flags(flags), + sectionKind(kind) {} + }; + InputSection(Kind kind, StringRef segname, StringRef name) - : name(name), segname(segname), sectionKind(kind) {} + : callSiteCount(0), isFinal(false), + shared(make(nullptr, name, segname, 0, kind)) {} InputSection(Kind kind, StringRef segname, StringRef name, InputFile *file, ArrayRef data, uint32_t align, uint32_t flags) - : file(file), name(name), segname(segname), align(align), flags(flags), - data(data), sectionKind(kind) {} + : align(align), callSiteCount(0), isFinal(false), data(data), + shared(make(file, name, segname, flags, kind)) {} -private: - Kind sectionKind; + const Shared *const shared; }; // ConcatInputSections are combined into (Concat)OutputSections through simple @@ -85,7 +100,8 @@ : InputSection(ConcatKind, segname, name) {} ConcatInputSection(StringRef segname, StringRef name, InputFile *file, - ArrayRef data, uint32_t align, uint32_t flags) + ArrayRef data, uint32_t align = 1, + uint32_t flags = 0) : InputSection(ConcatKind, segname, name, file, data, align, flags) {} uint64_t getOffset(uint64_t off) const override { return outSecOff + off; } @@ -128,6 +144,11 @@ uint64_t outSecOff = 0; }; +// Verify ConcatInputSection's size on 64-bit builds. +static_assert(sizeof(int) != 8 || sizeof(ConcatInputSection) == 112, + "Try to minimize ConcatInputSection's size, we create many " + "instances of it"); + // Helper functions to make it easy to sprinkle asserts. inline bool shouldOmitFromOutput(InputSection *isec) { diff --git a/lld/MachO/InputSection.cpp b/lld/MachO/InputSection.cpp --- a/lld/MachO/InputSection.cpp +++ b/lld/MachO/InputSection.cpp @@ -28,7 +28,7 @@ std::vector macho::inputSections; uint64_t InputSection::getFileSize() const { - return isZeroFill(flags) ? 0 : getSize(); + return isZeroFill(getFlags()) ? 0 : getSize(); } uint64_t InputSection::getVA(uint64_t off) const { @@ -49,7 +49,7 @@ // ICF needs to hash any section that might potentially be duplicated so // that it can match on content rather than identity. bool ConcatInputSection::isHashableForICF() const { - switch (sectionType(flags)) { + switch (sectionType(getFlags())) { case S_REGULAR: return true; case S_CSTRING_LITERALS: @@ -127,7 +127,7 @@ target->relaxGotLoad(loc, r.type); referentVA = resolveSymbolVA(referentSym, r.type) + r.addend; - if (isThreadLocalVariables(flags)) { + if (isThreadLocalVariables(getFlags())) { // References from thread-local variable sections are treated as offsets // relative to the start of the thread-local data memory area, which // is initialized via copying all the TLV data sections (which are all @@ -203,7 +203,7 @@ uint64_t WordLiteralInputSection::getOffset(uint64_t off) const { auto *osec = cast(parent); const uint8_t *buf = data.data(); - switch (sectionType(flags)) { + switch (sectionType(getFlags())) { case S_4BYTE_LITERALS: return osec->getLiteral4Offset(buf + off); case S_8BYTE_LITERALS: @@ -216,16 +216,16 @@ } bool macho::isCodeSection(const InputSection *isec) { - uint32_t type = sectionType(isec->flags); + uint32_t type = sectionType(isec->getFlags()); if (type != S_REGULAR && type != S_COALESCED) return false; - uint32_t attr = isec->flags & SECTION_ATTRIBUTES_USR; + uint32_t attr = isec->getFlags() & SECTION_ATTRIBUTES_USR; if (attr == S_ATTR_PURE_INSTRUCTIONS) return true; - if (isec->segname == segment_names::text) - return StringSwitch(isec->name) + if (isec->getSegName() == segment_names::text) + return StringSwitch(isec->getName()) .Cases(section_names::textCoalNt, section_names::staticInit, true) .Default(false); @@ -233,10 +233,10 @@ } bool macho::isCfStringSection(const InputSection *isec) { - return isec->name == section_names::cfString && - isec->segname == segment_names::data; + return isec->getName() == section_names::cfString && + isec->getSegName() == segment_names::data; } std::string lld::toString(const InputSection *isec) { - return (toString(isec->file) + ":(" + isec->name + ")").str(); + return (toString(isec->getFile()) + ":(" + isec->getName() + ")").str(); } diff --git a/lld/MachO/MarkLive.cpp b/lld/MachO/MarkLive.cpp --- a/lld/MachO/MarkLive.cpp +++ b/lld/MachO/MarkLive.cpp @@ -103,14 +103,14 @@ addSym(stubBinder); for (ConcatInputSection *isec : inputSections) { // Sections marked no_dead_strip - if (isec->flags & S_ATTR_NO_DEAD_STRIP) { + if (isec->getFlags() & S_ATTR_NO_DEAD_STRIP) { enqueue(isec, 0); continue; } // mod_init_funcs, mod_term_funcs sections - if (sectionType(isec->flags) == S_MOD_INIT_FUNC_POINTERS || - sectionType(isec->flags) == S_MOD_TERM_FUNC_POINTERS) { + if (sectionType(isec->getFlags()) == S_MOD_INIT_FUNC_POINTERS || + sectionType(isec->getFlags()) == S_MOD_TERM_FUNC_POINTERS) { enqueue(isec, 0); continue; } @@ -161,7 +161,7 @@ for (ConcatInputSection *isec : inputSections) { // FIXME: Check if copying all S_ATTR_LIVE_SUPPORT sections into a // separate vector and only walking that here is faster. - if (!(isec->flags & S_ATTR_LIVE_SUPPORT) || isec->live) + if (!(isec->getFlags() & S_ATTR_LIVE_SUPPORT) || isec->live) continue; for (const Reloc &r : isec->relocs) { diff --git a/lld/MachO/Symbols.h b/lld/MachO/Symbols.h --- a/lld/MachO/Symbols.h +++ b/lld/MachO/Symbols.h @@ -129,7 +129,7 @@ return isWeakDef() && isExternal() && !privateExtern; } bool isTlv() const override { - return !isAbsolute() && isThreadLocalVariables(isec->flags); + return !isAbsolute() && isThreadLocalVariables(isec->getFlags()); } bool isExternal() const { return external; } diff --git a/lld/MachO/SyntheticSections.cpp b/lld/MachO/SyntheticSections.cpp --- a/lld/MachO/SyntheticSections.cpp +++ b/lld/MachO/SyntheticSections.cpp @@ -776,7 +776,7 @@ if (defined->isAbsolute()) continue; InputSection *isec = defined->isec; - ObjFile *file = dyn_cast_or_null(isec->file); + ObjFile *file = dyn_cast_or_null(isec->getFile()); if (!file || !file->compileUnit) continue; symbolsNeedingStabs.push_back(defined); @@ -784,7 +784,7 @@ } llvm::stable_sort(symbolsNeedingStabs, [&](Defined *a, Defined *b) { - return a->isec->file->id < b->isec->file->id; + return a->isec->getFile()->id < b->isec->getFile()->id; }); // Emit STABS symbols so that dsymutil and/or the debugger can map address @@ -793,7 +793,7 @@ InputFile *lastFile = nullptr; for (Defined *defined : symbolsNeedingStabs) { InputSection *isec = defined->isec; - ObjFile *file = cast(isec->file); + ObjFile *file = cast(isec->getFile()); if (lastFile == nullptr || lastFile != file) { if (lastFile != nullptr) @@ -1294,7 +1294,7 @@ // finalized. isec->isFinal = true; const uint8_t *buf = isec->data.data(); - switch (sectionType(isec->flags)) { + switch (sectionType(isec->getFlags())) { case S_4BYTE_LITERALS: { for (size_t off = 0, e = isec->data.size(); off < e; off += 4) { if (!isec->isLive(off)) diff --git a/lld/MachO/UnwindInfoSection.cpp b/lld/MachO/UnwindInfoSection.cpp --- a/lld/MachO/UnwindInfoSection.cpp +++ b/lld/MachO/UnwindInfoSection.cpp @@ -142,8 +142,8 @@ template void UnwindInfoSectionImpl::addInput(ConcatInputSection *isec) { - assert(isec->segname == segment_names::ld && - isec->name == section_names::compactUnwind); + assert(isec->getSegName() == segment_names::ld && + isec->getName() == section_names::compactUnwind); compactUnwindSection->addInput(isec); } @@ -220,7 +220,7 @@ // the exact addresses that it references. So it is safe for compact unwind to // reference addresses in __TEXT, but not addresses in any other segment. static ConcatInputSection *checkTextSegment(InputSection *isec) { - if (isec->segname != segment_names::text) + if (isec->getSegName() != segment_names::text) error("compact unwind references address in " + toString(isec) + " which is not in segment __TEXT"); // __text should always be a ConcatInputSection. diff --git a/lld/MachO/Writer.cpp b/lld/MachO/Writer.cpp --- a/lld/MachO/Writer.cpp +++ b/lld/MachO/Writer.cpp @@ -582,7 +582,7 @@ // References from thread-local variable sections are treated as offsets // relative to the start of the referent section, and therefore have no // need of rebase opcodes. - if (!(isThreadLocalVariables(isec->flags) && isa(sym))) + if (!(isThreadLocalVariables(isec->getFlags()) && isa(sym))) addNonLazyBindingEntries(sym, isec, r.offset, r.addend); } } @@ -802,7 +802,8 @@ SymbolPriorityEntry &entry = it->second; size_t &priority = sectionPriorities[sym.isec]; - priority = std::max(priority, getSymbolPriority(entry, sym.isec->file)); + priority = + std::max(priority, getSymbolPriority(entry, sym.isec->getFile())); }; // TODO: Make sure this handles weak symbols correctly. @@ -889,7 +890,7 @@ for (ConcatInputSection *isec : inputSections) { if (isec->shouldOmitFromOutput()) continue; - NamePair names = maybeRenameSection({isec->segname, isec->name}); + NamePair names = maybeRenameSection({isec->getSegName(), isec->getName()}); ConcatOutputSection *&osec = concatOutputSections[names]; if (!osec) osec = make(names.second); @@ -913,7 +914,8 @@ if (it == concatOutputSections.end()) { getOrCreateOutputSegment(ssec->segname)->addOutputSection(ssec); } else { - fatal("section from " + toString(it->second->firstSection()->file) + + fatal("section from " + + toString(it->second->firstSection()->getFile()) + " conflicts with synthetic section " + ssec->segname + "," + ssec->name); }