From 59cab532835904f368b0aa99267afba5fda5ded2 Mon Sep 17 00:00:00 2001 From: "H.J. Lu" Date: Wed, 24 Jun 2015 10:13:55 -0700 Subject: [PATCH] Don't convert R_X86_64_GOTPCREL if it will overflow When converting "mov foo@GOTPCREL(%rip), %reg" to "lea foo(%rip), %reg" with R_X86_64_PC32 relocation, it may overflow if the target section is more than 2GB away. This patch estimates distance between mov instruction and the target section. We convert R_X86_64_GOTPCREL to R_X86_64_PC32 only if their distance is less than 2GB. PR ld/18591 * elf64-x86-64.c (elf_x86_64_convert_mov_to_lea): Don't convert R_X86_64_GOTPCREL to R_X86_64_PC32 if it will cause relocation overflow. --- bfd/ChangeLog | 7 ++ bfd/elf64-x86-64.c | 175 +++++++++++++++++++++++++++++++++++++-------- 2 files changed, 151 insertions(+), 31 deletions(-) diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 157e7f702f4..89188511f92 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,10 @@ +2015-06-24 H.J. Lu + + PR ld/18591 + * elf64-x86-64.c (elf_x86_64_convert_mov_to_lea): Don't convert + R_X86_64_GOTPCREL to R_X86_64_PC32 if it will cause relocation + overflow. + 2015-06-23 Jiong Wang * elfnn-aarch64.c (aarch64_readonly_dynrelocs): New function. diff --git a/bfd/elf64-x86-64.c b/bfd/elf64-x86-64.c index 072c00b7193..f7aea98d6bf 100644 --- a/bfd/elf64-x86-64.c +++ b/bfd/elf64-x86-64.c @@ -2901,6 +2901,7 @@ elf_x86_64_convert_mov_to_lea (bfd *abfd, asection *sec, bfd_boolean changed_contents; bfd_boolean changed_relocs; bfd_signed_vma *local_got_refcounts; + bfd_vma maxpagesize; /* Don't even try to convert non-ELF outputs. */ if (!is_elf_hash_table (link_info->hash)) @@ -2925,6 +2926,7 @@ elf_x86_64_convert_mov_to_lea (bfd *abfd, asection *sec, changed_contents = FALSE; changed_relocs = FALSE; local_got_refcounts = elf_local_got_refcounts (abfd); + maxpagesize = get_elf_backend_data (abfd)->maxpagesize; /* Get the section contents. */ if (elf_section_data (sec)->this_hdr.contents != NULL) @@ -2942,10 +2944,27 @@ elf_x86_64_convert_mov_to_lea (bfd *abfd, asection *sec, unsigned int r_symndx = htab->r_sym (irel->r_info); unsigned int indx; struct elf_link_hash_entry *h; + asection *tsec; + char symtype; + bfd_vma toff, roff; + enum { + none, local, global + } convert_mov_to_lea; if (r_type != R_X86_64_GOTPCREL) continue; + roff = irel->r_offset; + + /* Don't convert R_X86_64_GOTPCREL relocation if it isn't for mov + instruction. */ + if (roff < 2 + || bfd_get_8 (abfd, contents + roff - 2) != 0x8b) + continue; + + tsec = NULL; + convert_mov_to_lea = none; + /* Get the symbol referred to by the reloc. */ if (r_symndx < symtab_hdr->sh_info) { @@ -2954,46 +2973,140 @@ elf_x86_64_convert_mov_to_lea (bfd *abfd, asection *sec, isym = bfd_sym_from_r_symndx (&htab->sym_cache, abfd, r_symndx); - /* STT_GNU_IFUNC must keep R_X86_64_GOTPCREL relocation. */ - if (ELF_ST_TYPE (isym->st_info) != STT_GNU_IFUNC - && irel->r_offset >= 2 - && bfd_get_8 (abfd, contents + irel->r_offset - 2) == 0x8b) + symtype = ELF_ST_TYPE (isym->st_info); + + /* STT_GNU_IFUNC must keep R_X86_64_GOTPCREL relocation and + skip relocation against undefined symbols. */ + if (symtype != STT_GNU_IFUNC && isym->st_shndx != SHN_UNDEF) { - bfd_put_8 (abfd, 0x8d, contents + irel->r_offset - 2); - irel->r_info = htab->r_info (r_symndx, R_X86_64_PC32); - if (local_got_refcounts != NULL - && local_got_refcounts[r_symndx] > 0) - local_got_refcounts[r_symndx] -= 1; - changed_contents = TRUE; - changed_relocs = TRUE; + 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; + convert_mov_to_lea = local; } - continue; } + else + { + indx = r_symndx - symtab_hdr->sh_info; + h = elf_sym_hashes (abfd)[indx]; + BFD_ASSERT (h != NULL); - indx = r_symndx - symtab_hdr->sh_info; - h = elf_sym_hashes (abfd)[indx]; - BFD_ASSERT (h != NULL); + while (h->root.type == bfd_link_hash_indirect + || h->root.type == bfd_link_hash_warning) + h = (struct elf_link_hash_entry *) h->root.u.i.link; - while (h->root.type == bfd_link_hash_indirect - || 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. */ + if (h->def_regular + && h->type != STT_GNU_IFUNC + && 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; + } + } - /* STT_GNU_IFUNC must keep R_X86_64_GOTPCREL relocation. We also - avoid optimizing _DYNAMIC since ld.so may use its link-time - address. */ - if (h->def_regular - && h->type != STT_GNU_IFUNC - && h != htab->elf.hdynamic - && SYMBOL_REFERENCES_LOCAL (link_info, h) - && irel->r_offset >= 2 - && bfd_get_8 (abfd, contents + irel->r_offset - 2) == 0x8b) + if (convert_mov_to_lea == none) + continue; + + if (tsec->sec_info_type == SEC_INFO_TYPE_MERGE) + { + /* At this stage in linking, no SEC_MERGE symbol has been + adjusted, so all references to such symbols need to be + passed through _bfd_merged_section_offset. (Later, in + relocate_section, all SEC_MERGE symbols *except* for + section symbols have been adjusted.) + + gas may reduce relocations against symbols in SEC_MERGE + sections to a relocation against the section symbol when + the original addend was zero. When the reloc is against + a section symbol we should include the addend in the + offset passed to _bfd_merged_section_offset, since the + location of interest is the original symbol. On the + other hand, an access to "sym+addend" where "sym" is not + a section symbol should not include the addend; Such an + access is presumed to be an offset from "sym"; The + location of interest is just "sym". */ + if (symtype == STT_SECTION) + toff += irel->r_addend; + + toff = _bfd_merged_section_offset (abfd, &tsec, + elf_section_data (tsec)->sec_info, + toff); + + if (symtype != STT_SECTION) + toff += irel->r_addend; + } + else + toff += irel->r_addend; + + /* Don't convert if R_X86_64_PC32 relocation overflows. */ + if (tsec->output_section == sec->output_section) + { + if ((toff - roff + 0x80000000) > 0xffffffff) + continue; + } + else + { + asection *asect; + bfd_size_type size; + + /* At this point, we don't know the load addresses of TSEC + section nor SEC section. We estimate the distrance between + SEC and TSEC. */ + size = 0; + for (asect = sec->output_section; + asect != NULL && asect != tsec->output_section; + asect = asect->next) + { + asection *i; + for (i = asect->output_section->map_head.s; + i != NULL; + i = i->map_head.s) + { + size = align_power (size, i->alignment_power); + size += i->size; + } + } + + /* Don't convert R_X86_64_GOTPCREL if TSEC isn't placed after + SEC. */ + if (asect == NULL) + continue; + + /* Take PT_GNU_RELRO segment into account by adding + maxpagesize. */ + if ((toff + size + maxpagesize - roff + 0x80000000) + > 0xffffffff) + continue; + } + + 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 (local_got_refcounts != NULL + && local_got_refcounts[r_symndx] > 0) + local_got_refcounts[r_symndx] -= 1; + } + else { - bfd_put_8 (abfd, 0x8d, contents + irel->r_offset - 2); - irel->r_info = htab->r_info (r_symndx, R_X86_64_PC32); if (h->got.refcount > 0) h->got.refcount -= 1; - changed_contents = TRUE; - changed_relocs = TRUE; } } -- 2.30.2