bin/140143: dlopen doesn't promote RTLD_GLOBAL for linked libraries

David Naylor naylor.b.david at gmail.com
Sat Oct 31 15:40:02 UTC 2009


>Number:         140143
>Category:       bin
>Synopsis:       dlopen doesn't promote RTLD_GLOBAL for linked libraries
>Confidential:   no
>Severity:       serious
>Priority:       low
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Sat Oct 31 15:40:01 UTC 2009
>Closed-Date:
>Last-Modified:
>Originator:     David Naylor
>Release:        FreeBSD 8 RC2
>Organization:
Private
>Environment:
FreeBSD dragon.dg 8.0-RC2 FreeBSD 8.0-RC2 #0: Sat Oct 31 11:30:28 SAST 2009     root at dragon.dg:/tmp/usr/src/sys/GENERIC  i386
>Description:
If a shared library is linked to another than any attempts from the first library to promote the symbols of the second library to RTLD_GLOBAL does not work [via dlopen("libsecond.so", RTLD_NOLOAD|RTLD_GLOBAL), or using RTLD_LAZY or RTLD_NOW].

This is known to affect devel/kdebindings4-python-pykde4.

This is not a problem under Linux (i.e. linux exhibits expected behaviour).  
>How-To-Repeat:
[skip to bottom for coded example]

