gcc 使用的实际默认链接描述文件和设置

Posted

技术标签:

【中文标题】gcc 使用的实际默认链接描述文件和设置【英文标题】:Actual default linker script and settings gcc uses 【发布时间】:2015-04-25 15:27:56 【问题描述】:

在哪里可以找到 gcc 使用的实际链接器脚本和设置?


我尝试过的事情:

为了具体起见,让我们考虑一个小程序: 空.c

int main(void)
   
    return 0;

静态构建,看看结果:

$ gcc -static -o empty empty.c
$ readelf -W -l empty

Elf file type is EXEC (Executable file)
Entry point 0x400f4e
There are 6 program headers, starting at offset 64

Program Headers:
  Type           Offset   VirtAddr           PhysAddr           FileSiz  MemSiz   Flg Align
  LOAD           0x000000 0x0000000000400000 0x0000000000400000 0x0bf581 0x0bf581 R E 0x200000
  LOAD           0x0bfeb0 0x00000000006bfeb0 0x00000000006bfeb0 0x001d80 0x0042d8 RW  0x200000
  NOTE           0x000190 0x0000000000400190 0x0000000000400190 0x000044 0x000044 R   0x4
  TLS            0x0bfeb0 0x00000000006bfeb0 0x00000000006bfeb0 0x000020 0x000058 R   0x10
  GNU_STACK      0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW  0x10
  GNU_RELRO      0x0bfeb0 0x00000000006bfeb0 0x00000000006bfeb0 0x000150 0x000150 R   0x1

 Section to Segment mapping:
  Segment Sections...
   00     .note.ABI-tag .note.gnu.build-id .rela.plt .init .plt .text __libc_freeres_fn __libc_thread_freeres_fn .fini .rodata __libc_subfreeres __libc_atexit __libc_thread_subfreeres .eh_frame .gcc_except_table 
   01     .tdata .init_array .fini_array .jcr .data.rel.ro .got .got.plt .data .bss __libc_freeres_ptrs 
   02     .note.ABI-tag .note.gnu.build-id 
   03     .tdata .tbss 
   04     
   05     .tdata .init_array .fini_array .jcr .data.rel.ro .got 

注意各个部分,分组为段,并放入具有各种权限的内存区域。

现在让我们尝试尽可能多地了解它是如何进行这种链接的。

$ gcc -static -o empty empty.c -Wl,--verbose
GNU ld (GNU Binutils for Ubuntu) 2.24
  Supported emulations:
   elf_x86_64
   elf32_x86_64
   elf_i386
   i386linux
   elf_l1om
   elf_k1om
   i386pep
   i386pe
using internal linker script:
==================================================
/* Script for -z combreloc: combine and sort reloc sections */
OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64",
          "elf64-x86-64")
