8

C程序中两个库共存 | 重归混沌的BLOG

 3 years ago
source link: https://blog.gotocoding.com/archives/875
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

C程序中让两个不同版本的库共存

今天有同学提出,如何在一个C程序中让两个不同版本的库共存。

首先想到的方案是,把其中一个版本的库函数全部重命名,比如把每一个函数名都加一个_v2的后缀。

人工替换到没什么,但是如果函数个数超过10个,就有点不拿人当人使了。

而使有工具去替换就会遇到一些棘手的问题,如何识别哪些是函数,哪些是系统函数(系统函数不需要添加后缀)等。

随后想到的另一个解决方案是C++的方案,为其中一个版本库中的所有文件添加命名空间。然后使用g++将这部分代码编译成.o文件,之后再使用gcc将这些.o文件与整个程序中的其他代码进行链接。

不过需要注意的是,g++编译后所有导出接口名都会变化得不那么直观。


第三种方案完全解决了以上两种方案的痛点。

考虑一个C语言的编译链接过程。

首先会将每个c文件编译成.o文件。

在编译过程中,导出函数并不会被实际分配地址,而是将函数名以F符号的方式存在.o文件的符号表中。

在本c文件调用的函数如果不存在于本文件,也会生成一个UND的符号存在.o文件的符号表中。

在链接过程中,链接器接收输入的.o文件,为每个.o文件中的符号分存地址,并生成可执行文件。

有了这几点事实,问题就变得的简单多了。

首先将其中一个版本的库中所有代码编译为.o文件。然后收集所有.o文件中的F符号。

由于整个库代码有内部依赖关系,收集到的F符号必然是所有.o文件中UND符号的超集。

换句话说,所有的F符号名就是我们要重命名的所有函数名。

这里我们需要借助objdump和objcopy工具。objdump -t 用于列表.o文件的符号表,objcopy用于重命名符号。

我随手写了一段用于过虑F符号的lua脚本

--rename.lua
local list = {}
local reg = "([^%s]+)%s+([^%s]+)%s+([^%s]+)"..
"%s+([^%s]+)%s+([^%s]+)%s+([^%s]+)"
for l in io.stdin:lines() do
local a,b,c,d,e,f = string.match(l, reg)
if a and c == "F" then
list[#list + 1] = " --redefine-sym "
list[#list + 1] = string.format("%s=%s_v2", f, f)
end
end
print("#/bin/sh")
print("objcopy " .. table.concat(list) .. " $1")

我们可以使用如下命令来收集所有.o文件的F符号, 并产生修改符号所用的脚本

find . -name '*.o' | xargs objdump -t | ./lua rename > rename.sh

现在我们只需要再执行一条命令就可以把所有函数名增加一个_v2的后缀.

find . -name '*.o' | xargs -n 1 sh ./rename.sh

至此,我们这个版本的库代码的所有函数名已经全部增加了_v2后缀。

这些被处理过的.o文件与我们将所有.c代码中函数名重命名之后编译出的.o文件完全一等价。


8月2号补充:

在实际使用中发现, 局部函数(static 函数)符号有可能会被gcc做修饰,将被修饰的符号重命名会给我们带来一些麻烦,而我们原本也不需要去处理局部函数。

因此对rename.lua做如下修改,过虑掉非全局符号:

--rename.lua
local list = {}
local reg = "([^%s]+)%s+([^%s]+)%s+([^%s]+)"..
"%s+([^%s]+)%s+([^%s]+)%s+([^%s]+)"
for l in io.stdin:lines() do
local a,b,c,d,e,f = string.match(l, reg)
if a and c == "F" and b == "g" then
list[#list + 1] = " --redefine-sym "
list[#list + 1] = string.format("%s=%s_v2", f, f)
end
end
print("#/bin/sh")
print("objcopy " .. table.concat(list) .. " $1")

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK