动态库符号可见性及库裁剪
2020, Nov 30
// digit.h
#include <iostream>
int one();
int two();
int three();
int four();
int five();
// ---------
// digit.cc
#include "digit.h"
#define EXPORTED __attribute__((visibility ("default")))
EXPORTED int one() {
std::cout << "one: 1" << std::endl;
return 1;
}
EXPORTED int two() {
std::cout << "two: 2" << std::endl;
return 2;
}
int three() {
std::cout << "three: 3" << std::endl;
return 3;
}
int four() {
std::cout << "four: 4" << std::endl;
return 4;
}
int five() {
std::cout << "five: 5" << std::endl;
return 5;
}
// ---------
// main.cc
#include "digit.h"
int main() {
one();
two();
// three();
}
编译出动态库digit.so,size为8848字节。
g++ digit.cc -fPIC -shared -o digit.so
使用nm查看so内部的符号,发现digit.h定义的所有函数都被导出。
nm -C -D digit.so
0000000000000990 T one()
00000000000009c6 T two()
0000000000000a68 T five()
0000000000000a32 T four()
00000000000009fc T three()
编译可执行文件,size为8832字节。
g++ main.cc digit.so -Wl,-rpath=. -o main
符号可见性
编译过程中使用用visibility来控制符号可见性
利用符号的可见性防止依赖冲突。digit.so的size为8848字节,编译可执行文件,size为8832字节
g++ digit.cc -fvisibility=hidden -fPIC -shared -o digit.so
使用nm再次观察so内部的符号
0000000000000920 T one()
0000000000000956 T two()
编译main文件的时候,如果uncomment three函数,会报错,找不到three
/tmp/ccslv9VF.o: In function `main':
main.cc:(.text+0xf): undefined reference to `three()'
链接过程使用version-script来控制符号可见性
该方法仅对ELF格式文件有效。digit.map文件如下。
{
global:
*one*;
*two*;
local:
*;
};
g++ digit.cc -fPIC -shared -Wl,--version-script digit.map -o digit.so
库裁剪
符号可见性一节中介绍了导出符号的概念。在编译库的过程中,可以通过-ffunction-sections, -fdata-sections
来将未使用的function或data去除,从而减少库的体积。
g++ digit.cc -fPIC -shared -fvisibility=hidden -ffunction-sections -Wl,--gc-sections -o digit.so
digit.so的size为8744字节,digit.so中的three(), four(), five()函数都被去除。
References
- https://gcc.gnu.org/wiki/Visibility
- https://ftp.gnu.org/old-gnu/Manuals/ld-2.9.1/html_node/ld_25.html
- https://gcc.gnu.org/onlinedocs/gcc-8.4.0/gcc/Optimize-Options.html#Optimize-Options