aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorH.J. Lu <hjl.tools@gmail.com>2015-08-30 05:03:16 -0700
committerH.J. Lu <hjl.tools@gmail.com>2015-09-02 05:14:26 -0700
commit9c8f5f4baf659f60a3604fd0bacc08b2f6fd1d3b (patch)
tree9acd5d859362cee649580e08629a51ef2f5d1db5
parentc74be520ba8ed2d013d43916b923b837294343cc (diff)
downloadfsf-binutils-gdb-9c8f5f4baf659f60a3604fd0bacc08b2f6fd1d3b.zip
fsf-binutils-gdb-9c8f5f4baf659f60a3604fd0bacc08b2f6fd1d3b.tar.gz
fsf-binutils-gdb-9c8f5f4baf659f60a3604fd0bacc08b2f6fd1d3b.tar.bz2
Add R_X86_64_INDBR_GOTPCREL
Change relocation in "call/jmp *foo@GOTPCREL(%rip)" from R_X86_64_GOTPCREL to R_X86_64_INDBR_GOTPCREL.
-rw-r--r--bfd/bfd-in2.h1
-rw-r--r--bfd/elf64-x86-64.c126
-rw-r--r--bfd/libbfd.h1
-rw-r--r--bfd/reloc.c2
-rw-r--r--gas/config/tc-i386.c20
-rw-r--r--gas/testsuite/gas/i386/i386.exp2
-rw-r--r--gas/testsuite/gas/i386/ilp32/x86-64-gotpcrel.d27
-rw-r--r--gas/testsuite/gas/i386/x86-64-gotpcrel.d25
-rw-r--r--gas/testsuite/gas/i386/x86-64-gotpcrel.s23
-rw-r--r--include/elf/x86-64.h2
-rw-r--r--ld/testsuite/ld-x86-64/gotpcrel1.dd17
-rw-r--r--ld/testsuite/ld-x86-64/gotpcrel1.out8
-rw-r--r--ld/testsuite/ld-x86-64/gotpcrel1a.S18
-rw-r--r--ld/testsuite/ld-x86-64/gotpcrel1b.c7
-rw-r--r--ld/testsuite/ld-x86-64/gotpcrel1c.c7
-rw-r--r--ld/testsuite/ld-x86-64/gotpcrel1d.S26
-rw-r--r--ld/testsuite/ld-x86-64/x86-64.exp24
17 files changed, 308 insertions, 28 deletions
diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index 51fa54f..93948c4 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -3206,6 +3206,7 @@ instruction. */
BFD_RELOC_X86_64_IRELATIVE,
BFD_RELOC_X86_64_PC32_BND,
BFD_RELOC_X86_64_PLT32_BND,
+ BFD_RELOC_X86_64_INDBR_GOTPCREL,
/* ns32k relocations */
BFD_RELOC_NS32K_IMM_8,
diff --git a/bfd/elf64-x86-64.c b/bfd/elf64-x86-64.c
index f753c7a..a492f71 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,15 @@ 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_INDBR_GOTPCREL, 0, 2, 32, TRUE, 0, complain_overflow_signed,
+ bfd_elf_generic_reloc, "R_X86_64_INDBR_GOTPCREL", 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_INDBR_GOTPCREL + 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 +257,9 @@ 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_INDBR_GOTPCREL, R_X86_64_INDBR_GOTPCREL, },
{ BFD_RELOC_VTABLE_INHERIT, R_X86_64_GNU_VTINHERIT, },
{ BFD_RELOC_VTABLE_ENTRY, R_X86_64_GNU_VTENTRY, },
};
@@ -1545,7 +1550,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_mov_and_branch 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 +1731,7 @@ 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_INDBR_GOTPCREL:
case R_X86_64_GOTPCREL64:
if (htab->elf.dynobj == NULL)
htab->elf.dynobj = abfd;
@@ -1779,6 +1785,7 @@ 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_INDBR_GOTPCREL:
case R_X86_64_TLSGD:
case R_X86_64_GOT64:
case R_X86_64_GOTPCREL64:
@@ -2144,9 +2151,10 @@ do_size:
return FALSE;
}
- if (r_type == R_X86_64_GOTPCREL
+ if ((r_type == R_X86_64_GOTPCREL
+ || r_type == R_X86_64_INDBR_GOTPCREL)
&& (h == NULL || h->type != STT_GNU_IFUNC))
- sec->need_convert_mov_to_lea = 1;
+ sec->need_convert_mov_and_branch = 1;
}
return TRUE;
@@ -2950,11 +2958,15 @@ elf_x86_64_readonly_dynrelocs (struct elf_link_hash_entry *h,
mov foo@GOTPCREL(%rip), %reg
to
lea foo(%rip), %reg
- with the local symbol, foo. */
+ with the local symbol, foo, and convert
+ call/jmp *foo@GOTPCREL(%rip)
+ to
+ nop call foo/jmp foo nop
+ with the locally defined function, foo. */
static bfd_boolean
-elf_x86_64_convert_mov_to_lea (bfd *abfd, asection *sec,
- struct bfd_link_info *link_info)
+elf_x86_64_convert_mov_and_branch (bfd *abfd, asection *sec,
+ struct bfd_link_info *link_info)
{
Elf_Internal_Shdr *symtab_hdr;
Elf_Internal_Rela *internal_relocs;
@@ -2972,7 +2984,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_mov_and_branch == 0
|| bfd_is_abs_section (sec->output_section))
return TRUE;
@@ -3011,11 +3023,13 @@ elf_x86_64_convert_mov_to_lea (bfd *abfd, asection *sec,
char symtype;
bfd_vma toff, roff;
enum {
- none, local, global
- } convert_mov_to_lea;
+ none, mov_local, mov_global, relax
+ } convert_kind;
unsigned int opcode;
- if (r_type != R_X86_64_GOTPCREL)
+ if (r_type != R_X86_64_GOTPCREL
+ && (r_symndx < symtab_hdr->sh_info
+ || r_type != R_X86_64_INDBR_GOTPCREL))
continue;
roff = irel->r_offset;
@@ -3026,18 +3040,23 @@ elf_x86_64_convert_mov_to_lea (bfd *abfd, asection *sec,
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. */
- if (opcode != 0x8b)
+ isn't for mov instruction. Also support call/jmp instruction
+ with R_X86_64_INDBR_GOTPCREL relocation. */
+ if (!(opcode == 0x8b && r_type == R_X86_64_GOTPCREL)
+ && !(opcode == 0xff && r_type == R_X86_64_INDBR_GOTPCREL))
continue;
tsec = NULL;
- convert_mov_to_lea = none;
+ convert_kind = none;
/* Get the symbol referred to by the reloc. */
if (r_symndx < symtab_hdr->sh_info)
{
Elf_Internal_Sym *isym;
+ if (r_type != R_X86_64_GOTPCREL)
+ abort ();
+
/* Silence older GCC warning. */
h = NULL;
@@ -3060,7 +3079,7 @@ elf_x86_64_convert_mov_to_lea (bfd *abfd, asection *sec,
tsec = bfd_section_from_elf_index (abfd, isym->st_shndx);
toff = isym->st_value;
- convert_mov_to_lea = local;
+ convert_kind = mov_local;
}
}
else
@@ -3073,22 +3092,24 @@ 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 R_X86_64_GOTPCREL relocation againt
+ _DYNAMIC since ld.so may use its link-time address. */
if (h->def_regular
&& h->type != STT_GNU_IFUNC
- && h != htab->elf.hdynamic
+ && (r_type == R_X86_64_INDBR_GOTPCREL
+ || h != htab->elf.hdynamic)
&& SYMBOL_REFERENCES_LOCAL (link_info, h))
{
tsec = h->root.u.def.section;
toff = h->root.u.def.value;
symtype = h->type;
- convert_mov_to_lea = global;
+ convert_kind
+ = r_type == R_X86_64_GOTPCREL ? mov_global : relax;
}
}
- if (convert_mov_to_lea == none)
+ if (convert_kind == none)
continue;
if (tsec->sec_info_type == SEC_INFO_TYPE_MERGE)
@@ -3163,12 +3184,59 @@ elf_x86_64_convert_mov_to_lea (bfd *abfd, asection *sec,
continue;
}
- bfd_put_8 (abfd, 0x8d, contents + roff - 2);
+ if (convert_kind == relax)
+ {
+ /* We have "call/jmp *foo@GOTPCREL(%rip)". */
+ unsigned int modrm;
+ unsigned int nop;
+ bfd_vma nop_offset;
+
+ /* Convert R_X86_64_INDBR_GOTPCREL to R_X86_64_PC32. */
+ modrm = bfd_get_8 (abfd, contents + irel->r_offset - 1);
+ switch (modrm)
+ {
+ default:
+ /* Skip "lcall/ljmp *foo@GOTPCREL(%rip)". */
+ continue;
+ case 0x15:
+ modrm = 0xe8;
+ break;
+ case 0x25:
+ modrm = 0xe9;
+ break;
+ }
+ if (modrm == 0xe9)
+ {
+ unsigned int disp;
+ /* Convert to "jmp foo nop". */
+ 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. */
+ 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);
+ }
+ else
+ {
+ /* Convert "mov foo@GOTPCREL(%rip), %reg" to
+ "lea foo(%rip), %reg". */
+ bfd_put_8 (abfd, 0x8d, contents + roff - 2);
+ }
+
irel->r_info = htab->r_info (r_symndx, R_X86_64_PC32);
changed_contents = TRUE;
changed_relocs = TRUE;
- if (convert_mov_to_lea == local)
+ if (convert_kind == mov_local)
{
if (local_got_refcounts != NULL
&& local_got_refcounts[r_symndx] > 0)
@@ -3267,7 +3335,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_mov_and_branch (ibfd, s, info))
return FALSE;
for (p = (struct elf_dyn_relocs *)
@@ -3988,6 +4056,7 @@ elf_x86_64_relocate_section (bfd *output_bfd,
goto do_relocation;
case R_X86_64_GOTPCREL:
+ case R_X86_64_INDBR_GOTPCREL:
case R_X86_64_GOTPCREL64:
base_got = htab->elf.sgot;
off = h->got.offset;
@@ -4056,6 +4125,7 @@ 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_INDBR_GOTPCREL:
case R_X86_64_GOTPCREL64:
/* Use global offset table entry as symbol value. */
case R_X86_64_GOTPLT64:
@@ -4161,7 +4231,9 @@ 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_INDBR_GOTPCREL
+ && r_type != R_X86_64_GOTPCREL64)
relocation -= htab->elf.sgotplt->output_section->vma
- htab->elf.sgotplt->output_offset;
diff --git a/bfd/libbfd.h b/bfd/libbfd.h
index fc70e29..ee3b33d 100644
--- a/bfd/libbfd.h
+++ b/bfd/libbfd.h
@@ -1321,6 +1321,7 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@",
"BFD_RELOC_X86_64_IRELATIVE",
"BFD_RELOC_X86_64_PC32_BND",
"BFD_RELOC_X86_64_PLT32_BND",
+ "BFD_RELOC_X86_64_INDBR_GOTPCREL",
"BFD_RELOC_NS32K_IMM_8",
"BFD_RELOC_NS32K_IMM_16",
"BFD_RELOC_NS32K_IMM_32",
diff --git a/bfd/reloc.c b/bfd/reloc.c
index caa6fb4..93ad3a5 100644
--- a/bfd/reloc.c
+++ b/bfd/reloc.c
@@ -2737,6 +2737,8 @@ ENUMX
BFD_RELOC_X86_64_PC32_BND
ENUMX
BFD_RELOC_X86_64_PLT32_BND
+ENUMX
+ BFD_RELOC_X86_64_INDBR_GOTPCREL
ENUMDOC
x86-64/elf relocations
diff --git a/gas/config/tc-i386.c b/gas/config/tc-i386.c
index 13f1d27..72a4eb3 100644
--- a/gas/config/tc-i386.c
+++ b/gas/config/tc-i386.c
@@ -2947,6 +2947,7 @@ tc_i386_fix_adjustable (fixS *fixP ATTRIBUTE_UNUSED)
|| fixP->fx_r_type == BFD_RELOC_X86_64_PLT32
|| fixP->fx_r_type == BFD_RELOC_X86_64_GOT32
|| fixP->fx_r_type == BFD_RELOC_X86_64_GOTPCREL
+ || fixP->fx_r_type == BFD_RELOC_X86_64_INDBR_GOTPCREL
|| fixP->fx_r_type == BFD_RELOC_X86_64_TLSGD
|| fixP->fx_r_type == BFD_RELOC_X86_64_TLSLD
|| fixP->fx_r_type == BFD_RELOC_X86_64_DTPOFF32
@@ -8224,7 +8225,14 @@ i386_finalize_displacement (segT exp_seg ATTRIBUTE_UNUSED, expressionS *exp,
exp->X_op = O_subtract;
exp->X_op_symbol = GOT_symbol;
if (i.reloc[this_operand] == BFD_RELOC_X86_64_GOTPCREL)
- i.reloc[this_operand] = BFD_RELOC_32_PCREL;
+ {
+ if (i.operands == 1
+ && i.types[this_operand].bitfield.jumpabsolute)
+ /* Borrow the unused BFD_RELOC_X86_64_PC32_BND. */
+ i.reloc[this_operand] = BFD_RELOC_X86_64_PC32_BND;
+ else
+ i.reloc[this_operand] = BFD_RELOC_32_PCREL;
+ }
else if (i.reloc[this_operand] == BFD_RELOC_X86_64_GOTOFF64)
i.reloc[this_operand] = BFD_RELOC_64;
else
@@ -10335,6 +10343,14 @@ i386_validate_fix (fixS *fixp)
abort ();
fixp->fx_r_type = BFD_RELOC_X86_64_GOTPCREL;
}
+ else if (fixp->fx_r_type == BFD_RELOC_X86_64_PC32_BND)
+ {
+ /* Covert the borrowed BFD_RELOC_X86_64_PC32_BND back to
+ BFD_RELOC_X86_64_INDBR_GOTPCREL. */
+ if (!object_64bit)
+ abort ();
+ fixp->fx_r_type = BFD_RELOC_X86_64_INDBR_GOTPCREL;
+ }
else
{
if (!object_64bit)
@@ -10377,6 +10393,7 @@ tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
case BFD_RELOC_X86_64_PLT32:
case BFD_RELOC_X86_64_GOT32:
case BFD_RELOC_X86_64_GOTPCREL:
+ case BFD_RELOC_X86_64_INDBR_GOTPCREL:
case BFD_RELOC_386_PLT32:
case BFD_RELOC_386_GOT32:
case BFD_RELOC_386_GOTOFF:
@@ -10530,6 +10547,7 @@ tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
case BFD_RELOC_X86_64_PLT32:
case BFD_RELOC_X86_64_GOT32:
case BFD_RELOC_X86_64_GOTPCREL:
+ case BFD_RELOC_X86_64_INDBR_GOTPCREL:
case BFD_RELOC_X86_64_TLSGD:
case BFD_RELOC_X86_64_TLSLD:
case BFD_RELOC_X86_64_GOTTPOFF:
diff --git a/gas/testsuite/gas/i386/i386.exp b/gas/testsuite/gas/i386/i386.exp
index f59b598..4277b84 100644
--- a/gas/testsuite/gas/i386/i386.exp
+++ b/gas/testsuite/gas/i386/i386.exp
@@ -778,6 +778,8 @@ if [expr ([istarget "i*86-*-*"] || [istarget "x86_64-*-*"]) && [gas_64_check]] t
run_dump_test "x86-64-jump"
run_dump_test "x86-64-branch-2"
run_list_test "x86-64-branch-3" "-al -mintel64"
+
+ run_dump_test "x86-64-gotpcrel"
}
set ASFLAGS "$old_ASFLAGS"
diff --git a/gas/testsuite/gas/i386/ilp32/x86-64-gotpcrel.d b/gas/testsuite/gas/i386/ilp32/x86-64-gotpcrel.d
new file mode 100644
index 0000000..425a60b
--- /dev/null
+++ b/gas/testsuite/gas/i386/ilp32/x86-64-gotpcrel.d
@@ -0,0 +1,27 @@
+#source: ../x86-64-gotpcrel.s
+#objdump: -dwr
+#name: x86-64 (ILP32) gotpcrel
+
+.*: +file format .*
+
+
+Disassembly of section .text:
+
+0+ <_start>:
+[ ]*[a-f0-9]+: 48 c7 c0 00 00 00 00 mov \$0x0,%rax 3: R_X86_64_GOTPCREL foo
+[ ]*[a-f0-9]+: 48 8b 04 25 00 00 00 00 mov 0x0,%rax b: R_X86_64_GOTPCREL foo
+[ ]*[a-f0-9]+: 48 8b 05 00 00 00 00 mov 0x0\(%rip\),%rax # 16 <_start\+0x16> 12: R_X86_64_GOTPCREL foo-0x4
+[ ]*[a-f0-9]+: 48 8b 81 00 00 00 00 mov 0x0\(%rcx\),%rax 19: R_X86_64_GOTPCREL foo
+[ ]*[a-f0-9]+: ff 15 00 00 00 00 callq \*0x0\(%rip\) # 23 <_start\+0x23> 1f: R_X86_64_INDBR_GOTPCREL foo-0x4
+[ ]*[a-f0-9]+: ff 90 00 00 00 00 callq \*0x0\(%rax\) 25: R_X86_64_INDBR_GOTPCREL foo
+[ ]*[a-f0-9]+: ff 25 00 00 00 00 jmpq \*0x0\(%rip\) # 2f <_start\+0x2f> 2b: R_X86_64_INDBR_GOTPCREL foo-0x4
+[ ]*[a-f0-9]+: ff a1 00 00 00 00 jmpq \*0x0\(%rcx\) 31: R_X86_64_INDBR_GOTPCREL foo
+[ ]*[a-f0-9]+: 48 c7 c0 00 00 00 00 mov \$0x0,%rax 38: R_X86_64_GOTPCREL foo
+[ ]*[a-f0-9]+: 48 8b 04 25 00 00 00 00 mov 0x0,%rax 40: R_X86_64_GOTPCREL foo
+[ ]*[a-f0-9]+: 48 8b 05 00 00 00 00 mov 0x0\(%rip\),%rax # 4b <_start\+0x4b> 47: R_X86_64_GOTPCREL foo-0x4
+[ ]*[a-f0-9]+: 48 8b 81 00 00 00 00 mov 0x0\(%rcx\),%rax 4e: R_X86_64_GOTPCREL foo
+[ ]*[a-f0-9]+: ff 15 00 00 00 00 callq \*0x0\(%rip\) # 58 <_start\+0x58> 54: R_X86_64_INDBR_GOTPCREL foo-0x4
+[ ]*[a-f0-9]+: ff 90 00 00 00 00 callq \*0x0\(%rax\) 5a: R_X86_64_INDBR_GOTPCREL foo
+[ ]*[a-f0-9]+: ff 25 00 00 00 00 jmpq \*0x0\(%rip\) # 64 <_start\+0x64> 60: R_X86_64_INDBR_GOTPCREL foo-0x4
+[ ]*[a-f0-9]+: ff a1 00 00 00 00 jmpq \*0x0\(%rcx\) 66: R_X86_64_INDBR_GOTPCREL foo
+#pass
diff --git a/gas/testsuite/gas/i386/x86-64-gotpcrel.d b/gas/testsuite/gas/i386/x86-64-gotpcrel.d
new file mode 100644
index 0000000..e6d3072
--- /dev/null
+++ b/gas/testsuite/gas/i386/x86-64-gotpcrel.d
@@ -0,0 +1,25 @@
+#objdump: -dwr
+
+.*: +file format .*
+
+
+Disassembly of section .text:
+
+0+ <_start>:
+[ ]*[a-f0-9]+: 48 c7 c0 00 00 00 00 mov \$0x0,%rax 3: R_X86_64_GOTPCREL foo
+[ ]*[a-f0-9]+: 48 8b 04 25 00 00 00 00 mov 0x0,%rax b: R_X86_64_GOTPCREL foo
+[ ]*[a-f0-9]+: 48 8b 05 00 00 00 00 mov 0x0\(%rip\),%rax # 16 <_start\+0x16> 12: R_X86_64_GOTPCREL foo-0x4
+[ ]*[a-f0-9]+: 48 8b 81 00 00 00 00 mov 0x0\(%rcx\),%rax 19: R_X86_64_GOTPCREL foo
+[ ]*[a-f0-9]+: ff 15 00 00 00 00 callq \*0x0\(%rip\) # 23 <_start\+0x23> 1f: R_X86_64_INDBR_GOTPCREL foo-0x4
+[ ]*[a-f0-9]+: ff 90 00 00 00 00 callq \*0x0\(%rax\) 25: R_X86_64_INDBR_GOTPCREL foo
+[ ]*[a-f0-9]+: ff 25 00 00 00 00 jmpq \*0x0\(%rip\) # 2f <_start\+0x2f> 2b: R_X86_64_INDBR_GOTPCREL foo-0x4
+[ ]*[a-f0-9]+: ff a1 00 00 00 00 jmpq \*0x0\(%rcx\) 31: R_X86_64_INDBR_GOTPCREL foo
+[ ]*[a-f0-9]+: 48 c7 c0 00 00 00 00 mov \$0x0,%rax 38: R_X86_64_GOTPCREL foo
+[ ]*[a-f0-9]+: 48 8b 04 25 00 00 00 00 mov 0x0,%rax 40: R_X86_64_GOTPCREL foo
+[ ]*[a-f0-9]+: 48 8b 05 00 00 00 00 mov 0x0\(%rip\),%rax # 4b <_start\+0x4b> 47: R_X86_64_GOTPCREL foo-0x4
+[ ]*[a-f0-9]+: 48 8b 81 00 00 00 00 mov 0x0\(%rcx\),%rax 4e: R_X86_64_GOTPCREL foo
+[ ]*[a-f0-9]+: ff 15 00 00 00 00 callq \*0x0\(%rip\) # 58 <_start\+0x58> 54: R_X86_64_INDBR_GOTPCREL foo-0x4
+[ ]*[a-f0-9]+: ff 90 00 00 00 00 callq \*0x0\(%rax\) 5a: R_X86_64_INDBR_GOTPCREL foo
+[ ]*[a-f0-9]+: ff 25 00 00 00 00 jmpq \*0x0\(%rip\) # 64 <_start\+0x64> 60: R_X86_64_INDBR_GOTPCREL foo-0x4
+[ ]*[a-f0-9]+: ff a1 00 00 00 00 jmpq \*0x0\(%rcx\) 66: R_X86_64_INDBR_GOTPCREL foo
+#pass
diff --git a/gas/testsuite/gas/i386/x86-64-gotpcrel.s b/gas/testsuite/gas/i386/x86-64-gotpcrel.s
new file mode 100644
index 0000000..981b14f
--- /dev/null
+++ b/gas/testsuite/gas/i386/x86-64-gotpcrel.s
@@ -0,0 +1,23 @@
+ .text
+_start:
+ movq $foo@GOTPCREL, %rax
+ movq foo@GOTPCREL, %rax
+ movq foo@GOTPCREL(%rip), %rax
+ movq foo@GOTPCREL(%rcx), %rax
+
+ call *foo@GOTPCREL(%rip)
+ call *foo@GOTPCREL(%rax)
+ jmp *foo@GOTPCREL(%rip)
+ jmp *foo@GOTPCREL(%rcx)
+
+ .intel_syntax noprefix
+
+ mov rax, offset foo@gotpcrel
+ mov rax, QWORD PTR [foo@GOTPCREL]
+ mov rax, QWORD PTR [rip + foo@GOTPCREL]
+ mov rax, QWORD PTR [rcx + foo@GOTPCREL]
+
+ call QWORD PTR [rip + foo@GOTPCREL]
+ call QWORD PTR [rax + foo@GOTPCREL]
+ jmp QWORD PTR [rip + foo@GOTPCREL]
+ jmp QWORD PTR [rcx + foo@GOTPCREL]
diff --git a/include/elf/x86-64.h b/include/elf/x86-64.h
index f18ec66..9246bae 100644
--- a/include/elf/x86-64.h
+++ b/include/elf/x86-64.h
@@ -76,6 +76,8 @@ START_RELOC_NUMBERS (elf_x86_64_reloc_type)
signed with BND prefix */
RELOC_NUMBER (R_X86_64_PLT32_BND, 40) /* 32 bit PLT address with
BND prefix */
+ /* Indirect branch via 32 bit signed pc relative offset to GOT entry */
+ RELOC_NUMBER (R_X86_64_INDBR_GOTPCREL, 41)
RELOC_NUMBER (R_X86_64_GNU_VTINHERIT, 250) /* GNU C++ hack */
RELOC_NUMBER (R_X86_64_GNU_VTENTRY, 251) /* GNU C++ hack */
END_RELOC_NUMBERS (R_X86_64_max)
diff --git a/ld/testsuite/ld-x86-64/gotpcrel1.dd b/ld/testsuite/ld-x86-64/gotpcrel1.dd
new file mode 100644
index 0000000..187a1a5
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/gotpcrel1.dd
@@ -0,0 +1,17 @@
+#...
+[a-f0-9]+ <main>:
+[ ]*[a-f0-9]+: 48 83 ec 08 sub \$0x8,%rsp
+[ ]*[a-f0-9]+: [ a-f0-9]+ addr32 callq [a-f0-9]+ <foo>
+[ ]*[a-f0-9]+: [ a-f0-9]+ callq \*0x[a-f0-9]+\(%rip\) # [a-f0-9]+ <_DYNAMIC\+0x[a-f0-9]+>
+[ ]*[a-f0-9]+: [ a-f0-9]+ callq \*0x[a-f0-9]+\(%rip\) # [a-f0-9]+ <_DYNAMIC\+0x[a-f0-9]+>
+[ ]*[a-f0-9]+: [ a-f0-9]+ lea 0x[a-f0-9]+\(%rip\),%rax # [a-f0-9]+ <foo>
+[ ]*[a-f0-9]+: ff d0 callq \*%rax
+[ ]*[a-f0-9]+: [ a-f0-9]+ mov 0x[a-f0-9]+\(%rip\),%rcx # [a-f0-9]+ <_DYNAMIC\+0x[a-f0-9]+>
+[ ]*[a-f0-9]+: ff d1 callq \*%rcx
+[ ]*[a-f0-9]+: [ a-f0-9]+ mov 0x[a-f0-9]+\(%rip\),%rdx # [a-f0-9]+ <_DYNAMIC\+0x[a-f0-9]+>
+[ ]*[a-f0-9]+: ff d2 callq \*%rdx
+[ ]*[a-f0-9]+: 31 ff xor %edi,%edi
+[ ]*[a-f0-9]+: 48 83 c4 08 add \$0x8,%rsp
+[ ]*[a-f0-9]+: [ a-f0-9]+ jmpq [a-f0-9]+ <myexit>
+[ ]*[a-f0-9]+: 90 nop
+#pass
diff --git a/ld/testsuite/ld-x86-64/gotpcrel1.out b/ld/testsuite/ld-x86-64/gotpcrel1.out
new file mode 100644
index 0000000..4d35632
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/gotpcrel1.out
@@ -0,0 +1,8 @@
+foo
+bar
+plt
+plt
+foo
+bar
+plt
+plt
diff --git a/ld/testsuite/ld-x86-64/gotpcrel1a.S b/ld/testsuite/ld-x86-64/gotpcrel1a.S
new file mode 100644
index 0000000..58dfbb1
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/gotpcrel1a.S
@@ -0,0 +1,18 @@
+ .text
+ .globl main
+ .type main, @function
+main:
+ subq $8, %rsp
+ call *foo@GOTPCREL(%rip)
+ call *bar@GOTPCREL(%rip)
+ call *plt@GOTPCREL(%rip)
+ movq foo@GOTPCREL(%rip), %rax
+ call *%rax
+ movq bar@GOTPCREL(%rip), %rcx
+ call *%rcx
+ movq plt@GOTPCREL(%rip), %rdx
+ call *%rdx
+ xorl %edi, %edi
+ addq $8, %rsp
+ jmp *myexit@GOTPCREL(%rip)
+ .size main, .-main
diff --git a/ld/testsuite/ld-x86-64/gotpcrel1b.c b/ld/testsuite/ld-x86-64/gotpcrel1b.c
new file mode 100644
index 0000000..cf0c78e
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/gotpcrel1b.c
@@ -0,0 +1,7 @@
+#include <stdio.h>
+
+void
+foo (void)
+{
+ printf ("%s\n", __FUNCTION__);
+}
diff --git a/ld/testsuite/ld-x86-64/gotpcrel1c.c b/ld/testsuite/ld-x86-64/gotpcrel1c.c
new file mode 100644
index 0000000..05f5fc2
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/gotpcrel1c.c
@@ -0,0 +1,7 @@
+#include <stdlib.h>
+
+void
+myexit (int status)
+{
+ exit (status);
+}
diff --git a/ld/testsuite/ld-x86-64/gotpcrel1d.S b/ld/testsuite/ld-x86-64/gotpcrel1d.S
new file mode 100644
index 0000000..4b01499
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/gotpcrel1d.S
@@ -0,0 +1,26 @@
+ .text
+ .globl bar
+ .type bar, @function
+bar:
+ leaq __FUNCTION__.2215(%rip), %rdi
+ jmp *puts@GOTPCREL(%rip)
+ .size bar, .-bar
+ .globl plt
+ .type plt, @function
+plt:
+ leaq __FUNCTION__.2219(%rip), %rdi
+ subq $8, %rsp
+ call *puts@GOTPCREL(%rip)
+ leaq __FUNCTION__.2219(%rip), %rdi
+ addq $8, %rsp
+ jmp *puts@GOTPCREL(%rip)
+ .size plt, .-plt
+ .section .rodata
+ .type __FUNCTION__.2219, @object
+ .size __FUNCTION__.2219, 4
+__FUNCTION__.2219:
+ .string "plt"
+ .type __FUNCTION__.2215, @object
+ .size __FUNCTION__.2215, 4
+__FUNCTION__.2215:
+ .string "bar"
diff --git a/ld/testsuite/ld-x86-64/x86-64.exp b/ld/testsuite/ld-x86-64/x86-64.exp
index 6320999..d8521d2 100644
--- a/ld/testsuite/ld-x86-64/x86-64.exp
+++ b/ld/testsuite/ld-x86-64/x86-64.exp
@@ -500,6 +500,22 @@ if { [isnative] && [which $CC] != 0 } {
{{readelf {-Wrd} pr18900b.rd}} \
"pr18900b" \
] \
+ [list \
+ "Build gotpcrel1d.so" \
+ "-shared" \
+ "" \
+ { gotpcrel1d.S } \
+ "" \
+ "gotpcrel1d.so" \
+ ] \
+ [list \
+ "Build gotpcrel1" \
+ "tmpdir/gotpcrel1d.so" \
+ "" \
+ { gotpcrel1a.S gotpcrel1b.c gotpcrel1c.c } \
+ {{objdump {-dw} gotpcrel1.dd}} \
+ "gotpcrel1" \
+ ] \
]
run_ld_link_exec_tests [] [list \
@@ -554,6 +570,14 @@ if { [isnative] && [which $CC] != 0 } {
"pr18900" \
"pr18900.out" \
] \
+ [list \
+ "Run gotpcrel1" \
+ "tmpdir/gotpcrel1d.so" \
+ "" \
+ { gotpcrel1a.S gotpcrel1b.c gotpcrel1c.c } \
+ "gotpcrel1" \
+ "gotpcrel1.out" \
+ ] \
]
if { [istarget "x86_64-*-linux*"] \