diff options
Diffstat (limited to 'bfd/elf64-ia64.c')
-rw-r--r-- | bfd/elf64-ia64.c | 418 |
1 files changed, 408 insertions, 10 deletions
diff --git a/bfd/elf64-ia64.c b/bfd/elf64-ia64.c index c24c91d..ed9f8e3 100644 --- a/bfd/elf64-ia64.c +++ b/bfd/elf64-ia64.c @@ -1,5 +1,5 @@ /* IA-64 support for 64-bit ELF - Copyright 1998, 1999 Free Software Foundation, Inc. + Copyright 1998, 1999, 2000 Free Software Foundation, Inc. Contributed by David Mosberger-Tang <davidm@hpl.hp.com> This file is part of BFD, the Binary File Descriptor library. @@ -25,6 +25,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "opcode/ia64.h" #include "elf/ia64.h" + /* * THE RULES for all the stuff the linker creates -- * @@ -154,6 +155,9 @@ static reloc_howto_type *elf64_ia64_reloc_type_lookup PARAMS ((bfd *abfd, bfd_reloc_code_real_type bfd_code)); static void elf64_ia64_info_to_howto PARAMS ((bfd *abfd, arelent *bfd_reloc, Elf64_Internal_Rela *elf_reloc)); +static boolean elf64_ia64_relax_section + PARAMS((bfd *abfd, asection *sec, struct bfd_link_info *link_info, + boolean *again)); static boolean elf64_ia64_section_from_shdr PARAMS ((bfd *, Elf64_Internal_Shdr *, char *)); static boolean elf64_ia64_fake_sections @@ -329,12 +333,13 @@ static reloc_howto_type ia64_howto_table[] = IA64_HOWTO (R_IA64_PLTOFF64MSB, "PLTOFF64MSB", 4, false, true), IA64_HOWTO (R_IA64_PLTOFF64LSB, "PLTOFF64LSB", 4, false, true), - IA64_HOWTO (R_IA64_FPTR64I, "FPTR64I", 4, false, true), + IA64_HOWTO (R_IA64_FPTR64I, "FPTR64I", 0, false, true), IA64_HOWTO (R_IA64_FPTR32MSB, "FPTR32MSB", 2, false, true), IA64_HOWTO (R_IA64_FPTR32LSB, "FPTR32LSB", 2, false, true), IA64_HOWTO (R_IA64_FPTR64MSB, "FPTR64MSB", 4, false, true), IA64_HOWTO (R_IA64_FPTR64LSB, "FPTR64LSB", 4, false, true), + IA64_HOWTO (R_IA64_PCREL60B, "PCREL60B", 0, true, true), IA64_HOWTO (R_IA64_PCREL21B, "PCREL21B", 0, true, true), IA64_HOWTO (R_IA64_PCREL21M, "PCREL21M", 0, true, true), IA64_HOWTO (R_IA64_PCREL21F, "PCREL21F", 0, true, true), @@ -343,8 +348,8 @@ static reloc_howto_type ia64_howto_table[] = IA64_HOWTO (R_IA64_PCREL64MSB, "PCREL64MSB", 4, true, true), IA64_HOWTO (R_IA64_PCREL64LSB, "PCREL64LSB", 4, true, true), - IA64_HOWTO (R_IA64_LTOFF_FPTR22, "LTOFF_FPTR22", 4, false, true), - IA64_HOWTO (R_IA64_LTOFF_FPTR64I, "LTOFF_FPTR64I", 4, false, true), + IA64_HOWTO (R_IA64_LTOFF_FPTR22, "LTOFF_FPTR22", 0, false, true), + IA64_HOWTO (R_IA64_LTOFF_FPTR64I, "LTOFF_FPTR64I", 0, false, true), IA64_HOWTO (R_IA64_LTOFF_FPTR64MSB, "LTOFF_FPTR64MSB", 4, false, true), IA64_HOWTO (R_IA64_LTOFF_FPTR64LSB, "LTOFF_FPTR64LSB", 4, false, true), @@ -369,6 +374,10 @@ static reloc_howto_type ia64_howto_table[] = IA64_HOWTO (R_IA64_LTV64MSB, "LTV64MSB", 4, false, true), IA64_HOWTO (R_IA64_LTV64LSB, "LTV64LSB", 4, false, true), + IA64_HOWTO (R_IA64_PCREL21BI, "PCREL21BI", 0, true, true), + IA64_HOWTO (R_IA64_PCREL22, "PCREL22", 0, true, true), + IA64_HOWTO (R_IA64_PCREL64I, "PCREL64I", 0, true, true), + IA64_HOWTO (R_IA64_IPLTMSB, "IPLTMSB", 4, false, true), IA64_HOWTO (R_IA64_IPLTLSB, "IPLTLSB", 4, false, true), IA64_HOWTO (R_IA64_EPLTMSB, "EPLTMSB", 4, false, true), @@ -377,10 +386,10 @@ static reloc_howto_type ia64_howto_table[] = IA64_HOWTO (R_IA64_LTOFF22X, "LTOFF22X", 0, false, true), IA64_HOWTO (R_IA64_LDXMOV, "LDXMOV", 0, false, true), - IA64_HOWTO (R_IA64_TPREL22, "TPREL22", 4, false, false), + IA64_HOWTO (R_IA64_TPREL22, "TPREL22", 0, false, false), IA64_HOWTO (R_IA64_TPREL64MSB, "TPREL64MSB", 8, false, false), IA64_HOWTO (R_IA64_TPREL64LSB, "TPREL64LSB", 8, false, false), - IA64_HOWTO (R_IA64_LTOFF_TP22, "LTOFF_TP22", 4, false, false), + IA64_HOWTO (R_IA64_LTOFF_TP22, "LTOFF_TP22", 0, false, false), }; static unsigned char elf_code_to_howto_index[R_IA64_MAX_RELOC_CODE + 1]; @@ -451,8 +460,12 @@ elf64_ia64_reloc_type_lookup (abfd, bfd_code) case BFD_RELOC_IA64_FPTR64LSB: rtype = R_IA64_FPTR64LSB; break; case BFD_RELOC_IA64_PCREL21B: rtype = R_IA64_PCREL21B; break; + case BFD_RELOC_IA64_PCREL21BI: rtype = R_IA64_PCREL21BI; break; case BFD_RELOC_IA64_PCREL21M: rtype = R_IA64_PCREL21M; break; case BFD_RELOC_IA64_PCREL21F: rtype = R_IA64_PCREL21F; break; + case BFD_RELOC_IA64_PCREL22: rtype = R_IA64_PCREL22; break; + case BFD_RELOC_IA64_PCREL60B: rtype = R_IA64_PCREL60B; break; + case BFD_RELOC_IA64_PCREL64I: rtype = R_IA64_PCREL64I; break; case BFD_RELOC_IA64_PCREL32MSB: rtype = R_IA64_PCREL32MSB; break; case BFD_RELOC_IA64_PCREL32LSB: rtype = R_IA64_PCREL32LSB; break; case BFD_RELOC_IA64_PCREL64MSB: rtype = R_IA64_PCREL64MSB; break; @@ -549,6 +562,353 @@ static const bfd_byte plt_full_entry[PLT_FULL_ENTRY_SIZE] = }; #define ELF_DYNAMIC_INTERPRETER "/usr/lib/ld.so.1" + +/* Select out of range branch fixup type. Note that Itanium does + not support brl, and so it gets emulated by the kernel. */ +#undef USE_BRL + +static const bfd_byte oor_brl[16] = +{ + 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, /* [MLX] nop.m 0 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* brl.sptk.few tgt;; */ + 0x00, 0x00, 0x00, 0xc0 +}; + +static const bfd_byte oor_ip[48] = +{ + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, /* [MLX] nop.m 0 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, /* movl r15=0 */ + 0x01, 0x00, 0x00, 0x60, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, /* [MII] nop.m 0 */ + 0x00, 0x01, 0x00, 0x60, 0x00, 0x00, /* mov r16=ip;; */ + 0xf2, 0x80, 0x00, 0x80, /* add r16=r15,r16;; */ + 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, /* [MIB] nop.m 0 */ + 0x60, 0x80, 0x04, 0x80, 0x03, 0x00, /* mov b6=r16 */ + 0x60, 0x00, 0x80, 0x00 /* br b6;; */ +}; + +/* These functions do relaxation for IA-64 ELF. + + This is primarily to support branches to targets out of range; + relaxation of R_IA64_LTOFF22X and R_IA64_LDXMOV not yet supported. */ + +static boolean +elf64_ia64_relax_section (abfd, sec, link_info, again) + bfd *abfd; + asection *sec; + struct bfd_link_info *link_info; + boolean *again; +{ + struct one_fixup + { + struct one_fixup *next; + asection *tsec; + bfd_vma toff; + bfd_vma trampoff; + }; + + Elf_Internal_Shdr *symtab_hdr; + Elf_Internal_Rela *internal_relocs; + Elf_Internal_Rela *free_relocs; + Elf_Internal_Rela *irel, *irelend; + bfd_byte *contents; + bfd_byte *free_contents; + Elf64_External_Sym *extsyms; + Elf64_External_Sym *free_extsyms; + struct elf64_ia64_link_hash_table *ia64_info; + struct one_fixup *fixups = NULL; + boolean changed_contents = false; + boolean changed_relocs = false; + + /* Assume we're not going to change any sizes, and we we'll only + need one pass. */ + *again = false; + + /* Nothing to do if there are no relocations. */ + if ((sec->flags & SEC_RELOC) == 0 + || sec->reloc_count == 0) + return true; + + /* If this is the first time we have been called for this section, + initialize the cooked size. */ + if (sec->_cooked_size == 0) + sec->_cooked_size = sec->_raw_size; + + symtab_hdr = &elf_tdata (abfd)->symtab_hdr; + + /* Load the relocations for this section. */ + internal_relocs = (_bfd_elf64_link_read_relocs + (abfd, sec, (PTR) NULL, (Elf_Internal_Rela *) NULL, + link_info->keep_memory)); + if (internal_relocs == NULL) + goto error_return; + free_relocs = NULL; + if (! link_info->keep_memory) + free_relocs = internal_relocs; + + ia64_info = elf64_ia64_hash_table (link_info); + irelend = internal_relocs + sec->reloc_count; + + for (irel = internal_relocs; irel < irelend; irel++) + if (ELF64_R_TYPE (irel->r_info) == (int) R_IA64_PCREL21B) + break; + + /* No branch-type relocations. */ + if (irel == irelend) + { + if (free_relocs != NULL) + free (free_relocs); + return true; + } + + /* Get the section contents. */ + free_contents = NULL; + if (elf_section_data (sec)->this_hdr.contents != NULL) + contents = elf_section_data (sec)->this_hdr.contents; + else + { + contents = (bfd_byte *) bfd_malloc (sec->_raw_size); + if (contents == NULL) + goto error_return; + free_contents = contents; + + if (! bfd_get_section_contents (abfd, sec, contents, + (file_ptr) 0, sec->_raw_size)) + goto error_return; + } + + /* Read this BFD's symbols. */ + free_extsyms = NULL; + if (symtab_hdr->contents != NULL) + extsyms = (Elf64_External_Sym *) symtab_hdr->contents; + else + { + extsyms = (Elf64_External_Sym *) bfd_malloc (symtab_hdr->sh_size); + if (extsyms == NULL) + goto error_return; + free_extsyms = extsyms; + if (bfd_seek (abfd, symtab_hdr->sh_offset, SEEK_SET) != 0 + || (bfd_read (extsyms, 1, symtab_hdr->sh_size, abfd) + != symtab_hdr->sh_size)) + goto error_return; + } + + for (; irel < irelend; irel++) + { + bfd_vma symaddr, reladdr, trampoff, toff, roff; + Elf_Internal_Sym isym; + asection *tsec; + struct one_fixup *f; + + if (ELF64_R_TYPE (irel->r_info) != (int) R_IA64_PCREL21B) + continue; + + /* Get the value of the symbol referred to by the reloc. */ + if (ELF64_R_SYM (irel->r_info) < symtab_hdr->sh_info) + { + /* A local symbol. */ + bfd_elf64_swap_symbol_in (abfd, + extsyms + ELF64_R_SYM (irel->r_info), + &isym); + if (isym.st_shndx == SHN_UNDEF) + continue; /* We can't do anthing with undefined symbols. */ + else 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 > 0 && isym.st_shndx < SHN_LORESERVE) + tsec = bfd_section_from_elf_index (abfd, isym.st_shndx); + else + continue; /* who knows. */ + + toff = isym.st_value; + } + else + { + unsigned long indx; + struct elf_link_hash_entry *h; + struct elf64_ia64_dyn_sym_info *dyn_i; + + indx = ELF64_R_SYM (irel->r_info) - 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; + + dyn_i = get_dyn_sym_info (ia64_info, h, abfd, irel, false); + + /* For branches to dynamic symbols, we're interested instead + in a branch to the PLT entry. */ + if (dyn_i && dyn_i->want_plt2) + { + tsec = ia64_info->plt_sec; + toff = dyn_i->plt2_offset; + } + else + { + /* We can't do anthing with undefined symbols. */ + if (h->root.type == bfd_link_hash_undefined + || h->root.type == bfd_link_hash_undefweak) + continue; + + tsec = h->root.u.def.section; + toff = h->root.u.def.value; + } + } + + symaddr = (tsec->output_section->vma + + tsec->output_offset + + toff + + irel->r_addend); + + roff = irel->r_offset; + reladdr = (sec->output_section->vma + + sec->output_offset + + roff) & -4; + + /* If the branch is in range, no need to do anything. */ + if ((bfd_signed_vma) (symaddr - reladdr) >= -0x1000000 + && (bfd_signed_vma) (symaddr - reladdr) <= 0x0FFFFF0) + continue; + + /* If the branch and target are in the same section, you've + got one honking big section and we can't help you. You'll + get an error message later. */ + if (tsec == sec) + continue; + + /* Look for an existing fixup to this address. */ + for (f = fixups; f ; f = f->next) + if (f->tsec == tsec && f->toff == toff) + break; + + if (f == NULL) + { + /* Two alternatives: If it's a branch to a PLT entry, we can + make a copy of the FULL_PLT entry. Otherwise, we'll have + to use a `brl' insn to get where we're going. */ + + int size; + + if (tsec == ia64_info->plt_sec) + size = sizeof (plt_full_entry); + else + { +#ifdef USE_BRL + size = sizeof (oor_brl); +#else + size = sizeof (oor_ip); +#endif + } + + /* Resize the current section to make room for the new branch. */ + trampoff = (sec->_cooked_size + 15) & -16; + contents = (bfd_byte *) bfd_realloc (contents, trampoff + size); + if (contents == NULL) + goto error_return; + sec->_cooked_size = trampoff + size; + + if (tsec == ia64_info->plt_sec) + { + memcpy (contents + trampoff, plt_full_entry, size); + + /* Hijack the old relocation for use as the PLTOFF reloc. */ + irel->r_info = ELF64_R_INFO (ELF64_R_SYM (irel->r_info), + R_IA64_PLTOFF22); + irel->r_offset = trampoff; + } + else + { +#ifdef USE_BRL + memcpy (contents + trampoff, oor_brl, size); + irel->r_info = ELF64_R_INFO (ELF64_R_SYM (irel->r_info), + R_IA64_PCREL60B); + irel->r_offset = trampoff + 2; +#else + memcpy (contents + trampoff, oor_ip, size); + irel->r_info = ELF64_R_INFO (ELF64_R_SYM (irel->r_info), + R_IA64_PCREL64I); + irel->r_addend -= 16; + irel->r_offset = trampoff + 2; +#endif + } + + /* Record the fixup so we don't do it again this section. */ + f = (struct one_fixup *) bfd_malloc (sizeof (*f)); + f->next = fixups; + f->tsec = tsec; + f->toff = toff; + f->trampoff = trampoff; + fixups = f; + } + else + { + /* Nop out the reloc, since we're finalizing things here. */ + irel->r_info = ELF64_R_INFO (0, R_IA64_NONE); + } + + /* Fix up the existing branch to hit the trampoline. Hope like + hell this doesn't overflow too. */ + if (elf64_ia64_install_value (abfd, contents + roff, + f->trampoff - (roff & -4), + R_IA64_PCREL21B) != bfd_reloc_ok) + goto error_return; + + changed_contents = true; + changed_relocs = true; + } + + /* Clean up and go home. */ + while (fixups) + { + struct one_fixup *f = fixups; + fixups = fixups->next; + free (f); + } + + if (changed_relocs) + elf_section_data (sec)->relocs = internal_relocs; + else if (free_relocs != NULL) + free (free_relocs); + + if (changed_contents) + elf_section_data (sec)->this_hdr.contents = contents; + else if (free_contents != NULL) + { + if (! link_info->keep_memory) + free (free_contents); + else + { + /* Cache the section contents for elf_link_input_bfd. */ + elf_section_data (sec)->this_hdr.contents = contents; + } + } + + if (free_extsyms != NULL) + { + if (! link_info->keep_memory) + free (free_extsyms); + else + { + /* Cache the symbols for elf_link_input_bfd. */ + symtab_hdr->contents = extsyms; + } + } + + *again = changed_contents || changed_relocs; + return true; + + error_return: + if (free_relocs != NULL) + free (free_relocs); + if (free_contents != NULL) + free (free_contents); + if (free_extsyms != NULL) + free (free_extsyms); + return false; +} /* Handle an IA-64 specific section when reading an object file. This is called when elfcode.h finds a section with an unknown type. */ @@ -1468,6 +1828,7 @@ elf64_ia64_check_relocs (abfd, info, sec, relocs) break; case R_IA64_PCREL21B: + case R_IA64_PCREL60B: /* Depending on where this symbol is defined, we may or may not need a full plt entry. Only skip if we know we'll not need the entry -- static or symbolic, and the symbol definition @@ -1489,6 +1850,8 @@ elf64_ia64_check_relocs (abfd, info, sec, relocs) dynrel_type = R_IA64_DIR64LSB; break; + case R_IA64_PCREL22: + case R_IA64_PCREL64I: case R_IA64_PCREL32MSB: case R_IA64_PCREL32LSB: case R_IA64_PCREL64MSB: @@ -2185,15 +2548,21 @@ elf64_ia64_install_value (abfd, hit_addr, val, r_type) /* Instruction relocations. */ case R_IA64_IMM14: opnd = IA64_OPND_IMM14; break; + case R_IA64_PCREL21F: opnd = IA64_OPND_TGT25; break; case R_IA64_PCREL21M: opnd = IA64_OPND_TGT25b; break; - case R_IA64_PCREL21B: opnd = IA64_OPND_TGT25c; break; + case R_IA64_PCREL60B: opnd = IA64_OPND_TGT64; break; + case R_IA64_PCREL21B: + case R_IA64_PCREL21BI: + opnd = IA64_OPND_TGT25c; + break; case R_IA64_IMM22: case R_IA64_GPREL22: case R_IA64_LTOFF22: case R_IA64_LTOFF22X: case R_IA64_PLTOFF22: + case R_IA64_PCREL22: case R_IA64_LTOFF_FPTR22: opnd = IA64_OPND_IMM22; break; @@ -2202,6 +2571,7 @@ elf64_ia64_install_value (abfd, hit_addr, val, r_type) case R_IA64_GPREL64I: case R_IA64_LTOFF64I: case R_IA64_PLTOFF64I: + case R_IA64_PCREL64I: case R_IA64_FPTR64I: case R_IA64_LTOFF_FPTR64I: opnd = IA64_OPND_IMMU64; @@ -2308,6 +2678,31 @@ elf64_ia64_install_value (abfd, hit_addr, val, r_type) bfd_put_64 (abfd, t1, hit_addr + 8); break; + case IA64_OPND_TGT64: + hit_addr -= (long) hit_addr & 0x3; + t0 = bfd_get_64 (abfd, hit_addr); + t1 = bfd_get_64 (abfd, hit_addr + 8); + + /* tmpl/s: bits 0.. 5 in t0 + slot 0: bits 5..45 in t0 + slot 1: bits 46..63 in t0, bits 0..22 in t1 + slot 2: bits 23..63 in t1 */ + + /* First, clear the bits that form the 64 bit constant. */ + t0 &= ~(0x3ffffLL << 46); + t1 &= ~(0x7fffffLL + | ((1LL << 36 | 0xfffffLL << 13) << 23)); + + val >>= 4; + t0 |= ((val >> 20) & 0xffffLL) << 2 << 46; /* 16 lsbs of imm39 */ + t1 |= ((val >> 36) & 0x7fffffLL) << 0; /* 23 msbs of imm39 */ + t1 |= ((((val >> 0) & 0xfffffLL) << 13) /* imm20b */ + | (((val >> 59) & 0x1LL) << 36)) << 23; /* i */ + + bfd_put_64 (abfd, t0, hit_addr); + bfd_put_64 (abfd, t1, hit_addr + 8); + break; + default: switch ((long) hit_addr & 0x3) { @@ -3090,6 +3485,7 @@ elf64_ia64_relocate_section (output_bfd, info, input_bfd, input_section, } goto finish_pcrel; + case R_IA64_PCREL21BI: case R_IA64_PCREL21F: case R_IA64_PCREL21M: /* ??? These two are only used for speculation fixup code. @@ -3113,10 +3509,8 @@ elf64_ia64_relocate_section (output_bfd, info, input_bfd, input_section, goto finish_pcrel; case R_IA64_PCREL21B: + case R_IA64_PCREL60B: /* We should have created a PLT entry for any dynamic symbol. */ - /* ??? How to handle out of range branches, which are supposed - to be fixed up by a conforming linker. */ - dyn_i = NULL; if (h) dyn_i = get_dyn_sym_info (ia64_info, h, NULL, NULL, false); @@ -3145,6 +3539,8 @@ elf64_ia64_relocate_section (output_bfd, info, input_bfd, input_section, } goto finish_pcrel; + case R_IA64_PCREL22: + case R_IA64_PCREL64I: finish_pcrel: /* Make pc-relative. */ value -= (input_section->output_section->vma @@ -3650,6 +4046,8 @@ elf64_ia64_print_private_bfd_data (abfd, ptr) elf64_ia64_reloc_type_lookup #define bfd_elf64_bfd_is_local_label_name \ elf64_ia64_is_local_label_name +#define bfd_elf64_bfd_relax_section \ + elf64_ia64_relax_section /* Stuff for the BFD linker: */ #define bfd_elf64_bfd_link_hash_table_create \ |