说明
version script
也是ld command
的一种;
使用场景: 共享库
符号版本只有共享库场景使用; 因为共享库才会被动态链接依赖;
兼容
A
编译时依赖B
的旧版本符号;
但是到了执行的时候, 找到的是B
的新版本库; 新版本也有对应版本的函数; 但是根据A
记录的符号名和版本号; A
会从B
中找到A
对应的旧版本符号, 而不是最新;
include
: 支持, 即模块化;
链接使用脚本: --version-script
基本语法
{ version-script-commands }
VERSION { version-script-commands };
VERSION { version-script-commands } only_parent_VERSION;
Sun’s linker in Solaris 2.5
一样;
说明
一般都是枚举和纯增量, 很少用local
减少之前的符号;
继承案例: 父结点只能有一个;
VERS_1.1 {
global:
foo1;
local:
old*;
original*;
new*;
};
VERS_1.2 {
foo2;
} VERS_1.1;
VERS_2.0 {
bar1; bar2;
extern "C++" {
ns::*;
"f(int, double)";
};
} VERS_1.2;
VERS_1.0
; 根节点VERS_1.1 ==> VERS_1.0
;VERS_1.1
父节点是VERS_1.0
VERS_2.0 ==> VERS_1.1 ==> VERS_1.0
;VERS_2.0
父节点是VERS_1.1
, 祖宗结点是VERS_1.0
;
extern "C++" {
ns::*;
"f(int, double)";
};
以C++
的语法指定符号, 这样就可以将这些符号按照C++
的符号mangling
规则转换对应符号成C++
编译后的符号; 很少用;
注意: 不建议用通配符, 通配符的匹配和shell
的基本一样;
正则匹配
*
: 等价shell
; 尽量少用; 枚举而不是正则; 除非是叶子结点, 即最上层;"sym"
: 双引号则保持原样;
node name
没有特殊含义, 仅供阅读;
@@
和@
的区别
- 一个
@
表示可选: 一般是历史版本; 执行时使用, 链接了旧版本so
在新平台执行时使用; - 两个
@
表示默认: 一般是最新版本, 也可以是历史版本; 编译时使用, 编译使用的默认版本, 即可以保障使用了正确版本或者更通用版本;
编译链接
编译
记录编译时使用的符号版本;
执行
如果新库, 从新库中搜索就符号并使用; 没有则错误; 加载时可以快速定位;
符号版本管理
比SunOS
的小版本管理更复杂;
缺少符号
符号解析并不是一次性检查所有, 有的是执行时检查; 检查不通过会崩溃;
使用旧版本库, 可能没有某些符号, 而刚好这些符号在启动时没有解析; 而是很久之后用到了, 但是差不到; 就会导致突然崩溃;
通过symbol version
, 通过符号管理, 就可以因为大部分符号版本过旧而发出警告; 执行第一时间就可以得到相关信息;
GNU extensions
说明
需要了解Sun’s versioning approach
第一种: 别名(两者共存, version sciprt修改可见性)
版本必须存在, 即版本node
共享库会记录, 及时为空;
别名默认可见性为local
, 需要version script
修改可见性; 一般会把原名给local
修饰;
__asm__(".symver original_foo,foo@VERS_1.1");
第二种: 重载(即别名的基础上允许多版本共存)
多版本共存一般用于两个版本大概, 但是需要同时支持的情况;
__asm__(".symver original_foo,foo@");
__asm__(".symver old_foo,foo@VERS_1.1");
__asm__(".symver old_foo1,foo@VERS_1.2");
__asm__(".symver new_foo,foo@@VERS_2.0");
共存选谁: @@
修饰的表示默认, @
修饰的表示可选;
@@
和 @
共享库默认是@@
, 因为只能一个, 多了编译报错;
而.symver
则需要自行指定, @@
只能一个, 没有报错, 多了也报错;
@
可选一般是做旧版本兼容;
@@
一般是最新版本, 也可能是旧版本, 旧版本一般是考虑通用性;
版本机制
__attribute__((alias(target)))
, .symver
两种;
static int x() __attribute__ ((weak, weakref, alias ("y")));
语言相关
说明
这种是因为C++
代码会出现mangling
, 即符号加密, 但是大多数的开发并不是很了解符号加密规则;
即使了解, 但是手写加密规则或多或少都可能出错; 于是提供了这种机制;
- 指定语言, 确认加密规则;
- 指定符号, 需要进行加密的;
- 正则, 等价普通的正则;
案例
// lang 就是 language, c, c++ 之类的;
VERSION extern "lang" { version-script-commands }
编译指定版本和链接指定版本
alias, symver
不仅可以指定编译版本, 也可以指定链接版本;
案例说明
so
代码: 两个版本, 默认最新;exe
代码: 依赖旧版本;makefile
: 编译代码;ver.lds
: 链接声明版本node
;
so
代码: dd.c
#include <stdio.h>
__asm__(".symver good,show@VERS_1.0");
void good() {
printf("%s:%d\n", __FILE__, __LINE__);
}
__asm__(".symver job,show@@VERS_2.0");
void job() {
printf("%s:%d\n", __FILE__, __LINE__);
}
@@
指定当前so
吗默认版本是job
对应函数;
exe
可执行代码: test.c
__asm__(".symver show,show@VERS_1.0");
extern void show();
int main() {
show();
}
表示show
对应的版本是VERS_1.0
, 即上面共享库中的good
函数;
Makefile
: Makefile
.PHONY:all
all:
gcc -fPIC -shared dd.c -o dd.so -Wl,--version-script,./ver.lds
gcc test.c -o a.out ./dd.so -Wl,--version-script,./ver.lds
编译需要lds
作为版本结点声明; 否则报错;
ver.lds
: ver.lds
VERS_1.0 {};
VERS_2.0 {};
仅声明