diff options
Diffstat (limited to 'lld/MachO/InputFiles.cpp')
| -rw-r--r-- | lld/MachO/InputFiles.cpp | 118 |
1 files changed, 96 insertions, 22 deletions
diff --git a/lld/MachO/InputFiles.cpp b/lld/MachO/InputFiles.cpp index b463d7817594..fd0e4ec8834c 100644 --- a/lld/MachO/InputFiles.cpp +++ b/lld/MachO/InputFiles.cpp @@ -385,7 +385,7 @@ void ObjFile::parseSections(ArrayRef<SectionHeader> sectionHeaders) { } void ObjFile::splitEhFrames(ArrayRef<uint8_t> data, Section &ehFrameSection) { - EhReader reader(this, data, /*dataOff=*/0, target->wordSize); + EhReader reader(this, data, /*dataOff=*/0); size_t off = 0; while (off < reader.size()) { uint64_t frameOff = off; @@ -1290,10 +1290,25 @@ void ObjFile::registerCompactUnwind(Section &compactUnwindSection) { struct CIE { macho::Symbol *personalitySymbol = nullptr; - bool fdesHaveLsda = false; bool fdesHaveAug = false; + uint8_t lsdaPtrSize = 0; // 0 => no LSDA + uint8_t funcPtrSize = 0; }; +static uint8_t pointerEncodingToSize(uint8_t enc) { + switch (enc & 0xf) { + case dwarf::DW_EH_PE_absptr: + return target->wordSize; + case dwarf::DW_EH_PE_sdata4: + return 4; + case dwarf::DW_EH_PE_sdata8: + // ld64 doesn't actually support sdata8, but this seems simple enough... + return 8; + default: + return 0; + }; +} + static CIE parseCIE(const InputSection *isec, const EhReader &reader, size_t off) { // Handling the full generality of possible DWARF encodings would be a major @@ -1301,8 +1316,6 @@ static CIE parseCIE(const InputSection *isec, const EhReader &reader, // DWARF and handle just that. constexpr uint8_t expectedPersonalityEnc = dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_indirect | dwarf::DW_EH_PE_sdata4; - constexpr uint8_t expectedPointerEnc = - dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_absptr; CIE cie; uint8_t version = reader.readByte(&off); @@ -1329,16 +1342,17 @@ static CIE parseCIE(const InputSection *isec, const EhReader &reader, break; } case 'L': { - cie.fdesHaveLsda = true; uint8_t lsdaEnc = reader.readByte(&off); - if (lsdaEnc != expectedPointerEnc) + cie.lsdaPtrSize = pointerEncodingToSize(lsdaEnc); + if (cie.lsdaPtrSize == 0) reader.failOn(off, "unexpected LSDA encoding 0x" + Twine::utohexstr(lsdaEnc)); break; } case 'R': { uint8_t pointerEnc = reader.readByte(&off); - if (pointerEnc != expectedPointerEnc) + cie.funcPtrSize = pointerEncodingToSize(pointerEnc); + if (cie.funcPtrSize == 0 || !(pointerEnc & dwarf::DW_EH_PE_pcrel)) reader.failOn(off, "unexpected pointer encoding 0x" + Twine::utohexstr(pointerEnc)); break; @@ -1468,7 +1482,7 @@ void ObjFile::registerEhFrames(Section &ehFrameSection) { else if (isec->symbols[0]->value != 0) fatal("found symbol at unexpected offset in __eh_frame"); - EhReader reader(this, isec->data, subsec.offset, target->wordSize); + EhReader reader(this, isec->data, subsec.offset); size_t dataOff = 0; // Offset from the start of the EH frame. reader.skipValidLength(&dataOff); // readLength() already validated this. // cieOffOff is the offset from the start of the EH frame to the cieOff @@ -1507,20 +1521,20 @@ void ObjFile::registerEhFrames(Section &ehFrameSection) { continue; } + assert(cieMap.count(cieIsec)); + const CIE &cie = cieMap[cieIsec]; // Offset of the function address within the EH frame. const size_t funcAddrOff = dataOff; - uint64_t funcAddr = reader.readPointer(&dataOff) + ehFrameSection.addr + - isecOff + funcAddrOff; - uint32_t funcLength = reader.readPointer(&dataOff); + uint64_t funcAddr = reader.readPointer(&dataOff, cie.funcPtrSize) + + ehFrameSection.addr + isecOff + funcAddrOff; + uint32_t funcLength = reader.readPointer(&dataOff, cie.funcPtrSize); size_t lsdaAddrOff = 0; // Offset of the LSDA address within the EH frame. - assert(cieMap.count(cieIsec)); - const CIE &cie = cieMap[cieIsec]; Optional<uint64_t> lsdaAddrOpt; if (cie.fdesHaveAug) { reader.skipLeb128(&dataOff); lsdaAddrOff = dataOff; - if (cie.fdesHaveLsda) { - uint64_t lsdaOff = reader.readPointer(&dataOff); + if (cie.lsdaPtrSize != 0) { + uint64_t lsdaOff = reader.readPointer(&dataOff, cie.lsdaPtrSize); if (lsdaOff != 0) // FIXME possible to test this? lsdaAddrOpt = ehFrameSection.addr + isecOff + lsdaAddrOff + lsdaOff; } @@ -1944,6 +1958,14 @@ DylibFile::DylibFile(const InterfaceFile &interface, DylibFile *umbrella, } } +DylibFile::DylibFile(DylibFile *umbrella) + : InputFile(DylibKind, MemoryBufferRef{}), refState(RefState::Unreferenced), + explicitlyLinked(false), isBundleLoader(false) { + if (umbrella == nullptr) + umbrella = this; + this->umbrella = umbrella; +} + void DylibFile::parseReexports(const InterfaceFile &interface) { const InterfaceFile *topLevel = interface.getParent() == nullptr ? &interface : interface.getParent(); @@ -1955,6 +1977,39 @@ void DylibFile::parseReexports(const InterfaceFile &interface) { } } +bool DylibFile::isExplicitlyLinked() const { + if (!explicitlyLinked) + return false; + + // If this dylib was explicitly linked, but at least one of the symbols + // of the synthetic dylibs it created via $ld$previous symbols is + // referenced, then that synthetic dylib fulfils the explicit linkedness + // and we can deadstrip this dylib if it's unreferenced. + for (const auto *dylib : extraDylibs) + if (dylib->isReferenced()) + return false; + + return true; +} + +DylibFile *DylibFile::getSyntheticDylib(StringRef installName, + uint32_t currentVersion, + uint32_t compatVersion) { + for (DylibFile *dylib : extraDylibs) + if (dylib->installName == installName) { + // FIXME: Check what to do if different $ld$previous symbols + // request the same dylib, but with different versions. + return dylib; + } + + auto *dylib = make<DylibFile>(umbrella == this ? nullptr : umbrella); + dylib->installName = saver().save(installName); + dylib->currentVersion = currentVersion; + dylib->compatibilityVersion = compatVersion; + extraDylibs.push_back(dylib); + return dylib; +} + // $ld$ symbols modify the properties/behavior of the library (e.g. its install // name, compatibility version or hide/add symbols) for specific target // versions. @@ -1990,10 +2045,9 @@ void DylibFile::handleLDPreviousSymbol(StringRef name, StringRef originalName) { std::tie(platformStr, name) = name.split('$'); std::tie(startVersion, name) = name.split('$'); std::tie(endVersion, name) = name.split('$'); - std::tie(symbolName, rest) = name.split('$'); - // TODO: ld64 contains some logic for non-empty symbolName as well. - if (!symbolName.empty()) - return; + std::tie(symbolName, rest) = name.rsplit('$'); + + // FIXME: Does this do the right thing for zippered files? unsigned platform; if (platformStr.getAsInteger(10, platform) || platform != static_cast<unsigned>(config->platform())) @@ -2014,8 +2068,9 @@ void DylibFile::handleLDPreviousSymbol(StringRef name, StringRef originalName) { config->platformInfo.minimum >= end) return; - this->installName = saver().save(installName); - + // Initialized to compatibilityVersion for the symbolName branch below. + uint32_t newCompatibilityVersion = compatibilityVersion; + uint32_t newCurrentVersionForSymbol = currentVersion; if (!compatVersion.empty()) { VersionTuple cVersion; if (cVersion.tryParse(compatVersion)) { @@ -2023,8 +2078,27 @@ void DylibFile::handleLDPreviousSymbol(StringRef name, StringRef originalName) { "' ignored"); return; } - compatibilityVersion = encodeVersion(cVersion); + newCompatibilityVersion = encodeVersion(cVersion); + newCurrentVersionForSymbol = newCompatibilityVersion; + } + + if (!symbolName.empty()) { + // A $ld$previous$ symbol with symbol name adds a symbol with that name to + // a dylib with given name and version. + auto *dylib = getSyntheticDylib(installName, newCurrentVersionForSymbol, + newCompatibilityVersion); + + // Just adding the symbol to the symtab works because dylibs contain their + // symbols in alphabetical order, guaranteeing $ld$ symbols to precede + // normal symbols. + dylib->symbols.push_back(symtab->addDylib( + saver().save(symbolName), dylib, /*isWeakDef=*/false, /*isTlv=*/false)); + return; } + + // A $ld$previous$ symbol without symbol name modifies the dylib it's in. + this->installName = saver().save(installName); + this->compatibilityVersion = newCompatibilityVersion; } void DylibFile::handleLDInstallNameSymbol(StringRef name, |
