Commit 8a034336 authored by H.J. Lu's avatar H.J. Lu
Browse files

x86: Add R_X86_64_GPOFF/R_386_GPOFF relocation

On x86, a segment register is used as thread pointer to access thread
local storage (TLS) with __thread.  TLS is an OS-dependent, user-space
feature.

Here is a proposal to add R_X86_64_GPOFF/R_386_GPOFF relocation to
access symbol with an offset to global pointer, __gp.  The assembly
syntax is

1. Load value of foo relative to __gp, pointed by %seg, into %reg:

	op %seg:foo@GPOFF, %reg

2. Store value in %reg to foo relative to __gp, pointed by %seg:

	op %reg, %seg:foo@GPOFF

3. Compute offset of foo to __gp:

	lea foo@GPOFF, %reg
	.long foo@GPOFF

Linker sets __gp to the middle of the section which contains definitions
of symbols with GPOFF relocations and the maximum offset is [-2G, 2G).
Run-time must initialize the segment register, %seg, with the address of
global pointer, __gp.

bfd/

	* elf32-i386.c (elf_howto_table): Add R_386_GPOFF.
	(R_386_ext2): Replace R_386_GOT32X with R_386_GPOFF.
	(elf_i386_reloc_type_lookup): Support BFD_RELOC_GPREL32.
	(elf_i386_link_hash_entry): Add has_gpoff_reloc.
	(elf_i386_link_hash_table): Add gp.
	(elf_i386_link_hash_newfunc): Initialize has_gpoff_reloc to 0.
	(elf_i386_copy_indirect_symbol): Also copy has_gpoff_reloc.
	(elf_i386_check_relocs): Add a fake local symbol and set
	has_gpoff_reloc for GPOFF relocation.
	(elf_i386_allocate_local_dynrelocs): Skip local symbol with
	GPOFF relocation.
	(elf_i386_finish_local_dynamic_symbol): Likewise.
	(elf_i386_relocate_section): Process GPOFF relocation.
	(elf_i386_link_check_relocs): Cache and hide __gp symbol.
	(elf_i386_setup_gp): New function.
	(elf_i386_setup_gp_from_local_symbol): Likewise.
	(elf_i386_final_link): Likewise.
	(bfd_elf32_bfd_final_link): New.
	* elf64-x86-64.c (elf_howto_table): Add R_X86_64_GPOFF.
	(R_X86_64_standard): Replace R_X86_64_REX_GOTPCRELX with
	R_X86_64_GPOFF.
	(x86_64_reloc_map): Add BFD_RELOC_GPREL32.
	(elf_x86_64_link_hash_entry): Add has_gpoff_reloc.
	(elf_x86_64_link_hash_table): Add gp.
	(elf_x86_64_link_hash_newfunc): Initialize has_gpoff_reloc to 0.
	(elf_x86_64_copy_indirect_symbol): Also copy has_gpoff_reloc.
	(elf_x86_64_check_relocs): Add a fake local symbol and set
	has_gpoff_reloc for GPOFF relocation.
	(elf_x86_64_allocate_local_dynrelocs): Skip local symbol with
	GPOFF relocation.
	(elf_x86_64_finish_local_dynamic_symbol): Likewise.
	(elf_x86_64_relocate_section): Process GPOFF relocation.
	(elf_x86_64_link_check_relocs): Cache and hide __gp symbol.
	(elf_x86_64_setup_gp): New function.
	(elf_x86_64_setup_gp_from_local_symbol): Likewise.
	(elf_x86_64_final_link): Likewise.
	(bfd_elf64_bfd_final_link): New.
	(bfd_elf32_bfd_final_link): Likewise.

gas/

	* config/tc-i386.c (GP_symbol): New.
	(gotrel): Add "GPOFF".
	(lex_got): Support BFD_RELOC_GPREL32.
	(i386_displacement): Disallow BFD_RELOC_GPREL32 relocation
	with base or index registers.
	(md_undefined_symbol): Create GP_symbol if needed.
	(tc_gen_reloc): Handle BFD_RELOC_GPREL32.
	* config/tc-i386.h (GLOBAL_POINTER_NAME): New.
	* testsuite/gas/i386/gpoff.d: New file.
	* testsuite/gas/i386/gpoff.s: Likewise.
	* testsuite/gas/i386/inval-gpoff.l: Likewise.
	* testsuite/gas/i386/inval-gpoff.s: Likewise.
	* testsuite/gas/i386/x86-64-gpoff.d: Likewise.
	* testsuite/gas/i386/x86-64-gpoff.s: Likewise.
	* testsuite/gas/i386/x86-64-inval-gpoff.l: Likewise.
	* testsuite/gas/i386/x86-64-inval-gpoff.s: Likewise.
	* testsuite/gas/i386/i386.exp: Run gpoff, inval-gpoff,
	x86-64-gpoff and x86-64-inval-gpoff.

