当前位置:首页 > 公众号精选 > 程序喵大人
[导读]在Linux中有一个命令我们平时肯定用过,它就是strip。通过strip可以移除目标文件的符号信息,可以减少目标文件的体积。

#干了这碗鸡汤

生活总是这样,不能叫人处处都满意。但我们还要热情地活下去。人活一生,值得爱的东西很多,不要因为一个不满意,就灰心。


—— 路遥《平凡的世界》



在Linux中有一个命令我们平时肯定用过,它就是strip,


咳咳,跑题了,不是这个strip。


通过strip可以移除目标文件的符号信息,可以减少目标文件的体积,这里有几个问题:

  1. 什么是符号?

  2. 如何使用strip?

  3. strip的作用是什么?

  4. 动态链接库如果被strip后还能被链接成功吗?

  5. 静态链接库如果被strip后还能被链接成功吗?


什么是符号?


符号可以看作是链接中的粘合剂,整个链接过程需要基于符号才可以正确完成。链接过程的本质就是把多个不同的目标文件相互粘到一起,像积木一样各有凹凸部分,但还是可以拼接成一个整体,这个将多个目标文件粘到一起的东西就是符号。可以将函数和变量统称为符号,而函数名和变量名统称为符号名。


在Linux中可以通过一些命令来查看符号信息:


nm命令:

$ nm test.oU _GLOBAL_OFFSET_TABLE_0000000000000000 T mainU puts

objdump命令:

$ objdump -t test.o
test.o: file format elf64-x86-64
SYMBOL TABLE:0000000000000000 l df *ABS* 0000000000000000 test_c.cc0000000000000000 l d .text 0000000000000000 .text0000000000000000 l d .data 0000000000000000 .data0000000000000000 l d .bss 0000000000000000 .bss0000000000000000 l d .rodata 0000000000000000 .rodata0000000000000000 l d .note.GNU-stack 0000000000000000 .note.GNU-stack0000000000000000 l d .eh_frame 0000000000000000 .eh_frame0000000000000000 l d .comment 0000000000000000 .comment0000000000000000 g F .text 0000000000000017 main0000000000000000 *UND* 0000000000000000 _GLOBAL_OFFSET_TABLE_0000000000000000 *UND* 0000000000000000 puts

readelf命令:

readelf -s test.o
Symbol table '.symtab' contains 12 entries: Num: Value Size Type Bind Vis Ndx Name0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND1: 0000000000000000 0 FILE LOCAL DEFAULT ABS test_c.cc2: 0000000000000000 0 SECTION LOCAL DEFAULT 13: 0000000000000000 0 SECTION LOCAL DEFAULT 34: 0000000000000000 0 SECTION LOCAL DEFAULT 45: 0000000000000000 0 SECTION LOCAL DEFAULT 56: 0000000000000000 0 SECTION LOCAL DEFAULT 77: 0000000000000000 0 SECTION LOCAL DEFAULT 88: 0000000000000000 0 SECTION LOCAL DEFAULT 69: 0000000000000000 23 FUNC GLOBAL DEFAULT 1 main10: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _GLOBAL_OFFSET_TABLE_11: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND puts


如何使用strip?


在Linux中可以使用man strip查看strip使用方法,最主要的就是移除所有符号的-s参数,用于清除所有的符号信息:

strip -s xxx

在使用strip之前先使用nm查看下可执行程序的符号信息:

~/test$ nm a.out0000000000200da0 d _DYNAMIC0000000000200fa0 d _GLOBAL_OFFSET_TABLE_000000000000089b t _GLOBAL__sub_I__Z4funcPc0000000000000930 R _IO_stdin_usedw _ITM_deregisterTMCloneTablew _ITM_registerTMCloneTable0000000000000852 t _Z41__static_initialization_and_destruction_0ii00000000000007fa T _Z4funcPc000000000000081c T _Z4funciU _ZNSt8ios_base4InitC1Ev@@GLIBCXX_3.4U _ZNSt8ios_base4InitD1Ev@@GLIBCXX_3.40000000000201020 B _ZSt4cout@@GLIBCXX_3.40000000000000934 r _ZStL19piecewise_construct0000000000201131 b _ZStL8__ioinitU _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@@GLIBCXX_3.40000000000000b24 r __FRAME_END__0000000000000940 r __GNU_EH_FRAME_HDR0000000000201010 D __TMC_END__0000000000201010 B __bss_startU __cxa_atexit@@GLIBC_2.2.5w __cxa_finalize@@GLIBC_2.2.50000000000201000 D __data_start00000000000007b0 t __do_global_dtors_aux0000000000200d98 t __do_global_dtors_aux_fini_array_entry0000000000201008 D __dso_handle0000000000200d88 t __frame_dummy_init_array_entryw __gmon_start__0000000000200d98 t __init_array_end0000000000200d88 t __init_array_start0000000000000920 T __libc_csu_fini00000000000008b0 T __libc_csu_initU __libc_start_main@@GLIBC_2.2.50000000000201010 D _edata0000000000201138 B _end0000000000000924 T _fini0000000000000688 T _init00000000000006f0 T _start0000000000201130 b completed.76980000000000201000 W data_start0000000000000720 t deregister_tm_clones00000000000007f0 t frame_dummy000000000000083d T main0000000000000760 t register_tm_clones

当前这个可执行程序的文件大小是8840字节:

-rwxrwxrwx 1 a a 8840 Nov 29 14:54 a.out

使用strip清除符号信息:

~/test$ strip -s a.out

strip后再查看可执行文件的符号信息:

~/test$ nm a.outnm: a.out: no symbols

发现什么符号都没有了,但还是可以执行。

strip后的可执行程序文件大小是6120字节:

-rwxrwxrwx 1 a a 6120 Nov 29 14:54 a.out

由此可见通过strip我们可以减少程序的体积。

strip的作用是什么?


前面已经大体介绍过,strip最大的作用就是可以减少程序的体积,一般公司对发布的程序体积要求是极其严格的,strip命令是减少程序体积的一个很有效的方法。另一个作用就是提高了安全性,没有了这些符号,别人分析没有符号的程序会变得更加困难。


动态链接库如果被strip后还能被链接成功吗?


先说答案,可以。

先贴出两段代码:

// shared.cc#include 
void Print(int a) std::cout << "Hello World " << a << std::endl; }

// main.cc#include 
void Print(int a);
int main() {    Print(666);    return 0;}

将shared.cc编成一个动态链接库:

g++ shared.cc -o shared.so -shared -fPIC

使用readelf查看链接库的符号信息:

