更改Linux共享库(。文件)编译后的版本

时间:2022-03-04 18:19:12

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).

我正在编译Linux库(针对Android,使用NDK的g++,但我敢打赌我的问题对任何Linux系统都有意义)。当将这些库交付给合作伙伴时,我需要用版本号标记它们。我还必须能够程序化地访问版本号(在“About”对话框或GetVersion函数中显示)。

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).

我首先使用一个非版本标志(版本0.0)编译库,在将这个版本发送给合作伙伴之前,在完成测试时需要将这个版本更改为一个真正的版本。我知道这将是更容易修改源代码和编译,但我们不想这么做(因为我们应该再次测试一切如果我们重新编译代码,我们感觉它不太容易出错,看到这篇文章的评论,最后因为我们的开发环境的工作原理是这样的:我们做这个过程为Windows二进制文件:我们设定一个0.0资源版本字符串(.rc),后来我们改变它通过使用verpatch……我们希望在发送Linux二进制文件时使用相同的进程)。

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

这里最好的策略是什么?总而言之,要求是:

  1. Compile binaries with "unset" version (0.0 or anything else)
  2. 使用“未设置”版本(0.0或其他)编译二进制文件
  3. 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)
  4. 可以将这个“unset”版本修改为特定的版本,而无需重新编译二进制文件(理想情况下,运行第三方工具命令,就像我们在Windows下使用verpatch那样)
  5. Be able to have the library code retrieve it's version information at runtime
  6. 能够让库代码在运行时检索其版本信息

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.

如果你的答案是“重命名。so”,那么请为3提供一个解决方案。:如何检索版本名称(例如:文件名)在运行时。

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...?
  • 在代码中有一个版本变量(一个字符串或3个int),并有办法在以后的二进制文件中更改它?使用二进制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)
  • 在一个资源中有一个版本变量,并且以后有办法在二进制文件中修改它吗?(就像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.
  • 使用so(像SONAME)这样的字段,并使用一个工具来更改它……并使它可以从c++代码中访问。
  • Rename the lib + change SONAME (did not find how this can be achieved)...and find a way to retrieve it from C++ code.
  • 重新命名lib + change SONAME(没有发现如何实现这一点)……并找到一种方法从c++代码中检索它。
  • ...

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.

注意,我们使用QtCreator编译Android . So文件,但是它们可能不依赖于Qt,所以使用Qt资源不是一个理想的解决方案。

2 个解决方案

#1


2  

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:

恐怕你从一开始就开始解决你的问题了。首先,SONAME在链接时作为链接器的参数提供,所以在开始时,您需要找到一种方法从源代码获取版本并传递给链接器。一种可能的解决方案—使用ident实用程序并在二进制文件中提供版本字符串,例如:

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:

这个字符串应该出现在二进制文件中,ident实用程序将检测它。或者您可以直接使用grep或类似的东西解析源文件。如果存在冲突的可能性,请放置额外的标记,稍后您可以使用它来检测这个字符串,例如:

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.

因此,您可以从源文件或.o文件中检测版本号,并将其传递给链接器。这应该工作。

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.

对于具有0.0版本的调试版本来说,这很简单——在构建调试时避免检测,并无条件地使用0.0作为版本。

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.

对于第三方构建系统,我建议使用cmake,但这只是我个人的偏好。解决方案也可以在标准Makefile中轻松实现。我不确定qmake。

#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.

与Slava的讨论使我认识到,任何const char*实际上都可以在二进制文件中看到,然后可以很容易地将其修补到任何其他文件中。

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...)
    • const char版本的定义[]= "VERSIONSTRING: 00000.00000.00000.00000.00000 ";(我们需要它足够长的时间,因为我们以后可以安全地修改二进制文件内容,但不扩展它…)
    • 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
      • 如果版本是VERSIONSTRING: 00000.00000.00000.00000.00000
      • 2.3 if version is VERSIONSTRING:00002.00003.00000.00000
      • 2.3 if版本为VERSIONSTRING:00002.00003.00000.00000
      • 2.3.40 if version is VERSIONSTRING:00002.00003.00040.00000
      • 2.3.40如果版本是VERSIONSTRING:00002.00003.00040.00000。
      • ...
    • 如果版本是VERSIONSTRING: 00000.00000.00000.00000.00000,如果版本是VERSIONSTRING: 00002.00000.00000.00000。
  2. 创建一个库:const char版本的定义[]=“VERSIONSTRING: 00000.00000.00000.00000.00000”;(稍后我们需要足够长的时间我们可以安全地修改二进制文件的内容而不是扩展它…)一个GetVersion函数,将干净的版本上面变量(删除VERSIONSTRING:和无用的0)。它将返回:0.0版本是否VERSIONSTRING:00000.00000.00000.00000 2.3版本是否VERSIONSTRING:00002.00003.00000.00000 2.3.40如果版本VERSIONSTRING:00002.00003.00040.00000……
  3. Compile the library, let's name it mylib.so
  4. 编译这个库,我们把它命名为mylibs
  5. Load it from a program, ask its version (call GetVersion), it returns 0.0, no surprise
  6. 从程序中加载它,询问它的版本(调用GetVersion),它返回0,这并不奇怪
  7. 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)
    • 在内存中加载整个二进制文件内容(使用std::fstream with std::ios_base::二进制)
    • find VERSIONSTRING:00000.00000.00000.00000 in it
    • 发现VERSIONSTRING:00000.00000.00000.00000
    • 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...)
    • 确认它只出现一次(为了确保我们不修改我们不打算修改的内容,这就是为什么我在字符串前面加上了VERSIONSTRING,以使它更unic…)
    • patch it to VERSIONSTRING:00002.00003.00040.00000 if expected binary number is 2.3.40
    • 如果预期的二进制数是2.3.40,则将其补丁到VERSIONSTRING:00002.00003.00040.00000
    • save the binary file back from patched content
    • 将二进制文件从补丁内容中保存回来
  8. 创建一个小程序(用c++编写,但可以用Python或其他语言编写),它将:在内存中加载一个二进制文件内容(使用std::fstream std::ios_base::二进制)发现VERSIONSTRING:00000.00000.00000.00000证实它只出现一次(可以肯定的是我们不修改我们并不意味着,这就是为什么我与VERSIONSTRING前缀字符串,使它更尤尼克公司…)补丁它VERSIONSTRING:00002.00003.00040.00000如果预期的二进制数是2.3.40保存二进制文件回来打补丁的内容
  9. Patch mylib.so using the above tool (requesting version 2.3 for instance)
  10. 补丁mylib。因此使用上述工具(例如,请求版本2.3)
  11. Run the same program as step 3., it now reports 2.3!
  12. 运行与步骤3相同的程序。,现在报告2.3!

No recompilation nor linking, you patched the binary version!

没有重新编译或链接,您修补了二进制版本!

#1


2  

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:

恐怕你从一开始就开始解决你的问题了。首先,SONAME在链接时作为链接器的参数提供,所以在开始时,您需要找到一种方法从源代码获取版本并传递给链接器。一种可能的解决方案—使用ident实用程序并在二进制文件中提供版本字符串,例如:

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:

这个字符串应该出现在二进制文件中,ident实用程序将检测它。或者您可以直接使用grep或类似的东西解析源文件。如果存在冲突的可能性,请放置额外的标记,稍后您可以使用它来检测这个字符串,例如:

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.

因此,您可以从源文件或.o文件中检测版本号,并将其传递给链接器。这应该工作。

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.

对于具有0.0版本的调试版本来说,这很简单——在构建调试时避免检测,并无条件地使用0.0作为版本。

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.

对于第三方构建系统,我建议使用cmake,但这只是我个人的偏好。解决方案也可以在标准Makefile中轻松实现。我不确定qmake。

#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.

与Slava的讨论使我认识到,任何const char*实际上都可以在二进制文件中看到,然后可以很容易地将其修补到任何其他文件中。

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...)
    • const char版本的定义[]= "VERSIONSTRING: 00000.00000.00000.00000.00000 ";(我们需要它足够长的时间,因为我们以后可以安全地修改二进制文件内容,但不扩展它…)
    • 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
      • 如果版本是VERSIONSTRING: 00000.00000.00000.00000.00000
      • 2.3 if version is VERSIONSTRING:00002.00003.00000.00000
      • 2.3 if版本为VERSIONSTRING:00002.00003.00000.00000
      • 2.3.40 if version is VERSIONSTRING:00002.00003.00040.00000
      • 2.3.40如果版本是VERSIONSTRING:00002.00003.00040.00000。
      • ...
    • 如果版本是VERSIONSTRING: 00000.00000.00000.00000.00000,如果版本是VERSIONSTRING: 00002.00000.00000.00000。
  2. 创建一个库:const char版本的定义[]=“VERSIONSTRING: 00000.00000.00000.00000.00000”;(稍后我们需要足够长的时间我们可以安全地修改二进制文件的内容而不是扩展它…)一个GetVersion函数,将干净的版本上面变量(删除VERSIONSTRING:和无用的0)。它将返回:0.0版本是否VERSIONSTRING:00000.00000.00000.00000 2.3版本是否VERSIONSTRING:00002.00003.00000.00000 2.3.40如果版本VERSIONSTRING:00002.00003.00040.00000……
  3. Compile the library, let's name it mylib.so
  4. 编译这个库,我们把它命名为mylibs
  5. Load it from a program, ask its version (call GetVersion), it returns 0.0, no surprise
  6. 从程序中加载它,询问它的版本(调用GetVersion),它返回0,这并不奇怪
  7. 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)
    • 在内存中加载整个二进制文件内容(使用std::fstream with std::ios_base::二进制)
    • find VERSIONSTRING:00000.00000.00000.00000 in it
    • 发现VERSIONSTRING:00000.00000.00000.00000
    • 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...)
    • 确认它只出现一次(为了确保我们不修改我们不打算修改的内容,这就是为什么我在字符串前面加上了VERSIONSTRING,以使它更unic…)
    • patch it to VERSIONSTRING:00002.00003.00040.00000 if expected binary number is 2.3.40
    • 如果预期的二进制数是2.3.40,则将其补丁到VERSIONSTRING:00002.00003.00040.00000
    • save the binary file back from patched content
    • 将二进制文件从补丁内容中保存回来
  8. 创建一个小程序(用c++编写,但可以用Python或其他语言编写),它将:在内存中加载一个二进制文件内容(使用std::fstream std::ios_base::二进制)发现VERSIONSTRING:00000.00000.00000.00000证实它只出现一次(可以肯定的是我们不修改我们并不意味着,这就是为什么我与VERSIONSTRING前缀字符串,使它更尤尼克公司…)补丁它VERSIONSTRING:00002.00003.00040.00000如果预期的二进制数是2.3.40保存二进制文件回来打补丁的内容
  9. Patch mylib.so using the above tool (requesting version 2.3 for instance)
  10. 补丁mylib。因此使用上述工具(例如,请求版本2.3)
  11. Run the same program as step 3., it now reports 2.3!
  12. 运行与步骤3相同的程序。,现在报告2.3!

No recompilation nor linking, you patched the binary version!

没有重新编译或链接,您修补了二进制版本!