include/

	* elf/i386.h (R_386_GPOFF): New relocation.
	* elf/x86-64.h (R_X86_64_GPOFF): Likewise.

ld/

	* testsuite/ld-i386/gpoff-1a.S: New file.
	* testsuite/ld-i386/gpoff-1b.c: Likewise.
	* testsuite/ld-i386/gpoff-2a.S: Likewise.
	* testsuite/ld-i386/gpoff-2b.c: Likewise.
	* testsuite/ld-i386/gpoff-3.d: Likewise.
	* testsuite/ld-i386/gpoff-3.s: Likewise.
	* testsuite/ld-i386/gpoff-4.d: Likewise.
	* testsuite/ld-i386/gpoff-4.s: Likewise.
	* testsuite/ld-i386/gpoff-5.d: Likewise.
	* testsuite/ld-i386/gpoff-5.s: Likewise.
	* testsuite/ld-i386/gpoff-6.d: Likewise.
	* testsuite/ld-i386/gpoff-6.s: Likewise.
	* testsuite/ld-i386/gpoff-7.d: Likewise.
	* testsuite/ld-i386/gpoff-7.s: Likewise.
	* testsuite/ld-i386/gpoff-8.s: Likewise.
	* testsuite/ld-i386/gpoff-8.t: Likewise.
	* testsuite/ld-i386/gpoff-8a.d: Likewise.
	* testsuite/ld-i386/gpoff-8b.d: Likewise.
	* testsuite/ld-i386/gpoff-8c.d: Likewise.
	* testsuite/ld-i386/gpoff-8d.d: Likewise.
	* testsuite/ld-i386/gpoff-8e.d: Likewise.
	* testsuite/ld-i386/gpoff-8f.d: Likewise.
	* testsuite/ld-x86-64/gpoff-1a.S: Likewise.
	* testsuite/ld-x86-64/gpoff-1b.c: Likewise.
	* testsuite/ld-x86-64/gpoff-2a.S: Likewise.
	* testsuite/ld-x86-64/gpoff-2b.c: Likewise.
	* testsuite/ld-x86-64/gpoff-3.d: Likewise.
	* testsuite/ld-x86-64/gpoff-3.s: Likewise.
	* testsuite/ld-x86-64/gpoff-4.d: Likewise.
	* testsuite/ld-x86-64/gpoff-4.s: Likewise.
	* testsuite/ld-x86-64/gpoff-5.d: Likewise.
	* testsuite/ld-x86-64/gpoff-5.s: Likewise.
	* testsuite/ld-x86-64/gpoff-6.d: Likewise.
	* testsuite/ld-x86-64/gpoff-6.s: Likewise.
	* testsuite/ld-x86-64/gpoff-7.d: Likewise.
	* testsuite/ld-x86-64/gpoff-7.s: Likewise.
	* testsuite/ld-x86-64/gpoff-8.s: Likewise.
	* testsuite/ld-x86-64/gpoff-8.t: Likewise.
	* testsuite/ld-x86-64/gpoff-8a.d: Likewise.
	* testsuite/ld-x86-64/gpoff-8b.d: Likewise.
	* testsuite/ld-x86-64/gpoff-8c.d: Likewise.
	* testsuite/ld-x86-64/gpoff-8d.d: Likewise.
	* testsuite/ld-x86-64/gpoff-8e.d: Likewise.
	* testsuite/ld-x86-64/gpoff-8f.d: Likewise.
	* testsuite/ld-i386/i386.exp: Run R_386_GPOFF tests.
	* testsuite/ld-x86-64/x86-64.exp: Run R_X86_64_GPOFF tests.
