[llvm] [llvm-lib] Infer the DLL name from the def file name (PR #208700)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Jul 10 04:35:23 PDT 2026
https://github.com/omartijn created https://github.com/llvm/llvm-project/pull/208700
When a module definition file passed via `/def:` contains no LIBRARY statement, MSVC lib.exe names the DLL after the def file, with the extension replaced by ".dll". llvm-lib instead emitted import library members with an empty DLL name, silently producing archives that reference no importable module; the error only surfaces much later, when linking against the import library or loading the resulting binary.
Match lib.exe by deriving the DLL name from the def file name when the def file does not name one. This makes import libraries for projects whose build systems emit bare EXPORTS lists (e.g. FFmpeg's makedef) come out correctly.
The same fallback is applied to `/defArm64Native:`.
>From 6effa8d22be5d49a9ceb5c002ce833a9f51769ac Mon Sep 17 00:00:00 2001
From: Martijn Otto <martijn at resolume.com>
Date: Fri, 10 Jul 2026 12:58:12 +0200
Subject: [PATCH] [llvm-lib] Infer the DLL name from the def file name
When a module definition file passed via /def: contains no LIBRARY
statement, MSVC lib.exe names the DLL after the def file, with the
extension replaced by ".dll". llvm-lib instead emitted import library
members with an empty DLL name, silently producing archives that
reference no importable module; the error only surfaces much later,
when linking against the import library or loading the resulting
binary.
Match lib.exe by deriving the DLL name from the def file name when the
def file does not name one. This makes import libraries for projects
whose build systems emit bare EXPORTS lists (e.g. FFmpeg's makedef)
come out correctly.
The same fallback is applied to /defArm64Native:.
---
llvm/lib/ToolDrivers/llvm-lib/LibDriver.cpp | 23 +++++++++---
llvm/test/tools/llvm-lib/infer-dll-name.test | 39 ++++++++++++++++++++
2 files changed, 57 insertions(+), 5 deletions(-)
create mode 100644 llvm/test/tools/llvm-lib/infer-dll-name.test
diff --git a/llvm/lib/ToolDrivers/llvm-lib/LibDriver.cpp b/llvm/lib/ToolDrivers/llvm-lib/LibDriver.cpp
index 15d959d7712dd..20687fe948263 100644
--- a/llvm/lib/ToolDrivers/llvm-lib/LibDriver.cpp
+++ b/llvm/lib/ToolDrivers/llvm-lib/LibDriver.cpp
@@ -73,6 +73,14 @@ static std::string getDefaultOutputPath(const NewArchiveMember &FirstMember) {
return std::string(Val);
}
+// lib.exe names the import library members after the def file when the def
+// file does not contain a LIBRARY statement.
+static std::string getDefaultDllName(StringRef DefPath) {
+ SmallString<128> Val = sys::path::filename(DefPath);
+ sys::path::replace_extension(Val, ".dll");
+ return std::string(Val);
+}
+
static std::vector<StringRef> getSearchPaths(opt::InputArgList *Args,
StringSaver &Saver) {
std::vector<StringRef> Ret;
@@ -290,11 +298,11 @@ static void appendFile(std::vector<NewArchiveMember> &Members,
if (FileMachine != COFF::IMAGE_FILE_MACHINE_UNKNOWN) {
if (LibMachine == COFF::IMAGE_FILE_MACHINE_UNKNOWN) {
if (FileMachine == COFF::IMAGE_FILE_MACHINE_ARM64EC) {
- llvm::errs() << MB.getBufferIdentifier() << ": file machine type "
- << machineToStr(FileMachine)
- << " conflicts with inferred library machine type,"
- << " use /machine:arm64ec or /machine:arm64x\n";
- exit(1);
+ llvm::errs() << MB.getBufferIdentifier() << ": file machine type "
+ << machineToStr(FileMachine)
+ << " conflicts with inferred library machine type,"
+ << " use /machine:arm64ec or /machine:arm64x\n";
+ exit(1);
}
LibMachine = FileMachine;
LibMachineSource =
@@ -401,6 +409,8 @@ int llvm::libDriverMain(ArrayRef<const char *> ArgsArr) {
std::vector<COFFShortExport> NativeExports;
std::string OutputFile = Def->OutputFile;
+ if (OutputFile.empty())
+ OutputFile = getDefaultDllName(Args.getLastArg(OPT_deffile)->getValue());
if (isArm64EC(LibMachine) && Args.hasArg(OPT_nativedeffile)) {
std::unique_ptr<MemoryBuffer> NativeMB =
@@ -423,6 +433,9 @@ int llvm::libDriverMain(ArrayRef<const char *> ArgsArr) {
}
NativeExports = std::move(NativeDef->Exports);
OutputFile = std::move(NativeDef->OutputFile);
+ if (OutputFile.empty())
+ OutputFile =
+ getDefaultDllName(Args.getLastArg(OPT_nativedeffile)->getValue());
}
if (Error E =
diff --git a/llvm/test/tools/llvm-lib/infer-dll-name.test b/llvm/test/tools/llvm-lib/infer-dll-name.test
new file mode 100644
index 0000000000000..6bbeec79cea82
--- /dev/null
+++ b/llvm/test/tools/llvm-lib/infer-dll-name.test
@@ -0,0 +1,39 @@
+Test that the DLL name is inferred from the def file name when the def file
+contains no LIBRARY statement, like lib.exe does.
+
+RUN: rm -rf %t
+RUN: split-file %s %t
+RUN: cd %t
+
+RUN: llvm-lib -machine:amd64 -def:sub/mylib-1.def -out:test.lib
+RUN: llvm-lib -list test.lib | FileCheck %s --check-prefix=LIST --match-full-lines
+RUN: llvm-nm --print-armap test.lib | FileCheck %s --check-prefix=ARMAP
+
+The directory is stripped and the extension is replaced, so the members are
+named after mylib-1.dll.
+
+LIST: mylib-1.dll
+LIST-NEXT: mylib-1.dll
+LIST-NEXT: mylib-1.dll
+LIST-NEXT: mylib-1.dll
+
+ARMAP: Archive map
+ARMAP-NEXT: __IMPORT_DESCRIPTOR_mylib-1 in mylib-1.dll
+ARMAP-NEXT: __NULL_IMPORT_DESCRIPTOR in mylib-1.dll
+ARMAP-NEXT: __imp_func in mylib-1.dll
+ARMAP-NEXT: func in mylib-1.dll
+ARMAP-NEXT: mylib-1_NULL_THUNK_DATA in mylib-1.dll
+
+A LIBRARY statement still takes precedence over the def file name.
+
+RUN: llvm-lib -machine:amd64 -def:sub/other.def -out:test2.lib
+RUN: llvm-lib -list test2.lib | FileCheck %s --check-prefix=LIST --match-full-lines
+
+#--- sub/mylib-1.def
+EXPORTS
+ func
+
+#--- sub/other.def
+LIBRARY mylib-1.dll
+EXPORTS
+ func
More information about the llvm-commits
mailing list