ld64.lld: Add janky support for tbd files
With this, a simple hello world links against libSystem.tbd and the
old ld64.lld linker kind of works again with newer SDKs.
The motivation here is to have an arm64 cross linker that's good
enough to be able to run simple configure link checks on non-mac
systems for generating config.h files. Once -flavor darwinnew can
link arm64, we'll switch to that.
diff --git a/lld/lib/ReaderWriter/MachO/File.h b/lld/lib/ReaderWriter/MachO/File.h
index 0727029..a9a7faf 100644
--- a/lld/lib/ReaderWriter/MachO/File.h
+++ b/lld/lib/ReaderWriter/MachO/File.h
@@ -17,6 +17,8 @@
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/Support/Format.h"
+#include "llvm/TextAPI/MachO/InterfaceFile.h"
+#include "llvm/TextAPI/MachO/TextAPIReader.h"
#include <unordered_map>
namespace lld {
@@ -322,7 +324,8 @@
void loadReExportedDylibs(FindDylib find) {
for (ReExportedDylib &entry : _reExportedDylibs) {
- entry.file = find(entry.path);
+ if (!entry.file)
+ entry.file = find(entry.path);
}
}
@@ -339,7 +342,7 @@
return std::error_code();
}
-private:
+protected:
OwningAtomPtr<SharedLibraryAtom> exports(StringRef name,
StringRef installName) const {
// First, check if requested symbol is directly implemented by this dylib.
@@ -373,6 +376,7 @@
struct ReExportedDylib {
ReExportedDylib(StringRef p) : path(p), file(nullptr) { }
+ ReExportedDylib(StringRef p, MachODylibFile *file) : path(p), file(file) { }
StringRef path;
MachODylibFile *file;
};
@@ -393,6 +397,70 @@
mutable std::unordered_map<StringRef, AtomAndFlags> _nameToAtom;
};
+class TAPIFile : public MachODylibFile {
+public:
+
+ TAPIFile(std::unique_ptr<MemoryBuffer> mb, MachOLinkingContext *ctx)
+ : MachODylibFile(std::move(mb), ctx) {}
+
+ std::error_code doParse() override {
+
+ llvm::Expected<std::unique_ptr<llvm::MachO::InterfaceFile>> result =
+ llvm::MachO::TextAPIReader::get(*_mb);
+ if (!result)
+ return std::make_error_code(std::errc::invalid_argument);
+
+ std::unique_ptr<llvm::MachO::InterfaceFile> interface{std::move(*result)};
+ return loadFromInterface(*interface);
+ }
+
+private:
+ std::error_code loadFromInterface(llvm::MachO::InterfaceFile &interface) {
+ llvm::MachO::Architecture arch;
+ switch(_ctx->arch()) {
+ case MachOLinkingContext::arch_x86:
+ arch = llvm::MachO::AK_i386;
+ break;
+ case MachOLinkingContext::arch_x86_64:
+ arch = llvm::MachO::AK_x86_64;
+ break;
+ case MachOLinkingContext::arch_arm64:
+ arch = llvm::MachO::AK_arm64;
+ break;
+ default:
+ return std::make_error_code(std::errc::invalid_argument);
+ }
+
+ setInstallName(interface.getInstallName().copy(allocator()));
+ // TODO(compnerd) filter out symbols based on the target platform
+ for (const auto symbol : interface.symbols())
+ if (symbol->getArchitectures().has(arch))
+ addExportedSymbol(symbol->getName(), symbol->isWeakDefined(), true);
+
+ for (const llvm::MachO::InterfaceFileRef &reexport :
+ interface.reexportedLibraries())
+ addReExportedDylib(reexport.getInstallName().copy(allocator()));
+
+ for (const auto& document : interface.documents()) {
+ for (auto& reexport : _reExportedDylibs) {
+ if (reexport.path != document->getInstallName())
+ continue;
+ assert(!reexport.file);
+ _ownedFiles.push_back(std::make_unique<TAPIFile>(
+ MemoryBuffer::getMemBuffer("", _mb->getBufferIdentifier()), _ctx));
+ reexport.file = _ownedFiles.back().get();
+ std::error_code err = _ownedFiles.back()->loadFromInterface(*document);
+ if (!err)
+ return err;
+ }
+ }
+
+ return std::error_code();
+ }
+
+ std::vector<std::unique_ptr<TAPIFile>> _ownedFiles;
+};
+
} // end namespace mach_o
} // end namespace lld
diff --git a/lld/lib/ReaderWriter/MachO/MachOLinkingContext.cpp b/lld/lib/ReaderWriter/MachO/MachOLinkingContext.cpp
index 6fe9cde..acd919e 100644
--- a/lld/lib/ReaderWriter/MachO/MachOLinkingContext.cpp
+++ b/lld/lib/ReaderWriter/MachO/MachOLinkingContext.cpp
@@ -540,6 +540,12 @@
return llvm::None;
}
+ // Search for stub library
+ fullPath.assign(path);
+ llvm::sys::path::append(fullPath, Twine("lib") + libName + ".tbd");
+ if (fileExists(fullPath))
+ return fullPath.str().copy(_allocator);
+
// Search for dynamic library
fullPath.assign(path);
llvm::sys::path::append(fullPath, Twine("lib") + libName + ".dylib");
diff --git a/lld/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp b/lld/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp
index 963f122..87601ca 100644
--- a/lld/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp
+++ b/lld/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp
@@ -576,6 +576,26 @@
MachOLinkingContext &_ctx;
};
+class MachOTAPIReader : public Reader {
+public:
+ MachOTAPIReader(MachOLinkingContext &ctx) : _ctx(ctx) {}
+
+ bool canParse(file_magic magic, MemoryBufferRef mb) const override {
+ return magic == file_magic::tapi_file;
+ }
+
+ ErrorOr<std::unique_ptr<File>>
+ loadFile(std::unique_ptr<MemoryBuffer> mb,
+ const Registry ®istry) const override {
+ std::unique_ptr<File> ret =
+ std::make_unique<TAPIFile>(std::move(mb), &_ctx);
+ return std::move(ret);
+ }
+
+private:
+ MachOLinkingContext &_ctx;
+};
+
} // namespace normalized
} // namespace mach_o
@@ -583,6 +603,7 @@
MachOLinkingContext::Arch arch = ctx.arch();
add(std::unique_ptr<Reader>(new mach_o::normalized::MachOObjectReader(ctx)));
add(std::unique_ptr<Reader>(new mach_o::normalized::MachODylibReader(ctx)));
+ add(std::unique_ptr<Reader>(new mach_o::normalized::MachOTAPIReader(ctx)));
addKindTable(Reference::KindNamespace::mach_o, ctx.archHandler().kindArch(),
ctx.archHandler().kindStrings());
add(std::unique_ptr<YamlIOTaggedDocumentHandler>(
diff --git a/lld/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp b/lld/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp
index 55dcad9..42ac711 100644
--- a/lld/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp
+++ b/lld/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp
@@ -1037,7 +1037,7 @@
// Add all stabs.
for (auto &stab : _stabs) {
- Symbol sym;
+ lld::mach_o::normalized::Symbol sym;
sym.type = static_cast<NListType>(stab.type);
sym.scope = 0;
sym.sect = stab.other;
@@ -1066,7 +1066,7 @@
AtomAndIndex ai = { atom, sect->finalSectionIndex, symbolScope };
globals.push_back(ai);
} else {
- Symbol sym;
+ lld::mach_o::normalized::Symbol sym;
sym.name = atom->name();
sym.type = N_SECT;
sym.scope = symbolScope;
@@ -1082,7 +1082,7 @@
char tmpName[16];
sprintf(tmpName, "L%04u", tempNum++);
StringRef tempRef(tmpName);
- Symbol sym;
+ lld::mach_o::normalized::Symbol sym;
sym.name = tempRef.copy(file.ownedAllocations);
sym.type = N_SECT;
sym.scope = 0;
@@ -1099,7 +1099,7 @@
std::sort(globals.begin(), globals.end(), AtomSorter());
const uint32_t globalStartIndex = file.localSymbols.size();
for (AtomAndIndex &ai : globals) {
- Symbol sym;
+ lld::mach_o::normalized::Symbol sym;
sym.name = ai.atom->name();
sym.type = N_SECT;
sym.scope = ai.scope;
@@ -1124,7 +1124,7 @@
std::sort(undefs.begin(), undefs.end(), AtomSorter());
const uint32_t start = file.globalSymbols.size() + file.localSymbols.size();
for (AtomAndIndex &ai : undefs) {
- Symbol sym;
+ lld::mach_o::normalized::Symbol sym;
uint16_t desc = 0;
if (!rMode) {
uint8_t ordinal = 0;
diff --git a/lld/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp b/lld/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp
index 80a1bf0..0a59e24 100644
--- a/lld/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp
+++ b/lld/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp
@@ -223,10 +223,11 @@
llvm_unreachable("unknown scope value!");
}
-void appendSymbolsInSection(const std::vector<Symbol> &inSymbols,
- uint32_t sectionIndex,
- SmallVector<const Symbol *, 64> &outSyms) {
- for (const Symbol &sym : inSymbols) {
+void appendSymbolsInSection(
+ const std::vector<lld::mach_o::normalized::Symbol> &inSymbols,
+ uint32_t sectionIndex,
+ SmallVector<const lld::mach_o::normalized::Symbol *, 64> &outSyms) {
+ for (const lld::mach_o::normalized::Symbol &sym : inSymbols) {
// Only look at definition symbols.
if ((sym.type & N_TYPE) != N_SECT)
continue;
@@ -286,13 +287,14 @@
}
// Find all symbols in this section.
- SmallVector<const Symbol *, 64> symbols;
+ SmallVector<const lld::mach_o::normalized::Symbol *, 64> symbols;
appendSymbolsInSection(normalizedFile.globalSymbols, sectIndex, symbols);
appendSymbolsInSection(normalizedFile.localSymbols, sectIndex, symbols);
// Sort symbols.
std::sort(symbols.begin(), symbols.end(),
- [](const Symbol *lhs, const Symbol *rhs) -> bool {
+ [](const lld::mach_o::normalized::Symbol *lhs,
+ const lld::mach_o::normalized::Symbol *rhs) -> bool {
if (lhs == rhs)
return false;
// First by address.
@@ -300,7 +302,7 @@
uint64_t rhsAddr = rhs->value;
if (lhsAddr != rhsAddr)
return lhsAddr < rhsAddr;
- // If same address, one is an alias so sort by scope.
+ // If same address, one is an alias so sort by scope.
Atom::Scope lScope = atomScope(lhs->scope);
Atom::Scope rScope = atomScope(rhs->scope);
if (lScope != rScope)
@@ -339,8 +341,8 @@
scatterable, copyRefs);
}
- const Symbol *lastSym = nullptr;
- for (const Symbol *sym : symbols) {
+ const lld::mach_o::normalized::Symbol *lastSym = nullptr;
+ for (const lld::mach_o::normalized::Symbol *sym : symbols) {
if (lastSym != nullptr) {
// Ignore any assembler added "ltmpNNN" symbol at start of section
// if there is another symbol at the start.
@@ -550,7 +552,7 @@
auto atomBySymbol = [&] (uint32_t symbolIndex, const lld::Atom **result)
-> llvm::Error {
// Find symbol from index.
- const Symbol *sym = nullptr;
+ const lld::mach_o::normalized::Symbol *sym = nullptr;
uint32_t numStabs = normalizedFile.stabsSymbols.size();
uint32_t numLocal = normalizedFile.localSymbols.size();
uint32_t numGlobal = normalizedFile.globalSymbols.size();
diff --git a/lld/test/mach-o/Inputs/MacOSX.sdk/usr/lib/libSystem.tbd b/lld/test/mach-o/Inputs/MacOSX.sdk/usr/lib/libSystem.tbd
new file mode 100644
index 0000000..1a90cff
--- /dev/null
+++ b/lld/test/mach-o/Inputs/MacOSX.sdk/usr/lib/libSystem.tbd
@@ -0,0 +1,42 @@
+--- !tapi-tbd-v3
+archs: [ x86_64 ]
+uuids: [ 'x86_64: 00000000-0000-0000-0000-000000000000' ]
+platform: macosx
+install-name: '/usr/lib/libSystem.B.dylib'
+current-version: 0001.001.1
+exports:
+ - archs: [ 'x86_64' ]
+ re-exports: [ '/usr/lib/system/libdyld.dylib',
+ '/usr/lib/system/libsystem_c.dylib',
+ '/usr/lib/system/libsystem_m.dylib' ]
+--- !tapi-tbd-v3
+archs: [ x86_64 ]
+uuids: [ 'x86_64: 00000000-0000-0000-0000-000000000001' ]
+platform: macosx
+install-name: '/usr/lib/libdyld.dylib'
+current-version: 0001.001.1
+parent-umbrella: System
+exports:
+ - archs: [ 'x86_64' ]
+ symbols: [ dyld_stub_binder ]
+--- !tapi-tbd-v3
+archs: [ x86_64 ]
+uuids: [ 'x86_64: 00000000-0000-0000-0000-000000000002' ]
+platform: macosx
+install-name: '/usr/lib/libsystem_c.dylib'
+current-version: 0001.001.1
+parent-umbrella: System
+exports:
+ - archs: [ 'x86_64' ]
+ symbols: [ ]
+--- !tapi-tbd-v3
+archs: [ x86_64 ]
+uuids: [ 'x86_64: 00000000-0000-0000-0000-000000000003' ]
+platform: macosx
+install-name: '/usr/lib/libsystem_m.dylib'
+current-version: 0001.001.1
+parent-umbrella: System
+exports:
+ - archs: [ 'x86_64' ]
+ symbols: [ ___nan ]
+...
diff --git a/lld/test/mach-o/stub-link.s b/lld/test/mach-o/stub-link.s
new file mode 100644
index 0000000..fb12b76
--- /dev/null
+++ b/lld/test/mach-o/stub-link.s
@@ -0,0 +1,21 @@
+# REQUIRES: x86
+
+# RUN: mkdir -p %t
+#
+# RUN: llvm-mc -filetype obj -triple x86_64-apple-darwin %s -o %t/test.o
+# RUN: ld64.lld -o %t/test -Z -L%S/Inputs/MacOSX.sdk/usr/lib -lSystem %t/test.o
+#
+# RUN: llvm-objdump --bind --no-show-raw-insn -d -r %t/test | FileCheck %s
+
+# CHECK: Disassembly of section __TEXT,__text:
+# CHECK: movq {{.*}} # [[ADDR:[0-9a-f]+]]
+
+# CHECK: Bind table:
+# CHECK: __DATA __got 0x[[ADDR]] pointer 0 libSystem ___nan
+
+.section __TEXT,__text
+.global _main
+
+_main:
+ movq ___nan@GOTPCREL(%rip), %rax
+ ret