OUTPUT_ARCH(i386:x86-64)
ENTRY(_start)
SEARCH_DIR("/usr/x86_64-linux-gnu/lib64"); SEARCH_DIR("=/usr/local/lib/x86_64-linux-gnu"); SEARCH_DIR("=/usr/local/lib64"); SEARCH_DIR("=/lib/x86_64-linux-gnu"); SEARCH_DIR("=/lib64"); SEARCH_DIR("=/usr/lib/x86_64-linux-gnu"); SEARCH_DIR("=/usr/lib64"); SEARCH_DIR("=/usr/local/lib"); SEARCH_DIR("=/lib"); SEARCH_DIR("=/usr/lib");
SECTIONS

  /* Read-only sections, merged into text segment: */
  PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x400000)); . = SEGMENT_START("text-segment", 0x400000) + SIZEOF_HEADERS;
  .interp         :  *(.interp) 
  .note.gnu.build-id :  *(.note.gnu.build-id) 
  .hash           :  *(.hash) 
  .gnu.hash       :  *(.gnu.hash) 
  .dynsym         :  *(.dynsym) 
  .dynstr         :  *(.dynstr) 
  .gnu.version    :  *(.gnu.version) 
  .gnu.version_d  :  *(.gnu.version_d) 
  .gnu.version_r  :  *(.gnu.version_r) 
  .rela.dyn       :
    
      *(.rela.init)
      *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*)
      *(.rela.fini)
      *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*)
      *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*)
      *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*)
      *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*)
      *(.rela.ctors)
      *(.rela.dtors)
      *(.rela.got)
      *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*)
      *(.rela.ldata .rela.ldata.* .rela.gnu.linkonce.l.*)
      *(.rela.lbss .rela.lbss.* .rela.gnu.linkonce.lb.*)
      *(.rela.lrodata .rela.lrodata.* .rela.gnu.linkonce.lr.*)
      *(.rela.ifunc)
    
  .rela.plt       :
    
      *(.rela.plt)
      PROVIDE_HIDDEN (__rela_iplt_start = .);
      *(.rela.iplt)
      PROVIDE_HIDDEN (__rela_iplt_end = .);
    
  .init           :
  
    KEEP (*(SORT_NONE(.init)))
  
  .plt            :  *(.plt) *(.iplt) 
  .text           :
  
    *(.text.unlikely .text.*_unlikely .text.unlikely.*)
    *(.text.exit .text.exit.*)
    *(.text.startup .text.startup.*)
    *(.text.hot .text.hot.*)
    *(.text .stub .text.* .gnu.linkonce.t.*)
    /* .gnu.warning sections are handled specially by elf32.em.  */
    *(.gnu.warning)
  
  .fini           :
  
    KEEP (*(SORT_NONE(.fini)))
  
  PROVIDE (__etext = .);
  PROVIDE (_etext = .);
  PROVIDE (etext = .);
  .rodata         :  *(.rodata .rodata.* .gnu.linkonce.r.*) 
  .rodata1        :  *(.rodata1) 
  .eh_frame_hdr :  *(.eh_frame_hdr) 
  .eh_frame       : ONLY_IF_RO  KEEP (*(.eh_frame)) 
  .gcc_except_table   : ONLY_IF_RO  *(.gcc_except_table
  .gcc_except_table.*) 
  /* These sections are generated by the Sun/Oracle C++ compiler.  */
  .exception_ranges   : ONLY_IF_RO  *(.exception_ranges
  .exception_ranges*) 
  /* Adjust the address for the data segment.  We want to adjust up to
     the same address within the page on the next page up.  */
  . = ALIGN (CONSTANT (MAXPAGESIZE)) - ((CONSTANT (MAXPAGESIZE) - .) & (CONSTANT (MAXPAGESIZE) - 1)); . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE));
  /* Exception handling  */
  .eh_frame       : ONLY_IF_RW  KEEP (*(.eh_frame)) 
  .gcc_except_table   : ONLY_IF_RW  *(.gcc_except_table .gcc_except_table.*) 
  .exception_ranges   : ONLY_IF_RW  *(.exception_ranges .exception_ranges*) 
  /* Thread Local Storage sections  */
  .tdata      :  *(.tdata .tdata.* .gnu.linkonce.td.*) 
  .tbss       :  *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) 
  .preinit_array     :
  
    PROVIDE_HIDDEN (__preinit_array_start = .);
    KEEP (*(.preinit_array))
    PROVIDE_HIDDEN (__preinit_array_end = .);
  
  .init_array     :
  
    PROVIDE_HIDDEN (__init_array_start = .);
    KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
    KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors))
    PROVIDE_HIDDEN (__init_array_end = .);
  
  .fini_array     :
  
    PROVIDE_HIDDEN (__fini_array_start = .);
    KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))
    KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors))
    PROVIDE_HIDDEN (__fini_array_end = .);
  
  .ctors          :
  
    /* gcc uses crtbegin.o to find the start of
       the constructors, so we make sure it is
       first.  Because this is a wildcard, it
       doesn't matter if the user does not
       actually link against crtbegin.o; the
       linker won't look for a file to match a
       wildcard.  The wildcard also means that it
       doesn't matter which directory crtbegin.o
       is in.  */
    KEEP (*crtbegin.o(.ctors))
    KEEP (*crtbegin?.o(.ctors))
    /* We don't want to include the .ctor section from
       the crtend.o file until after the sorted ctors.
       The .ctor section from the crtend file contains the
       end of ctors marker and it must be last */
    KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
    KEEP (*(SORT(.ctors.*)))
    KEEP (*(.ctors))
  
  .dtors          :
  
    KEEP (*crtbegin.o(.dtors))
    KEEP (*crtbegin?.o(.dtors))
    KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
    KEEP (*(SORT(.dtors.*)))
    KEEP (*(.dtors))
  
  .jcr            :  KEEP (*(.jcr)) 
  .data.rel.ro :  *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) 
  .dynamic        :  *(.dynamic) 
  .got            :  *(.got) *(.igot) 
  . = DATA_SEGMENT_RELRO_END (SIZEOF (.got.plt) >= 24 ? 24 : 0, .);
  .got.plt        :  *(.got.plt)  *(.igot.plt) 
  .data           :
  
    *(.data .data.* .gnu.linkonce.d.*)
    SORT(CONSTRUCTORS)
  
  .data1          :  *(.data1) 
  _edata = .; PROVIDE (edata = .);
  . = .;
  __bss_start = .;
  .bss            :
  
   *(.dynbss)
   *(.bss .bss.* .gnu.linkonce.b.*)
   *(COMMON)
   /* Align here to ensure that the .bss section occupies space up to
      _end.  Align after .bss to ensure correct alignment even if the
      .bss section disappears because there are no input sections.
      FIXME: Why do we need it? When there is no .bss section, we don't
      pad the .data section.  */
   . = ALIGN(. != 0 ? 64 / 8 : 1);
  
  .lbss   :
  
    *(.dynlbss)
    *(.lbss .lbss.* .gnu.linkonce.lb.*)
    *(LARGE_COMMON)
  
  . = ALIGN(64 / 8);
  . = SEGMENT_START("ldata-segment", .);
  .lrodata   ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) :
  
    *(.lrodata .lrodata.* .gnu.linkonce.lr.*)
  
  .ldata   ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) :
  
    *(.ldata .ldata.* .gnu.linkonce.l.*)
    . = ALIGN(. != 0 ? 64 / 8 : 1);
  
  . = ALIGN(64 / 8);
  _end = .; PROVIDE (end = .);
  . = DATA_SEGMENT_END (.);
  /* Stabs debugging sections.  */
  .stab          0 :  *(.stab) 
  .stabstr       0 :  *(.stabstr) 
  .stab.excl     0 :  *(.stab.excl) 
  .stab.exclstr  0 :  *(.stab.exclstr) 
  .stab.index    0 :  *(.stab.index) 
  .stab.indexstr 0 :  *(.stab.indexstr) 
  .comment       0 :  *(.comment) 
  /* DWARF debug sections.
     Symbols in the DWARF debugging sections are relative to the beginning
     of the section so we begin them at 0.  */
  /* DWARF 1 */
  .debug          0 :  *(.debug) 
  .line           0 :  *(.line) 
  /* GNU DWARF 1 extensions */
  .debug_srcinfo  0 :  *(.debug_srcinfo) 
  .debug_sfnames  0 :  *(.debug_sfnames) 
  /* DWARF 1.1 and DWARF 2 */
  .debug_aranges  0 :  *(.debug_aranges) 
  .debug_pubnames 0 :  *(.debug_pubnames) 
  /* DWARF 2 */
  .debug_info     0 :  *(.debug_info .gnu.linkonce.wi.*) 
  .debug_abbrev   0 :  *(.debug_abbrev) 
  .debug_line     0 :  *(.debug_line .debug_line.* .debug_line_end ) 
  .debug_frame    0 :  *(.debug_frame) 
  .debug_str      0 :  *(.debug_str) 
  .debug_loc      0 :  *(.debug_loc) 
  .debug_macinfo  0 :  *(.debug_macinfo) 
  /* SGI/MIPS DWARF 2 extensions */
  .debug_weaknames 0 :  *(.debug_weaknames) 
  .debug_funcnames 0 :  *(.debug_funcnames) 
  .debug_typenames 0 :  *(.debug_typenames) 
  .debug_varnames  0 :  *(.debug_varnames) 
  /* DWARF 3 */
  .debug_pubtypes 0 :  *(.debug_pubtypes) 
  .debug_ranges   0 :  *(.debug_ranges) 
  /* DWARF Extension.  */
  .debug_macro    0 :  *(.debug_macro) 
  .gnu.attributes 0 :  KEEP (*(.gnu.attributes)) 
  /DISCARD/ :  *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) 



