This is the mail archive of the libc-hacker@sourceware.cygnus.com mailing list for the glibc project.

Note that libc-hacker is a closed list. You may look at the archives of this list, but subscription and posting are not open.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

shlib-compat.h


I have implemented some changes to better manage symbol version sets and
old-versioned ABI compatibility code.  The intent of these changes is that
when a port to a new platform is done or a library's soname gets changed,
libraries built for the new platform/soname not pay the overhead to support
obsolete symbol versions that were never supported before by that soname on
that platform.

I control this with a new (optional) parameter in shlib-versions that gives
the earliest symbol version set that needs to be supported.  This has two
effects: 

1. In the version map file we fold all the symbols placed in
   earlier version sets into the specified earliest version.
   For example, if the matched line says "libfoo=6 GLIBC_2.1.3"
   and libfoo.map would ordinarily look like:
	GLIBC_2.0 { global: foo; };
	GLIBC_2.1 { global: bar; } GLIBC_2.0;
	GLIBC_2.1.3 { global: baz; } GLIBC_2.1;
	GLIBC_2.2 { global: quux; } GLIBC_2.1.3;
   Then it will be coalesced into:
	GLIBC_2.1.3 { global: foo; bar; baz; };
	GLIBC_2.2 { global: quux; } GLIBC_2.1;
   Having fewer version sets reduces the size of the dynamic information
   and the overhead for startup and symbol resolution in the dynamic linker.