~/test$ readelf -S shared.soThere are 28 section headers, starting at offset 0x1aa0:
Section Headers:[Nr] Name Type Address OffsetSize EntSize Flags Link Info Align[ 0] NULL 0000000000000000 000000000000000000000000 0000000000000000 0 0 0[ 1] .note.gnu.build-i NOTE 00000000000001c8 000001c80000000000000024 0000000000000000 A 0 0 4[ 2] .gnu.hash GNU_HASH 00000000000001f0 000001f0000000000000003c 0000000000000000 A 3 0 8[ 3] .dynsym DYNSYM 0000000000000230 0000023000000000000001c8 0000000000000018 A 4 1 8[ 4] .dynstr STRTAB 00000000000003f8 000003f80000000000000189 0000000000000000 A 0 0 1[ 5] .gnu.version VERSYM 0000000000000582 000005820000000000000026 0000000000000002 A 3 0 2[ 6] .gnu.version_r VERNEED 00000000000005a8 000005a80000000000000040 0000000000000000 A 4 2 8[ 7] .rela.dyn RELA 00000000000005e8 000005e80000000000000108 0000000000000018 A 3 0 8[ 8] .rela.plt RELA 00000000000006f0 000006f00000000000000078 0000000000000018 AI 3 21 8[ 9] .init PROGBITS 0000000000000768 000007680000000000000017 0000000000000000 AX 0 0 4[10] .plt PROGBITS 0000000000000780 000007800000000000000060 0000000000000010 AX 0 0 16[11] .plt.got PROGBITS 00000000000007e0 000007e00000000000000008 0000000000000008 AX 0 0 8[12] .text PROGBITS 00000000000007f0 000007f00000000000000181 0000000000000000 AX 0 0 16[13] .fini PROGBITS 0000000000000974 000009740000000000000009 0000000000000000 AX 0 0 4[14] .rodata PROGBITS 000000000000097d 0000097d000000000000000e 0000000000000000 A 0 0 1[15] .eh_frame_hdr PROGBITS 000000000000098c 0000098c0000000000000034 0000000000000000 A 0 0 4[16] .eh_frame PROGBITS 00000000000009c0 000009c000000000000000bc 0000000000000000 A 0 0 8[17] .init_array INIT_ARRAY 0000000000200de0 00000de00000000000000010 0000000000000008 WA 0 0 8[18] .fini_array FINI_ARRAY 0000000000200df0 00000df00000000000000008 0000000000000008 WA 0 0 8[19] .dynamic DYNAMIC 0000000000200df8 00000df800000000000001d0 0000000000000010 WA 4 0 8[20] .got PROGBITS 0000000000200fc8 00000fc80000000000000038 0000000000000008 WA 0 0 8[21] .got.plt PROGBITS 0000000000201000 000010000000000000000040 0000000000000008 WA 0 0 8[22] .data PROGBITS 0000000000201040 000010400000000000000008 0000000000000000 WA 0 0 8[23] .bss NOBITS 0000000000201048 000010480000000000000008 0000000000000000 WA 0 0 1[24] .comment PROGBITS 0000000000000000 000010480000000000000029 0000000000000001 MS 0 0 1[25] .symtab SYMTAB 0000000000000000 000010780000000000000600 0000000000000018 26 46 8[26] .strtab STRTAB 0000000000000000 000016780000000000000330 0000000000000000 0 0 1[27] .shstrtab STRTAB 0000000000000000 000019a800000000000000f1 0000000000000000 0 0 1Key to Flags:W (write), A (alloc), X (execute), M (merge), S (strings), I (info),L (link order), O (extra OS processing required), G (group), T (TLS),C (compressed), x (unknown), o (OS specific), E (exclude),l (large), p (processor specific)

注意这里有28个符号段,主要有symtab、strtab、dynsym、dynstr段。


strip后再看下符号信息:

