1. 程式人生 > 其它 >strip過的動態連結庫還能用嗎?

strip過的動態連結庫還能用嗎?

技術標籤:linux

在Linux中有一個命令我們平時肯定用過,它就是strip,

圖片

咳咳,跑題了,不是這個strip。

通過strip可以移除目標檔案的符號資訊,可以減少目標檔案的體積,這裡有幾個問題:

  1. 什麼是符號?

  2. 如何使用strip?

  3. strip的作用是什麼?

  4. 動態連結庫如果被strip後還能被連結成功嗎?

  5. 靜態連結庫如果被strip後還能被連結成功嗎?

什麼是符號?

符號可以看作是連結中的粘合劑,整個連結過程需要基於符號才可以正確完成。連結過程的本質就是把多個不同的目標檔案相互粘到一起,像積木一樣各有凹凸部分,但還是可以拼接成一個整體,這個將多個目標檔案粘到一起的東西就是符號。可以將函式和變數統稱為符號,而函式名和變數名統稱為符號名。

在Linux中可以通過一些命令來檢視符號資訊:

nm命令:

$ nm test.o
U _GLOBAL_OFFSET_TABLE_
0000000000000000 T main
U puts

objdump命令:

$ objdump -t test.o

test.o:     file format elf64-x86-64

SYMBOL TABLE:
0000000000000000 l    df *ABS*  0000000000000000 test_c.cc
0000000000000000 l    d  .text  0000000000000000 .text
0000000000000000 l    d  .data  0000000000000000 .data
0000000000000000 l    d  .bss   0000000000000000 .bss
0000000000000000 l    d  .rodata        0000000000000000 .rodata
0000000000000000 l    d  .note.GNU-stack        0000000000000000 .note.GNU-stack
0000000000000000 l    d  .eh_frame      0000000000000000 .eh_frame
0000000000000000 l    d  .comment       0000000000000000 .comment
0000000000000000 g     F .text  0000000000000017 main
0000000000000000         *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 Name
0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS test_c.cc
2: 0000000000000000     0 SECTION LOCAL  DEFAULT    1
3: 0000000000000000     0 SECTION LOCAL  DEFAULT    3
4: 0000000000000000     0 SECTION LOCAL  DEFAULT    4
5: 0000000000000000     0 SECTION LOCAL  DEFAULT    5
6: 0000000000000000     0 SECTION LOCAL  DEFAULT    7
7: 0000000000000000     0 SECTION LOCAL  DEFAULT    8
8: 0000000000000000     0 SECTION LOCAL  DEFAULT    6
9: 0000000000000000    23 FUNC    GLOBAL DEFAULT    1 main
10: 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.out
0000000000200da0 d _DYNAMIC
0000000000200fa0 d _GLOBAL_OFFSET_TABLE_
000000000000089b t _GLOBAL__sub_I__Z4funcPc
0000000000000930 R _IO_stdin_used
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
0000000000000852 t _Z41__static_initialization_and_destruction_0ii
00000000000007fa T _Z4funcPc
000000000000081c T _Z4funci
U [email protected]@GLIBCXX_3.4
U [email protected]@GLIBCXX_3.4
0000000000201020 B [email protected]@GLIBCXX_3.4
0000000000000934 r _ZStL19piecewise_construct
0000000000201131 b _ZStL8__ioinit
U [email protected]@GLIBCXX_3.4
0000000000000b24 r __FRAME_END__
0000000000000940 r __GNU_EH_FRAME_HDR
0000000000201010 D __TMC_END__
0000000000201010 B __bss_start
U [email protected]@GLIBC_2.2.5
w [email protected]@GLIBC_2.2.5
0000000000201000 D __data_start
00000000000007b0 t __do_global_dtors_aux
0000000000200d98 t __do_global_dtors_aux_fini_array_entry
0000000000201008 D __dso_handle
0000000000200d88 t __frame_dummy_init_array_entry
w __gmon_start__
0000000000200d98 t __init_array_end
0000000000200d88 t __init_array_start
0000000000000920 T __libc_csu_fini
00000000000008b0 T __libc_csu_init
U [email protected]@GLIBC_2.2.5
0000000000201010 D _edata
0000000000201138 B _end
0000000000000924 T _fini
0000000000000688 T _init
00000000000006f0 T _start
0000000000201130 b completed.7698
0000000000201000 W data_start
0000000000000720 t deregister_tm_clones
00000000000007f0 t frame_dummy
000000000000083d T main
0000000000000760 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.out
nm: 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 <iostream>

void Print(int a) { std::cout << "Hello World " << a << std::endl; }

// main.cc
#include <iostream>

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.so
There are 28 section headers, starting at offset 0x1aa0:

