summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bfd/elf64-x86-64.c95
1 files changed, 71 insertions, 24 deletions
diff --git a/bfd/elf64-x86-64.c b/bfd/elf64-x86-64.c
index 0bcd59dae54..ab9d68cb96b 100644
--- a/bfd/elf64-x86-64.c
+++ b/bfd/elf64-x86-64.c
@@ -1723,25 +1723,30 @@ elf_x86_64_convert_load_reloc (bfd *abfd, asection *sec,
/* Get the symbol referred to by the reloc. */
if (h == NULL)
{
- Elf_Internal_Sym *isym
- = bfd_sym_from_r_symndx (&htab->sym_cache, abfd, r_symndx);
+ /* Check R_X86_64_PC32 relocation overflow only if R_X86_64_PC32
+ is required. */
+ if (require_reloc_pc32)
+ {
+ Elf_Internal_Sym *isym
+ = bfd_sym_from_r_symndx (&htab->sym_cache, abfd, r_symndx);
- /* Skip relocation against undefined symbols. */
- if (isym->st_shndx == SHN_UNDEF)
- return TRUE;
+ /* Skip relocation against undefined symbols. */
+ if (isym->st_shndx == SHN_UNDEF)
+ return TRUE;
- symtype = ELF_ST_TYPE (isym->st_info);
+ symtype = ELF_ST_TYPE (isym->st_info);
- if (isym->st_shndx == SHN_ABS)
- tsec = bfd_abs_section_ptr;
- else if (isym->st_shndx == SHN_COMMON)
- tsec = bfd_com_section_ptr;
- else if (isym->st_shndx == SHN_X86_64_LCOMMON)
- tsec = &_bfd_elf_large_com_section;
- else
- tsec = bfd_section_from_elf_index (abfd, isym->st_shndx);
+ if (isym->st_shndx == SHN_ABS)
+ tsec = bfd_abs_section_ptr;
+ else if (isym->st_shndx == SHN_COMMON)
+ tsec = bfd_com_section_ptr;
+ else if (isym->st_shndx == SHN_X86_64_LCOMMON)
+ tsec = &_bfd_elf_large_com_section;
+ else
+ tsec = bfd_section_from_elf_index (abfd, isym->st_shndx);
- toff = isym->st_value;
+ toff = isym->st_value;
+ }
}
else
{
@@ -1806,8 +1811,9 @@ elf_x86_64_convert_load_reloc (bfd *abfd, asection *sec,
return TRUE;
}
- /* We can only estimate relocation overflow for R_X86_64_PC32. */
- if (!to_reloc_pc32)
+ /* We can only estimate relocation overflow for R_X86_64_PC32. Skip
+ R_X86_64_PC32 relocation overflow check if it isn't required. */
+ if (!to_reloc_pc32 || !require_reloc_pc32)
goto convert;
if (tsec->sec_info_type == SEC_INFO_TYPE_MERGE)
@@ -2046,6 +2052,8 @@ elf_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info,
asection *sreloc;
bfd_byte *contents;
bfd_boolean use_plt_got;
+ bfd_boolean changed;
+ bfd_boolean no_relax;
if (bfd_link_relocatable (info))
return TRUE;
@@ -2068,6 +2076,7 @@ elf_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info,
return FALSE;
}
+ no_relax = info->disable_target_specific_optimizations > 1;
use_plt_got = get_elf_x86_64_backend_data (abfd) == &elf_x86_64_arch_bed;
symtab_hdr = &elf_symtab_hdr (abfd);
@@ -2261,21 +2270,50 @@ elf_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info,
eh->has_got_reloc = 1;
break;
+ case R_X86_64_GOTPCREL:
+ case R_X86_64_GOTPCRELX:
+ case R_X86_64_REX_GOTPCRELX:
+ if (!no_relax
+ && (h == NULL || h->type != STT_GNU_IFUNC))
+ {
+ bfd_boolean converted = FALSE;
+ if (!elf_x86_64_convert_load_reloc (abfd, sec, contents,
+ (Elf_Internal_Rela *) rel,
+ h, &converted, info))
+ goto error_return;
+
+ if (converted)
+ {
+ changed = TRUE;
+ r_type = ELF32_R_TYPE (rel->r_info);
+ switch (r_type)
+ {
+ default:
+ abort ();
+ break;
+ case R_X86_64_32:
+ case R_X86_64_32S:
+ case R_X86_64_PC32:
+ break;
+ }
+ break;
+ }
+ }
+ goto do_reloc_gotpcrel;
+
case R_X86_64_GOTTPOFF:
if (!bfd_link_executable (info))
info->flags |= DF_STATIC_TLS;
/* Fall through */
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:
case R_X86_64_GOTPLT64:
case R_X86_64_GOTPC32_TLSDESC:
case R_X86_64_TLSDESC_CALL:
+do_reloc_gotpcrel:
/* This symbol requires a global offset table entry. */
{
int tls_type, old_tls_type;
@@ -2646,16 +2684,17 @@ do_size:
goto error_return;
}
- if ((r_type == R_X86_64_GOTPCREL
- || r_type == R_X86_64_GOTPCRELX
- || r_type == R_X86_64_REX_GOTPCRELX)
+ if (no_relax
+ && (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_load = 1;
}
if (elf_section_data (sec)->this_hdr.contents != contents)
{
- if (!info->keep_memory)
+ if (!changed && !info->keep_memory)
free (contents);
else
{
@@ -2664,6 +2703,14 @@ do_size:
}
}
+ if (elf_section_data (sec)->relocs != relocs && changed)
+ {
+ /* Must cache relocations if they are changed. */
+ if (elf_section_data (sec)->relocs != NULL)
+ abort ();
+ elf_section_data (sec)->relocs = (Elf_Internal_Rela *) relocs;
+ }
+
return TRUE;
error_return: