[PATCH 45/59] Check if linker sets --no-undefined-version by default

Sam James sam@gentoo.org
Wed Oct 22 18:09:44 GMT 2025


Adhemerval Zanella Netto <adhemerval.zanella@linaro.org> writes:

> On 18/10/25 03:56, Sam James wrote:
>> Adhemerval Zanella <adhemerval.zanella@linaro.org> writes:
>> 
>>> Recent lld version default to --no-undefined-version, which triggers
>>> errors when building multiple libraries.  For ld.so it fails with:
>>>
>>> ld.lld: error: version script assignment of 'GLIBC_2.4' to symbol '__stack_chk_guard' failed: symbol not defined
>>> ld.lld: error: version script assignment of 'GLIBC_PRIVATE' to symbol '__nptl_set_robust_list_avail' failed: symbol not defined
>>> ld.lld: error: version script assignment of 'GLIBC_PRIVATE' to symbol '__pointer_chk_guard' failed: symbol not defined
>>> ld.lld: error: version script assignment of 'GLIBC_PRIVATE' to symbol '_dl_starting_up' failed: symbol not defined
>>>
>>> While for libc.so:
>>>
>>> ld.lld: error: version script assignment of 'GLIBC_2.17' to symbol '_IO_clearerr' failed: symbol not defined
>>> ld.lld: error: version script assignment of 'GLIBC_2.17' to symbol '_IO_fgetc' failed: symbol not defined
>>> ld.lld: error: version script assignment of 'GLIBC_2.17' to symbol '_IO_fileno' failed: symbol not defined
>>> ld.lld: error: version script assignment of 'GLIBC_2.17' to symbol '_IO_freopen' failed: symbol not defined
>>> ld.lld: error: version script assignment of 'GLIBC_2.17' to symbol '_IO_fscanf' failed: symbol not defined
>>> ld.lld: error: version script assignment of 'GLIBC_2.17' to symbol '_IO_fseek' failed: symbol not defined
>>> ld.lld: error: version script assignment of 'GLIBC_2.17' to symbol '_IO_peekc_unlocked' failed: symbol not defined
>>> ld.lld: error: version script assignment of 'GLIBC_2.17' to symbol '_IO_stderr_' failed: symbol not defined
>>> ld.lld: error: version script assignment of 'GLIBC_2.17' to symbol '_IO_stdin_' failed: symbol not defined
>>> ld.lld: error: version script assignment of 'GLIBC_2.17' to symbol '_IO_stdout_' failed: symbol not defined
>>> ld.lld: error: version script assignment of 'GLIBC_2.17' to symbol '_IO_pclose' failed: symbol not defined
>>> ld.lld: error: version script assignment of 'GLIBC_2.17' to symbol '_IO_perror' failed: symbol not defined
>>> ld.lld: error: version script assignment of 'GLIBC_2.17' to symbol '_IO_rewind' failed: symbol not defined
>>> ld.lld: error: version script assignment of 'GLIBC_2.17' to symbol '_IO_scanf' failed: symbol not defined
>>> ld.lld: error: version script assignment of 'GLIBC_2.17' to symbol '_IO_setbuf' failed: symbol not defined
>>> ld.lld: error: version script assignment of 'GLIBC_2.17' to symbol '_IO_setlinebuf' failed: symbol not defined
>>> ld.lld: error: version script assignment of 'GLIBC_2.17' to symbol '_IO_wdefault_setbuf' failed: symbol not defined
>>> ld.lld: error: version script assignment of 'GLIBC_2.17' to symbol '_IO_wfile_setbuf' failed: symbol not defined
>>> ld.lld: error: version script assignment of 'GLIBC_2.17' to symbol '__ctype32_tolower' failed: symbol not defined
>>> ld.lld: error: version script assignment of 'GLIBC_2.17' to symbol '__ctype32_toupper' failed: symbol not defined
>>> ld.lld: error: too many errors emitted, stopping now (use --error-limit=0 to see all errors)
>>>
>>> The way version script is created with multiple missing symbols is
>>> to simplify the build for multiple ABIs with different symbol
>>> symbols.  For instance, __stack_chk_guard is defined by default to
>>> avoid the need of each ABI to add this symbol on its own version
>>> script depending of which stack protector ABI it uses.
>> 
>> Yes. The warning is helpful as a linter but there's a lot of cases like
>> this where it ends up being more harm than help.
>> 
>>>
>>> The libc.so warnings does show unused symbol being defined (like
>>> _IO_clearerr), which might trigger potential errors depending of
>>> how symbol are exported.  However, since we do have teh ABI checks
>> 
>> s/teh/the/
>
> Ack.
>
>> 
>>> for missing and extra symbol the linker extra checks are not really
>>> required.  So disable them for now.
>> 
>> Can we just check for -Wl,--no-undefined-version please? I don't expect
>> us to ever change the default in bfd.
>> 
>> (Or was there a particular reason to check for the default I'm missing?)
>
> The does exactly that at configure.  With gcc we will get:
>
> $ grep undefined-version config.make
> undefined-version =
>
> While with lld:
>
> $ grep undefined-version config.make
> undefined-version = -Wl,--undefined-version

What I'm saying is: check if the linker supports -Wl,--undefined-version
(as we do with other linker args, like the execstack warning/error),
don't bother checking if it errors out by default (i.e. don't pass any
linker script) to simplify?