Section Headers:
[Nr] Name              Type             Address           Offset
Size              EntSize          Flags  Link  Info  Align
[ 0]                   NULL             0000000000000000  00000000
0000000000000000  0000000000000000           0     0     0
[ 1] .note.gnu.build-i NOTE             00000000000001c8  000001c8
0000000000000024  0000000000000000   A       0     0     4
[ 2] .gnu.hash         GNU_HASH         00000000000001f0  000001f0
000000000000003c  0000000000000000   A       3     0     8
[ 3] .dynsym           DYNSYM           0000000000000230  00000230
00000000000001c8  0000000000000018   A       4     1     8
[ 4] .dynstr           STRTAB           00000000000003f8  000003f8
0000000000000189  0000000000000000   A       0     0     1
[ 5] .gnu.version      VERSYM           0000000000000582  00000582
0000000000000026  0000000000000002   A       3     0     2
[ 6] .gnu.version_r    VERNEED          00000000000005a8  000005a8
0000000000000040  0000000000000000   A       4     2     8
[ 7] .rela.dyn         RELA             00000000000005e8  000005e8
0000000000000108  0000000000000018   A       3     0     8
[ 8] .rela.plt         RELA             00000000000006f0  000006f0
0000000000000078  0000000000000018  AI       3    21     8
[ 9] .init             PROGBITS         0000000000000768  00000768
0000000000000017  0000000000000000  AX       0     0     4
[10] .plt              PROGBITS         0000000000000780  00000780
0000000000000060  0000000000000010  AX       0     0     16
[11] .plt.got          PROGBITS         00000000000007e0  000007e0
0000000000000008  0000000000000008  AX       0     0     8
[12] .text             PROGBITS         00000000000007f0  000007f0
0000000000000181  0000000000000000  AX       0     0     16
[13] .fini             PROGBITS         0000000000000974  00000974
0000000000000009  0000000000000000  AX       0     0     4
[14] .rodata           PROGBITS         000000000000097d  0000097d
000000000000000e  0000000000000000   A       0     0     1
[15] .eh_frame_hdr     PROGBITS         000000000000098c  0000098c
0000000000000034  0000000000000000   A       0     0     4
[16] .eh_frame         PROGBITS         00000000000009c0  000009c0
00000000000000bc  0000000000000000   A       0     0     8
[17] .init_array       INIT_ARRAY       0000000000200de0  00000de0
0000000000000010  0000000000000008  WA       0     0     8
[18] .fini_array       FINI_ARRAY       0000000000200df0  00000df0
0000000000000008  0000000000000008  WA       0     0     8
[19] .dynamic          DYNAMIC          0000000000200df8  00000df8
00000000000001d0  0000000000000010  WA       4     0     8
[20] .got              PROGBITS         0000000000200fc8  00000fc8
0000000000000038  0000000000000008  WA       0     0     8
[21] .got.plt          PROGBITS         0000000000201000  00001000
0000000000000040  0000000000000008  WA       0     0     8
[22] .data             PROGBITS         0000000000201040  00001040
0000000000000008  0000000000000000  WA       0     0     8
[23] .bss              NOBITS           0000000000201048  00001048
0000000000000008  0000000000000000  WA       0     0     1
[24] .comment          PROGBITS         0000000000000000  00001048
0000000000000029  0000000000000001  MS       0     0     1
[25] .symtab           SYMTAB           0000000000000000  00001078
0000000000000600  0000000000000018          26    46     8
[26] .strtab           STRTAB           0000000000000000  00001678
0000000000000330  0000000000000000           0     0     1
[27] .shstrtab         STRTAB           0000000000000000  000019a8
00000000000000f1  0000000000000000           0     0     1
Key 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.so
There are 26 section headers, starting at offset 0x1158:

