https://github.com/jhuber6 created https://github.com/llvm/llvm-project/pull/216239
Summary: The windows link patterns were slightly wrong because `foo.lib` on the command line as an input implies searching. We track this for the `-libpath` argument and search them now as the linker would. Additionally, fixes a problem with whole-archive not being handled properly. >From 3ce9b074ade94274d276481f5b5ceaf4eb99b63f Mon Sep 17 00:00:00 2001 From: Joseph Huber <[email protected]> Date: Thu, 13 Aug 2026 22:46:41 -0500 Subject: [PATCH] [LinkerWrapper] Fix search paths for Windows link style invocations Summary: The windows link patterns were slightly wrong because `foo.lib` on the command line as an input implies searching. We track this for the `-libpath` argument and search them now as the linker would. Additionally, fixes a problem with whole-archive not being handled properly. --- .../clang-linker-wrapper/linker-wrapper.c | 18 ++++++++++ .../ClangLinkerWrapper.cpp | 33 ++++++++++++++++--- .../clang-linker-wrapper/LinkerWrapperOpts.td | 7 ++-- 3 files changed, 51 insertions(+), 7 deletions(-) diff --git a/clang/test/OffloadTools/clang-linker-wrapper/linker-wrapper.c b/clang/test/OffloadTools/clang-linker-wrapper/linker-wrapper.c index eebae1a0cc153..e6026f79398c7 100644 --- a/clang/test/OffloadTools/clang-linker-wrapper/linker-wrapper.c +++ b/clang/test/OffloadTools/clang-linker-wrapper/linker-wrapper.c @@ -169,6 +169,24 @@ __attribute__((visibility("protected"), used)) int x; // COFF: "/usr/bin/lld-link" {{.*}}.o -libpath:./ -out:a.exe {{.*}}openmp.image.wrapper{{.*}} +// RUN: rm -rf %t.dir && mkdir -p %t.dir +// RUN: llvm-ar rcs %t.dir/foo.lib %t.o +// RUN: llvm-ar rcs %t.dir/libbar.dll.a %t.o +// RUN: clang-linker-wrapper --host-triple=x86_64-unknown-windows-msvc --dry-run \ +// RUN: --linker-path=/usr/bin/lld-link %t.o -libpath:%t.dir foo.lib -out:a.exe 2>&1 \ +// RUN: | FileCheck %s --check-prefix=COFF-LIBRARY +// RUN: clang-linker-wrapper --host-triple=x86_64-w64-windows-gnu --dry-run \ +// RUN: --linker-path=/usr/bin/ld.lld %t.o -L%t.dir -lbar -o a.exe 2>&1 \ +// RUN: | FileCheck %s --check-prefix=COFF-LIBRARY + +// COFF-LIBRARY: clang{{.*}} --target=nvptx64-nvidia-cuda -march=sm_70 {{.*}}.o {{.*}}.o + +// RUN: clang-linker-wrapper --host-triple=x86_64-unknown-windows-msvc --dry-run \ +// RUN: --linker-path=/usr/bin/lld-link -wholearchive:%t.dir/foo.lib -out:a.exe 2>&1 \ +// RUN: | FileCheck %s --check-prefix=COFF-WHOLE-ARCHIVE + +// COFF-WHOLE-ARCHIVE: clang{{.*}} --target=nvptx64-nvidia-cuda -march=sm_70 {{[^ ]*}}.o{{$}} + // RUN: llvm-offload-binary -o %t-lib.out \ // RUN: --image=file=%t.elf.o,kind=openmp,triple=amdgpu9.0a-amd-amdhsa,arch=gfx90a // RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t.o -fembed-offload-object=%t-lib.out diff --git a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp index f2a58774e99af..4a32962c0dabc 100644 --- a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp +++ b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp @@ -1261,6 +1261,12 @@ searchLibraryBaseName(StringRef Name, StringRef Root, if (std::optional<std::string> File = findFile(Dir, Root, "lib" + Name + ".a")) return File; + // Windows linkers also accept the MinGW and the MSVC spelling of a library. + if (std::optional<std::string> File = + findFile(Dir, Root, "lib" + Name + ".dll.a")) + return File; + if (std::optional<std::string> File = findFile(Dir, Root, Name + ".lib")) + return File; } return std::nullopt; } @@ -1276,6 +1282,15 @@ std::optional<std::string> searchLibrary(StringRef Input, StringRef Root, return searchLibraryBaseName(Input, Root, SearchPaths); } +/// Search for an input file given by name, e.g. `foo.lib`. COFF linkers use +/// this in place of `-lfoo` and look it up in \p SearchPaths. +std::optional<std::string> searchInput(StringRef Input, StringRef Root, + ArrayRef<StringRef> SearchPaths) { + if (sys::fs::exists(Input)) + return std::string(Input); + return findFromSearchPaths(Input, Root, SearchPaths); +} + /// In verbose mode we need to replay the extracted files so the user can /// reproduce the generated. This only prints the steps that would result in the /// same output files given the input. @@ -1350,6 +1365,11 @@ getDeviceInput(const ArgList &Args) { for (const opt::Arg *Arg : Args.filtered(OPT_library_path, OPT_libpath)) LibraryPaths.push_back(Arg->getValue()); + // Only `link.exe` style linkers search for their input files. + SmallVector<StringRef> InputPaths; + for (const opt::Arg *Arg : Args.filtered(OPT_libpath)) + InputPaths.push_back(Arg->getValue()); + BumpPtrAllocator Alloc; StringSaver Saver(Alloc); @@ -1358,8 +1378,9 @@ getDeviceInput(const ArgList &Args) { SmallVector<OffloadFile> ObjectFilesToExtract; SmallVector<OffloadFile> ArchiveFilesToExtract; DenseMap<StringRef, StringRef> SourceForImage; - for (const opt::Arg *Arg : Args.filtered( - OPT_INPUT, OPT_library, OPT_whole_archive, OPT_no_whole_archive)) { + for (const opt::Arg *Arg : + Args.filtered(OPT_INPUT, OPT_library, OPT_wholearchive_file, + OPT_whole_archive, OPT_no_whole_archive)) { if (Arg->getOption().matches(OPT_whole_archive) || Arg->getOption().matches(OPT_no_whole_archive)) { WholeArchive = Arg->getOption().matches(OPT_whole_archive); @@ -1369,7 +1390,7 @@ getDeviceInput(const ArgList &Args) { std::optional<std::string> Filename = Arg->getOption().matches(OPT_library) ? searchLibrary(Arg->getValue(), Root, LibraryPaths) - : std::string(Arg->getValue()); + : searchInput(Arg->getValue(), Root, InputPaths); if (!Filename && Arg->getOption().matches(OPT_library)) return createStringError("unable to find library -l%s", Arg->getValue()); @@ -1378,6 +1399,10 @@ getDeviceInput(const ArgList &Args) { sys::fs::is_directory(*Filename)) continue; + // Unlike `--whole-archive`, `/wholearchive:` applies to a single library. + bool ExtractWholeArchive = + WholeArchive || Arg->getOption().matches(OPT_wholearchive_file); + ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr = MemoryBuffer::getFileOrSTDIN(*Filename); if (std::error_code EC = BufferOrErr.getError()) @@ -1397,7 +1422,7 @@ getDeviceInput(const ArgList &Args) { Binary.getBinary()->getMemoryBufferRef().getBufferIdentifier(), Saver.save(StringRef(*Filename))); if (identify_magic(Buffer.getBuffer()) == file_magic::archive && - !WholeArchive) + !ExtractWholeArchive) ArchiveFilesToExtract.emplace_back(std::move(Binary)); else ObjectFilesToExtract.emplace_back(std::move(Binary)); diff --git a/clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td b/clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td index 53b6c596de291..fc48eac2637ee 100644 --- a/clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td +++ b/clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td @@ -120,9 +120,9 @@ def library_path_EQ : Joined<["--", "-"], "library-path=">, Flags<[HelpHidden]>, def library : JoinedOrSeparate<["-"], "l">, MetaVarName<"<libname>">, HelpText<"Search for library <libname>">; def library_S : Separate<["--", "-"], "library">, Flags<[HelpHidden]>, - Alias<library_path>; + Alias<library>; def library_EQ : Joined<["--", "-"], "library=">, Flags<[HelpHidden]>, - Alias<library_path>; + Alias<library>; def rpath : Separate<["--", "-"], "rpath">; def rpath_EQ : Joined<["--", "-"], "rpath=">, Flags<[HelpHidden]>, Alias<rpath>; @@ -139,4 +139,5 @@ def r : Flag<["-"], "r">, Alias<relocatable>; // link.exe-style linker options. def out : Joined<["/", "-", "/?", "-?"], "out:">, Flags<[HelpHidden]>; def libpath : Joined<["/", "-", "/?", "-?"], "libpath:">, Flags<[HelpHidden]>; -def wholearchive_flag : Joined<["/", "-", "/?", "-?"], "wholearchive">, Flags<[HelpHidden]>; +def wholearchive_file : Joined<["/", "-", "/?", "-?"], "wholearchive:">, Flags<[HelpHidden]>; +def wholearchive_flag : Flag<["/", "-", "/?", "-?"], "wholearchive">, Flags<[HelpHidden]>; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