2. The generated file abi-versions.h defines macros that are used
   by the convenience macros in <shlib-compat.h> to make it easy
   to elide obsolete compatibility code.  Code can be conditionalized like:
	#include <shlib-compat.h>
	#if SHLIB_VERSION (libfoo, GLIBC_2_0)
	...
	symbol_version (old_foo, foo, GLIBC_2.0)
	#endif
   There is also a convenient macro for defining the new-versioned name
   with the appropriate version set name:
	versioned_symbol (libc, new_foo, foo, GLIBC_2_1)
   This will define the default version for `foo' as GLIBC_2.1 if that
   version set is being supported, but in the example above it would
   define in the GLIBC_2.1.3 version set instead.  This macro also
   conveniently just does the normal (weak) alias when not compiling a
   versioned shared library.


Before the patches to implement these infrastructure changes, here are a
couple of sample patches showing how existing code would be changed to use
the new facilities:

Index: libio/fileops.c
===================================================================
RCS file: /cvs/glibc/libc/libio/fileops.c,v
retrieving revision 1.47
diff -u -r1.47 fileops.c
--- libio/fileops.c	2000/01/26 06:48:22	1.47
+++ libio/fileops.c	2000/03/19 06:12:53
@@ -1014,35 +1014,16 @@
   JUMP_INIT(imbue, _IO_default_imbue)
 };
 
-
-#if defined PIC && DO_VERSIONING
-default_symbol_version (_IO_new_do_write, _IO_do_write, GLIBC_2.1);
-default_symbol_version (_IO_new_file_attach, _IO_file_attach, GLIBC_2.1);
-default_symbol_version (_IO_new_file_close_it, _IO_file_close_it, GLIBC_2.1);
-default_symbol_version (_IO_new_file_finish, _IO_file_finish, GLIBC_2.1);
-default_symbol_version (_IO_new_file_fopen, _IO_file_fopen, GLIBC_2.1);
-default_symbol_version (_IO_new_file_init, _IO_file_init, GLIBC_2.1);
-default_symbol_version (_IO_new_file_setbuf, _IO_file_setbuf, GLIBC_2.1);
-default_symbol_version (_IO_new_file_sync, _IO_file_sync, GLIBC_2.1);
-default_symbol_version (_IO_new_file_overflow, _IO_file_overflow, GLIBC_2.1);
-default_symbol_version (_IO_new_file_seekoff, _IO_file_seekoff, GLIBC_2.1);
-default_symbol_version (_IO_new_file_underflow, _IO_file_underflow, GLIBC_2.1);
-default_symbol_version (_IO_new_file_write, _IO_file_write, GLIBC_2.1);
-default_symbol_version (_IO_new_file_xsputn, _IO_file_xsputn, GLIBC_2.1);
-#else
-# ifdef strong_alias
-strong_alias (_IO_new_do_write, _IO_do_write);
-strong_alias (_IO_new_file_attach, _IO_file_attach);
-strong_alias (_IO_new_file_close_it, _IO_file_close_it);
-strong_alias (_IO_new_file_finish, _IO_file_finish);
-strong_alias (_IO_new_file_fopen, _IO_file_fopen);
-strong_alias (_IO_new_file_init, _IO_file_init);
-strong_alias (_IO_new_file_setbuf, _IO_file_setbuf);
-strong_alias (_IO_new_file_sync, _IO_file_sync);
-strong_alias (_IO_new_file_overflow, _IO_file_overflow);
-strong_alias (_IO_new_file_seekoff, _IO_file_seekoff);
-strong_alias (_IO_new_file_underflow, _IO_file_underflow);
-strong_alias (_IO_new_file_write, _IO_file_write);
-strong_alias (_IO_new_file_xsputn, _IO_file_xsputn);
-# endif
-#endif
+versioned_symbol (libc, _IO_new_do_write, _IO_do_write, GLIBC_2_1);
+versioned_symbol (libc, _IO_new_file_attach, _IO_file_attach, GLIBC_2_1);
+versioned_symbol (libc, _IO_new_file_close_it, _IO_file_close_it, GLIBC_2_1);
+versioned_symbol (libc, _IO_new_file_finish, _IO_file_finish, GLIBC_2_1);
+versioned_symbol (libc, _IO_new_file_fopen, _IO_file_fopen, GLIBC_2_1);
+versioned_symbol (libc, _IO_new_file_init, _IO_file_init, GLIBC_2_1);
+versioned_symbol (libc, _IO_new_file_setbuf, _IO_file_setbuf, GLIBC_2_1);
+versioned_symbol (libc, _IO_new_file_sync, _IO_file_sync, GLIBC_2_1);
+versioned_symbol (libc, _IO_new_file_overflow, _IO_file_overflow, GLIBC_2_1);
+versioned_symbol (libc, _IO_new_file_seekoff, _IO_file_seekoff, GLIBC_2_1);
+versioned_symbol (libc, _IO_new_file_underflow, _IO_file_underflow, GLIBC_2_1);
+versioned_symbol (libc, _IO_new_file_write, _IO_file_write, GLIBC_2_1);
+versioned_symbol (libc, _IO_new_file_xsputn, _IO_file_xsputn, GLIBC_2_1);
Index: libio/iofclose.c
===================================================================
RCS file: /cvs/glibc/libc/libio/iofclose.c,v
retrieving revision 1.14
diff -u -r1.14 iofclose.c
--- libio/iofclose.c	2000/01/26 06:50:05	1.14
+++ libio/iofclose.c	2000/03/19 06:12:53
@@ -86,13 +86,5 @@
   return status;
 }
 
-#if defined PIC && DO_VERSIONING
-strong_alias (_IO_new_fclose, __new_fclose)
-default_symbol_version (_IO_new_fclose, _IO_fclose, GLIBC_2.1);
-default_symbol_version (__new_fclose, fclose, GLIBC_2.1);
-#else
-# ifdef weak_alias
-weak_alias (_IO_new_fclose, _IO_fclose)
-weak_alias (_IO_new_fclose, fclose)
-# endif
-#endif
+versioned_symbol (libc, _IO_new_fclose, _IO_fclose, GLIBC_2_1);
+versioned_symbol (libc, _IO_new_fclose, fclose, GLIBC_2_1);
Index: libio/oldiofclose.c
===================================================================
RCS file: /cvs/glibc/libc/libio/oldiofclose.c,v
retrieving revision 1.5
diff -u -r1.5 oldiofclose.c
--- libio/oldiofclose.c	1999/07/26 02:36:48	1.5
+++ libio/oldiofclose.c	2000/03/19 06:12:53
@@ -23,6 +23,9 @@
    other reasons why the executable file might be covered by the GNU
    General Public License.  */
 
+#include <shlib-compat.h>
+#if SHLIB_COMPAT (libc, GLIBC_2_0)
+
 #define _IO_USE_OLD_IO_FILE
 #include "libioP.h"
 #ifdef __STDC__
@@ -66,3 +69,5 @@
 strong_alias (_IO_old_fclose, __old_fclose)
 symbol_version (_IO_old_fclose, _IO_fclose, GLIBC_2.0);
 symbol_version (__old_fclose, fclose, GLIBC_2.0);
+
+#endif
Index: linuxthreads/pthread.c
===================================================================
RCS file: /cvs/glibc/libc/linuxthreads/pthread.c,v
retrieving revision 1.34
diff -u -r1.34 pthread.c
--- linuxthreads/pthread.c	2000/03/14 16:27:48	1.34
+++ linuxthreads/pthread.c	2000/03/19 06:12:53
@@ -506,8 +506,11 @@
   return THREAD_GETMEM(self, p_retcode);
 }
 
+versioned_symbol (libpthread, __pthread_create_2_1, pthread_create, GLIBC_2_1);
+
 #if defined HAVE_ELF && defined PIC && defined DO_VERSIONING
 default_symbol_version (__pthread_create_2_1, pthread_create, GLIBC_2.1);
+#if SHLIB_VERSION (libpthread, GLIBC_2_0)
 
 int __pthread_create_2_0(pthread_t *thread, const pthread_attr_t *attr,
 			 void * (*start_routine)(void *), void *arg)
@@ -531,9 +534,6 @@
     }
   return __pthread_create_2_1 (thread, attr, start_routine, arg);
 }
-symbol_version (__pthread_create_2_0, pthread_create, GLIBC_2.0);
-#else
-strong_alias (__pthread_create_2_1, pthread_create)
 #endif
 
 /* Simple operations on thread identifiers */


I haven't bothered making all these kinds of changes to existing code,
until people agree on this approach.  If and when these infrastructure
changes get checked in, then someone (perhaps even someone other than me)
can go through all the uses of symbol_version and default_symbol_version
and convert them to use the new style.

Now, the actual patches.  (If you like this, I can check it in directly.)


2000-03-19  Roland McGrath  <roland@baalperazim.frob.com>

	* shlib-versions [USE_IN_LIBIO] (.*-.*-gnu-gnu*): Set earliest
	supported version for libc 0.2.90.libio to GLIBC_2.2.

	* Makeconfig (soversions.mk): Grok new third column in shlib-versions,
	and use it to emit new variable `map-firstversions'.
	* Makerules (Versions.all): Use scripts/firstversions.awk and
	the $(map-firstversions) value to generate a modified versions list
	that includes renames in "A = B" syntax for each version set earlier
	than the "earliest symbol version" named in shlib-versions.
	* scripts/versions.awk: Recognize "A = B" lines in the input to mean
	rename version set A to B in the output to the intermediate file.
	* scripts/abi-versions.awk: New file.
	* Makerules (abi-versions.h): New target, generated by that script.
	[$(versioning) = yes] (before-compile): Prepend abi-versions.h.
	* include/shlib-compat.h: New file, uses that generated header.

