Skip to content

Commit

Permalink
Reland "[lld-macho] Implement -load_hidden"
Browse files Browse the repository at this point in the history
This flag was introduced in ld64-609. It instructs the linker to link to
a static library while treating its symbols as if they had hidden
visibility. This is useful when building a dylib that links to static
libraries but we don't want the symbols from those to be exported.

Closes #51505

This reland adds bitcode file handling, so we won't get any compile
errors due to BitcodeFile::forceHidden being unused.

Differential Revision: https://reviews.llvm.org/D130473
  • Loading branch information
BertalanD committed Jul 25, 2022
1 parent 8fbc3c1 commit 595fc59
Show file tree
Hide file tree
Showing 8 changed files with 160 additions and 31 deletions.
10 changes: 8 additions & 2 deletions lld/MachO/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,8 @@ static DenseMap<StringRef, ArchiveFileInfo> loadedArchives;

static InputFile *addFile(StringRef path, LoadType loadType,
bool isLazy = false, bool isExplicit = true,
bool isBundleLoader = false) {
bool isBundleLoader = false,
bool isForceHidden = false) {
Optional<MemoryBufferRef> buffer = readFile(path);
if (!buffer)
return nullptr;
Expand All @@ -293,7 +294,7 @@ static InputFile *addFile(StringRef path, LoadType loadType,

if (!archive->isEmpty() && !archive->hasSymbolTable())
error(path + ": archive has no index; run ranlib to add one");
file = make<ArchiveFile>(std::move(archive));
file = make<ArchiveFile>(std::move(archive), isForceHidden);
} else {
file = entry->second.file;
// Command-line loads take precedence. If file is previously loaded via
Expand Down Expand Up @@ -1035,6 +1036,11 @@ static void createFiles(const InputArgList &args) {
case OPT_force_load:
addFile(rerootPath(arg->getValue()), LoadType::CommandLineForce);
break;
case OPT_load_hidden:
addFile(rerootPath(arg->getValue()), LoadType::CommandLine,
/*isLazy=*/false, /*isExplicit=*/true, /*isBundleLoader=*/false,
/*isForceHidden=*/true);
break;
case OPT_l:
case OPT_needed_l:
case OPT_reexport_l:
Expand Down
1 change: 1 addition & 0 deletions lld/MachO/DriverUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ std::string macho::createResponseFile(const InputArgList &args) {
break;
case OPT_force_load:
case OPT_weak_library:
case OPT_load_hidden:
os << arg->getSpelling() << " "
<< quote(rewriteInputPath(arg->getValue())) << "\n";
break;
Expand Down
60 changes: 34 additions & 26 deletions lld/MachO/InputFiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -768,7 +768,7 @@ void ObjFile::parseRelocations(ArrayRef<SectionHeader> sectionHeaders,
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
Expand All @@ -787,7 +787,10 @@ static macho::Symbol *createDefined(const NList &sym, StringRef name,
(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
Expand Down Expand Up @@ -844,11 +847,12 @@ static macho::Symbol *createDefined(const NList &sym, StringRef name,
// 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);
}
Expand All @@ -864,15 +868,16 @@ template <class NList>
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));
Expand Down Expand Up @@ -944,7 +949,8 @@ void ObjFile::parseSymbols(ArrayRef<typename LP::section> sectionHeaders,
" at misaligned offset");
continue;
}
symbols[symIndex] = createDefined(sym, name, isec, 0, isec->getSize());
symbols[symIndex] =
createDefined(sym, name, isec, 0, isec->getSize(), forceHidden);
}
continue;
}
Expand Down Expand Up @@ -979,8 +985,8 @@ void ObjFile::parseSymbols(ArrayRef<typename LP::section> sectionHeaders,
// 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);
Expand All @@ -998,8 +1004,8 @@ void ObjFile::parseSymbols(ArrayRef<typename LP::section> sectionHeaders,

// 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.
Expand Down Expand Up @@ -1036,8 +1042,8 @@ OpaqueFile::OpaqueFile(MemoryBufferRef mb, StringRef segName,
}

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)
Expand Down Expand Up @@ -2061,26 +2067,27 @@ void DylibFile::checkAppExtensionSafety(bool dylibIsAppExtensionSafe) const {
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() +
Expand All @@ -2104,8 +2111,8 @@ Error ArchiveFile::fetch(const object::Archive::Child &c, StringRef reason) {
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();
Expand Down Expand Up @@ -2153,7 +2160,8 @@ static macho::Symbol *createBitcodeSymbol(const lto::InputFile::Symbol &objSym,
case GlobalValue::DefaultVisibility:
break;
}
isPrivateExtern = isPrivateExtern || objSym.canBeOmittedFromSymbolTable();
isPrivateExtern = isPrivateExtern || objSym.canBeOmittedFromSymbolTable() ||
file.forceHidden;

if (objSym.isCommon())
return symtab->addCommon(name, &file, objSym.getCommonSize(),
Expand All @@ -2168,8 +2176,8 @@ static macho::Symbol *createBitcodeSymbol(const lto::InputFile::Symbol &objSym,
}

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
Expand Down
12 changes: 9 additions & 3 deletions lld/MachO/InputFiles.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ struct FDE {
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();

Expand All @@ -171,6 +171,7 @@ class ObjFile final : public InputFile {
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;
Expand Down Expand Up @@ -259,7 +260,8 @@ class DylibFile final : public InputFile {
// .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
Expand All @@ -273,16 +275,20 @@ class ArchiveFile final : public InputFile {
// 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();
Expand Down
4 changes: 4 additions & 0 deletions lld/MachO/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,10 @@ def force_load : Separate<["-"], "force_load">,
def force_load_swift_libs : Flag<["-"], "force_load_swift_libs">,
HelpText<"Apply -force_load to libraries listed in LC_LINKER_OPTIONS whose names start with 'swift'">,
Group<grp_libs>;
def load_hidden : Separate<["-"], "load_hidden">,
MetaVarName<"<path>">,
HelpText<"Load all symbols from static library with hidden visibility">,
Group<grp_libs>;

def grp_content : OptionGroup<"content">, HelpText<"ADDITIONAL CONTENT">;

Expand Down
28 changes: 28 additions & 0 deletions lld/test/MachO/load-hidden.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
; REQUIRES: x86
; RUN: rm -rf %t; split-file %s %t
; RUN: llvm-as %t/archive.ll -o %t/archive.o
; RUN: llvm-ar rcs %t/archive.a %t/archive.o
; RUN: llvm-as %t/obj.ll -o %t/obj.o

; RUN: %lld -dylib -lSystem %t/obj.o -load_hidden %t/archive.a -o %t/test.dylib
; RUN: llvm-nm %t/test.dylib | FileCheck %s
; CHECK: t _foo

;--- archive.ll
target triple = "x86_64-apple-macosx10.15.0"
target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"

define void @foo() noinline optnone {
ret void
}

;--- obj.ll
target triple = "x86_64-apple-macosx10.15.0"
target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"

declare void @foo();

define void @main() {
call void @foo()
ret void
}
72 changes: 72 additions & 0 deletions lld/test/MachO/load-hidden.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# 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
4 changes: 4 additions & 0 deletions lld/test/MachO/reroot-path.s
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@
# RUN: tar xf %t/repro4.tar -C %t
# RUN: cd %t/repro4; %no-arg-lld @response.txt | FileCheck %s -DDIR="%:t/%:t"

# RUN: %lld --reproduce %t/repro7.tar -lSystem -syslibroot %t -force_load %t/foo.a -force_load %t/bar.a %t/test.o -o /dev/null -t | FileCheck %s -DDIR="%t/%:t"
# RUN: tar xf %t/repro7.tar -C %t
# RUN: cd %t/repro7; %no-arg-lld @response.txt | FileCheck %s -DDIR="%:t/%:t"

# RUN: echo "%t/libfoo.dylib" > %t/filelist
# RUN: echo "%t/libbar.dylib" >> %t/filelist
# RUN: %lld --reproduce %t/repro5.tar -lSystem -syslibroot %t -filelist %t/filelist %t/test.o -o /dev/null -t | FileCheck %s -DDIR="%t/%:t"
Expand Down

0 comments on commit 595fc59

Please sign in to comment.