~/test$ readelf -S shared.soThere are 26 section headers, starting at offset 0x1158:
Section Headers:[Nr] Name Type Address OffsetSize EntSize Flags Link Info Align[ 0] NULL 0000000000000000 000000000000000000000000 0000000000000000 0 0 0[ 1] .note.gnu.build-i NOTE 00000000000001c8 000001c80000000000000024 0000000000000000 A 0 0 4[ 2] .gnu.hash GNU_HASH 00000000000001f0 000001f0000000000000003c 0000000000000000 A 3 0 8[ 3] .dynsym DYNSYM 0000000000000230 0000023000000000000001c8 0000000000000018 A 4 1 8[ 4] .dynstr STRTAB 00000000000003f8 000003f80000000000000189 0000000000000000 A 0 0 1[ 5] .gnu.version VERSYM 0000000000000582 000005820000000000000026 0000000000000002 A 3 0 2[ 6] .gnu.version_r VERNEED 00000000000005a8 000005a80000000000000040 0000000000000000 A 4 2 8[ 7] .rela.dyn RELA 00000000000005e8 000005e80000000000000108 0000000000000018 A 3 0 8[ 8] .rela.plt RELA 00000000000006f0 000006f00000000000000078 0000000000000018 AI 3 21 8[ 9] .init PROGBITS 0000000000000768 000007680000000000000017 0000000000000000 AX 0 0 4[10] .plt PROGBITS 0000000000000780 000007800000000000000060 0000000000000010 AX 0 0 16[11] .plt.got PROGBITS 00000000000007e0 000007e00000000000000008 0000000000000008 AX 0 0 8[12] .text PROGBITS 00000000000007f0 000007f00000000000000181 0000000000000000 AX 0 0 16[13] .fini PROGBITS 0000000000000974 000009740000000000000009 0000000000000000 AX 0 0 4[14] .rodata PROGBITS 000000000000097d 0000097d000000000000000e 0000000000000000 A 0 0 1[15] .eh_frame_hdr PROGBITS 000000000000098c 0000098c0000000000000034 0000000000000000 A 0 0 4[16] .eh_frame PROGBITS 00000000000009c0 000009c000000000000000bc 0000000000000000 A 0 0 8[17] .init_array INIT_ARRAY 0000000000200de0 00000de00000000000000010 0000000000000008 WA 0 0 8[18] .fini_array FINI_ARRAY 0000000000200df0 00000df00000000000000008 0000000000000008 WA 0 0 8[19] .dynamic DYNAMIC 0000000000200df8 00000df800000000000001d0 0000000000000010 WA 4 0 8[20] .got PROGBITS 0000000000200fc8 00000fc80000000000000038 0000000000000008 WA 0 0 8[21] .got.plt PROGBITS 0000000000201000 000010000000000000000040 0000000000000008 WA 0 0 8[22] .data PROGBITS 0000000000201040 000010400000000000000008 0000000000000000 WA 0 0 8[23] .bss NOBITS 0000000000201048 000010480000000000000008 0000000000000000 WA 0 0 1[24] .comment PROGBITS 0000000000000000 000010480000000000000029 0000000000000001 MS 0 0 1[25] .shstrtab STRTAB 0000000000000000 0000107100000000000000e1 0000000000000000 0 0 1Key to Flags:W (write), A (alloc), X (execute), M (merge), S (strings), I (info),L (link order), O (extra OS processing required), G (group), T (TLS),C (compressed), x (unknown), o (OS specific), E (exclude),l (large), p (processor specific)

注意这里有26个符号段,主要有dynsym、dynstr段,这两个段symtab、strtab被清除掉。


而且依旧可以被链接成功并且成功执行程序:

~/test$ g++ main.cc -o main ./shared.so;./mainHello World 666

为什么动态链接库被strip后还可以链接成功呢?因为strip只清除普通符号表,会保留动态符号表,即dynsym、dynstr段,而动态链接依靠的就是动态符号表。


静态链接库如果被strip后还能被链接成功吗?


也是先说答案,合理strip后就可以。


先贴出两段代码:

// static.cc#include 
void Print(int a) std::cout << "Hello World " << a << std::endl; }

#include 
void Print(int a);
int main() {    Print(666);    return 0;}

先将static.cc打包成libsta.a:

gcc -c staticd.cc -o sta.oar -r libsta.a sta.o

查看下静态库的符号:

~/test$ nm libsta.a
sta.o:U _GLOBAL_OFFSET_TABLE_000000000000008f t _GLOBAL__sub_I__Z5Printi0000000000000046 t _Z41__static_initialization_and_destruction_0ii0000000000000000 T _Z5PrintiU _ZNSolsEPFRSoS_EU _ZNSolsEiU _ZNSt8ios_base4InitC1EvU _ZNSt8ios_base4InitD1EvU _ZSt4coutU _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_0000000000000000 r _ZStL19piecewise_construct0000000000000000 b _ZStL8__ioinitU _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKcU __cxa_atexitU __dso_handle

将libsta.a库strip后发现什么符号都没有,且链接会失败:

~/test$ strip -s libsta.a~/test$ nm libsta.asta.o:nm: sta.o: no symbols~/test$ g++ main.cc -o main -L. -lsta; ./main./libsta.a: error adding symbols: Archive has no index; run ranlib to add onecollect2: error: ld returned 1 exit status-bash: ./main: No such file or directory

