aboutsummaryrefslogtreecommitdiff
path: root/lld/MachO/InputFiles.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lld/MachO/InputFiles.cpp')
-rw-r--r--lld/MachO/InputFiles.cpp118
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,