[Please CC any replies, thanks] NOTE: I am not requesting that this patch is integrated, only that it exists in the archive in case future problems emerge requiring it. It's essentially something that only works for Linux and select other OSes and the bug it fixes is somewhat outside the scope of the PostgreSQL community (see below).
This patch: a) When building a shared library on Linux and an exports.txt file exists, it will generate an appropriate version script for ld to limit the symbols exported. b) Additionally, if SHLIB_VERSION is defined in the Makefile, the symbols of the library will be versioned with that version. The net effect is that the exported symbol list from libpq (the only lib with an exports file) drops from 216 symbols to the 123 listed as exports. The bug I'm referring to is somewhat theoretical in that I don't think anyone's run into it, but you never know. Say someone starts with Debian Unstable today and installs postgresql-8.0 and libnss-pgsql1. The former is linked against libpq4, the latter against libpq3 but they have largely the same symbols. The user then configures libnss-pgsql for hostname lookups and starts psql to connect to a remote server. psql then does a gethostbyname which goes via NSS to dlopen libnss-pgsql and its symbols will be linked against libpq4 because they're already loaded. Any remaining symbols will be resolved against libpq3. Finally, any global symbols in libpq3 that have a symbol of the same name in libpq4 will be resolved against the version in libpq4. I don't think I need to explain that this situation may have undefined results. Note: rpath won't save you hare since that applies to finding libraries, but doesn't affect symbol lookup order. The above will also apply if above user compiles their own version of postgresql but not libnss-pgsql. Symbol versioning solves this in that each library and program will link to the version it expects. This patch paves the way. And if the user deletes the other library in an attempt to make it work, they get a helpful message like: ./psql: $PATH/libpq.so.4: version `LIBPQ_4.1' not found (required by ./psql) Note, this is the only reason I would expect it to be adopted by core, for debugging purposes. The only time versioning may cause incompatabilities is if different distributions start versioning the same library differently. Unversioned binaries work against versioned libs and vice-versa. Hence, we should at least get straight the tags. I suggest: LIBPQ_$(SO_MAJOR_VERSION).$(SO_MINOR_VERSION) eg: LIBPQ_4.1 This is in line with how other libraries do it (eg GLIBC_2.0) and provides maximum differentiation. It's not like we're trying to provide any kind of backward compatability at that level. This patch will work on any system using GNU ld and where the system dynamic linker supports versioned symbols. The only one I'm sure of is Linux but others are easily added. Thanks for your attention, -- Martijn van Oosterhout <kleptog@svana.org> http://svana.org/kleptog/ > Patent. n. Genius is 5% inspiration and 95% perspiration. A patent is a > tool for doing 5% of the work and then sitting around waiting for someone > else to do the other 95% so you can sue them.
Index: src/Makefile.shlib =================================================================== RCS file: /projects/cvsroot/pgsql/src/Makefile.shlib,v retrieving revision 1.97 diff -u -r1.97 Makefile.shlib --- src/Makefile.shlib 8 Aug 2005 03:35:13 -0000 1.97 +++ src/Makefile.shlib 18 Oct 2005 17:54:10 -0000 @@ -184,6 +184,12 @@ ifeq ($(PORTNAME), linux) LINK.shared = $(COMPILER) -shared -Wl,-soname,$(soname) + + ifeq ($(wildcard exports.txt),exports.txt) + LINK.shared += -Wl,--version-script,exports.gnuld + EXPORTS_FILE = exports.gnuld + endif + endif ifeq ($(PORTNAME), solaris) @@ -255,6 +261,12 @@ endif # enable_shared +# If you wish to version the symbols, just define SHLIB_VERSION to be +# something like LIBPQ_4.1 in the Makefile for that library. Otherwise the +# symbols will be unversioned (as per default). Produced version script is +# for GNU ld only and is currently created only on Linux. +exports.gnuld: exports.txt + awk 'BEGIN { print "$(SHLIB_VERSION) { global: " } { if( $$1 != "#" ) {print $$1,";"} } END { print "local: *; };" }' < $< > $@ ## @@ -291,7 +303,7 @@ ifneq ($(PORTNAME), aix) # Normal case -$(shlib): $(OBJS) +$(shlib): $(OBJS) $(EXPORTS_FILE) $(LINK.shared) $(LDFLAGS_SL) $(OBJS) $(SHLIB_LINK) -o $@ # If we're using major and minor versions, then make a symlink to major-version-only. ifneq ($(shlib), $(shlib_major)) @@ -411,7 +423,7 @@ clean-lib: rm -f lib$(NAME).a ifeq ($(enable_shared), yes) - rm -f $(shlib_bare) $(shlib_major) $(shlib) + rm -f $(shlib_bare) $(shlib_major) $(shlib) $(EXPORTS_FILE) ifdef EXPSUFF rm -f lib$(NAME)$(EXPSUFF) endif
pgppAeQeyPWSU.pgp
Description: PGP signature