template <class NList>
static macho::Symbol *createDefined(const NList &sym, StringRef name,
InputSection *isec, uint64_t value,
- uint64_t size) {
+ uint64_t size, bool forceHidden) {
// Symbol scope is determined by sym.n_type & (N_EXT | N_PEXT):
// N_EXT: Global symbols. These go in the symbol table during the link,
// and also in the export table of the output so that the dynamic
(sym.n_desc & (N_WEAK_DEF | N_WEAK_REF)) == (N_WEAK_DEF | N_WEAK_REF);
if (sym.n_type & N_EXT) {
- bool isPrivateExtern = sym.n_type & N_PEXT;
+ // -load_hidden makes us treat global symbols as linkage unit scoped.
+ // Duplicates are reported but the symbol does not go in the export trie.
+ bool isPrivateExtern = sym.n_type & N_PEXT || forceHidden;
+
// lld's behavior for merging symbols is slightly different from ld64:
// ld64 picks the winning symbol based on several criteria (see
// pickBetweenRegularAtoms() in ld64's SymbolTable.cpp), while lld
// InputSection. They cannot be weak.
template <class NList>
static macho::Symbol *createAbsolute(const NList &sym, InputFile *file,
- StringRef name) {
+ StringRef name, bool forceHidden) {
if (sym.n_type & N_EXT) {
+ bool isPrivateExtern = sym.n_type & N_PEXT || forceHidden;
return symtab->addDefined(
name, file, nullptr, sym.n_value, /*size=*/0,
- /*isWeakDef=*/false, sym.n_type & N_PEXT, sym.n_desc & N_ARM_THUMB_DEF,
+ /*isWeakDef=*/false, isPrivateExtern, sym.n_desc & N_ARM_THUMB_DEF,
/*isReferencedDynamically=*/false, sym.n_desc & N_NO_DEAD_STRIP,
/*isWeakDefCanBeHidden=*/false);
}
macho::Symbol *ObjFile::parseNonSectionSymbol(const NList &sym,
StringRef name) {
uint8_t type = sym.n_type & N_TYPE;
+ bool isPrivateExtern = sym.n_type & N_PEXT || forceHidden;
switch (type) {
case N_UNDF:
return sym.n_value == 0
? symtab->addUndefined(name, this, sym.n_desc & N_WEAK_REF)
: symtab->addCommon(name, this, sym.n_value,
1 << GET_COMM_ALIGN(sym.n_desc),
- sym.n_type & N_PEXT);
+ isPrivateExtern);
case N_ABS:
- return createAbsolute(sym, this, name);
+ return createAbsolute(sym, this, name, forceHidden);
case N_PBUD:
case N_INDR:
error("TODO: support symbols of type " + std::to_string(type));
" at misaligned offset");
continue;
}
- symbols[symIndex] = createDefined(sym, name, isec, 0, isec->getSize());
+ symbols[symIndex] =
+ createDefined(sym, name, isec, 0, isec->getSize(), forceHidden);
}
continue;
}
// 4. If we have a literal section (e.g. __cstring and __literal4).
if (!subsectionsViaSymbols || symbolOffset == 0 ||
sym.n_desc & N_ALT_ENTRY || !isa<ConcatInputSection>(isec)) {
- symbols[symIndex] =
- createDefined(sym, name, isec, symbolOffset, symbolSize);
+ symbols[symIndex] = createDefined(sym, name, isec, symbolOffset,
+ symbolSize, forceHidden);
continue;
}
auto *concatIsec = cast<ConcatInputSection>(isec);
// By construction, the symbol will be at offset zero in the new
// subsection.
- symbols[symIndex] =
- createDefined(sym, name, nextIsec, /*value=*/0, symbolSize);
+ symbols[symIndex] = createDefined(sym, name, nextIsec, /*value=*/0,
+ symbolSize, forceHidden);
// TODO: ld64 appears to preserve the original alignment as well as each
// subsection's offset from the last aligned address. We should consider
// emulating that behavior.
}
ObjFile::ObjFile(MemoryBufferRef mb, uint32_t modTime, StringRef archiveName,
- bool lazy)
- : InputFile(ObjKind, mb, lazy), modTime(modTime) {
+ bool lazy, bool forceHidden)
+ : InputFile(ObjKind, mb, lazy), modTime(modTime), forceHidden(forceHidden) {
this->archiveName = std::string(archiveName);
if (lazy) {
if (target->wordSize == 8)
warn("using '-application_extension' with unsafe dylib: " + toString(this));
}
-ArchiveFile::ArchiveFile(std::unique_ptr<object::Archive> &&f)
- : InputFile(ArchiveKind, f->getMemoryBufferRef()), file(std::move(f)) {}
+ArchiveFile::ArchiveFile(std::unique_ptr<object::Archive> &&f, bool forceHidden)
+ : InputFile(ArchiveKind, f->getMemoryBufferRef()), file(std::move(f)),
+ forceHidden(forceHidden) {}
void ArchiveFile::addLazySymbols() {
for (const object::Archive::Symbol &sym : file->symbols())
symtab->addLazyArchive(sym.getName(), this, sym);
}
-static Expected<InputFile *> loadArchiveMember(MemoryBufferRef mb,
- uint32_t modTime,
- StringRef archiveName,
- uint64_t offsetInArchive) {
+static Expected<InputFile *>
+loadArchiveMember(MemoryBufferRef mb, uint32_t modTime, StringRef archiveName,
+ uint64_t offsetInArchive, bool forceHidden) {
if (config->zeroModTime)
modTime = 0;
switch (identify_magic(mb.getBuffer())) {
case file_magic::macho_object:
- return make<ObjFile>(mb, modTime, archiveName);
+ return make<ObjFile>(mb, modTime, archiveName, /*lazy=*/false, forceHidden);
case file_magic::bitcode:
- return make<BitcodeFile>(mb, archiveName, offsetInArchive);
+ return make<BitcodeFile>(mb, archiveName, offsetInArchive, /*lazy=*/false,
+ forceHidden);
default:
return createStringError(inconvertibleErrorCode(),
mb.getBufferIdentifier() +
if (!modTime)
return modTime.takeError();
- Expected<InputFile *> file =
- loadArchiveMember(*mb, toTimeT(*modTime), getName(), c.getChildOffset());
+ Expected<InputFile *> file = loadArchiveMember(
+ *mb, toTimeT(*modTime), getName(), c.getChildOffset(), forceHidden);
if (!file)
return file.takeError();
case GlobalValue::DefaultVisibility:
break;
}
- isPrivateExtern = isPrivateExtern || objSym.canBeOmittedFromSymbolTable();
+ isPrivateExtern = isPrivateExtern || objSym.canBeOmittedFromSymbolTable() ||
+ file.forceHidden;
if (objSym.isCommon())
return symtab->addCommon(name, &file, objSym.getCommonSize(),
}
BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName,
- uint64_t offsetInArchive, bool lazy)
- : InputFile(BitcodeKind, mb, lazy) {
+ uint64_t offsetInArchive, bool lazy, bool forceHidden)
+ : InputFile(BitcodeKind, mb, lazy), forceHidden(forceHidden) {
this->archiveName = std::string(archiveName);
std::string path = mb.getBufferIdentifier().str();
// ThinLTO assumes that all MemoryBufferRefs given to it have a unique
class ObjFile final : public InputFile {
public:
ObjFile(MemoryBufferRef mb, uint32_t modTime, StringRef archiveName,
- bool lazy = false);
+ bool lazy = false, bool forceHidden = false);
ArrayRef<llvm::MachO::data_in_code_entry> getDataInCode() const;
template <class LP> void parse();
std::unique_ptr<lld::DWARFCache> dwarfCache;
Section *addrSigSection = nullptr;
const uint32_t modTime;
+ bool forceHidden;
std::vector<ConcatInputSection *> debugSections;
std::vector<CallGraphEntry> callGraph;
llvm::DenseMap<ConcatInputSection *, FDE> fdes;
// .a file
class ArchiveFile final : public InputFile {
public:
- explicit ArchiveFile(std::unique_ptr<llvm::object::Archive> &&file);
+ explicit ArchiveFile(std::unique_ptr<llvm::object::Archive> &&file,
+ bool forceHidden);
void addLazySymbols();
void fetch(const llvm::object::Archive::Symbol &);
// LLD normally doesn't use Error for error-handling, but the underlying
// Keep track of children fetched from the archive by tracking
// which address offsets have been fetched already.
llvm::DenseSet<uint64_t> seen;
+ // Load all symbols with hidden visibility (-load_hidden).
+ bool forceHidden;
};
class BitcodeFile final : public InputFile {
public:
explicit BitcodeFile(MemoryBufferRef mb, StringRef archiveName,
- uint64_t offsetInArchive, bool lazy = false);
+ uint64_t offsetInArchive, bool lazy = false,
+ bool forceHidden = false);
static bool classof(const InputFile *f) { return f->kind() == BitcodeKind; }
void parse();
std::unique_ptr<llvm::lto::InputFile> obj;
+ bool forceHidden;
private:
void parseLazy();
--- /dev/null
+# REQUIRES: x86
+# RUN: rm -rf %t; split-file %s %t
+# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/archive-foo.s -o %t/archive-foo.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/archive-baz.s -o %t/archive-baz.o
+# RUN: llvm-ar rcs %t/foo.a %t/archive-foo.o
+# RUN: llvm-ar rcs %t/baz.a %t/archive-baz.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/obj.s -o %t/obj.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/obj-bar.s -o %t/obj-bar.o
+
+# RUN: %lld -dylib -lSystem %t/obj.o -load_hidden %t/foo.a -o %t/test.dylib
+# RUN: llvm-nm %t/test.dylib | FileCheck %s
+# CHECK-DAG: t _foo
+# CHECK-DAG: d _bar
+
+## If an archive has already been loaded without -load_hidden earlier in the command line,
+## -load_hidden does not have an effect.
+# RUN: %lld -dylib -lSystem %t/obj.o %t/foo.a -load_hidden %t/foo.a -o %t/test-regular-then-hidden.dylib
+# RUN: llvm-nm %t/test-regular-then-hidden.dylib | FileCheck %s --check-prefix=REGULAR-THEN-HIDDEN
+# REGULAR-THEN-HIDDEN-DAG: T _foo
+# REGULAR-THEN-HIDDEN-DAG: D _bar
+
+## If -load_hidden comes first, the symbols will have hidden visibility.
+# RUN: %lld -dylib -lSystem %t/obj.o -load_hidden %t/foo.a %t/foo.a -o %t/test-hidden-then-regular.dylib
+# RUN: llvm-nm %t/test-hidden-then-regular.dylib | FileCheck %s --check-prefix=HIDDEN-THEN-REGULAR
+# HIDDEN-THEN-REGULAR-DAG: t _foo
+# HIDDEN-THEN-REGULAR-DAG: d _bar
+
+## If both -load_hidden and -force_load are specified, the earlier one will have an effect.
+# RUN: %lld -dylib -lSystem %t/obj.o %t/foo.a -force_load %t/baz.a -load_hidden %t/baz.a -o %t/test-force-then-hidden.dylib
+# RUN: llvm-nm %t/test-force-then-hidden.dylib | FileCheck %s --check-prefix=FORCE-THEN-HIDDEN
+# FORCE-THEN-HIDDEN: T _baz
+# RUN: %lld -dylib -lSystem %t/obj.o %t/foo.a -load_hidden %t/baz.a -force_load %t/baz.a -o %t/test-hidden-then-force.dylib
+# RUN: llvm-nm %t/test-hidden-then-force.dylib | FileCheck %s --check-prefix=HIDDEN-THEN-FORCE
+# HIDDEN-THEN-FORCE-NOT: _baz
+
+## -load_hidden does not cause the library to be loaded eagerly.
+# RUN: %lld -dylib -lSystem %t/obj.o -load_hidden %t/foo.a -load_hidden %t/baz.a -o %t/test-lazy.dylib
+# RUN: llvm-nm %t/test-lazy.dylib | FileCheck %s --check-prefix=LAZY
+# LAZY-NOT: _baz
+
+## Specifying the same library twice is fine.
+# RUN: %lld -dylib -lSystem %t/obj.o -load_hidden %t/foo.a -load_hidden %t/foo.a -o %t/test-twice.dylib
+# RUN: llvm-nm %t/test-twice.dylib | FileCheck %s --check-prefix=TWICE
+# TWICE-DAG: t _foo
+# TWICE-DAG: d _bar
+
+## -load_hidden causes the symbols to have "private external" visibility, so duplicate definitions
+## are not allowed.
+# RUN: not %lld -dylib -lSystem %t/obj.o %t/obj-bar.o -load_hidden %t/foo.a 2>&1 | FileCheck %s --check-prefix=DUP
+# DUP: error: duplicate symbol: _bar
+
+#--- archive-foo.s
+.globl _foo
+_foo:
+
+.data
+.globl _bar
+_bar:
+
+#--- archive-baz.s
+.globl _baz
+_baz:
+
+#--- obj-bar.s
+.data
+.globl _bar
+_bar:
+
+#--- obj.s
+.globl _test
+_test:
+ call _foo