==================================================
... <snip searching and linking actual object files>

但脚本虽然很长,但缺少前面列出的大部分导入信息。

它如何知道将哪些部分聚集到不同的负载段中? 没有 PHDRS 命令,虽然使用 SEGMENT_START 表明该系统在其他地方定义了一些标准段,但没有这些段与关联的段一起列出。

此外,它如何知道在哪里加载这些段,或者这些内存区域有什么权限? 没有 MEMORY 命令。同样,如果该系统在其他地方定义了一些标准内存区域,则 none 部分会列出要使用的内存区域。

当我之前看到微控制器的默认链接器脚本时,它们非常详细。但是,此输出表明某处还有更多脚本和设置。

这些其他链接描述文件定义和设置存储在哪里?

【问题讨论】:

1) 对于您提到的每个行为,很可能有一个默认值可以解释它。在一个问题上隔离一个你不理解的单一行为会更好。 2)“它如何知道将哪些部分聚集到不同的负载段中?”大多数分段映射都是显式编写的。哪一个你不明白? 【参考方案1】:

好吧,我知道这是一个老问题,但我也发现没有关于链接过程中使用的选项的准确信息令人沮丧。这个答案展示了我寻找它们的过程。

首先,我查看了官方文档 https://gcc.gnu.org/onlinedocs/ - 我搜索了 GCC 手册GCC 内部手册。我发现的唯一有意义的信息是 gcc 使用名为 collect2 的内部工具来调用链接器。根据https://gcc.gnu.org/onlinedocs/gccint/Collect2.html“程序collect2的工作原理是将程序链接一次并在链接器输出文件中查看具有特定名称的符号,表明它们是构造函数”。所以它被用来使链接成为可能。