Index: Makeconfig
===================================================================
RCS file: /cvs/glibc/libc/Makeconfig,v
retrieving revision 1.225
diff -u -r1.225 Makeconfig
--- Makeconfig	2000/03/19 00:32:47	1.225
+++ Makeconfig	2000/03/19 07:10:28
@@ -687,23 +687,23 @@
 	 for f in $$file; do \
 	   sed 's/#.*$$//;s/^[ 	]*%/#/' $$f \
 	   | $(CC) -include $(common-objpfx)config.h -E -x c - \
-	   | while read conf versions; do \
-	     test -n "$$versions" && \
+	   | while read conf version setname; do \
+	     test -n "$$version" && \
 	     test `expr '$(config-machine)-$(config-vendor)-$(config-os)' \
 			: "$$conf"` != 0 || continue; \
-	     for v in $$versions; do \
-	       lib=`echo $$v | sed 's/=.*$$//'`; \
-	       if eval "test -z \"\$$vers_lib$$lib\""; then \
-		 eval vers_lib$${lib}=yes; \
-		 number=`echo $$v | sed "s/^.*=//"`; \
-		 case $$number in \
-		   [0-9]*) echo "$$lib.so-version=.$$number"; \
-			   echo "all-sonames+=$$lib.so\$$($$lib.so-version)";;\
-		   *) echo "$$lib.so-version=$$number"; \
-		      echo "all-sonames+=\$$($$lib.so-version)";;  \
-	         esac; \
-	       fi; \
-	     done; \
+	       lib=`echo $$version | sed 's/=.*$$//'`; \
+	     if eval "test -z \"\$$versioners_lib$$lib\""; then \
+	       eval vers_lib$${lib}=yes; \
+	       number=`echo $$version | sed "s/^.*=//"`; \
+	       case $$number in \
+		 [0-9]*) echo "$$lib.so-version=.$$number"; \
+			 echo "all-sonames+=$$lib.so\$$($$lib.so-version)";;\
+		 *) echo "$$lib.so-version=$$number"; \
+		    echo "all-sonames+=\$$($$lib.so-version)";;  \
+	       esac; \
+	       test -z "$$setname" || \
+	         echo "map-firstversions+=$${lib}:$${setname}"; \
+	     fi; \
 	   done; \
 	 done;) > $@T; exit 0
 	mv -f $@T $@
Index: Makerules
===================================================================
RCS file: /cvs/glibc/libc/Makerules,v
retrieving revision 1.325
diff -u -r1.325 Makerules
--- Makerules	1999/07/31 06:01:33	1.325
+++ Makerules	2000/03/19 07:10:28
@@ -1,4 +1,5 @@
-# Copyright (C) 1991,92,93,94,95,96,97,98,99 Free Software Foundation, Inc.
+# Copyright (C) 1991,92,93,94,95,96,97,98,99,2000
+#	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
@@ -128,6 +129,18 @@
 		  $(filter-out $(common-objpfx)mach% $(common-objpfx)hurd%,\
 			       $(before-compile))
 
+# Even before that, we need abi-versions.h which is generated right here.
+ifeq ($(versioning),yes)
+ifndef avoid-generated
+before-compile := $(common-objpfx)abi-versions.h $(before-compile)
+$(common-objpfx)abi-versions.h: $(..)scripts/abi-versions.awk \
+				$(common-objpfx)Versions.all
+	LC_ALL=C $(AWK) -f $^ > $@T
+	mv -f $@T $@
+endif # avoid-generated
+endif # $(versioning) = yes
+
+
 # Remove existing files from `before-compile'.  Things are added there when
 # they must exist for dependency generation to work right, but once they
 # exist there is no further need for every single file to depend on them,
@@ -277,9 +290,15 @@
 sysd-versions-force = FORCE
 FORCE:
 endif
-$(common-objpfx)Versions.all: $(..)Versions.def \
-			      $(wildcard $(add-ons:%=$(..)%/Versions.def))
-	cat $^ > $@T
+$(common-objpfx)Versions.all: $(..)scripts/firstversions.awk \
+	                      $(..)Versions.def \
+			      $(wildcard $(add-ons:%=$(..)%/Versions.def)) \
+			      $(common-objpfx)soversions.mk
+	{ for map in $(map-firstversions); do \
+	    echo $$map; \
+	  done | sed 's/:/ : /'; \
+	  cat $(filter-out $< $(common-objpfx)soversions.mk,$^); \
+	} | LC_ALL=C $(AWK) -f $< > $@T
 	mv -f $@T $@
 $(common-objpfx)sysd-versions: $(common-objpfx)Versions.all \
 			       $(..)scripts/versions.awk \
@@ -295,6 +314,7 @@
 endif # avoid-generated
 endif # $(versioning) = yes
 endif # sysd-dirs-done
+
 
 ifndef compile-command.S
 compile-command.S = $(compile.S) $(OUTPUT_OPTION)
Index: shlib-versions
===================================================================
RCS file: /cvs/glibc/libc/shlib-versions,v
retrieving revision 1.39
diff -u -r1.39 shlib-versions
--- shlib-versions	2000/03/19 00:32:47	1.39
+++ shlib-versions	2000/03/19 07:10:29
@@ -8,8 +8,8 @@
 # This file can use cpp-style conditionals starting with % instead of #
 # to test the symbols defined in config.h by configure.
 
-# Configuration		Library versions
-# -------------		------- --------
+# Configuration		Library=version		Earliest symbol set (optional)
+# -------------		---------------		------------------------------
 
 # The interface to -lm depends mostly only on cpu, not on operating system.
 i.86-.*-.*		libm=6
@@ -33,7 +33,7 @@
 
 # libc.so.0.2 is for the Hurd alpha release 0.2.
 %ifdef USE_IN_LIBIO /* experimental only! */
-.*-.*-gnu-gnu*		libc=0.2.90.libio
+.*-.*-gnu-gnu*		libc=0.2.90.libio	GLIBC_2.2
 %else
 .*-.*-gnu-gnu*		libc=0.2
 %endif
