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

原文连接:http://blog.gotocoding.com/archives/875

今天有同学提出,如何在一个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脚本


1

2

3

4

5

6

7

8

9

10

11

12

13


--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符号, 并产生修改符号所用的脚本


1

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

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


1

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

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

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



8月2号补充:

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

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


1

2

3

4

5

6

7

8

9

10

11

12

13


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

时间: 2024-08-25 22:49:47

C程序中让两个不同版本的库共存的相关文章

在windows中安装两个不同版本的Python

这段时间买了一本 利用Python进行数据分析的书.书上要我将原来安装的Python的环境去掉,但是我觉得这样做不行,我以前写过的很多东西还在呢.遂在博客中找到了解决方法,记录之. 首先,我们安装了两个python版本.我们将其中一个文件中的python.exe 改成 python2.exe,接下来将新的python的路径以及其 Scripts 的路径写入到环境变量.要利用原来的版本执行test.py文件直接用test.py就好了.要用新的python执行则要加上 python2 test.py

工具--在一台电脑中安装两个jdk版本

一.安装好JDK1.6和1.8: 方式就不列出了,要是忘了,参考这篇[工具--windows中tomcat的配置(包含jdk配置)] 二.配置环境变量: 1. 创建三个JAVA_HOME变量.JAVA6_HOME,存放JDK1.6的安装路径.JAVA8_HOME,存放JDK1.8的安装路径.JAVA_HOME,如果需要1.6版本变量值设为%JAVA6_HOME%,如果需要1.8版本变量值设为%JAVA8_HOME%,便于切换. 2. 配置CLASSPATH.新建,变量名CLASSPATH,变量值

如何在新工程中添加两个不同版本的的echarts库

emmmmm.....标题我就觉得起的很变态.闲话不多说,先说出现的背景吧--. 因为业务上的需求,跟一个硬件对接,要做大屏展示大厅客流热力图分布(背景图是客户那边给的).然后这个机子传过来的数据就可能20来条.如果用最新的echarts4来做,但是数据太小不足以画出图来,但是如果数据太大传输也是一个问题.后来看来echarts2的热力图,仿佛找到来救星--(https://echarts.baidu.com/echarts2/doc/example/heatmap.html)里面还有个valu

自动化的在程序中显示SVN版本

有时候会有这样的情况,策划拿着应用过来提一个bug,但我们却不好确定策划的手机上装的应用对应的是那个代码版本. 为了解决这个问题,我们希望能在应用上显示出当前应用所对应的代码版本,即svn版本. 构想了下,希望最后达到的效果有: 自动化,不需要每次编译版本的时候有人为的步骤,比如编版本的时候人为修改某个值或者点击某个脚本. 跨平台,对外发布的win32/iOS/Android版本都能正确表现 全面性,无论用Windows上用vs开发/Mac OS上用XCode/Eclipse来进行开发,无论是D

程序员面试100题之十:快速找出一个数组中的两个数字,让这两个数字之和等于一个给定的值(转)

能否快速找出一个数组中的两个数字,让这两个数字之和等于一个给定的值,为了简化起见,我们假设这个数组中肯定存在至少一组符合要求的解. 假如有如下的两个数组,如图所示: 5,6,1,4,7,9,8 给定Sum= 10 1,5,6,7,8,9 给定Sum= 10 分析与解法 这个题目不是很难,也很容易理解.但是要得出高效率的解法,还是需要一番思考的. 解法一 一个直接的解法就是穷举:从数组中任意取出两个数字,计算两者之和是否为给定的数字. 显然其时间复杂度为N(N-1)/2即O(N^2).这个算法很简

019写程序在一棵二叉树中找到两个结点的最近共同祖先(keep it up)

写程序在一棵二叉树中找到两个结点的最近共同祖先. 分两种情况来讨论这个题: 第一种情况结点中没有指向父结点的指针 第二种情况接种有指向父节点的指针 我们先看第一种情况,结点中没有指向父结点的指针. 我们可以采用暴力搜索每一个结点,如果这个结点的子树中 有已知的两个结点,那我们就继续沿着左右子树找,如果左子树 能找到,我们就继续沿着左子树找,如果有子树能找到,我们就 沿着右子树找,不存在两个子树都能够找到. 代码: struct TreeNode {<pre name="code"

在VC中创建两套资源解决中英文版本发布的问题

原文http://blog.csdn.net/ylforever/article/details/6745611 一.背景 当软件需要发布多个不同语言版本时,对资源编辑的菜单,按钮,对话框大小形状有不同的要求.比如同个单词用中文/英文描述字符长度差异较大,如果这个单词 用作按钮的标题,对按钮的大小要求就不一样.这时可以有两种解决方法:1.通过代码动态调整按钮的大小,较麻烦,也破坏了VC资源编辑所见即所得规则. 2.准备两套资源,编译不同的版本用不同的资源.下面重点描述第二种处理方法的详细步骤.

在Java Web程序中使用监听器可以通过以下两种方法

之前学习了很多涉及servlet的内容,本小结我们说一下监听器,说起监听器,编过桌面程序和手机App的都不陌生,常见的套路都是拖一个控件,然后给它绑定一个监听器,即可以对该对象的事件进行监听以便发生响应,从本质上来说这些都是观察者模式的具体实现,在web程序中的监听器也不例外.在Java Web程序中使用监听器可以通过以下两种方法:通过注解@WebListener来标识一个自定义的监听器:[java] view plain copy@WebListener public class Custom

VC++ 在两个程序中 传送字符串等常量值的方法:使用了 WM_COPYDATA 消息(转载)

转载:http://www.cnblogs.com/renyuan/p/5037536.html VC++ 在两个程序中 传递字符串等常量值的方法:使用了 WM_COPYDATA 消息的 消息作用:    在进程间共享数据(内部通过创建内存映射文件) 消息介绍:需要用到的数据结构/类型:typedef struct tagCOPYDATASTRUCT {    ULONG_PTR dwData;    DWORD cbData;    PVOID lpData;} COPYDATASTRUCT,