[lld] 595fc59 - Reland "[lld-macho] Implement -load_hidden"

Daniel Bertalan via llvm-commits llvm-commits at lists.llvm.org
Mon Jul 25 13:52:21 PDT 2022


Author: Daniel Bertalan
Date: 2022-07-25T22:51:24+02:00
New Revision: 595fc59f742aeee398eb35ff69182a413a882317

URL: https://github.com/llvm/llvm-project/commit/595fc59f742aeee398eb35ff69182a413a882317
DIFF: https://github.com/llvm/llvm-project/commit/595fc59f742aeee398eb35ff69182a413a882317.diff

LOG: Reland "[lld-macho] Implement -load_hidden"

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

Added: 
    lld/test/MachO/load-hidden.ll
    lld/test/MachO/load-hidden.s

Modified: 
    lld/MachO/Driver.cpp
    lld/MachO/DriverUtils.cpp
    lld/MachO/InputFiles.cpp
    lld/MachO/InputFiles.h
    lld/MachO/Options.td
    lld/test/MachO/reroot-path.s

Removed: 
    


################################################################################
diff  --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp
index 454708fad4ef2..c6445438dfeb3 100644
--- a/lld/MachO/Driver.cpp
+++ b/lld/MachO/Driver.cpp
@@ -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;
@@ -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
@@ -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:

diff  --git a/lld/MachO/DriverUtils.cpp b/lld/MachO/DriverUtils.cpp
index b52d5e851c62d..d8e474d15cfd3 100644
--- a/lld/MachO/DriverUtils.cpp
+++ b/lld/MachO/DriverUtils.cpp
@@ -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;

diff  --git a/lld/MachO/InputFiles.cpp b/lld/MachO/InputFiles.cpp
index e3bf553e53347..b463d78175944 100644
--- a/lld/MachO/InputFiles.cpp
+++ b/lld/MachO/InputFiles.cpp
@@ -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
@@ -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 
diff erent from ld64:
     // ld64 picks the winning symbol based on several criteria (see
     // pickBetweenRegularAtoms() in ld64's SymbolTable.cpp), while lld
@@ -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);
   }
@@ -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));
@@ -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;
     }
@@ -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);
@@ -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.
@@ -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)
@@ -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() +
@@ -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();
@@ -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(),
@@ -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

diff  --git a/lld/MachO/InputFiles.h b/lld/MachO/InputFiles.h
index 5deb05272a6b1..ea6802814e4c4 100644
--- a/lld/MachO/InputFiles.h
+++ b/lld/MachO/InputFiles.h
@@ -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();
 
@@ -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;
@@ -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
@@ -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();

diff  --git a/lld/MachO/Options.td b/lld/MachO/Options.td
index b3d74a83f5822..38b79f820f805 100644
--- a/lld/MachO/Options.td
+++ b/lld/MachO/Options.td
@@ -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">;
 

diff  --git a/lld/test/MachO/load-hidden.ll b/lld/test/MachO/load-hidden.ll
new file mode 100644
index 0000000000000..110b96ae5cf5a
--- /dev/null
+++ b/lld/test/MachO/load-hidden.ll
@@ -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
+}

diff  --git a/lld/test/MachO/load-hidden.s b/lld/test/MachO/load-hidden.s
new file mode 100644
index 0000000000000..003409c278fa4
--- /dev/null
+++ b/lld/test/MachO/load-hidden.s
@@ -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

diff  --git a/lld/test/MachO/reroot-path.s b/lld/test/MachO/reroot-path.s
index b99d99176f991..43ff21d1b915f 100644
--- a/lld/test/MachO/reroot-path.s
+++ b/lld/test/MachO/reroot-path.s
@@ -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"


        


More information about the llvm-commits mailing list