Index: include/shlib-compat.h
===================================================================
RCS file: shlib-compat.h
diff -N shlib-compat.h
--- /dev/null	Tue May  5 13:32:27 1998
+++ include/shlib-compat.h	Sat Mar 18 23:10:29 2000
@@ -0,0 +1,61 @@
+/* Macros for managing ABI-compatibility definitions using ELF symbol versions.
+   Copyright (C) 2000 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 Library General Public License as
+   published by the Free Software Foundation; either version 2 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#ifndef _SHLIB_COMPAT_H
+#define _SHLIB_COMPAT_H	1
+
+#if defined HAVE_ELF && defined PIC && defined DO_VERSIONING
+
+#include <abi-versions.h>	/* header generated by abi-versions.awk */
+
+/* The file abi-versions.h (generated by scripts/abi-versions.awk) defines
+   symbols like `ABI_libm_GLIBC_2_0' to either 1 or 0 indicating whether or
+   not we want to build binary compatibility for e.g. the GLIBC_2.0 version
+   set into the libm shared object.  If this evaluates to zero, then there
+   is no need to compile in extra code to support this version set where it
+   has been superseded by a newer version.  The compatibility code should
+   be conditionalized with `#if SHLIB_COMPAT (libm, GLIBC_2_0)'.  */
+
+#define SHLIB_COMPAT(lib, version)	ABI_##lib##_##version
+
+/* 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) \
+  default_symbol_version (local, symbol, VERSION_##lib##_##version)
+
+#else
+
+/* Not compiling ELF shared libraries at all, so never any old versions.  */
+#define SHLIB_COMPAT(lib, version)	0
+
+/* No versions to worry about, just make this the global definition.  */
+#define versioned_symbol(lib, local, symbol, version) \
+  weak_alias (local, symbol)
+
+#endif
+
+
+#endif	/* shlib-compat.h */
Index: scripts/abi-versions.awk
===================================================================
RCS file: abi-versions.awk
diff -N abi-versions.awk
--- /dev/null	Tue May  5 13:32:27 1998
+++ scripts/abi-versions.awk	Sat Mar 18 23:10:35 2000
@@ -0,0 +1,39 @@
+# Script to generate <abi-versions.h> header file from Versions.all list.
+# See include/shlib-compat.h comments for explanation.
+
+BEGIN {
+  print "/* This file is automatically generated by abi-versions.awk.";
+  print "   It defines symbols used by shlib-compat.h, which see.  */";
+  print "\n#ifndef _ABI_VERSIONS_H\n#define _ABI_VERSIONS_H";
+}
+
+NF == 2 && $2 == "{" {
+  thislib = $1;
+  gsub(/[^A-Za-z0-9_ 	]/, "_"); libid = $1;
+  printf "\n/* start %s */\n", thislib;
+  next;
+}
+$1 == "}" {
+  printf "/* end %s */\n", thislib;
+  next;
+}
+
+$2 == "=" {
+  new = $3;
+  gsub(/[^A-Za-z0-9_ 	]/, "_"); id = $1;
+  printf "#define ABI_%s_%s\t0\t/* earliest supported %s */\n", libid, id, new;
+  printf "#define VERSION_%s_%s\t%s\n", libid, id, new;
+  next;
+}
+
+{
+  vers = $1;
+  gsub(/[^A-Za-z0-9_ 	]/, "_"); id = $1;
+  printf "#define ABI_%s_%s\t1\t/* support %s */\n", libid, id, vers;
+  printf "#define VERSION_%s_%s\t%s\n", libid, id, vers;
+  next;
+}
+
+END {
+  print "\n#endif /* abi-versions.h */";
+}
Index: scripts/firstversions.awk
===================================================================
RCS file: firstversions.awk
diff -N firstversions.awk
--- /dev/null	Tue May  5 13:32:27 1998
+++ scripts/firstversions.awk	Sat Mar 18 23:10:35 2000
@@ -0,0 +1,27 @@
+# Script to preprocess Versions.all lists based on "earliest version"
+# specifications in the shlib-versions file.
+
+NF == 3 && $2 == ":" { firstversion[$1] = $3; next }
+
+NF == 2 && $2 == "{" { thislib = $1; print; next }
+
+$1 == "}" {
+  if (firstversion[thislib]) {
+    # We haven't seen the stated version, but have produced
+    # others pointing to it, so we synthesize it now.
+    printf "  %s\n", firstversion[thislib];
+  }
+  print;
+  next;
+}
+
+{
+  if (! firstversion[thislib])
+    print;
+  else if ($1 == firstversion[thislib]) {
+    print;
+    firstversion[thislib] = 0;
+  }
+  else
+    print $1, "=", firstversion[thislib];
+}
Index: scripts/versions.awk
===================================================================
RCS file: /cvs/glibc/libc/scripts/versions.awk,v
retrieving revision 1.5
diff -u -r1.5 versions.awk
--- scripts/versions.awk	1999/11/15 06:59:51	1.5
+++ scripts/versions.awk	2000/03/19 07:10:36
@@ -16,7 +16,10 @@
       libs[$1] = 1;
       curlib = $1;
       while (getline < defsfile && ! /^}/) {
-	versions[$1] = 1;
+        if ($2 == "=")
+	  renamed[$1] = $3;
+	else
+	  versions[$1] = 1;
       }
     }
   }
@@ -33,6 +36,7 @@
 
 # This matches the beginning of the version information for a new library.
 /^[a-zA-Z0-9_.]+/ {
+  delete renamed;
   actlib = $1;
   if (!libs[$1]) {
     printf("no versions defined for %s\n", $1) > "/dev/stderr";
@@ -43,11 +47,14 @@
 
 # This matches the beginning of a new version for the current library.
 /^  [A-Za-z_]/ {
-  actver = $1;
-  if (!versions[$1]) {
+  if (renamed[$1])
+    actver = renamed[$1];
+  else if (!versions[$1]) {
     printf("version %s not defined\n", $1) > "/dev/stderr";
     exit 1;
   }
+  else
+    actver = $1;
   next;
 }
 

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]