diff --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp --- a/lld/MachO/Driver.cpp +++ b/lld/MachO/Driver.cpp @@ -249,7 +249,8 @@ static DenseMap loadedArchives; static InputFile *addFile(StringRef path, ForceLoad forceLoadArchive, - bool isExplicit = true, bool isBundleLoader = false) { + bool isLazy = false, bool isExplicit = true, + bool isBundleLoader = false) { Optional buffer = readFile(path); if (!buffer) return nullptr; @@ -319,7 +320,7 @@ break; } case file_magic::macho_object: - newFile = make(mbref, getModTime(path), ""); + newFile = make(mbref, getModTime(path), "", isLazy); break; case file_magic::macho_dynamically_linked_shared_lib: case file_magic::macho_dynamically_linked_shared_lib_stub: @@ -331,7 +332,7 @@ } break; case file_magic::bitcode: - newFile = make(mbref, "", 0); + newFile = make(mbref, "", 0, isLazy); break; case file_magic::macho_executable: case file_magic::macho_bundle: @@ -346,9 +347,20 @@ error(path + ": unhandled file type"); } if (newFile && !isa(newFile)) { + if ((magic == file_magic::macho_object || magic == file_magic::bitcode) && + newFile->lazy && config->forceLoadObjC) { + for (Symbol *sym : newFile->symbols) + if (sym && sym->getName().startswith(objc::klass)) { + extract(*newFile, "-ObjC"); + break; + } + if (newFile->lazy && hasObjCSection(mbref)) + extract(*newFile, "-ObjC"); + } + // printArchiveMemberLoad() prints both .a and .o names, so no need to - // print the .a name here. - if (config->printEachFile && magic != file_magic::archive) + // print the .a name here. Similarly skip lazy files. + if (config->printEachFile && magic != file_magic::archive && !isLazy) message(toString(newFile)); inputFiles.insert(newFile); } @@ -360,7 +372,7 @@ ForceLoad forceLoadArchive) { if (Optional path = findLibrary(name)) { if (auto *dylibFile = dyn_cast_or_null( - addFile(*path, forceLoadArchive, isExplicit))) { + addFile(*path, forceLoadArchive, false, isExplicit))) { if (isNeeded) dylibFile->forceNeeded = true; if (isWeak) @@ -380,7 +392,7 @@ ForceLoad forceLoadArchive) { if (Optional path = findFramework(name)) { if (auto *dylibFile = dyn_cast_or_null( - addFile(*path, forceLoadArchive, isExplicit))) { + addFile(*path, forceLoadArchive, false, isExplicit))) { if (isNeeded) dylibFile->forceNeeded = true; if (isWeak) @@ -425,13 +437,13 @@ } } -static void addFileList(StringRef path) { +static void addFileList(StringRef path, bool lazy) { Optional buffer = readFile(path); if (!buffer) return; MemoryBufferRef mbref = *buffer; for (StringRef path : args::getLines(mbref)) - addFile(rerootPath(path), ForceLoad::Default); + addFile(rerootPath(path), ForceLoad::Default, lazy); } // An order file has one entry per line, in the following format: @@ -545,7 +557,8 @@ auto *lto = make(); for (InputFile *file : inputFiles) if (auto *bitcodeFile = dyn_cast(file)) - lto->add(*bitcodeFile); + if (!file->lazy) + lto->add(*bitcodeFile); for (ObjFile *file : lto->compile()) inputFiles.insert(file); @@ -962,6 +975,7 @@ TimeTraceScope timeScope("Load input files"); // This loop should be reserved for options whose exact ordering matters. // Other options should be handled via filtered() and/or getLastArg(). + bool isLazy = false; for (const Arg *arg : args) { const Option &opt = arg->getOption(); warnIfDeprecatedOption(opt); @@ -969,7 +983,7 @@ switch (opt.getID()) { case OPT_INPUT: - addFile(rerootPath(arg->getValue()), ForceLoad::Default); + addFile(rerootPath(arg->getValue()), ForceLoad::Default, isLazy); break; case OPT_needed_library: if (auto *dylibFile = dyn_cast_or_null( @@ -989,7 +1003,7 @@ dylibFile->forceWeakImport = true; break; case OPT_filelist: - addFileList(arg->getValue()); + addFileList(arg->getValue(), isLazy); break; case OPT_force_load: addFile(rerootPath(arg->getValue()), ForceLoad::Yes); @@ -1011,6 +1025,16 @@ opt.getID() == OPT_reexport_framework, /*isExplicit=*/true, ForceLoad::Default); break; + case OPT_start_lib: + if (isLazy) + error("nested --start-lib"); + isLazy = true; + break; + case OPT_end_lib: + if (!isLazy) + error("stray --end-lib"); + isLazy = false; + break; default: break; } @@ -1247,7 +1271,8 @@ if (const Arg *arg = args.getLastArg(OPT_bundle_loader)) { if (config->outputType != MH_BUNDLE) error("-bundle_loader can only be used with MachO bundle output"); - addFile(arg->getValue(), ForceLoad::Default, /*isExplicit=*/false, + addFile(arg->getValue(), ForceLoad::Default, /*lazy=*/false, + /*isExplicit=*/false, /*isBundleLoader=*/true); } if (const Arg *arg = args.getLastArg(OPT_umbrella)) { diff --git a/lld/MachO/InputFiles.h b/lld/MachO/InputFiles.h --- a/lld/MachO/InputFiles.h +++ b/lld/MachO/InputFiles.h @@ -94,16 +94,21 @@ std::vector symbols; std::vector
sections; - // Provides an easy way to sort InputFiles deterministically. - const int id; // If not empty, this stores the name of the archive containing this file. // We use this string for creating error messages. std::string archiveName; + // Provides an easy way to sort InputFiles deterministically. + const int id; + + // True if this is a lazy ObjFile or BitcodeFile. + bool lazy = false; + protected: - InputFile(Kind kind, MemoryBufferRef mb) - : mb(mb), id(idCount++), fileKind(kind), name(mb.getBufferIdentifier()) {} + InputFile(Kind kind, MemoryBufferRef mb, bool lazy = false) + : mb(mb), id(idCount++), lazy(lazy), fileKind(kind), + name(mb.getBufferIdentifier()) {} InputFile(Kind, const llvm::MachO::InterfaceFile &); @@ -117,8 +122,10 @@ // .o file class ObjFile final : public InputFile { public: - ObjFile(MemoryBufferRef mb, uint32_t modTime, StringRef archiveName); + ObjFile(MemoryBufferRef mb, uint32_t modTime, StringRef archiveName, + bool lazy = false); ArrayRef getDataInCode() const; + template void parse(); static bool classof(const InputFile *f) { return f->kind() == ObjKind; } @@ -130,7 +137,7 @@ private: Section *compactUnwindSection = nullptr; - template void parse(); + template void parseLazy(); template void parseSections(ArrayRef); template void parseSymbols(ArrayRef sectionHeaders, @@ -229,8 +236,10 @@ class BitcodeFile final : public InputFile { public: explicit BitcodeFile(MemoryBufferRef mb, StringRef archiveName, - uint64_t offsetInArchive); + uint64_t offsetInArchive, bool lazy = false); static bool classof(const InputFile *f) { return f->kind() == BitcodeKind; } + void parse(); + void parseLazy(); std::unique_ptr obj; }; @@ -240,6 +249,8 @@ llvm::Optional readFile(StringRef path); +void extract(InputFile &file, StringRef reason); + namespace detail { template diff --git a/lld/MachO/InputFiles.cpp b/lld/MachO/InputFiles.cpp --- a/lld/MachO/InputFiles.cpp +++ b/lld/MachO/InputFiles.cpp @@ -836,13 +836,21 @@ sections.back().subsections.push_back({0, isec}); } -ObjFile::ObjFile(MemoryBufferRef mb, uint32_t modTime, StringRef archiveName) - : InputFile(ObjKind, mb), modTime(modTime) { +ObjFile::ObjFile(MemoryBufferRef mb, uint32_t modTime, StringRef archiveName, + bool lazy) + : InputFile(ObjKind, mb, lazy), modTime(modTime) { this->archiveName = std::string(archiveName); - if (target->wordSize == 8) - parse(); - else - parse(); + if (lazy) { + if (target->wordSize == 8) + parseLazy(); + else + parseLazy(); + } else { + if (target->wordSize == 8) + parse(); + else + parse(); + } } template void ObjFile::parse() { @@ -904,6 +912,32 @@ registerCompactUnwind(); } +template void ObjFile::parseLazy() { + using Header = typename LP::mach_header; + using NList = typename LP::nlist; + + auto *buf = reinterpret_cast(mb.getBufferStart()); + auto *hdr = reinterpret_cast(mb.getBufferStart()); + const load_command *cmd = findCommand(hdr, LC_SYMTAB); + if (!cmd) + return; + auto *c = reinterpret_cast(cmd); + ArrayRef nList(reinterpret_cast(buf + c->symoff), + c->nsyms); + const char *strtab = reinterpret_cast(buf) + c->stroff; + symbols.resize(nList.size()); + for (auto it : llvm::enumerate(nList)) { + const NList &sym = it.value(); + if ((sym.n_type & N_EXT) && !isUndef(sym)) { + // TODO: Bound checking + StringRef name = strtab + sym.n_strx; + symbols[it.index()] = symtab->addLazyObject(name, *this); + if (!lazy) + break; + } + } +} + void ObjFile::parseDebugInfo() { std::unique_ptr dObj = DwarfObject::create(this); if (!dObj) @@ -1548,8 +1582,8 @@ } BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName, - uint64_t offsetInArchive) - : InputFile(BitcodeKind, mb) { + uint64_t offsetInArchive, bool lazy) + : InputFile(BitcodeKind, mb, lazy) { this->archiveName = std::string(archiveName); std::string path = mb.getBufferIdentifier().str(); // ThinLTO assumes that all MemoryBufferRefs given to it have a unique @@ -1565,12 +1599,47 @@ utostr(offsetInArchive))); obj = check(lto::InputFile::create(mbref)); + if (lazy) + parseLazy(); + else + parse(); +} +void BitcodeFile::parse() { // Convert LTO Symbols to LLD Symbols in order to perform resolution. The // "winning" symbol will then be marked as Prevailing at LTO compilation // time. + symbols.clear(); for (const lto::InputFile::Symbol &objSym : obj->symbols()) symbols.push_back(createBitcodeSymbol(objSym, *this)); } +void BitcodeFile::parseLazy() { + symbols.resize(obj->symbols().size()); + for (auto it : llvm::enumerate(obj->symbols())) { + const lto::InputFile::Symbol &objSym = it.value(); + if (!objSym.isUndefined()) { + symbols[it.index()] = + symtab->addLazyObject(saver.save(objSym.getName()), *this); + if (!lazy) + break; + } + } +} + +void macho::extract(InputFile &file, StringRef reason) { + assert(file.lazy); + file.lazy = false; + printArchiveMemberLoad(reason, &file); + if (auto *bitcode = dyn_cast(&file)) { + bitcode->parse(); + } else { + auto &f = cast(file); + if (target->wordSize == 8) + f.parse(); + else + f.parse(); + } +} + template void ObjFile::parse(); diff --git a/lld/MachO/Options.td b/lld/MachO/Options.td --- a/lld/MachO/Options.td +++ b/lld/MachO/Options.td @@ -73,6 +73,10 @@ Group; def O : JoinedOrSeparate<["-"], "O">, HelpText<"Optimize output file size">; +def start_lib: Flag<["--"], "start-lib">, + HelpText<"Start a grouping of objects that should be treated as if they were together in an archive">; +def end_lib: Flag<["--"], "end-lib">, + HelpText<"End a grouping of objects that should be treated as if they were together in an archive">; def no_warn_dylib_install_name: Joined<["--"], "no-warn-dylib-install-name">, HelpText<"Do not warn on -install-name if -dylib is not passed (default)">, Group; diff --git a/lld/MachO/SymbolTable.h b/lld/MachO/SymbolTable.h --- a/lld/MachO/SymbolTable.h +++ b/lld/MachO/SymbolTable.h @@ -53,6 +53,7 @@ Symbol *addLazyArchive(StringRef name, ArchiveFile *file, const llvm::object::Archive::Symbol &sym); + Symbol *addLazyObject(StringRef name, InputFile &file); Defined *addSynthetic(StringRef name, InputSection *, uint64_t value, bool isPrivateExtern, bool includeInSymtab, diff --git a/lld/MachO/SymbolTable.cpp b/lld/MachO/SymbolTable.cpp --- a/lld/MachO/SymbolTable.cpp +++ b/lld/MachO/SymbolTable.cpp @@ -115,6 +115,8 @@ replaceSymbol(s, name, file, refState); else if (auto *lazy = dyn_cast(s)) lazy->fetchArchiveMember(); + else if (isa(s)) + extract(*s->getFile(), s->getName()); else if (auto *dynsym = dyn_cast(s)) dynsym->reference(refState); else if (auto *undefined = dyn_cast(s)) @@ -199,6 +201,26 @@ return s; } +Symbol *SymbolTable::addLazyObject(StringRef name, InputFile &file) { + Symbol *s; + bool wasInserted; + std::tie(s, wasInserted) = insert(name, &file); + + if (wasInserted) { + replaceSymbol(s, file, name); + } else if (isa(s)) { + extract(file, name); + } else if (auto *dysym = dyn_cast(s)) { + if (dysym->isWeakDef()) { + if (dysym->getRefState() != RefState::Unreferenced) + extract(file, name); + else + replaceSymbol(s, file, name); + } + } + return s; +} + Defined *SymbolTable::addSynthetic(StringRef name, InputSection *isec, uint64_t value, bool isPrivateExtern, bool includeInSymtab, diff --git a/lld/MachO/Symbols.h b/lld/MachO/Symbols.h --- a/lld/MachO/Symbols.h +++ b/lld/MachO/Symbols.h @@ -38,6 +38,7 @@ CommonKind, DylibKind, LazyArchiveKind, + LazyObjectKind, }; virtual ~Symbol() {} @@ -51,6 +52,9 @@ } bool isLive() const { return used; } + bool isLazy() const { + return symbolKind == LazyArchiveKind || symbolKind == LazyObjectKind; + } virtual uint64_t getVA() const { return 0; } @@ -294,12 +298,25 @@ const llvm::object::Archive::Symbol sym; }; +// A defined symbol in an ObjFile/BitcodeFile surrounded by --start-lib and +// --end-lib. +class LazyObject : public Symbol { +public: + LazyObject(InputFile &file, StringRef name) + : Symbol(LazyObjectKind, name, &file) { + isUsedInRegularObj = false; + } + + static bool classof(const Symbol *s) { return s->kind() == LazyObjectKind; } +}; + union SymbolUnion { alignas(Defined) char a[sizeof(Defined)]; alignas(Undefined) char b[sizeof(Undefined)]; alignas(CommonSymbol) char c[sizeof(CommonSymbol)]; alignas(DylibSymbol) char d[sizeof(DylibSymbol)]; alignas(LazyArchive) char e[sizeof(LazyArchive)]; + alignas(LazyObject) char f[sizeof(LazyObject)]; }; template diff --git a/lld/MachO/UnwindInfoSection.cpp b/lld/MachO/UnwindInfoSection.cpp --- a/lld/MachO/UnwindInfoSection.cpp +++ b/lld/MachO/UnwindInfoSection.cpp @@ -226,7 +226,7 @@ // (See discussions/alternatives already considered on D107533) if (!defined->isExternal()) if (Symbol *sym = symtab->find(defined->getName())) - if (sym->kind() != Symbol::LazyArchiveKind) + if (!sym->isLazy()) r.referent = s = sym; } if (auto *undefined = dyn_cast(s)) { diff --git a/lld/test/MachO/objc-uses-custom-personality.s b/lld/test/MachO/objc-uses-custom-personality.s --- a/lld/test/MachO/objc-uses-custom-personality.s +++ b/lld/test/MachO/objc-uses-custom-personality.s @@ -7,6 +7,8 @@ # RUN: llvm-ar r %t/pack.a %t/defined.o %t/combined.o # RUN: %lld -dylib -arch x86_64 -platform_version ios-simulator 12.0.0 15.0 -ObjC %t/pack.a -o %t/a.dylib # RUN: llvm-objdump --macho --syms %t/a.dylib | FileCheck %s +# RUN: %lld -dylib -arch x86_64 -platform_version ios-simulator 12.0.0 15.0 -ObjC --start-lib %t/defined.o %t/combined.o --end-lib -o %t/a.dylib +# RUN: llvm-objdump --macho --syms %t/a.dylib | FileCheck %s # CHECK: SYMBOL TABLE: # CHECK: {{.*}} l F __TEXT,__text _my_personality diff --git a/lld/test/MachO/objc.s b/lld/test/MachO/objc.s --- a/lld/test/MachO/objc.s +++ b/lld/test/MachO/objc.s @@ -19,6 +19,9 @@ # RUN: %lld -lSystem %t/test.o -o %t/test -L%t -lHasSomeObjC2 -ObjC # RUN: llvm-objdump --section-headers --syms %t/test | FileCheck %s --check-prefix=OBJC +# RUN: %lld -lSystem %t/test.o -o %t/test --start-lib %t/no-objc.o %t/has-objc-symbol.o %t/has-objc-category.o %t/has-swift.o %t/wrong-arch.o --end-lib -ObjC +# RUN: llvm-objdump --section-headers --syms %t/test | FileCheck %s --check-prefix=OBJC + # OBJC: Sections: # OBJC-NEXT: Idx Name Size VMA Type # OBJC-NEXT: 0 __text {{.*}} TEXT @@ -28,7 +31,7 @@ # OBJC-EMPTY: # OBJC-NEXT: SYMBOL TABLE: # OBJC-NEXT: g F __TEXT,__text _main -# OBJC-NEXT: g F __TEXT,__text _OBJC_CLASS_$_MyObject +# OBJC: g F __TEXT,__text _OBJC_CLASS_$_MyObject # RUN: %lld -lSystem %t/test.o -o %t/test -L%t -lHasSomeObjC # RUN: llvm-objdump --section-headers --syms %t/test | FileCheck %s --check-prefix=NO-OBJC diff --git a/lld/test/MachO/start-lib.s b/lld/test/MachO/start-lib.s new file mode 100644 --- /dev/null +++ b/lld/test/MachO/start-lib.s @@ -0,0 +1,129 @@ +# REQUIRES: x86 + +# RUN: rm -rf %t; split-file %s %t && cd %t +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin main.s -o main.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin calls-foo.s -o calls-foo.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin 1.s -o 1.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin 2.s -o 2.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin common.s -o common.o + +# RUN: llvm-as 1.ll -o 1.bc +# RUN: llvm-as 2.ll -o 2.bc + +## Neither 1.o nor 2.o is loaded. +# RUN: %lld main.o --start-lib 1.o 2.o --end-lib -why_load | count 0 +# RUN: %lld main.o --start-lib -filelist filelist --end-lib -why_load | count 0 +# RUN: llvm-readobj -s a.out | FileCheck %s +# CHECK-NOT: Name: _foo +# CHECK-NOT: Name: _bar + +## _bar loads 2.o. The last --end-lib can be omitted. +# RUN: %lld main.o -u _bar --start-lib 1.o 2.o -t -why_load | FileCheck %s --check-prefix=CHECK2WHY +# RUN: %lld main.o -u _bar --start-lib -filelist filelist -t -why_load | FileCheck %s --check-prefix=CHECK2WHY +# RUN: llvm-readobj -s a.out | FileCheck --check-prefix=CHECK2 %s +# CHECK2WHY: main.o +# CHECK2WHY-NEXT: 2.o +# CHECK2WHY-NEXT: _bar forced load of 2.o +# CHECK2WHY-EMPTY: +# CHECK2-NOT: Name: _foo +# CHECK2: Name: _bar +# CHECK2-NOT: Name: _foo + +## _foo loads 1.o. 1.o loads 2.o. +# RUN: %lld main.o -u _foo --start-lib 1.o 2.o -why_load | FileCheck %s --check-prefix=CHECK3WHY +# RUN: llvm-readobj -s a.out | FileCheck --check-prefix=CHECK3 %s +# RUN: %lld main.o -u _foo --start-lib 2.o --end-lib --start-lib 1.o -why_load | FileCheck %s --check-prefix=CHECK3WHY +# RUN: llvm-readobj -s a.out | FileCheck --check-prefix=CHECK3 %s +# CHECK3WHY: _foo forced load of 1.o +# CHECK3WHY-NEXT: _bar forced load of 2.o +# CHECK3WHY-EMPTY: +# CHECK3-DAG: Name: _foo +# CHECK3-DAG: Name: _bar + +## Don't treat undefined _bar in 1.o as a lazy definition. +# RUN: not %lld main.o -u _bar --start-lib 1.o 2>&1 | FileCheck %s --check-prefix=CHECK4 +# CHECK4: error: undefined symbol: _bar + +# RUN: %lld main.o -u _common --start-lib common.o +# RUN: llvm-readobj -s a.out | FileCheck %s --check-prefix=COMMON1 +# COMMON1: Name: _common + +# RUN: %lld main.o --start-lib common.o +# RUN: llvm-readobj -s a.out | FileCheck %s --check-prefix=COMMON2 +# COMMON2-NOT: Name: _common + +## Neither 1.bc nor 2.bc is loaded. +# RUN: %lld main.o --start-lib 1.bc 2.bc -why_load | count 0 +# RUN: llvm-readobj -s a.out | FileCheck %s --check-prefix=BITCODE +# BITCODE-NOT: Name: _foo +# BITCODE-NOT: Name: _bar + +## _bar loads 2.bc. +# RUN: %lld main.o -u _bar --start-lib 1.bc 2.bc -why_load | FileCheck %s --check-prefix=BITCODE2WHY +# RUN: llvm-readobj -s a.out | FileCheck %s --check-prefix=BITCODE2 +# BITCODE2WHY: _bar forced load of 2.bc +# BITCODE2WHY-EMPTY: +# BITCODE2-NOT: Name: _foo +# BITCODE2: Name: _bar +# BITCODE2-NOT: Name: _foo + +## calls-foo.o loads 1.bc. 1.bc loads 2.bc. +# RUN: %lld calls-foo.o --start-lib 1.bc 2.bc -why_load | FileCheck %s --check-prefix=BITCODE3WHY +# RUN: llvm-readobj -s a.out | FileCheck --check-prefix=BITCODE3 %s +# RUN: %lld calls-foo.o --start-lib 2.bc --end-lib --start-lib 1.bc -why_load | FileCheck %s --check-prefix=BITCODE3WHY +# RUN: llvm-readobj -s a.out | FileCheck --check-prefix=BITCODE3 %s +# BITCODE3WHY: _foo forced load of 1.bc +# BITCODE3WHY-NEXT: _bar forced load of 2.bc +# BITCODE3WHY-EMPTY: +# BITCODE3-DAG: Name: _foo + +# RUN: not %lld main.o --start-lib --start-lib 2>&1 | FileCheck -check-prefix=NESTED-LIB %s +# NESTED-LIB: error: nested --start-lib + +# RUN: not %lld --end-lib 2>&1 | FileCheck %s --check-prefix=STRAY +# STRAY: error: stray --end-lib + +#--- main.s +.globl _main +_main: + +#--- calls-foo.s +.globl _main +_main: + call _foo + +#--- 1.s +.globl _foo +_foo: + call _bar + +#--- 2.s +.globl _bar +_bar: + ret + +#--- common.s +.comm _common, 1 + +#--- 1.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() { + tail call void () @bar() + ret void +} + +declare void @bar() + +#--- 2.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 @bar() { + ret void +} + +#--- filelist +1.o +2.o diff --git a/lld/test/MachO/weak-definition-direct-fetch.s b/lld/test/MachO/weak-definition-direct-fetch.s --- a/lld/test/MachO/weak-definition-direct-fetch.s +++ b/lld/test/MachO/weak-definition-direct-fetch.s @@ -65,6 +65,11 @@ # RUN: %lld -lSystem -o %t/weak-ar-weak-dylib -L%t %t/weakfoo.a -lweakfoo %t/refs-foo.o # RUN: llvm-objdump --macho --lazy-bind --syms %t/weak-ar-weak-dylib | FileCheck %s --check-prefix=PREFER-WEAK-OBJECT +# RUN: %lld -lSystem -o %t/weak-dylib-weak-ar -L%t -lweakfoo --start-lib %t/weakfoo.o --end-lib %t/refs-foo.o +# RUN: llvm-objdump --macho --lazy-bind --syms %t/weak-dylib-weak-ar | FileCheck %s --check-prefix=PREFER-WEAK-OBJECT +# RUN: %lld -lSystem -o %t/weak-ar-weak-dylib -L%t --start-lib %t/weakfoo.o --end-lib -lweakfoo %t/refs-foo.o +# RUN: llvm-objdump --macho --lazy-bind --syms %t/weak-ar-weak-dylib | FileCheck %s --check-prefix=PREFER-WEAK-OBJECT + ## (Weak) archive symbols have the same precedence as dylib symbols. # RUN: %lld -lSystem -o %t/weak-ar-nonweak-dylib -L%t %t/weakfoo.a -lfoo %t/refs-foo.o # RUN: llvm-objdump --macho --lazy-bind --syms %t/weak-ar-nonweak-dylib | FileCheck %s --check-prefix=PREFER-WEAK-OBJECT @@ -96,6 +101,11 @@ # RUN: %lld -dylib -lSystem -o %t/weak-unref-dylib-weak-ar -L%t -lweakfoo %t/weakfoo.a # RUN: llvm-objdump --macho --lazy-bind --syms %t/weak-unref-dylib-weak-ar | FileCheck %s --check-prefix=NO-SYM +# RUN: %lld -dylib -lSystem -o %t/weak-ar-weak-unref-dylib -L%t --start-lib %t/weakfoo.o --end-lib -lweakfoo +# RUN: llvm-objdump --macho --lazy-bind --syms %t/weak-ar-weak-unref-dylib | FileCheck %s --check-prefix=NO-SYM +# RUN: %lld -dylib -lSystem -o %t/weak-unref-dylib-weak-ar -L%t -lweakfoo --start-lib %t/weakfoo.o --end-lib +# RUN: llvm-objdump --macho --lazy-bind --syms %t/weak-unref-dylib-weak-ar | FileCheck %s --check-prefix=NO-SYM + ## However, weak references are sufficient to cause the archive to be loaded. # RUN: %lld -dylib -lSystem -o %t/weak-ar-weak-ref-weak-dylib -L%t %t/weakfoo.a -lweakfoo %t/weak-refs-foo.o # RUN: llvm-objdump --macho --lazy-bind --syms %t/weak-ar-weak-ref-weak-dylib | FileCheck %s --check-prefix=PREFER-WEAK-OBJECT @@ -104,6 +114,13 @@ # RUN: %lld -dylib -lSystem -o %t/weak-ref-weak-dylib-weak-ar -L%t -lweakfoo %t/weak-refs-foo.o %t/weakfoo.a # RUN: llvm-objdump --macho --lazy-bind --syms %t/weak-ref-weak-dylib-weak-ar | FileCheck %s --check-prefix=PREFER-WEAK-OBJECT +# RUN: %lld -dylib -lSystem -o %t/weak-ar-weak-ref-weak-dylib -L%t --start-lib %t/weakfoo.o --end-lib -lweakfoo %t/weak-refs-foo.o +# RUN: llvm-objdump --macho --lazy-bind --syms %t/weak-ar-weak-ref-weak-dylib | FileCheck %s --check-prefix=PREFER-WEAK-OBJECT +# RUN: %lld -dylib -lSystem -o %t/weak-ref-weak-dylib-weak-ar -L%t -lweakfoo --start-lib %t/weakfoo.o --end-lib %t/weak-refs-foo.o +# RUN: llvm-objdump --macho --lazy-bind --syms %t/weak-ref-weak-dylib-weak-ar | FileCheck %s --check-prefix=PREFER-WEAK-OBJECT +# RUN: %lld -dylib -lSystem -o %t/weak-ref-weak-dylib-weak-ar -L%t -lweakfoo %t/weak-refs-foo.o --start-lib %t/weakfoo.o --end-lib +# RUN: llvm-objdump --macho --lazy-bind --syms %t/weak-ref-weak-dylib-weak-ar | FileCheck %s --check-prefix=PREFER-WEAK-OBJECT + #--- foo.s .globl _foo .section __TEXT,nonweak