[PATCH 08/27] gdb/testsuite: Export all DLL symbols on windows-msvc via generated .def
Pedro Alves <[email protected]> Thu, 23 Jul 2026 14:00:59 +0100
| Newsgroups | gmane.comp.gdb.patches |
|---|---|
| Message-ID | <[email protected]> |
When building a shared library for a test, gdb_compile_shlib arranges
for the DLL's symbols to be exported and for an import library to be
produced, so that the test executable can link against the DLL. With
GNU ld this works implicitly: linking a DLL exports all of its symbols
by default.
lld-link, which is the linker clang drives on a windows-msvc target,
does not do that. It exports nothing unless explicitly told to, via
__declspec(dllexport) in the sources, a /EXPORT switch per symbol, or
a module-definition (.def) file. The testsuite's shared-library
sources carry (almost) no dllexport annotations, so the import library
comes out empty and linking the test executable fails, e.g.:
lld-link: error: undefined symbol: pendfunc
Fix this by restoring the export-all-symbols behavior by generating a
.def file that lists every defined, external symbol in the DLL's
objects, and passing it to lld-link with /DEF. The symbol list is
collected with nm.
FWIW, this is the same approach CMake takes for its
WINDOWS_EXPORT_ALL_SYMBOLS target property.
Change-Id: I1a9638259986910d473c4f02fbf60437e3653e04
---
gdb/testsuite/lib/future.exp | 8 +++++
gdb/testsuite/lib/gdb.exp | 64 +++++++++++++++++++++++++++++++++++-
2 files changed, 71 insertions(+), 1 deletion(-)
diff --git a/gdb/testsuite/lib/future.exp b/gdb/testsuite/lib/future.exp
index ca37fdaaf52..e308bef0a60 100644
--- a/gdb/testsuite/lib/future.exp
+++ b/gdb/testsuite/lib/future.exp
@@ -224,6 +224,14 @@ proc gdb_find_readelf {} {
return [gdb_transform_tool readelf]
}
+proc gdb_find_nm {} {
+ global NM_FOR_TARGET
+ if {[info exists NM_FOR_TARGET]} {
+ return $NM_FOR_TARGET
+ }
+ return [gdb_transform_tool nm]
+}
+
proc gdb_find_windres {} {
global WINDRES_FOR_TARGET
if {[info exists WINDRES_FOR_TARGET]} {
diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index c9ee8e9753d..b3588f0edec 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -6812,7 +6812,11 @@ proc gdb_compile {source dest type options} {
}
if { $shlib_found == 0 } {
set shlib_found 1
- if { [is_pecoff_target] } {
+ # lld-link does not understand GNU ld's
+ # --enable-auto-import. On windows-msvc the DLL's
+ # symbols are exported via a generated .def instead.
+ # See gdb_windows_gen_def_file.
+ if { [is_pecoff_target] && ![istarget "*-*-windows-msvc*"] } {
lappend new_options "ldflags=-Wl,--enable-auto-import"
}
if { [test_compiler_info "gcc-*"] || [test_compiler_info "clang-*"] } {
@@ -7208,6 +7212,50 @@ proc gdb_compile_pthreads {source dest type options} {
}
}
+# Write a module-definition (.def) file to DEFFILE that exports every
+# defined, external symbol found in OBJECTS, and return DEFFILE.
+# Unlike GNU ld, which auto-exports a DLL's symbols, lld-link exports
+# nothing unless told to, so the import library it produces would be
+# empty and executables linking against the DLL would fail with
+# undefined symbols. Feeding lld-link this generated .def restores
+# the export-all-symbols behavior the testsuite relies on. Returns
+# empty string on failure.
+
+proc gdb_windows_gen_def_file {objects deffile} {
+ set nm [gdb_find_nm]
+
+ set exports {}
+ foreach obj $objects {
+ if {[catch {exec $nm $obj} output]} {
+ verbose -log "gdb_windows_gen_def_file: nm $obj failed: $output"
+ return ""
+ }
+ foreach line [split $output "\n"] {
+ # nm prints "<value> <type> <name>". A defined external
+ # symbol has an uppercase type letter, undefined (U) has a
+ # blank value, and local symbols use lowercase letters.
+ # Export text/weak as code and data/bss/rodata/common as
+ # DATA.
+ if {![regexp {^\S* +(\S) +(\S+)$} $line -> type name]} {
+ continue
+ }
+ switch -- $type {
+ "T" - "W" { lappend exports $name }
+ "D" - "B" - "R" - "C" { lappend exports "$name DATA" }
+ }
+ }
+ }
+
+ set f [open $deffile w]
+ puts $f "EXPORTS"
+ foreach e [lsort -unique $exports] {
+ puts $f " $e"
+ }
+ close $f
+
+ return $deffile
+}
+
# Build a shared library from SOURCES.
proc gdb_compile_shlib_1 {sources dest options} {
@@ -7310,6 +7358,20 @@ proc gdb_compile_shlib_1 {sources dest options} {
}
if { [istarget "*-*-windows-msvc*"] } {
lappend link_options "ldflags=-Wl,/IMPLIB:${name}.a"
+
+ # lld-link exports nothing by default, so generate a
+ # .def listing every symbol and feed it in, to get the
+ # export-all-symbols behavior GNU ld gives us
+ # implicitly.
+ set deffile [gdb_windows_gen_def_file $objects ${dest}.def]
+ if { $deffile eq "" } {
+ return -1
+ }
+ # Pass the path in Windows-native form so MSYS2's
+ # argument conversion doesn't mangle the "/DEF:/..."
+ # value.
+ set deffile [host_file_normalize $deffile]
+ lappend link_options "ldflags=-Wl,/DEF:${deffile}"
} else {
lappend link_options "ldflags=-Wl,--out-implib,${name}.a"
}
--
2.54.0