接下来我尝试的是通过源代码。你可以在这里浏览代码https://code.woboq.org/gcc/gcc/collect2.c.html。问题是它并没有真正的帮助。但我注意到 collect2 fork_execute 函数调用 ld。您可以深入了解fork_execute 以发现它会分叉(在分叉的程序中执行一个新程序)并等待它完成。因为 fork 和 exec 都是系统调用(简而言之,系统调用是应用程序用来与系统通信的方式/功能)。我决定试一试。

所以我制作了一个不需要任何编译的简单程序(它已经编译为目标文件 - 所以gcc 要做的一切就是链接)。

[Alex@Normandy tmp]$ gcc hello.c.s  -o hello_gcc
[Alex@Normandy tmp]$ ./hello_gcc 
Hello, World!

然后我使用带有以下选项的 strace:

-o forked.log 将输出保存到 forked.log -s 1024 小于 1024 个字符的变量不会被截断(默认 32 个不够) -f - 在分叉的进程上启用 strace -e trace=/exec - 过滤系统调用,只显示以exec 开头的调用

最后的输出如下。

[Alex@Normandy tmp]$ strace -o forked.log -s 1024 -f -e trace=/exec gcc hello.c.s  -o hello_gcc
[Alex@Normandy tmp]$ grep 'ld' forked.log 
2153  execve("/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/collect2", ["/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/collect2", "--build-id", "--no-add-needed", "--eh-frame-hdr", "--hash-style=gnu", "-m", "elf_x86_64", "-dynamic-linker", "/lib64/ld-linux-x86-64.so.2", "-o", "hello_gcc", "/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crt1.o", "/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crti.o", "/usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtbegin.o", "-L/usr/lib/gcc/x86_64-redhat-linux/4.8.5", "-L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64", "-L/lib/../lib64", "-L/usr/lib/../lib64", "-L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../..", "/tmp/ccyl36jf.o", "-lgcc", "--as-needed", "-lgcc_s", "--no-as-needed", "-lc", "-lgcc", "--as-needed", "-lgcc_s", "--no-as-needed", "/usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtend.o", "/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crtn.o"], 0x17b9da0 /* 61 vars */) = 0
2154  execve("/usr/bin/ld", ["/usr/bin/ld", "--build-id", "--no-add-needed", "--eh-frame-hdr", "--hash-style=gnu", "-m", "elf_x86_64", "-dynamic-linker", "/lib64/ld-linux-x86-64.so.2", "-o", "hello_gcc", "/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crt1.o", "/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crti.o", "/usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtbegin.o", "-L/usr/lib/gcc/x86_64-redhat-linux/4.8.5", "-L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64", "-L/lib/../lib64", "-L/usr/lib/../lib64", "-L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../..", "/tmp/ccyl36jf.o", "-lgcc", "--as-needed", "-lgcc_s", "--no-as-needed", "-lc", "-lgcc", "--as-needed", "-lgcc_s", "--no-as-needed", "/usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtend.o", "/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crtn.o"], 0x7fff14226a98 /* 61 vars */) = 0

