diff options
author | Jeff Law <law@redhat.com> | 1999-09-01 19:42:17 +0000 |
---|---|---|
committer | Jeff Law <law@redhat.com> | 1999-09-01 19:42:17 +0000 |
commit | 2eb429af04fa553ce4637b8abfcf72c695b05185 (patch) | |
tree | af2a050067aa5fc04e068127a5dbd942f0784b7b /bfd/elf-hppa.h | |
parent | 21461bfd9b629b8a3a9dc3e100936e5366840768 (diff) | |
download | gdb-2eb429af04fa553ce4637b8abfcf72c695b05185.zip gdb-2eb429af04fa553ce4637b8abfcf72c695b05185.tar.gz gdb-2eb429af04fa553ce4637b8abfcf72c695b05185.tar.bz2 |
* elf-hppa.h (elf_hppa_add_symbol_hook): New function.
(elf_hppa_final_link, elf_hppa_relocate_section): Likewise.
(elf_hppa_final_link_relocate, elf_hppa_relocate_insn): Likewise.
Diffstat (limited to 'bfd/elf-hppa.h')
-rw-r--r-- | bfd/elf-hppa.h | 540 |
1 files changed, 540 insertions, 0 deletions
diff --git a/bfd/elf-hppa.h b/bfd/elf-hppa.h index 44d4f51..c90078d 100644 --- a/bfd/elf-hppa.h +++ b/bfd/elf-hppa.h @@ -19,15 +19,50 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define ELF_HOWTO_TABLE_SIZE R_PARISC_UNIMPLEMENTED + 1 +/* This file is included by multiple PA ELF BFD backends with different + sizes. + + Most of the routines are written to be size independent, but sometimes + external constraints require 32 or 64 bit specific code. We remap + the definitions/functions as necessary here. */ #if ARCH_SIZE == 64 #define ELF_R_TYPE(X) ELF64_R_TYPE(X) +#define ELF_R_SYM(X) ELF64_R_SYM(X) #define _bfd_elf_hppa_gen_reloc_type _bfd_elf64_hppa_gen_reloc_type +#define elf_hppa_relocate_section elf64_hppa_relocate_section +#define bfd_elf_bfd_final_link bfd_elf64_bfd_final_link +#define elf_hppa_final_link elf64_hppa_final_link #endif #if ARCH_SIZE == 32 #define ELF_R_TYPE(X) ELF32_R_TYPE(X) +#define ELF_R_SYM(X) ELF32_R_SYM(X) #define _bfd_elf_hppa_gen_reloc_type _bfd_elf32_hppa_gen_reloc_type +#define elf_hppa_relocate_section elf32_hppa_relocate_section +#define bfd_elf_bfd_final_link bfd_elf32_bfd_final_link +#define elf_hppa_final_link elf32_hppa_final_link #endif +static boolean +elf_hppa_relocate_section + PARAMS ((bfd *, struct bfd_link_info *, bfd *, asection *, + bfd_byte *, Elf_Internal_Rela *, Elf_Internal_Sym *, asection **)); + +static bfd_reloc_status_type elf_hppa_final_link_relocate + PARAMS ((reloc_howto_type *, bfd *, bfd *, asection *, + bfd_byte *, bfd_vma, bfd_vma, bfd_vma, struct bfd_link_info *, + asection *, const char *, int)); + +static unsigned long elf_hppa_relocate_insn + PARAMS ((bfd *, asection *, unsigned long, unsigned long, long, + long, unsigned long, unsigned long, unsigned long)); + +static boolean elf_hppa_add_symbol_hook + PARAMS ((bfd *, struct bfd_link_info *, const Elf_Internal_Sym, + const char **, flagword *, asection **, bfd_vma *)); + +static boolean elf_hppa_final_link + PARAMS ((bfd *, struct bfd_link_info *)); + /* ELF/PA relocation howto entries. */ static reloc_howto_type elf_hppa_howto_table[ELF_HOWTO_TABLE_SIZE] = @@ -667,3 +702,508 @@ elf_hppa_fake_sections (abfd, hdr, sec) return true; } +/* Hook called by the linker routine which adds symbols from an object + file. HP's libraries define symbols with HP specific section + indices, which we have to handle. */ + +static boolean +elf_hppa_add_symbol_hook (abfd, info, sym, namep, flagsp, secp, valp) + bfd *abfd; + struct bfd_link_info *info ATTRIBUTE_UNUSED; + const Elf_Internal_Sym *sym; + const char **namep ATTRIBUTE_UNUSED; + flagword *flagsp ATTRIBUTE_UNUSED; + asection **secp; + bfd_vma *valp; +{ + int index = sym->st_shndx; + + switch (index) + { + case SHN_PARISC_ANSI_COMMON: + *secp = bfd_make_section_old_way (abfd, ".PARISC.ansi.common"); + (*secp)->flags |= SEC_IS_COMMON; + *valp = sym->st_size; + break; + + case SHN_PARISC_HUGE_COMMON: + *secp = bfd_make_section_old_way (abfd, ".PARISC.huge.common"); + (*secp)->flags |= SEC_IS_COMMON; + *valp = sym->st_size; + break; + } + + return true; +} + +/* Called after we have seen all the input files/sections, but before + final symbol resolution and section placement has been determined. + + We use this hook to (possibly) provide a value for __gp, then we + fall back to the generic ELF final link routine. */ + +static boolean +elf_hppa_final_link (abfd, info) + bfd *abfd; + struct bfd_link_info *info; +{ + /* Make sure we've got ourselves a suitable __gp value. */ + if (!info->relocateable) + { + bfd_vma min_short_vma = (bfd_vma -1), max_short_vma = 0; + struct elf_link_hash_entry *gp; + bfd_vma gp_val = 0; + asection *os; + + /* Find the min and max vma of all short sections. */ + for (os = abfd->sections; os ; os = os->next) + { + bfd_vma lo, hi; + + if ((os->flags & SEC_ALLOC) == 0) + continue; + + lo = os->vma; + hi = os->vma + os->_raw_size; + if (hi < lo) + hi = (bfd_vma) -1; + + /* This would be cleaner if we marked sections with an attribute + indicating they are short sections. */ + if (strcmp (os->name, ".sbss") == 0 + || strcmp (os->name, ".sdata") == 0) + { + if (min_short_vma > lo) + min_short_vma = lo; + if (max_short_vma < hi) + max_short_vma = hi; + } + } + + /* See if the user wants to force a value. */ + gp = elf_link_hash_lookup (elf_hash_table (info), "__gp", false, + false, false); + + if (gp + && (gp->root.type == bfd_link_hash_defined + || gp->root.type == bfd_link_hash_defweak)) + { + asection *gp_sec = gp->root.u.def.section; + gp_val = (gp->root.u.def.value + + gp_sec->output_section->vma + + gp_sec->output_offset); + } + else if (max_short_vma != 0) + { + /* Pick a sensible value. */ + gp_val = min_short_vma; + + /* If we don't cover all the short data, adjust. */ + if (max_short_vma - gp_val >= 0x2000) + gp_val = min_short_vma + 0x2000; + + /* If we're addressing stuff past the end, adjust back. */ + if (gp_val > max_vma) + gp_val = max_vma - 0x2000 + 8; + + /* If there was no __gp symbol, create one. */ + if (!gp) + gp = elf_link_hash_lookup (elf_hash_table (info), "__gp", true, + true, false); + + /* We now know the value for the global pointer, figure out which + section to shove it into and mark it as defined in the hash + table. */ + for (os = abfd->sections; os ; os = os->next) + { + bfd_vma low = os->output_offset + os->output_section->vma; + bfd_vma high = low + os->_raw_size; + + if (gp_val >= low && gp_val <= high) + { + gp->root.type = bfd_link_hash_defined; + gp->root.u.def.section = os; + gp->root.u.def.value = gp_val - low; + break; + } + } + } + + /* Validate whether all short sections are within + range of the chosen GP. */ + + if (max_short_vma != 0) + { + if (max_short_vma - min_short_vma >= 0x4000) + { + (*_bfd_error_handler) + (_("%s: short data segment overflowed (0x%lx >= 0x4000)"), + bfd_get_filename (abfd), + (unsigned long)(max_short_vma - min_short_vma)); + return false; + } + else if ((gp_val > min_short_vma + && gp_val - min_short_vma > 0x2000) + || (gp_val < max_short_vma + && max_short_vma - gp_val >= 0x2000)) + { + (*_bfd_error_handler) + (_("%s: __gp does not cover short data segment"), + bfd_get_filename (abfd)); + return false; + } + } + + _bfd_set_gp_value (abfd, gp_val); + } + + /* Invoke the regular ELF backend linker to do all the work. */ + return bfd_elf_bfd_final_link (abfd, info); +} + +/* Relocate an HPPA ELF section. */ + +static boolean +elf_hppa_relocate_section (output_bfd, info, input_bfd, input_section, + contents, relocs, local_syms, local_sections) + bfd *output_bfd; + struct bfd_link_info *info; + bfd *input_bfd; + asection *input_section; + bfd_byte *contents; + Elf_Internal_Rela *relocs; + Elf_Internal_Sym *local_syms; + asection **local_sections; +{ + Elf_Internal_Shdr *symtab_hdr; + Elf_Internal_Rela *rel; + Elf_Internal_Rela *relend; + + symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr; + + rel = relocs; + relend = relocs + input_section->reloc_count; + for (; rel < relend; rel++) + { + int r_type; + reloc_howto_type *howto; + unsigned long r_symndx; + struct elf_link_hash_entry *h; + Elf_Internal_Sym *sym; + asection *sym_sec; + bfd_vma relocation; + bfd_reloc_status_type r; + const char *sym_name; + + r_type = ELF_R_TYPE (rel->r_info); + if (r_type < 0 || r_type >= (int) R_PARISC_UNIMPLEMENTED) + { + bfd_set_error (bfd_error_bad_value); + return false; + } + howto = elf_hppa_howto_table + r_type; + + r_symndx = ELF_R_SYM (rel->r_info); + + if (info->relocateable) + { + /* This is a relocateable link. We don't have to change + anything, unless the reloc is against a section symbol, + in which case we have to adjust according to where the + section symbol winds up in the output section. */ + if (r_symndx < symtab_hdr->sh_info) + { + sym = local_syms + r_symndx; + if (ELF_ST_TYPE (sym->st_info) == STT_SECTION) + { + sym_sec = local_sections[r_symndx]; + rel->r_addend += sym_sec->output_offset; + } + } + + continue; + } + + /* This is a final link. */ + h = NULL; + sym = NULL; + sym_sec = NULL; + if (r_symndx < symtab_hdr->sh_info) + { + sym = local_syms + r_symndx; + sym_sec = local_sections[r_symndx]; + relocation = ((ELF_ST_TYPE (sym->st_info) == STT_SECTION + ? 0 : sym->st_value) + + sym_sec->output_offset + + sym_sec->output_section->vma); + } + else + { + long indx; + + indx = r_symndx - symtab_hdr->sh_info; + h = elf_sym_hashes (input_bfd)[indx]; + 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; + if (h->root.type == bfd_link_hash_defined + || h->root.type == bfd_link_hash_defweak) + { + sym_sec = h->root.u.def.section; + relocation = (h->root.u.def.value + + sym_sec->output_offset + + sym_sec->output_section->vma); + } + else if (h->root.type == bfd_link_hash_undefweak) + relocation = 0; + else + { + if (!((*info->callbacks->undefined_symbol) + (info, h->root.root.string, input_bfd, + input_section, rel->r_offset))) + return false; + break; + } + } + + if (h != NULL) + sym_name = h->root.root.string; + else + { + sym_name = bfd_elf_string_from_elf_section (input_bfd, + symtab_hdr->sh_link, + sym->st_name); + if (sym_name == NULL) + return false; + if (*sym_name == '\0') + sym_name = bfd_section_name (input_bfd, sym_sec); + } + + r = elf_hppa_final_link_relocate (howto, input_bfd, output_bfd, + input_section, contents, + rel->r_offset, relocation, + rel->r_addend, info, sym_sec, + sym_name, h == NULL); + + if (r != bfd_reloc_ok) + { + switch (r) + { + default: + abort (); + case bfd_reloc_overflow: + { + if (!((*info->callbacks->reloc_overflow) + (info, sym_name, howto->name, (bfd_vma) 0, + input_bfd, input_section, rel->r_offset))) + return false; + } + break; + } + } + } + return true; +} + + +/* Actually perform a relocation as part of a final link. */ + +static bfd_reloc_status_type +elf_hppa_final_link_relocate (howto, input_bfd, output_bfd, + input_section, contents, offset, value, + addend, info, sym_sec, sym_name, is_local) + reloc_howto_type *howto; + bfd *input_bfd; + bfd *output_bfd ATTRIBUTE_UNUSED; + asection *input_section; + bfd_byte *contents; + bfd_vma offset; + bfd_vma value; + bfd_vma addend; + struct bfd_link_info *info; + asection *sym_sec; + const char *sym_name; + int is_local; +{ + unsigned long insn; + unsigned long r_type = howto->type; + unsigned long r_format = howto->bitsize; + unsigned long r_field = e_fsel; + bfd_byte *hit_data = contents + offset; + boolean r_pcrel = howto->pc_relative; + + insn = bfd_get_32 (input_bfd, hit_data); + +/* For reference here a quick summary of the relocations found in the + HPUX 11.00 PA64 .o and .a files, but not yet implemented. This is mostly + a guide to help prioritize what relocation support is worked on first. + The list will be deleted eventually. + + 27210 R_PARISC_SEGREL32 + 8458 R_PARISC_DLTREL14R + 8284 R_PARISC_DLTIND21L + 8218 R_PARISC_DLTIND14DR + 6675 R_PARISC_FPTR64 + 6561 R_PARISC_DLTREL21L + 3974 R_PARISC_DIR64 + 3715 R_PARISC_DLTREL14DR + 1584 R_PARISC_LTOFF_FPTR14DR + 1565 R_PARISC_LTOFF_FPTR21L + 1120 R_PARISC_PCREL64 + 1096 R_PARISC_LTOFF_TP14DR + 982 R_PARISC_LTOFF_TP21L + 791 R_PARISC_GPREL64 + 772 R_PARISC_PLTOFF14DR + 386 R_PARISC_PLTOFF21L + 77 R_PARISC_DLTREL14WR + 6 R_PARISC_LTOFF64 + 5 R_PARISC_SEGREL64 + 1 R_PARISC_DLTIND14R + 1 R_PARISC_PCREL21L + 1 R_PARISC_PCREL14R */ + + switch (r_type) + { + case R_PARISC_NONE: + break; + + case R_PARISC_PCREL22F: + case R_PARISC_PCREL17F: + { + bfd_vma location; + r_field = e_fsel; + + /* Find out where we are and where we're going. */ + location = (offset + + input_section->output_offset + + input_section->output_section->vma); + + insn = elf_hppa_relocate_insn (input_bfd, input_section, insn, + offset, value, addend, r_format, + r_field, r_pcrel); + break; + } + + /* Something we don't know how to handle. */ + default: + /* ?!? This is temporary as we flesh out basic linker support, once + the basic support is functional we will return the not_supported + error conditional appropriately. */ +#if 0 + return bfd_reloc_not_supported; +#else + return bfd_reloc_ok; +#endif + } + + /* Update the instruction word. */ + bfd_put_32 (input_bfd, insn, hit_data); + return (bfd_reloc_ok); +} + +/* Relocate the given INSN given the various input parameters. */ + +static unsigned long +elf_hppa_relocate_insn (abfd, input_sect, insn, address, sym_value, + r_addend, r_format, r_field, pcrel) + bfd *abfd; + asection *input_sect; + unsigned long insn; + unsigned long address; + long sym_value; + long r_addend; + unsigned long r_format; + unsigned long r_field; + unsigned long pcrel; +{ + unsigned char opcode = get_opcode (insn); + long constant_value; + + switch (opcode) + { + /* This is any 17 or 22bit PC-relative branch. In PA2.0 syntax it + corresponds to the "B" instruction. */ + case BL: + /* Turn SYM_VALUE into a proper PC relative address. */ + sym_value -= (address + input_sect->output_offset + + input_sect->output_section->vma); + + /* Adjust for any field selectors. */ + sym_value = hppa_field_adjust (sym_value, -8, r_field); + + /* All PC relative branches are implicitly shifted by 2 places. */ + sym_value >>= 2; + + /* Now determine if this is a 17 or 22 bit branch and take + appropriate action. */ + if (((insn >> 13) & 0x7) == 0x4 + || ((insn >> 13) & 0x7) == 0x5) + { + unsigned int w3, w2, w1, w; + + /* These are 22 bit branches. Mask off bits we do not care + about. */ + sym_value &= 0x3fffff; + + /* Now extract the W1, W2, W3 and W fields from the value. */ + dis_assemble_22 (sym_value, &w3, &w1, &w2, &w); + + /* Mask out bits for the value in the instruction. */ + insn &= 0xfc00e002; + + /* Insert the bits for the W1, W2 and W fields into the + instruction. */ + insn |= (w3 << 21) | (w2 << 2) | (w1 << 16) | w; + return insn; + } + else + { + unsigned int w2, w1, w; + /* These are 17 bit branches. Mask off bits we do not care + about. */ + sym_value &= 0x1ffff; + + /* Now extract the W1, W2 and W fields from the value. */ + dis_assemble_17 (sym_value, &w1, &w2, &w); + + /* Mask out bits for the value in the instruction. */ + insn &= 0xffe0e002; + + /* Insert the bits for the W1, W2 and W fields into the + instruction. */ + insn |= (w2 << 2) | (w1 << 16) | w; + return insn; + } + + /* This corresponds to any 17 bit absolute branch. */ + case BE: + case BLE: + { + unsigned int w2, w1, w; + + /* Adjust for any field selectors. */ + sym_value = hppa_field_adjust (sym_value, 0, r_field); + + /* All absolute branches are implicitly shifted by 2 places. */ + sym_value >>= 2; + + /* These are 17 bit branches. Mask off bits we do not care + about. */ + sym_value &= 0x1ffff; + + /* Now extract the W1, W2 and W fields from the value. */ + dis_assemble_17 (sym_value, &w1, &w2, &w); + + /* Mask out bits for the value in the instruction. */ + insn &= 0xffe0e002; + + /* Insert the bits for the W1, W2 and W fields into the + instruction. */ + insn |= (w2 << 2) | (w1 << 16) | w; + return insn; + } + + default: + return insn; + } +} |