17

I'm compiling Linux libraries (for Android, using NDK's g++, but I bet my question makes sense for any Linux system). When delivering those libraries to partners, I need to mark them with a version number. I must also be able to access the version number programatically (to show it in an "About" dialog or a GetVersion function for instance).

I first compile the libraries with an unversioned flag (version 0.0) and need to change this version to a real one when I'm done testing just before sending it to the partner. I know it would be easier to modify the source and recompile, but we don't want to do that (because we should then test everything again if we recompile the code, we feel like it would be less error prone, see comments to this post and finally because our development environment works this way: we do this process for Windows binaries: we set a 0.0 resources version string (.rc) and we later change it by using verpatch...we'd like to work with the same kind of process when shipping Linux binaries).

What would be the best strategy here? To summarize, requirements are:

  1. Compile binaries with "unset" version (0.0 or anything else)
  2. Be able to modify this "unset" version to a specific one without having to recompile the binary (ideally, run a 3rd party tool command, as we do with verpatch under Windows)
  3. Be able to have the library code retrieve it's version information at runtime

If your answer is "rename the .so", then please provide a solution for 3.: how to retrieve version name (i.e.: file name) at runtime.

I was thinking of some solutions but have no idea if they could work and how to achieve them.

  • Have a version variable (one string or 3 int) in the code and have a way to change it in the binary file later? Using a binary sed...?
  • Have a version variable within a resource and have a way to change it in the binary file later? (as we do for win32/win64)
  • Use a field of the .so (like SONAME) dedicated to this and have a tool allowing to change it...and make it accessible from C++ code.
  • Rename the lib + change SONAME (did not find how this can be achieved)...and find a way to retrieve it from C++ code.
  • ...

Note that we use QtCreator to compile the Android .so files, but they may not rely on Qt. So using Qt resources is not an ideal solution.

5
  • 2
    You are afraid that recompiling the exact same code with one version string exchanged might break things and necessitates new testing, but are perfectly fine with a solution that involves hacking yourself a script that patches the changed string into the binary? That's.. interesting. Commented Jun 8, 2016 at 14:21
  • Your remarks perfectly makes sense. Actually we used to do it that way: change the version number (to 3.2 for instance) in the code and recompile. We are a small structure, people doing both development and integration...then, developer doing this often forgets to reset the version string to 0.0 and then later you get development binaries marked 3.2 when they are definitely not the 3.2 release...that's why marking binaries once post compilation appeared to be safer for us (and that's the way we end up working under Windows). Commented Jun 8, 2016 at 14:27
  • My comment still applies :-) Why don't you solve your problem at its root, and alter your build environment to change the version number back to 0.0 for any debug build? The easiest way to do that would be to use a preprocessor definition for the version number. The neat thing about that solution is that it can be easily adopted to include e.g. revision ids in version strings of debug builds. Commented Jun 8, 2016 at 14:41
  • You question is not accurate, changing SONAME does not require (re)compilation, just (re)linking already compiled objects. So you do not need to change any source code to change SONAME and recreate a shared library with proper SONAME. Commented Jun 8, 2016 at 14:44
  • Yes that is really bad design not give a way to remove the version info. Commented Sep 4, 2018 at 17:54

2 Answers 2

2
+50

I am afraid you started to solve your problem from the end. First of all SONAME is provided at link time as a parameter of linker, so in the beginning you need to find a way to get version from source and pass to the linker. One of the possible solutions - use ident utility and supply a version string in your binary, for example:

const char version[] = "$Revision:1.2$" 

this string should appear in binary and ident utility will detect it. Or you can parse source file directly with grep or something alike instead. If there is possibility of conflicts put additional marker, that you can use later to detect this string, for example:

const char version[] = "VERSION_1.2_VERSION" 

So you detect version number either from source file or from .o file and just pass it to linker. This should work.

As for debug version to have version 0.0 it is easy - just avoid detection when you build debug and just use 0.0 as version unconditionally.

For 3rd party build system I would recommend to use cmake, but this is just my personal preference. Solution can be easily implemented in standard Makefile as well. I am not sure about qmake though.

Sign up to request clarification or add additional context in comments.

9 Comments

Your proposal requires version number to be specified in the code at compilation time. This does not precisely answer my question which requests a way to change version number directly inside an already generated .so file. But maybe what I'm asking is impossible to do under Linux (while its doable under Windows)....I'll wait for the end of the bounty period to see if I get something better. Thanks for the help.
First of all you asked to change SONAME without recompilation, my answer describes how to do that. You need to be more clear with your requirements.
I don't understand. How can I do what you propose without compiling when you propose to "get version for source". My goal is to compile the .so file with version "0.0" and later change it to something else without recompiling/linking (using any external tool).
You did not say without relinking, you said without recompile. Now you are adding requirements on the fly. As I said you need to be more clear about requirements.
In my mind compilation/linkage are the same operation. As I do with verpatch under Windows. My input is a .so file and I wish to change a string (or whatever) in it from "0.0" to "3.2"....
|
2

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!

2 Comments

You do not have to load whole file into memory and then override whole file. You can use strings to find your string and it will tell you file offset then just open that file for writing, position and override your string.
@Slava: Sure. I tried to code this quickly to verify the concept, it can definitely be optimized. Loading the whole file makes it easy to checkthe string appears once only.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.