parent 98c5dfc9
......@@ -150,9 +150,12 @@ static reloc_howto_type elf_howto_table[]=
HOWTO(R_386_GOT32X, 0, 2, 32, FALSE, 0, complain_overflow_bitfield,
bfd_elf_generic_reloc, "R_386_GOT32X",
TRUE, 0xffffffff, 0xffffffff, FALSE),
HOWTO(R_386_GPOFF, 0, 2, 32, FALSE, 0, complain_overflow_signed,
bfd_elf_generic_reloc, "R_386_GPOFF",
TRUE, 0xffffffff, 0xffffffff, FALSE),
/* Another gap. */
#define R_386_ext2 (R_386_GOT32X + 1 - R_386_tls_offset)
#define R_386_ext2 (R_386_GPOFF + 1 - R_386_tls_offset)
#define R_386_vt_offset (R_386_GNU_VTINHERIT - R_386_ext2)
/* GNU extension to record C++ vtable hierarchy. */
......@@ -340,6 +343,10 @@ elf_i386_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
TRACE ("BFD_RELOC_386_GOT32X");
return &elf_howto_table[R_386_GOT32X - R_386_tls_offset];
case BFD_RELOC_GPREL32:
TRACE ("BFD_RELOC_GPREL32");
return &elf_howto_table[R_386_GPOFF - R_386_tls_offset];
case BFD_RELOC_VTABLE_INHERIT:
TRACE ("BFD_RELOC_VTABLE_INHERIT");
return &elf_howto_table[R_386_GNU_VTINHERIT - R_386_vt_offset];
......@@ -986,6 +993,9 @@ struct elf_i386_link_hash_entry
/* Symbol is referenced by R_386_GOTOFF relocation. */
unsigned int gotoff_ref : 1;
/* TRUE if symbol has GPOFF relocations. */
unsigned int has_gpoff_reloc : 1;
/* Symbol has GOT or PLT relocations. */
unsigned int has_got_reloc : 1;
......@@ -1062,6 +1072,8 @@ struct elf_i386_link_hash_table
asection *plt_got;
asection *plt_got_eh_frame;
struct elf_link_hash_entry *gp;
/* Parameters describing PLT generation. */
struct elf_i386_plt_layout plt;
......@@ -1144,6 +1156,7 @@ elf_i386_link_hash_newfunc (struct bfd_hash_entry *entry,
eh->dyn_relocs = NULL;
eh->tls_type = GOT_UNKNOWN;
eh->gotoff_ref = 0;
eh->has_gpoff_reloc = 0;
eh->has_got_reloc = 0;
eh->has_non_got_reloc = 0;
eh->no_finish_dynamic_symbol = 0;
......@@ -1330,6 +1343,7 @@ elf_i386_copy_indirect_symbol (struct bfd_link_info *info,
generate a R_386_COPY reloc. */
edir->gotoff_ref |= eind->gotoff_ref;
edir->has_gpoff_reloc |= eind->has_gpoff_reloc;
edir->has_got_reloc |= eind->has_got_reloc;
edir->has_non_got_reloc |= eind->has_non_got_reloc;
......@@ -2041,17 +2055,26 @@ elf_i386_check_relocs (bfd *abfd,
if (isym == NULL)
goto error_return;
/* Check relocation against local STT_GNU_IFUNC symbol. */
if (ELF32_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
/* Check relocation against local STT_GNU_IFUNC symbol and
GPOFF relocation. */
if (r_type == R_386_GPOFF
|| ELF32_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
{
h = elf_i386_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_386_GPOFF)
/* 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;
......@@ -2428,6 +2451,11 @@ do_size:
goto error_return;
break;
case R_386_GPOFF:
if (eh != NULL)
eh->has_gpoff_reloc = 1;
break;
default:
break;
}
......@@ -3128,6 +3156,10 @@ elf_i386_allocate_local_dynrelocs (void **slot, void *inf)
struct elf_link_hash_entry *h
= (struct elf_link_hash_entry *) *slot;
/* Skip local symbol with GPOFF relocation. */
if (((struct elf_i386_link_hash_entry *) h)->has_gpoff_reloc)
return TRUE;
if (h->type != STT_GNU_IFUNC
|| !h->def_regular
|| !h->ref_regular
......@@ -5326,6 +5358,41 @@ disallow_got32:
relocation = -elf_i386_tpoff (info, relocation);
break;
case R_386_GPOFF:
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 GPOFF 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: GPOFF 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;
}
......@@ -5854,6 +5921,10 @@ elf_i386_finish_local_dynamic_symbol (void **slot, void *inf)
struct bfd_link_info *info
= (struct bfd_link_info *) inf;
/* Skip local symbol with GPOFF relocation. */
if (((struct elf_i386_link_hash_entry *) h)->has_gpoff_reloc)
return TRUE;
return elf_i386_finish_dynamic_symbol (info->output_bfd, info,
h, NULL);
}
......@@ -7137,12 +7208,124 @@ elf_i386_link_check_relocs (bfd *abfd, struct bfd_link_info *info)
FALSE, FALSE, FALSE);
if (h != NULL)
((struct elf_i386_link_hash_entry *) h)->tls_get_addr = 1;
/* Cache and hide __gp symbol. */
h = elf_link_hash_lookup (elf_hash_table (info), "__gp", FALSE,
FALSE, FALSE);
if (h != NULL)
{
const struct elf_backend_data *bed;
struct elf_i386_link_hash_table *htab;
htab = elf_i386_hash_table (info);
if (htab == NULL)
return FALSE;
htab->gp = h;
/* It should be defined by elf_x86_64_setup_gp later. */
if (h->root.type != bfd_link_hash_defined
&& h->root.type != bfd_link_hash_defweak)
h->def_regular = 1;
h->other = STV_HIDDEN;
bed = get_elf_backend_data (info->output_bfd);
bed->elf_backend_hide_symbol (info, h, TRUE);
}
}
/* Invoke the regular ELF backend linker to do all the work. */
return _bfd_elf_link_check_relocs (abfd, info);
}
/* Set up GP section from symbols with GPOFF relocations. */
static bfd_boolean
elf_i386_setup_gp (struct elf_link_hash_entry *h, void * inf)
{
struct bfd_link_info *info;
struct elf_i386_link_hash_table *htab;
struct elf_i386_link_hash_entry *eh;
struct elf_link_hash_entry *gp;
asection *gpsection;
bfd_size_type gpsection_size;
eh = (struct elf_i386_link_hash_entry *) h;
/* Skip if there is no GPOFF relocation or symbol is undefined. */
if (!eh->has_gpoff_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_i386_hash_table (info);
if (htab == NULL)
return FALSE;
gpsection = h->root.u.def.section->output_section;
gpsection_size = bfd_get_section_size (gpsection);
gp = htab->gp;
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;
/* Found GP section. No need to continue. */
return FALSE;
}
/* Set up GP section from local symbols with GPOFF relocations. */
static bfd_boolean
elf_i386_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_i386_setup_gp (h, info);
}
/* Set up GP section for __gp symbol. */
static bfd_boolean
elf_i386_final_link (bfd *abfd, struct bfd_link_info *info)
{
if (!bfd_link_relocatable (info))
{
struct elf_link_hash_entry *gp;
struct elf_i386_link_hash_table *htab;
htab = elf_i386_hash_table (info);
if (htab == NULL)
return FALSE;
gp = htab->gp;
if (gp != NULL
&& gp->root.type != bfd_link_hash_defined
&& gp->root.type != bfd_link_hash_defweak)
{
/* Set up __gp from a symbol with GPOFF relocations. */
elf_link_hash_traverse (&htab->elf,
elf_i386_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 GPOFF
relocations. */
htab_traverse (htab->loc_hash_table,
elf_i386_setup_gp_from_local_symbol,
info);
}
}
}
/* Invoke the regular ELF backend linker to do all the work. */
return bfd_elf_final_link (abfd, info);
}
#define TARGET_LITTLE_SYM i386_elf32_vec
#define TARGET_LITTLE_NAME "elf32-i386"
#define ELF_ARCH bfd_arch_i386
......@@ -7174,6 +7357,7 @@ elf_i386_link_check_relocs (bfd *abfd, struct bfd_link_info *info)
#define bfd_elf32_bfd_reloc_name_lookup elf_i386_reloc_name_lookup
#define bfd_elf32_get_synthetic_symtab elf_i386_get_synthetic_symtab
#define bfd_elf32_bfd_link_check_relocs elf_i386_link_check_relocs
#define bfd_elf32_bfd_final_link elf_i386_final_link
#define elf_backend_adjust_dynamic_symbol elf_i386_adjust_dynamic_symbol
#define elf_backend_relocs_compatible _bfd_elf_relocs_compatible
......
......@@ -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_GPOFF, 0, 2, 32, FALSE, 0, complain_overflow_signed,
bfd_elf_generic_reloc, "R_X86_64_GPOFF",
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_GPOFF + 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_GPOFF, },
{ BFD_RELOC_VTABLE_INHERIT, R_X86_64_GNU_VTINHERIT, },
{ BFD_RELOC_VTABLE_ENTRY, R_X86_64_GNU_VTENTRY, },
};
......@@ -1092,6 +1096,9 @@ 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 has GPOFF relocations. */
unsigned int has_gpoff_reloc : 1;
/* TRUE if symbol has GOT or PLT relocations. */
unsigned int has_got_reloc : 1;
......@@ -1169,6 +1176,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;
......@@ -1259,6 +1268,7 @@ 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->has_gpoff_reloc = 0;
eh->has_got_reloc = 0;
eh->has_non_got_reloc = 0;
eh->no_finish_dynamic_symbol = 0;
......@@ -1421,6 +1431,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_gpoff_reloc |= eind->has_gpoff_reloc;
edir->has_got_reloc |= eind->has_got_reloc;
edir->has_non_got_reloc |= eind->has_non_got_reloc;
......@@ -2431,18 +2442,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
GPOFF relocation. */
if (r_type == R_X86_64_GPOFF
|| 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_GPOFF)
/* 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;
......@@ -2869,6 +2888,11 @@ do_size:
goto error_return;
break;
case R_X86_64_GPOFF:
if (eh != NULL)
eh->has_gpoff_reloc = 1;
break;
default:
break;
}
......@@ -3526,6 +3550,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 GPOFF relocation. */
if (((struct elf_x86_64_link_hash_entry *) h)->has_gpoff_reloc)
return TRUE;
if (h->type != STT_GNU_IFUNC
|| !h->def_regular
|| !h->ref_regular
......@@ -5694,6 +5722,41 @@ direct:
relocation -= elf_x86_64_dtpoff_base (info);
break;
case R_X86_64_GPOFF:
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 GPOFF 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: GPOFF 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;
}
......@@ -6203,8 +6266,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 GPOFF relocation. */
if (((struct elf_x86_64_link_hash_entry *) h)->has_gpoff_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
......@@ -7696,12 +7763,131 @@ elf_x86_64_link_check_relocs (bfd *abfd, struct bfd_link_info *info)
FALSE, FALSE, FALSE);
if (h != NULL)
((struct elf_x86_64_link_hash_entry *) h)->tls_get_addr = 1;
/* Cache and hide __gp symbol. */
h = elf_link_hash_lookup (elf_hash_table (info), "__gp", FALSE,
FALSE, FALSE);
if (h != NULL)
{
const struct elf_backend_data *bed;
struct elf_x86_64_link_hash_table *htab;
htab = elf_x86_64_hash_table (info);
if (htab == NULL)
return FALSE;
htab->gp = h;
/* It should be defined by elf_x86_64_setup_gp later. */
if (h->root.type != bfd_link_hash_defined
&& h->root.type != bfd_link_hash_defweak)
h->def_regular = 1;
h->other = STV_HIDDEN;
bed = get_elf_backend_data (info->output_bfd);
bed->elf_backend_hide_symbol (info, h, TRUE);
}
}
/* Invoke the regular ELF backend linker to do all the work. */
return _bfd_elf_link_check_relocs (abfd, info);
}
/* Set up GP section from symbols with GPOFF 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;
asection *gpsection;
bfd_size_type gpsection_size;
eh = (struct elf_x86_64_link_hash_entry *) h;
/* Skip if there is no GPOFF relocation or symbol is undefined. */
if (!eh->has_gpoff_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->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;
/* Found GP section. No need to continue. */
return FALSE;
}
/* Set up GP section from local symbols with GPOFF 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
&& gp->root.type != bfd_link_hash_defined
&& gp->root.type != bfd_link_hash_defweak)
{
/* Set up __gp from a symbol with GPOFF 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 GPOFF
relocations. */
htab_traverse (htab->loc_hash_table,
elf_x86_64_setup_gp_from_local_symbol,
info);
}
}
}
/* 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[]=
{
......@@ -7767,6 +7953,7 @@ elf_x86_64_special_sections[]=
#define bfd_elf64_mkobject elf_x86_64_mkobject
#define bfd_elf64_get_synthetic_symtab elf_x86_64_get_synthetic_symtab
#define bfd_elf64_bfd_link_check_relocs elf_x86_64_link_check_relocs
#define bfd_elf64_bfd_final_link elf_x86_64_final_link
#define elf_backend_section_from_shdr \
elf_x86_64_section_from_shdr
......@@ -8068,6 +8255,8 @@ elf32_x86_64_nacl_elf_object_p (bfd *abfd)
elf_x86_64_get_synthetic_symtab
#define bfd_elf32_bfd_link_check_relocs \
elf_x86_64_link_check_relocs
#define bfd_elf32_bfd_final_link \
elf_x86_64_final_link
#undef elf_backend_object_p
#define elf_backend_object_p \
......
......@@ -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,