Linux executable file slimming instruction strip usage example

When developing software under a Linux system, the output executable file can be large or small. If the running environment is on a server, the resources may be sufficient, but if it is in an embedded environment, storage resources must be competed for every inch. Therefore, there will be a need to slim down the executable file, such as using the strip command.

Regarding the operation of strip, some people jokingly call it “taking off clothes”. My description here will not be so naked and will be changed to “slimming down”.

In the man manual, the description of strip is:

discard symbols from object files

This means that the symbol information in the file is removed, so that the file size is reduced without affecting the execution of the executable file. In addition, removing some information from the executable file helps prevent reverse engineering.

The strip command format is as follows:

strip [options] filename …

Below I use a few examples to illustrate how to use this command:

It’s hard to make a meal without rice, so you need to prepare an executable file test first. The source file test.cpp to generate the executable file is as follows:

#include<stdio.h>

class test{
};

int main(void)
{
    printf("sizeof(class test)=%d\\
", (int)sizeof(test));
    return 0;
}

Remove all symbol information

Let’s first take a look at the internal symbol information of the executable file, read it through the command readelf

$ readelf -s test

Symbol table '.dynsym' contains 7 entries:
   Num: Value Size Type Bind Vis Ndx Name
     0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
     1: 0000000000000000 0 NOTYPE WEAK DEFAULT _ITM_deregisterTMCloneTab
     2: 0000000000000000 0 FUNC GLOBAL DEFAULT UND printf@GLIBC_2.2.5 (2)
     3: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.2.5 (2)
     4: 0000000000000000 0 NOTYPE WEAK DEFAULT __gmon_start__
     5: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMCloneTable
     6: 0000000000000000 0 FUNC WEAK DEFAULT __cxa_finalize@GLIBC_2.2.5 (2)

Symbol table '.symtab' contains 63 entries:
   Num: Value Size Type Bind Vis Ndx Name
     0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
     1: 0000000000000238 0 SECTION LOCAL DEFAULT 1
     2: 0000000000000254 0 SECTION LOCAL DEFAULT 2
     3: 0000000000000274 0 SECTION LOCAL DEFAULT 3
     4: 0000000000000298 0 SECTION LOCAL DEFAULT 4
     5: 00000000000002b8 0 SECTION LOCAL DEFAULT 5
     6: 0000000000000360 0 SECTION LOCAL DEFAULT 6
     7: 00000000000003e4 0 SECTION LOCAL DEFAULT 7
     8: 00000000000003f8 0 SECTION LOCAL DEFAULT 8
     9: 0000000000000418 0 SECTION LOCAL DEFAULT 9
    10: 00000000000004d8 0 SECTION LOCAL DEFAULT 10
    11: 00000000000004f0 0 SECTION LOCAL DEFAULT 11
    12: 0000000000000510 0 SECTION LOCAL DEFAULT 12
    13: 0000000000000530 0 SECTION LOCAL DEFAULT 13
    14: 0000000000000540 0 SECTION LOCAL DEFAULT 14
    15: 00000000000006e4 0 SECTION LOCAL DEFAULT 15
    16: 00000000000006f0 0 SECTION LOCAL DEFAULT 16
    17: 000000000000070c 0 SECTION LOCAL DEFAULT 17
    18: 0000000000000748 0 SECTION LOCAL DEFAULT 18
    19: 0000000000200db8 0 SECTION LOCAL DEFAULT 19
    20: 0000000000200dc0 0 SECTION LOCAL DEFAULT 20
    21: 0000000000200dc8 0 SECTION LOCAL DEFAULT 21
    22: 0000000000200fb8 0 SECTION LOCAL DEFAULT 22
    23: 0000000000201000 0 SECTION LOCAL DEFAULT 23
    24: 0000000000201010 0 SECTION LOCAL DEFAULT 24
    25: 0000000000000000 0 SECTION LOCAL DEFAULT 25
    26: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtstuff.c
    27: 0000000000000570 0 FUNC LOCAL DEFAULT 14 deregister_tm_clones
    28: 00000000000005b0 0 FUNC LOCAL DEFAULT 14 register_tm_clones
    29: 0000000000000600 0 FUNC LOCAL DEFAULT 14 __do_global_dtors_aux
    30: 0000000000201010 1 OBJECT LOCAL DEFAULT 24 completed.7698
    31: 0000000000200dc0 0 OBJECT LOCAL DEFAULT 20 __do_global_dtors_aux_fin
    32: 0000000000000640 0 FUNC LOCAL DEFAULT 14 frame_dummy
    33: 0000000000200db8 0 OBJECT LOCAL DEFAULT 19 __frame_dummy_init_array_
    34: 0000000000000000 0 FILE LOCAL DEFAULT ABS test.cpp
    35: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtstuff.c
    36: 000000000000084c 0 OBJECT LOCAL DEFAULT 18 __FRAME_END__
    37: 0000000000000000 0 FILE LOCAL DEFAULT ABS
    38: 0000000000200dc0 0 NOTYPE LOCAL DEFAULT 19 __init_array_end
    39: 0000000000200dc8 0 OBJECT LOCAL DEFAULT 21 _DYNAMIC
    40: 0000000000200db8 0 NOTYPE LOCAL DEFAULT 19 __init_array_start
    41: 000000000000070c 0 NOTYPE LOCAL DEFAULT 17 __GNU_EH_FRAME_HDR
    42: 0000000000200fb8 0 OBJECT LOCAL DEFAULT 22 _GLOBAL_OFFSET_TABLE_
    43: 00000000000006e0 2 FUNC GLOBAL DEFAULT 14 __libc_csu_fini
    44: 0000000000000000 0 NOTYPE WEAK DEFAULT _ITM_deregisterTMCloneTab
    45: 0000000000201000 0 NOTYPE WEAK DEFAULT 23 data_start
    46: 0000000000201010 0 NOTYPE GLOBAL DEFAULT 23 _edata
    47: 00000000000006e4 0 FUNC GLOBAL DEFAULT 15 _fini
    48: 0000000000000000 0 FUNC GLOBAL DEFAULT UND printf@@GLIBC_2.2.5
    49: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@@GLIBC_
    50: 0000000000201000 0 NOTYPE GLOBAL DEFAULT 23 __data_start
    51: 0000000000000000 0 NOTYPE WEAK DEFAULT __gmon_start__
    52: 0000000000201008 0 OBJECT GLOBAL HIDDEN 23 __dso_handle
    53: 00000000000006f0 4 OBJECT GLOBAL DEFAULT 16 _IO_stdin_used
    54: 0000000000000670 101 FUNC GLOBAL DEFAULT 14 __libc_csu_init
    55: 0000000000201018 0 NOTYPE GLOBAL DEFAULT 24 _end
    56: 0000000000000540 43 FUNC GLOBAL DEFAULT 14 _start
    57: 0000000000201010 0 NOTYPE GLOBAL DEFAULT 24 __bss_start
    58: 000000000000064a 33 FUNC GLOBAL DEFAULT 14 main
    59: 0000000000201010 0 OBJECT GLOBAL HIDDEN 23 __TMC_END__
    60: 0000000000000000 0 NOTYPE WEAK DEFAULT _ITM_registerTMCloneTable
    61: 0000000000000000 0 FUNC WEAK DEFAULT UND __cxa_finalize@@GLIBC_2.2
    62: 00000000000004f0 0 FUNC GLOBAL DEFAULT 11 _init

Use strip plus -s to remove all symbol information, and then read the file information again to see

$ strip -s test
$ readelf -s test

Symbol table '.dynsym' contains 7 entries:
   Num: Value Size Type Bind Vis Ndx Name
     0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
     1: 0000000000000000 0 NOTYPE WEAK DEFAULT _ITM_deregisterTMCloneTab
     2: 0000000000000000 0 FUNC GLOBAL DEFAULT UND printf@GLIBC_2.2.5 (2)
     3: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.2.5 (2)
     4: 0000000000000000 0 NOTYPE WEAK DEFAULT __gmon_start__
     5: 0000000000000000 0 NOTYPE WEAK DEFAULT _ITM_registerTMCloneTable
     6: 0000000000000000 0 FUNC WEAK DEFAULT __cxa_finalize@GLIBC_2.2.5 (2)

From the output above, you can see that the symbol information within the file has been removed. However, the .dynsym and .dynstr table information still exists. These two table information are used for dynamic link library linking, so executing strip -s will not affect the use of the dynamic link library.

Remove debugging information

If you only need to remove debugging information, you can use the option –strip-debug

strip --strip-debug test

Then compare the read symbol information and find that the following content is missing from the file information after slimming down.

 ...
    26: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtstuff.c
    ...
    34: 0000000000000000 0 FILE LOCAL DEFAULT ABS test.cpp
    35: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtstuff.c
    ...

Remove specific field information

In some cases, we need to remove specific field header information, then we can use the option -R

First, let’s read the field header information of the unslimmed executable file.

$ readelf -S test

There are 29 section headers, starting at offset 0x18b8:

Section Headers:
  [Nr] Name Type Address Offset
       Size EntSize Flags Link Info Align
  [0] NULL 0000000000000000 00000000
       0000000000000000 0000000000000000 0 0 0
  [1] .interp PROGBITS 0000000000000238 00000238
       000000000000001c 0000000000000000 A 0 0 1
  [2] .note.ABI-tag NOTE 0000000000000254 00000254
       0000000000000020 0000000000000000 A 0 0 4
  [3] .note.gnu.build-i NOTE 0000000000000274 00000274
       0000000000000024 0000000000000000 A 0 0 4
  [4] .gnu.hash GNU_HASH 0000000000000298 00000298
       000000000000001c 0000000000000000 A 5 0 8
  [5] .dynsym DYNSYM 00000000000002b8 000002b8
       00000000000000a8 0000000000000018 A 6 1 8
  [6] .dynstr STRTAB 0000000000000360 00000360
       0000000000000084 0000000000000000 A 0 0 1
  [7] .gnu.versionVERSYM 00000000000003e4 000003e4
       000000000000000e 0000000000000002 A 5 0 2
  [8] .gnu.version_r VERNEED 00000000000003f8 000003f8
       0000000000000020 0000000000000000 A 6 1 8
  [9] .rela.dyn RELA 0000000000000418 00000418
       00000000000000c0 0000000000000018 A 5 0 8
  [10] .rela.plt RELA 00000000000004d8 000004d8
       0000000000000018 0000000000000018 AI 5 22 8
  [11] .init PROGBITS 00000000000004f0 000004f0
       0000000000000017 0000000000000000 AX 0 0 4
  [12] .plt PROGBITS 0000000000000510 00000510
       0000000000000020 0000000000000010 AX 0 0 16
  [13] .plt.got PROGBITS 0000000000000530 00000530
       0000000000000008 0000000000000008 AX 0 0 8
  [14] .text PROGBITS 0000000000000540 00000540
       00000000000001a2 0000000000000000 AX 0 0 16
  [15] .fini PROGBITS 00000000000006e4 000006e4
       0000000000000009 0000000000000000 AX 0 0 4
  [16] .rodata PROGBITS 00000000000006f0 000006f0
       000000000000001b 0000000000000000 A 0 0 4
  [17] .eh_frame_hdr PROGBITS 000000000000070c 0000070c
       000000000000003c 0000000000000000 A 0 0 4
  [18] .eh_frame PROGBITS 0000000000000748 00000748
       0000000000000108 0000000000000000 A 0 0 8
  [19] .init_array INIT_ARRAY 0000000000200db8 00000db8
       0000000000000008 0000000000000008 WA 0 0 8
  [20] .fini_array FINI_ARRAY 0000000000200dc0 00000dc0
       0000000000000008 0000000000000008 WA 0 0 8
  [21] .dynamic DYNAMIC 0000000000200dc8 00000dc8
       00000000000001f0 0000000000000010 WA 6 0 8
  [22] .got PROGBITS 0000000000200fb8 00000fb8
       0000000000000048 0000000000000008 WA 0 0 8
  [23] .data PROGBITS 0000000000201000 00001000
       0000000000000010 0000000000000000 WA 0 0 8
  [24] .bss NOBITS 0000000000201010 00001010
       0000000000000008 0000000000000000 WA 0 0 1
  [25] .comment PROGBITS 0000000000000000 00001010
       0000000000000029 0000000000000001 MS 0 0 1
  [26] .symtab SYMTAB 0000000000000000 00001040
       0000000000000588 0000000000000018 27 39 8
  [27] .strtab STRTAB 0000000000000000 000015c8
       00000000000001f2 0000000000000000 0 0 1
  [28] .shstrtab STRTAB 0000000000000000 000017ba
       00000000000000fe 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)

