Symbol versioning
2020-11-26 17:00:00 Author: maskray.me(查看原文) 阅读量:249 收藏

Ulrich Drepper和Eric Youngdale在1990+年借鉴Solaris symbol versioning,设计了用于glibc的GNU风格symbol versioning,意图是给shared objects提供backward compatibility:当一个shared object升级须要变更某个符号的行为时,在添加新的符号的同时,保留compatible符号兼容旧的依赖的shared objects。

下面描述version index的取值,然后从assembler、链接器、ld.so几个角度描述symbol versioning行为。

Version index values

Index 0称为VER_NDX_LOCAL,标记一个定义的符号的binding应改变为STB_LOCAL
Index 1称为VER_NDX_GLOBAL,没有特殊作用,相当于一个unversioned符号。
Index 2到0xffef用于其他versions。

定义的versioned符号有两种形式:

  • foo@@v2,称为default version。只有定义的符号可以具有这种形式
  • foo@v2,称为non-default version,也叫hidden version,其version id设置了VERSYM_HIDDEN bit

通常只在shared object中定义versioned符号,但可执行档也是可以获得versioned符号的。
(一个shared object更新保留旧符号使其他shared objects不须重新链接,而可执行档通常不提供versioned符号供其他shared objects引用。)

未定义符号只有foo@v2这一种形式。

Assembler行为

GNU as和LLVM integrated assembler提供实现。

  • 对于.symver foo, foo@v1
    • 如果foo未定义,.o中有一个名为foo@v1的符号
    • 如果foo被定义,.o中有两个符号:foofoo@v1,两者的binding一致(均为STB_LOCAK,或均为STB_WEAK,或均为STB_GLOBAL),st_other一致(visibility一致)
  • 对于.symver foo, foo@@v1
    • 如果foo未定义,assembler报错
    • 如果foo被定义,.o中有两个符号:foofoo@@v1,两者的binding和st_other一致
  • 对于.symver foo, foo@@@v1
    • 如果foo未定义,.o中有一个名为foo@v1的符号
    • 如果foo被定义,.o中有一个名为foo@@v1的符号

个人推荐:

  • 定义default-version符号时使用.symver foo, foo@@@v2,在.o中只产生foo@@v2,不产生foo
  • 定义non-default符号时在原符号名后加后缀(.symver foo_v1, foo@v1)防止和foo冲突。在.o中会同时有foo_v1foo@v1。目前没有便捷方法去除(通常不想要的)foo_v1,一般在指定version script时注意把foo_v1设置为local
  • 未定义的versioned符号通常是链接时绑定的,object files不须要指定符号。如果确实要引用,推荐.symver foo, foo@@@v1,即使能.symver foo, foo@v1达到相同效果

在.o中,@是实际出现在symbol table中的。

链接器行为

链接器在读入object files、archive files、shared objects、LTO files、linker scripts等后就进入符号解析阶段。符号解析规则:

  • 定义的foo可以满足未定义的foo(传统unversioned符号规则)
  • 定义的foo@v1可以满足未定义的foo@v1
  • 定义的foo@@v1可以同时满足未定义的foofoo@v1

若存在多个default version的定义(如foo@@v1 foo@@v2),触发duplicate definition error。通常一个符号有零或一个default version(@@)定义,任意个non-default version(@)定义。

(LLD的实现中,看到shared object中的foo@@v1则在符号表中同时插入foofoo@v1,因此可以满足未定义的foofoo@v1。)
(GNU ld用indirect symbol表示versioned符号,在很多阶段都有复杂的规则。)

在输出的shared object或可执行档中定义version必须指定version script。若所有versioned符号均为未定义状态则无需version script。
Version script有三个用途:

  • 定义versions
  • 指定一些模式,使得匹配的、定义的、unversioned的符号具有指定的version
  • Local version:local:可以改变匹配的、定义的、unversioned的符号的binding为STB_LOCAL,不会导出到dynamic symbol table

在shared objects和可执行档中,对于static symbol table,@直接出现在符号名中;对于dynamic symbol table,version index信息由一个parallel table .gnu.version提供。实际的version信息则存储在.gnu.version_d.gnu.version_r中。

Versioned symbols产生方式

对于一个符号,它获得version的可能途径:

  • 它是未定义的。该符号须要被某个shared object定义,否则GNU ld会报错
  • 它是定义的
    • 在.o中符号名形如foo@v1foo@@v1。Version v1须要被version script定义,否则报错
    • 原本unversioned,被version script的规则匹配而获得version

ld.so行为

Dynamic table中的DT_VERNEEDDT_VERNEEDNUM标识了一个shared object/可执行档需要的外部version定义,及该定义须由哪个soname提供。
如果目标soname包含DT_VERDEF表但不包含需要的version,且该verneed项不是VER_FLG_WEAK则报错。

接下来是符号解析阶段。

  • 未定义unversioned foo可以解析到定义foofoo@@v2(v2的version index应为1(VER_NDX_GLOBAL)或2)
  • 未定义versioned foo@v1可以解析到定义foofoo@v1foo@@v1

注意(未定义versioned foo@v1解析到定义foo)这种情况是ld.so允许而链接器不允许的。这提供了一种机制:在不阻碍运行时符号解析的情况下拒绝链接旧的符号。
假如某个旧版本shared object定义bar而希望在新版本废弃这个符号,可以去除bar而定义bar@compat。依赖该.so的库中的未定义bar仍可以解析,但该库无法重新链接。

LLD

LLD的实现有尚有一些不足。

1
2
3
4
5
6
7
8
9
10
# RUN: not ld.lld a.o b.o
# RUN: ld.bfd a.o b.o

//--- a.s
.symver foo, foo@@@v1
.globl foo
foo:
//--- b.s
.symver foo, foo@@@v1
call foo

评价

GCC/Clang支持asm specifier和#pragma redefine_extname重命名一个符号。比如声明int foo() asm("foo_v1");再引用foo,.o中的符号会是foo_v1
那么symbol versioning还有什么意义呢?我细细琢磨,有如下优点:

  • 在不阻碍运行时符号解析的情况下拒绝链接旧的符号
  • version定义可以延迟决定到链接时。链接时的version script提供灵活的pattern matching机制指定versions
  • version script中的local:可以使符号获得值为VER_NDX_LOCAL的version,具有把weak/global符号变成local的效果
  • 对编译器认识的builtin functions,在GCC/Clang的实现里重命名有一些语义上的问题(符号foo含有内建语义X)2020-10-15-intra-call-and-libc-symbol-renaming
  • 对于一个.so,ld.so检查是否所有DT_VERNEED需要的versions都存在,不存在则在符号解析前报错

其实可能只有前两条是比较好的。local:有用,但假如没有version script也可以设计一个其他的类似--version-script--dynamic-list的机制提供该功能。


文章来源: http://maskray.me/blog/2020-11-26-symbol-versioning
如有侵权请联系:admin#unsafe.sh