编译一个尽量小的so

  1. 参考文档

  2. gcc -ffunction-sections

    • 文档

      • 原文

        '''
        Place each function or data item into its own section in the output file if the
        target supports arbitrary sections. The name of the function or the name of
        the data item determines the section’s name in the output file.
        
        Use these options on systems where the linker can perform optimizations to
        improve locality of reference in the instruction space. Most systems using the
        ELF object format have linkers with such optimizations. On AIX, the linker
        rearranges sections (CSECTs) based on the call graph. The performance impact
        varies.
        
        Together with a linker garbage collection (linker ‘--gc-sections’ option) these
        options may lead to smaller statically-linked executables (after stripping).
        On ELF/DWARF systems these options do not degenerate the quality of the
        debug information. There could be issues with other object files/debug info
        formats.
        
        Only use these options when there are significant benefits from doing so. When
        you specify these options, the assembler and linker create larger object and
        executable files and are also slower. These options affect code generation. They
        prevent optimizations by the compiler and assembler using relative locations
        inside a translation unit since the locations are unknown until link time. An
        example of such an optimization is relaxing calls to short call instructions.
        '''
        
      • 翻译

        • 如果目标文件支持任意数量的段,那么为每个函数和全局,静态变量创建一个段,并存放对应代码.段名是.text | .data前缀,后跟对应的名字.函数对应.text开头,变量对应.data开头.
        • gcc -c test.c -ffunction-sections -fdata-sections && readelf -e test.o,注意test.c里面的函数变量尽量不要和API重名了.否则结果会异常.
        • 链接器可以执行将调用依赖的代码紧密的拼凑在一起.以此优化本地指令空间. 大多数ELF系统都支持这个选项.有的系统甚至根据调用图来对代码顺序进行重排.
        • ld--gc-sectioins选项,可以创建一个静态链接的文件,代码区仅仅包含自己用到的变量和函数.没有其他额外的符号.
        • ELF/DWARF格式的调试信息和这个编译选项不存在冲突.但是其他格式的可能存在冲突.
        • 真的可以通过这个编译选项获得好处的时候才用,不然,尽量不要用. 这个编译选项会导致汇编器和链接器生成的文件变大,而且可执行文件也会执行得更慢. 这些文件还会影响代码生成. 这个代码禁止相对路径优化. 因为都变成了段代码,不知道位置,只有链接的时候才知道.
    • 案例

      • 代码

        void show(){
                     
                     }
        void cool(){
                     
                     }
        int main()
        {
                     
                     
           show();
           cool();
        }
        
        
        • gcc -c test.c -ffunction-sections -fdata-sections
      • test.o:     file format elf64-x86-64
        
        
        Disassembly of section .text.show:
        
        0000000000000000 <show>:
          0:   55                      push   %rbp
          1:   48 89 e5                mov    %rsp,%rbp
          4:   5d                      pop    %rbp
          5:   c3                      retq
        
        Disassembly of section .text.cool:
        
        0000000000000000 <cool>:
          0:   55                      push   %rbp
          1:   48 89 e5                mov    %rsp,%rbp
          4:   5d                      pop    %rbp
          5:   c3                      retq
        
        Disassembly of section .text.main:
        
        0000000000000000 <main>:
          0:   55                      push   %rbp
          1:   48 89 e5                mov    %rsp,%rbp
          4:   b8 00 00 00 00          mov    $0x0,%eax
          9:   e8 00 00 00 00          callq  e <main+0xe>
          e:   b8 00 00 00 00          mov    $0x0,%eax
         13:   e8 00 00 00 00          callq  18 <main+0x18>
         18:   5d                      pop    %rbp
         19:   c3                      retq
        
        • objdump -d test.o
      • 说明

        • 生成了函数对应的段.
      • 去掉main中的show

        • 编译后的代码gcc -Wl,--gc-sections test.o -o test.out
  3. ld --gc-sections

    • 说明

      • 功能

        • 尽量的保留用到的段,没有用的都删掉.
    • 文档

      • 原文

        Enable garbage collection of unused input sections. It is ignored on targets
        that do not support this option. The default behaviour (of not performing this
        garbage collection) can be restored by specifying ‘--no-gc-sections’ on the
        command line. Note that garbage collection for COFF and PE format targets is
        supported, but the implementation is currently considered to be experimental.--gc-sections’ decides which input sections are used by examining symbols
        and relocations. The section containing the entry symbol and all sections 
        containing symbols undefined on the command-line will be kept, as will sections
        containing symbols referenced by dynamic objects. Note that when building
        shared libraries, the linker must assume that any visible symbol is referenced.
        Once this initial set of sections has been determined, the linker recursively
        marks as used any section referenced by their relocations. See ‘--entry’,--undefined’, and ‘--gc-keep-exported’.
        
        This option can be set when doing a partial link (enabled with option ‘-r’). In
        this case the root of symbols kept must be explicitly specified either by one of
        the options ‘--entry’,--undefined’, or ‘--gc-keep-exported’ or by a ENTRY
        command in the linker script.
        
        As a GNU extension, ELF input sections marked with the SHF_GNU_RETAIN
        flag will not be garbage collected.
        
      • 翻译

        • 允许垃圾回收机制回收未使用的段(即用到的段才放入最终生成产物,没有用到的就删除掉.).对于不支持的平台,这个参数会被ld忽略.默认不开启,可以对一些.o文件开启,对一些关闭(ld --gc-sections a.o --no-gc-sections,就只有a.o才会进行选择性提取).垃圾收集机制对于COFF,PE格式的.o文件同样支持,但是目前是开发体验版,不建议用.
        • --gc-sections会检测哪些断和符号被用到了. 符号表相关的段保留;段中有通过指令指定的未定义的符号(ld -u name指定的符号,表示强制未定义);段中的符号被其他动态对象引用. 编译共享库,所有可见的符号都被当成是被引用了(可以使用可见性控制机制来编译,__attribute__的形式指定某几个,或者编译时 -fvisibility=xxx 设置整个). 第一阶段处理完保留的段,第二阶段就是递归的检测引用到的代码,即间接依赖的段.
        • 当只需要链接部分内容的时候可以使用这个选(-r选项是编译可重定位类型,即.o),那么保留的符号就可以使用--entry,--undefined,--gc-keep-exported,ENTRY来设定.
      • 总结

        • 保留用到的,并且保留依赖的.
        • 一般和-ffunction-sections,-fdata-sections使用.
  4. 编译案例

    • 普通

      • 代码

        void show(){
                     
                     }
        void cool(){
                     
                     }
        int main()
        {
                     
                     
           cool();
        }
        
        
      • 编译后

        • 编译:gcc -c test.c -ffunction-sections -fdata-sections
        • objdump -d test.o | less
        test.o:     file format elf64-x86-64
        
        
        Disassembly of section .text.show:
        
        0000000000000000 <show>:
          0:   55                      push   %rbp
          1:   48 89 e5                mov    %rsp,%rbp
          4:   5d                      pop    %rbp
          5:   c3                      retq
        
        Disassembly of section .text.cool:
        
        0000000000000000 <cool>:
          0:   55                      push   %rbp
          1:   48 89 e5                mov    %rsp,%rbp
          4:   5d                      pop    %rbp
          5:   c3                      retq
        
        Disassembly of section .text.main:
        
        0000000000000000 <main>:
          0:   55                      push   %rbp
          1:   48 89 e5                mov    %rsp,%rbp
          4:   b8 00 00 00 00          mov    $0x0,%eax
          9:   e8 00 00 00 00          callq  e <main+0xe>
          e:   5d                      pop    %rbp
          f:   c3                      retq
        
        
        • 生成:gcc test.o -Wl,-gc-sections -o test.out
        • 查看:objdump -d test.out
        test.out:     file format elf64-x86-64
        
        
        Disassembly of section .text:
        
        00000000004000b0 <cool>:
         4000b0:       55                      push   %rbp
         4000b1:       48 89 e5                mov    %rsp,%rbp
         4000b4:       5d                      pop    %rbp
         4000b5:       c3                      retq
        
        00000000004000b6 <main>:
         4000b6:       55                      push   %rbp
         4000b7:       48 89 e5                mov    %rsp,%rbp
         4000ba:       b8 00 00 00 00          mov    $0x0,%eax
         4000bf:       e8 ec ff ff ff          callq  4000b0 <cool>
         4000c4:       5d                      pop    %rbp
         4000c5:       c3                      retq
        
        
        • 测试实际指令
        gcc -c test.c -ffunction-sections -fdata-sections -fno-builtin
        ld test.o --gc-sections -static -o test.out -e main
        objdump -d test.out | less
        
    • 共享库

      • 代码

        • 同上
      • 编译后

        • gcc -fPIC -shared -c test.c -ffunction-sections -fdata-sections -fno-builtin
        • ld test.o -fPIC -shared --gc-sections -static -o test.so
        test.so:     file format elf64-x86-64
        
        
        Disassembly of section .plt:
        
        0000000000000280 <.plt>:
        280:   ff 35 82 0d 20 00       pushq  0x200d82(%rip)        # 201008 <_GLOBAL_OFFSET_TABLE_+0x8>
        286:   ff 25 84 0d 20 00       jmpq   *0x200d84(%rip)        # 201010 <_GLOBAL_OFFSET_TABLE_+0x10>
        28c:   0f 1f 40 00             nopl   0x0(%rax)
        
        0000000000000290 <cool@plt>:
        290:   ff 25 82 0d 20 00       jmpq   *0x200d82(%rip)        # 201018 <cool+0x200d72>
        296:   68 00 00 00 00          pushq  $0x0
        29b:   e9 e0 ff ff ff          jmpq   280 <.plt>
        
        Disassembly of section .text:
        
        00000000000002a0 <show>:
        2a0:   55                      push   %rbp
        2a1:   48 89 e5                mov    %rsp,%rbp
        2a4:   5d                      pop    %rbp
        2a5:   c3                      retq
        
        00000000000002a6 <cool>:
        2a6:   55                      push   %rbp
        2a7:   48 89 e5                mov    %rsp,%rbp
        2aa:   5d                      pop    %rbp
        2ab:   c3                      retq
        
        00000000000002ac <main>:
        2ac:   55                      push   %rbp
        2ad:   48 89 e5                mov    %rsp,%rbp
        2b0:   b8 00 00 00 00          mov    $0x0,%eax
        2b5:   e8 d6 ff ff ff          callq  290 <cool@plt>
        2ba:   5d                      pop    %rbp
        2bb:   c3                      retq
        
        • 这里没有设置可见性.默认都可见.
    • 不可见

      • 说明

        • 所有符号都不可见
      • 代码

        //#pragma GCC visibility push(hidden)
        void show(){
                     
                     }
        void cool(){
                     
                     }
        //#pragma GCC visibility pop
        int __attribute__((visibility("default"))) main()
        {
                     
                     
           //show();
           cool();
        }
        
        
      • 编译

        • gcc -fPIC -shared -c test.c -ffunction-sections -fdata-sections -fno-builtin -fvisibility=hidden
        • ld test.o -fPIC -shared --gc-sections -static -o test.so -u main
  5. 可见性控制

    • 编译控制

      • 编译选项-fvisibility=[default|internal|hidden|protected]
    • 代码宏定义

      • #pragma GCC visibility push(hidden) | #pragma GCC visibility pop
      • 以栈顶的属性为准.pop为弹栈.栈顶到pop都是以栈顶的为主.
    • 属性

      • __attribute__ ((visibility("default")))
      • ret_type attribute funname(__V_ARGS__)
    • 优先级

      • 属性>代码>编译控制

Guess you like

Origin blog.csdn.net/rubikchen/article/details/119223445