diff --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp index 3a51fde3cb89..b568609a65e1 100644 --- a/lld/MachO/Driver.cpp +++ b/lld/MachO/Driver.cpp @@ -326,11 +326,13 @@ static InputFile *addFile(StringRef path, bool forceLoadArchive, return newFile; } -static void addLibrary(StringRef name, bool isWeak, bool isReexport, - bool isExplicit) { +static void addLibrary(StringRef name, bool isNeeded, bool isWeak, + bool isReexport, bool isExplicit) { if (Optional path = findLibrary(name)) { if (auto *dylibFile = dyn_cast_or_null( addFile(*path, /*forceLoadArchive=*/false, isExplicit))) { + if (isNeeded) + dylibFile->forceNeeded = true; if (isWeak) dylibFile->forceWeakImport = true; if (isReexport) { @@ -343,11 +345,13 @@ static void addLibrary(StringRef name, bool isWeak, bool isReexport, error("library not found for -l" + name); } -static void addFramework(StringRef name, bool isWeak, bool isReexport, - bool isExplicit) { +static void addFramework(StringRef name, bool isNeeded, bool isWeak, + bool isReexport, bool isExplicit) { if (Optional path = findFramework(name)) { if (auto *dylibFile = dyn_cast_or_null( addFile(*path, /*forceLoadArchive=*/false, isExplicit))) { + if (isNeeded) + dylibFile->forceNeeded = true; if (isWeak) dylibFile->forceWeakImport = true; if (isReexport) { @@ -383,12 +387,12 @@ void macho::parseLCLinkerOption(InputFile *f, unsigned argc, StringRef data) { for (const Arg *arg : args) { switch (arg->getOption().getID()) { case OPT_l: - addLibrary(arg->getValue(), /*isWeak=*/false, /*isReexport=*/false, - /*isExplicit=*/false); + addLibrary(arg->getValue(), /*isNeeded=*/false, /*isWeak=*/false, + /*isReexport=*/false, /*isExplicit=*/false); break; case OPT_framework: - addFramework(arg->getValue(), /*isWeak=*/false, /*isReexport=*/false, - /*isExplicit=*/false); + addFramework(arg->getValue(), /*isNeeded=*/false, /*isWeak=*/false, + /*isReexport=*/false, /*isExplicit=*/false); break; default: error(arg->getSpelling() + " is not allowed in LC_LINKER_OPTION"); @@ -880,6 +884,11 @@ void createFiles(const InputArgList &args) { case OPT_INPUT: addFile(rerootPath(arg->getValue()), /*forceLoadArchive=*/false); break; + case OPT_needed_library: + if (auto *dylibFile = dyn_cast_or_null( + addFile(rerootPath(arg->getValue()), false))) + dylibFile->forceNeeded = true; + break; case OPT_reexport_library: if (auto *dylibFile = dyn_cast_or_null(addFile( rerootPath(arg->getValue()), /*forceLoadArchive=*/false))) { @@ -899,15 +908,19 @@ void createFiles(const InputArgList &args) { addFile(rerootPath(arg->getValue()), /*forceLoadArchive=*/true); break; case OPT_l: + case OPT_needed_l: case OPT_reexport_l: case OPT_weak_l: - addLibrary(arg->getValue(), opt.getID() == OPT_weak_l, - opt.getID() == OPT_reexport_l, /*isExplicit=*/true); + addLibrary(arg->getValue(), opt.getID() == OPT_needed_l, + opt.getID() == OPT_weak_l, opt.getID() == OPT_reexport_l, + /*isExplicit=*/true); break; case OPT_framework: + case OPT_needed_framework: case OPT_reexport_framework: case OPT_weak_framework: - addFramework(arg->getValue(), opt.getID() == OPT_weak_framework, + addFramework(arg->getValue(), opt.getID() == OPT_needed_framework, + opt.getID() == OPT_weak_framework, opt.getID() == OPT_reexport_framework, /*isExplicit=*/true); break; default: diff --git a/lld/MachO/InputFiles.h b/lld/MachO/InputFiles.h index 321ea0e6774f..c26cd0f97622 100644 --- a/lld/MachO/InputFiles.h +++ b/lld/MachO/InputFiles.h @@ -156,6 +156,7 @@ public: int64_t ordinal = 0; // Ordinal numbering starts from 1, so 0 is a sentinel RefState refState; bool reexport = false; + bool forceNeeded = false; bool forceWeakImport = false; bool deadStrippable = false; bool explicitlyLinked = false; diff --git a/lld/MachO/Options.td b/lld/MachO/Options.td index cc5ab09ef550..47c6d9997e98 100644 --- a/lld/MachO/Options.td +++ b/lld/MachO/Options.td @@ -114,6 +114,14 @@ def weak_library : Separate<["-"], "weak_library">, MetaVarName<"">, HelpText<"Like bare , but mark library and its references as weak imports">, Group; +def needed_l : Joined<["-"], "needed-l">, + MetaVarName<"">, + HelpText<"Like -l, but link library even if its symbols are not used and -dead_strip_dylibs is active">, + Group; +def needed_library : Separate<["-"], "needed_library">, + MetaVarName<"">, + HelpText<"Like bare , but link library even if its symbols are not used and -dead_strip_dylibs is active">, + Group; def reexport_l : Joined<["-"], "reexport-l">, MetaVarName<"">, HelpText<"Like -l, but export all symbols of from newly created library">, @@ -157,6 +165,10 @@ def weak_framework : Separate<["-"], "weak_framework">, MetaVarName<"">, HelpText<"Like -framework , but mark framework and its references as weak imports">, Group; +def needed_framework : Separate<["-"], "needed_framework">, + MetaVarName<"">, + HelpText<"Like -framework , but link even if none of its symbols are used and -dead_strip_dylibs is active">, + Group; def reexport_framework : Separate<["-"], "reexport_framework">, MetaVarName<"">, HelpText<"Like -framework , but export all symbols of from the newly created library">, diff --git a/lld/MachO/Writer.cpp b/lld/MachO/Writer.cpp index 22bb20aa4604..da2ae690c45b 100644 --- a/lld/MachO/Writer.cpp +++ b/lld/MachO/Writer.cpp @@ -697,7 +697,7 @@ template void Writer::createLoadCommands() { // FIXME: `isReferenced()` is currently computed before dead code // stripping, so references from dead code keep a dylib alive. This // matches ld64, but it's something we should do better. - if (!dylibFile->isReferenced() && + if (!dylibFile->isReferenced() && !dylibFile->forceNeeded && (!dylibFile->explicitlyLinked || dylibFile->deadStrippable || config->deadStripDylibs)) continue; diff --git a/lld/test/MachO/dead-strip-dylibs.s b/lld/test/MachO/dead-strip-dylibs.s index 821c36c565b0..4ea5041c59d6 100644 --- a/lld/test/MachO/dead-strip-dylibs.s +++ b/lld/test/MachO/dead-strip-dylibs.s @@ -4,6 +4,7 @@ # RUN: llvm-mc %t/bar.s -triple=x86_64-apple-macos -filetype=obj -o %t/bar.o # RUN: %lld -dylib %t/bar.o -o %t/bar.dylib +# RUN: %lld -dylib %t/bar.o -o %t/libbar.dylib # RUN: %lld -dylib -mark_dead_strippable_dylib %t/bar.o -o %t/bar-strip.dylib # RUN: llvm-mc %t/foo.s -triple=x86_64-apple-macos -filetype=obj -o %t/foo.o @@ -36,7 +37,7 @@ # RUN: -dead_strip_dylibs # RUN: llvm-otool -L %t/main | FileCheck --check-prefix=NOBAR %s -## ...or bar is explicitly marked dead-strippable +## ...or bar is explicitly marked dead-strippable. # RUN: %lld -lSystem %t/main.o -o %t/main %t/foo.dylib %t/bar-strip.dylib # RUN: llvm-otool -L %t/main | FileCheck --check-prefix=NOBARSTRIP %s # NOBARSTRIP-NOT: bar-strip.dylib @@ -45,9 +46,16 @@ # NOBARSTRIP: foo.dylib # NOBARSTRIP-NOT: bar-strip.dylib +## But -needed_library and -needed-l win over -dead_strip_dylibs again. +# RUN: %lld -lSystem %t/main.o -o %t/main %t/foo_with_bar.dylib \ +# RUN: -needed_library %t/bar.dylib -dead_strip_dylibs +# RUN: llvm-otool -L %t/main | FileCheck --check-prefix=BAR %s +# RUN: %lld -lSystem %t/main.o -o %t/main %t/foo_with_bar.dylib \ +# RUN: -L%t -needed-lbar -dead_strip_dylibs +# RUN: llvm-otool -L %t/main | FileCheck --check-prefix=BAR %s + ## LC_LINKER_OPTION does not count as an explicit reference. # RUN: llvm-mc %t/linkopt_bar.s -triple=x86_64-apple-macos -filetype=obj -o %t/linkopt_bar.o -# RUN: %lld -dylib %t/bar.o -o %t/libbar.dylib # RUN: %lld -lSystem %t/main.o %t/linkopt_bar.o -o %t/main -L %t %t/foo.dylib # RUN: llvm-otool -L %t/main | FileCheck --check-prefix=NOLIBBAR %s # NOLIBBAR-NOT: libbar.dylib