From 451875b4f976a527395e9303224c7881b65e12ed Mon Sep 17 00:00:00 2001 From: "H.J. Lu" Date: Wed, 14 Feb 2018 03:50:40 -0800 Subject: [PATCH] x86-64: Use PLT address for PC-relative reloc Since PLT in PDE and PC-relative PLT in PIE can be used as function address, there is no need for dynamic PC-relative relocation against a dynamic function definition in PIE. Linker should resolve PC-relative reference to its PLT address. NB: i386 has non-PIC PLT and PIC PLT. Only non-PIC PLT in PDE can be used as function address. PIC PLT in PIE can't be used as function address. bfd/ PR ld/22842 * elf32-i386.c (elf_i386_check_relocs): Pass FALSE for non PC-relative PLT to NEED_DYNAMIC_RELOCATION_P. * elf64-x86-64.c (elf_x86_64_check_relocs): Create PLT for R_X86_64_PC32 reloc against dynamic function in data section. Pass TRUE for PC-relative PLT to NEED_DYNAMIC_RELOCATION_P. (elf_x86_64_relocate_section): Use PLT for R_X86_64_PC32 reloc against dynamic function in data section. * elfxx-x86.c (elf_x86_allocate_dynrelocs): Use PLT in PIE as function address only if pcrel_plt is true. (_bfd_x86_elf_link_hash_table_create): Set pcrel_plt. * elfxx-x86.h (NEED_DYNAMIC_RELOCATION_P): Add PCREL_PLT for PC-relative PLT. If PLT is PC-relative, don't generate dynamic PC-relative relocation against a function definition in data secton in PIE. Remove the obsolete comments. (elf_x86_link_hash_table): Add pcrel_plt. ld/ PR ld/22842 * testsuite/ld-i386/i386.exp: Run PR ld/22842 tests. * testsuite/ld-x86-64/x86-64.exp: Likewise. * testsuite/ld-i386/pr22842a.c: New file. * testsuite/ld-i386/pr22842b.S: Likewise. * testsuite/ld-x86-64/pr22842a.c: Likewise. * testsuite/ld-x86-64/pr22842a.rd: Likewise. * testsuite/ld-x86-64/pr22842b.S: Likewise. * testsuite/ld-x86-64/pr22842b.rd: Likewise. --- bfd/ChangeLog | 19 +++++++++++++++ bfd/elf32-i386.c | 2 +- bfd/elf64-x86-64.c | 24 ++++++++++++++++-- bfd/elfxx-x86.c | 22 ++++++++++++----- bfd/elfxx-x86.h | 50 +++++++++++++++++++++++--------------- ld/ChangeLog | 12 +++++++++ ld/testsuite/ld-i386/i386.exp | 16 ++++++++++++ ld/testsuite/ld-i386/pr22842a.c | 20 +++++++++++++++ ld/testsuite/ld-i386/pr22842b.S | 41 +++++++++++++++++++++++++++++++ ld/testsuite/ld-x86-64/pr22842a.c | 20 +++++++++++++++ ld/testsuite/ld-x86-64/pr22842a.rd | 4 +++ ld/testsuite/ld-x86-64/pr22842b.S | 20 +++++++++++++++ ld/testsuite/ld-x86-64/pr22842b.rd | 4 +++ ld/testsuite/ld-x86-64/x86-64.exp | 25 +++++++++++++++++++ 14 files changed, 251 insertions(+), 28 deletions(-) create mode 100644 ld/testsuite/ld-i386/pr22842a.c create mode 100644 ld/testsuite/ld-i386/pr22842b.S create mode 100644 ld/testsuite/ld-x86-64/pr22842a.c create mode 100644 ld/testsuite/ld-x86-64/pr22842a.rd create mode 100644 ld/testsuite/ld-x86-64/pr22842b.S create mode 100644 ld/testsuite/ld-x86-64/pr22842b.rd diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 77cdfe8..b3afd99 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,22 @@ +2018-02-14 H.J. Lu + + PR ld/22842 + * elf32-i386.c (elf_i386_check_relocs): Pass FALSE for non + PC-relative PLT to NEED_DYNAMIC_RELOCATION_P. + * elf64-x86-64.c (elf_x86_64_check_relocs): Create PLT for + R_X86_64_PC32 reloc against dynamic function in data section. + Pass TRUE for PC-relative PLT to NEED_DYNAMIC_RELOCATION_P. + (elf_x86_64_relocate_section): Use PLT for R_X86_64_PC32 reloc + against dynamic function in data section. + * elfxx-x86.c (elf_x86_allocate_dynrelocs): Use PLT in PIE as + function address only if pcrel_plt is true. + (_bfd_x86_elf_link_hash_table_create): Set pcrel_plt. + * elfxx-x86.h (NEED_DYNAMIC_RELOCATION_P): Add PCREL_PLT for + PC-relative PLT. If PLT is PC-relative, don't generate dynamic + PC-relative relocation against a function definition in data + secton in PIE. Remove the obsolete comments. + (elf_x86_link_hash_table): Add pcrel_plt. + 2018-02-13 H.J. Lu * elfxx-x86.c (elf_x86_allocate_dynrelocs): Check bfd_link_dll, diff --git a/bfd/elf32-i386.c b/bfd/elf32-i386.c index e74da3a..1a4b53a 100644 --- a/bfd/elf32-i386.c +++ b/bfd/elf32-i386.c @@ -1812,7 +1812,7 @@ do_relocation: size_reloc = FALSE; do_size: - if (NEED_DYNAMIC_RELOCATION_P (info, h, sec, r_type, + if (NEED_DYNAMIC_RELOCATION_P (info, FALSE, h, sec, r_type, R_386_32)) { struct elf_dyn_relocs *p; diff --git a/bfd/elf64-x86-64.c b/bfd/elf64-x86-64.c index 0e4bb2e..00ed5d1 100644 --- a/bfd/elf64-x86-64.c +++ b/bfd/elf64-x86-64.c @@ -2135,7 +2135,17 @@ pointer: as pointer, make sure that PLT is used if foo is a function defined in a shared library. */ if ((sec->flags & SEC_CODE) == 0) - h->pointer_equality_needed = 1; + { + h->pointer_equality_needed = 1; + if (bfd_link_pie (info) + && h->type == STT_FUNC + && !h->def_regular + && h->def_dynamic) + { + h->needs_plt = 1; + h->plt.refcount = 1; + } + } } else if (r_type != R_X86_64_PC32_BND && r_type != R_X86_64_PC64) @@ -2173,7 +2183,7 @@ pointer: size_reloc = FALSE; do_size: - if (NEED_DYNAMIC_RELOCATION_P (info, h, sec, r_type, + if (NEED_DYNAMIC_RELOCATION_P (info, TRUE, h, sec, r_type, htab->pointer_r_type)) { struct elf_dyn_relocs *p; @@ -2968,6 +2978,7 @@ do_ifunc_pointer: break; } +use_plt: if (h->plt.offset != (bfd_vma) -1) { if (htab->plt_second != NULL) @@ -3046,6 +3057,15 @@ do_ifunc_pointer: return elf_x86_64_need_pic (info, input_bfd, input_section, h, NULL, NULL, howto); } + /* Since x86-64 has PC-relative PLT, we can use PLT in PIE + as function address. */ + else if (h != NULL + && (input_section->flags & SEC_CODE) == 0 + && bfd_link_pie (info) + && h->type == STT_FUNC + && !h->def_regular + && h->def_dynamic) + goto use_plt; /* Fall through. */ case R_X86_64_8: diff --git a/bfd/elfxx-x86.c b/bfd/elfxx-x86.c index bd36707..05d283b 100644 --- a/bfd/elfxx-x86.c +++ b/bfd/elfxx-x86.c @@ -179,6 +179,7 @@ elf_x86_allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) asection *s = htab->elf.splt; asection *second_s = htab->plt_second; asection *got_s = htab->plt_got; + bfd_boolean use_plt; /* If this is the first .plt entry, make room for the special first entry. The .plt section is used by prelink to undo @@ -196,12 +197,19 @@ elf_x86_allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) } /* If this symbol is not defined in a regular file, and we are - not generating a shared library, then set the symbol to this - location in the .plt. This is required to make function - pointers compare as equal between the normal executable and - the shared library. */ - if (! bfd_link_dll (info) - && !h->def_regular) + generating PDE, then set the symbol to this location in the + .plt. This is required to make function pointers compare + as equal between PDE and the shared library. + + NB: If PLT is PC-relative, we can use the .plt in PIE for + function address. */ + if (h->def_regular) + use_plt = FALSE; + else if (htab->pcrel_plt) + use_plt = ! bfd_link_dll (info); + else + use_plt = bfd_link_pde (info); + if (use_plt) { if (use_plt_got) { @@ -771,6 +779,7 @@ _bfd_x86_elf_link_hash_table_create (bfd *abfd) ret->dt_reloc_sz = DT_RELASZ; ret->dt_reloc_ent = DT_RELAENT; ret->got_entry_size = 8; + ret->pcrel_plt = TRUE; ret->tls_get_addr = "__tls_get_addr"; } if (ABI_64_P (abfd)) @@ -798,6 +807,7 @@ _bfd_x86_elf_link_hash_table_create (bfd *abfd) ret->dt_reloc_ent = DT_RELENT; ret->sizeof_reloc = sizeof (Elf32_External_Rel); ret->got_entry_size = 4; + ret->pcrel_plt = FALSE; ret->pointer_r_type = R_386_32; ret->dynamic_interpreter = ELF32_DYNAMIC_INTERPRETER; ret->dynamic_interpreter_size diff --git a/bfd/elfxx-x86.h b/bfd/elfxx-x86.h index 0162a90..f84fe01 100644 --- a/bfd/elfxx-x86.h +++ b/bfd/elfxx-x86.h @@ -76,14 +76,11 @@ into the shared library. However, if we are linking with -Bsymbolic, we do not need to copy a reloc against a global symbol which is defined in an object we are including in the link (i.e., DEF_REGULAR - is set). At this point we have not seen all the input files, so it - is possible that DEF_REGULAR is not set now but will be set later (it - is never cleared). In case of a weak definition, DEF_REGULAR may be - cleared later by a strong definition in a shared library. We account - for that possibility below by storing information in the relocs_copied - field of the hash table entry. A similar situation occurs when - creating shared libraries and symbol visibility changes render the - symbol local. + is set). + + If PCREL_PLT is true, don't generate dynamic relocation in PIE for + PC-relative relocation against a dynamic function definition in data + section when PLT address can be used. If on the other hand, we are creating an executable, we may need to keep relocations for symbols satisfied by a dynamic library if we @@ -91,23 +88,30 @@ We also need to generate dynamic pointer relocation against STT_GNU_IFUNC symbol in the non-code section. */ -#define NEED_DYNAMIC_RELOCATION_P(INFO, H, SEC, R_TYPE, POINTER_TYPE) \ +#define NEED_DYNAMIC_RELOCATION_P(INFO, PCREL_PLT, H, SEC, R_TYPE, \ + POINTER_TYPE) \ ((bfd_link_pic (INFO) \ && (! X86_PCREL_TYPE_P (R_TYPE) \ || ((H) != NULL \ && (! (bfd_link_pie (INFO) \ || SYMBOLIC_BIND ((INFO), (H))) \ || (H)->root.type == bfd_link_hash_defweak \ - || !(H)->def_regular)))) \ - || ((H) != NULL \ - && (H)->type == STT_GNU_IFUNC \ - && (R_TYPE) == POINTER_TYPE \ - && ((SEC)->flags & SEC_CODE) == 0) \ - || (ELIMINATE_COPY_RELOCS \ - && !bfd_link_pic (INFO) \ - && (H) != NULL \ - && ((H)->root.type == bfd_link_hash_defweak \ - || !(H)->def_regular))) + || (!(bfd_link_pie (INFO) \ + && (PCREL_PLT) \ + && (H)->plt.refcount > 0 \ + && ((SEC)->flags & SEC_CODE) == 0 \ + && (H)->type == STT_FUNC \ + && (H)->def_dynamic) \ + && !(H)->def_regular))))) \ + || ((H) != NULL \ + && (H)->type == STT_GNU_IFUNC \ + && (R_TYPE) == POINTER_TYPE \ + && ((SEC)->flags & SEC_CODE) == 0) \ + || (ELIMINATE_COPY_RELOCS \ + && !bfd_link_pic (INFO) \ + && (H) != NULL \ + && ((H)->root.type == bfd_link_hash_defweak \ + || !(H)->def_regular))) /* TRUE if dynamic relocation should be generated. Don't copy a pc-relative relocation into the output file if the symbol needs @@ -482,6 +486,14 @@ struct elf_x86_link_hash_table /* TRUE if GOT is referenced. */ unsigned int got_referenced : 1; + /* TRUE if PLT is PC-relative. PLT in PDE and PC-relative PLT in PIE + can be used as function address. + + NB: i386 has non-PIC PLT and PIC PLT. Only non-PIC PLT in PDE can + be used as function address. PIC PLT in PIE can't be used as + function address. */ + unsigned int pcrel_plt : 1; + bfd_vma (*r_info) (bfd_vma, bfd_vma); bfd_vma (*r_sym) (bfd_vma); bfd_boolean (*is_reloc_section) (const char *); diff --git a/ld/ChangeLog b/ld/ChangeLog index 42ddf8b..c8a967a 100644 --- a/ld/ChangeLog +++ b/ld/ChangeLog @@ -1,3 +1,15 @@ +2018-02-14 H.J. Lu + + PR ld/22842 + * testsuite/ld-i386/i386.exp: Run PR ld/22842 tests. + * testsuite/ld-x86-64/x86-64.exp: Likewise. + * testsuite/ld-i386/pr22842a.c: New file. + * testsuite/ld-i386/pr22842b.S: Likewise. + * testsuite/ld-x86-64/pr22842a.c: Likewise. + * testsuite/ld-x86-64/pr22842a.rd: Likewise. + * testsuite/ld-x86-64/pr22842b.S: Likewise. + * testsuite/ld-x86-64/pr22842b.rd: Likewise. + 2018-02-14 Maciej W. Rozycki * ldlex.h (ldlex_command): Remove prototype. diff --git a/ld/testsuite/ld-i386/i386.exp b/ld/testsuite/ld-i386/i386.exp index e4ec07d..ba2cce8 100644 --- a/ld/testsuite/ld-i386/i386.exp +++ b/ld/testsuite/ld-i386/i386.exp @@ -1273,6 +1273,14 @@ if { [isnative] {} \ "pr21997-1.so" \ ] \ + [list \ + "Build pr22842.so" \ + "-shared" \ + "-fPIC" \ + { pr22842a.c } \ + {} \ + "pr22842.so" \ + ] \ ] run_ld_link_exec_tests [list \ @@ -1371,6 +1379,14 @@ if { [isnative] "pr21997-1-pie-2" \ "pass.out" \ ] \ + [list \ + "Build pr22842" \ + "-pie -Wl,--no-as-needed tmpdir/pr22842.so" \ + "" \ + { pr22842b.S } \ + "pr22842" \ + "pass.out" \ + ] \ ] if { [at_least_gcc_version 5 0] } { diff --git a/ld/testsuite/ld-i386/pr22842a.c b/ld/testsuite/ld-i386/pr22842a.c new file mode 100644 index 0000000..52489bb --- /dev/null +++ b/ld/testsuite/ld-i386/pr22842a.c @@ -0,0 +1,20 @@ +#include +#include + +void +test (void) +{ + static int count; + if (count) + printf("PASS\n"); + count++; +} + +void +foo (void (*bar) (void)) +{ + if (bar != test) + abort (); + bar (); + test (); +} diff --git a/ld/testsuite/ld-i386/pr22842b.S b/ld/testsuite/ld-i386/pr22842b.S new file mode 100644 index 0000000..d959a5f --- /dev/null +++ b/ld/testsuite/ld-i386/pr22842b.S @@ -0,0 +1,41 @@ + .text + .globl main + .type main, @function +main: + leal 4(%esp), %ecx + andl $-16, %esp + pushl -4(%ecx) + pushl %ebp + movl %esp, %ebp + pushl %ebx + pushl %ecx + call __x86.get_pc_thunk.bx + addl $_GLOBAL_OFFSET_TABLE_, %ebx + subl $12, %esp + movl bar@GOT(%ebx), %eax + addl (%eax), %eax + pushl %eax + call foo@PLT + addl $16, %esp + leal -8(%ebp), %esp + xorl %eax, %eax + popl %ecx + popl %ebx + popl %ebp + leal -4(%ecx), %esp + ret + .size main, .-main + .section .text.__x86.get_pc_thunk.bx,"axG",@progbits,__x86.get_pc_thunk.bx,comdat + .globl __x86.get_pc_thunk.bx + .hidden __x86.get_pc_thunk.bx + .type __x86.get_pc_thunk.bx, @function +__x86.get_pc_thunk.bx: + movl (%esp), %ebx + ret + + .data + .p2align 2 +bar: + .long test - . + + .section .note.GNU-stack,"",@progbits diff --git a/ld/testsuite/ld-x86-64/pr22842a.c b/ld/testsuite/ld-x86-64/pr22842a.c new file mode 100644 index 0000000..52489bb --- /dev/null +++ b/ld/testsuite/ld-x86-64/pr22842a.c @@ -0,0 +1,20 @@ +#include +#include + +void +test (void) +{ + static int count; + if (count) + printf("PASS\n"); + count++; +} + +void +foo (void (*bar) (void)) +{ + if (bar != test) + abort (); + bar (); + test (); +} diff --git a/ld/testsuite/ld-x86-64/pr22842a.rd b/ld/testsuite/ld-x86-64/pr22842a.rd new file mode 100644 index 0000000..d78ea2f --- /dev/null +++ b/ld/testsuite/ld-x86-64/pr22842a.rd @@ -0,0 +1,4 @@ +#failif +#... +[0-9a-f ]+R_X86_64_NONE.* +#... diff --git a/ld/testsuite/ld-x86-64/pr22842b.S b/ld/testsuite/ld-x86-64/pr22842b.S new file mode 100644 index 0000000..f0659cd --- /dev/null +++ b/ld/testsuite/ld-x86-64/pr22842b.S @@ -0,0 +1,20 @@ + .text + .globl main + .type main,@function +main: + pushq %rax + movslq bar(%rip), %rax + leaq bar(%rip), %rdi + addq %rax, %rdi + + callq foo + xorl %eax, %eax + popq %rcx + retq + + .data + .p2align 2 +bar: + .long test - . + + .section .note.GNU-stack,"",@progbits diff --git a/ld/testsuite/ld-x86-64/pr22842b.rd b/ld/testsuite/ld-x86-64/pr22842b.rd new file mode 100644 index 0000000..f1036d5 --- /dev/null +++ b/ld/testsuite/ld-x86-64/pr22842b.rd @@ -0,0 +1,4 @@ +#failif +#... +[0-9a-f ]+R_X86_64_PC32 +[0-9a-f]+ +test \+ 0 +#... diff --git a/ld/testsuite/ld-x86-64/x86-64.exp b/ld/testsuite/ld-x86-64/x86-64.exp index 8442663..2a46266 100644 --- a/ld/testsuite/ld-x86-64/x86-64.exp +++ b/ld/testsuite/ld-x86-64/x86-64.exp @@ -1190,6 +1190,23 @@ if { [isnative] && [which $CC] != 0 } { {{readelf -drW pr22791-2.rd}} \ "pr22791-2" \ ] \ + [list \ + "Build pr22842.so" \ + "-shared" \ + "-fPIC" \ + { pr22842a.c } \ + {} \ + "pr22842.so" \ + ] \ + [list \ + "Build pr22842" \ + "-pie -Wl,--no-as-needed tmpdir/pr22842.so" \ + "" \ + { pr22842b.S } \ + {{readelf -rW pr22842a.rd} \ + {readelf -rW pr22842b.rd}} \ + "pr22842" \ + ] \ ] if {[istarget "x86_64-*-linux*-gnux32"]} { @@ -1524,6 +1541,14 @@ if { [isnative] && [which $CC] != 0 } { "pass.out" \ "$NOPIE_CFLAGS" \ ] \ + [list \ + "Build pr22842" \ + "-pie -Wl,--no-as-needed tmpdir/pr22842.so" \ + "" \ + { pr22842b.S } \ + "pr22842" \ + "pass.out" \ + ] \ ] # Run-time tests which require working ifunc attribute support. -- 2.7.4