>
> I will adjust the commit message stating it is a configure test.
>
>> 
>>> ---
>>>  Makerules    |  2 +-
>>>  configure    | 40 ++++++++++++++++++++++++++++++++++++++++
>>>  configure.ac | 17 +++++++++++++++++
>>>  3 files changed, 58 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/Makerules b/Makerules
>>> index 7714888cdc..425bf758a1 100644
>>> --- a/Makerules
>>> +++ b/Makerules
>>> @@ -508,7 +508,7 @@ ifeq ($(build-shared),yes)
>>>  map-file = $(firstword $($(@F:.so=-map)) \
>>>  		       $(addprefix $(common-objpfx), \
>>>  				   $(filter $(@F:.so=.map),$(version-maps))))
>>> -load-map-file = $(map-file:%=-Wl,--version-script=%)
>>> +load-map-file = $(map-file:%=-Wl,--version-script=%) $(undefined-version)
>>>  endif
>>>  
>>>  # Compiler arguments to use to link a shared object with libc and
>>> diff --git a/configure b/configure
>>> index 38e7d820c5..26f6682c1e 100755
>>> --- a/configure
>>> +++ b/configure
>>> @@ -8839,6 +8839,46 @@ fi
>>>  config_vars="$config_vars
>>>  no-time-bits-source = $no_time_bits_source"
>>>  
>>> +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the linker sets --no-undefined-version by default" >&5
>>> +printf %s "checking whether the linker sets --no-undefined-version by default... " >&6; }
>>> +if test ${libc_cv_undefined_version+y}
>>> +then :
>>> +  printf %s "(cached) " >&6
>>> +else case e in #(
>>> +  e) cat > conftest.script << EOF
>>> +VERSION_1.0 { global: bar; };
>>> +EOF
>>> +old_LDFLAGS="$LDFLAGS"
>>> +LDFLAGS="-Wl,--version-script=conftest.script -shared"
>>> +cat confdefs.h - <<_ACEOF >conftest.$ac_ext
>>> +/* end confdefs.h.  */
>>> +extern int und (void);
>>> +int
>>> +main (void)
>>> +{
>>> +int foo (void) { return und (); }
>>> +  ;
>>> +  return 0;
>>> +}
>>> +_ACEOF
>>> +if ac_fn_c_try_link "$LINENO"
>>> +then :
>>> +  libc_cv_undefined_version=
>>> +else case e in #(
>>> +  e) libc_cv_undefined_version=-Wl,--undefined-version ;;
>>> +esac
>>> +fi
>>> +rm -f core conftest.err conftest.$ac_objext conftest.beam \
>>> +    conftest$ac_exeext conftest.$ac_ext
>>> +LDFLAGS="$old_LDFLAGS"
>>> +rm conftest.* ;;
>>> +esac
>>> +fi
>>> +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $libc_cv_undefined_version" >&5
>>> +printf "%s\n" "$libc_cv_undefined_version" >&6; }
>>> +config_vars="$config_vars
>>> +undefined-version = $libc_cv_undefined_version"
>>> +
>>>  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the assembler requires one version per symbol" >&5
>>>  printf %s "checking whether the assembler requires one version per symbol... " >&6; }
>>>  if test ${libc_cv_symver_needs_alias+y}
>>> diff --git a/configure.ac b/configure.ac
>>> index 0a6badeb19..7111d081f2 100644
>>> --- a/configure.ac
>>> +++ b/configure.ac
>>> @@ -1853,6 +1853,23 @@ AS_IF([test "$libc_cv_compiler_default_time_bits" = yes],
>>>  AC_SUBST(no_time_bits_source)
>>>  LIBC_CONFIG_VAR([no-time-bits-source], [$no_time_bits_source])
>>>  
>>> +dnl Check if linker sets --no-undefined-version by default
>>> +AC_CACHE_CHECK([whether the linker sets --no-undefined-version by default],
>>> +	       [libc_cv_undefined_version],
>>> +	       [dnl
>>> +cat > conftest.script << EOF
>>> +VERSION_1.0 { global: bar; };
>>> +EOF
>>> +old_LDFLAGS="$LDFLAGS"
>>> +LDFLAGS="-Wl,--version-script=conftest.script -shared"
>>> +AC_LINK_IFELSE([AC_LANG_PROGRAM([[extern int und (void);]],
>>> +                                [[int foo (void) { return und (); }]])],
>>> +                                [libc_cv_undefined_version=],
>>> +                                [libc_cv_undefined_version=-Wl,--undefined-version])
>>> +LDFLAGS="$old_LDFLAGS"
>>> +rm conftest.*])
>>> +LIBC_CONFIG_VAR([undefined-version], [$libc_cv_undefined_version])
>>> +
>>>  dnl Starting with binutils 2.35, GAS can attach multiple symbol versions
>>>  dnl to one symbol (PR 23840).
>>>  AC_CACHE_CHECK(whether the assembler requires one version per symbol,


More information about the Libc-alpha mailing list