If you need to remove the field header .gnu.version information, then execute

$ strip -R .gnu.version test

Use readelf -S again to read file information and you will find that the specified information has been removed.

Remove unnecessary symbol information for relocation

If you need to remove symbol information that is not needed for relocation, you can use the option –strip-unneeded

$ strip --strip-unneeded test

For example, when you need to slim down the static link library file, this option does not affect the link use of the static library.

Keep some symbol information when slimming

In some cases, slimming down executable files is not a one-size-fits-all approach. You may also need to leave out some symbol information. You can use the option -K

$ strip -s -Kframe_dummy test

$ readelf -s test
Symbol table '.dynsym' contains 7 entries:
   Num: Value Size Type Bind Vis Ndx Name
     0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
     1: 0000000000000000 0 NOTYPE WEAK DEFAULT _ITM_deregisterTMCloneTab
     2: 0000000000000000 0 FUNC GLOBAL DEFAULT UND printf@GLIBC_2.2.5 (2)
     3: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.2.5 (2)
     4: 0000000000000000 0 NOTYPE WEAK DEFAULT __gmon_start__
     5: 0000000000000000 0 NOTYPE WEAK DEFAULT _ITM_registerTMCloneTable
     6: 0000000000000000 0 FUNC WEAK DEFAULT __cxa_finalize@GLIBC_2.2.5 (2)

