For glibc with: From ac187dc4abde9ca6504c646106e2a7f7b2806262 Mon Sep 17 00:00:00 2001 From: H.J. Lu <hjl.tools@gmail.com> Date: Thu, 9 Jun 2016 04:43:16 -0700 Subject: [PATCH] Always indirect branch to __libc_start_main via GOT Since __libc_start_main in libc.so is called very early, lazy binding isn't relevant. Always call __libc_start_main with indirect branch via GOT to avoid extra branch to PLT slot. In case of static executable, ld in binutils 2.26 or above can convert indirect branch into direct branch: 0000000000400a80 <_start>: 400a80: 31 ed xor %ebp,%ebp 400a82: 49 89 d1 mov %rdx,%r9 400a85: 5e pop %rsi 400a86: 48 89 e2 mov %rsp,%rdx 400a89: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp 400a8d: 50 push %rax 400a8e: 54 push %rsp 400a8f: 49 c7 c0 20 1b 40 00 mov $0x401b20,%r8 400a96: 48 c7 c1 90 1a 40 00 mov $0x401a90,%rcx 400a9d: 48 c7 c7 c0 03 40 00 mov $0x4003c0,%rdi 400aa4: 67 e8 96 09 00 00 addr32 callq 401440 <__libc_start_main> 400aaa: f4 hlt * sysdeps/x86_64/start.S (_start): Always indirect branch to __libc_start_main via GOT. I got (gdb) r --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 incremental_test /usr/lib/gcc/x86_64-redhat-linux/5.3.1/../../../../lib64/crt1.o /usr/lib/gcc/x86_64-redhat-linux/5.3.1/../../../../lib64/crti.o /usr/lib/gcc/x86_64-redhat-linux/5.3.1/crtbegin.o -Lgcctestdir -L/usr/lib/gcc/x86_64-redhat-linux/5.3.1 -L/usr/lib/gcc/x86_64-redhat-linux/5.3.1/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -L/usr/lib/gcc/x86_64-redhat-linux/5.3.1/../../.. --incremental-full -z norelro incremental_test_1.o incremental_test_2.o -lgcc -lgcc_eh -lc -lgcc -lgcc_eh /usr/lib/gcc/x86_64-redhat-linux/5.3.1/crtend.o /usr/lib/gcc/x86_64-redhat-linux/5.3.1/../../../../lib64/crtn.o Starting program: /export/build/gnu/binutils-gold/build-x86_64-linux/gold/ld-new --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 incremental_test /usr/lib/gcc/x86_64-redhat-linux/5.3.1/../../../../lib64/crt1.o /usr/lib/gcc/x86_64-redhat-linux/5.3.1/../../../../lib64/crti.o /usr/lib/gcc/x86_64-redhat-linux/5.3.1/crtbegin.o -Lgcctestdir -L/usr/lib/gcc/x86_64-redhat-linux/5.3.1 -L/usr/lib/gcc/x86_64-redhat-linux/5.3.1/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -L/usr/lib/gcc/x86_64-redhat-linux/5.3.1/../../.. --incremental-full -z norelro incremental_test_1.o incremental_test_2.o -lgcc -lgcc_eh -lc -lgcc -lgcc_eh /usr/lib/gcc/x86_64-redhat-linux/5.3.1/crtend.o /usr/lib/gcc/x86_64-redhat-linux/5.3.1/../../../../lib64/crtn.o Missing separate debuginfos, use: dnf debuginfo-install glibc-2.22-17.1.fc23.x86_64 [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib64/libthread_db.so.1". Program received signal SIGSEGV, Segmentation fault. 0x000000000044a9a6 in (anonymous namespace)::Output_data_plt_x86_64<64>::get_plt_entry_size (this=0x0) at /export/gnu/import/git/sources/binutils-gdb/gold/x86_64.cc:195 195 { return this->do_get_plt_entry_size(); } (gdb) bt #0 0x000000000044a9a6 in (anonymous namespace)::Output_data_plt_x86_64<64>::get_plt_entry_size (this=0x0) at /export/gnu/import/git/sources/binutils-gdb/gold/x86_64.cc:195 #1 0x000000000044a994 in (anonymous namespace)::Output_data_plt_x86_64<64>::first_plt_entry_offset (this=0x0) at /export/gnu/import/git/sources/binutils-gdb/gold/x86_64.cc:190 #2 0x000000000044575e in (anonymous namespace)::Target_x86_64<64>::first_plt_entry_offset (this=0xe8b3f0) at /export/gnu/import/git/sources/binutils-gdb/gold/x86_64.cc:1825 #3 0x0000000000702f35 in gold::Output_section_incremental_inputs<64, false>::write_got_plt (this=0xfb2b60, pov=0x7ffff7f9b148 "", view_size=28) at /export/gnu/import/git/sources/binutils-gdb/gold/incremental.cc:1949 #4 0x00000000006fee44 in gold::Output_section_incremental_inputs<64, false>::do_write (this=0xfb2b60, of=0x1083ae0) at /export/gnu/import/git/sources/binutils-gdb/gold/incremental.cc:1444 #5 0x000000000053fef6 in gold::Output_data::write (this=0xfb2b60, file=0x1083ae0) at /export/gnu/import/git/sources/binutils-gdb/gold/output.h:374 #6 0x0000000000779077 in gold::Output_section::Input_section::write ( this=0xfb2bb0, of=0x1083ae0) at /export/gnu/import/git/sources/binutils-gdb/gold/output.cc:2269 #7 0x000000000077c171 in gold::Output_section::do_write (this=0x1082840, of=0x1083ae0) ---Type <return> to continue, or q <return> to quit--- at /export/gnu/import/git/sources/binutils-gdb/gold/output.cc:3729 #8 0x000000000053fef6 in gold::Output_data::write (this=0x1082840, file=0x1083ae0) at /export/gnu/import/git/sources/binutils-gdb/gold/output.h:374 #9 0x0000000000713489 in gold::Layout::write_sections_after_input_sections ( this=0x7fffffff5500, of=0x1083ae0) at /export/gnu/import/git/sources/binutils-gdb/gold/layout.cc:5395 #10 0x0000000000713e73 in gold::Write_after_input_sections_task::run ( this=0x10840e0) at /export/gnu/import/git/sources/binutils-gdb/gold/layout.cc:5634 #11 0x0000000000853311 in gold::Workqueue::find_and_run_task ( this=0x7fffffff5de0, thread_number=0) at /export/gnu/import/git/sources/binutils-gdb/gold/workqueue.cc:319 #12 0x0000000000853922 in gold::Workqueue::process (this=0x7fffffff5de0, thread_number=0) at /export/gnu/import/git/sources/binutils-gdb/gold/workqueue.cc:495 #13 0x00000000004044d7 in main (argc=32, argv=0x7fffffffdd48) at /export/gnu/import/git/sources/binutils-gdb/gold/main.cc:252 (gdb) f 2 #2 0x000000000044575e in (anonymous namespace)::Target_x86_64<64>::first_plt_entry_offset (this=0xe8b3f0) at /export/gnu/import/git/sources/binutils-gdb/gold/x86_64.cc:1825 1825 return this->plt_->first_plt_entry_offset(); (gdb) p this->plt_ $1 = ((anonymous namespace)::Output_data_plt_x86_64<64> *) 0x0 (gdb)
A patch is posted at https://sourceware.org/ml/binutils/2016-06/msg00291.html
Fixed for 2.27.
The master branch has been updated by H.J. Lu <hjl@sourceware.org>: https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=8474a88fd23a3c9c33dfc8ded31a9a15e31e7765 commit 8474a88fd23a3c9c33dfc8ded31a9a15e31e7765 Author: H.J. Lu <hjl.tools@gmail.com> Date: Mon Jun 20 12:28:20 2016 -0700 gold/x86: Handle output without PLT If there is no PLT in output, return 0 for first_plt_entry_offset and plt_entry_size. PR gold/20245 * i386.cc (Target_i386::first_plt_entry_offset): Return 0 if plt_ is NULL. (Target_i386::plt_entry_size): Likewise. (Target_x86_64<size>::first_plt_entry_offset): Likewise. (Target_x86_64<size>::plt_entry_size): Likewise.