How to Avoid Having Version Numbers in .So File Name

How to avoid having version numbers in .so file name

What you are looking for is making a plugin.

Add CONFIG += plugin to your project file, and qmake will generate a Makefile that builds a libFoo.so file, without the numbered links

Git: Any way to ignore files that have gotten version number changed only

This is not incredibly robust, but maybe good enough. In .git/config put:

[diff "remove-rev"]
textconv=sed '/IP.Board v[0-9]*\\.[0-9]*\\.[0-9]*/d'

And then in .gitattributes or .git/info/attributes, put:

* diff=remove-rev

Now, whenever git-diff is run, the sed script will be applied to the file before gitdiffcore decides which file pairs differ. You may need to tweek the sed command to suit your needs, but this should help.

Difference between version number, minor number and release number

Each time you release the library, it should arguably have a different version number. However, some releases only make changes to the internal workings of the library without affecting users at all other than by fixing bugs. Other releases may also add new functions to the library, but the interface details of all the existing functions are the same as before, so software written to use an older version of the library will continue to work with the new version. Other changes may break backwards compatibility; a function interface changes, a structure changes size, or a function is dropped (or a global variable — perish the thought — changes, etc).

The 'bug fix only' versions might not bother with renumbering the library, but if you previously had liberror.so.1.0.2, the new version might be liberror.so.1.0.3, a change in the release number.

The 'additional features' versions should be given a new minor number, so the new version after liberror.so.1.0.2 might be liberror.so.1.1.0.

If you break compatibility, then you use a new version number, so the new version after liberror.so.1.0.2 might be liberror.so.2.0.0.

Code built to use liberror.so.1.0.2 can and will use either liberror.so.1.0.3 or liberror-1.1.0 without problem, but will not attempt to use liberror.so.2.0.0 or later versions.


What code (in a GNU binutils stack, for example) controls which versions will be linked to, and is this behavior fixed or overridable?

Good question. This is my understanding, but I might have some details wrong (in which case, someone will probably point out the error of my ways). The theory above is nice an easy; this is a little less easy.

You may have noticed that there are 'development' packages for libraries as well as the 'standard' versions of the libraries. The difference between them is part of the explanation.

If you're an ordinary end user who is not writing programs using a library but simply running programs that someone else has written, then you typically end up with one file and one symlink in the installation directory. Continuing with the hypothetical liberror.so.1.0.2 example (installed in /usr/local/lib), you would find in the base release:

liberror.so.1.0.2   — the real shared object
liberror.so.1 — symlink to the the real shared object

If you installed the development version, you'd probably find some header files in /usr/local/include, some man pages (perhaps in /usr/local/man, perhaps in /usr/share instead), and an extra symlink:

liberror.so         — another symlink, either to liberror.so.1 or to liberror.so.1.0.2

When the program using it is compiled, you might specify:

gcc -I/usr/local/include usererror.c -o usererror -L/usr/local/lib -lerror

This will link with the name liberror.so, but reading the metadata from the liberror.so.1.0.2 file, it would know that the version to use is liberror.so.1.0.2 or later (but not liberror.so.2.0.0 or later).

Now let's suppose you upgrade the installation to liberror.so.2.0.0. You now have files:

liberror.so.1.0.2   — the real shared object
liberror.so.1 — symlink to the the real shared object
liberror.so.2.0.0 — the real shared object
liberror.so.2 — symlink to the the real shared object
liberror.so — another symlink, either to liberror.so.2 or to liberror.so.2.0.0

Old code built to use liberror.so.1 still runs using that library. New code built to use liberror.so.2 also runs using the new library. And at link time, new programs pick up liberror.so.2.0.0 via the symlink liberror.so.

You can control it so that the default on your system is still liberror.so.1 by adjusting the liberror.so symlink to point to liberror.so.1.0.2. The only tricky part is making sure that the correct versions of the headers are available for compilation. It is a bad idea to build with the headers for liberror.so.2 and link with liberror.so.1 because the one thing you know for sure is that the interfaces are different!


