[binutils-gdb] ld/testsuite: Add comprehensive PE COFF weak external tests
Alan Modra via Binutils-cvs <[email protected]> Tue, 30 Jun 2026 23:48:59 +0000 (GMT)
| Newsgroups | gmane.comp.gnu.binutils.cvs |
|---|---|
| Message-ID | <[email protected]> |
https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=3f8a2c9722b1dcb5525ce3943c5c3f98404ace67 commit 3f8a2c9722b1dcb5525ce3943c5c3f98404ace67 Author: Peter Damianov <[email protected]> Date: Tue Jun 23 17:38:07 2026 -0400 ld/testsuite: Add comprehensive PE COFF weak external tests Add tests covering the full matrix of weak/strong symbol interactions for PE COFF weak externals, based on testcases by Martin Storsjo. The archive test covers PE weak externals whose public name is backed by a real fallback definition, and ensures weak declarations with the null fallback are not advertised as archive providers. ld/testsuite/ * ld-pe/pe-compile.exp (weak_ext_test): New proc. Compiles source files, links via gcc, and runs natively if possible. (weak_ext_archive_test): New proc. Add tests: normal, weak-undef, weak-defined, weak-decl-weak-def, strong-undef-weak-def-archive, weak-use, weak-override, weak-duplicate, weak-def-override, weak-def-use. * ld-pe/weak-ext-main.c: New file. * ld-pe/weak-ext-main-weak.c: New file. * ld-pe/weak-ext-main-weak-def.c: New file. * ld-pe/weak-ext-add2.c: New file. * ld-pe/weak-ext-add1-weak-chained.c: New file. * ld-pe/weak-ext-dummy.c: New file. * ld-pe/weak-ext-expected1.c: New file. * ld-pe/weak-ext-expected3.c: New file. * ld-pe/weak-ext-expected5.c: New file. * ld-pe/weak-ext-expected3-add1-weak.c: New file. Diff: --- ld/testsuite/ld-pe/pe-compile.exp | 167 ++++++++++++++++++++++ ld/testsuite/ld-pe/weak-ext-add1-weak-chained.c | 13 ++ ld/testsuite/ld-pe/weak-ext-add2.c | 8 ++ ld/testsuite/ld-pe/weak-ext-dummy.c | 5 + ld/testsuite/ld-pe/weak-ext-expected1.c | 1 + ld/testsuite/ld-pe/weak-ext-expected3-add1-weak.c | 9 ++ ld/testsuite/ld-pe/weak-ext-expected3.c | 1 + ld/testsuite/ld-pe/weak-ext-expected5.c | 1 + ld/testsuite/ld-pe/weak-ext-main-weak-def.c | 26 ++++ ld/testsuite/ld-pe/weak-ext-main-weak.c | 22 +++ ld/testsuite/ld-pe/weak-ext-main.c | 21 +++ 11 files changed, 274 insertions(+) diff --git a/ld/testsuite/ld-pe/pe-compile.exp b/ld/testsuite/ld-pe/pe-compile.exp index 041a1cb9344..aac8711dff2 100644 --- a/ld/testsuite/ld-pe/pe-compile.exp +++ b/ld/testsuite/ld-pe/pe-compile.exp @@ -138,3 +138,170 @@ set align_tests { } run_ld_link_tests $align_tests + +# Test PE COFF weak external symbol resolution. +# These tests cover the full matrix of weak/strong interactions to verify +# that the linker correctly resolves weak externals in PE COFF objects. +# Based on testcases by Martin Storsjo. + +proc weak_ext_test { testname sources } { + global CC_FOR_TARGET + global srcdir + global subdir + + set objfiles {} + foreach src $sources { + set fileroot [file rootname [file tail $src]] + set obj "tmpdir/$fileroot.o" + if ![ld_compile $CC_FOR_TARGET $srcdir/$subdir/$src $obj] { + fail "$testname (compile $src)" + return + } + lappend objfiles $obj + } + + set output "tmpdir/weak-ext-test.exe" + if ![ld_link $CC_FOR_TARGET $output $objfiles] { + fail "$testname (link)" + return + } + + if ![isnative] { + pass "$testname (link only)" + return + } + + catch "exec $output" prog_output + if { $prog_output eq "" } { + pass $testname + } else { + verbose $prog_output + fail "$testname ($prog_output)" + } +} + +proc weak_ext_archive_test { } { + global CC_FOR_TARGET + global ar + global nm + global srcdir + global subdir + + set testname "weak external: archive strong undef + weak def" + set main_obj "tmpdir/weak-ext-main.o" + set weak_decl_obj "tmpdir/weak-ext-main-weak.o" + set weak_obj "tmpdir/weak-ext-add1-weak-chained.o" + set expected_obj "tmpdir/weak-ext-expected3.o" + set weak_decl_archive "tmpdir/weak-ext-decl-archive.a" + set archive "tmpdir/weak-ext-archive.a" + set output "tmpdir/weak-ext-archive-test.exe" + + set sources {weak-ext-main.c weak-ext-main-weak.c \ + weak-ext-add1-weak-chained.c weak-ext-expected3.c} + set objects [list $main_obj $weak_decl_obj $weak_obj $expected_obj] + foreach src $sources obj $objects { + if ![ld_compile $CC_FOR_TARGET $srcdir/$subdir/$src $obj] { + fail "$testname (compile $src)" + return + } + } + + set got [run_host_cmd "$ar" "rcs $weak_decl_archive $weak_decl_obj"] + if ![string match "" $got] { + fail "$testname (weak decl archive)" + return + } + + # A weak declaration with the null fallback must not advertise its + # public name as an archive-map provider. + set got [run_host_cmd "$nm" "--print-armap $weak_decl_archive"] + if [regexp "(^|\n)_?func in weak-ext-main-weak\\.o" $got] { + verbose -log $got + fail "$testname (weak decl armap)" + return + } + + set got [run_host_cmd "$ar" "rcs $archive $weak_obj"] + if ![string match "" $got] { + fail "$testname (archive)" + return + } + + # A weak external with a real fallback should advertise the public + # alias, so a strong undefined reference can extract the member. + set got [run_host_cmd "$nm" "--print-armap $archive"] + if ![regexp "(^|\n)_?func in weak-ext-add1-weak-chained\\.o" $got] { + verbose -log $got + fail "$testname (armap)" + return + } + + # Search the archive twice to cover command lines where the driver + # adds a library implicitly and the user also names it explicitly. + if ![ld_link $CC_FOR_TARGET $output \ + [list $main_obj $expected_obj $archive $archive]] { + fail "$testname (link)" + return + } + + if ![isnative] { + pass "$testname (link only)" + return + } + + catch "exec $output" prog_output + if { $prog_output eq "" } { + pass $testname + } else { + verbose $prog_output + fail "$testname ($prog_output)" + } +} + +# Strong undefined reference + strong definition: basic sanity check. +weak_ext_test "weak external: normal (strong undef + strong def)" \ + {weak-ext-main.c weak-ext-add2.c weak-ext-expected3.c weak-ext-dummy.c} + +# Weak declaration with no definition available: func remains NULL, +# never called. +weak_ext_test "weak external: weak undef (no def)" \ + {weak-ext-main-weak.c weak-ext-expected1.c weak-ext-dummy.c} + +# Weak declaration resolved by a strong definition in another object. +weak_ext_test "weak external: weak decl + strong def" \ + {weak-ext-main-weak.c weak-ext-add2.c weak-ext-expected3.c weak-ext-dummy.c} + +# Two weak externals for the same symbol meet: the one whose fallback +# alias points to an actual function body should win over the one whose +# fallback is NULL. +weak_ext_test "weak external: weak decl + weak def" \ + {weak-ext-main-weak.c weak-ext-add1-weak-chained.c weak-ext-expected3.c} + +# Strong undefined reference is seen before the weak definition: the +# linker must still store the weak external's aux record so the fallback +# alias can be resolved later. +weak_ext_test "weak external: strong undef + weak def" \ + {weak-ext-main.c weak-ext-add1-weak-chained.c weak-ext-expected3.c} + +weak_ext_archive_test + +# Strong definition overrides a weak definition: both call sites (in +# main and in dummy) should resolve to the strong def. +weak_ext_test "weak external: strong override of weak" \ + {weak-ext-main.c weak-ext-add1-weak-chained.c weak-ext-add2.c \ + weak-ext-expected5.c} + +# Two identical weak definitions for the same symbol: no conflict, +# the linker picks one (first wins) and discards the duplicate. +weak_ext_test "weak external: duplicate weak defs" \ + {weak-ext-main.c weak-ext-add1-weak-chained.c \ + weak-ext-expected3-add1-weak.c} + +# Weak definition in main overridden by a strong definition elsewhere. +weak_ext_test "weak external: weak def overridden by strong" \ + {weak-ext-main-weak-def.c weak-ext-add2.c weak-ext-expected3.c \ + weak-ext-dummy.c} + +# Weak definition in main used directly when no strong definition exists. +weak_ext_test "weak external: weak def used (no strong)" \ + {weak-ext-main-weak-def.c weak-ext-expected5.c weak-ext-dummy.c} diff --git a/ld/testsuite/ld-pe/weak-ext-add1-weak-chained.c b/ld/testsuite/ld-pe/weak-ext-add1-weak-chained.c new file mode 100644 index 00000000000..5b68b8435e2 --- /dev/null +++ b/ld/testsuite/ld-pe/weak-ext-add1-weak-chained.c @@ -0,0 +1,13 @@ +/* Weak definition of func (adds 1), plus dummy that calls func. */ +extern int value; + +__attribute__((weak)) void func (void) +{ + value += 1; +} + +void +dummy (void) +{ + func (); +} diff --git a/ld/testsuite/ld-pe/weak-ext-add2.c b/ld/testsuite/ld-pe/weak-ext-add2.c new file mode 100644 index 00000000000..691554d17e3 --- /dev/null +++ b/ld/testsuite/ld-pe/weak-ext-add2.c @@ -0,0 +1,8 @@ +/* Strong definition of func (adds 2). */ +extern int value; + +void +func (void) +{ + value += 2; +} diff --git a/ld/testsuite/ld-pe/weak-ext-dummy.c b/ld/testsuite/ld-pe/weak-ext-dummy.c new file mode 100644 index 00000000000..1c97c3cf97f --- /dev/null +++ b/ld/testsuite/ld-pe/weak-ext-dummy.c @@ -0,0 +1,5 @@ +/* Empty dummy function. */ +void +dummy (void) +{ +} diff --git a/ld/testsuite/ld-pe/weak-ext-expected1.c b/ld/testsuite/ld-pe/weak-ext-expected1.c new file mode 100644 index 00000000000..5b4138b0bb1 --- /dev/null +++ b/ld/testsuite/ld-pe/weak-ext-expected1.c @@ -0,0 +1 @@ +int expected = 1; diff --git a/ld/testsuite/ld-pe/weak-ext-expected3-add1-weak.c b/ld/testsuite/ld-pe/weak-ext-expected3-add1-weak.c new file mode 100644 index 00000000000..585830bd304 --- /dev/null +++ b/ld/testsuite/ld-pe/weak-ext-expected3-add1-weak.c @@ -0,0 +1,9 @@ +/* Second weak definition of func (adds 1) plus expected value. */ +extern int value; + +__attribute__((weak)) void func (void) +{ + value += 1; +} + +int expected = 3; diff --git a/ld/testsuite/ld-pe/weak-ext-expected3.c b/ld/testsuite/ld-pe/weak-ext-expected3.c new file mode 100644 index 00000000000..3d67d70bcb0 --- /dev/null +++ b/ld/testsuite/ld-pe/weak-ext-expected3.c @@ -0,0 +1 @@ +int expected = 3; diff --git a/ld/testsuite/ld-pe/weak-ext-expected5.c b/ld/testsuite/ld-pe/weak-ext-expected5.c new file mode 100644 index 00000000000..fef6b322c03 --- /dev/null +++ b/ld/testsuite/ld-pe/weak-ext-expected5.c @@ -0,0 +1 @@ +int expected = 5; diff --git a/ld/testsuite/ld-pe/weak-ext-main-weak-def.c b/ld/testsuite/ld-pe/weak-ext-main-weak-def.c new file mode 100644 index 00000000000..05f81d189b4 --- /dev/null +++ b/ld/testsuite/ld-pe/weak-ext-main-weak-def.c @@ -0,0 +1,26 @@ +/* Weak definition of func in main (adds 4). */ +#include <stdio.h> + +int value = 1; + +__attribute__((weak)) void func (void) +{ + value += 4; +} + +void dummy (void); +extern int expected; + +int +main (void) +{ + if (func) + func (); + dummy (); + if (value != expected) + { + printf ("expected %d, got %d\n", expected, value); + return 1; + } + return 0; +} diff --git a/ld/testsuite/ld-pe/weak-ext-main-weak.c b/ld/testsuite/ld-pe/weak-ext-main-weak.c new file mode 100644 index 00000000000..bc1501f62c0 --- /dev/null +++ b/ld/testsuite/ld-pe/weak-ext-main-weak.c @@ -0,0 +1,22 @@ +/* Weak declaration of func (no definition). */ +#include <stdio.h> + +__attribute__((weak)) void func (void); +void dummy (void); + +int value = 1; +extern int expected; + +int +main (void) +{ + if (func) + func (); + dummy (); + if (value != expected) + { + printf ("expected %d, got %d\n", expected, value); + return 1; + } + return 0; +} diff --git a/ld/testsuite/ld-pe/weak-ext-main.c b/ld/testsuite/ld-pe/weak-ext-main.c new file mode 100644 index 00000000000..916bb246e5f --- /dev/null +++ b/ld/testsuite/ld-pe/weak-ext-main.c @@ -0,0 +1,21 @@ +/* Strong undefined reference to func. */ +#include <stdio.h> + +void func (void); +void dummy (void); + +int value = 1; +extern int expected; + +int +main (void) +{ + func (); + dummy (); + if (value != expected) + { + printf ("expected %d, got %d\n", expected, value); + return 1; + } + return 0; +}