ld to library without version information in Linux

When compiling a C/C++ program, is there a way to link to library exports in Linux without having ld embed the version information?

I am building a shared object that links to libxul.so in Firefox (it's a Firefox binary extension). I want to build my shared object and link to libxul.so so that at runtime, the loader does not care what version libxul.so is.

Right now, my output .so file has the following dependency:

readelf -V myext.so
  0x0080: Version: 1  File: libxul.so  Cnt: 1
  0x0090:   Name: xul24.0  Flags: none  Version: 9

(Note that it depends on the version 'xul24.0')

The exported functions do not change between Firefox versions. So, I want to remove this version instruction.

When attempting to load into Firefox 26, LD_DEBUG=file provides the following error:

/usr/lib/firefox/libxul.so: error: version lookup error:
version `xul24.0' not found (required by /.../myext.so) (fatal)

In the case of the Firefox 26 version of libxul.so, the version is 'xul26'.

So, how can I prevent ld from embedding version information into my library?

Upvotes: 5

Views: 3468

Answers (2)

I was able to build the library as-desired, without the version information.

What I ended up doing was creating a dummy library with the appropriate exported symbols--without version (or soname) information--and linking to that dummy library while compiling and linking my library. At runtime, the loader loads the real library (libxul.so) without failing based on the versioning problems since my library does not contain version information for the real library. To figure out which exported symbols I needed, I first linked to the real libxul.so, then used readelf --dyn-syms to determine dwhich symbols were actually needed.

Where ARCH is 32 or 64 (for 32-bit or 64-bit compilation): gcc -o $ARCH/libxul.so -fPIC -shared -DM$ARCH -m$ARCH xulstubs.c

xulstubs.c:

/*
 2: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND NS_Realloc@xul26 (2)
 3: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND NS_UTF16ToCString@xul26 (2)
 7: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND NS_CStringCloneData@xul26 (2)
13: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND NS_GetMemoryManager@xul26 (2)
...
 */
void NS_Realloc() {}
void NS_UTF16ToCString() {}
void NS_CStringCloneData() {}
void NS_GetMemoryManager() {}
/* ... etc. ... */

Now, the imported symbols from my library do not have the version appendage:

23: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND NS_Realloc
29: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND NS_UTF16ToCString
33: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND NS_CStringCloneData
37: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND NS_GetMemoryManager

and it runs as desired.

I would still like a more “elegant” solution, one that does not require creating a dummy library. If this is the only workaround using the standard toolchain, however, I can live with it.

Upvotes: 2

Netch
Netch

Reputation: 4572

Provided that the most widespread GNU toolchain is considered, you can load any shared library without "lib" prefix and version suffix, but only directly using dlopen() from a code. Automatic loading during binary file start or another library loading won't work because, linker will find a library file, read its "soname" and "rpath" attributes from the library headers and apply them to find the library during the built target load. Soname is more important for the linker than the library file name. More so, at some systems, ldconfig can rename libraries in public search paths to match soname recorded inside (I've seen such on Linux).

A small example. Consider a file t.c where main() prints a value from library function and a library source file tt.c where the function returns a constant. Make them:

$ make clean; make
rm -f t t.o libtt.so tt.o
gcc -o libtt.so -shared -fPIC tt.c
gcc -o t t.c -L. -ltt

With these commands, ./t will fail because libtt.so isn't in public search paths, so LD_LIBRARY_PATH is needed to start it. But we can fix this:

$ gcc -o libtt.so -shared -fPIC tt.c
$ gcc -o t t.c -L. -ltt -Wl,-rpath=`pwd`
$ ldd ./t
./t:
        libtt.so => /var2/homes/netch/prog/tests/soname/libtt.so (0x80081a000)
        libc.so.7 => /lib/libc.so.7 (0x800a1b000)

But, if I request another library name during its building, the search will fail:

$ gcc -o libtt.so -shared -fPIC tt.c -Wl,-soname=libtt2.so
$ gcc -o t t.c -L. -ltt -Wl,-rpath=`pwd`
$ ldd ./t
./t:
        libtt2.so => not found (0)
        libc.so.7 => /lib/libc.so.7 (0x80081a000)

Note that, without seeing libtt.so, linker will fail because it doesn't know where to find unresolved links.

All this works for names other style than the described one, despite linker shows something strange:

$ gcc -o lybtt.so -shared -fPIC tt.c
$ gcc -o t t.c lybtt.so -Wl,-rpath=`pwd`
$ ./t
233
$ ldd ./t
./t:
        lybtt.so (0x80081a000)
        libc.so.7 => /lib/libc.so.7 (0x800a1b000)

If you are library builder, you can name it without version and all tools will recognize this. But this is usually considered the bad style and is banned in recommendations. All libraries in public search paths are declared to have versions in their names, and this is strictly recommended for any software packaged not for home use.

For your question, if you really need to shoot yourself in foot, you can copy the libxul, change soname in it and put into the proper search path again. But I doubt that's what you want.

Upvotes: 0

Related Questions