summaryrefslogtreecommitdiff
path: root/bfd/elf32-i386.c
diff options
context:
space:
mode:
Diffstat (limited to 'bfd/elf32-i386.c')
-rw-r--r--bfd/elf32-i386.c198
1 files changed, 191 insertions, 7 deletions
diff --git a/bfd/elf32-i386.c b/bfd/elf32-i386.c
index d5477c42153..578dbe6e2c7 100644
--- a/bfd/elf32-i386.c
+++ b/bfd/elf32-i386.c
@@ -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