所以使用的 ld 命令是

/usr/bin/ld --build-id --no-add-needed --eh-frame-hdr --hash-style=gnu -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o hello_gcc /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crt1.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crti.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtbegin.o -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5 -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../.. /tmp/ccyl36jf.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtend.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crtn.o

那么他妈的是什么?好吧,所有选项都可以在手册中找到。这是分解后的输出。

/usr/bin/ld - 链接器程序 --build-id - 将 build-id 添加到二进制文件。在我的系统中默认是 sha1。 --no-add-needed - 它是 --no-copy-dt-needed-entries 的过时名称 - 它与 ELF 中的 DT_NEEDED 标签连接,如果我理解正确,则意味着不会从输入库中复制 DT_NEEDED 标签。。李> --eh-frame-hdr - “请求创建“.eh_frame_hdr”段和 ELF “PT_GNU_EH_FRAME”段标头。”不管那是什么意思。 --hash-style=gnu - “设置链接器哈希表的类型。”默认为 sysv,但有更新的格式 gnu。二进制也可以有两种格式的哈希表。 -m elf_x86_64 - 链接器模拟(为 x86_64 制作 elf 类型二进制文件) -dynamic-linker /lib64/ld-linux-x86-64.so.2 - 设置预期动态链接器的名称 -o hello_gcc - 设置输出二进制 /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crt1.o - 在实际程序的主之前运行的代码 /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crti.o- 实际程序主之前运行的代码 /usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtbegin.o - 在实际程序的主之前运行的代码 -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5 - 额外的库搜索路径 -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64 - 额外的库搜索路径 -L/lib/../lib64 - 额外的库搜索路径 -L/usr/lib/../lib64 - 额外的库搜索路径 -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../.. - 额外的库搜索路径 /tmp/ccyl36jf.o - 这是带有主要功能的实际程序(二进制对象) -lgcc - -l 选项 - “将 namespec 指定的存档或目标文件添加到要链接的文件列表中。”在这种情况下,它是 gcc。 --as-needed - 启用“按需”模式,检查是否需要在特定点跟随库(命名空间?) -lgcc_s - 添加 gcc_s 请注意,仅当此时确实需要它时。 --no-as-needed - 禁用“按需”模式,检查是否需要在特定点跟随库(命名空间?) -lc- 标准 C 命名空间/库 -lgcc - 这个库应该已经设置好了。此选项的此用法与之前的用法之间可能存在差异。 --as-needed - 设置“按需模式。此选项的使用与之前的使用可能存在差异。 -lgcc_s - 已经描述过了。此选项的此用法与之前的用法之间可能存在差异。 --no-as-needed -- 禁用“按需模式”。 /usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtend.o - 程序完成时运行的附加代码 /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crtn.o - 程序完成时运行的附加代码。

更多关于:crt1.ocrti.ocrtbegin.ocrtend.ocrtn.o - 它们是启动、初始化、构造函数、析构函数和终结文件(根据由 Karim Yaghmour 构建的嵌入式 Linux 系统)。

可能更简单的方法

在编写此答案期间,我还“发现”您可以使用-v 选项调用gcc,它将返回COLLECT_GCC_OPTIONS,这与调用的ld 相同

COLLECT_GCC_OPTIONS='-v' '-o' 'hello_gcc' '-mtune=generic' '-march=x86-64'
 /usr/libexec/gcc/x86_64-redhat-linux/4.8.5/collect2 --build-id --no-add-needed --eh-frame-hdr --hash-style=gnu -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o hello_gcc /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crt1.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crti.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtbegin.o -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5 -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../.. hello_gcc.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtend.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crtn.o

不过,如果您想 100% 确定 ld 是如何被调用的,那么 strace 是您最好的选择。

最后,请注意,我使用 Enterprise Linux v7 和 v8 系统来检查我是否正确。它们都使用 x86_64 架构,在不同的架构上结果可能会有所不同。

【讨论】:

以上是关于gcc 使用的实际默认链接描述文件和设置的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Mac OSX 下使用 gcc 设置可执行文件的运行时路径(-rpath)?

gcc 编译控制选项

如何指定GCC的默认头文件路径

gcc/g++链接时.o文件及库的顺序问题

链接库

使用 GCC 在可执行文件中嵌入资源