summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorH.J. Lu <hjl.tools@gmail.com>2017-08-03 09:43:28 -0700
committerH.J. Lu <hjl.tools@gmail.com>2017-08-04 10:21:07 -0700
commitdc044a14027136c864d69c83598f3ba37c44fc9c (patch)
treef09cf65dd04daaf5a59c8056e09b48cb61698e1e
parentf02fd7745d003d65fd3b981618e07b874b721d79 (diff)
downloadbinutils-gdb-users/hjl/gprel/master.tar.gz
Add R_X86_64_GPRELusers/hjl/gprel/master
"%seg:foo@GPREL" is used to address symbol, foo, relative to __gp. foo is addressed by %seg + offset of foo relative to __gp. Linker sets __gp to the middle of GP section which contains definitions of symbols with GPREL relocations. Run-time should load address of __gp into segment register, %seg before accessing foo via "%seg:foo@GPREL".
-rw-r--r--bfd/elf64-x86-64.c224
-rw-r--r--gas/config/tc-i386.c42
-rw-r--r--gas/config/tc-i386.h6
-rw-r--r--gas/testsuite/gas/i386/i386.exp3
-rw-r--r--gas/testsuite/gas/i386/x86-64-gprel.d10
-rw-r--r--gas/testsuite/gas/i386/x86-64-gprel.s3
-rw-r--r--gas/testsuite/gas/i386/x86-64-inval-gprel.l34
-rw-r--r--gas/testsuite/gas/i386/x86-64-inval-gprel.s8
-rw-r--r--include/elf/x86-64.h2
-rw-r--r--ld/testsuite/ld-x86-64/gprel-1a.S15
-rw-r--r--ld/testsuite/ld-x86-64/gprel-1b.c32
-rw-r--r--ld/testsuite/ld-x86-64/gprel-2a.S14
-rw-r--r--ld/testsuite/ld-x86-64/gprel-2b.c30
-rw-r--r--ld/testsuite/ld-x86-64/gprel-3.d3
-rw-r--r--ld/testsuite/ld-x86-64/gprel-3.s4
-rw-r--r--ld/testsuite/ld-x86-64/gprel-4.d3
-rw-r--r--ld/testsuite/ld-x86-64/gprel-4.s17
-rw-r--r--ld/testsuite/ld-x86-64/gprel-5.d3
-rw-r--r--ld/testsuite/ld-x86-64/gprel-5.s19
-rw-r--r--ld/testsuite/ld-x86-64/x86-64.exp48
20 files changed, 509 insertions, 11 deletions
diff --git a/bfd/elf64-x86-64.c b/bfd/elf64-x86-64.c
index 448599eb9b3..320c6983220 100644
--- a/bfd/elf64-x86-64.c
+++ b/bfd/elf64-x86-64.c
@@ -183,12 +183,15 @@ static reloc_howto_type x86_64_elf_howto_table[] =
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),
+ HOWTO(R_X86_64_GPREL, 0, 2, 32, FALSE, 0, complain_overflow_signed,
+ bfd_elf_generic_reloc, "R_X86_64_GPREL",
+ FALSE, MINUS_ONE, MINUS_ONE, FALSE),
/* 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_REX_GOTPCRELX + 1)
+#define R_X86_64_standard (R_X86_64_GPREL + 1)
#define R_X86_64_vt_offset (R_X86_64_GNU_VTINHERIT - R_X86_64_standard)
/* GNU extension to record C++ vtable hierarchy. */
@@ -264,6 +267,7 @@ static const struct elf_reloc_map x86_64_reloc_map[] =
{ 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_GPREL32, R_X86_64_GPREL, },
{ BFD_RELOC_VTABLE_INHERIT, R_X86_64_GNU_VTINHERIT, },
{ BFD_RELOC_VTABLE_ENTRY, R_X86_64_GNU_VTENTRY, },
};
@@ -1092,6 +1096,12 @@ struct elf_x86_64_link_hash_entry
real definition and check it when allowing copy reloc in PIE. */
unsigned int needs_copy : 1;
+ /* TRUE if symbol is __gp. */
+ unsigned int is_gp : 1;
+
+ /* TRUE if symbol has GPREL relocations. */
+ unsigned int has_gprel_reloc : 1;
+
/* TRUE if symbol has GOT or PLT relocations. */
unsigned int has_got_reloc : 1;
@@ -1171,6 +1181,8 @@ struct elf_x86_64_link_hash_table
asection *plt_got;
asection *plt_got_eh_frame;
+ struct elf_link_hash_entry *gp;
+
/* Parameters describing PLT generation, lazy or non-lazy. */
struct elf_x86_64_plt_layout plt;
@@ -1261,6 +1273,8 @@ elf_x86_64_link_hash_newfunc (struct bfd_hash_entry *entry,
eh->dyn_relocs = NULL;
eh->tls_type = GOT_UNKNOWN;
eh->needs_copy = 0;
+ eh->is_gp = 0;
+ eh->has_gprel_reloc = 0;
eh->has_got_reloc = 0;
eh->has_non_got_reloc = 0;
eh->no_finish_dynamic_symbol = 0;
@@ -1423,6 +1437,7 @@ elf_x86_64_copy_indirect_symbol (struct bfd_link_info *info,
edir = (struct elf_x86_64_link_hash_entry *) dir;
eind = (struct elf_x86_64_link_hash_entry *) ind;
+ edir->has_gprel_reloc |= eind->has_gprel_reloc;
edir->has_got_reloc |= eind->has_got_reloc;
edir->has_non_got_reloc |= eind->has_non_got_reloc;
@@ -2098,6 +2113,7 @@ elf_x86_64_convert_load_reloc (bfd *abfd, asection *sec,
/* Avoid optimizing GOTPCREL relocations againt _DYNAMIC since
ld.so may use its link-time address. */
else if (h->start_stop
+ || ((struct elf_x86_64_link_hash_entry *) h)->is_gp
|| ((h->def_regular
|| h->root.type == bfd_link_hash_defined
|| h->root.type == bfd_link_hash_defweak)
@@ -2108,8 +2124,10 @@ elf_x86_64_convert_load_reloc (bfd *abfd, asection *sec,
set by an assignment in a linker script in
bfd_elf_record_link_assignment. start_stop is set
on __start_SECNAME/__stop_SECNAME which mark section
- SECNAME. */
+ SECNAME. is_gp is set on __gp symbol which will be
+ resolved to GP section and marked as hidden. */
if (h->start_stop
+ || ((struct elf_x86_64_link_hash_entry *) h)->is_gp
|| (h->def_regular
&& (h->root.type == bfd_link_hash_new
|| h->root.type == bfd_link_hash_undefined
@@ -2453,18 +2471,26 @@ elf_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info,
if (isym == NULL)
goto error_return;
- /* Check relocation against local STT_GNU_IFUNC symbol. */
- if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
+ /* Check relocation against local STT_GNU_IFUNC symbol and
+ GPREL relocation. */
+ if (r_type == R_X86_64_GPREL
+ || ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
{
h = elf_x86_64_get_local_sym_hash (htab, abfd, rel,
TRUE);
if (h == NULL)
goto error_return;
- /* Fake a STT_GNU_IFUNC symbol. */
- h->root.root.string = bfd_elf_sym_name (abfd, symtab_hdr,
- isym, NULL);
- h->type = STT_GNU_IFUNC;
+ if (r_type == R_X86_64_GPREL)
+ /* Prepare for GP section. */
+ h->root.u.def.section
+ = bfd_section_from_elf_index (abfd, isym->st_shndx);
+ else
+ /* Fake a STT_GNU_IFUNC symbol. */
+ h->root.root.string = bfd_elf_sym_name (abfd, symtab_hdr,
+ isym, NULL);
+
+ h->type = ELF_ST_TYPE (isym->st_info);
h->def_regular = 1;
h->ref_regular = 1;
h->forced_local = 1;
@@ -2891,6 +2917,11 @@ do_size:
goto error_return;
break;
+ case R_X86_64_GPREL:
+ if (eh != NULL)
+ eh->has_gprel_reloc = 1;
+ break;
+
default:
break;
}
@@ -3548,6 +3579,10 @@ elf_x86_64_allocate_local_dynrelocs (void **slot, void *inf)
struct elf_link_hash_entry *h
= (struct elf_link_hash_entry *) *slot;
+ /* Skip local symbol with GPREL relocation. */
+ if (((struct elf_x86_64_link_hash_entry *) h)->has_gprel_reloc)
+ return TRUE;
+
if (h->type != STT_GNU_IFUNC
|| !h->def_regular
|| !h->ref_regular
@@ -5716,6 +5751,41 @@ direct:
relocation -= elf_x86_64_dtpoff_base (info);
break;
+ case R_X86_64_GPREL:
+ if (h == NULL || h->def_regular)
+ {
+ asection *def_sec;
+
+ if (h != NULL)
+ def_sec = h->root.u.def.section;
+ else
+ def_sec = local_sections[r_symndx];
+
+ if (htab->gp->root.u.def.section
+ != def_sec->output_section)
+ {
+ if (h != NULL && h->root.root.string != NULL)
+ _bfd_error_handler
+ /* xgettext:c-format */
+ (_("%B: symbol `%s' with GPREL relocation "
+ "defined in %B(%A) isn't in GP section `%A'"),
+ input_bfd, h->root.root.string, def_sec->owner,
+ def_sec, htab->gp->root.u.def.section);
+ else
+ _bfd_error_handler
+ /* xgettext:c-format */
+ (_("%B: GPREL relocation at %#Lx in section "
+ "`%A' must be against symbol defined in GP "
+ "section `%A'"),
+ input_bfd, rel->r_offset, input_section,
+ htab->gp->root.u.def.section);
+ return FALSE;
+ }
+ relocation -= (htab->gp->root.u.def.section->vma
+ + htab->gp->root.u.def.value);
+ }
+ break;
+
default:
break;
}
@@ -6225,8 +6295,12 @@ elf_x86_64_finish_local_dynamic_symbol (void **slot, void *inf)
struct bfd_link_info *info
= (struct bfd_link_info *) inf;
+ /* Skip local symbol with GPREL relocation. */
+ if (((struct elf_x86_64_link_hash_entry *) h)->has_gprel_reloc)
+ return TRUE;
+
return elf_x86_64_finish_dynamic_symbol (info->output_bfd,
- info, h, NULL);
+ info, h, NULL);
}
/* Finish up undefined weak symbol handling in PIE. Fill its PLT entry
@@ -7332,6 +7406,7 @@ elf_x86_64_link_setup_gnu_properties (struct bfd_link_info *info)
bfd_boolean use_ibt_plt;
unsigned int plt_alignment, features;
struct elf_x86_64_link_hash_table *htab;
+ struct elf_link_hash_entry *gp;
bfd *pbfd;
features = 0;
@@ -7712,9 +7787,137 @@ error_alignment:
goto error_alignment;
}
+ /* Set is_gp for __gp symbol. */
+ gp = elf_link_hash_lookup (elf_hash_table (info), "__gp", FALSE,
+ FALSE, FALSE);
+ if (gp != NULL)
+ {
+ ((struct elf_x86_64_link_hash_entry *) gp)->is_gp = 1;
+ htab->gp = gp;
+ }
+
return pbfd;
}
+/* Set up GP section from symbols with GPREL relocations. */
+
+static bfd_boolean
+elf_x86_64_setup_gp (struct elf_link_hash_entry *h, void * inf)
+{
+ struct bfd_link_info *info;
+ struct elf_x86_64_link_hash_table *htab;
+ struct elf_x86_64_link_hash_entry *eh;
+ struct elf_link_hash_entry *gp;
+ const struct elf_backend_data *bed;
+ asection *gpsection;
+ bfd_size_type gpsection_size;
+
+ eh = (struct elf_x86_64_link_hash_entry *) h;
+
+ /* Skip if there is no GPREL relocation or symbol is undefined. */
+ if (!eh->has_gprel_reloc
+ || (h->root.type != bfd_link_hash_defined
+ && h->root.type != bfd_link_hash_defweak))
+ return TRUE;
+
+ info = (struct bfd_link_info *) inf;
+ htab = elf_x86_64_hash_table (info);
+ if (htab == NULL)
+ return FALSE;
+
+ gpsection = h->root.u.def.section->output_section;
+ gpsection_size = bfd_get_section_size (gpsection);
+ if (gpsection_size > 0xffffffff)
+ {
+ info->callbacks->einfo (_("%F%B: GP section `%A' size overflow\n"),
+ info->output_bfd, gpsection);
+ return FALSE;
+ }
+
+ gp = htab->gp;
+ gp->def_regular = 1;
+ gp->root.type = bfd_link_hash_defined;
+ gp->root.u.def.value = gpsection_size / 2;
+ gp->root.u.def.section = gpsection;
+ gp->root.linker_def = 1;
+ gp->other = STV_HIDDEN;
+ bed = get_elf_backend_data (info->output_bfd);
+ bed->elf_backend_hide_symbol (info, gp, TRUE);
+
+ return FALSE;
+}
+
+/* Set up GP section from local symbols with GPREL relocations. */
+
+static bfd_boolean
+elf_x86_64_setup_gp_from_local_symbol (void **slot, void *inf)
+{
+ struct elf_link_hash_entry *h
+ = (struct elf_link_hash_entry *) *slot;
+ struct bfd_link_info *info
+ = (struct bfd_link_info *) inf;
+
+ return elf_x86_64_setup_gp (h, info);
+}
+
+/* Set up GP section for __gp symbol. */
+
+static bfd_boolean
+elf_x86_64_final_link (bfd *abfd, struct bfd_link_info *info)
+{
+ if (!bfd_link_relocatable (info))
+ {
+ struct elf_link_hash_entry *gp;
+ struct elf_x86_64_link_hash_table *htab;
+
+ htab = elf_x86_64_hash_table (info);
+ if (htab == NULL)
+ return FALSE;
+
+ gp = htab->gp;
+ if (gp != NULL)
+ {
+ if (gp->root.type == bfd_link_hash_defined
+ || gp->root.type == bfd_link_hash_defweak)
+ {
+ /* Hide __gp. */
+ const struct elf_backend_data *bed
+ = get_elf_backend_data (abfd);
+ gp->other = STV_HIDDEN;
+ bed->elf_backend_hide_symbol (info, gp, TRUE);
+ }
+ else
+ {
+ /* Set up __gp from a symbol with GPREL relocations. */
+ elf_link_hash_traverse (&htab->elf,
+ elf_x86_64_setup_gp,
+ info);
+
+ if (gp->root.type != bfd_link_hash_defined
+ && gp->root.type != bfd_link_hash_defweak)
+ {
+ /* Set up __gp from a local symbol with GPREL
+ relocations. */
+ htab_traverse (htab->loc_hash_table,
+ elf_x86_64_setup_gp_from_local_symbol,
+ info);
+ }
+
+ if (gp->root.type != bfd_link_hash_defined
+ && gp->root.type != bfd_link_hash_defweak)
+ {
+ info->callbacks->einfo (_("%F%B: undefined __gp symbol\n"),
+ info->output_bfd);
+ return FALSE;
+ }
+ }
+ }
+ }
+
+ /* Invoke the regular ELF backend linker to do all the work. */
+ return bfd_elf_final_link (abfd, info);
+}
+
static const struct bfd_elf_special_section
elf_x86_64_special_sections[]=
{
@@ -7779,6 +7982,7 @@ elf_x86_64_special_sections[]=
#define elf_backend_object_p elf64_x86_64_elf_object_p
#define bfd_elf64_mkobject elf_x86_64_mkobject
#define bfd_elf64_get_synthetic_symtab elf_x86_64_get_synthetic_symtab
+#define bfd_elf64_bfd_final_link elf_x86_64_final_link
#define elf_backend_section_from_shdr \
elf_x86_64_section_from_shdr
@@ -8078,6 +8282,8 @@ elf32_x86_64_nacl_elf_object_p (bfd *abfd)
elf_x86_64_mkobject
#define bfd_elf32_get_synthetic_symtab \
elf_x86_64_get_synthetic_symtab
+#define bfd_elf32_bfd_final_link \
+ elf_x86_64_final_link
#undef elf_backend_object_p
#define elf_backend_object_p \
diff --git a/gas/config/tc-i386.c b/gas/config/tc-i386.c
index 456be9e07cf..c15742b1c12 100644
--- a/gas/config/tc-i386.c
+++ b/gas/config/tc-i386.c
@@ -672,6 +672,9 @@ static enum rc_type evexrcig = rne;
/* Pre-defined "_GLOBAL_OFFSET_TABLE_". */
static symbolS *GOT_symbol;
+/* Pre-defined "__gp". */
+static symbolS *GP_symbol;
+
/* The dwarf2 return column, adjusted for 32 or 64 bit. */
unsigned int x86_dwarf2_return_column;
@@ -7776,6 +7779,9 @@ lex_got (enum bfd_reloc_code_real *rel,
{ STRING_COMMA_LEN ("GOTPCREL"), { _dummy_first_bfd_reloc_code_real,
BFD_RELOC_X86_64_GOTPCREL },
OPERAND_TYPE_IMM32_32S_DISP32 },
+ { STRING_COMMA_LEN ("GPREL"), { _dummy_first_bfd_reloc_code_real,
+ BFD_RELOC_GPREL32 },
+ OPERAND_TYPE_IMM32_32S_DISP32 },
{ STRING_COMMA_LEN ("TLSGD"), { BFD_RELOC_386_TLS_GD,
BFD_RELOC_X86_64_TLSGD },
OPERAND_TYPE_IMM32_32S_DISP32 },
@@ -7848,8 +7854,19 @@ lex_got (enum bfd_reloc_code_real *rel,
*types = gotrel[j].types64;
}
- if (j != 0 && GOT_symbol == NULL)
- GOT_symbol = symbol_find_or_make (GLOBAL_OFFSET_TABLE_NAME);
+ if (j != 0)
+ {
+ if (gotrel[j].rel[1] == BFD_RELOC_GPREL32)
+ {
+ if (GP_symbol == NULL)
+ GP_symbol = symbol_find_or_make (GLOBAL_POINTER_NAME);
+ }
+ else
+ {
+ if (GOT_symbol == NULL)
+ GOT_symbol = symbol_find_or_make (GLOBAL_OFFSET_TABLE_NAME);
+ }
+ }
/* The length of the first part of our input line. */
first = cp - input_line_pointer;
@@ -8506,6 +8523,11 @@ i386_displacement (char *disp_start, char *disp_end)
if (gotfree_input_line)
input_line_pointer = gotfree_input_line;
+ if (i.reloc[this_operand] == BFD_RELOC_GPREL32
+ && (i.seg[0] == NULL
+ || i.types[this_operand].bitfield.baseindex))
+ as_bad (_("invalid GP relocation"));
+
exp_seg = expression (exp);
SKIP_WHITESPACE ();
@@ -10740,6 +10762,21 @@ md_undefined_symbol (char *name)
};
return GOT_symbol;
}
+ else if (name[0] == GLOBAL_POINTER_NAME[0]
+ && name[1] == GLOBAL_POINTER_NAME[1]
+ && name[2] == GLOBAL_POINTER_NAME[2]
+ && name[3] == GLOBAL_POINTER_NAME[3]
+ && strcmp (name, GLOBAL_POINTER_NAME) == 0)
+ {
+ if (!GP_symbol)
+ {
+ if (symbol_find (name))
+ as_bad (_("GP already in symbol table"));
+ GP_symbol = symbol_new (name, undefined_section,
+ (valueT) 0, &zero_address_frag);
+ };
+ return GP_symbol;
+ }
return 0;
}
@@ -10899,6 +10936,7 @@ tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
case BFD_RELOC_X86_64_PLTOFF64:
case BFD_RELOC_X86_64_GOTPC32_TLSDESC:
case BFD_RELOC_X86_64_TLSDESC_CALL:
+ case BFD_RELOC_GPREL32:
case BFD_RELOC_RVA:
case BFD_RELOC_VTABLE_ENTRY:
case BFD_RELOC_VTABLE_INHERIT:
diff --git a/gas/config/tc-i386.h b/gas/config/tc-i386.h
index f54924c4382..beaa4614ddc 100644
--- a/gas/config/tc-i386.h
+++ b/gas/config/tc-i386.h
@@ -128,6 +128,12 @@ extern const char *i386_comment_chars;
#define GLOBAL_OFFSET_TABLE_NAME "_GLOBAL_OFFSET_TABLE_"
#endif
+/* The name of the global pointer. Allow this to be overridden if need
+ be. */
+#ifndef GLOBAL_POINTER_NAME
+#define GLOBAL_POINTER_NAME "__gp"
+#endif
+
#if (defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)) && !defined (LEX_AT)
#define TC_PARSE_CONS_EXPRESSION(EXP, NBYTES) x86_cons (EXP, NBYTES)
#endif
diff --git a/gas/testsuite/gas/i386/i386.exp b/gas/testsuite/gas/i386/i386.exp
index 67a7a13b259..427a758b35a 100644
--- a/gas/testsuite/gas/i386/i386.exp
+++ b/gas/testsuite/gas/i386/i386.exp
@@ -868,6 +868,9 @@ if [expr ([istarget "i*86-*-*"] || [istarget "x86_64-*-*"]) && [gas_64_check]] t
run_dump_test "x86-64-gotpcrel-no-relax"
run_dump_test "x86-64-addend"
+
+ run_dump_test "x86-64-gprel"
+ run_list_test "x86-64-inval-gprel" "-al"
}
set ASFLAGS "$old_ASFLAGS"
diff --git a/gas/testsuite/gas/i386/x86-64-gprel.d b/gas/testsuite/gas/i386/x86-64-gprel.d
new file mode 100644
index 00000000000..f70624ab42e
--- /dev/null
+++ b/gas/testsuite/gas/i386/x86-64-gprel.d
@@ -0,0 +1,10 @@
+#objdump: -drw
+#name: x86-64 gprel
+
+.*: +file format .*
+
+Disassembly of section .text:
+
+0+ <_start>:
+ +[a-f0-9]+: 65 8b 04 25 00 00 00 00 mov %gs:0x0,%eax 4: R_X86_64_GPREL foo
+#pass
diff --git a/gas/testsuite/gas/i386/x86-64-gprel.s b/gas/testsuite/gas/i386/x86-64-gprel.s
new file mode 100644
index 00000000000..96b3ce90444
--- /dev/null
+++ b/gas/testsuite/gas/i386/x86-64-gprel.s
@@ -0,0 +1,3 @@
+ .text
+_start:
+ movl %gs:foo@GPREL, %eax
diff --git a/gas/testsuite/gas/i386/x86-64-inval-gprel.l b/gas/testsuite/gas/i386/x86-64-inval-gprel.l
new file mode 100644
index 00000000000..b6683b036d1
--- /dev/null
+++ b/gas/testsuite/gas/i386/x86-64-inval-gprel.l
@@ -0,0 +1,34 @@
+.*: Assembler messages:
+.*:3: Error: invalid GP relocation
+.*:3: Error: non-pc-relative relocation for pc-relative field
+.*:4: Error: invalid GP relocation
+.*:5: Error: invalid GP relocation
+.*:6: Error: invalid GP relocation
+.*:6: Error: non-pc-relative relocation for pc-relative field
+.*:7: Error: invalid GP relocation
+.*:8: Error: invalid GP relocation
+GAS LISTING .*
+
+
+[ ]*1[ ]+\.text
+[ ]*2[ ]+_start:
+[ ]*3[ ]+\?\?\?\? 648B0500 movl %fs:foo@GPREL\(%rip\), %eax
+\*\*\*\* Error: invalid GP relocation
+\*\*\*\* Error: non-pc-relative relocation for pc-relative field
+[ ]*3[ ]+000000
+[ ]*4[ ]+\?\?\?\? 648B8000 movl %fs:foo@GPREL\(%rax\), %eax
+\*\*\*\* Error: invalid GP relocation
+[ ]*4[ ]+000000
+[ ]*5[ ]+\?\?\?\? 8B844800 movl %ds:foo@GPREL\(%rax, %rcx, 2\), %eax
+\*\*\*\* Error: invalid GP relocation
+[ ]*5[ ]+000000
+[ ]*6[ ]+\?\?\?\? 8B050000 movl foo@GPREL\(%rip\), %eax
+\*\*\*\* Error: invalid GP relocation
+\*\*\*\* Error: non-pc-relative relocation for pc-relative field
+[ ]*6[ ]+0000
+[ ]*7[ ]+\?\?\?\? 8B800000 movl foo@GPREL\(%rax\), %eax
+\*\*\*\* Error: invalid GP relocation
+[ ]*7[ ]+0000
+[ ]*8[ ]+\?\?\?\? 8B042500 movl foo@GPREL, %eax
+\*\*\*\* Error: invalid GP relocation
+[ ]*8[ ]+000000
diff --git a/gas/testsuite/gas/i386/x86-64-inval-gprel.s b/gas/testsuite/gas/i386/x86-64-inval-gprel.s
new file mode 100644
index 00000000000..8507926860d
--- /dev/null
+++ b/gas/testsuite/gas/i386/x86-64-inval-gprel.s
@@ -0,0 +1,8 @@
+ .text
+_start:
+ movl %fs:foo@GPREL(%rip), %eax
+ movl %fs:foo@GPREL(%rax), %eax
+ movl %ds:foo@GPREL(%rax, %rcx, 2), %eax
+ movl foo@GPREL(%rip), %eax
+ movl foo@GPREL(%rax), %eax
+ movl foo@GPREL, %eax
diff --git a/include/elf/x86-64.h b/include/elf/x86-64.h
index 976f4fe347b..f9697ebd009 100644
--- a/include/elf/x86-64.h
+++ b/include/elf/x86-64.h
@@ -82,6 +82,8 @@ START_RELOC_NUMBERS (elf_x86_64_reloc_type)
/* Load from 32 bit signed pc relative offset to GOT entry with
REX prefix, relaxable. */
RELOC_NUMBER (R_X86_64_REX_GOTPCRELX, 42)
+ RELOC_NUMBER (R_X86_64_GPREL, 43) /* 32 bit signed pc relative
+ offset to GP */
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/gprel-1a.S b/ld/testsuite/ld-x86-64/gprel-1a.S
new file mode 100644
index 00000000000..db099abb6a7
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/gprel-1a.S
@@ -0,0 +1,15 @@
+ .text
+ .globl get_foo
+get_foo:
+ movl %gs:foo@GPREL, %eax
+ ret
+
+ .data
+ .globl foo_gprel
+foo_gprel:
+ .long foo@GPREL
+
+ .data
+ .globl foo
+foo:
+ .long 0x12345678
diff --git a/ld/testsuite/ld-x86-64/gprel-1b.c b/ld/testsuite/ld-x86-64/gprel-1b.c
new file mode 100644
index 00000000000..97d3b553b95
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/gprel-1b.c
@@ -0,0 +1,32 @@
+#include <stdio.h>
+#include <syscall.h>
+#include <asm/prctl.h>
+
+extern int foo;
+extern int __gp;
+extern int foo_gprel;
+extern int get_foo (void);
+
+int
+setup_gp (void *p)
+{
+ int result;
+ asm volatile ("syscall"
+ : "=a" (result)
+ : "0" ((unsigned long int) SYS_arch_prctl),
+ "D" ((unsigned long int) ARCH_SET_GS),
+ "S" (p)
+ : "memory", "cc", "r11", "cx");
+ return result;
+}
+
+int
+main ()
+{
+ setup_gp (&__gp);
+ if (foo == 0x12345678
+ && *(int *) ((char *) &__gp + foo_gprel) == 0x12345678
+ && get_foo () == 0x12345678)
+ printf ("PASS\n");
+ return 0;
+}
diff --git a/ld/testsuite/ld-x86-64/gprel-2a.S b/ld/testsuite/ld-x86-64/gprel-2a.S
new file mode 100644
index 00000000000..99165e5b6a4
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/gprel-2a.S
@@ -0,0 +1,14 @@
+ .text
+ .globl get_foo
+get_foo:
+ movl %gs:foo@GPREL, %eax
+ ret
+
+ .data
+ .globl foo_gprel
+foo_gprel:
+ .long foo@GPREL
+
+ .data
+foo:
+ .long 0x12345678
diff --git a/ld/testsuite/ld-x86-64/gprel-2b.c b/ld/testsuite/ld-x86-64/gprel-2b.c
new file mode 100644
index 00000000000..3b53fad432c
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/gprel-2b.c
@@ -0,0 +1,30 @@
+#include <stdio.h>
+#include <syscall.h>
+#include <asm/prctl.h>
+
+extern int __gp;
+extern int foo_gprel;
+extern int get_foo (void);
+
+int
+setup_gp (void *p)
+{
+ int result;
+ asm volatile ("syscall"
+ : "=a" (result)
+ : "0" ((unsigned long int) SYS_arch_prctl),
+ "D" ((unsigned long int) ARCH_SET_GS),
+ "S" (p)
+ : "memory", "cc", "r11", "cx");
+ return result;
+}
+
+int
+main ()
+{
+ setup_gp (&__gp);
+ if (*(int *) ((char *) &__gp + foo_gprel) == 0x12345678
+ && get_foo () == 0x12345678)
+ printf ("PASS\n");
+ return 0;
+}
diff --git a/ld/testsuite/ld-x86-64/gprel-3.d b/ld/testsuite/ld-x86-64/gprel-3.d
new file mode 100644
index 00000000000..c870a95ad7f
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/gprel-3.d
@@ -0,0 +1,3 @@
+#as: --64
+#ld: -melf_x86_64
+#error: undefined __gp symbol
diff --git a/ld/testsuite/ld-x86-64/gprel-3.s b/ld/testsuite/ld-x86-64/gprel-3.s
new file mode 100644
index 00000000000..ba79246fda8
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/gprel-3.s
@@ -0,0 +1,4 @@
+ .text
+ .globl _start
+_start:
+ movl %gs:foo@GPREL, %eax
diff --git a/ld/testsuite/ld-x86-64/gprel-4.d b/ld/testsuite/ld-x86-64/gprel-4.d
new file mode 100644
index 00000000000..024e06634e1
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/gprel-4.d
@@ -0,0 +1,3 @@
+#as: --64
+#ld: -melf_x86_64
+#error: GPREL relocation at 0x4 in section `\.text' must be against symbol defined in GP section `\.rodata'
diff --git a/ld/testsuite/ld-x86-64/gprel-4.s b/ld/testsuite/ld-x86-64/gprel-4.s
new file mode 100644
index 00000000000..a0a89ba0cd9
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/gprel-4.s
@@ -0,0 +1,17 @@
+ .text
+ .globl _start
+_start:
+ movl %gs:foo@GPREL, %eax
+
+ .data
+ .globl bar_gprel
+bar_gprel:
+ .long bar@GPREL
+
+foo:
+ .long 0x12345678
+
+ .section .rodata,"a",@progbits
+bar:
+ .long 0x12345678
+
diff --git a/ld/testsuite/ld-x86-64/gprel-5.d b/ld/testsuite/ld-x86-64/gprel-5.d
new file mode 100644
index 00000000000..3186c346b4e
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/gprel-5.d
@@ -0,0 +1,3 @@
+#as: --64
+#ld: -melf_x86_64
+#error: symbol `bar' with GPREL relocation defined in .*\.o\(\.rodata\) isn't in GP section `\.data'
diff --git a/ld/testsuite/ld-x86-64/gprel-5.s b/ld/testsuite/ld-x86-64/gprel-5.s
new file mode 100644
index 00000000000..a49adec8dab
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/gprel-5.s
@@ -0,0 +1,19 @@
+ .text
+ .globl _start
+_start:
+ movl %gs:foo@GPREL, %eax
+
+ .data
+ .globl bar_gprel
+bar_gprel:
+ .long bar@GPREL
+
+ .globl foo
+foo:
+ .long 0x12345678
+
+ .section .rodata,"a",@progbits
+ .globl bar
+bar:
+ .long 0x12345678
+
diff --git a/ld/testsuite/ld-x86-64/x86-64.exp b/ld/testsuite/ld-x86-64/x86-64.exp
index c5825822978..8f6f0a40384 100644
--- a/ld/testsuite/ld-x86-64/x86-64.exp
+++ b/ld/testsuite/ld-x86-64/x86-64.exp
@@ -567,6 +567,9 @@ run_dump_test "pr20253-4f"
run_dump_test "pr20253-5a"
run_dump_test "pr20253-5b"
run_dump_test "tlsdesc2"
+run_dump_test "gprel-3"
+run_dump_test "gprel-4"
+run_dump_test "gprel-5"
proc undefined_weak {cflags ldflags} {
set testname "Undefined weak symbol"
@@ -1571,6 +1574,51 @@ run_ld_link_tests [list \
] \
]
+run_ld_link_exec_tests [list \
+ [list \
+ "Run GPREL 1" \
+ "" \
+ "" \
+ {gprel-1a.S gprel-1b.c} \
+ "gprel-1" "pass.out" \
+ ] \
+ [list \
+ "Run GPREL 1 (PIE)" \
+ "-pie" \
+ "" \
+ {gprel-1a.S gprel-1b.c} \
+ "gprel-1-pie" "pass.out" "-fPIE" \
+ ] \
+ [list \
+ "Run GPREL 1 (static)" \
+ "-static" \
+ "" \
+ {gprel-1a.S gprel-1b.c} \
+ "gprel-1-static" "pass.out" \
+ ] \
+ [list \
+ "Run GPREL 2" \
+ "" \
+ "" \
+ {gprel-2a.S gprel-2b.c} \
+ "gprel-2" "pass.out" \
+ ] \
+ [list \
+ "Run GPREL 2 (PIE)" \
+ "-pie" \
+ "" \
+ {gprel-2a.S gprel-2b.c} \
+ "gprel-2-pie" "pass.out" "-fPIE" \
+ ] \
+ [list \
+ "Run GPREL 2 (static)" \
+ "-static" \
+ "" \
+ {gprel-2a.S gprel-2b.c} \
+ "gprel-2-static" "pass.out" \
+ ] \
+]
+
# Linux only tests
run_dump_test "pr17618"
run_dump_test "pltgot-1"