Re: [PATCH v3 4/4] ld/testsuite: Add comprehensive PE COFF weak external tests
Peter0x44 via Binutils <[email protected]>
| Newsgroups | gmane.comp.gnu.binutils |
|---|---|
| Message-ID | <[email protected]> |
Oops I forgot to amend this commit with my testsuite changes exercising the issue with archives. Will have to submit a v4. But otherwise, the practical changes are good and I tested them thoroughly. Including the fix for gcc PR124263 Jun 23, 2026 5:38:29 PM Peter Damianov <[email protected]>: > 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. > --- > ld/testsuite/ld-pe/pe-compile.exp | 167 ++++++++++++++++++ > .../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-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(+) > create mode 100644 ld/testsuite/ld-pe/weak-ext-add1-weak-chained.c > create mode 100644 ld/testsuite/ld-pe/weak-ext-add2.c > create mode 100644 ld/testsuite/ld-pe/weak-ext-dummy.c > create mode 100644 ld/testsuite/ld-pe/weak-ext-expected1.c > create mode 100644 ld/testsuite/ld-pe/weak-ext-expected3-add1-weak.c > create mode 100644 ld/testsuite/ld-pe/weak-ext-expected3.c > create mode 100644 ld/testsuite/ld-pe/weak-ext-expected5.c > create mode 100644 ld/testsuite/ld-pe/weak-ext-main-weak-def.c > create mode 100644 ld/testsuite/ld-pe/weak-ext-main-weak.c > create mode 100644 ld/testsuite/ld-pe/weak-ext-main.c > > 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; > +} > -- > 2.54.0