diff options
author | H.J. Lu <hjl.tools@gmail.com> | 2015-10-22 04:49:20 -0700 |
---|---|---|
committer | H.J. Lu <hjl.tools@gmail.com> | 2015-10-22 04:49:38 -0700 |
commit | 56ceb5b5405af23eddd12e12d8ba849010120324 (patch) | |
tree | 3404c0c3ed3df6a50bcb4bcf979e0bb276d84857 /bfd/elf64-x86-64.c | |
parent | 02a866936de7e63ad4962ccba56c8cd05c231275 (diff) | |
download | binutils-56ceb5b5405af23eddd12e12d8ba849010120324.zip binutils-56ceb5b5405af23eddd12e12d8ba849010120324.tar.gz binutils-56ceb5b5405af23eddd12e12d8ba849010120324.tar.bz2 |
Add R_X86_64_[REX_]GOTPCRELX support to gas and ld
This patch adds support for the R_X86_64_GOTPCRELX and
R_X86_64_REX_GOTPCRELX relocations proposed in
https://groups.google.com/forum/#!topic/x86-64-abi/n9AWHogmVY0
to gas and ld. It updates gas to generate R_X86_64_GOTPCRELX,
R_X86_64_REX_GOTPCRELX if there is a REX prefix, relocation for memory
operand, foo@GOTPCREL(%rip). With the locally defined symbol, foo, we
convert
mov foo@GOTPCREL(%rip), %reg
to
lea foo(%rip), %reg
and convert
call/jmp *foo@GOTPCREL(%rip)
to
nop call foo/jmp foo nop
When PIC is false, convert
test %reg, foo@GOTPCREL(%rip)
to
test $foo, %reg
and convert
binop foo@GOTPCREL(%rip), %reg
to
binop $foo, %reg
where binop is one of adc, add, and, cmp, or, sbb, sub, xor instructions.
bfd/
* elf64-x86-64.c: Include opcode/i386.h.
(x86_64_elf_howto_table): Add R_X86_64_GOTPCRELX and
R_X86_64_REX_GOTPCRELX.
(R_X86_64_standard): Replace R_X86_64_PLT32_BND with
R_X86_64_REX_GOTPCRELX.
(x86_64_reloc_map): Add BFD_RELOC_X86_64_GOTPCRELX and
BFD_RELOC_X86_64_REX_GOTPCRELX.
(need_convert_mov_to_lea): Renamed to ...
(need_convert_load): This.
(elf_x86_64_check_relocs): Handle R_X86_64_GOTPCRELX and
R_X86_64_REX_GOTPCRELX. Replace need_convert_mov_to_lea with
need_convert_load.
(elf_x86_64_gc_sweep_hook): Handle R_X86_64_GOTPCRELX and
R_X86_64_REX_GOTPCRELX.
(elf_x86_64_size_dynamic_sections): Likewise.
(elf_x86_64_relocate_section): Likewise.
(elf_x86_64_convert_mov_to_lea): Renamed to ...
(elf_x86_64_convert_load): This. Replace need_convert_mov_to_lea
with need_convert_load. Support R_X86_64_GOTPCRELX and
R_X86_64_REX_GOTPCRELX transformations.
* reloc.c (BFD_RELOC_X86_64_GOTPCRELX): New.
(BFD_RELOC_X86_64_REX_GOTPCRELX): Likewise.
* bfd-in2.h: Regenerated.
* libbfd.h: Likewise.
gas/
* config/tc-i386.c (tc_i386_fix_adjustable): Handle
BFD_RELOC_X86_64_GOTPCRELX and BFD_RELOC_X86_64_REX_GOTPCRELX.
(tc_gen_reloc): Likewise.
(i386_validate_fix): Generate BFD_RELOC_X86_64_GOTPCRELX or
BFD_RELOC_X86_64_REX_GOTPCRELX if fx_tcbit2 is set.
* config/tc-i386.h (TC_FORCE_RELOCATION_LOCAL): Also return
true for BFD_RELOC_X86_64_GOTPCRELX and
BFD_RELOC_X86_64_REX_GOTPCRELX.
gas/testsuite/
* gas/i386/i386.exp: Run x86-64-gotpcrel.
* gas/i386/x86-64-gotpcrel.d: New file.
* gas/i386/x86-64-gotpcrel.s: Likewise.
* gas/i386/ilp32/x86-64-gotpcrel.d: Likewise.
* gas/i386/x86-64-localpic.d: Replace R_X86_64_GOTPCREL with
R_X86_64_REX_GOTPCRELX.
* gas/i386/ilp32/x86-64-localpic.d: Likewise.
include/elf/
* x86-64.h (R_X86_64_GOTPCRELX): New.
(R_X86_64_REX_GOTPCRELX): Likewise.
ld/testsuite/
* ld-ifunc/ifunc-5r-local-x86-64.d: Replace R_X86_64_GOTPCREL
with R_X86_64_REX_GOTPCRELX.
* ld-x86-64/plt-main1.rd: Likewise.
* ld-x86-64/plt-main3.rd: Likewise.
* ld-x86-64/plt-main4.rd: Likewise.
* ld-x86-64/gotpcrel1.dd: New file.
* ld-x86-64/gotpcrel1.out: Likewise.
* ld-x86-64/gotpcrel1a.S: Likewise.
* ld-x86-64/gotpcrel1b.c: Likewise.
* ld-x86-64/gotpcrel1c.c: Likewise.
* ld-x86-64/gotpcrel1d.S: Likewise.
* ld-x86-64/load1.s: Likewise.
* ld-x86-64/load1a.d: Likewise.
* ld-x86-64/load1b.d: Likewise.
* ld-x86-64/load1c.d: Likewise.
* ld-x86-64/load1d.d: Likewise.
* ld-x86-64/x86-64.exp: Run load1a, load1b, load1c and load1d
tests. Run gotpcrel1 test.
Diffstat (limited to 'bfd/elf64-x86-64.c')
-rw-r--r-- | bfd/elf64-x86-64.c | 194 |
1 files changed, 164 insertions, 30 deletions
diff --git a/bfd/elf64-x86-64.c b/bfd/elf64-x86-64.c index dc5fd0d..f8bb0d5 100644 --- a/bfd/elf64-x86-64.c +++ b/bfd/elf64-x86-64.c @@ -31,6 +31,7 @@ #include "dwarf2.h" #include "libiberty.h" +#include "opcode/i386.h" #include "elf/x86-64.h" #ifdef CORE_HEADER @@ -176,12 +177,18 @@ static reloc_howto_type x86_64_elf_howto_table[] = HOWTO(R_X86_64_PLT32_BND, 0, 2, 32, TRUE, 0, complain_overflow_signed, bfd_elf_generic_reloc, "R_X86_64_PLT32_BND", FALSE, 0xffffffff, 0xffffffff, TRUE), + HOWTO(R_X86_64_GOTPCRELX, 0, 2, 32, TRUE, 0, complain_overflow_signed, + bfd_elf_generic_reloc, "R_X86_64_GOTPCRELX", FALSE, 0xffffffff, + 0xffffffff, TRUE), + HOWTO(R_X86_64_REX_GOTPCRELX, 0, 2, 32, TRUE, 0, complain_overflow_signed, + bfd_elf_generic_reloc, "R_X86_64_REX_GOTPCRELX", FALSE, 0xffffffff, + 0xffffffff, TRUE), /* We have a gap in the reloc numbers here. R_X86_64_standard counts the number up to this point, and R_X86_64_vt_offset is the value to subtract from a reloc type of R_X86_64_GNU_VT* to form an index into this table. */ -#define R_X86_64_standard (R_X86_64_PLT32_BND + 1) +#define R_X86_64_standard (R_X86_64_REX_GOTPCRELX + 1) #define R_X86_64_vt_offset (R_X86_64_GNU_VTINHERIT - R_X86_64_standard) /* GNU extension to record C++ vtable hierarchy. */ @@ -253,8 +260,10 @@ static const struct elf_reloc_map x86_64_reloc_map[] = { BFD_RELOC_X86_64_TLSDESC_CALL, R_X86_64_TLSDESC_CALL, }, { BFD_RELOC_X86_64_TLSDESC, R_X86_64_TLSDESC, }, { BFD_RELOC_X86_64_IRELATIVE, R_X86_64_IRELATIVE, }, - { BFD_RELOC_X86_64_PC32_BND, R_X86_64_PC32_BND,}, - { BFD_RELOC_X86_64_PLT32_BND, R_X86_64_PLT32_BND,}, + { BFD_RELOC_X86_64_PC32_BND, R_X86_64_PC32_BND, }, + { BFD_RELOC_X86_64_PLT32_BND, R_X86_64_PLT32_BND, }, + { BFD_RELOC_X86_64_GOTPCRELX, R_X86_64_GOTPCRELX, }, + { BFD_RELOC_X86_64_REX_GOTPCRELX, R_X86_64_REX_GOTPCRELX, }, { BFD_RELOC_VTABLE_INHERIT, R_X86_64_GNU_VTINHERIT, }, { BFD_RELOC_VTABLE_ENTRY, R_X86_64_GNU_VTENTRY, }, }; @@ -1545,7 +1554,7 @@ elf_x86_64_tls_transition (struct bfd_link_info *info, bfd *abfd, /* Rename some of the generic section flags to better document how they are used here. */ -#define need_convert_mov_to_lea sec_flg0 +#define need_convert_load sec_flg0 /* Look through the relocs for a section during the first phase, and calculate needed space in the global offset table, procedure @@ -1726,6 +1735,8 @@ elf_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info, case R_X86_64_32S: case R_X86_64_PC64: case R_X86_64_GOTPCREL: + case R_X86_64_GOTPCRELX: + case R_X86_64_REX_GOTPCRELX: case R_X86_64_GOTPCREL64: if (htab->elf.dynobj == NULL) htab->elf.dynobj = abfd; @@ -1779,6 +1790,8 @@ elf_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info, case R_X86_64_GOT32: case R_X86_64_GOTPCREL: + case R_X86_64_GOTPCRELX: + case R_X86_64_REX_GOTPCRELX: case R_X86_64_TLSGD: case R_X86_64_GOT64: case R_X86_64_GOTPCREL64: @@ -2151,9 +2164,11 @@ do_size: return FALSE; } - if (r_type == R_X86_64_GOTPCREL + if ((r_type == R_X86_64_GOTPCREL + || r_type == R_X86_64_GOTPCRELX + || r_type == R_X86_64_REX_GOTPCRELX) && (h == NULL || h->type != STT_GNU_IFUNC)) - sec->need_convert_mov_to_lea = 1; + sec->need_convert_load = 1; } return TRUE; @@ -2279,6 +2294,8 @@ elf_x86_64_gc_sweep_hook (bfd *abfd, struct bfd_link_info *info, case R_X86_64_GOTTPOFF: case R_X86_64_GOT32: case R_X86_64_GOTPCREL: + case R_X86_64_GOTPCRELX: + case R_X86_64_REX_GOTPCRELX: case R_X86_64_GOT64: case R_X86_64_GOTPCREL64: case R_X86_64_GOTPLT64: @@ -2953,15 +2970,28 @@ elf_x86_64_readonly_dynrelocs (struct elf_link_hash_entry *h, return TRUE; } -/* Convert +/* With the local symbol, foo, we convert mov foo@GOTPCREL(%rip), %reg to lea foo(%rip), %reg - with the local symbol, foo. */ + and convert + call/jmp *foo@GOTPCREL(%rip) + to + nop call foo/jmp foo nop + When PIC is false, convert + test %reg, foo@GOTPCREL(%rip) + to + test $foo, %reg + and convert + binop foo@GOTPCREL(%rip), %reg + to + binop $foo, %reg + where binop is one of adc, add, and, cmp, or, sbb, sub, xor + instructions. */ static bfd_boolean -elf_x86_64_convert_mov_to_lea (bfd *abfd, asection *sec, - struct bfd_link_info *link_info) +elf_x86_64_convert_load (bfd *abfd, asection *sec, + struct bfd_link_info *link_info) { Elf_Internal_Shdr *symtab_hdr; Elf_Internal_Rela *internal_relocs; @@ -2979,7 +3009,7 @@ elf_x86_64_convert_mov_to_lea (bfd *abfd, asection *sec, /* Nothing to do if there is no need or no output. */ if ((sec->flags & (SEC_CODE | SEC_RELOC)) != (SEC_CODE | SEC_RELOC) - || sec->need_convert_mov_to_lea == 0 + || sec->need_convert_load == 0 || bfd_is_abs_section (sec->output_section)) return TRUE; @@ -3017,22 +3047,45 @@ elf_x86_64_convert_mov_to_lea (bfd *abfd, asection *sec, asection *tsec; char symtype; bfd_vma toff, roff; + bfd_signed_vma raddend; unsigned int opcode; + unsigned int modrm; - if (r_type != R_X86_64_GOTPCREL) + if (r_type != R_X86_64_GOTPCREL + && r_type != R_X86_64_GOTPCRELX + && r_type != R_X86_64_REX_GOTPCRELX) continue; roff = irel->r_offset; + if (roff < (r_type == R_X86_64_REX_GOTPCRELX ? 3 : 2)) + continue; - if (roff < 2) + raddend = irel->r_addend; + /* Addend for 32-bit PC-relative relocation must be -4. */ + if (raddend != -4) continue; opcode = bfd_get_8 (abfd, contents + roff - 2); - /* PR ld/18591: Don't convert R_X86_64_GOTPCREL relocation if it - isn't for mov instruction. */ + /* It is OK to convert mov to lea. */ if (opcode != 0x8b) - continue; + { + /* Only convert R_X86_64_GOTPCRELX and R_X86_64_REX_GOTPCRELX + for mov call, jmp or one of adc, add, and, cmp, or, sbb, + sub, test, xor instructions. */ + if (r_type != R_X86_64_GOTPCRELX + && r_type != R_X86_64_REX_GOTPCRELX) + continue; + + /* It is OK to convert indirect branch to direct branch. */ + if (opcode != 0xff) + { + /* It is OK to convert adc, add, and, cmp, or, sbb, sub, + test, xor only when PIC is false. */ + if (bfd_link_pic (link_info)) + continue; + } + } /* Get the symbol referred to by the reloc. */ if (r_symndx < symtab_hdr->sh_info) @@ -3044,8 +3097,8 @@ elf_x86_64_convert_mov_to_lea (bfd *abfd, asection *sec, symtype = ELF_ST_TYPE (isym->st_info); - /* STT_GNU_IFUNC must keep R_X86_64_GOTPCREL relocation and - skip relocation against undefined symbols. */ + /* STT_GNU_IFUNC must keep GOTPCREL relocations and skip + relocation against undefined symbols. */ if (symtype == STT_GNU_IFUNC || isym->st_shndx == SHN_UNDEF) continue; @@ -3071,9 +3124,9 @@ elf_x86_64_convert_mov_to_lea (bfd *abfd, asection *sec, || h->root.type == bfd_link_hash_warning) h = (struct elf_link_hash_entry *) h->root.u.i.link; - /* STT_GNU_IFUNC must keep R_X86_64_GOTPCREL relocation. We also - avoid optimizing _DYNAMIC since ld.so may use its link-time - address. */ + /* STT_GNU_IFUNC must keep GOTPCREL relocations. We also + avoid optimizing GOTPCREL relocations againt _DYNAMIC + since ld.so may use its link-time address. */ if ((h->root.type == bfd_link_hash_defined || h->root.type == bfd_link_hash_defweak) && h->type != STT_GNU_IFUNC @@ -3107,17 +3160,17 @@ elf_x86_64_convert_mov_to_lea (bfd *abfd, asection *sec, access is presumed to be an offset from "sym"; The location of interest is just "sym". */ if (symtype == STT_SECTION) - toff += irel->r_addend; + toff += raddend; toff = _bfd_merged_section_offset (abfd, &tsec, elf_section_data (tsec)->sec_info, toff); if (symtype != STT_SECTION) - toff += irel->r_addend; + toff += raddend; } else - toff += irel->r_addend; + toff += raddend; /* Don't convert if R_X86_64_PC32 relocation overflows. */ if (tsec->output_section == sec->output_section) @@ -3148,8 +3201,8 @@ elf_x86_64_convert_mov_to_lea (bfd *abfd, asection *sec, } } - /* Don't convert R_X86_64_GOTPCREL if TSEC isn't placed after - SEC. */ + /* Don't convert GOTPCREL relocations if TSEC isn't placed + after SEC. */ if (asect == NULL) continue; @@ -3160,8 +3213,82 @@ elf_x86_64_convert_mov_to_lea (bfd *abfd, asection *sec, continue; } - bfd_put_8 (abfd, 0x8d, contents + roff - 2); - irel->r_info = htab->r_info (r_symndx, R_X86_64_PC32); + if (opcode == 0xff) + { + /* We have "call/jmp *foo@GOTPCREL(%rip)". */ + unsigned int nop; + bfd_vma nop_offset; + + /* Convert R_X86_64_GOTPCRELX and R_X86_64_REX_GOTPCRELX to + R_X86_64_PC32. */ + modrm = bfd_get_8 (abfd, contents + roff - 1); + if (modrm == 0x25) + { + unsigned int disp; + /* Convert to "jmp foo nop". */ + modrm = 0xe9; + nop = NOP_OPCODE; + nop_offset = irel->r_offset + 3; + disp = bfd_get_32 (abfd, contents + irel->r_offset); + irel->r_offset -= 1; + bfd_put_32 (abfd, disp, contents + irel->r_offset); + } + else + { + /* Convert to "nop call foo". ADDR_PREFIX_OPCODE + is a nop prefix. */ + modrm = 0xe8; + nop = ADDR_PREFIX_OPCODE; + nop_offset = irel->r_offset - 2; + } + bfd_put_8 (abfd, nop, contents + nop_offset); + bfd_put_8 (abfd, modrm, contents + irel->r_offset - 1); + r_type = R_X86_64_PC32; + } + else + { + if (opcode == 0x8b) + { + /* Convert "mov foo@GOTPCREL(%rip), %reg" to + "lea foo(%rip), %reg". */ + opcode = 0x8d; + r_type = R_X86_64_PC32; + } + else + { + modrm = bfd_get_8 (abfd, contents + roff - 1); + if (opcode == 0x85) + { + /* Convert "test %reg, foo@GOTPCREL(%rip)" to + "test $foo, %reg". */ + modrm = 0xc0 | (modrm & 0x38) >> 3; + opcode = 0xf7; + } + else + { + /* Convert "binop foo@GOTPCREL(%rip), %reg" to + "binop $foo, %reg". */ + modrm = 0xc0 | (modrm & 0x38) >> 3 | (opcode & 0x3c); + opcode = 0x81; + } + bfd_put_8 (abfd, modrm, contents + roff - 1); + + if (r_type == R_X86_64_REX_GOTPCRELX) + { + /* Move the R bit to the B bit in REX byte. */ + unsigned int rex = bfd_get_8 (abfd, contents + roff - 3); + rex = (rex & ~REX_R) | (rex & REX_R) >> 2; + bfd_put_8 (abfd, rex, contents + roff - 3); + } + /* No addend for R_X86_64_32S relocation. */ + irel->r_addend = 0; + r_type = R_X86_64_32S; + } + + bfd_put_8 (abfd, opcode, contents + roff - 2); + } + + irel->r_info = htab->r_info (r_symndx, r_type); changed_contents = TRUE; changed_relocs = TRUE; @@ -3264,7 +3391,7 @@ elf_x86_64_size_dynamic_sections (bfd *output_bfd, { struct elf_dyn_relocs *p; - if (!elf_x86_64_convert_mov_to_lea (ibfd, s, info)) + if (!elf_x86_64_convert_load (ibfd, s, info)) return FALSE; for (p = (struct elf_dyn_relocs *) @@ -3985,6 +4112,8 @@ elf_x86_64_relocate_section (bfd *output_bfd, goto do_relocation; case R_X86_64_GOTPCREL: + case R_X86_64_GOTPCRELX: + case R_X86_64_REX_GOTPCRELX: case R_X86_64_GOTPCREL64: base_got = htab->elf.sgot; off = h->got.offset; @@ -4053,6 +4182,8 @@ elf_x86_64_relocate_section (bfd *output_bfd, /* Relocation is to the entry for this symbol in the global offset table. */ case R_X86_64_GOTPCREL: + case R_X86_64_GOTPCRELX: + case R_X86_64_REX_GOTPCRELX: case R_X86_64_GOTPCREL64: /* Use global offset table entry as symbol value. */ case R_X86_64_GOTPLT64: @@ -4158,7 +4289,10 @@ elf_x86_64_relocate_section (bfd *output_bfd, relocation = base_got->output_section->vma + base_got->output_offset + off; - if (r_type != R_X86_64_GOTPCREL && r_type != R_X86_64_GOTPCREL64) + if (r_type != R_X86_64_GOTPCREL + && r_type != R_X86_64_GOTPCRELX + && r_type != R_X86_64_REX_GOTPCRELX + && r_type != R_X86_64_GOTPCREL64) relocation -= htab->elf.sgotplt->output_section->vma - htab->elf.sgotplt->output_offset; |