forked from OSchip/llvm-project
[lld][MachO] Add first bits to support special symbols
This diff adds first bits to support special symbols $ld$previous* in LLD. $ld$* symbols modify properties/behavior of the library (e.g. its install name, compatibility version or hide/add symbols) for specific target versions. Test plan: make check-lld-macho Differential revision: https://reviews.llvm.org/D103505
This commit is contained in:
parent
5b0e50550c
commit
1309c181a8
|
@ -38,6 +38,12 @@ struct PlatformInfo {
|
||||||
llvm::VersionTuple sdk;
|
llvm::VersionTuple sdk;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inline uint32_t encodeVersion(const llvm::VersionTuple &version) {
|
||||||
|
return ((version.getMajor() << 020) |
|
||||||
|
(version.getMinor().getValueOr(0) << 010) |
|
||||||
|
version.getSubminor().getValueOr(0));
|
||||||
|
}
|
||||||
|
|
||||||
enum class NamespaceKind {
|
enum class NamespaceKind {
|
||||||
twolevel,
|
twolevel,
|
||||||
flat,
|
flat,
|
||||||
|
|
|
@ -848,10 +848,13 @@ DylibFile::DylibFile(MemoryBufferRef mb, DylibFile *umbrella,
|
||||||
auto *c = reinterpret_cast<const dyld_info_command *>(cmd);
|
auto *c = reinterpret_cast<const dyld_info_command *>(cmd);
|
||||||
parseTrie(buf + c->export_off, c->export_size,
|
parseTrie(buf + c->export_off, c->export_size,
|
||||||
[&](const Twine &name, uint64_t flags) {
|
[&](const Twine &name, uint64_t flags) {
|
||||||
|
StringRef savedName = saver.save(name);
|
||||||
|
if (handleLdSymbol(savedName))
|
||||||
|
return;
|
||||||
bool isWeakDef = flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION;
|
bool isWeakDef = flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION;
|
||||||
bool isTlv = flags & EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL;
|
bool isTlv = flags & EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL;
|
||||||
symbols.push_back(symtab->addDylib(
|
symbols.push_back(symtab->addDylib(savedName, exportingFile,
|
||||||
saver.save(name), exportingFile, isWeakDef, isTlv));
|
isWeakDef, isTlv));
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
error("LC_DYLD_INFO_ONLY not found in " + toString(this));
|
error("LC_DYLD_INFO_ONLY not found in " + toString(this));
|
||||||
|
@ -934,6 +937,9 @@ DylibFile::DylibFile(const InterfaceFile &interface, DylibFile *umbrella,
|
||||||
if (!symbol->getArchitectures().has(config->arch()))
|
if (!symbol->getArchitectures().has(config->arch()))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (handleLdSymbol(symbol->getName()))
|
||||||
|
continue;
|
||||||
|
|
||||||
switch (symbol->getKind()) {
|
switch (symbol->getKind()) {
|
||||||
case SymbolKind::GlobalSymbol:
|
case SymbolKind::GlobalSymbol:
|
||||||
addSymbol(symbol->getName());
|
addSymbol(symbol->getName());
|
||||||
|
@ -965,6 +971,73 @@ void DylibFile::parseReexports(const InterfaceFile &interface) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// $ld$ symbols modify the properties/behavior of the library (e.g. its install
|
||||||
|
// name, compatibility version or hide/add symbols) for specific target
|
||||||
|
// versions.
|
||||||
|
bool DylibFile::handleLdSymbol(StringRef originalName) {
|
||||||
|
// $ld$ previous $ <installname> $ <compatversion> $ <platformstr> $
|
||||||
|
// <startversion> $ <endversion> $ <symbol-name> $
|
||||||
|
if (!originalName.startswith("$ld$"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
StringRef action;
|
||||||
|
StringRef name;
|
||||||
|
std::tie(action, name) = originalName.drop_front(4 /* $ld$ */).split('$');
|
||||||
|
if (action.empty() || action != "previous")
|
||||||
|
return true;
|
||||||
|
|
||||||
|
StringRef installName;
|
||||||
|
StringRef compatVersion;
|
||||||
|
StringRef platformStr;
|
||||||
|
StringRef startVersion;
|
||||||
|
StringRef endVersion;
|
||||||
|
StringRef symbolName;
|
||||||
|
StringRef rest;
|
||||||
|
|
||||||
|
std::tie(installName, name) = name.split('$');
|
||||||
|
std::tie(compatVersion, name) = name.split('$');
|
||||||
|
std::tie(platformStr, name) = name.split('$');
|
||||||
|
std::tie(startVersion, name) = name.split('$');
|
||||||
|
std::tie(endVersion, symbolName) = name.split('$');
|
||||||
|
std::tie(symbolName, rest) = symbolName.split('$');
|
||||||
|
// TODO: ld64 contains some logic for non-empty symbolName as well.
|
||||||
|
if (!symbolName.empty())
|
||||||
|
return true;
|
||||||
|
unsigned platform;
|
||||||
|
if (platformStr.getAsInteger(10, platform) ||
|
||||||
|
platform != static_cast<unsigned>(config->platform()))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
VersionTuple start;
|
||||||
|
if (start.tryParse(startVersion)) {
|
||||||
|
warn("failed to parse start version, symbol '" + originalName +
|
||||||
|
"' ignored");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
VersionTuple end;
|
||||||
|
if (end.tryParse(endVersion)) {
|
||||||
|
warn("failed to parse end version, symbol '" + originalName + "' ignored");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (config->platformInfo.minimum < start ||
|
||||||
|
config->platformInfo.minimum >= end)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
dylibName = saver.save(installName);
|
||||||
|
|
||||||
|
if (!compatVersion.empty()) {
|
||||||
|
VersionTuple cVersion;
|
||||||
|
if (cVersion.tryParse(compatVersion)) {
|
||||||
|
warn("failed to parse compatibility version, symbol '" + originalName +
|
||||||
|
"' ignored");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
compatibilityVersion = encodeVersion(cVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
ArchiveFile::ArchiveFile(std::unique_ptr<object::Archive> &&f)
|
ArchiveFile::ArchiveFile(std::unique_ptr<object::Archive> &&f)
|
||||||
: InputFile(ArchiveKind, f->getMemoryBufferRef()), file(std::move(f)) {
|
: InputFile(ArchiveKind, f->getMemoryBufferRef()), file(std::move(f)) {
|
||||||
for (const object::Archive::Symbol &sym : file->symbols())
|
for (const object::Archive::Symbol &sym : file->symbols())
|
||||||
|
|
|
@ -173,6 +173,9 @@ public:
|
||||||
// implemented in the bundle. When used like this, it is very similar
|
// implemented in the bundle. When used like this, it is very similar
|
||||||
// to a Dylib, so we re-used the same class to represent it.
|
// to a Dylib, so we re-used the same class to represent it.
|
||||||
bool isBundleLoader;
|
bool isBundleLoader;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool handleLdSymbol(StringRef name);
|
||||||
};
|
};
|
||||||
|
|
||||||
// .a file
|
// .a file
|
||||||
|
|
|
@ -359,12 +359,6 @@ private:
|
||||||
StringRef path;
|
StringRef path;
|
||||||
};
|
};
|
||||||
|
|
||||||
static uint32_t encodeVersion(const VersionTuple &version) {
|
|
||||||
return ((version.getMajor() << 020) |
|
|
||||||
(version.getMinor().getValueOr(0) << 010) |
|
|
||||||
version.getSubminor().getValueOr(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
class LCMinVersion : public LoadCommand {
|
class LCMinVersion : public LoadCommand {
|
||||||
public:
|
public:
|
||||||
explicit LCMinVersion(const PlatformInfo &platformInfo)
|
explicit LCMinVersion(const PlatformInfo &platformInfo)
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
# REQUIRES: x86
|
||||||
|
|
||||||
|
# RUN: rm -rf %t; split-file --no-leading-lines %s %t
|
||||||
|
|
||||||
|
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/foo.s -o %t/foo.o
|
||||||
|
|
||||||
|
## Case 1: special symbol $ld$previous affects the install name / compatibility version
|
||||||
|
## since the specified version 11.0.0 is within the affected range [3.0, 14.0).
|
||||||
|
|
||||||
|
# RUN: %lld -o %t/libfoo1.dylib %t/libLDPreviousInstallName.tbd %t/foo.o -dylib -platform_version macos 11.0.0 11.0.0
|
||||||
|
# RUN: llvm-objdump --macho --dylibs-used %t/libfoo1.dylib | FileCheck --check-prefix=CASE1 %s
|
||||||
|
# CASE1: /New (compatibility version 1.2.3, current version 5.0.0)
|
||||||
|
|
||||||
|
## Case 2: special symbol $ld$previous does not affect the install name / compatibility version
|
||||||
|
## since the specified version 2.0.0 is lower than the affected range [3.0, 14.0).
|
||||||
|
|
||||||
|
# RUN: %lld -o %t/libfoo2.dylib %t/libLDPreviousInstallName.tbd %t.o -dylib -platform_version macos 2.0.0 2.0.0
|
||||||
|
# RUN: llvm-objdump --macho --dylibs-used %t/libfoo2.dylib | FileCheck --check-prefix=CASE2 %s
|
||||||
|
# CASE2: /Old (compatibility version 1.1.1, current version 5.0.0)
|
||||||
|
|
||||||
|
## Case 3: special symbol $ld$previous does not affect the install name / compatibility version
|
||||||
|
## since the specified version 14.0.0 is higher than the affected range [3.0, 14.0).
|
||||||
|
|
||||||
|
# RUN: %lld -o %t/libfoo3.dylib %t/libLDPreviousInstallName.tbd %t.o -dylib -platform_version macos 2.0.0 2.0.0
|
||||||
|
# RUN: llvm-objdump --macho --dylibs-used %t/libfoo3.dylib | FileCheck --check-prefix=CASE3 %s
|
||||||
|
# CASE3: /Old (compatibility version 1.1.1, current version 5.0.0)
|
||||||
|
|
||||||
|
## Check that we emit a warning for an invalid start, end and compatibility versions.
|
||||||
|
|
||||||
|
# RUN: %no_fatal_warnings_lld -o %t/libfoo1.dylib %t/libLDPreviousInvalid.tbd %t/foo.o -dylib \
|
||||||
|
# RUN: -platform_version macos 11.0.0 11.0.0 2>&1 | FileCheck --check-prefix=INVALID-VERSION %s
|
||||||
|
|
||||||
|
# INVALID-VERSION-DAG: failed to parse start version, symbol '$ld$previous$/New$1.2.3$1$3.a$14.0$$' ignored
|
||||||
|
# INVALID-VERSION-DAG: failed to parse end version, symbol '$ld$previous$/New$1.2.3$1$3.0$14.b$$' ignored
|
||||||
|
# INVALID-VERSION-DAG: failed to parse compatibility version, symbol '$ld$previous$/New$1.2.c$1$3.0$14.0$$' ignored
|
||||||
|
|
||||||
|
#--- foo.s
|
||||||
|
.long _xxx@GOTPCREL
|
||||||
|
|
||||||
|
#--- libLDPreviousInstallName.tbd
|
||||||
|
--- !tapi-tbd-v3
|
||||||
|
archs: [ x86_64 ]
|
||||||
|
uuids: [ 'x86_64: 19311019-01AB-342E-812B-73A74271A715' ]
|
||||||
|
platform: macosx
|
||||||
|
install-name: '/Old'
|
||||||
|
current-version: 5
|
||||||
|
compatibility-version: 1.1.1
|
||||||
|
exports:
|
||||||
|
- archs: [ x86_64 ]
|
||||||
|
symbols: [ '$ld$previous$/New$1.2.3$1$3.0$14.0$$', _xxx ]
|
||||||
|
...
|
||||||
|
|
||||||
|
#--- libLDPreviousInvalid.tbd
|
||||||
|
--- !tapi-tbd-v3
|
||||||
|
archs: [ x86_64 ]
|
||||||
|
uuids: [ 'x86_64: 19311019-01AB-342E-112B-73A74271A715' ]
|
||||||
|
platform: macosx
|
||||||
|
install-name: '/Old'
|
||||||
|
current-version: 5
|
||||||
|
compatibility-version: 1.1.1
|
||||||
|
exports:
|
||||||
|
- archs: [ x86_64 ]
|
||||||
|
symbols: [ '$ld$previous$/New$1.2.3$1$3.a$14.0$$',
|
||||||
|
'$ld$previous$/New$1.2.3$1$3.0$14.b$$',
|
||||||
|
'$ld$previous$/New$1.2.c$1$3.0$14.0$$',
|
||||||
|
_xxx ]
|
||||||
|
...
|
Loading…
Reference in New Issue