那难道静态链接库就不能strip了吗?不strip的文件岂不是体积很大?


其实还是可以strip的,但需要合理的使用strip,这里需要换一个strip的参数,就是--strip-unneeded,它确保strip掉的是没有用的符号,保留用于链接的符号,尽管--strip-unneeded不如-s清除的彻底,但是保留了很多有用的信息,确保该链接库是可用的。

~/test$ strip --strip-unneeded libsta.a~/test$ nm libsta.asta.o:0000000000000000 T _Z5PrintiU _ZNSolsEPFRSoS_EU _ZNSolsEiU _ZNSt8ios_base4InitC1EvU _ZNSt8ios_base4InitD1EvU _ZSt4coutU _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_U _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKcU __cxa_atexitU __dso_handle

从上面可以看出:通过--strip-unneeded即清除了部分符号的信息,还能保证库可用,减少程序体积。


关于strip,今天先介绍到这里,相信大家看完可以对strip理解的更深刻,并能更合理的使用strip。关于编译和链接,大家可以后台发送关键字“程序链接”了解更多细节。


参考资料

https://zhuanlan.zhihu.com/p/72475595

https://xuanxuanblingbling.github.io/ctf/tools/2019/09/06/symbol/



往期推荐



免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

9月2日消息,不造车的华为或将催生出更大的独角兽公司,随着阿维塔和赛力斯的入局,华为引望愈发显得引人瞩目。

关键字: 阿维塔 塞力斯 华为

加利福尼亚州圣克拉拉县2024年8月30日 /美通社/ -- 数字化转型技术解决方案公司Trianz今天宣布,该公司与Amazon Web Services (AWS)签订了...

关键字: AWS AN BSP 数字化

伦敦2024年8月29日 /美通社/ -- 英国汽车技术公司SODA.Auto推出其旗舰产品SODA V,这是全球首款涵盖汽车工程师从创意到认证的所有需求的工具,可用于创建软件定义汽车。 SODA V工具的开发耗时1.5...

关键字: 汽车 人工智能 智能驱动 BSP

北京2024年8月28日 /美通社/ -- 越来越多用户希望企业业务能7×24不间断运行,同时企业却面临越来越多业务中断的风险,如企业系统复杂性的增加,频繁的功能更新和发布等。如何确保业务连续性,提升韧性,成...

关键字: 亚马逊 解密 控制平面 BSP

8月30日消息,据媒体报道,腾讯和网易近期正在缩减他们对日本游戏市场的投资。

关键字: 腾讯 编码器 CPU

8月28日消息,今天上午,2024中国国际大数据产业博览会开幕式在贵阳举行,华为董事、质量流程IT总裁陶景文发表了演讲。

关键字: 华为 12nm EDA 半导体

8月28日消息,在2024中国国际大数据产业博览会上,华为常务董事、华为云CEO张平安发表演讲称,数字世界的话语权最终是由生态的繁荣决定的。

关键字: 华为 12nm 手机 卫星通信

要点: 有效应对环境变化,经营业绩稳中有升 落实提质增效举措,毛利润率延续升势 战略布局成效显著,战新业务引领增长 以科技创新为引领,提升企业核心竞争力 坚持高质量发展策略,塑强核心竞争优势...

关键字: 通信 BSP 电信运营商 数字经济

北京2024年8月27日 /美通社/ -- 8月21日,由中央广播电视总台与中国电影电视技术学会联合牵头组建的NVI技术创新联盟在BIRTV2024超高清全产业链发展研讨会上宣布正式成立。 活动现场 NVI技术创新联...

关键字: VI 传输协议 音频 BSP

北京2024年8月27日 /美通社/ -- 在8月23日举办的2024年长三角生态绿色一体化发展示范区联合招商会上,软通动力信息技术(集团)股份有限公司(以下简称"软通动力")与长三角投资(上海)有限...

关键字: BSP 信息技术
关闭
关闭