How to Embed Version Information into Shared Library and Binary

How to embed version information into shared library and binary?

One way to do it if using cvs or subversion is to have a special id string formatted specially in your source file. Then add a pre-commit hook to cvs or svn that updates that special variable with the new version of the file when a change is committed. Then, when the binary is built, you can use ident to extract that indformation. For example:

Add something like this to your cpp file:

static char fileid[] = "$Id: fname.cc,v 1.124 2010/07/21 06:38:45 author Exp $";

And running ident (which you can find by installing rcs) on the program should show the info about the files that have an id string in them.

ident program
program:
$Id: fname.cc,v 1.124 2010/07/21 06:38:45 author Exp $

Note As people have mentioned in the comments this technique is archaic. Having the source control system automatically change your source code is ugly and the fact that source control has improved since the days when cvs was the only option means that you can find a better way to achieve the same goals.

How do I embed version information into a windows binary?

It looks as though the best solution (for us at least) is to use an RC file.

1 VERSIONINFO
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904E4"
BEGIN
VALUE "File Version", "1.0.4"
VALUE "Build Number", "3452"
END
END
END

Which is compiled into a .res file

rc.exe /fo Results/version.res version.rc

Which is then linked in with the rest of the object files.

correct way to encode/embed version number in program code

Normally, for major and minor version numbers (as in, 1.2, 1 is major and 2 is minor), they are most often written in the code directly, usually as a #define (because you might need them for conditional compilations, i.e., #if blocks).

You would typically have a separate header that contains only those defines and nothing else (except the header-guard), to minimize dependencies.

Some people use the build system (like cmake) to pull a version number from the version control (git, svn, cvs, etc..) and then put that version number into their "version" header. Or, they put the version number into the build system configuration files and then put that onto the header, as shown on the cmake tutorial. Personally, I don't like this approach because it tends to modify your header files too often and cause frequent and pointless recompilations.

I prefer writing the version number in the header file, and then pulling out those version numbers (major, minor, ..) from the header onto the build system. This is another thing that cmake can very easily do.

If you want to embed a very day-by-day version number into your software, such as a build number or revision number, then you should not put it as a #define in a header file, but rather as a extern const variable that you define in one cpp file. For example, you can use cmake to pull a revision number from your version control system, tack that onto the cpp file that defines this extern const int revision; variable (through cmake's configure_file function), and link your programs with that cpp / object file. This way, the revision number is built into your programs automatically at every re-build, and it won't trigger full recompilations every time it's updated (which is at every commit).

The point is that major and minor version numbers are not changed frequently enough to require any kind of automatic maintenance, but you need to manually write them in one place only, and automatically propagate it everywhere else where it might be relevant (I would recommend that this place be the header file itself). It's only the revision or build numbers that need to be fully automated (generated by the version control, and propagated everywhere else automatically).

How to add library version information to elf file while linking archive files and all archive files has their version info?

All my archives have their version info but not writing this to elf except libcrt.

In order to understand this result, you need to understand

  1. How the what command works and
  2. How the linker works.

