From bd7ab16b4537788ad53521c45469a1bdae84ad4a Mon Sep 17 00:00:00 2001 From: "H.J. Lu" Date: Tue, 13 Feb 2018 07:34:22 -0800 Subject: x86-64: Generate branch with PLT32 relocation Since there is no need to prepare for PLT branch on x86-64, generate R_X86_64_PLT32, instead of R_X86_64_PC32, if possible, which can be used as a marker for 32-bit PC-relative branches. To compile Linux kernel, this patch: From: "H.J. Lu" Subject: [PATCH] x86: Treat R_X86_64_PLT32 as R_X86_64_PC32 On i386, there are 2 types of PLTs, PIC and non-PIC. PIE and shared objects must use PIC PLT. To use PIC PLT, you need to load _GLOBAL_OFFSET_TABLE_ into EBX first. There is no need for that on x86-64 since x86-64 uses PC-relative PLT. On x86-64, for 32-bit PC-relative branches, we can generate PLT32 relocation, instead of PC32 relocation, which can also be used as a marker for 32-bit PC-relative branches. Linker can always reduce PLT32 relocation to PC32 if function is defined locally. Local functions should use PC32 relocation. As far as Linux kernel is concerned, R_X86_64_PLT32 can be treated the same as R_X86_64_PC32 since Linux kernel doesn't use PLT. is needed. It is available on hjl/plt32/master branch at https://github.com/hjl-tools/linux bfd/ PR gas/22791 * elf64-x86-64.c (is_32bit_relative_branch): Removed. (elf_x86_64_relocate_section): Check PIC relocations in PIE. Remove is_32bit_relative_branch usage. Disallow PC32 reloc against protected function in shared object. gas/ PR gas/22791 * config/tc-i386.c (need_plt32_p): New function. (output_jump): Generate BFD_RELOC_X86_64_PLT32 if possible. (md_estimate_size_before_relax): Likewise. * testsuite/gas/i386/reloc64.d: Updated. * testsuite/gas/i386/x86-64-jump.d: Likewise. * testsuite/gas/i386/x86-64-mpx-branch-1.d: Likewise. * testsuite/gas/i386/x86-64-mpx-branch-2.d: Likewise. * testsuite/gas/i386/x86-64-relax-2.d: Likewise. * testsuite/gas/i386/x86-64-relax-3.d: Likewise. * testsuite/gas/i386/ilp32/reloc64.d: Likewise. * testsuite/gas/i386/ilp32/x86-64-branch.d: Likewise. ld/ PR gas/22791 * testsuite/ld-x86-64/mpx1c.rd: Updated. * testsuite/ld-x86-64/pr22791-1.err: New file. * testsuite/ld-x86-64/pr22791-1a.c: Likewise. * testsuite/ld-x86-64/pr22791-1b.s: Likewise. * testsuite/ld-x86-64/pr22791-2.rd: Likewise. * testsuite/ld-x86-64/pr22791-2a.s: Likewise. * testsuite/ld-x86-64/pr22791-2b.c: Likewise. * testsuite/ld-x86-64/pr22791-2c.s: Likewise. * testsuite/ld-x86-64/x86-64.exp: Run PR ld/22791 tests. --- gas/config/tc-i386.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) (limited to 'gas/config') diff --git a/gas/config/tc-i386.c b/gas/config/tc-i386.c index 552c1b8..1a5be1b 100644 --- a/gas/config/tc-i386.c +++ b/gas/config/tc-i386.c @@ -7023,12 +7023,46 @@ output_branch (void) frag_var (rs_machine_dependent, 5, i.reloc[0], subtype, sym, off, p); } +#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) +/* Return TRUE iff PLT32 relocation should be used for branching to + symbol S. */ + +static bfd_boolean +need_plt32_p (symbolS *s) +{ + /* PLT32 relocation is ELF only. */ + if (!IS_ELF) + return FALSE; + + /* Since there is no need to prepare for PLT branch on x86-64, we + can generate R_X86_64_PLT32, instead of R_X86_64_PC32, which can + be used as a marker for 32-bit PC-relative branches. */ + if (!object_64bit) + return FALSE; + + /* Weak or undefined symbol need PLT32 relocation. */ + if (S_IS_WEAK (s) || !S_IS_DEFINED (s)) + return TRUE; + + /* Non-global symbol doesn't need PLT32 relocation. */ + if (! S_IS_EXTERNAL (s)) + return FALSE; + + /* Other global symbols need PLT32 relocation. NB: Symbol with + non-default visibilities are treated as normal global symbol + so that PLT32 relocation can be used as a marker for 32-bit + PC-relative branches. It is useful for linker relaxation. */ + return TRUE; +} +#endif + static void output_jump (void) { char *p; int size; fixS *fixP; + bfd_reloc_code_real_type jump_reloc = i.reloc[0]; if (i.tm.opcode_modifier.jumpbyte) { @@ -7096,8 +7130,17 @@ output_jump (void) abort (); } +#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) + if (size == 4 + && jump_reloc == NO_RELOC + && need_plt32_p (i.op[0].disps->X_add_symbol)) + jump_reloc = BFD_RELOC_X86_64_PLT32; +#endif + + jump_reloc = reloc (size, 1, 1, jump_reloc); + fixP = fix_new_exp (frag_now, p - frag_now->fr_literal, size, - i.op[0].disps, 1, reloc (size, 1, 1, i.reloc[0])); + i.op[0].disps, 1, jump_reloc); /* All jumps handled here are signed, but don't use a signed limit check for 32 and 16 bit jumps as we want to allow wrap around at @@ -9315,6 +9358,10 @@ md_estimate_size_before_relax (fragS *fragP, segT segment) reloc_type = (enum bfd_reloc_code_real) fragP->fr_var; else if (size == 2) reloc_type = BFD_RELOC_16_PCREL; +#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) + else if (need_plt32_p (fragP->fr_symbol)) + reloc_type = BFD_RELOC_X86_64_PLT32; +#endif else reloc_type = BFD_RELOC_32_PCREL; -- cgit v1.1