将非启动模块默认为自定义链接器脚本中的新部分

Defaulting non-startup modules to new section in custom linker script

我正在尝试创建一个自定义 linker 脚本,它采用除 main 和内部启动函数之外的所有函数,并将它们放入名为 .text_enc 的不同段中。

示例代码:

mod1.c:

#include <stdio.h>

void foo()
{
    printf("in foo\n");
}

main.c:

#include <stdio.h>

void foo();

int main()
{
    printf("in main\n");
    foo();
    return 0;
}

首先,我采用了默认的 linker 脚本,并在 .text 的部分之前为新部分添加了一个部分,明确指定了具有要放入的函数的模块新部分:

  .text_enc : {
    mod1.o(.text)
  }
  .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)
  }

我编译:

gcc -Wall -Wextra -c mystart.c
gcc -Wall -Wextra -c mod1.c
gcc -Wall -Wextra -o prog -T myenc1.ld mystart.o mod1.o

结果与预期的一样,foo 位于 .text_enc 部分:

[dbush@db-centos7 enc]$ objdump -t main.o | grep text
0000000000000000 l    d  .text  0000000000000000 .text
0000000000000000 g     F .text  000000000000000a foo
0000000000000010 g     F .text  0000000000000057 enc_main
[dbush@db-centos7 enc]$ objdump -t mod1.o | grep text
0000000000000000 l    d  .text  0000000000000000 .text
0000000000000000 g     F .text  0000000000000010 foo
[dbush@db-centos7 enc]$ objdump -t prog | grep text
0000000000400440 l    d  .text_enc  0000000000000000              .text_enc
0000000000400450 l    d  .text  0000000000000000              .text
0000000000400480 l     F .text  0000000000000000              deregister_tm_clones
00000000004004b0 l     F .text  0000000000000000              register_tm_clones
00000000004004f0 l     F .text  0000000000000000              __do_global_dtors_aux
0000000000400510 l     F .text  0000000000000000              frame_dummy
00000000004005d0 g     F .text  0000000000000002              __libc_csu_fini
0000000000400560 g     F .text  0000000000000065              __libc_csu_init
0000000000400440 g     F .text_enc  0000000000000010              foo
0000000000400450 g     F .text  0000000000000000              _start
000000000040053d g     F .text  000000000000001f              main

在这个简单的例子中,除了带 main 的源文件之外,只有一个其他源文件。一个“真实”的例子可能有几十个源文件,每个源文件都有几十个函数。我想避免在 linker 脚本中列出每个单独的源文件。我想改为告诉 linker 将包含 main 的文件和具有系统启动功能的文件的代码放在 .text 中,并将所有其他文件中的代码放在.text_enc。这样,如果添加或删除源文件,我就不必担心保持最新,并且我可以将此脚本用于多个项目。

使用 gcc 的 -v 选项,我看到它在 link 命令中包含 crt1.o、crti.o 和 crtbegin.o(CentOS 7.2 , gcc 4.8.5),其中包含对象转储中列出的与启动相关的函数。所以我尝试修改 linker 脚本如下:

  .text : {    
    mystart.o(.text)
    crt1.o(.text)
    crti.o(.text)
    crtbegin.o(.text)
  }
  .text_enc           :
  {
    *(.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)
  }

但是 linking 失败了:

gcc -v -Wall -Wextra -o prog -T myenc2.ld mystart.o mod1.o
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/lto-wrapper
Target: x86_64-redhat-linux
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-linker-hash-style=gnu --enable-languages=c,c++,objc,obj-c++,java,fortran,ada,go,lto --enable-plugin --enable-initfini-array --disable-libgcj --with-isl=/builddir/build/BUILD/gcc-4.8.5-20150702/obj-x86_64-redhat-linux/isl-install --with-cloog=/builddir/build/BUILD/gcc-4.8.5-20150702/obj-x86_64-redhat-linux/cloog-install --enable-gnu-indirect-function --with-tune=generic --with-arch_32=x86-64 --build=x86_64-redhat-linux
Thread model: posix
gcc version 4.8.5 20150623 (Red Hat 4.8.5-39) (GCC) 
COMPILER_PATH=/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/:/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/:/usr/libexec/gcc/x86_64-redhat-linux/:/usr/lib/gcc/x86_64-redhat-linux/4.8.5/:/usr/lib/gcc/x86_64-redhat-linux/
LIBRARY_PATH=/usr/lib/gcc/x86_64-redhat-linux/4.8.5/:/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/:/lib/../lib64/:/usr/lib/../lib64/:/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-v' '-Wall' '-Wextra' '-o' 'prog' '-T' 'myenc2.ld' '-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 prog /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/../../.. mystart.o mod1.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 -T myenc2.ld
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crt1.o: In function `_start':
(.text+0x0): multiple definition of `_start'
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crt1.o:(.text+0x0): first defined here
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crt1.o:(.rodata.cst4+0x0): multiple definition of `_IO_stdin_used'
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crt1.o:(.rodata.cst4+0x0): first defined here
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crt1.o: In function `data_start':
(.data+0x0): multiple definition of `__data_start'
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crt1.o:(.data+0x0): first defined here
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crti.o: In function `_init':
(.init+0x0): multiple definition of `_init'
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crti.o:(.init+0x0): first defined here
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crti.o: In function `_fini':
(.fini+0x0): multiple definition of `_fini'
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crti.o:(.fini+0x0): first defined here
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtbegin.o:(.rodata+0x0): multiple definition of `__dso_handle'
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtbegin.o:(.rodata+0x0): first defined here
collect2: error: ld returned 1 exit status
make: *** [prog] Error 1

这似乎导致启动文件被拉入 linker 两次。所以我尝试将 -nostartfiles 传递给 gcc,但是缺少依赖项:

/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crt1.o: In function `_start':
(.text+0x12): undefined reference to `__libc_csu_fini'
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crt1.o: In function `_start':
(.text+0x19): undefined reference to `__libc_csu_init'
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtbegin.o: In function `deregister_tm_clones':
crtstuff.c:(.text+0x1): undefined reference to `__TMC_END__'
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtbegin.o: In function `register_tm_clones':
crtstuff.c:(.text+0x31): undefined reference to `__TMC_END__'
/usr/bin/ld: prog: hidden symbol `__TMC_END__' isn't defined
/usr/bin/ld: final link failed: Bad value
collect2: error: ld returned 1 exit status
make: *** [prog] Error 1

设置 linker 脚本以使所有应用程序 .o 文件将它们的代码放入 .text_enc 而不必显式调用它们中的每一个(除了一个 main)?

我设法弄明白了。我没有调用 .text 部分中的自动链接文件,导致它们被链接两次,而是在 .text_enc 部分中使用 EXCLUDE_FILE

  .text_enc : {
    *(EXCLUDE_FILE (*/crt1.o */crti.o */crtbegin.o */crtend.o */libc_nonshared.a mystart.o) .text)
  }
  .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)
  }

所以我可以保留原来的 .text 部分不变,只是在上面添加了 .text_enc 以及要排除的文件。这提供了与我最初尝试相同的结果,我最初尝试明确列出要包含在 .text_enc 中的文件,并且无需采取任何措施来防止双重链接。