X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;ds=inline;f=bfd%2Felf32-i386.c;h=f56b39e838640ab6ca5d369cb062893f46623318;hb=02a866936de7e63ad4962ccba56c8cd05c231275;hp=8c1dadb4cf3a1dd1711a3ea53da3f140f418704a;hpb=99ba51255402201c6e193d803ee1f1fc3f2de0e6;p=external%2Fbinutils.git diff --git a/bfd/elf32-i386.c b/bfd/elf32-i386.c index 8c1dadb..f56b39e 100644 --- a/bfd/elf32-i386.c +++ b/bfd/elf32-i386.c @@ -29,6 +29,7 @@ #include "objalloc.h" #include "hashtab.h" #include "dwarf2.h" +#include "opcode/i386.h" /* 386 uses REL relocations instead of RELA. */ #define USE_REL 1 @@ -146,9 +147,12 @@ static reloc_howto_type elf_howto_table[]= HOWTO(R_386_IRELATIVE, 0, 2, 32, FALSE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_386_IRELATIVE", TRUE, 0xffffffff, 0xffffffff, FALSE), + HOWTO(R_386_GOT32X, 0, 2, 32, FALSE, 0, complain_overflow_bitfield, + bfd_elf_generic_reloc, "R_386_GOT32X", + TRUE, 0xffffffff, 0xffffffff, FALSE), /* Another gap. */ -#define R_386_ext2 (R_386_IRELATIVE + 1 - R_386_tls_offset) +#define R_386_ext2 (R_386_GOT32X + 1 - R_386_tls_offset) #define R_386_vt_offset (R_386_GNU_VTINHERIT - R_386_ext2) /* GNU extension to record C++ vtable hierarchy. */ @@ -332,6 +336,10 @@ elf_i386_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED, TRACE ("BFD_RELOC_386_IRELATIVE"); return &elf_howto_table[R_386_IRELATIVE - R_386_tls_offset]; + case BFD_RELOC_386_GOT32X: + TRACE ("BFD_RELOC_386_GOT32X"); + return &elf_howto_table[R_386_GOT32X - 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]; @@ -1468,7 +1476,7 @@ elf_i386_tls_transition (struct bfd_link_info *info, bfd *abfd, /* Rename some of the generic section flags to better document how they are used here. */ -#define need_convert_mov_to_lea sec_flg0 +#define need_convert_load sec_flg0 /* Look through the relocs for a section during the first phase, and calculate needed space in the global offset table, procedure linkage @@ -1580,6 +1588,7 @@ elf_i386_check_relocs (bfd *abfd, case R_386_PC32: case R_386_PLT32: case R_386_GOT32: + case R_386_GOT32X: if (htab->elf.dynobj == NULL) htab->elf.dynobj = abfd; if (!_bfd_elf_create_ifunc_sections (htab->elf.dynobj, info)) @@ -1637,6 +1646,7 @@ elf_i386_check_relocs (bfd *abfd, /* Fall through */ case R_386_GOT32: + case R_386_GOT32X: case R_386_TLS_GD: case R_386_TLS_GOTDESC: case R_386_TLS_DESC_CALL: @@ -1647,7 +1657,10 @@ elf_i386_check_relocs (bfd *abfd, switch (r_type) { default: - case R_386_GOT32: tls_type = GOT_NORMAL; break; + case R_386_GOT32: + case R_386_GOT32X: + tls_type = GOT_NORMAL; + break; case R_386_TLS_GD: tls_type = GOT_TLS_GD; break; case R_386_TLS_GOTDESC: case R_386_TLS_DESC_CALL: @@ -1948,9 +1961,9 @@ do_size: return FALSE; } - if (r_type == R_386_GOT32 + if ((r_type == R_386_GOT32 || r_type == R_386_GOT32X) && (h == NULL || h->type != STT_GNU_IFUNC)) - sec->need_convert_mov_to_lea = 1; + sec->need_convert_load = 1; } return TRUE; @@ -2074,6 +2087,7 @@ elf_i386_gc_sweep_hook (bfd *abfd, case R_386_TLS_IE: case R_386_TLS_GOTIE: case R_386_GOT32: + case R_386_GOT32X: if (h != NULL) { if (h->got.refcount > 0) @@ -2718,14 +2732,27 @@ elf_i386_readonly_dynrelocs (struct elf_link_hash_entry *h, void *inf) return TRUE; } -/* Convert - mov foo@GOT(%reg), %reg +/* With the local symbol, foo, we convert + mov foo@GOT[(%reg1)], %reg2 + to + lea foo[@GOTOFF(%reg1)], %reg2 + and convert + call/jmp *foo@GOT[(%reg)] to - lea foo@GOTOFF(%reg), %reg - with the local symbol, foo. */ + nop call foo/jmp foo nop + When PIC is false, convert + test %reg1, foo@GOT[(%reg2)] + to + test $foo, %reg1 + and convert + binop foo@GOT[(%reg1)], %reg2 + to + binop $foo, %reg2 + where binop is one of adc, add, and, cmp, or, sbb, sub, xor + instructions. */ static bfd_boolean -elf_i386_convert_mov_to_lea (bfd *abfd, asection *sec, +elf_i386_convert_load (bfd *abfd, asection *sec, struct bfd_link_info *link_info) { Elf_Internal_Shdr *symtab_hdr; @@ -2743,7 +2770,7 @@ elf_i386_convert_mov_to_lea (bfd *abfd, asection *sec, /* Nothing to do if there is no need or no output. */ if ((sec->flags & (SEC_CODE | SEC_RELOC)) != (SEC_CODE | SEC_RELOC) - || sec->need_convert_mov_to_lea == 0 + || sec->need_convert_load == 0 || bfd_is_abs_section (sec->output_section)) return TRUE; @@ -2777,32 +2804,92 @@ elf_i386_convert_mov_to_lea (bfd *abfd, asection *sec, unsigned int r_symndx = ELF32_R_SYM (irel->r_info); unsigned int indx; struct elf_link_hash_entry *h; + unsigned int opcode; + unsigned int modrm; + bfd_vma roff; + bfd_boolean baseless; + Elf_Internal_Sym *isym; + unsigned int addend; + unsigned int nop; + bfd_vma nop_offset; - if (r_type != R_386_GOT32) + if (r_type != R_386_GOT32 && r_type != R_386_GOT32X) continue; - /* Get the symbol referred to by the reloc. */ - if (r_symndx < symtab_hdr->sh_info) + roff = irel->r_offset; + if (roff < 2) + continue; + + modrm = bfd_get_8 (abfd, contents + roff - 1); + baseless = (modrm & 0xc7) == 0x5; + + if (r_type == R_386_GOT32X + && baseless + && bfd_link_pic (link_info)) { - Elf_Internal_Sym *isym; + /* For PIC, disallow R_386_GOT32X without a base register + since we don't know what the GOT base is. Allow + R_386_GOT32 for existing object files. */ + const char *name; + + if (r_symndx < symtab_hdr->sh_info) + { + isym = bfd_sym_from_r_symndx (&htab->sym_cache, abfd, + r_symndx); + name = bfd_elf_sym_name (abfd, symtab_hdr, isym, NULL); + } + else + { + indx = r_symndx - symtab_hdr->sh_info; + h = elf_sym_hashes (abfd)[indx]; + BFD_ASSERT (h != NULL); + name = h->root.root.string; + } + + (*_bfd_error_handler) + (_("%B: direct GOT relocation R_386_GOT32X against `%s' without base register can not be used when making a shared object"), + abfd, name); + goto error_return; + } + + opcode = bfd_get_8 (abfd, contents + roff - 2); + + /* It is OK to convert mov to lea. */ + if (opcode != 0x8b) + { + /* Only convert R_386_GOT32X relocation for call, jmp or + one of adc, add, and, cmp, or, sbb, sub, test, xor + instructions. */ + if (r_type != R_386_GOT32X) + continue; + + /* It is OK to convert indirect branch to direct branch. It + is OK to convert adc, add, and, cmp, or, sbb, sub, test, + xor only when PIC is false. */ + if (opcode != 0xff && bfd_link_pic (link_info)) + continue; + } + /* Try to convert R_386_GOT32 and R_386_GOT32X. Get the symbol + referred to by the reloc. */ + if (r_symndx < symtab_hdr->sh_info) + { isym = bfd_sym_from_r_symndx (&htab->sym_cache, abfd, r_symndx); - /* STT_GNU_IFUNC must keep R_386_GOT32 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) - { - bfd_put_8 (abfd, 0x8d, contents + irel->r_offset - 2); - irel->r_info = ELF32_R_INFO (r_symndx, R_386_GOTOFF); - if (local_got_refcounts != NULL - && local_got_refcounts[r_symndx] > 0) - local_got_refcounts[r_symndx] -= 1; - changed_contents = TRUE; - changed_relocs = TRUE; - } - continue; + /* STT_GNU_IFUNC must keep GOT32 relocations. */ + if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC) + continue; + + h = NULL; + if (opcode == 0x0ff) + /* Convert "call/jmp *foo@GOT[(%reg)]". */ + goto convert_branch; + else + /* Convert "mov foo@GOT[(%reg1)], %reg2", + "test %reg1, foo@GOT(%reg2)" and + "binop foo@GOT[(%reg1)], %reg2". */ + goto convert_load; } indx = r_symndx - symtab_hdr->sh_info; @@ -2813,22 +2900,147 @@ elf_i386_convert_mov_to_lea (bfd *abfd, asection *sec, || h->root.type == bfd_link_hash_warning) h = (struct elf_link_hash_entry *) h->root.u.i.link; - /* STT_GNU_IFUNC must keep R_386_GOT32 relocation. We also avoid - optimizing _DYNAMIC since ld.so may use its link-time address. */ - if ((h->root.type == bfd_link_hash_defined - || h->root.type == bfd_link_hash_defweak) - && 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) + /* STT_GNU_IFUNC must keep GOT32 relocations. */ + if (h->type == STT_GNU_IFUNC) + continue; + + if (opcode == 0xff) + { + /* We have "call/jmp *foo@GOT[(%reg)]". */ + if ((h->root.type == bfd_link_hash_defined + || h->root.type == bfd_link_hash_defweak) + && SYMBOL_REFERENCES_LOCAL (link_info, h)) + { + /* The function is locally defined. */ +convert_branch: + addend = bfd_get_32 (abfd, contents + roff); + /* Addend for R_386_GOT32X relocation must be 0. */ + if (addend != 0) + continue; + + /* Convert R_386_GOT32X to R_386_PC32. */ + if (modrm == 0x15 || (modrm & 0xf8) == 0x90) + { + /* Convert to "nop call foo". ADDR_PREFIX_OPCODE + is a nop prefix. */ + modrm = 0xe8; + nop = ADDR_PREFIX_OPCODE; + nop_offset = roff - 2; + } + else + { + /* Convert to "jmp foo nop". */ + modrm = 0xe9; + nop = NOP_OPCODE; + nop_offset = roff + 3; + irel->r_offset -= 1; + } + + bfd_put_8 (abfd, nop, contents + nop_offset); + bfd_put_8 (abfd, modrm, contents + irel->r_offset - 1); + /* When converting to PC-relative relocation, we + need to adjust addend by -4. */ + bfd_put_32 (abfd, -4, contents + irel->r_offset); + irel->r_info = ELF32_R_INFO (r_symndx, R_386_PC32); + + if (h) + { + if (h->got.refcount > 0) + h->got.refcount -= 1; + } + else + { + if (local_got_refcounts != NULL + && local_got_refcounts[r_symndx] > 0) + local_got_refcounts[r_symndx] -= 1; + } + + changed_contents = TRUE; + changed_relocs = TRUE; + } + } + else { - bfd_put_8 (abfd, 0x8d, contents + irel->r_offset - 2); - irel->r_info = ELF32_R_INFO (r_symndx, R_386_GOTOFF); - if (h->got.refcount > 0) - h->got.refcount -= 1; - changed_contents = TRUE; - changed_relocs = TRUE; + /* We have "mov foo@GOT[(%re1g)], %reg2", + "test %reg1, foo@GOT(%reg2)" and + "binop foo@GOT[(%reg1)], %reg2". + + Avoid optimizing _DYNAMIC since ld.so may use its + link-time address. */ + if (h == htab->elf.hdynamic) + continue; + + if ((h->root.type == bfd_link_hash_defined + || h->root.type == bfd_link_hash_defweak) + && SYMBOL_REFERENCES_LOCAL (link_info, h)) + { +convert_load: + if (opcode == 0x8b) + { + /* Convert "mov foo@GOT(%reg1), %reg2" to + "lea foo@GOTOFF(%reg1), %reg2". */ + if (r_type == R_386_GOT32X + && (baseless || !bfd_link_pic (link_info))) + { + r_type = R_386_32; + /* For R_386_32, convert + "lea foo@GOTOFF(%reg1), %reg2" to + "lea foo@GOT, %reg2". */ + if (!baseless) + { + modrm = 0x5 | (modrm & 0x38); + bfd_put_8 (abfd, modrm, contents + roff - 1); + } + } + else + r_type = R_386_GOTOFF; + opcode = 0x8d; + } + else + { + /* Addend for R_386_GOT32X relocation must be 0. */ + addend = bfd_get_32 (abfd, contents + roff); + if (addend != 0) + continue; + + if (opcode == 0x85) + { + /* Convert "test %reg1, foo@GOT(%reg2)" to + "test $foo, %reg1". */ + modrm = 0xc0 | (modrm & 0x38) >> 3; + opcode = 0xf7; + } + else + { + /* Convert "binop foo@GOT(%reg1), %reg2" to + "binop $foo, %reg2". */ + modrm = (0xc0 + | (modrm & 0x38) >> 3 + | (opcode & 0x3c)); + opcode = 0x81; + } + bfd_put_8 (abfd, modrm, contents + roff - 1); + r_type = R_386_32; + } + + bfd_put_8 (abfd, opcode, contents + roff - 2); + irel->r_info = ELF32_R_INFO (r_symndx, r_type); + + if (h) + { + if (h->got.refcount > 0) + h->got.refcount -= 1; + } + else + { + if (local_got_refcounts != NULL + && local_got_refcounts[r_symndx] > 0) + local_got_refcounts[r_symndx] -= 1; + } + + changed_contents = TRUE; + changed_relocs = TRUE; + } } } @@ -2914,7 +3126,7 @@ elf_i386_size_dynamic_sections (bfd *output_bfd, struct bfd_link_info *info) { struct elf_dyn_relocs *p; - if (!elf_i386_convert_mov_to_lea (ibfd, s, info)) + if (!elf_i386_convert_load (ibfd, s, info)) return FALSE; for (p = ((struct elf_dyn_relocs *) @@ -3664,6 +3876,7 @@ elf_i386_relocate_section (bfd *output_bfd, goto do_relocation; case R_386_GOT32: + case R_386_GOT32X: base_got = htab->elf.sgot; off = h->got.offset; @@ -3741,7 +3954,61 @@ elf_i386_relocate_section (bfd *output_bfd, eh = (struct elf_i386_link_hash_entry *) h; switch (r_type) { + case R_386_GOT32X: + /* Avoid optimizing _DYNAMIC since ld.so may use its + link-time address. */ + if (h == htab->elf.hdynamic) + goto r_386_got32; + + if (bfd_link_pic (info)) + { + /* It is OK to convert mov to lea and convert indirect + branch to direct branch. It is OK to convert adc, + add, and, cmp, or, sbb, sub, test, xor only when PIC + is false. */ + unsigned int opcode; + opcode = bfd_get_8 (abfd, contents + rel->r_offset - 2); + if (opcode != 0x8b && opcode != 0xff) + goto r_386_got32; + } + + /* Resolve "mov GOT[(%reg)], %reg", + "call/jmp *GOT[(%reg)]", "test %reg, foo@GOT[(%reg)]" + and "binop foo@GOT[(%reg)], %reg". */ + if (h == NULL + || (h->plt.offset == (bfd_vma) -1 + && h->got.offset == (bfd_vma) -1) + || htab->elf.sgotplt == NULL) + abort (); + + offplt = (htab->elf.sgotplt->output_section->vma + + htab->elf.sgotplt->output_offset); + + /* It is relative to .got.plt section. */ + if (h->got.offset != (bfd_vma) -1) + /* Use GOT entry. */ + relocation = (htab->elf.sgot->output_section->vma + + htab->elf.sgot->output_offset + + h->got.offset - offplt); + else + /* Use GOTPLT entry. */ + relocation = (h->plt.offset / plt_entry_size - 1 + 3) * 4; + + if (!bfd_link_pic (info)) + { + /* If not PIC, add the .got.plt section address for + baseless adressing. */ + unsigned int modrm; + modrm = bfd_get_8 (abfd, contents + rel->r_offset - 1); + if ((modrm & 0xc7) == 0x5) + relocation += offplt; + } + + unresolved_reloc = FALSE; + break; + case R_386_GOT32: +r_386_got32: /* Relocation is to the entry for this symbol in the global offset table. */ if (htab->elf.sgot == NULL)