On with the show. The what command is very simple: it scans an arbitrary binary looking for and ASCII string starting with "special" symbol sequence @(#) and prints any string that follows that sequence (ending with the NUL character). Documentation.

In order for the string @(#) Lib ssh swfp version BL910291 to appear in the linked executable bos_epb.ppc.elf, the object file containing that string must be selected from libssh.a to become part of the executable. Which brings us to issue #2 above.

Just because such an object is present in libssh.a, you can't assume that it will be linked into the final binary. The algorithm that the linker uses to decide whether to include an object into the final executable or not is described here or here.

You can garantee that the entire libssh.a is included in the final binary by using -Wl,--whole-archive -lssh -Wl,--no-whole-archive, but this is ill-advised. It may cause your binary to fail to link, and is guaranteed to make it larger than it should be.

How to embed data in shared library?

There 2 methods works for me now.

Method 1:
Use objcopy to convert data to ".o", then link to ".so".
Then link the ".so" and main code with "-fPIC".

objcopy -B i386 -I binary -O elf64-x86-64 dicmap.bin dicmap.o
g++ -shared -fPIC dicmap.o -o libdicmap1.so
# -fPIC is very import in the following line,
# But it is very unusual when you compile and link main code.
g++ -fPIC test_dicmap.cpp libdicmap1.so -o test_dicmap1-PIC

Method 2:
Use assmbler to wrap the data, and store the size in a different way of objcopy.

g++ -shared -fPIC dicmap3.s -o libdicmap3.so
g++ test_dicmap3.cpp libdicmap3.so -o test-dicmap3

Codes:

test_dicmap.cpp:

#include <stdio.h>
#include <stdint.h>
#include <assert.h>
extern "C" {
extern const uint8_t _binary_dicmap_bin_start[];
extern const uint8_t _binary_dicmap_bin_end[];
extern const void* _binary_dicmap_bin_size;
}
int main()
{
size_t data_size = (size_t)&_binary_dicmap_bin_size;
printf("start=%p, end=%p\nend-start=%zd, size=%zd\n",
_binary_dicmap_bin_start,
_binary_dicmap_bin_end,
_binary_dicmap_bin_end - _binary_dicmap_bin_start,
data_size);
printf("data[0..8]=%02x %02x %02x %02x %02x %02x %02x %02x\n",
_binary_dicmap_bin_start[0], _binary_dicmap_bin_start[1],
_binary_dicmap_bin_start[2], _binary_dicmap_bin_start[3],
_binary_dicmap_bin_start[4], _binary_dicmap_bin_start[5],
_binary_dicmap_bin_start[6], _binary_dicmap_bin_start[7]);
assert(_binary_dicmap_bin_end - _binary_dicmap_bin_start == data_size);
}

test_dicmap3.cpp:

#include <stdio.h>
#include <stdint.h>
#include <assert.h>

extern "C" {
extern const uint8_t _binary_dicmap_bin_start[];
extern const uint8_t _binary_dicmap_bin_end[];
extern const size_t _binary_dicmap_bin_size;
}


int main()
{
size_t data_size = _binary_dicmap_bin_size;
printf("start=%p, end=%p\nend-start=%zd, size=%zd\n",
_binary_dicmap_bin_start,
_binary_dicmap_bin_end,
_binary_dicmap_bin_end - _binary_dicmap_bin_start,
data_size);
printf("data[0..8]=%02x %02x %02x %02x %02x %02x %02x %02x\n",
_binary_dicmap_bin_start[0], _binary_dicmap_bin_start[1],
_binary_dicmap_bin_start[2], _binary_dicmap_bin_start[3],
_binary_dicmap_bin_start[4], _binary_dicmap_bin_start[5],
_binary_dicmap_bin_start[6], _binary_dicmap_bin_start[7]);
// _binary_dicmap_bin_end is invalid
//assert(_binary_dicmap_bin_end - _binary_dicmap_bin_start == data_size);
}

dicmap3.s:

    .globl  _binary_dicmap_bin_start
.globl _binary_dicmap_bin_end
.globl _binary_dicmap_bin_size
.section .rodata
.type _binary_dicmap_bin_start, @object
.align 8
_binary_dicmap_bin_start:
.incbin "dicmap.bin"
.align 1
.size _binary_dicmap_bin_end, 1
_binary_dicmap_bin_end:
.byte 0
.size _binary_dicmap_bin_start, _binary_dicmap_bin_end - _binary_dicmap_bin_start
.type _binary_dicmap_bin_size, @object
.size _binary_dicmap_bin_size, 8
.align 8
_binary_dicmap_bin_size:
.quad _binary_dicmap_bin_end - _binary_dicmap_bin_start

dicmap.bin:

helloworld

Is there a way to store the version information in a Rust compiled executable or library?

While I don't think there is an immediate way to use ELF facilities for versioning (they are not cross-platform anyway), it is possible to use version information from Cargo:

const VERSION: &'static str = env!("CARGO_PKG_VERSION");

VERSION will now be equal to the version specified in the manifest when cargo build is run. Alternatively, you can use env_opt!() if you want to build your program without Cargo:

const VERSION: Option<&'static str> = env_opt!("CARGO_PKG_VERSION");

Standard way to embed version into Python package?

Not directly an answer to your question, but you should consider naming it __version__, not version.

This is almost a quasi-standard. Many modules in the standard library use __version__, and this is also used in lots of 3rd-party modules, so it's the quasi-standard.

Usually, __version__ is a string, but sometimes it's also a float or tuple.

As mentioned by S.Lott (Thank you!), PEP 8 says it explicitly:

Module Level Dunder Names

Module level "dunders" (i.e. names with two leading and two trailing
underscores) such as __all__, __author__, __version__, etc.
should be placed after the module docstring but before any import
statements except from __future__ imports.

You should also make sure that the version number conforms to the format described in PEP 440 (PEP 386 a previous version of this standard).

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!



Related Topics



Leave a reply



Submit