Some raw data from a Red Hat Enterprise Linux 5 (RHEL5) x86_64 machine.

$ cd /lib64
$ ls libc*
-rwxr-xr-x 1 root root 1713088 2009-01-05 16:32 libc-2.5.so
lrwxrwxrwx 1 root root 11 2012-02-22 15:05 libcap.so -> libcap.so.1
lrwxrwxrwx 1 root root 14 2012-02-22 15:05 libcap.so.1 -> libcap.so.1.10
-rwxr-xr-x 1 root root 17384 2006-11-14 01:36 libcap.so.1.10
-rwxr-xr-x 1 root root 197744 2009-01-05 16:32 libcidn-2.5.so
lrwxrwxrwx 1 root root 14 2012-02-22 15:05 libcidn.so.1 -> libcidn-2.5.so
lrwxrwxrwx 1 root root 17 2012-02-22 15:05 libcom_err.so.2 -> libcom_err.so.2.1
-rwxr-xr-x 1 root root 10000 2008-09-30 13:27 libcom_err.so.2.1
-rwxr-xr-x 1 root root 48600 2009-01-05 16:32 libcrypt-2.5.so
-rwxr-xr-x 1 root root 1048728 2005-10-31 06:47 libcrypto.so.0.9.6b
-rwxr-xr-x 1 root root 1365504 2008-12-16 08:09 libcrypto.so.0.9.8e
lrwxrwxrwx 1 root root 19 2012-02-22 15:05 libcrypto.so.2 -> libcrypto.so.0.9.6b
lrwxrwxrwx 1 root root 19 2012-02-22 15:05 libcrypto.so.4 -> libcrypto.so.0.9.8e
lrwxrwxrwx 1 root root 19 2012-02-22 15:05 libcrypto.so.6 -> libcrypto.so.0.9.8e
lrwxrwxrwx 1 root root 15 2012-02-22 15:05 libcrypt.so.1 -> libcrypt-2.5.so
lrwxrwxrwx 1 root root 11 2012-02-22 15:05 libc.so.6 -> libc-2.5.so
$

You can see the libc.so.6 is a symlink to libc-2.5.so. You can also a number of versions of libcrypto, not including the link-time library libcrypto.so. You can also see libraries with only two parts to the version number, etc. The represented libraries are libc, libcap, libcidn, libcom_err, libcrypt and libcrypto.

Change Linux shared library (.so file) version after it was compiled

Discussion with Slava made me realize that any const char* was actually visible in the binary file and could then be easily patched to anything else.

So here is a nice way to fix my own problem:

  1. Create a library with:

    • a definition of const char version[] = "VERSIONSTRING:00000.00000.00000.00000"; (we need it long enough as we can later safely modify the binary file content but not extend it...)
    • a GetVersion function that would clean the version variable above (remove VERSIONSTRING: and useless 0). It would return:

      • 0.0 if version is VERSIONSTRING:00000.00000.00000.00000
      • 2.3 if version is VERSIONSTRING:00002.00003.00000.00000
      • 2.3.40 if version is VERSIONSTRING:00002.00003.00040.00000
      • ...
  2. Compile the library, let's name it mylib.so
  3. Load it from a program, ask its version (call GetVersion), it returns 0.0, no surprise
  4. Create a little program (did it in C++, but could be done in Python or any other languauge) that will:

    • load a whole binary file content in memory (using std::fstream with std::ios_base::binary)
    • find VERSIONSTRING:00000.00000.00000.00000 in it
    • confirms it appears once only (to be sure we don't modify something we did not mean to, that's why I prefix the string with VERSIONSTRING, to make it more unic...)
    • patch it to VERSIONSTRING:00002.00003.00040.00000 if expected binary number is 2.3.40
    • save the binary file back from patched content
  5. Patch mylib.so using the above tool (requesting version 2.3 for instance)
  6. Run the same program as step 3., it now reports 2.3!

No recompilation nor linking, you patched the binary version!

ld to library without version information in Linux

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.



Related Topics



Leave a reply



Submit