符号别名,编译指定版本,链接指定版本

说明
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.0VERS_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:allall:gcc -fPIC -shared dd.c -o dd.so -Wl,--version-script,./ver.ldsgcc test.c -o a.out ./dd.so -Wl,--version-script,./ver.lds
编译需要lds作为版本结点声明; 否则报错;
ver.lds: ver.lds
VERS_1.0 {};
VERS_2.0 {};
仅声明