Have a shared library that is linked (at compile time) to a second library then have the second library load a third that requires symbols from the second but does not declare its dependency on the second library [e.g. libpython and lib/python26/lib-dynload/*].  

If the second library is loaded (without being linked) with RTLD_GLOBAL then all is well.  

See attached for rtld_global.shar.  Extract content and run:
# make  *
# make -DTRIGGER **

This runs two tests.  The first test is the first library that dlopen's the second library (it should run fine).  The second test is the first library that is linked to the second library and attempts to promote the second libraries symbols to RTLD_GLOBAL.  See below:

* Runs with work-around (see Fix below).  This works
** Runs without work-around [aka dlopen("libmaster.so", RTLD_NOW | RTLD_GLOBAL)] with 'libmaster.so' being the second library.  This does not work.
>Fix:
There is only a work-around that may not work in all situations:
Promote the first libraries symbols to RTLD_GLOBAL.  This, however, I think is overly broad and deviates from established behaviour (under Linux).  

In the example attached this amounts to:
dlopen("libplugin.so", RTLD_NOLOAD | RTLD_GLOBAL)
where libplugin.so is the first library.  

Patch attached with submission follows:

# This is a shell archive.  Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file".  Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
#	rtld_global
#	rtld_global/Makefile
#	rtld_global/libmaster.c
#	rtld_global/main.c
#	rtld_global/libslave.c
#	rtld_global/common.h
#	rtld_global/libplugin.c
#
echo c - rtld_global
mkdir -p rtld_global > /dev/null 2>&1
echo x - rtld_global/Makefile
sed 's/^X//' >rtld_global/Makefile << '54185eeffb995a36288ad17f38c2bdc0'
X.SUFFIXES: .a .c .o .so .so.1
X.SILENT:
X
XCFLAGS=-Wall -rdynamic
X
X.if defined(TRIGGER)
XCFLAGS+=-DTRIGGER
X.endif
X
XCC=cc
XAR=ar
XLN=ln
X
Xall:
X	echo "Running test without linking: "
X	make clean test #> /dev/null
X	echo "done"
X	echo
X	echo "Running test with linking: "
X	make clean test -DLINKED #> /dev/null
X	echo "done"
X
Xclean:
X	rm -f *.a *.o *.so* main
X
Xtest: main libmaster.so libslave.so
X	env LD_LIBRARY_PATH=${PWD} ./main
X
X.c.o:
X	${CC} ${CFLAGS} -c -fPIC -o $@ $<
X
X.c.so:
X	${CC} ${CFLAGS} -fPIC -shared -Wl,-soname,$@ -o $@ $<
X
X.if defined(LINKED)
Xlibplugin.so: libplugin.c libmaster.so
X	${CC} ${CFLAGS} -DLINKED -fPIC -L. -lmaster -shared -Wl,-soname,$@ -o $@ $<
X.endif
X
Xmain: main.o libplugin.so
X	cc ${CFLAGS} -o main main.o
54185eeffb995a36288ad17f38c2bdc0
echo x - rtld_global/libmaster.c
sed 's/^X//' >rtld_global/libmaster.c << '4c7e49ccff36dd33ab5357b611fe5d83'
X#include "common.h"
X
Xint master_func() {
X  void* libslave;
X  func_t func;
X
X  printf("libmaster: opening libslave:");
X  libslave = dlopen("libslave.so", RTLD_NOW);
X  STATUS_CHECK(libslave);
X
X  printf("libmaster: getting symbol 'libslave::slave_func':");
X  func = (func_t)dlfunc(libslave, "slave_func");
X  STATUS_CHECK(func);
X
X  printf("libmaster: calling 'libslave::slave_func'...\n");
X  return func();
X}
X
Xconst char* master_name() {
X  return "libmaster.1";
X}
4c7e49ccff36dd33ab5357b611fe5d83
echo x - rtld_global/main.c
sed 's/^X//' >rtld_global/main.c << 'cb6b43e30862d408f1380a58a3050ed8'
X#include "common.h"
X
Xint main(int argc, char** argv) {
X  void* libplugin;
X  func_t func;
X
X  printf("main: opening libplugin:");
X  libplugin = dlopen("libplugin.so", RTLD_LAZY /*| RTLD_GLOBAL*/);
X  STATUS_CHECK(libplugin);
X
X  printf("main: getting symbol 'libplugin::plugin_func':");
X  func = (func_t)dlfunc(libplugin, "plugin_func");
X  STATUS_CHECK(func);
X
X  printf("main: calling 'libplugin::plugin_func'...\n");
X  return func();
X}
cb6b43e30862d408f1380a58a3050ed8
echo x - rtld_global/libslave.c
sed 's/^X//' >rtld_global/libslave.c << '6027f0c5e11bc3f1d64c5398f3095d35'
X#include <stdio.h>
X
X/* Forward declaration from libmaster */
Xconst char* master_name();
X
Xint slave_func() {
X  printf("libslave: %s\n", master_name());
X  return 0;
X}
6027f0c5e11bc3f1d64c5398f3095d35
echo x - rtld_global/common.h
sed 's/^X//' >rtld_global/common.h << '96ded3809880f52ca04e85b8175d157f'
X#include <dlfcn.h>
X#include <stdio.h>
X
X#define STATUS_CHECK(var)                                                     \
X  if ((var) == NULL) {                                                          \
X    printf(" failure (%s)\n", dlerror());                                     \
X    return 1;                                                                 \
X  } else                                                                      \
X    printf(" success\n")
X
Xtypedef int (*func_t)();
96ded3809880f52ca04e85b8175d157f
echo x - rtld_global/libplugin.c
sed 's/^X//' >rtld_global/libplugin.c << 'a806cc4670bd702634b1d6e2d06150e5'
X#include "common.h"
X
X#ifdef LINKED
Xint master_func();
X#endif
X
Xint plugin_func() {
X  func_t func;
X
X#if !defined(LINKED) || defined(TRIGGER)
X  void* libmaster;
X
X  printf("libplugin: opening libmaster:");
X  libmaster = dlopen("libmaster.so", RTLD_LAZY | RTLD_GLOBAL);
X  STATUS_CHECK(libmaster);
X#else
X  void* libplugin;
X
X  printf("libplugin: exposing symbols globally:");
X  libplugin = dlopen("libplugin.so", RTLD_NOLOAD | RTLD_GLOBAL);
X  STATUS_CHECK(libplugin);
X#endif
X
X#ifndef LINKED
X  printf("libplugin: getting symbol 'libmaster::master_func':");
X  func = (func_t)dlfunc(libmaster, "master_func");
X  STATUS_CHECK(func);
X#else
X  func = &master_func;
X#endif
X
X  printf("libplugin: calling 'libmaster::master_func'...\n");
X  return func();
X}
a806cc4670bd702634b1d6e2d06150e5
exit



>Release-Note:
>Audit-Trail:
>Unformatted:


More information about the freebsd-bugs mailing list