记一次库连接的问题。

问题

场景是这样的

1. liba.so link the grpc++.a
2. binary b link the liba.so and the grpc++.a

因为 link 顺序的导致 binary b 优先使用 liba.so 中有的 grpc++.a symbol,liba.so 中没有的才去 link grpc++.a,这导致一些比较微妙的运行时问题,而且是偶现的

所以还是对于写动态库没有经验,这个问题说明要控制动态库 visibility 的 symbol

控制 symbol visibility 的方法

1. -fvisibility=hidden

在代码中加入

void __attribute((visibility("default")))__ foo();

然后编译时使用 -fvisibility=hidden 选项就可以只暴露 foo 这种 API

但是这个方法不适合开始介绍的问题,因为 grpc++.a 中的 symbol visibility 不受我们的代码控制

2. -Wl,—exclude-libs,…

ld 中有 --exclude-libs 这个选项,可以 man 查看详情,会移除 archive 中 automatic export 的符号,经实验,使用前 grpc 相关的符号有 4000,使用后 grpc 相关的符号 300,还是很有用

3. ld version-script

最强控制 export,参考 script 写法,甚至支持 C++ mangle 前的写法

{
  global:
   extern "C++" {
    foo::zoo::*;
   };
   moo;
}

liba.so 只会暴露 script 指定的 symbol,grpc public 的只剩下两个

image-20230111174331686

番外

void foo(std::string p) {
  std::cout << "foo:" << p << std::endl;
}
 
void foo(std::string p) asm("rename");
extern "C" {
  void rename(std::string p);
}
 
int main() {
  rename("test");
  return 0;
}

会发生什么?调用到了 foo 函数!

reference

使用到的 research 工具

nm -C -g liba.so
readelf -a liba.so | rg RPATH
chrpath liba.so -r <new path> 
  or use patchelf, but do not this time
readelf -a liba.so | rg -i RPATH