[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[patches] Make --disable-versioning work in EGLIBC



Hello,

The following patch allows EGLIBC to build without symbol versions; this can be requested by adding --disable-versioning option to configure line.

The main benefit of not using symbol versions in EGLIBC is in making the library smaller by removing compatibility code that implements older APIs.

Of cause, this presents compatibility hazards as a program linked against EGLIBC with symbol versions will not run on a system with EGLIBC without symbol versions installed. In this case the run-time linker will fail with an assertion failure. Similarly, a program linked against one release of EGLIBC may not execute correctly on a system with another release of EGLIBC installed (both releases are assumed to have the library w/o symbol versions); unfortunately, run-time linker will not detect an error in this case and the program may fail unexpectedly.

The above restrictions do not apply to third libraries and run-time linker will still properly bind versioned symbols in application to versioned symbols exported by third library.

On embedded systems, where rootfs'es are compiled and linked against the same libraries the above compatibility issues are acceptable trade-offs. Library footprint reduction for common embedded architectures is the following (for stripped libc.so binary):

* ARM: 3.9KB / 0.34%
* MIPS: 23KB / 1.51%
* PPC:  41KB / 2.80%

The patch was tested on ARM, MIPS and PowerPC with no regressions and on x86 with 2 regressions (I'm looking into these).

From technical point of view, this patch solves the following problems in trunk EGLIBC that arise when attempting to build with --disable-versioning:

* Undefined symbols when linking shared libraries (libc.so mainly). This is due to [E]GLIBC relying on version maps to make certain symbol global. These version maps were only built when using symbol versioning. This patch makes the build process to generate version maps, but tweaks versions.awk script to output the map files with all symbols assigned to one common base version. As a result the version map file will only specify which symbols should be made global and don't do anything else.

* Symbol aliases made with versioned_symbol and similar macros turn out to be undefined. Due to [E]GLIBC employing a PLT/GOT bypass scheme, changes had to be made to libc-symbols.h, shlib-compat.h and similar headers to account for --disable-versioning case. This changes merely define necessary macros for !DO_VERSIONING case.

* Several small fixes were made to correct handling of !DO_VERSIONING case in different parts of the library.

Comments?  OK for trunk?


Thank you,

--
Maxim
2009-05-07  Maxim Kuvyrkov  <maxim@xxxxxxxxxxxxxxxx>

	Make --disable-versioning work.

	* scripts/versions.awk (versioning): New parameter, don't output
	version information if it is defined to 'no'.
	* include/compat.h (IS_IN_libc, versioned_symbol, versioned_symbol_1),
	(default_symbol_version): Define when !DO_VERSIONING.
	* include/libc-symbols.h (default_symbol_version): Redefine when
	!DO_VERSIONING.
	(__hidden_ver2): New macro, a more general version of __hidden_ver1.
	(__hidden_ver1): Use __hidden_ver2.
	* Makerules: Generate version maps when !DO_VERSIONING to properly
	set symbol binding.
	(abi_versions_file): Define, use instead of abi-versions.h.
	(libc_gcclibs): Define, link against libgcc_eh when !DO_VERSIONING.
	* extra-lib.mk, elf/Makefile: Update, handle --disable-versioning.
	* elf/do-rel.h: Handle !DO_VERSIONING.
	* sysdeps/powerpc/powerpc32/dl-machine.c: Don't error when
	!DO_VERSIONING.
	* sysdeps/irrr754/ldbl-opt/math_ldbl_opt.h: Define stuff when
	!DO_VERSIONING.
	* nptl/pthread_kill_other_threads.c (compat_symbol): Surround with
	ifdef DO_VERSIONING.
	
Index: glibc-trunk-2/scripts/versions.awk
===================================================================
--- glibc-trunk-2/scripts/versions.awk	(revision 8413)
+++ glibc-trunk-2/scripts/versions.awk	(working copy)
@@ -6,6 +6,7 @@
 # defsfile		name of Versions.def file
 # buildroot		name of build directory with trailing slash
 # move_if_change	move-if-change command
+# versioning            "yes", if symbol versioning is being used
 
 # Read definitions for the versions.
 BEGIN {
@@ -67,7 +68,10 @@ BEGIN {
   sortver=actver
   # Ensure GLIBC_ versions come always first
   sub(/^GLIBC_/," GLIBC_",sortver)
-  printf("%s %s %s\n", actlib, sortver, $0) | sort;
+  if (versioning == "yes") printf("%s %s %s\n", actlib, sortver, $0) | sort;
+  # When not using symbol versioning, assign all symbols non-existent GLIBC
+  # version 1.1; this will allow us to make all necessary symbols global.
+  else printf("%s GLIBC_1.1 %s\n", actlib, $0) | sort;
 }
 
 
@@ -81,7 +85,7 @@ function closeversion(name, oldname) {
   # or FOO_x and FOO_y but not GLIBC_x and FOO_y.
   pfx = oldname;
   sub(/[0-9.]+/,".+",pfx);
-  if (oldname == "" || name !~ pfx) print "};" > outfile;
+  if (oldname == "" || name !~ pfx || versioning != "yes") print "};" > outfile;
   else printf("} %s;\n", oldname) > outfile;
 }
 
@@ -121,7 +125,10 @@ END {
 	closeversion(oldver, veryoldver);
 	veryoldver = oldver;
       }
-      printf("%s {\n  global:\n", $2) > outfile;
+      if (versioning == "yes") printf("%s {\n  global:\n", $2) > outfile;
+      # When not using symbol versioning, just output which symbols should be
+      # made global.
+      else print "{\n  global:\n" > outfile;
       oldver = $2;
     }
     printf("   ") > outfile;
Index: glibc-trunk-2/include/shlib-compat.h
===================================================================
--- glibc-trunk-2/include/shlib-compat.h	(revision 8413)
+++ glibc-trunk-2/include/shlib-compat.h	(working copy)
@@ -26,6 +26,36 @@
 #include <abi-versions.h>	/* header generated by abi-versions.awk */
 #endif
 
+#if defined HAVE_ELF && defined SHARED
+
+# ifndef NOT_IN_libc
+#  define IS_IN_libc 1
+# endif
+
+/* That header also defines symbols like `VERSION_libm_GLIBC_2_1' to
+   the version set name to use for e.g. symbols first introduced into
+   libm in the GLIBC_2.1 version.  Definitions of symbols with explicit
+   versions should look like:
+   	versioned_symbol (libm, new_foo, foo, GLIBC_2_1);
+   This will define the symbol `foo' with the appropriate default version,
+   i.e. either GLIBC_2.1 or the "earliest version" specified in
+   shlib-versions if that is newer.  */
+
+# define versioned_symbol(lib, local, symbol, version) \
+  versioned_symbol_1 (lib, local, symbol, version)
+# define versioned_symbol_1(lib, local, symbol, version) \
+  versioned_symbol_2 (local, symbol, VERSION_##lib##_##version)
+# define versioned_symbol_2(local, symbol, name) \
+  default_symbol_version (local, symbol, name)
+
+#else
+
+/* No versions to worry about, just make this the global definition.  */
+# define versioned_symbol(lib, local, symbol, version) \
+  weak_alias (local, symbol)
+
+#endif
+
 #if defined HAVE_ELF && defined SHARED && defined DO_VERSIONING
 
 /* The file abi-versions.h (generated by scripts/abi-versions.awk) defines
@@ -50,26 +80,6 @@
    && (!(ABI_##lib##_##obsoleted - 0)					      \
        || ((ABI_##lib##_##introduced - 0) < (ABI_##lib##_##obsoleted - 0))))
 
-# ifndef NOT_IN_libc
-#  define IS_IN_libc 1
-# endif
-
-/* That header also defines symbols like `VERSION_libm_GLIBC_2_1' to
-   the version set name to use for e.g. symbols first introduced into
-   libm in the GLIBC_2.1 version.  Definitions of symbols with explicit
-   versions should look like:
-   	versioned_symbol (libm, new_foo, foo, GLIBC_2_1);
-   This will define the symbol `foo' with the appropriate default version,
-   i.e. either GLIBC_2.1 or the "earliest version" specified in
-   shlib-versions if that is newer.  */
-
-# define versioned_symbol(lib, local, symbol, version) \
-  versioned_symbol_1 (lib, local, symbol, version)
-# define versioned_symbol_1(lib, local, symbol, version) \
-  versioned_symbol_2 (local, symbol, VERSION_##lib##_##version)
-# define versioned_symbol_2(local, symbol, name) \
-  default_symbol_version (local, symbol, name)
-
 # define compat_symbol(lib, local, symbol, version) \
   compat_symbol_1 (lib, local, symbol, version)
 # define compat_symbol_1(lib, local, symbol, version) \
@@ -82,10 +92,6 @@
 /* Not compiling ELF shared libraries at all, so never any old versions.  */
 # define SHLIB_COMPAT(lib, introduced, obsoleted)	0
 
-/* No versions to worry about, just make this the global definition.  */
-# define versioned_symbol(lib, local, symbol, version) \
-  weak_alias (local, symbol)
-
 /* This should not appear outside `#if SHLIB_COMPAT (...)'.  */
 # define compat_symbol(lib, local, symbol, version) ...
 
Index: glibc-trunk-2/include/libc-symbols.h
===================================================================
--- glibc-trunk-2/include/libc-symbols.h	(revision 8413)
+++ glibc-trunk-2/include/libc-symbols.h	(working copy)
@@ -449,8 +449,21 @@ for linking")
 # endif
 #else
 # define symbol_version(real, name, version)
-# define default_symbol_version(real, name, version) \
+# ifndef __ASSEMBLER__
+/* We can't use simple strong_alias in C here due to
+   symbols defined with hidden_def and company.
+   As you see above, symbol versions are output in plain assembly,
+   so, to archieve the same effect, we use __hidden_ver2 here.
+
+   Define an alias to REAL with C name __FI_##NAME and
+   asm name NAME.  We use __FI_* namespace to fill a gap
+   between __EI_* and __GI_* namespaces.  */
+#  define default_symbol_version(real, name, version) \
+  __hidden_ver2(#real, #name, real, __FI_##name)
+# else
+#  define default_symbol_version(real, name, version) \
   strong_alias(real, name)
+# endif
 #endif
 
 #if defined SHARED || defined LIBC_NONSHARED
@@ -561,7 +574,18 @@ for linking")
    versioned_symbol (libc, __real_foo, foo, GLIBC_2_1);
    libc_hidden_ver (__real_foo, foo)  */
 
-#if defined SHARED && defined DO_VERSIONING && !defined NO_HIDDEN
+#ifndef __ASSEMBLER__
+/* This is a general way of creating a global symbol
+   of the same type as NAME1,
+   having C name NAME2,
+   having asm name INTERNAL and
+   being an alias to LOCAL.  */
+# define __hidden_ver2(local, internal, name1, name2)	\
+  extern __typeof (name1) name2 __asm__(internal);	\
+  extern __typeof (name1) name2 __attribute__((alias (local)))
+#endif
+
+#if defined SHARED && !defined NO_HIDDEN
 # ifndef __ASSEMBLER__
 #  define __hidden_proto_hiddenattr(attrs...) \
   __attribute__ ((visibility ("hidden"), ##attrs))
@@ -574,10 +598,9 @@ for linking")
   __hidden_asmname1 (__USER_LABEL_PREFIX__, name)
 #  define __hidden_asmname1(prefix, name) __hidden_asmname2(prefix, name)
 #  define __hidden_asmname2(prefix, name) #prefix name
-#  define __hidden_ver1(local, internal, name) \
-  extern __typeof (name) __EI_##name __asm__(__hidden_asmname (#internal)); \
-  extern __typeof (name) __EI_##name \
-	__attribute__((alias (__hidden_asmname (#local))))
+#  define __hidden_ver1(local, internal, name)	\
+  __hidden_ver2 (__hidden_asmname (#local), __hidden_asmname (#internal), \
+		 name, __EI_##name)
 #  define hidden_ver(local, name)	__hidden_ver1(local, __GI_##name, name);
 #  define hidden_data_ver(local, name)	hidden_ver(local, name)
 #  define hidden_def(name)		__hidden_ver1(__GI_##name, name, name);
Index: glibc-trunk-2/sysdeps/powerpc/powerpc32/dl-machine.c
===================================================================
--- glibc-trunk-2/sysdeps/powerpc/powerpc32/dl-machine.c	(revision 8413)
+++ glibc-trunk-2/sysdeps/powerpc/powerpc32/dl-machine.c	(working copy)
@@ -33,9 +33,10 @@ extern int __cache_line_size attribute_h
 /* Because ld.so is now versioned, these functions can be in their own file;
    no relocations need to be done to call them.
    Of course, if ld.so is not versioned...  */
+/* eglibc: This does work without symbol versioning.
 #if defined SHARED && !(DO_VERSIONING - 0)
 #error This will not work with versioning turned off, sorry.
-#endif
+#endif */
 
 
 /* Stuff for the PLT.  */
Index: glibc-trunk-2/sysdeps/ieee754/ldbl-opt/math_ldbl_opt.h
===================================================================
--- glibc-trunk-2/sysdeps/ieee754/ldbl-opt/math_ldbl_opt.h	(revision 8413)
+++ glibc-trunk-2/sysdeps/ieee754/ldbl-opt/math_ldbl_opt.h	(working copy)
@@ -10,7 +10,7 @@
   SHLIB_COMPAT(lib, introduced, LONG_DOUBLE_COMPAT_VERSION)
 #define long_double_symbol(lib, local, symbol) \
   long_double_symbol_1 (lib, local, symbol, LONG_DOUBLE_COMPAT_VERSION)
-#if defined HAVE_ELF && defined SHARED && defined DO_VERSIONING
+#if defined HAVE_ELF && defined SHARED
 # define ldbl_hidden_def(local, name) libc_hidden_ver (local, name)
 # define ldbl_strong_alias(name, aliasname) \
   strong_alias (name, __GL_##name##_##aliasname) \
Index: glibc-trunk-2/Makerules
===================================================================
--- glibc-trunk-2/Makerules	(revision 8413)
+++ glibc-trunk-2/Makerules	(working copy)
@@ -106,6 +106,7 @@ $(common-objpfx)%.latest: $(common-objpf
 	sed -n '/ VERSION_$*_/{s/^.*_\([A-Z0-9_]*\).*$$/\1/;h;};$${g;p;}' \
 	    $(common-objpfx)abi-versions.h > $@T
 	mv -f $@T $@
+abi_versions_file := $(common-objpfx)abi-versions.h
 endif # avoid-generated
 endif # $(versioning) = yes
 
@@ -304,7 +305,9 @@ endif
 
 # Generate version maps, but wait until sysdep-subdirs is known
 ifeq ($(sysd-sorted-done),t)
-ifeq ($(versioning),yes)
+# eglibc: Even when not using symbol versioning we still need to generate
+# eglibc: version maps to make all the necessary symbols global.
+# eglibc: ifeq ($(versioning),yes)
 -include $(common-objpfx)sysd-versions
 $(addprefix $(common-objpfx),$(version-maps)): $(common-objpfx)sysd-versions
 common-generated += $(version-maps)
@@ -332,7 +335,7 @@ $(common-objpfx)Versions.all: $(..)scrip
 # See %.v/%.v.i implicit rules in Makeconfig.
 $(common-objpfx)Versions.v.i: $(wildcard $(subdirs:%=$(..)%/Versions)) \
 			      $(wildcard $(sysdirs:%=%/Versions)) \
-			      $(common-objpfx)abi-versions.h \
+			      $(abi_versions_file) \
 			      $(sysd-versions-force)
 $(common-objpfx)sysd-versions: $(common-objpfx)Versions.all \
 			       $(common-objpfx)Versions.v \
@@ -341,11 +344,12 @@ $(common-objpfx)sysd-versions: $(common-
 	  cat $(word 2,$^) \
 	  | LC_ALL=C $(AWK) -v buildroot=$(common-objpfx) -v defsfile=$< \
 			    -v move_if_change='$(move-if-change)' \
+			    -v versioning=$(versioning) \
 			    -f $(word 3,$^); \
 	) > $@T
 	mv -f $@T $@
 endif # avoid-generated
-endif # $(versioning) = yes
+# eglibc: endif # $(versioning) = yes
 endif # sysd-sorted-done
 
 # Generate .dT files as we compile.
@@ -439,12 +443,12 @@ ifeq (yes,$(build-shared))
 
 # Reference map file only when versioning is selected and a map file name
 # is given.
-ifeq ($(versioning),yes)
+# eglibc: ifeq ($(versioning),yes)
 map-file = $(firstword $($(@F:.so=-map)) \
 		       $(addprefix $(common-objpfx), \
 				   $(filter $(@F:.so=.map),$(version-maps))))
 load-map-file = $(map-file:%=-Wl,--version-script=%)
-endif
+# eglibc: endif
 
 # Pattern rule to build a shared object from an archive of PIC objects.
 # This must come after the installation rules so Make doesn't try to
@@ -615,16 +619,22 @@ ifeq ($(elf),yes)
 $(common-objpfx)libc_pic.os: $(common-objpfx)libc_pic.a
 	$(LINK.o) -nostdlib -nostartfiles -r -o $@ \
 	$(LDFLAGS-c_pic.os) -Wl,-d -Wl,--whole-archive $^
+ifneq ($(versioning),yes)
+# When EGLIBC is built without symbol versioning, local copy of
+# unwind-dw2-fde-glibc.c is not compiled, so we need to link against
+# libgcc_eh to get _Unwind_Find_FDE.
+libc_gcclibs := -lgcc_eh
+endif
 # Use our own special initializer and finalizer files for libc.so.
 $(common-objpfx)libc.so: $(elfobjdir)/soinit.os \
 			 $(common-objpfx)libc_pic.os \
 			 $(elfobjdir)/sofini.os \
 			 $(elfobjdir)/interp.os $(elfobjdir)/ld.so \
 			 $(common-objpfx)shlib.lds
-	$(build-shlib)
-ifeq ($(versioning),yes)
+	$(build-shlib) $(libc_gcclibs)
+# eglibc: ifeq ($(versioning),yes)
 $(common-objpfx)libc.so: $(common-objpfx)libc.map
-endif
+# eglibc: endif
 common-generated += libc.so libc_pic.os
 ifndef subdir
 install-extras := soinit.o sofini.o
Index: glibc-trunk-2/extra-lib.mk
===================================================================
--- glibc-trunk-2/extra-lib.mk	(revision 8413)
+++ glibc-trunk-2/extra-lib.mk	(working copy)
@@ -45,8 +45,11 @@ alltypes-$(lib) := $(foreach o,$(object-
 ifeq (,$(filter $(lib),$(extra-libs-others)))
 lib-noranlib: $(alltypes-$(lib))
 ifeq (yes,$(build-shared))
+# Build shared library only if there're routines to include.
+ifneq (,$(filter-out $($(lib)-static-only-routines),$(all-$(lib)-routines)))
 lib-noranlib: $(objpfx)$(lib).so$($(lib).so-version)
 endif
+endif
 else
 others: $(alltypes-$(lib))
 endif
@@ -87,13 +90,13 @@ $(objpfx)$(patsubst %,$(libtype.oS),$(li
 	$(build-extra-lib)
 endif
 
-ifeq ($(versioning),yes)
+# eglibc: ifeq ($(versioning),yes)
 # Add the version script to the dependencies of the shared library.
 $(objpfx)$(lib).so: $(firstword $($(lib)-map) \
 				$(addprefix $(common-objpfx), \
 					    $(filter $(lib).map, \
 						     $(version-maps))))
-endif
+# eglibc: endif
 
 endif
 
Index: glibc-trunk-2/nptl/pthread_kill_other_threads.c
===================================================================
--- glibc-trunk-2/nptl/pthread_kill_other_threads.c	(revision 8413)
+++ glibc-trunk-2/nptl/pthread_kill_other_threads.c	(working copy)
@@ -32,6 +32,8 @@ void
 __pthread_kill_other_threads_np (void)
 {
 }
+# ifdef DO_VERSIONING
 compat_symbol (libpthread, __pthread_kill_other_threads_np,
 	       pthread_kill_other_threads_np, GLIBC_2_0);
+# endif
 #endif
Index: glibc-trunk-2/elf/Makefile
===================================================================
--- glibc-trunk-2/elf/Makefile	(revision 8413)
+++ glibc-trunk-2/elf/Makefile	(working copy)
@@ -110,9 +110,9 @@ before-compile  = $(objpfx)trusted-dirs.
 generated	:= trusted-dirs.h trusted-dirs.st for-renamed/renamed.so
 generated-dirs	:= for-renamed
 
-ifeq ($(versioning),yes)
+# eglibc: ifeq ($(versioning),yes)
 ld-map		= $(common-objpfx)ld.map
-endif
+# eglibc: endif
 
 ifeq (yes,$(build-shared))
 extra-objs	= $(all-rtld-routines:%=%.os) soinit.os sofini.os interp.os
Index: glibc-trunk-2/elf/do-rel.h
===================================================================
--- glibc-trunk-2/elf/do-rel.h	(revision 8413)
+++ glibc-trunk-2/elf/do-rel.h	(working copy)
@@ -104,7 +104,7 @@ elf_dynamic_do_rel (struct link_map *map
 	  for (; relative < r; ++relative)
 	    DO_ELF_MACHINE_REL_RELATIVE (map, l_addr, relative);
 
-#ifdef RTLD_BOOTSTRAP
+#if defined RTLD_BOOTSTRAP && defined DO_VERSIONING
       /* The dynamic linker always uses versioning.  */
       assert (map->l_info[VERSYMIDX (DT_VERSYM)] != NULL);
 #else
@@ -122,7 +122,7 @@ elf_dynamic_do_rel (struct link_map *map
 			       (void *) (l_addr + r->r_offset));
 	    }
 	}
-#ifndef RTLD_BOOTSTRAP
+#if !defined RTLD_BOOTSTRAP || !defined DO_VERSIONING
       else
 	for (; r < end; ++r)
 	  elf_machine_rel (map, r, &symtab[ELFW(R_SYM) (r->r_info)], NULL,