Symbol table '.symtab' contains 27 entries:
   Num: Value Size Type Bind Vis Ndx Name
    ...
     1: 0000000000000640 0 FUNC LOCAL DEFAULT 14 frame_dummy
    ...

It can be seen that the symbols specified after the -K option are retained and are not removed together.

In addition, the -K option can be used multiple times at the same time in the same strip command.

Remove specific symbol information

If you only need to remove specific symbol information and keep other symbol information, you can use option -N

Assume that the executable file has not been slimmed down, and then execute the following instructions

$ strip -Nframe_dummy test

After executing the above command, use readelf -s to read the symbol information. You can find that the symbols following the -N option have been removed.

Save file as

After removing the relevant information, if you want to keep the internal information of the original executable file unchanged and save the slimmed-down file as a new file, you can use the option -o

$ strip -s -otest_striped test

After outputting the slimming file, use readelf -s to read the files test_striped and test respectively. By comparison, it can be seen that the symbol information of the file test has not changed, while the symbol information of the file test_striped has been removed.

Keep original access and modification times

When you need to retain the original access and modification time information, you can use the option -p

Assuming that the executable file has never been slimmed down, use stat to see the detailed status of the file.

$ stat test

  File: test
  Size: 8304 Blocks: 24 IO Block: 4096 regular file
