> 文章列表 > 符号别名,编译指定版本,链接指定版本

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

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

说明

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: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 {};

仅声明