Does Gcc Have Any Options to Add Version Info in Elf Binary File

Does gcc have any options to add version info in ELF binary file?

You can emit your version info into a text file, then turn that text file into an object file which you then statically link into your executable.

The first step is simple but you have to write some code: a script or something to write your version info in any format you like as a plain text file. Then write a makefile rule to produce say version.o from version.txt, using objcopy. Now you'll have an object file with two useful symbols defined in it: the beginning and end of the textual version info. Add that generated object to your executable, and you'll be able to access the version two ways: by running strings on the binary, or by writing code in the application to print the version string (you'll need to declare the start and end symbols as variables in some header file).

How to add command line option to ELF binary using cmake and gcc?

You can use the CMake configure_file directive to generate a header file with APP_VERSION macro.

CMakeLists.txt:

...
configure_file(${CMAKE_SOURCE_DIR}/version.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/version.h)
...

version.h.cmake:

...
#define APP_VERSION "@PROJECT_VERSION@"
...

This will take version.h.cmake (template) from your source directory, substitute @PROJECT_VERSION@ with VERSION value you set in project(...) in CMakeLists.txt and dump it into version.h file in build directory.

This is not limited to the project version, you can do this with any CMake variable. For list of CMake defined ones see cmake-variables(7).

version.h can be then included in your codebase and macro APP_VERSION used to display version with --version without need of modifying any source files (you just need to bump version in a single central place - CMakeLists.txt).

Command line arguments are passed to the program through its main() entrypoint as arguments argc (count of given arguments) and argv (array of C strings representing each argument). First argument is always the command itself. Very naive command line argument processing code example:

#include <string>
#include <iostream>
#include "version.h"
...
int main(int argc, char *argv[]) {
for (int i = 1; i < argc; i++) {
std::string_view a(argv[i]);
if (a == "-v" || a == "--version") {
std::cout << APP_VERSION << std::endl;
} else {
std::cout << "unknown argument: " << a << std::endl;
}
}
return 0;
}

For more complicated CLI interfaces (such as some arguments taking a value, value validation, formatting, mutual exclusion etc.) I recommend using some existing library such as CLI11.

identify whether an ELF binary is built with optimizations


However sometimes I want use scripts to decide whether an elf binary was built with optimizations, so I can decide what to do next.

The problem with your question is that your binary is very likely to contain some code built with optimizations (e.g. crt0.o -- part of GLIBC).

Your final binary is composed of a bunch of object files, and these files do not have to have consistent optimization flags.

Most likely you only care about the code you wrote (as opposed to code linked from other libraries). You could use -frecord-gcc-switches for that. See this answer.

How to link Data in ELF file to show up during run time? STM32

For Anyone who is interested,
adding the following lines to the STM32L4< x >_FLASH.ld between the .rodata (~line 74) and the .ARM.extab (~line 82) links the .gitcommit and .version section properly.

 .gitcommit :
{
. = ALIGN(4);
KEEP (*(.gitcommit)) /* .gitcommit sections (constants, strings, etc.) */
KEEP (*(.gitcommit*)) /* .gitcommit* sections (constants, strings, etc.) */
. = ALIGN(4);
} >FLASH

.version :
{
. = ALIGN(4);
KEEP (*(.version)) /* .version sections (constants, strings, etc.) */
KEEP (*(.version*)) /* .version* sections (constants, strings, etc.) */
. = ALIGN(4);
} >FLASH

Full Explanation - How to have version number and git commit saved on flash (STM32):

Inside the Application Folder:

Keep the version number inside a version_number.txt
Every version update, update the text file.

Within the source files:

Declare your variables for holding the git-commit and version number as follows (change section names as you wish but be sure to be consistent with the names)

static uint32_t git_commit __attribute__((section(".gitcommit"))) = 0xffffffff;
static uint32_t version_number __attribute__((section(".version"))) =
0xffffffff;

linker file (STM32< MCU ID >_FLASH.ld):

insert the following lines to link between the variables and the values they need to hold (as mentioned above - between the .rodata and .ARM.extab sections)

 .gitcommit :
{
. = ALIGN(4);
KEEP (*(.gitcommit)) /* .gitcommit sections (constants, strings, etc.) */
KEEP (*(.gitcommit*)) /* .gitcommit* sections (constants, strings, etc.) */
. = ALIGN(4);
} >FLASH

.version :
{
. = ALIGN(4);
KEEP (*(.version)) /* .version sections (constants, strings, etc.) */
KEEP (*(.version*)) /* .version* sections (constants, strings, etc.) */
. = ALIGN(4);
} >FLASH

Finally, as post-build, run the python script to take the version_number and git commit and insert them into the proper sections!

"""This script gets git commit and version number and push it to STM32 elf and bin
file into specific section"""
import sys
import subprocess
import os
import binascii
import struct
import platform

def write_to_file_binary(file_name, data):
"""write data to binary file"""
with open(file_name, 'w+b') as file_obj:
data = binascii.unhexlify("0" + data)
file_obj.write(data)

def get_git_revision_short_hash(dir_path):
"""get git commit number"""
return subprocess.check_output(['git', 'rev-parse', '--short', 'HEAD'], cwd=dir_path)

def write_version_to_binary_file(version_array, file_path):
"""write version major minor patch commit to binary file"""
version_major = version_array[0]
version_minor = version_array[1]
version_patch = version_array[2]
with open(file_path, 'w+b') as file_obj:
file_obj.write(struct.pack("B", int(version_major)))
file_obj.write(struct.pack("B", int(version_minor)))
file_obj.write(struct.pack("H", int(version_patch)))

def main(args):
"""This script connects to git and gets the commit number of the project and writes it
to commit_number_file.txt,

Then get the project version number from version_number.txt.

Write binary to temp.txt file the version number and git commit number.

Access to elf file and push git commit to .gitcommit and git version to .version section.

Change the bin file.

Change the bin file name to contain the version number and git commit number.
"""
path_name = args[1]
project_name = os.path.split(path_name)[-1]
print("project name:" + project_name)
debug_path_in_project = path_name + "/Debug"
git_commit_number = get_git_revision_short_hash(path_name)[:-1].decode()
commit_file_name = "commit_number_file.txt"
commit_file_path = debug_path_in_project + "/" + commit_file_name
write_to_file_binary(commit_file_path, git_commit_number)
version_array = open(path_name + "/version_number.txt", "r").readline().split('.')
version_file_temp = debug_path_in_project + "/temp.txt"
print(version_file_temp)
write_version_to_binary_file(version_array, version_file_temp)
version = version_array[0] + '.' + version_array[1] + '.' + version_array[2] + \
'.' + git_commit_number
print("version: " + version)
if(platform.system() == 'Darwin'):
out = subprocess.check_output(['arm-none-eabi-objcopy', '--update-section', '.gitcommit=' + \
commit_file_name, project_name + '.elf'], cwd=debug_path_in_project)
out = subprocess.check_output(['arm-none-eabi-objcopy', '--update-section', '.version=' + \
version_file_temp, project_name + '.elf'], cwd=debug_path_in_project)
out = subprocess.check_output(['arm-none-eabi-objcopy', '-O', 'binary', project_name + '.elf', \
project_name + '.bin'], cwd=debug_path_in_project)
if(platform.system() == 'Windows'):
subprocess.check_output(['arm-none-eabi-objcopy.exe', '--update-section', '.gitcommit=' + \
commit_file_name, project_name + '.elf'], cwd=debug_path_in_project)
subprocess.check_output(['arm-none-eabi-objcopy.exe', '--update-section', '.version=' + \
version_file_temp, project_name + '.elf'], cwd=debug_path_in_project)
subprocess.check_output(['arm-none-eabi-objcopy.exe', '-O', 'binary', project_name + '.elf', \
project_name + '.bin'], cwd=debug_path_in_project)
list_of_files_in_directory = os.listdir(debug_path_in_project)
for file_in_list in list_of_files_in_directory:
if file_in_list != (project_name + '.bin'):
if file_in_list.endswith('.bin') and file_in_list is not project_name + '.bin':
os.remove(file_in_list)
os.rename(project_name + '.bin', project_name + '_' + version + '.bin')
os.remove(version_file_temp)
os.remove(commit_file_path)

if __name__ == '__main__':
main(sys.argv)

That's it, when running the code you should have git commit hold the hash value and version_number hold the version number.

Good Luck

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.

Why does the compiler version appear in my ELF executable?

That's in a comment section in the ELF binary. You can strip it out:

$ gcc -m32 -O2 -s -o t t.c
$ ls -l t
-rwxr-xr-x 1 me users 5488 Jun 7 11:58 t
$ readelf -p .comment t

String dump of section '.comment':
[ 0] GCC: (Gentoo 4.5.1-r1 p1.4, pie-0.4.5) 4.5.1
[ 2d] GCC: (Gentoo 4.5.2 p1.1, pie-0.4.5) 4.5.2


$ strip -R .comment t


$ readelf -p .comment t
readelf: Warning: Section '.comment' was not dumped because it does not exist!
$ ls -l t
-rwxr-xr-x 1 me users 5352 Jun 7 11:58 t

The gains are tiny though, not sure it's worth it.

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.



Related Topics



Leave a reply



Submit