调试符号如何影响GCC编译的Linux可执行文件的性能?

How do debug symbols affect performance of a Linux executable compiled by GCC?

所有其他因素都相等(例如,优化级别),ELF或SO中的调试符号将如何影响:

  • 加载时间。
  • 运行时内存占用量。
  • 运行时性能?
  • 怎样减轻负面影响呢?

    编辑
    我已经看到了这个问题,但是我发现讨论没有帮助,因为代码优化因素使那里的问题感到困惑。 为什么在为性能分析(-pg)进行编译时,我的代码在多线程下的运行速度比单线程慢?


    调试符号位于与代码/数据部分完全不同的部分。您可以使用objdump进行检查:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    $ objdump -h a.out

    a.out:     file format elf64-x86-64

    Sections:
    Idx Name          Size      VMA               LMA               File off  Algn
      0 .interp       0000001c  0000000000400200  0000000000400200  00000200  2**0
                      CONTENTS, ALLOC, LOAD, READONLY, DATA
      1 .note.ABI-tag 00000020  000000000040021c  000000000040021c  0000021c  2**2
                      CONTENTS, ALLOC, LOAD, READONLY, DATA
      2 .note.gnu.build-id 00000024  000000000040023c  000000000040023c  0000023c  2**2
                      CONTENTS, ALLOC, LOAD, READONLY, DATA
      3 .hash         00000018  0000000000400260  0000000000400260  00000260  2**3
                      CONTENTS, ALLOC, LOAD, READONLY, DATA
      4 .gnu.hash     0000001c  0000000000400278  0000000000400278  00000278  2**3
                      CONTENTS, ALLOC, LOAD, READONLY, DATA
      5 .dynsym       00000048  0000000000400298  0000000000400298  00000298  2**3
                      CONTENTS, ALLOC, LOAD, READONLY, DATA
      6 .dynstr       00000038  00000000004002e0  00000000004002e0  000002e0  2**0
                      CONTENTS, ALLOC, LOAD, READONLY, DATA
      7 .gnu.version  00000006  0000000000400318  0000000000400318  00000318  2**1
                      CONTENTS, ALLOC, LOAD, READONLY, DATA
      8 .gnu.version_r 00000020  0000000000400320  0000000000400320  00000320  2**3
                      CONTENTS, ALLOC, LOAD, READONLY, DATA
      9 .rela.dyn     00000018  0000000000400340  0000000000400340  00000340  2**3
                      CONTENTS, ALLOC, LOAD, READONLY, DATA
     10 .rela.plt     00000018  0000000000400358  0000000000400358  00000358  2**3
                      CONTENTS, ALLOC, LOAD, READONLY, DATA
     11 .init         00000018  0000000000400370  0000000000400370  00000370  2**2
                      CONTENTS, ALLOC, LOAD, READONLY, CODE
     12 .plt          00000020  0000000000400388  0000000000400388  00000388  2**2
                      CONTENTS, ALLOC, LOAD, READONLY, CODE
     13 .text         000001c8  00000000004003b0  00000000004003b0  000003b0  2**4
                      CONTENTS, ALLOC, LOAD, READONLY, CODE
     14 .fini         0000000e  0000000000400578  0000000000400578  00000578  2**2
                      CONTENTS, ALLOC, LOAD, READONLY, CODE
     15 .rodata       00000004  0000000000400588  0000000000400588  00000588  2**2
                      CONTENTS, ALLOC, LOAD, READONLY, DATA
     16 .eh_frame_hdr 00000024  000000000040058c  000000000040058c  0000058c  2**2
                      CONTENTS, ALLOC, LOAD, READONLY, DATA
     17 .eh_frame     0000007c  00000000004005b0  00000000004005b0  000005b0  2**3
                      CONTENTS, ALLOC, LOAD, READONLY, DATA
     18 .ctors        00000010  0000000000600630  0000000000600630  00000630  2**3
                      CONTENTS, ALLOC, LOAD, DATA
     19 .dtors        00000010  0000000000600640  0000000000600640  00000640  2**3
                      CONTENTS, ALLOC, LOAD, DATA
     20 .jcr          00000008  0000000000600650  0000000000600650  00000650  2**3
                      CONTENTS, ALLOC, LOAD, DATA
     21 .dynamic      000001a0  0000000000600658  0000000000600658  00000658  2**3
                      CONTENTS, ALLOC, LOAD, DATA
     22 .got          00000008  00000000006007f8  00000000006007f8  000007f8  2**3
                      CONTENTS, ALLOC, LOAD, DATA
     23 .got.plt      00000020  0000000000600800  0000000000600800  00000800  2**3
                      CONTENTS, ALLOC, LOAD, DATA
     24 .data         00000010  0000000000600820  0000000000600820  00000820  2**3
                      CONTENTS, ALLOC, LOAD, DATA
     25 .bss          00000010  0000000000600830  0000000000600830  00000830  2**3
                      ALLOC
     26 .comment      00000039  0000000000000000  0000000000000000  00000830  2**0
                      CONTENTS, READONLY
     27 .debug_aranges 00000030  0000000000000000  0000000000000000  00000869  2**0
                      CONTENTS, READONLY, DEBUGGING
     28 .debug_pubnames 0000001b  0000000000000000  0000000000000000  00000899  2**0
                      CONTENTS, READONLY, DEBUGGING
     29 .debug_info   00000055  0000000000000000  0000000000000000  000008b4  2**0
                      CONTENTS, READONLY, DEBUGGING
     30 .debug_abbrev 00000034  0000000000000000  0000000000000000  00000909  2**0
                      CONTENTS, READONLY, DEBUGGING
     31 .debug_line   0000003b  0000000000000000  0000000000000000  0000093d  2**0
                      CONTENTS, READONLY, DEBUGGING
     32 .debug_str    00000026  0000000000000000  0000000000000000  00000978  2**0
                      CONTENTS, READONLY, DEBUGGING
     33 .debug_loc    0000004c  0000000000000000  0000000000000000  0000099e  2**0
                      CONTENTS, READONLY, DEBUGGING

    您可以看到其他部分(27至33)。这些部分不会在运行时加载,因此不会有任何性能损失。使用gdb,您还可以在运行时检查它们

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    $ gdb ./a.out
    (gdb) break main
    (gdb) run
    (gdb) info files
    // blah blah ....
    Local exec file:
            `/home/kghost/a.out', file type elf64-x86-64.
            Entry point: 0x4003b0
            0x0000000000400200 - 0x000000000040021c is .interp
            0x000000000040021c - 0x000000000040023c is .note.ABI-tag
            0x000000000040023c - 0x0000000000400260 is .note.gnu.build-id
            0x0000000000400260 - 0x0000000000400278 is .hash
            0x0000000000400278 - 0x0000000000400294 is .gnu.hash
            0x0000000000400298 - 0x00000000004002e0 is .dynsym
            0x00000000004002e0 - 0x0000000000400318 is .dynstr
            0x0000000000400318 - 0x000000000040031e is .gnu.version
            0x0000000000400320 - 0x0000000000400340 is .gnu.version_r
            0x0000000000400340 - 0x0000000000400358 is .rela.dyn
            0x0000000000400358 - 0x0000000000400370 is .rela.plt
            0x0000000000400370 - 0x0000000000400388 is .init
            0x0000000000400388 - 0x00000000004003a8 is .plt
            0x00000000004003b0 - 0x0000000000400578 is .text
            0x0000000000400578 - 0x0000000000400586 is .fini
            0x0000000000400588 - 0x000000000040058c is .rodata
            0x000000000040058c - 0x00000000004005b0 is .eh_frame_hdr
            0x00000000004005b0 - 0x000000000040062c is .eh_frame
            0x0000000000600630 - 0x0000000000600640 is .ctors
            0x0000000000600640 - 0x0000000000600650 is .dtors
            0x0000000000600650 - 0x0000000000600658 is .jcr
            0x0000000000600658 - 0x00000000006007f8 is .dynamic
            0x00000000006007f8 - 0x0000000000600800 is .got
            0x0000000000600800 - 0x0000000000600820 is .got.plt
            0x0000000000600820 - 0x0000000000600830 is .data
            0x0000000000600830 - 0x0000000000600840 is .bss
    // blah blah ....

    因此,唯一的惩罚是您需要额外的磁盘空间来存储此信息。您还可以使用strip删除调试信息:

    1
    $ strip a.out

    使用objdump再次检查它,您将看到区别。

    编辑:

    取而代之的是查看加载器,实际上是加载器根据Program Header加载elf文件,而objdump -p可以看到。 (以下示例使用其他的elf二进制文件)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    $ objdump -p /bin/cat

    /bin/cat:     file format elf64-x86-64

    Program Header:
        PHDR off    0x0000000000000040 vaddr 0x0000000000000040 paddr 0x0000000000000040 align 2**3
             filesz 0x00000000000001f8 memsz 0x00000000000001f8 flags r-x
      INTERP off    0x0000000000000238 vaddr 0x0000000000000238 paddr 0x0000000000000238 align 2**0
             filesz 0x000000000000001c memsz 0x000000000000001c flags r--
        LOAD off    0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**21
             filesz 0x00000000000078bc memsz 0x00000000000078bc flags r-x
        LOAD off    0x0000000000007c28 vaddr 0x0000000000207c28 paddr 0x0000000000207c28 align 2**21
             filesz 0x0000000000000678 memsz 0x0000000000000818 flags rw-
     DYNAMIC off    0x0000000000007dd8 vaddr 0x0000000000207dd8 paddr 0x0000000000207dd8 align 2**3
             filesz 0x00000000000001e0 memsz 0x00000000000001e0 flags rw-
        NOTE off    0x0000000000000254 vaddr 0x0000000000000254 paddr 0x0000000000000254 align 2**2
             filesz 0x0000000000000044 memsz 0x0000000000000044 flags r--
    EH_FRAME off    0x0000000000006980 vaddr 0x0000000000006980 paddr 0x0000000000006980 align 2**2
             filesz 0x0000000000000274 memsz 0x0000000000000274 flags r--
       STACK off    0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**4
             filesz 0x0000000000000000 memsz 0x0000000000000000 flags rw-
       RELRO off    0x0000000000007c28 vaddr 0x0000000000207c28 paddr 0x0000000000207c28 align 2**0
             filesz 0x00000000000003d8 memsz 0x00000000000003d8 flags r--

    程序头告诉哪个段将加载哪些rwx标志,具有相同标志的多个节将合并到一个段中。

    顺便说一句:

    加载程序在加载elf文件时并不关心各节,但在需要时它将查找几个与符号相关的节来解析符号。


    您可能想看看为什么在为剖析(-pg)进行编译时,为什么我的代码在多线程下运行比单线程慢?快速了解调试符号如何影响优化。

    要回答您的3个问题:

  • 当存在调试符号时,加载时间将增加,而在不存在时,加载时间将增加
  • 磁盘占用空间会更大
  • 如果您使用零优化进行编译,那么您将一无所获。如果设置优化,则由于调试符号,优化后的代码将不太优化。