惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

GbyAI
GbyAI
爱范儿
爱范儿
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
月光博客
月光博客
腾讯CDC
Last Week in AI
Last Week in AI
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
博客园_首页
量子位
博客园 - 聂微东
Jina AI
Jina AI
小众软件
小众软件
The Cloudflare Blog
有赞技术团队
有赞技术团队
V
V2EX
博客园 - 司徒正美
Apple Machine Learning Research
Apple Machine Learning Research
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
大猫的无限游戏
大猫的无限游戏
博客园 - 三生石上(FineUI控件)
WordPress大学
WordPress大学
阮一峰的网络日志
阮一峰的网络日志
B
Blog
MongoDB | Blog
MongoDB | Blog
L
LangChain Blog
宝玉的分享
宝玉的分享
C
Check Point Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
IT之家
IT之家
N
Netflix TechBlog - Medium
I
InfoQ
J
Java Code Geeks
S
SegmentFault 最新的问题
V
Visual Studio Blog
Microsoft Security Blog
Microsoft Security Blog
博客园 - 叶小钗
D
DataBreaches.Net
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
B
Blog RSS Feed
S
Schneier on Security
Webroot Blog
Webroot Blog
P
Proofpoint News Feed
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
T
Threatpost
Project Zero
Project Zero
Scott Helme
Scott Helme
C
CERT Recently Published Vulnerability Notes
P
Privacy International News Feed
T
The Exploit Database - CXSecurity.com
D
Darknet – Hacking Tools, Hacker News & Cyber Security

重归混沌的BLOG

给silly实现了一个ernro模块 | 重归混沌的BLOG 给silly实现了一个ernro模块 | 重归混沌的BLOG 第一次在生产环境使用 Vibe Coding | 重归混沌的BLOG 第一次在生产环境使用 Vibe Coding | 重归混沌的BLOG API 设计的艰难抉择 | 重归混沌的BLOG API 设计的艰难抉择 | 重归混沌的BLOG 十年 | 重归混沌的BLOG 十年 | 重归混沌的BLOG 在Go语言中如何使XML加载内存无限趋近于0 | 重归混沌的BLOG 在Go语言中如何使XML加载内存无限趋近于0 | 重归混沌的BLOG 对跨服玩法中的分布式一致性问题进行简单抽象 | 重归混沌的BLOG 对跨服玩法中的分布式一致性问题进行简单抽象 | 重归混沌的BLOG Go语言逃逸分析之slice和map | 重归混沌的BLOG Go语言逃逸分析之slice和map | 重归混沌的BLOG 谈谈观测 | 重归混沌的BLOG 谈谈观测 | 重归混沌的BLOG 写了个AI Agent服务端 | 重归混沌的BLOG 写了个AI Agent服务端 | 重归混沌的BLOG 谈谈代码设计中“严丝合缝” | 重归混沌的BLOG 谈谈代码设计中“严丝合缝” | 重归混沌的BLOG 一次艰难的线上游戏服务器内存排查经历 | 重归混沌的BLOG 一次艰难的线上游戏服务器内存排查经历 如何基于LanguageServerProtocol来编写lint工具 谈谈游戏服务器中RPC模块的设计 谈谈游戏服务器代码抽象 最近碰到的一个分布式一致性问题 谈谈游戏服务器的自动化测试 对Raft协议的一点理解 使用mmap来学习/proc/pid/smaps 2023(完) 再次实现了一个Lua性能分析器 终于给Silly的定时器增加了取消功能 一次虚拟内存排查经历 游戏服务器分布式数据的一种同步的思路 为silly增加了互斥锁 2022(完) Go语言之闭包篇 一例误用unsafe包引起的内存问题 Go语言之内存篇 初识Go语言 重新抽象图形API 给Lua实现了一个数学库 谈谈跨平台图形API的抽象 寻路和Flocking算法的结合 行为树的一种高效实现 内测过程中Shader出现的问题 彻底解决多国语言 谈谈数据库的选型 再谈Lua热更新(终) 初窥Rust 关于游戏服务器的服务拆分 ECS的初步实现 ECS初探 屏幕空间(SreenSpace)的想象力 一些对辐射度量学的理解 深度缓冲和半透明渲染 Mysql的间隙锁 更新一些GPU相关知识 2020 地形渲染之爬过的坑 Lua5.3 GC源码阅读(5) 实现一个数据库存储队列 再学计算机图形学入门 再谈分布式服务架构 游戏上线一个月后的反思 一次并发Bug 双向链表的三种实现 谈谈随机数的使用 再谈性能优化 2019 Lua中的函数式编程 重构登录逻辑 Unity资源管理(续) 谈谈Unity的资源管理 一次关于Cache的性能分析 历史之2018 DC3算法 移动平台native代码遭遇的坑 从CPU层面谈谈优化 开卷有益(UNIX编程艺术篇) GC竞争问题 通过Mesh投影来实现贴花系统 谈谈我对数据同步的理解 又一个类型提升引起的Bug Lua5.3 GC源码阅读(4) Lua5.3 GC源码阅读(3) Lua5.3 GC源码阅读(2) Lua5.3 GC源码阅读(1) 三角形光栅化时遇到的坑 一次git事故 再见2017 又一个lua调试器 客户端缓存落地方案 Paxos算法 HTTP服务器的特点 一次性能优化经历 关于CPU分支预测 实现了一个AOI模块 一个高可伸缩的游戏服务器架构 关于网络协议封装的一些新想法
C程序中让两个不同版本的库共存
重归混沌 · 2017-08-01 · via 重归混沌的BLOG

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