Message ID | 20240523134650.14966-1-hjl.tools@gmail.com |
---|---|
State | New |
Headers | show |
Series | [v4] Check global symbols in static library against shared library | expand |
* H. J. Lu: > diff --git a/sysdeps/alpha/Makefile b/sysdeps/alpha/Makefile > index 11d5e65503..ed658c143e 100644 > --- a/sysdeps/alpha/Makefile > +++ b/sysdeps/alpha/Makefile > @@ -33,6 +33,11 @@ sysdep_routines += stxcpy stxncpy > endif > > ifeq ($(subdir),elf) > +# These global symbols are only in libc.so. > +libc-shared-only-symbols += \ > + __nldbl__IO_vfscanf \ > +# libc-shared-only-symbols > + > # The ld.so startup code cannot use literals until it self-relocates. > CFLAGS-rtld.c = -mbuild-constants > endif Why doesn't this indicate a bug? Shouldn't this part of libc.a, too? If it's not a bug, I think this belongs into sysdeps/ieee754/ldbl-opt/Makefile. (not a complete review, sorry, just realized this and wanted to share) Thanks, Florian
On Thu, May 23, 2024 at 6:51 AM Florian Weimer <fweimer@redhat.com> wrote: > > * H. J. Lu: > > > diff --git a/sysdeps/alpha/Makefile b/sysdeps/alpha/Makefile > > index 11d5e65503..ed658c143e 100644 > > --- a/sysdeps/alpha/Makefile > > +++ b/sysdeps/alpha/Makefile > > @@ -33,6 +33,11 @@ sysdep_routines += stxcpy stxncpy > > endif > > > > ifeq ($(subdir),elf) > > +# These global symbols are only in libc.so. > > +libc-shared-only-symbols += \ > > + __nldbl__IO_vfscanf \ > > +# libc-shared-only-symbols > > + > > # The ld.so startup code cannot use literals until it self-relocates. > > CFLAGS-rtld.c = -mbuild-constants > > endif > > Why doesn't this indicate a bug? Shouldn't this part of libc.a, too? It is referenced from libnldbl_nonshared.a. > If it's not a bug, I think this belongs into > sysdeps/ieee754/ldbl-opt/Makefile. I will fix it. > (not a complete review, sorry, just realized this and wanted to share) > > Thanks, > Florian > Thanks.
* H. J. Lu: > On Thu, May 23, 2024 at 6:51 AM Florian Weimer <fweimer@redhat.com> wrote: >> >> * H. J. Lu: >> >> > diff --git a/sysdeps/alpha/Makefile b/sysdeps/alpha/Makefile >> > index 11d5e65503..ed658c143e 100644 >> > --- a/sysdeps/alpha/Makefile >> > +++ b/sysdeps/alpha/Makefile >> > @@ -33,6 +33,11 @@ sysdep_routines += stxcpy stxncpy >> > endif >> > >> > ifeq ($(subdir),elf) >> > +# These global symbols are only in libc.so. >> > +libc-shared-only-symbols += \ >> > + __nldbl__IO_vfscanf \ >> > +# libc-shared-only-symbols >> > + >> > # The ld.so startup code cannot use literals until it self-relocates. >> > CFLAGS-rtld.c = -mbuild-constants >> > endif >> >> Why doesn't this indicate a bug? Shouldn't this part of libc.a, too? > > It is referenced from libnldbl_nonshared.a. > >> If it's not a bug, I think this belongs into >> sysdeps/ieee754/ldbl-opt/Makefile. > > I will fix it. What I meant is that you should probably be able to call scanf from a program that is linked with -static -lnldbl, and the bug is the missing __nldbl__IO_vfscanf function in libc.a. Thanks, Florian
On Thu, May 23, 2024 at 8:54 AM Florian Weimer <fweimer@redhat.com> wrote: > > * H. J. Lu: > > > On Thu, May 23, 2024 at 6:51 AM Florian Weimer <fweimer@redhat.com> wrote: > >> > >> * H. J. Lu: > >> > >> > diff --git a/sysdeps/alpha/Makefile b/sysdeps/alpha/Makefile > >> > index 11d5e65503..ed658c143e 100644 > >> > --- a/sysdeps/alpha/Makefile > >> > +++ b/sysdeps/alpha/Makefile > >> > @@ -33,6 +33,11 @@ sysdep_routines += stxcpy stxncpy > >> > endif > >> > > >> > ifeq ($(subdir),elf) > >> > +# These global symbols are only in libc.so. > >> > +libc-shared-only-symbols += \ > >> > + __nldbl__IO_vfscanf \ > >> > +# libc-shared-only-symbols > >> > + > >> > # The ld.so startup code cannot use literals until it self-relocates. > >> > CFLAGS-rtld.c = -mbuild-constants > >> > endif > >> > >> Why doesn't this indicate a bug? Shouldn't this part of libc.a, too? > > > > It is referenced from libnldbl_nonshared.a. > > > >> If it's not a bug, I think this belongs into > >> sysdeps/ieee754/ldbl-opt/Makefile. > > > > I will fix it. The v5 patch is at https://patchwork.sourceware.org/project/glibc/list/?series=34278 > What I meant is that you should probably be able to call scanf > from a program that is linked with -static -lnldbl, and the bug is the > missing __nldbl__IO_vfscanf function in libc.a. > There is no libnldbl.a.
* H. J. Lu: > On Thu, May 23, 2024 at 8:54 AM Florian Weimer <fweimer@redhat.com> wrote: >> What I meant is that you should probably be able to call scanf >> from a program that is linked with -static -lnldbl, and the bug is the >> missing __nldbl__IO_vfscanf function in libc.a. > > There is no libnldbl.a. Thus is how I expect this to work. With GCC and Clang, we have __asm__ redirect support, so __LDBL_REDIR etc. produce the expected __nldbl_ symbol directly. This happens if you compile with -mlong-double-64 on power64le-linux-gnu, for example, because the compiler defines __LDBL_COMPAT. Without __asm__ redirect support, I assume you are expected to link with -lnldbl_nonshared. This can currently fail with a linker error: /usr/lib/gcc/ppc64le-redhat-linux/8/../../../../lib64/libc.a(vfscanf.o): In function `___vfscanf': (.text+0x5880): multiple definition of `__vfscanf' /usr/lib/gcc/ppc64le-redhat-linux/8/../../../../lib64/libnldbl_nonshared.a(nldbl-vfscanf.oS):(.text+0x0): first defined here This appears to be a bug: diff --git a/sysdeps/ieee754/ldbl-opt/nldbl-vfscanf.c b/sysdeps/ieee754/ldbl-opt/nldbl-vfscanf.c index 6f5ebc3045..e3d879cb22 100644 --- a/sysdeps/ieee754/ldbl-opt/nldbl-vfscanf.c +++ b/sysdeps/ieee754/ldbl-opt/nldbl-vfscanf.c @@ -11,5 +11,4 @@ __vfscanf (FILE *s, const char *fmt, va_list ap) { return __nldbl__IO_vfscanf (s, fmt, ap, NULL); } -extern __typeof (__vfscanf) vfscanf attribute_hidden; weak_alias (__vfscanf, vfscanf) But afterwards, the __nldbl__IO_vfscanf symbol is required. And I see it in our downstream glibc 2.28 variant. But with our glibc 2.34 variant, I get a link failure: /usr/bin/ld: /tmp/ccOU5PJG.o: in function `my_scanf': t.c:(.text+0x5c): undefined reference to `__nldbl__IO_vfscanf' collect2: error: ld returned 1 exit status So your tests have found another genuine bug, I think. Thanks, Florian
On Thu, May 23, 2024 at 11:29 AM Florian Weimer <fweimer@redhat.com> wrote: > > * H. J. Lu: > > > On Thu, May 23, 2024 at 8:54 AM Florian Weimer <fweimer@redhat.com> wrote: > >> What I meant is that you should probably be able to call scanf > >> from a program that is linked with -static -lnldbl, and the bug is the > >> missing __nldbl__IO_vfscanf function in libc.a. > > > > There is no libnldbl.a. > > Thus is how I expect this to work. With GCC and Clang, we have __asm__ > redirect support, so __LDBL_REDIR etc. produce the expected __nldbl_ > symbol directly. This happens if you compile with -mlong-double-64 on > power64le-linux-gnu, for example, because the compiler defines > __LDBL_COMPAT. > > Without __asm__ redirect support, I assume you are expected to link with > -lnldbl_nonshared. This can currently fail with a linker error: > > /usr/lib/gcc/ppc64le-redhat-linux/8/../../../../lib64/libc.a(vfscanf.o): In function `___vfscanf': > (.text+0x5880): multiple definition of `__vfscanf' > /usr/lib/gcc/ppc64le-redhat-linux/8/../../../../lib64/libnldbl_nonshared.a(nldbl-vfscanf.oS):(.text+0x0): first defined here > > This appears to be a bug: > > diff --git a/sysdeps/ieee754/ldbl-opt/nldbl-vfscanf.c b/sysdeps/ieee754/ldbl-opt/nldbl-vfscanf.c > index 6f5ebc3045..e3d879cb22 100644 > --- a/sysdeps/ieee754/ldbl-opt/nldbl-vfscanf.c > +++ b/sysdeps/ieee754/ldbl-opt/nldbl-vfscanf.c > @@ -11,5 +11,4 @@ __vfscanf (FILE *s, const char *fmt, va_list ap) > { > return __nldbl__IO_vfscanf (s, fmt, ap, NULL); > } > -extern __typeof (__vfscanf) vfscanf attribute_hidden; > weak_alias (__vfscanf, vfscanf) > > But afterwards, the __nldbl__IO_vfscanf symbol is required. And I see > it in our downstream glibc 2.28 variant. > > But with our glibc 2.34 variant, I get a link failure: > > /usr/bin/ld: /tmp/ccOU5PJG.o: in function `my_scanf': > t.c:(.text+0x5c): undefined reference to `__nldbl__IO_vfscanf' > collect2: error: ld returned 1 exit status > > So your tests have found another genuine bug, I think. > Can you open a glibc bug? We should fix it with a testcase. What should happen to __nldbl__IO_vfscanf in libc.a and libc.so? Thanks. -- H.J.
* H. J. Lu: >> But with our glibc 2.34 variant, I get a link failure: >> >> /usr/bin/ld: /tmp/ccOU5PJG.o: in function `my_scanf': >> t.c:(.text+0x5c): undefined reference to `__nldbl__IO_vfscanf' >> collect2: error: ld returned 1 exit status >> >> So your tests have found another genuine bug, I think. >> > > Can you open a glibc bug? We should fix it with a testcase. > What should happen to __nldbl__IO_vfscanf in libc.a and libc.so? Digging somewhat further, I believe this was an intentional change as part of this commit: commit 349718d4d7841df46bcc36df9bc2baef4c40d6f5 Author: Zack Weinberg <zackw@panix.com> Date: Wed Mar 7 14:31:58 2018 -0500 Add __vfscanf_internal and __vfwscanf_internal with flags arguments. (The commit message has a longer explanation of the change.) It is this part which I expect causes the change to be dropped from the static build: diff --git a/sysdeps/ieee754/ldbl-opt/nldbl-compat.c b/sysdeps/ieee754/ldbl-opt/nldbl-compat.c index 7a1e89c1a3..91ea27a423 100644 --- a/sysdeps/ieee754/ldbl-opt/nldbl-compat.c +++ b/sysdeps/ieee754/ldbl-opt/nldbl-compat.c @@ -330,16 +330,20 @@ __nldbl_wprintf (const wchar_t *fmt, ...) return done; } +#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_29) int attribute_compat_text_section __nldbl__IO_vfscanf (FILE *s, const char *fmt, va_list ap, int *errp) { int res; set_no_long_double (); - res = _IO_vfscanf (s, fmt, ap, errp); + res = __vfscanf_internal (s, fmt, ap, 0); clear_no_long_double (); + if (__glibc_unlikely (errp != 0)) + *errp = (res == -1); return res; } +#endif int attribute_compat_text_section But __nldbl__IO_vfscanf is not marked as a compat symbol: # eu-readelf --symbols=.dynsym /lib64/libc.so.6 | grep __nldbl__IO_vfscanf 1203: 00000000001c2240 132 FUNC GLOBAL DEFAULT 11 __nldbl__IO_vfscanf@@GLIBC_2.17 And this must be what trips your test case. We should turn it into a compat symbol, rather than fixing the test case. Thanks, Florian
On Thu, May 23, 2024 at 12:42 PM Florian Weimer <fweimer@redhat.com> wrote: > > * H. J. Lu: > > >> But with our glibc 2.34 variant, I get a link failure: > >> > >> /usr/bin/ld: /tmp/ccOU5PJG.o: in function `my_scanf': > >> t.c:(.text+0x5c): undefined reference to `__nldbl__IO_vfscanf' > >> collect2: error: ld returned 1 exit status > >> > >> So your tests have found another genuine bug, I think. > >> > > > > Can you open a glibc bug? We should fix it with a testcase. > > What should happen to __nldbl__IO_vfscanf in libc.a and libc.so? > > Digging somewhat further, I believe this was an intentional change as > part of this commit: > > commit 349718d4d7841df46bcc36df9bc2baef4c40d6f5 > Author: Zack Weinberg <zackw@panix.com> > Date: Wed Mar 7 14:31:58 2018 -0500 > > Add __vfscanf_internal and __vfwscanf_internal with flags arguments. > > (The commit message has a longer explanation of the change.) > > It is this part which I expect causes the change to be dropped from the > static build: > > diff --git a/sysdeps/ieee754/ldbl-opt/nldbl-compat.c b/sysdeps/ieee754/ldbl-opt/nldbl-compat.c > index 7a1e89c1a3..91ea27a423 100644 > --- a/sysdeps/ieee754/ldbl-opt/nldbl-compat.c > +++ b/sysdeps/ieee754/ldbl-opt/nldbl-compat.c > @@ -330,16 +330,20 @@ __nldbl_wprintf (const wchar_t *fmt, ...) > return done; > } > > +#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_29) > int > attribute_compat_text_section > __nldbl__IO_vfscanf (FILE *s, const char *fmt, va_list ap, int *errp) > { > int res; > set_no_long_double (); > - res = _IO_vfscanf (s, fmt, ap, errp); > + res = __vfscanf_internal (s, fmt, ap, 0); > clear_no_long_double (); > + if (__glibc_unlikely (errp != 0)) > + *errp = (res == -1); > return res; > } > +#endif > > int > attribute_compat_text_section > > But __nldbl__IO_vfscanf is not marked as a compat symbol: > > # eu-readelf --symbols=.dynsym /lib64/libc.so.6 | grep __nldbl__IO_vfscanf > 1203: 00000000001c2240 132 FUNC GLOBAL DEFAULT 11 __nldbl__IO_vfscanf@@GLIBC_2.17 > > And this must be what trips your test case. We should turn it into a > compat symbol, rather than fixing the test case. > > Thanks, > Florian > How should libnldbl_nonshared.a be used? There are many references to __nldbl__IO_vfscanf in libnldbl_nonshared.
* H. J. Lu: >> It is this part which I expect causes the change to be dropped from the >> static build: >> >> diff --git a/sysdeps/ieee754/ldbl-opt/nldbl-compat.c b/sysdeps/ieee754/ldbl-opt/nldbl-compat.c >> index 7a1e89c1a3..91ea27a423 100644 >> --- a/sysdeps/ieee754/ldbl-opt/nldbl-compat.c >> +++ b/sysdeps/ieee754/ldbl-opt/nldbl-compat.c >> @@ -330,16 +330,20 @@ __nldbl_wprintf (const wchar_t *fmt, ...) >> return done; >> } >> >> +#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_29) >> int >> attribute_compat_text_section >> __nldbl__IO_vfscanf (FILE *s, const char *fmt, va_list ap, int *errp) >> { >> +#endif >> But __nldbl__IO_vfscanf is not marked as a compat symbol: >> >> # eu-readelf --symbols=.dynsym /lib64/libc.so.6 | grep __nldbl__IO_vfscanf >> 1203: 00000000001c2240 132 FUNC GLOBAL DEFAULT 11 __nldbl__IO_vfscanf@@GLIBC_2.17 >> >> And this must be what trips your test case. We should turn it into a >> compat symbol, rather than fixing the test case. > How should libnldbl_nonshared.a be used? There are many > references to __nldbl__IO_vfscanf in libnldbl_nonshared. Ahh, good point. I think it's expected to call __nldbl___isoc99_vfscanf instead. All this makes more sense once you realize that vfscanf was not part of C89. 8-) Thanks, Florian
On Thu, May 23, 2024 at 1:00 PM Florian Weimer <fweimer@redhat.com> wrote: > > * H. J. Lu: > > >> It is this part which I expect causes the change to be dropped from the > >> static build: > >> > >> diff --git a/sysdeps/ieee754/ldbl-opt/nldbl-compat.c b/sysdeps/ieee754/ldbl-opt/nldbl-compat.c > >> index 7a1e89c1a3..91ea27a423 100644 > >> --- a/sysdeps/ieee754/ldbl-opt/nldbl-compat.c > >> +++ b/sysdeps/ieee754/ldbl-opt/nldbl-compat.c > >> @@ -330,16 +330,20 @@ __nldbl_wprintf (const wchar_t *fmt, ...) > >> return done; > >> } > >> > >> +#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_29) > >> int > >> attribute_compat_text_section > >> __nldbl__IO_vfscanf (FILE *s, const char *fmt, va_list ap, int *errp) > >> { > > >> +#endif > > >> But __nldbl__IO_vfscanf is not marked as a compat symbol: > >> > >> # eu-readelf --symbols=.dynsym /lib64/libc.so.6 | grep __nldbl__IO_vfscanf > >> 1203: 00000000001c2240 132 FUNC GLOBAL DEFAULT 11 __nldbl__IO_vfscanf@@GLIBC_2.17 > >> > >> And this must be what trips your test case. We should turn it into a > >> compat symbol, rather than fixing the test case. > > > How should libnldbl_nonshared.a be used? There are many > > references to __nldbl__IO_vfscanf in libnldbl_nonshared. > > Ahh, good point. I think it's expected to call __nldbl___isoc99_vfscanf > instead. > > All this makes more sense once you realize that vfscanf was not part of > C89. 8-) > I reopened: https://sourceware.org/bugzilla/show_bug.cgi?id=31776
diff --git a/Makerules b/Makerules index 275110dda8..24ec69b1ff 100644 --- a/Makerules +++ b/Makerules @@ -1238,6 +1238,21 @@ ifeq ($(build-shared),yes) LC_ALL=C $(OBJDUMP) --dynamic-syms $< > $@T mv -f $@T $@ +# Extract global symbol definitions from shared and static libraries. +NM-FLAGS=--extern-only --defined-only --just-symbols + +%.dynamicsymlist: %.so + LC_ALL=C; export LC_ALL; $(NM) --dynamic $(NM-FLAGS) $< \ + | grep -v "^_.*GLIBC_PRIVATE" | grep @@ | sed -e "s/@@.*//" \ + | sort > $@T + mv -f $@T $@ + +%.staticsymlist: %.a + LC_ALL=C; export LC_ALL; $(NM) $(NM-FLAGS) $< \ + | grep -v DW.ref.__gcc_personality_v0 \ + | sort > $@T + mv -f $@T $@ + vpath %.abilist $(+sysdep_dirs) # The .PRECIOUS rule prevents the files built by an implicit rule whose @@ -1246,6 +1261,16 @@ vpath %.abilist $(+sysdep_dirs) # 'make clean', which is handled by the 'generated' variable. .PRECIOUS: %.symlist generated += $(extra-libs:=.symlist) +ifeq (yes,$(nm-has-symbol-version)) +.PRECIOUS: %.dynamicsymlist %.staticsymlist +# Only check global symbols in static libraries in $(extra-libs). +static-libs := $(foreach lib, $(extra-libs), \ + $(if $($(lib)-inhibit-o),,$(lib))) +generated += \ + $(static-libs:=.dynamicsymlist) \ + $(static-libs:=.staticsymlist) \ +# generated +endif $(objpfx)check-abi-%.out: $(common-objpfx)config.make %.abilist \ $(objpfx)%.symlist @@ -1259,6 +1284,24 @@ define check-abi diff -p -U 0 $(filter %.abilist,$^) $(filter %.symlist,$^) > $@ endef +$(objpfx)check-static-abi-%.out: $(common-objpfx)config.make \ + $(objpfx)%.dynamicsymlist \ + $(objpfx)%.staticsymlist + $(check-static-abi); \ + $(evaluate-test) +$(objpfx)check-static-abi-%.out: $(common-objpfx)config.make \ + $(common-objpfx)%.dynamicsymlist \ + $(common-objpfx)%.staticsymlist + $(check-static-abi); \ + $(evaluate-test) +define check-static-abi + $(SHELL) $(..)scripts/check-static-abi.sh $* \ + $(subst elf/libc.a,libc.a,$(objpfx)$*.a) $(NM) $(AWK) \ + $(filter %.dynamicsymlist,$^) \ + $(filter %.staticsymlist,$^) \ + "$($*-shared-only-symbols)" > $@ +endef + update-abi-%: $(objpfx)%.symlist %.abilist $(update-abi) update-abi-%: $(common-objpfx)%.symlist %.abilist @@ -1290,6 +1333,11 @@ update-all-abi: $(patsubst %.so,update-all-abi-%,$(install-lib.so-versioned)) check-abi-list = $(patsubst %.so,$(objpfx)check-abi-%.out, \ $(install-lib.so-versioned)) check-abi: $(check-abi-list) +ifeq (yes,$(nm-has-symbol-version)) +check-static-abi-list = $(patsubst %.a,$(objpfx)check-static-abi-%.out, \ + $(static-libs:=.a)) +check-abi: $(check-static-abi-list) +endif ifdef subdir subdir_check-abi: check-abi subdir_update-abi: update-abi @@ -1298,6 +1346,10 @@ else check-abi: subdir_check-abi if grep -q '^FAIL:' $(objpfx)*/check-abi*.test-result; then \ cat $(objpfx)*/check-abi*.out && exit 1; fi +ifeq (yes,$(nm-has-symbol-version)) + if grep -q '^FAIL:' $(objpfx)*/check-static-abi*.test-result; \ + then cat $(objpfx)*/check-static-abi*.out && exit 1; fi +endif update-abi: subdir_update-abi update-all-abi: subdir_update-all-abi endif @@ -1308,11 +1360,28 @@ tests-special += $(objpfx)check-abi-libc.out update-abi: update-abi-libc update-all-abi: update-all-abi-libc common-generated += libc.symlist +ifeq (yes,$(nm-has-symbol-version)) +check-abi: $(objpfx)check-static-abi-libc.out +tests-special += $(objpfx)check-static-abi-libc.out +common-generated += \ + libc.dynamicsymlist \ + libc.staticsymlist \ + libc_nonshared.staticsymlist \ + # common-generated +# check-static-abi.sh needs libc_nonshared.staticsymlist and +# libm.dynamicsymlist to check the global symbol list in libc.a. +$(objpfx)check-static-abi-libc.out: \ + $(common-objpfx)libc_nonshared.staticsymlist \ + $(common-objpfx)math/libm.dynamicsymlist +endif endif ifeq ($(build-shared),yes) ifdef subdir tests-special += $(check-abi-list) +ifeq (yes,$(nm-has-symbol-version)) +tests-special += $(check-static-abi-list) +endif endif endif diff --git a/configure b/configure index 432e40a592..8328bc59e0 100755 --- a/configure +++ b/configure @@ -7566,6 +7566,48 @@ if test "$libc_cv_symver_needs_alias" = yes; then fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether nm displays symbol versions for dynamic symbols" >&5 +printf %s "checking whether nm displays symbol versions for dynamic symbols... " >&6; } +if test ${libc_cv_nm_has_symbol_version+y} +then : + printf %s "(cached) " >&6 +else $as_nop + cat > conftest.s <<EOF + .text + .globl new_foo + .type new_foo, %function +new_foo: + .byte 0 + .symver new_foo,foo@@VERS_2.0 +EOF + cat > conftest.t <<EOF +VERS_2.0 { +global: + foo; +local: + *; +}; +EOF + libc_cv_nm_has_symbol_version=no + if { ac_try='${CC-cc} -nostdlib -nostartfiles -fPIC -shared conftest.s -Wl,-version-script=conftest.t -o conftest.so' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5 + (eval $ac_try) 2>&5 + ac_status=$? + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then + if $NM --dynamic --extern-only --defined-only --just-symbols conftest.so 2>&1 \ + | grep -q foo@@VERS_2.0; then + libc_cv_nm_has_symbol_version=yes + fi + fi + rm conftest.* + +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $libc_cv_nm_has_symbol_version" >&5 +printf "%s\n" "$libc_cv_nm_has_symbol_version" >&6; } +config_vars="$config_vars +nm-has-symbol-version = $libc_cv_nm_has_symbol_version" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for __builtin_trap with no external dependencies" >&5 printf %s "checking for __builtin_trap with no external dependencies... " >&6; } if test ${libc_cv_builtin_trap+y} diff --git a/configure.ac b/configure.ac index bdc385d03c..db27a6ce2a 100644 --- a/configure.ac +++ b/configure.ac @@ -1554,6 +1554,37 @@ if test "$libc_cv_symver_needs_alias" = yes; then AC_DEFINE(SYMVER_NEEDS_ALIAS) fi +dnl Starting with binutils 2.35, NM displays symbol versions for dynamic +dnl symbols (PR 25708). +AC_CACHE_CHECK(whether nm displays symbol versions for dynamic symbols, + libc_cv_nm_has_symbol_version, [dnl + cat > conftest.s <<EOF + .text + .globl new_foo + .type new_foo, %function +new_foo: + .byte 0 + .symver new_foo,foo@@VERS_2.0 +EOF + cat > conftest.t <<EOF +VERS_2.0 { +global: + foo; +local: + *; +}; +EOF + libc_cv_nm_has_symbol_version=no + if AC_TRY_COMMAND([${CC-cc} -nostdlib -nostartfiles -fPIC -shared conftest.s -Wl,-version-script=conftest.t -o conftest.so]); then + if $NM --dynamic --extern-only --defined-only --just-symbols conftest.so 2>&1 \ + | grep -q foo@@VERS_2.0; then + libc_cv_nm_has_symbol_version=yes + fi + fi + rm conftest.* +]) +LIBC_CONFIG_VAR([nm-has-symbol-version], [$libc_cv_nm_has_symbol_version]) + AC_CACHE_CHECK(for __builtin_trap with no external dependencies, libc_cv_builtin_trap, [dnl libc_cv_builtin_trap=no diff --git a/elf/Makefile b/elf/Makefile index 280e777c19..eb4357a0dc 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -182,6 +182,21 @@ CFLAGS-rtld.os += $(rtld-early-cflags) ifeq ($(unwind-find-fde),yes) routines += unwind-dw2-fde-glibc shared-only-routines += unwind-dw2-fde-glibc + +# These global symbols are only in libc.so. +libc-shared-only-symbols += \ + _Unwind_Find_FDE \ + __deregister_frame \ + __deregister_frame_info \ + __deregister_frame_info_bases \ + __register_frame \ + __register_frame_info \ + __register_frame_info_bases \ + __register_frame_info_bases \ + __register_frame_info_table \ + __register_frame_info_table_bases \ + __register_frame_table \ +# libc-shared-only-symbols endif before-compile += $(objpfx)trusted-dirs.h diff --git a/math/Makefile b/math/Makefile index 58e5c070cf..ae268e0088 100644 --- a/math/Makefile +++ b/math/Makefile @@ -1674,3 +1674,7 @@ object-suffixes-left := $(libmvec-tests) include $(o-iterator) $(objpfx)test-fenv-tls: $(shared-thread-library) + +# check-static-abi.sh needs libc.staticsymlist to check the global symbol +# list in libm.a. +$(objpfx)check-static-abi-libm.out: $(common-objpfx)libc.staticsymlist diff --git a/scripts/archive-function.awk b/scripts/archive-function.awk new file mode 100644 index 0000000000..56f3c7b002 --- /dev/null +++ b/scripts/archive-function.awk @@ -0,0 +1,41 @@ +# Script to report functions with filenames in an archive. + +# Copyright (C) 2018-2024 Free Software Foundation, Inc. +# This file is part of the GNU C Library. + +# The GNU C Library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +# The GNU C Library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with the GNU C Library; if not, see +# <https://www.gnu.org/licenses/>. + +NF == 1 { + thisfile = $1 + filenames[$1] = 1 + next; +} + +NF == 3 { + allsymbols[thisfile, $3] = 1 + next; +} + +END { + # functions is a string of function names separated by a space. + split (functions, symbols, " ") + for (s in symbols) { + for (f in filenames) { + if (allsymbols[f, symbols[s]] == 1) { + print f, symbols[s] + } + } + } +} diff --git a/scripts/check-static-abi.sh b/scripts/check-static-abi.sh new file mode 100644 index 0000000000..196e8754f3 --- /dev/null +++ b/scripts/check-static-abi.sh @@ -0,0 +1,112 @@ +#!/bin/sh +# Check global symbols in static library against global symbols in shared +# library. +# Copyright (C) 2024 Free Software Foundation, Inc. +# This file is part of the GNU C Library. +# +# The GNU C Library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# The GNU C Library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with the GNU C Library; if not, see +# <https://www.gnu.org/licenses/>. + +export LC_ALL=C + +lib="$1" +shift +archive="$1" +shift +nm="$1" +shift +awk="$1" +shift +dynamicsymlist="$1" +shift +if test $lib = libc; then + libm_dynamicsymlist="$1" + shift +fi +staticsymlist="$1" +shift +if test $lib = libc; then + libc_nonshared_staticsymlist="$1" + shift +elif test $lib = libm; then + libc_staticsymlist="$1" + shift +fi +shared_only_functions="$1" + +staticdiff="$(mktemp)" +staticlist="$(mktemp)" + +cleanup () { + rm -f -- "$staticdiff" "$staticlist" +} + +trap cleanup 0 + +status=0 + +diff -up $dynamicsymlist $staticsymlist > $staticdiff + +# Check missing global symbols, including symbols with a leading +# underscore, in static library. +shared_only="$(grep '^-[^-]' $staticdiff | sed -e 's/^-//')" +if test -n "$shared_only"; then + # Exclude shared only functions. + for f in $shared_only; do + case " $shared_only_functions " in + *" $f "*) + ;; + *) + # Skip the missing function, $f, in libm.a if $f is in libc.a. + if test $lib = libm \ + && grep -q "^$f$" $libc_staticsymlist; then + continue + fi + missing="$missing $f" + ;; + esac + done + if test -n "$missing"; then + status=1 + echo Missing functions in ${lib}.a: + echo "$missing" + fi +fi + +# Check extra global symbols, excluding symbols with a leading underscore +# which can be internal symbols, in static library. +static_only="$(grep '^+[^_^\+]' $staticdiff | sed -e 's/^+//')" +if test -n "$static_only"; then + # Exclude static only functions. + for f in $static_only; do + # Skip the extra function, $f, in libc.a if $f is in libc_nonshared.a + # or libm.so. + if test $lib = libc; then + if grep -q "^$f$" $libc_nonshared_staticsymlist \ + || grep -q "^$f$" $libm_dynamicsymlist; then + continue + fi + fi + extra="$extra $f" + done + if test -n "$extra"; then + status=1 + echo Extra functions in ${lib}.a: + $nm --extern-only --defined-only $archive > $staticlist + $awk -v functions="$extra" -f $(dirname "$0")/archive-function.awk \ + $staticlist + fi +fi + +exit $status diff --git a/sysdeps/alpha/Makefile b/sysdeps/alpha/Makefile index 11d5e65503..ed658c143e 100644 --- a/sysdeps/alpha/Makefile +++ b/sysdeps/alpha/Makefile @@ -33,6 +33,11 @@ sysdep_routines += stxcpy stxncpy endif ifeq ($(subdir),elf) +# These global symbols are only in libc.so. +libc-shared-only-symbols += \ + __nldbl__IO_vfscanf \ +# libc-shared-only-symbols + # The ld.so startup code cannot use literals until it self-relocates. CFLAGS-rtld.c = -mbuild-constants endif diff --git a/sysdeps/generic/Makefile b/sysdeps/generic/Makefile index 72522d1f0e..f24c517bbd 100644 --- a/sysdeps/generic/Makefile +++ b/sysdeps/generic/Makefile @@ -25,7 +25,18 @@ ifeq (yes:yes,$(build-shared):$(unwind-find-fde)) # This is needed to support g++ v2 and v3. sysdep_routines += framestate unwind-pe shared-only-routines += framestate unwind-pe + +# These global symbols are only in libc.so. +libc-shared-only-symbols += __frame_state_for endif + +# These global symbols are only in libc.so. +libc-shared-only-symbols += \ + __xmknod \ + __xmknodat \ + _dl_mcount_wrapper_check \ + re_max_failures \ +# libc-shared-only-symbols endif ifeq ($(subdir),malloc) diff --git a/sysdeps/powerpc/Makefile b/sysdeps/powerpc/Makefile index 5e6cb07ce6..dfecc4f260 100644 --- a/sysdeps/powerpc/Makefile +++ b/sysdeps/powerpc/Makefile @@ -3,6 +3,11 @@ CFLAGS-memcmp.c += -Wno-uninitialized endif ifeq ($(subdir),elf) +# These global symbols are only in libc.so. +libc-shared-only-symbols += \ + __nldbl__IO_vfscanf \ + # libc-shared-only-symbols + # extra shared linker files to link into dl-allobjs.so and libc sysdep-dl-routines += dl-machine hwcapinfo sysdep_routines += dl-machine hwcapinfo diff --git a/sysdeps/s390/Makefile b/sysdeps/s390/Makefile index b793b26112..664a22b357 100644 --- a/sysdeps/s390/Makefile +++ b/sysdeps/s390/Makefile @@ -43,6 +43,11 @@ $(modpfx)gconv-modules-s390.conf: ../sysdeps/s390/gconv-modules-s390.conf \ endif ifeq ($(subdir),elf) +# These global symbols are only in libc.so. +libc-shared-only-symbols += \ + __nldbl__IO_vfscanf \ +# libc-shared-only-symbols + sysdep-dl-routines += dl-procinfo-s390 ifeq ($(build-shared),yes) diff --git a/sysdeps/sparc/sparc32/Makefile b/sysdeps/sparc/sparc32/Makefile index 4ba8794aa0..b4be6cd869 100644 --- a/sysdeps/sparc/sparc32/Makefile +++ b/sysdeps/sparc/sparc32/Makefile @@ -15,6 +15,19 @@ # License along with the GNU C Library; if not, see # <https://www.gnu.org/licenses/>. +ifeq ($(subdir),elf) +# These global symbols are only in libc.so. Filter out the +# $(shared-only-routines) routines. +libc-shared-only-symbols += \ + .div \ + .rem \ + .udiv \ + .umul \ + .urem \ + __nldbl__IO_vfscanf \ + # libc-shared-only-symbols +endif + ifeq ($(subdir),gnulib) sysdep_routines = dotmul umul $(divrem) alloca shared-only-routines += umul $(divrem)
Here is the v4 patch to check global symbols in static library against shared library. The test results may not be clean due to: https://sourceware.org/bugzilla/show_bug.cgi?id=31781 https://sourceware.org/bugzilla/show_bug.cgi?id=31779 https://sourceware.org/bugzilla/show_bug.cgi?id=31778 https://sourceware.org/bugzilla/show_bug.cgi?id=31777 H.J. -- Changes in v4: 1. Use a per library shared-only-symbols. 2. Report extra functions with filenames in an archive. Changes in v3: 1. Update the shared-only-symbols order in sysdeps/sparc/sparc32/Makefile. Changes in v2: 1. Add export LC_ALL for nm. 2. Pass $* to check-static-abi.sh. 3. Don't pass $(static-only-symbols) to check-static-abi.sh. 4. Get installed static libraries from $(extra-libs). 5. Add shared-only-symbols for powerpc and sparc32. --- The shared library ABIs are checked against the abilist files. Extract global symbol definitions from static libraries and check them against exported global symbol definitions in shared libraries. Special cases are handled: 1. Some global symbols are available only in libc.so. 2. Some math functions are in libc.a, but not in libc.so. 3. The libgcc symbols are in libc.so only on some targets. 4. Internal symbols have global visibility only in the static library. This check is enabled only if nm displays symbol versions for dynamic symbols. Signed-off-by: H.J. Lu <hjl.tools@gmail.com> --- Makerules | 69 ++++++++++++++++++++ configure | 42 +++++++++++++ configure.ac | 31 +++++++++ elf/Makefile | 15 +++++ math/Makefile | 4 ++ scripts/archive-function.awk | 41 ++++++++++++ scripts/check-static-abi.sh | 112 +++++++++++++++++++++++++++++++++ sysdeps/alpha/Makefile | 5 ++ sysdeps/generic/Makefile | 11 ++++ sysdeps/powerpc/Makefile | 5 ++ sysdeps/s390/Makefile | 5 ++ sysdeps/sparc/sparc32/Makefile | 13 ++++ 12 files changed, 353 insertions(+) create mode 100644 scripts/archive-function.awk create mode 100644 scripts/check-static-abi.sh