Device: 801h/2049d Inode: 803646 Links: 1
Access: (0775/-rwxrwxr-x) Uid: ( 1000/ if) Gid: ( 1000/ if)
Access: 2023-06-26 21:02:49.274028914 + 0800
Modify: 2023-06-26 21:02:49.274028914 + 0800
Change: 2023-06-26 21:02:49.274028914 + 0800
 Birth: -

Then execute the strip command and try to retain the original access and modification time information

$ strip -s -p test

Take a look at the detailed status of the file

$ stat test
  File: test
  Size: 6120 Blocks: 16 IO Block: 4096 regular file
Device: 801h/2049d Inode: 803647 Links: 1
Access: (0775/-rwxrwxr-x) Uid: ( 1000/ if) Gid: ( 1000/ if)
Access: 2023-06-26 21:02:49.000000000 + 0800
Modify: 2023-06-26 21:02:49.000000000 + 0800
Change: 2023-06-26 21:10:53.991892402 + 0800
 Birth: -

It can be seen that the original access and modification time information is retained, down to the unit of seconds.

Read option content from file

During the operation and maintenance process, in order to facilitate flexible switching of different configurations, parameter configurations are often stored in files. Different requirements require different configuration files.

The strip command also supports reading configuration options from files, in the format:

$ strip @filename

The file filename contains parameter options in text form, etc. for example

$ echo "-s test" > options.txt
$ strip @options.txt

The effect is equivalent to

$ strip -s test

Output detailed process

Detailed information can be output at the same time during the execution of the strip command, use the option -v

$ strip -v -s test
copy from `test' [elf64-x86-64] to `stPp23jj' [elf64-x86-64]

The knowledge points of the article match the official knowledge files, and you can further learn relevant knowledge. CS entry skill treeLinux introductionFirst introduction to Linux 38067 people are learning the system