| //===- InputSection.cpp ---------------------------------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "InputSection.h" | |
| #include "ConcatOutputSection.h" | |
| #include "Config.h" | |
| #include "InputFiles.h" | |
| #include "OutputSegment.h" | |
| #include "Sections.h" | |
| #include "Symbols.h" | |
| #include "SyntheticSections.h" | |
| #include "Target.h" | |
| #include "Writer.h" | |
| #include "lld/Common/ErrorHandler.h" | |
| #include "lld/Common/Memory.h" | |
| #include "llvm/Support/xxhash.h" | |
| using namespace llvm; | |
| using namespace llvm::MachO; | |
| using namespace llvm::support; | |
| using namespace lld; | |
| using namespace lld::macho; | |
| // Verify ConcatInputSection's size on 64-bit builds. The size of std::vector | |
| // can differ based on STL debug levels (e.g. iterator debugging on MSVC's STL), | |
| // so account for that. | |
| static_assert(sizeof(void *) != 8 || sizeof(ConcatInputSection) == | |
| sizeof(std::vector<Relocation>) + 88, | |
| "Try to minimize ConcatInputSection's size, we create many " | |
| "instances of it"); | |
| std::vector<ConcatInputSection *> macho::inputSections; | |
| int macho::inputSectionsOrder = 0; | |
| // Call this function to add a new InputSection and have it routed to the | |
| // appropriate container. Depending on its type and current config, it will | |
| // either be added to 'inputSections' vector or to a synthetic section. | |
| void lld::macho::addInputSection(InputSection *inputSection) { | |
| if (auto *isec = dyn_cast<ConcatInputSection>(inputSection)) { | |
| if (isec->isCoalescedWeak()) | |
| return; | |
| if (config->emitRelativeMethodLists && | |
| ObjCMethListSection::isMethodList(isec)) { | |
| if (in.objcMethList->inputOrder == UnspecifiedInputOrder) | |
| in.objcMethList->inputOrder = inputSectionsOrder++; | |
| in.objcMethList->addInput(isec); | |
| isec->parent = in.objcMethList; | |
| return; | |
| } | |
| if (config->emitInitOffsets && | |
| sectionType(isec->getFlags()) == S_MOD_INIT_FUNC_POINTERS) { | |
| in.initOffsets->addInput(isec); | |
| return; | |
| } | |
| isec->outSecOff = inputSectionsOrder++; | |
| auto *osec = ConcatOutputSection::getOrCreateForInput(isec); | |
| isec->parent = osec; | |
| inputSections.push_back(isec); | |
| } else if (auto *isec = dyn_cast<CStringInputSection>(inputSection)) { | |
| bool useSectionName = config->separateCstringLiteralSections || | |
| isec->getName() == section_names::objcMethname; | |
| auto *osec = in.getOrCreateCStringSection( | |
| useSectionName ? isec->getName() : section_names::cString); | |
| if (osec->inputOrder == UnspecifiedInputOrder) | |
| osec->inputOrder = inputSectionsOrder++; | |
| osec->addInput(isec); | |
| } else if (auto *isec = dyn_cast<WordLiteralInputSection>(inputSection)) { | |
| if (in.wordLiteralSection->inputOrder == UnspecifiedInputOrder) | |
| in.wordLiteralSection->inputOrder = inputSectionsOrder++; | |
| in.wordLiteralSection->addInput(isec); | |
| } else { | |
| llvm_unreachable("unexpected input section kind"); | |
| } | |
| assert(inputSectionsOrder <= UnspecifiedInputOrder); | |
| } | |
| uint64_t InputSection::getFileSize() const { | |
| return isZeroFill(getFlags()) ? 0 : getSize(); | |
| } | |
| uint64_t InputSection::getVA(uint64_t off) const { | |
| return parent->addr + getOffset(off); | |
| } | |
| uint64_t macho::resolveSymbolOffsetVA(const Symbol *sym, uint8_t type, | |
| int64_t offset) { | |
| const RelocAttrs &relocAttrs = target->getRelocAttrs(type); | |
| uint64_t symVA; | |
| if (relocAttrs.hasAttr(RelocAttrBits::BRANCH)) { | |
| // For branch relocations with non-zero offsets, use the actual function | |
| // address rather than the stub address. Branching to an interior point | |
| // of a function (e.g., _func+16) implies reliance on the original | |
| // function's layout, which an interposed replacement wouldn't preserve. | |
| // There's no meaningful way to "interpose" an interior offset. | |
| symVA = (offset != 0) ? sym->getVA() : sym->resolveBranchVA(); | |
| } else if (relocAttrs.hasAttr(RelocAttrBits::GOT)) { | |
| symVA = sym->resolveGotVA(); | |
| } else if (relocAttrs.hasAttr(RelocAttrBits::TLV)) { | |
| symVA = sym->resolveTlvVA(); | |
| } else { | |
| symVA = sym->getVA(); | |
| } | |
| return symVA + offset; | |
| } | |
| const Defined *InputSection::getContainingSymbol(uint64_t off) const { | |
| auto *nextSym = llvm::upper_bound( | |
| symbols, off, [](uint64_t a, const Defined *b) { return a < b->value; }); | |
| if (nextSym == symbols.begin()) | |
| return nullptr; | |
| return *std::prev(nextSym); | |
| } | |
| std::string InputSection::getLocation(uint64_t off) const { | |
| // First, try to find a symbol that's near the offset. Use it as a reference | |
| // point. | |
| if (auto *sym = getContainingSymbol(off)) | |
| return (toString(getFile()) + ":(symbol " + toString(*sym) + "+0x" + | |
| Twine::utohexstr(off - sym->value) + ")") | |
| .str(); | |
| // If that fails, use the section itself as a reference point. | |
| for (const Subsection &subsec : section.subsections) { | |
| if (subsec.isec == this) { | |
| off += subsec.offset; | |
| break; | |
| } | |
| } | |
| return (toString(getFile()) + ":(" + getName() + "+0x" + | |
| Twine::utohexstr(off) + ")") | |
| .str(); | |
| } | |
| std::string InputSection::getSourceLocation(uint64_t off) const { | |
| auto *obj = dyn_cast_or_null<ObjFile>(getFile()); | |
| if (!obj) | |
| return {}; | |
| DWARFCache *dwarf = obj->getDwarf(); | |
| if (!dwarf) | |
| return std::string(); | |
| for (const Subsection &subsec : section.subsections) { | |
| if (subsec.isec == this) { | |
| off += subsec.offset; | |
| break; | |
| } | |
| } | |
| auto createMsg = [&](StringRef path, unsigned line) { | |
| std::string filename = sys::path::filename(path).str(); | |
| std::string lineStr = (":" + Twine(line)).str(); | |
| if (filename == path) | |
| return filename + lineStr; | |
| return (filename + lineStr + " (" + path + lineStr + ")").str(); | |
| }; | |
| // First, look up a function for a given offset. | |
| if (std::optional<DILineInfo> li = dwarf->getDILineInfo( | |
| section.addr + off, object::SectionedAddress::UndefSection)) | |
| return createMsg(li->FileName, li->Line); | |
| // If it failed, look up again as a variable. | |
| if (const Defined *sym = getContainingSymbol(off)) { | |
| // Symbols are generally prefixed with an underscore, which is not included | |
| // in the debug information. | |
| StringRef symName = sym->getName(); | |
| symName.consume_front("_"); | |
| if (std::optional<std::pair<std::string, unsigned>> fileLine = | |
| dwarf->getVariableLoc(symName)) | |
| return createMsg(fileLine->first, fileLine->second); | |
| } | |
| // Try to get the source file's name from the DWARF information. | |
| if (obj->compileUnit) | |
| return obj->sourceFile(); | |
| return {}; | |
| } | |
| const Relocation *InputSection::getRelocAt(uint32_t off) const { | |
| auto it = llvm::find_if(relocs, | |
| [=](const Relocation &r) { return r.offset == off; }); | |
| if (it == relocs.end()) | |
| return nullptr; | |
| return &*it; | |
| } | |
| void ConcatInputSection::foldIdentical(ConcatInputSection *copy, | |
| Symbol::ICFFoldKind foldKind) { | |
| align = std::max(align, copy->align); | |
| copy->live = false; | |
| copy->wasCoalesced = true; | |
| copy->replacement = this; | |
| for (auto ©Sym : copy->symbols) | |
| copySym->identicalCodeFoldingKind = foldKind; | |
| if (copy->symbols.empty()) | |
| return; | |
| auto *it = copy->symbols.begin(); | |
| // The first symbol in the merged section (symbols.front()) must keep its | |
| // unwind entry. If this section is empty, the copy's first symbol becomes the | |
| // new front, so we skip clearing it. | |
| if (symbols.empty()) | |
| ++it; | |
| for (; it != copy->symbols.end(); ++it) { | |
| assert((*it)->value == 0); | |
| (*it)->originalUnwindEntry = nullptr; | |
| } | |
| symbols.insert(symbols.end(), copy->symbols.begin(), copy->symbols.end()); | |
| copy->symbols.clear(); | |
| } | |
| void ConcatInputSection::writeTo(uint8_t *buf) { | |
| assert(!shouldOmitFromOutput()); | |
| if (getFileSize() == 0) | |
| return; | |
| memcpy(buf, data.data(), data.size()); | |
| for (size_t i = 0; i < relocs.size(); i++) { | |
| const Relocation &r = relocs[i]; | |
| uint8_t *loc = buf + r.offset; | |
| uint64_t referentVA = 0; | |
| const bool needsFixup = config->emitChainedFixups && | |
| target->hasAttr(r.type, RelocAttrBits::UNSIGNED); | |
| if (target->hasAttr(r.type, RelocAttrBits::SUBTRAHEND)) { | |
| const Symbol *fromSym = cast<Symbol *>(r.referent); | |
| const Relocation &minuend = relocs[++i]; | |
| uint64_t minuendVA; | |
| if (const Symbol *toSym = minuend.referent.dyn_cast<Symbol *>()) | |
| minuendVA = toSym->getVA() + minuend.addend; | |
| else { | |
| auto *referentIsec = cast<InputSection *>(minuend.referent); | |
| assert(!::shouldOmitFromOutput(referentIsec)); | |
| minuendVA = referentIsec->getVA(minuend.addend); | |
| } | |
| referentVA = minuendVA - fromSym->getVA(); | |
| } else if (auto *referentSym = r.referent.dyn_cast<Symbol *>()) { | |
| if (target->hasAttr(r.type, RelocAttrBits::LOAD) && | |
| !referentSym->isInGot()) | |
| target->relaxGotLoad(loc, r.type); | |
| // For dtrace symbols, do not handle them as normal undefined symbols | |
| if (referentSym->getName().starts_with("___dtrace_")) { | |
| // Change dtrace call site to pre-defined instructions | |
| target->handleDtraceReloc(referentSym, r, loc); | |
| continue; | |
| } | |
| referentVA = resolveSymbolOffsetVA(referentSym, r.type, r.addend); | |
| if (isThreadLocalVariables(getFlags()) && isa<Defined>(referentSym)) { | |
| // 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 | |
| // contiguous). | |
| referentVA -= firstTLVDataSection->addr; | |
| } else if (needsFixup) { | |
| writeChainedFixup(loc, referentSym, r.addend); | |
| continue; | |
| } | |
| } else if (auto *referentIsec = r.referent.dyn_cast<InputSection *>()) { | |
| assert(!::shouldOmitFromOutput(referentIsec)); | |
| referentVA = referentIsec->getVA(r.addend); | |
| if (needsFixup) { | |
| writeChainedRebase(loc, referentVA); | |
| continue; | |
| } | |
| } | |
| target->relocateOne(loc, r, referentVA, getVA() + r.offset); | |
| } | |
| } | |
| ConcatInputSection *macho::makeSyntheticInputSection(StringRef segName, | |
| StringRef sectName, | |
| uint32_t flags, | |
| ArrayRef<uint8_t> data, | |
| uint32_t align) { | |
| Section §ion = | |
| *make<Section>(/*file=*/nullptr, segName, sectName, flags, /*addr=*/0); | |
| auto isec = make<ConcatInputSection>(section, data, align); | |
| // Since this is an explicitly created 'fake' input section, | |
| // it should not be dead stripped. | |
| isec->live = true; | |
| section.subsections.push_back({0, isec}); | |
| return isec; | |
| } | |
| 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(getLocation(off) + ": string is not null terminated"); | |
| uint32_t hash = deduplicateLiterals ? xxh3_64bits(s.take_front(end)) : 0; | |
| pieces.emplace_back(off, hash); | |
| size_t size = end + 1; // include null terminator | |
| s = s.substr(size); | |
| off += size; | |
| } | |
| } | |
| StringPiece &CStringInputSection::getStringPiece(uint64_t off) { | |
| 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]; | |
| } | |
| const StringPiece &CStringInputSection::getStringPiece(uint64_t off) const { | |
| return const_cast<CStringInputSection *>(this)->getStringPiece(off); | |
| } | |
| size_t CStringInputSection::getStringPieceIndex(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 std::distance(pieces.begin(), it) - 1; | |
| } | |
| uint64_t CStringInputSection::getOffset(uint64_t off) const { | |
| const StringPiece &piece = getStringPiece(off); | |
| uint64_t addend = off - piece.inSecOff; | |
| return piece.outSecOff + addend; | |
| } | |
| WordLiteralInputSection::WordLiteralInputSection(const Section §ion, | |
| ArrayRef<uint8_t> data, | |
| uint32_t align) | |
| : InputSection(WordLiteralKind, section, data, align) { | |
| switch (sectionType(getFlags())) { | |
| case S_4BYTE_LITERALS: | |
| power2LiteralSize = 2; | |
| break; | |
| case S_8BYTE_LITERALS: | |
| power2LiteralSize = 3; | |
| break; | |
| case S_16BYTE_LITERALS: | |
| power2LiteralSize = 4; | |
| break; | |
| default: | |
| llvm_unreachable("invalid literal section type"); | |
| } | |
| live.resize(data.size() >> power2LiteralSize, !config->deadStrip); | |
| } | |
| uint64_t WordLiteralInputSection::getOffset(uint64_t off) const { | |
| if (off >= data.size()) | |
| fatal(toString(this) + ": offset is outside the section"); | |
| auto *osec = cast<WordLiteralSection>(parent); | |
| const uintptr_t buf = reinterpret_cast<uintptr_t>(data.data()); | |
| switch (sectionType(getFlags())) { | |
| case S_4BYTE_LITERALS: | |
| return osec->getLiteral4Offset(buf + (off & ~3LLU)) | (off & 3); | |
| case S_8BYTE_LITERALS: | |
| return osec->getLiteral8Offset(buf + (off & ~7LLU)) | (off & 7); | |
| case S_16BYTE_LITERALS: | |
| return osec->getLiteral16Offset(buf + (off & ~15LLU)) | (off & 15); | |
| default: | |
| llvm_unreachable("invalid literal section type"); | |
| } | |
| } | |
| bool macho::isCodeSection(const InputSection *isec) { | |
| return sections::isCodeSection(isec->getName(), isec->getSegName(), | |
| isec->getFlags()); | |
| } | |
| bool macho::isCfStringSection(const InputSection *isec) { | |
| return isec->getName() == section_names::cfString && | |
| isec->getSegName() == segment_names::data; | |
| } | |
| bool macho::isClassRefsSection(const InputSection *isec) { | |
| return isec->getName() == section_names::objcClassRefs && | |
| isec->getSegName() == segment_names::data; | |
| } | |
| bool macho::isSelRefsSection(const InputSection *isec) { | |
| return isec->getName() == section_names::objcSelrefs && | |
| isec->getSegName() == segment_names::data; | |
| } | |
| bool macho::isEhFrameSection(const InputSection *isec) { | |
| return isec->getName() == section_names::ehFrame && | |
| isec->getSegName() == segment_names::text; | |
| } | |
| bool macho::isGccExceptTabSection(const InputSection *isec) { | |
| return isec->getName() == section_names::gccExceptTab && | |
| isec->getSegName() == segment_names::text; | |
| } | |
| std::string lld::toString(const InputSection *isec) { | |
| return (toString(isec->getFile()) + ":(" + isec->getName() + ")").str(); | |
| } |