Section Headers:
[Nr] Name              Type             Address           Offset
Size              EntSize          Flags  Link  Info  Align
[ 0]                   NULL             0000000000000000  00000000
0000000000000000  0000000000000000           0     0     0
[ 1] .note.gnu.build-i NOTE             00000000000001c8  000001c8
0000000000000024  0000000000000000   A       0     0     4
[ 2] .gnu.hash         GNU_HASH         00000000000001f0  000001f0
000000000000003c  0000000000000000   A       3     0     8
[ 3] .dynsym           DYNSYM           0000000000000230  00000230
00000000000001c8  0000000000000018   A       4     1     8
[ 4] .dynstr           STRTAB           00000000000003f8  000003f8
0000000000000189  0000000000000000   A       0     0     1
[ 5] .gnu.version      VERSYM           0000000000000582  00000582
0000000000000026  0000000000000002   A       3     0     2
[ 6] .gnu.version_r    VERNEED          00000000000005a8  000005a8
0000000000000040  0000000000000000   A       4     2     8
[ 7] .rela.dyn         RELA             00000000000005e8  000005e8
0000000000000108  0000000000000018   A       3     0     8
[ 8] .rela.plt         RELA             00000000000006f0  000006f0
0000000000000078  0000000000000018  AI       3    21     8
[ 9] .init             PROGBITS         0000000000000768  00000768
0000000000000017  0000000000000000  AX       0     0     4
[10] .plt              PROGBITS         0000000000000780  00000780
0000000000000060  0000000000000010  AX       0     0     16
[11] .plt.got          PROGBITS         00000000000007e0  000007e0
0000000000000008  0000000000000008  AX       0     0     8
[12] .text             PROGBITS         00000000000007f0  000007f0
0000000000000181  0000000000000000  AX       0     0     16
[13] .fini             PROGBITS         0000000000000974  00000974
0000000000000009  0000000000000000  AX       0     0     4
[14] .rodata           PROGBITS         000000000000097d  0000097d
000000000000000e  0000000000000000   A       0     0     1
[15] .eh_frame_hdr     PROGBITS         000000000000098c  0000098c
0000000000000034  0000000000000000   A       0     0     4
[16] .eh_frame         PROGBITS         00000000000009c0  000009c0
00000000000000bc  0000000000000000   A       0     0     8
[17] .init_array       INIT_ARRAY       0000000000200de0  00000de0
0000000000000010  0000000000000008  WA       0     0     8
[18] .fini_array       FINI_ARRAY       0000000000200df0  00000df0
0000000000000008  0000000000000008  WA       0     0     8
[19] .dynamic          DYNAMIC          0000000000200df8  00000df8
00000000000001d0  0000000000000010  WA       4     0     8
[20] .got              PROGBITS         0000000000200fc8  00000fc8
0000000000000038  0000000000000008  WA       0     0     8
[21] .got.plt          PROGBITS         0000000000201000  00001000
0000000000000040  0000000000000008  WA       0     0     8
[22] .data             PROGBITS         0000000000201040  00001040
0000000000000008  0000000000000000  WA       0     0     8
[23] .bss              NOBITS           0000000000201048  00001048
0000000000000008  0000000000000000  WA       0     0     1
[24] .comment          PROGBITS         0000000000000000  00001048
0000000000000029  0000000000000001  MS       0     0     1
[25] .shstrtab         STRTAB           0000000000000000  00001071
00000000000000e1  0000000000000000           0     0     1
Key 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;./main
Hello World 666

為什麼動態連結庫被strip後還可以連結成功呢?因為strip只清除普通符號表,會保留動態符號表,即dynsym、dynstr段,而動態連結依靠的就是動態符號表。

靜態連結庫如果被strip後還能被連結成功嗎?


也是先說答案,合理strip後就可以。

先貼出兩段程式碼:

// static.cc
#include <iostream>

void Print(int a) { std::cout << "Hello World " << a << std::endl; }
#include <iostream>

void Print(int a);

int main() {
    Print(666);
    return 0;
}

先將static.cc打包成libsta.a:​​​​​​​

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

檢視下靜態庫的符號:

~/test$ nm libsta.a

sta.o:
U _GLOBAL_OFFSET_TABLE_
000000000000008f t _GLOBAL__sub_I__Z5Printi
0000000000000046 t _Z41__static_initialization_and_destruction_0ii
0000000000000000 T _Z5Printi
U _ZNSolsEPFRSoS_E
U _ZNSolsEi
U _ZNSt8ios_base4InitC1Ev
U _ZNSt8ios_base4InitD1Ev
U _ZSt4cout
U _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
0000000000000000 r _ZStL19piecewise_construct
0000000000000000 b _ZStL8__ioinit
U _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
U __cxa_atexit
U __dso_handle

將libsta.a庫strip後發現什麼符號都沒有,且連結會失敗:

~/test$ strip -s libsta.a
~/test$ nm libsta.a
sta.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 one
collect2: 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.a
sta.o:
0000000000000000 T _Z5Printi
U _ZNSolsEPFRSoS_E
U _ZNSolsEi
U _ZNSt8ios_base4InitC1Ev
U _ZNSt8ios_base4InitD1Ev
U _ZSt4cout
U _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
U _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
U __cxa_atexit
U __dso_handle

從上面可以看出:通過--strip-unneeded即清除了部分符號的資訊,還能保證庫可用,減少程式體積。

關於strip,今天先介紹到這裡,相信大家看完可以對strip理解的更深刻,並能更合理的使用strip。關於編譯和連結,大家可以後臺傳送關鍵字“程式連結”瞭解更多細節。

參考資料

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

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