Index: lld/MachO/Config.h =================================================================== --- lld/MachO/Config.h +++ lld/MachO/Config.h @@ -205,11 +205,38 @@ } }; -// Whether to force-load an archive. -enum class ForceLoad { - Default, // Apply -all_load or -ObjC behaviors if those flags are enabled - Yes, // Always load the archive, regardless of other flags - No, // Never load the archive, regardless of other flags +// If there is a cached archive file: +// --------------------------------------------------------------------------- +// | cache\now | AllLoad | ForceLoad | ObjCLoad | CLILazy | LinkerOptLazy | +// --------------------------------------------------------------------------- +// | AllLoad | Skip | Skip | Skip | Skip | Skip | +// --------------------------------------------------------------------------- +// | ForceLoad | Skip | Skip | Skip | Skip | Skip | +// --------------------------------------------------------------------------- +// | ObjCLoad | \ | Skip | Skip | Skip | Skip | +// --------------------------------------------------------------------------- +// | CLILazy | \ | Skip | \ | Skip | Skip | +// --------------------------------------------------------------------------- +// | LinkerOptLazy| Force | Force | ObjC | Skip | Skip | +// --------------------------------------------------------------------------- +// \ means we should not compare these cases because -all_load & -ObjC +// apply to all inputs in CLI + +enum class LoadFrom { + AllLoad, // Passed from CLI with -all_load enabled + ForceLoad, // Passed from CLI with -force_load enabled + ObjCLoad, // Passed from CLI with -ObjC enabled + CLILazy, // Passed from CLI with no flags enabled + LinkerOptLazy, // Passed from LC_LINKER_OPTION, lazily loaded regardless of + // any flags above +}; + +// What symbols we actually loaded from archives +enum class LoadScope { + Skip, // Archive already loaded and we can skip this + Lazy, // Loaded symbol table lazily + ObjC, // Loaded all ObjC symbols + All, // Loaded all symbols }; extern std::unique_ptr config; Index: lld/MachO/Driver.cpp =================================================================== --- lld/MachO/Driver.cpp +++ lld/MachO/Driver.cpp @@ -248,8 +248,9 @@ } static DenseMap loadedArchives; +static DenseSet commandLineInputs; -static InputFile *addFile(StringRef path, ForceLoad forceLoadArchive, +static InputFile *addFile(StringRef path, LoadFrom loadFrom, bool isLazy = false, bool isExplicit = true, bool isBundleLoader = false) { Optional buffer = readFile(path); @@ -268,23 +269,59 @@ // loadArchiveMember() call below may recursively call addFile() and // invalidate this reference. auto entry = loadedArchives.find(path); - if (entry != loadedArchives.end()) - return entry->second; - std::unique_ptr archive = CHECK( - object::Archive::create(mbref), path + ": failed to parse archive"); + // -all_load & -ObjC have an effect on command line arguments + if (loadFrom == LoadFrom::CLILazy && config->allLoad) { + loadFrom = LoadFrom::AllLoad; + } else if (loadFrom == LoadFrom::CLILazy && config->forceLoadObjC) { + loadFrom = LoadFrom::ObjCLoad; + } + + ArchiveFile *file; + LoadScope loadScope = LoadScope::Skip; + + if (entry == loadedArchives.end()) { + // No cached archive, we need to create a new one + std::unique_ptr archive = CHECK( + object::Archive::create(mbref), path + ": failed to parse archive"); + + if (!archive->isEmpty() && !archive->hasSymbolTable()) + error(path + ": archive has no index; run ranlib to add one"); + file = make(std::move(archive)); + file->loadFrom = loadFrom; + if (loadFrom == LoadFrom::AllLoad || loadFrom == LoadFrom::ForceLoad) + loadScope = LoadScope::All; + else if (loadFrom == LoadFrom::ObjCLoad) + loadScope = LoadScope::ObjC; + else + loadScope = LoadScope::Lazy; + } else { + // Decide if we need to fetch more members based on + file = entry->second; + // See comments in Config.h for more explanation of the following code + if (file->loadFrom == LoadFrom::LinkerOptLazy) { + if (loadFrom == LoadFrom::AllLoad) { + file->loadFrom = LoadFrom::AllLoad; + loadScope = LoadScope::All; + } else if (loadFrom == LoadFrom::ForceLoad) { + file->loadFrom = LoadFrom::ForceLoad; + loadScope = LoadScope::All; + } else if (loadFrom == LoadFrom::ObjCLoad) { + file->loadFrom = LoadFrom::ObjCLoad; + loadScope = LoadScope::ObjC; + } + } + } - if (!archive->isEmpty() && !archive->hasSymbolTable()) - error(path + ": archive has no index; run ranlib to add one"); + if (loadScope == LoadScope::Skip) + return file; - auto *file = make(std::move(archive)); - if ((forceLoadArchive == ForceLoad::Default && config->allLoad) || - forceLoadArchive == ForceLoad::Yes) { + if (loadScope == LoadScope::All) { if (Optional buffer = readFile(path)) { Error e = Error::success(); for (const object::Archive::Child &c : file->getArchive().children(e)) { StringRef reason = - forceLoadArchive == ForceLoad::Yes ? "-force_load" : "-all_load"; + loadFrom == LoadFrom::ForceLoad ? "-force_load" : "-all_load"; if (Error e = file->fetch(c, reason)) error(toString(file) + ": " + reason + " failed to load archive member: " + toString(std::move(e))); @@ -293,8 +330,7 @@ error(toString(file) + ": Archive::children failed: " + toString(std::move(e))); } - } else if (forceLoadArchive == ForceLoad::Default && - config->forceLoadObjC) { + } else if (loadScope == LoadScope::ObjC) { for (const object::Archive::Symbol &sym : file->getArchive().symbols()) if (sym.getName().startswith(objc::klass)) file->fetch(sym); @@ -368,11 +404,10 @@ } static void addLibrary(StringRef name, bool isNeeded, bool isWeak, - bool isReexport, bool isExplicit, - ForceLoad forceLoadArchive) { + bool isReexport, bool isExplicit, LoadFrom loadFrom) { if (Optional path = findLibrary(name)) { if (auto *dylibFile = dyn_cast_or_null( - addFile(*path, forceLoadArchive, /*isLazy=*/false, isExplicit))) { + addFile(*path, loadFrom, /*isLazy=*/false, isExplicit))) { if (isNeeded) dylibFile->forceNeeded = true; if (isWeak) @@ -389,14 +424,12 @@ static DenseSet loadedObjectFrameworks; static void addFramework(StringRef name, bool isNeeded, bool isWeak, - bool isReexport, bool isExplicit, - ForceLoad forceLoadArchive) { + bool isReexport, bool isExplicit, LoadFrom loadFrom) { if (Optional path = findFramework(name)) { if (loadedObjectFrameworks.contains(*path)) return; - InputFile *file = - addFile(*path, forceLoadArchive, /*isLazy=*/false, isExplicit); + InputFile *file = addFile(*path, loadFrom, /*isLazy=*/false, isExplicit); if (auto *dylibFile = dyn_cast_or_null(file)) { if (isNeeded) dylibFile->forceNeeded = true; @@ -436,15 +469,16 @@ unsigned i = 0; StringRef arg = argv[i]; if (arg.consume_front("-l")) { - ForceLoad forceLoadArchive = - config->forceLoadSwift && arg.startswith("swift") ? ForceLoad::Yes - : ForceLoad::No; + LoadFrom loadFrom = config->forceLoadSwift && arg.startswith("swift") + ? LoadFrom::ForceLoad + : LoadFrom::LinkerOptLazy; addLibrary(arg, /*isNeeded=*/false, /*isWeak=*/false, - /*isReexport=*/false, /*isExplicit=*/false, forceLoadArchive); + /*isReexport=*/false, /*isExplicit=*/false, loadFrom); } else if (arg == "-framework") { StringRef name = argv[++i]; addFramework(name, /*isNeeded=*/false, /*isWeak=*/false, - /*isReexport=*/false, /*isExplicit=*/false, ForceLoad::No); + /*isReexport=*/false, /*isExplicit=*/false, + LoadFrom::LinkerOptLazy); } else { error(arg + " is not allowed in LC_LINKER_OPTION"); } @@ -456,7 +490,7 @@ return; MemoryBufferRef mbref = *buffer; for (StringRef path : args::getLines(mbref)) - addFile(rerootPath(path), ForceLoad::Default, isLazy); + addFile(rerootPath(path), LoadFrom::CLILazy, isLazy); } // We expect sub-library names of the form "libfoo", which will match a dylib @@ -976,30 +1010,33 @@ switch (opt.getID()) { case OPT_INPUT: - addFile(rerootPath(arg->getValue()), ForceLoad::Default, isLazy); + commandLineInputs.insert(rerootPath(arg->getValue())); + addFile(rerootPath(arg->getValue()), LoadFrom::CLILazy, isLazy); break; case OPT_needed_library: if (auto *dylibFile = dyn_cast_or_null( - addFile(rerootPath(arg->getValue()), ForceLoad::Default))) + addFile(rerootPath(arg->getValue()), LoadFrom::CLILazy))) dylibFile->forceNeeded = true; break; case OPT_reexport_library: if (auto *dylibFile = dyn_cast_or_null( - addFile(rerootPath(arg->getValue()), ForceLoad::Default))) { + addFile(rerootPath(arg->getValue()), LoadFrom::CLILazy))) { config->hasReexports = true; dylibFile->reexport = true; } break; case OPT_weak_library: if (auto *dylibFile = dyn_cast_or_null( - addFile(rerootPath(arg->getValue()), ForceLoad::Default))) + addFile(rerootPath(arg->getValue()), LoadFrom::CLILazy))) dylibFile->forceWeakImport = true; break; case OPT_filelist: addFileList(arg->getValue(), isLazy); break; case OPT_force_load: - addFile(rerootPath(arg->getValue()), ForceLoad::Yes); + // Do not force-load archives that are already loaded from command line + if (!commandLineInputs.contains(rerootPath(arg->getValue()))) + addFile(rerootPath(arg->getValue()), LoadFrom::ForceLoad); break; case OPT_l: case OPT_needed_l: @@ -1007,7 +1044,7 @@ case OPT_weak_l: addLibrary(arg->getValue(), opt.getID() == OPT_needed_l, opt.getID() == OPT_weak_l, opt.getID() == OPT_reexport_l, - /*isExplicit=*/true, ForceLoad::Default); + /*isExplicit=*/true, LoadFrom::CLILazy); break; case OPT_framework: case OPT_needed_framework: @@ -1016,7 +1053,7 @@ addFramework(arg->getValue(), opt.getID() == OPT_needed_framework, opt.getID() == OPT_weak_framework, opt.getID() == OPT_reexport_framework, /*isExplicit=*/true, - ForceLoad::Default); + LoadFrom::CLILazy); break; case OPT_start_lib: if (isLazy) @@ -1264,7 +1301,7 @@ 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, /*isLazy=*/false, + addFile(arg->getValue(), LoadFrom::CLILazy, /*isLazy=*/false, /*isExplicit=*/false, /*isBundleLoader=*/true); } Index: lld/MachO/InputFiles.h =================================================================== --- lld/MachO/InputFiles.h +++ lld/MachO/InputFiles.h @@ -9,6 +9,7 @@ #ifndef LLD_MACHO_INPUT_FILES_H #define LLD_MACHO_INPUT_FILES_H +#include "Config.h" #include "MachOStructs.h" #include "Target.h" @@ -265,6 +266,7 @@ Error fetch(const llvm::object::Archive::Child &, StringRef reason); const llvm::object::Archive &getArchive() const { return *file; }; static bool classof(const InputFile *f) { return f->kind() == ArchiveKind; } + LoadFrom loadFrom; private: std::unique_ptr file; Index: lld/test/MachO/lc-linker-option.ll =================================================================== --- lld/test/MachO/lc-linker-option.ll +++ lld/test/MachO/lc-linker-option.ll @@ -77,6 +77,39 @@ ; SYMS-NEXT: g F __TEXT,__text __mh_execute_header ; SYMS-EMPTY: +;; Make sure -all_load has effect when libraries are loaded via LC_LINKER_OPTION flags and explicitly passed as well +; RUN: %lld -all_load %t/load-framework-foo.o %t/load-library-foo.o %t/main.o -o %t/main -F%t -L%t -lfoo +; RUN: llvm-objdump --macho --syms %t/main | FileCheck %s --check-prefix=SYMS_ALL_LOAD + +;; Note that _OBJC_CLASS_$_TestClass is *included* here. +; SYMS_ALL_LOAD: SYMBOL TABLE: +; SYMS_ALL_LOAD-NEXT: g F __TEXT,__text _main +; SYMS_ALL_LOAD-NEXT: g O __DATA,__objc_data _OBJC_CLASS_$_TestClass +; SYMS_ALL_LOAD-NEXT: g F __TEXT,__text __mh_execute_header +; SYMS_ALL_LOAD-EMPTY: + +;; Make sure -force_load has effect when libraries are loaded via LC_LINKER_OPTION flags and explicitly passed as well +; RUN: %lld %t/load-library-foo.o %t/main.o -o %t/main -F%t -L%t -force_load %t/libfoo.a +; RUN: llvm-objdump --macho --syms %t/main | FileCheck %s --check-prefix=SYMS_FORCE_LOAD + +;; Note that _OBJC_CLASS_$_TestClass is *included* here. +; SYMS_FORCE_LOAD: SYMBOL TABLE: +; SYMS_FORCE_LOAD-NEXT: g F __TEXT,__text _main +; SYMS_FORCE_LOAD-NEXT: g O __DATA,__objc_data _OBJC_CLASS_$_TestClass +; SYMS_FORCE_LOAD-NEXT: g F __TEXT,__text __mh_execute_header +; SYMS_FORCE_LOAD-EMPTY: + +;; Make sure -ObjC has effect when frameworks are loaded via LC_LINKER_OPTION flags and explicitly passed as well +; RUN: %lld -ObjC %t/load-framework-foo.o %t/load-library-foo.o %t/main.o -o %t/main -F%t -L%t -framework Foo +; RUN: llvm-objdump --macho --syms %t/main | FileCheck %s --check-prefix=SYMS_OBJC_LOAD + +;; Note that _OBJC_CLASS_$_TestClass is *included* here. +; SYMS_OBJC_LOAD: SYMBOL TABLE: +; SYMS_OBJC_LOAD-NEXT: g F __TEXT,__text _main +; SYMS_OBJC_LOAD-NEXT: g O __DATA,__objc_data _OBJC_CLASS_$_TestClass +; SYMS_OBJC_LOAD-NEXT: g F __TEXT,__text __mh_execute_header +; SYMS_OBJC_LOAD-EMPTY: + ;; Make sure that frameworks containing object files or bitcode instead of ;; dylibs or archives do not cause duplicate symbol errors ; RUN: mkdir -p %t/Foo.framework