[lld-macho] Use export trie instead of symtab when linking against dylibs

Summary:
This allows us to link against stripped dylibs. Moreover, it's simply
more correct: The symbol table includes symbols that the dylib uses but
doesn't export.

This temporarily regresses our ability to do lazy symbol binding because
dyld_stub_binder isn't in libSystem's export trie. Rather, it is in one
of the sub-libraries libSystem re-exports. (This doesn't affect our
tests since we are mocking out dyld_stub_binder there.) A follow-up diff
will address this by adding support for sub-libraries.

Depends on D79114.

Reviewers: ruiu, pcc, MaskRay, smeenai, alexshap, gkm, Ktwu, christylee

Subscribers: mgorny, llvm-commits

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D79226
This commit is contained in:
Jez Ng 2020-04-22 20:00:57 -07:00
parent 5d3feefa0d
commit 7bbdbacd00
5 changed files with 79 additions and 17 deletions

View File

@ -37,6 +37,7 @@
#include "ExportTrie.h"
#include "Symbols.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Memory.h"
#include "llvm/ADT/Optional.h"
#include "llvm/BinaryFormat/MachO.h"
@ -232,5 +233,57 @@ void TrieBuilder::writeTo(uint8_t *buf) const {
node->writeTo(buf);
}
namespace {
// Parse a serialized trie and invoke a callback for each entry.
class TrieParser {
public:
TrieParser(const uint8_t *buf, size_t size, const TrieEntryCallback &callback)
: start(buf), end(start + size), callback(callback) {}
void parse(const uint8_t *buf, const Twine &cumulativeString);
void parse() { parse(start, ""); }
const uint8_t *start;
const uint8_t *end;
const TrieEntryCallback &callback;
};
} // namespace
void TrieParser::parse(const uint8_t *buf, const Twine &cumulativeString) {
if (buf >= end)
fatal("Node offset points outside export section");
unsigned ulebSize;
uint64_t terminalSize = decodeULEB128(buf, &ulebSize);
buf += ulebSize;
uint64_t flags = 0;
size_t offset;
if (terminalSize != 0) {
flags = decodeULEB128(buf, &ulebSize);
callback(cumulativeString, flags);
}
buf += terminalSize;
uint8_t numEdges = *buf++;
for (uint8_t i = 0; i < numEdges; ++i) {
const char *cbuf = reinterpret_cast<const char *>(buf);
StringRef substring = StringRef(cbuf, strnlen(cbuf, end - buf));
buf += substring.size() + 1;
offset = decodeULEB128(buf, &ulebSize);
buf += ulebSize;
parse(start + offset, cumulativeString + substring);
}
}
void parseTrie(const uint8_t *buf, size_t size,
const TrieEntryCallback &callback) {
if (size == 0)
return;
TrieParser(buf, size, callback).parse();
}
} // namespace macho
} // namespace lld

View File

@ -10,6 +10,7 @@
#define LLD_MACHO_EXPORT_TRIE_H
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/STLExtras.h"
#include <vector>
@ -35,6 +36,11 @@ private:
std::vector<TrieNode *> nodes;
};
using TrieEntryCallback =
llvm::function_ref<void(const llvm::Twine & /*name*/, uint64_t /*flags*/)>;
void parseTrie(const uint8_t *buf, size_t size, const TrieEntryCallback &);
} // namespace macho
} // namespace lld

View File

@ -42,6 +42,7 @@
//===----------------------------------------------------------------------===//
#include "InputFiles.h"
#include "ExportTrie.h"
#include "InputSection.h"
#include "OutputSection.h"
#include "SymbolTable.h"
@ -245,21 +246,14 @@ DylibFile::DylibFile(MemoryBufferRef mb) : InputFile(DylibKind, mb) {
}
// Initialize symbols.
if (const load_command *cmd = findCommand(hdr, LC_SYMTAB)) {
auto *c = reinterpret_cast<const symtab_command *>(cmd);
const char *strtab = reinterpret_cast<const char *>(buf + c->stroff);
ArrayRef<const nlist_64> nList(
reinterpret_cast<const nlist_64 *>(buf + c->symoff), c->nsyms);
symbols.reserve(c->nsyms);
for (const nlist_64 &sym : nList) {
StringRef name = strtab + sym.n_strx;
// TODO: Figure out what to do about undefined symbols: ignore or warn
// if unsatisfied? Also make sure we handle re-exported symbols
// correctly.
symbols.push_back(symtab->addDylib(name, this));
}
if (const load_command *cmd = findCommand(hdr, LC_DYLD_INFO_ONLY)) {
auto *c = reinterpret_cast<const dyld_info_command *>(cmd);
parseTrie(buf + c->export_off, c->export_size,
[&](const Twine &name, uint64_t flags) {
symbols.push_back(symtab->addDylib(saver.save(name), this));
});
} else {
error("LC_DYLD_INFO_ONLY not found in " + getName());
}
}

View File

@ -36,8 +36,8 @@ if (NOT LLD_BUILT_STANDALONE)
list(APPEND LLD_TEST_DEPS
FileCheck count llc llvm-ar llvm-as llvm-bcanalyzer llvm-config llvm-cvtres
llvm-dis llvm-dwarfdump llvm-lib llvm-lipo llvm-mc llvm-nm llvm-objcopy
llvm-objdump llvm-pdbutil llvm-readelf llvm-readobj not obj2yaml opt
yaml2obj
llvm-objdump llvm-pdbutil llvm-readelf llvm-readobj llvm-strip not obj2yaml
opt yaml2obj
)
endif()

View File

@ -8,6 +8,15 @@
# RUN: @executable_path/libhello.dylib %t/libhello.o -o %t/libhello.dylib
# RUN: lld -flavor darwinnew -dylib -install_name \
# RUN: @executable_path/libgoodbye.dylib %t/libgoodbye.o -o %t/libgoodbye.dylib
## Make sure we are using the export trie and not the symbol table when linking
## against these dylibs.
# RUN: llvm-strip %t/libhello.dylib
# RUN: llvm-strip %t/libgoodbye.dylib
# RUN: llvm-nm %t/libhello.dylib 2>&1 | FileCheck %s --check-prefix=NOSYM
# RUN: llvm-nm %t/libgoodbye.dylib 2>&1 | FileCheck %s --check-prefix=NOSYM
# NOSYM: no symbols
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %s -o %t/dylink.o
# RUN: lld -flavor darwinnew -o %t/dylink -Z -L%t -lhello -lgoodbye %t/dylink.o
# RUN: llvm-objdump --bind -d %t/dylink | FileCheck %s