| /* Support for HPPA 64-bit ELF |
| Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 |
| Free Software Foundation, Inc. |
| |
| This file is part of BFD, the Binary File Descriptor library. |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, write to the Free Software |
| Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, |
| MA 02110-1301, USA. */ |
| |
| #include "sysdep.h" |
| #include "bfd.h" |
| #include "libbfd.h" |
| #include "elf-bfd.h" |
| #include "elf/hppa.h" |
| #include "libhppa.h" |
| #include "elf64-hppa.h" |
| |
| /* This is the code recommended in the autoconf documentation, almost |
| verbatim. */ |
| #ifndef __GNUC__ |
| # if HAVE_ALLOCA_H |
| # include <alloca.h> |
| # else |
| # ifdef _AIX |
| /* Indented so that pre-ansi C compilers will ignore it, rather than |
| choke on it. Some versions of AIX require this to be the first |
| thing in the file. */ |
| #pragma alloca |
| # else |
| # ifndef alloca /* predefined by HP cc +Olibcalls */ |
| # if !defined (__STDC__) && !defined (__hpux) |
| extern char *alloca (); |
| # else |
| extern void *alloca (); |
| # endif /* __STDC__, __hpux */ |
| # endif /* alloca */ |
| # endif /* _AIX */ |
| # endif /* HAVE_ALLOCA_H */ |
| #else |
| extern void *alloca (size_t); |
| #endif /* __GNUC__ */ |
| |
| |
| #define ARCH_SIZE 64 |
| |
| #define PLT_ENTRY_SIZE 0x10 |
| #define DLT_ENTRY_SIZE 0x8 |
| #define OPD_ENTRY_SIZE 0x20 |
| |
| #define ELF_DYNAMIC_INTERPRETER "/usr/lib/pa20_64/dld.sl" |
| |
| /* The stub is supposed to load the target address and target's DP |
| value out of the PLT, then do an external branch to the target |
| address. |
| |
| LDD PLTOFF(%r27),%r1 |
| BVE (%r1) |
| LDD PLTOFF+8(%r27),%r27 |
| |
| Note that we must use the LDD with a 14 bit displacement, not the one |
| with a 5 bit displacement. */ |
| static char plt_stub[] = {0x53, 0x61, 0x00, 0x00, 0xe8, 0x20, 0xd0, 0x00, |
| 0x53, 0x7b, 0x00, 0x00 }; |
| |
| struct elf64_hppa_dyn_hash_entry |
| { |
| struct bfd_hash_entry root; |
| |
| /* Offsets for this symbol in various linker sections. */ |
| bfd_vma dlt_offset; |
| bfd_vma plt_offset; |
| bfd_vma opd_offset; |
| bfd_vma stub_offset; |
| |
| /* The symbol table entry, if any, that this was derived from. */ |
| struct elf_link_hash_entry *h; |
| |
| /* The index of the (possibly local) symbol in the input bfd and its |
| associated BFD. Needed so that we can have relocs against local |
| symbols in shared libraries. */ |
| long sym_indx; |
| bfd *owner; |
| |
| /* Dynamic symbols may need to have two different values. One for |
| the dynamic symbol table, one for the normal symbol table. |
| |
| In such cases we store the symbol's real value and section |
| index here so we can restore the real value before we write |
| the normal symbol table. */ |
| bfd_vma st_value; |
| int st_shndx; |
| |
| /* Used to count non-got, non-plt relocations for delayed sizing |
| of relocation sections. */ |
| struct elf64_hppa_dyn_reloc_entry |
| { |
| /* Next relocation in the chain. */ |
| struct elf64_hppa_dyn_reloc_entry *next; |
| |
| /* The type of the relocation. */ |
| int type; |
| |
| /* The input section of the relocation. */ |
| asection *sec; |
| |
| /* The index of the section symbol for the input section of |
| the relocation. Only needed when building shared libraries. */ |
| int sec_symndx; |
| |
| /* The offset within the input section of the relocation. */ |
| bfd_vma offset; |
| |
| /* The addend for the relocation. */ |
| bfd_vma addend; |
| |
| } *reloc_entries; |
| |
| /* Nonzero if this symbol needs an entry in one of the linker |
| sections. */ |
| unsigned want_dlt; |
| unsigned want_plt; |
| unsigned want_opd; |
| unsigned want_stub; |
| }; |
| |
| struct elf64_hppa_dyn_hash_table |
| { |
| struct bfd_hash_table root; |
| }; |
| |
| struct elf64_hppa_link_hash_table |
| { |
| struct elf_link_hash_table root; |
| |
| /* Shortcuts to get to the various linker defined sections. */ |
| asection *dlt_sec; |
| asection *dlt_rel_sec; |
| asection *plt_sec; |
| asection *plt_rel_sec; |
| asection *opd_sec; |
| asection *opd_rel_sec; |
| asection *other_rel_sec; |
| |
| /* Offset of __gp within .plt section. When the PLT gets large we want |
| to slide __gp into the PLT section so that we can continue to use |
| single DP relative instructions to load values out of the PLT. */ |
| bfd_vma gp_offset; |
| |
| /* Note this is not strictly correct. We should create a stub section for |
| each input section with calls. The stub section should be placed before |
| the section with the call. */ |
| asection *stub_sec; |
| |
| bfd_vma text_segment_base; |
| bfd_vma data_segment_base; |
| |
| struct elf64_hppa_dyn_hash_table dyn_hash_table; |
| |
| /* We build tables to map from an input section back to its |
| symbol index. This is the BFD for which we currently have |
| a map. */ |
| bfd *section_syms_bfd; |
| |
| /* Array of symbol numbers for each input section attached to the |
| current BFD. */ |
| int *section_syms; |
| }; |
| |
| #define elf64_hppa_hash_table(p) \ |
| ((struct elf64_hppa_link_hash_table *) ((p)->hash)) |
| |
| typedef struct bfd_hash_entry *(*new_hash_entry_func) |
| PARAMS ((struct bfd_hash_entry *, struct bfd_hash_table *, const char *)); |
| |
| static struct bfd_hash_entry *elf64_hppa_new_dyn_hash_entry |
| PARAMS ((struct bfd_hash_entry *entry, struct bfd_hash_table *table, |
| const char *string)); |
| static struct bfd_link_hash_table *elf64_hppa_hash_table_create |
| PARAMS ((bfd *abfd)); |
| static struct elf64_hppa_dyn_hash_entry *elf64_hppa_dyn_hash_lookup |
| PARAMS ((struct elf64_hppa_dyn_hash_table *table, const char *string, |
| bfd_boolean create, bfd_boolean copy)); |
| static void elf64_hppa_dyn_hash_traverse |
| PARAMS ((struct elf64_hppa_dyn_hash_table *table, |
| bfd_boolean (*func) (struct elf64_hppa_dyn_hash_entry *, PTR), |
| PTR info)); |
| |
| static const char *get_dyn_name |
| PARAMS ((bfd *, struct elf_link_hash_entry *, |
| const Elf_Internal_Rela *, char **, size_t *)); |
| |
| /* This must follow the definitions of the various derived linker |
| hash tables and shared functions. */ |
| #include "elf-hppa.h" |
| |
| static bfd_boolean elf64_hppa_object_p |
| PARAMS ((bfd *)); |
| |
| static void elf64_hppa_post_process_headers |
| PARAMS ((bfd *, struct bfd_link_info *)); |
| |
| static bfd_boolean elf64_hppa_create_dynamic_sections |
| PARAMS ((bfd *, struct bfd_link_info *)); |
| |
| static bfd_boolean elf64_hppa_adjust_dynamic_symbol |
| PARAMS ((struct bfd_link_info *, struct elf_link_hash_entry *)); |
| |
| static bfd_boolean elf64_hppa_mark_milli_and_exported_functions |
| PARAMS ((struct elf_link_hash_entry *, PTR)); |
| |
| static bfd_boolean elf64_hppa_size_dynamic_sections |
| PARAMS ((bfd *, struct bfd_link_info *)); |
| |
| static bfd_boolean elf64_hppa_link_output_symbol_hook |
| PARAMS ((struct bfd_link_info *, const char *, Elf_Internal_Sym *, |
| asection *, struct elf_link_hash_entry *)); |
| |
| static bfd_boolean elf64_hppa_finish_dynamic_symbol |
| PARAMS ((bfd *, struct bfd_link_info *, |
| struct elf_link_hash_entry *, Elf_Internal_Sym *)); |
| |
| static enum elf_reloc_type_class elf64_hppa_reloc_type_class |
| PARAMS ((const Elf_Internal_Rela *)); |
| |
| static bfd_boolean elf64_hppa_finish_dynamic_sections |
| PARAMS ((bfd *, struct bfd_link_info *)); |
| |
| static bfd_boolean elf64_hppa_check_relocs |
| PARAMS ((bfd *, struct bfd_link_info *, |
| asection *, const Elf_Internal_Rela *)); |
| |
| static bfd_boolean elf64_hppa_dynamic_symbol_p |
| PARAMS ((struct elf_link_hash_entry *, struct bfd_link_info *)); |
| |
| static bfd_boolean elf64_hppa_mark_exported_functions |
| PARAMS ((struct elf_link_hash_entry *, PTR)); |
| |
| static bfd_boolean elf64_hppa_finalize_opd |
| PARAMS ((struct elf64_hppa_dyn_hash_entry *, PTR)); |
| |
| static bfd_boolean elf64_hppa_finalize_dlt |
| PARAMS ((struct elf64_hppa_dyn_hash_entry *, PTR)); |
| |
| static bfd_boolean allocate_global_data_dlt |
| PARAMS ((struct elf64_hppa_dyn_hash_entry *, PTR)); |
| |
| static bfd_boolean allocate_global_data_plt |
| PARAMS ((struct elf64_hppa_dyn_hash_entry *, PTR)); |
| |
| static bfd_boolean allocate_global_data_stub |
| PARAMS ((struct elf64_hppa_dyn_hash_entry *, PTR)); |
| |
| static bfd_boolean allocate_global_data_opd |
| PARAMS ((struct elf64_hppa_dyn_hash_entry *, PTR)); |
| |
| static bfd_boolean get_reloc_section |
| PARAMS ((bfd *, struct elf64_hppa_link_hash_table *, asection *)); |
| |
| static bfd_boolean count_dyn_reloc |
| PARAMS ((bfd *, struct elf64_hppa_dyn_hash_entry *, |
| int, asection *, int, bfd_vma, bfd_vma)); |
| |
| static bfd_boolean allocate_dynrel_entries |
| PARAMS ((struct elf64_hppa_dyn_hash_entry *, PTR)); |
| |
| static bfd_boolean elf64_hppa_finalize_dynreloc |
| PARAMS ((struct elf64_hppa_dyn_hash_entry *, PTR)); |
| |
| static bfd_boolean get_opd |
| PARAMS ((bfd *, struct bfd_link_info *, struct elf64_hppa_link_hash_table *)); |
| |
| static bfd_boolean get_plt |
| PARAMS ((bfd *, struct bfd_link_info *, struct elf64_hppa_link_hash_table *)); |
| |
| static bfd_boolean get_dlt |
| PARAMS ((bfd *, struct bfd_link_info *, struct elf64_hppa_link_hash_table *)); |
| |
| static bfd_boolean get_stub |
| PARAMS ((bfd *, struct bfd_link_info *, struct elf64_hppa_link_hash_table *)); |
| |
| static int elf64_hppa_elf_get_symbol_type |
| PARAMS ((Elf_Internal_Sym *, int)); |
| |
| static bfd_boolean |
| elf64_hppa_dyn_hash_table_init (struct elf64_hppa_dyn_hash_table *ht, |
| bfd *abfd ATTRIBUTE_UNUSED, |
| new_hash_entry_func new, |
| unsigned int entsize) |
| { |
| memset (ht, 0, sizeof (*ht)); |
| return bfd_hash_table_init (&ht->root, new, entsize); |
| } |
| |
| static struct bfd_hash_entry* |
| elf64_hppa_new_dyn_hash_entry (entry, table, string) |
| struct bfd_hash_entry *entry; |
| struct bfd_hash_table *table; |
| const char *string; |
| { |
| struct elf64_hppa_dyn_hash_entry *ret; |
| ret = (struct elf64_hppa_dyn_hash_entry *) entry; |
| |
| /* Allocate the structure if it has not already been allocated by a |
| subclass. */ |
| if (!ret) |
| ret = bfd_hash_allocate (table, sizeof (*ret)); |
| |
| if (!ret) |
| return 0; |
| |
| /* Call the allocation method of the superclass. */ |
| ret = ((struct elf64_hppa_dyn_hash_entry *) |
| bfd_hash_newfunc ((struct bfd_hash_entry *) ret, table, string)); |
| |
| /* Initialize our local data. All zeros. */ |
| memset (&ret->dlt_offset, 0, |
| (sizeof (struct elf64_hppa_dyn_hash_entry) |
| - offsetof (struct elf64_hppa_dyn_hash_entry, dlt_offset))); |
| |
| return &ret->root; |
| } |
| |
| /* Create the derived linker hash table. The PA64 ELF port uses this |
| derived hash table to keep information specific to the PA ElF |
| linker (without using static variables). */ |
| |
| static struct bfd_link_hash_table* |
| elf64_hppa_hash_table_create (abfd) |
| bfd *abfd; |
| { |
| struct elf64_hppa_link_hash_table *ret; |
| |
| ret = bfd_zalloc (abfd, (bfd_size_type) sizeof (*ret)); |
| if (!ret) |
| return 0; |
| if (!_bfd_elf_link_hash_table_init (&ret->root, abfd, |
| _bfd_elf_link_hash_newfunc, |
| sizeof (struct elf_link_hash_entry))) |
| { |
| bfd_release (abfd, ret); |
| return 0; |
| } |
| |
| if (!elf64_hppa_dyn_hash_table_init (&ret->dyn_hash_table, abfd, |
| elf64_hppa_new_dyn_hash_entry, |
| sizeof (struct elf64_hppa_dyn_hash_entry))) |
| return 0; |
| return &ret->root.root; |
| } |
| |
| /* Look up an entry in a PA64 ELF linker hash table. */ |
| |
| static struct elf64_hppa_dyn_hash_entry * |
| elf64_hppa_dyn_hash_lookup(table, string, create, copy) |
| struct elf64_hppa_dyn_hash_table *table; |
| const char *string; |
| bfd_boolean create, copy; |
| { |
| return ((struct elf64_hppa_dyn_hash_entry *) |
| bfd_hash_lookup (&table->root, string, create, copy)); |
| } |
| |
| /* Traverse a PA64 ELF linker hash table. */ |
| |
| static void |
| elf64_hppa_dyn_hash_traverse (table, func, info) |
| struct elf64_hppa_dyn_hash_table *table; |
| bfd_boolean (*func) PARAMS ((struct elf64_hppa_dyn_hash_entry *, PTR)); |
| PTR info; |
| { |
| (bfd_hash_traverse |
| (&table->root, |
| (bfd_boolean (*) PARAMS ((struct bfd_hash_entry *, PTR))) func, |
| info)); |
| } |
| |
| /* Return nonzero if ABFD represents a PA2.0 ELF64 file. |
| |
| Additionally we set the default architecture and machine. */ |
| static bfd_boolean |
| elf64_hppa_object_p (abfd) |
| bfd *abfd; |
| { |
| Elf_Internal_Ehdr * i_ehdrp; |
| unsigned int flags; |
| |
| i_ehdrp = elf_elfheader (abfd); |
| if (strcmp (bfd_get_target (abfd), "elf64-hppa-linux") == 0) |
| { |
| /* GCC on hppa-linux produces binaries with OSABI=Linux, |
| but the kernel produces corefiles with OSABI=SysV. */ |
| if (i_ehdrp->e_ident[EI_OSABI] != ELFOSABI_LINUX |
| && i_ehdrp->e_ident[EI_OSABI] != ELFOSABI_NONE) /* aka SYSV */ |
| return FALSE; |
| } |
| else |
| { |
| /* HPUX produces binaries with OSABI=HPUX, |
| but the kernel produces corefiles with OSABI=SysV. */ |
| if (i_ehdrp->e_ident[EI_OSABI] != ELFOSABI_HPUX |
| && i_ehdrp->e_ident[EI_OSABI] != ELFOSABI_NONE) /* aka SYSV */ |
| return FALSE; |
| } |
| |
| flags = i_ehdrp->e_flags; |
| switch (flags & (EF_PARISC_ARCH | EF_PARISC_WIDE)) |
| { |
| case EFA_PARISC_1_0: |
| return bfd_default_set_arch_mach (abfd, bfd_arch_hppa, 10); |
| case EFA_PARISC_1_1: |
| return bfd_default_set_arch_mach (abfd, bfd_arch_hppa, 11); |
| case EFA_PARISC_2_0: |
| if (i_ehdrp->e_ident[EI_CLASS] == ELFCLASS64) |
| return bfd_default_set_arch_mach (abfd, bfd_arch_hppa, 25); |
| else |
| return bfd_default_set_arch_mach (abfd, bfd_arch_hppa, 20); |
| case EFA_PARISC_2_0 | EF_PARISC_WIDE: |
| return bfd_default_set_arch_mach (abfd, bfd_arch_hppa, 25); |
| } |
| /* Don't be fussy. */ |
| return TRUE; |
| } |
| |
| /* Given section type (hdr->sh_type), return a boolean indicating |
| whether or not the section is an elf64-hppa specific section. */ |
| static bfd_boolean |
| elf64_hppa_section_from_shdr (bfd *abfd, |
| Elf_Internal_Shdr *hdr, |
| const char *name, |
| int shindex) |
| { |
| asection *newsect; |
| |
| switch (hdr->sh_type) |
| { |
| case SHT_PARISC_EXT: |
| if (strcmp (name, ".PARISC.archext") != 0) |
| return FALSE; |
| break; |
| case SHT_PARISC_UNWIND: |
| if (strcmp (name, ".PARISC.unwind") != 0) |
| return FALSE; |
| break; |
| case SHT_PARISC_DOC: |
| case SHT_PARISC_ANNOT: |
| default: |
| return FALSE; |
| } |
| |
| if (! _bfd_elf_make_section_from_shdr (abfd, hdr, name, shindex)) |
| return FALSE; |
| newsect = hdr->bfd_section; |
| |
| return TRUE; |
| } |
| |
| /* Construct a string for use in the elf64_hppa_dyn_hash_table. The |
| name describes what was once potentially anonymous memory. We |
| allocate memory as necessary, possibly reusing PBUF/PLEN. */ |
| |
| static const char * |
| get_dyn_name (abfd, h, rel, pbuf, plen) |
| bfd *abfd; |
| struct elf_link_hash_entry *h; |
| const Elf_Internal_Rela *rel; |
| char **pbuf; |
| size_t *plen; |
| { |
| asection *sec = abfd->sections; |
| size_t nlen, tlen; |
| char *buf; |
| size_t len; |
| |
| if (h && rel->r_addend == 0) |
| return h->root.root.string; |
| |
| if (h) |
| nlen = strlen (h->root.root.string); |
| else |
| nlen = 8 + 1 + sizeof (rel->r_info) * 2 - 8; |
| tlen = nlen + 1 + sizeof (rel->r_addend) * 2 + 1; |
| |
| len = *plen; |
| buf = *pbuf; |
| if (len < tlen) |
| { |
| if (buf) |
| free (buf); |
| *pbuf = buf = malloc (tlen); |
| *plen = len = tlen; |
| if (!buf) |
| return NULL; |
| } |
| |
| if (h) |
| { |
| memcpy (buf, h->root.root.string, nlen); |
| buf[nlen++] = '+'; |
| sprintf_vma (buf + nlen, rel->r_addend); |
| } |
| else |
| { |
| nlen = sprintf (buf, "%x:%lx", |
| sec->id & 0xffffffff, |
| (long) ELF64_R_SYM (rel->r_info)); |
| if (rel->r_addend) |
| { |
| buf[nlen++] = '+'; |
| sprintf_vma (buf + nlen, rel->r_addend); |
| } |
| } |
| |
| return buf; |
| } |
| |
| /* SEC is a section containing relocs for an input BFD when linking; return |
| a suitable section for holding relocs in the output BFD for a link. */ |
| |
| static bfd_boolean |
| get_reloc_section (abfd, hppa_info, sec) |
| bfd *abfd; |
| struct elf64_hppa_link_hash_table *hppa_info; |
| asection *sec; |
| { |
| const char *srel_name; |
| asection *srel; |
| bfd *dynobj; |
| |
| srel_name = (bfd_elf_string_from_elf_section |
| (abfd, elf_elfheader(abfd)->e_shstrndx, |
| elf_section_data(sec)->rel_hdr.sh_name)); |
| if (srel_name == NULL) |
| return FALSE; |
| |
| BFD_ASSERT ((CONST_STRNEQ (srel_name, ".rela") |
| && strcmp (bfd_get_section_name (abfd, sec), |
| srel_name + 5) == 0) |
| || (CONST_STRNEQ (srel_name, ".rel") |
| && strcmp (bfd_get_section_name (abfd, sec), |
| srel_name + 4) == 0)); |
| |
| dynobj = hppa_info->root.dynobj; |
| if (!dynobj) |
| hppa_info->root.dynobj = dynobj = abfd; |
| |
| srel = bfd_get_section_by_name (dynobj, srel_name); |
| if (srel == NULL) |
| { |
| srel = bfd_make_section_with_flags (dynobj, srel_name, |
| (SEC_ALLOC |
| | SEC_LOAD |
| | SEC_HAS_CONTENTS |
| | SEC_IN_MEMORY |
| | SEC_LINKER_CREATED |
| | SEC_READONLY)); |
| if (srel == NULL |
| || !bfd_set_section_alignment (dynobj, srel, 3)) |
| return FALSE; |
| } |
| |
| hppa_info->other_rel_sec = srel; |
| return TRUE; |
| } |
| |
| /* Add a new entry to the list of dynamic relocations against DYN_H. |
| |
| We use this to keep a record of all the FPTR relocations against a |
| particular symbol so that we can create FPTR relocations in the |
| output file. */ |
| |
| static bfd_boolean |
| count_dyn_reloc (abfd, dyn_h, type, sec, sec_symndx, offset, addend) |
| bfd *abfd; |
| struct elf64_hppa_dyn_hash_entry *dyn_h; |
| int type; |
| asection *sec; |
| int sec_symndx; |
| bfd_vma offset; |
| bfd_vma addend; |
| { |
| struct elf64_hppa_dyn_reloc_entry *rent; |
| |
| rent = (struct elf64_hppa_dyn_reloc_entry *) |
| bfd_alloc (abfd, (bfd_size_type) sizeof (*rent)); |
| if (!rent) |
| return FALSE; |
| |
| rent->next = dyn_h->reloc_entries; |
| rent->type = type; |
| rent->sec = sec; |
| rent->sec_symndx = sec_symndx; |
| rent->offset = offset; |
| rent->addend = addend; |
| dyn_h->reloc_entries = rent; |
| |
| return TRUE; |
| } |
| |
| /* Scan the RELOCS and record the type of dynamic entries that each |
| referenced symbol needs. */ |
| |
| static bfd_boolean |
| elf64_hppa_check_relocs (abfd, info, sec, relocs) |
| bfd *abfd; |
| struct bfd_link_info *info; |
| asection *sec; |
| const Elf_Internal_Rela *relocs; |
| { |
| struct elf64_hppa_link_hash_table *hppa_info; |
| const Elf_Internal_Rela *relend; |
| Elf_Internal_Shdr *symtab_hdr; |
| const Elf_Internal_Rela *rel; |
| asection *dlt, *plt, *stubs; |
| char *buf; |
| size_t buf_len; |
| unsigned int sec_symndx; |
| |
| if (info->relocatable) |
| return TRUE; |
| |
| /* If this is the first dynamic object found in the link, create |
| the special sections required for dynamic linking. */ |
| if (! elf_hash_table (info)->dynamic_sections_created) |
| { |
| if (! _bfd_elf_link_create_dynamic_sections (abfd, info)) |
| return FALSE; |
| } |
| |
| hppa_info = elf64_hppa_hash_table (info); |
| symtab_hdr = &elf_tdata (abfd)->symtab_hdr; |
| |
| /* If necessary, build a new table holding section symbols indices |
| for this BFD. */ |
| |
| if (info->shared && hppa_info->section_syms_bfd != abfd) |
| { |
| unsigned long i; |
| unsigned int highest_shndx; |
| Elf_Internal_Sym *local_syms = NULL; |
| Elf_Internal_Sym *isym, *isymend; |
| bfd_size_type amt; |
| |
| /* We're done with the old cache of section index to section symbol |
| index information. Free it. |
| |
| ?!? Note we leak the last section_syms array. Presumably we |
| could free it in one of the later routines in this file. */ |
| if (hppa_info->section_syms) |
| free (hppa_info->section_syms); |
| |
| /* Read this BFD's local symbols. */ |
| if (symtab_hdr->sh_info != 0) |
| { |
| local_syms = (Elf_Internal_Sym *) symtab_hdr->contents; |
| if (local_syms == NULL) |
| local_syms = bfd_elf_get_elf_syms (abfd, symtab_hdr, |
| symtab_hdr->sh_info, 0, |
| NULL, NULL, NULL); |
| if (local_syms == NULL) |
| return FALSE; |
| } |
| |
| /* Record the highest section index referenced by the local symbols. */ |
| highest_shndx = 0; |
| isymend = local_syms + symtab_hdr->sh_info; |
| for (isym = local_syms; isym < isymend; isym++) |
| { |
| if (isym->st_shndx > highest_shndx |
| && isym->st_shndx < SHN_LORESERVE) |
| highest_shndx = isym->st_shndx; |
| } |
| |
| /* Allocate an array to hold the section index to section symbol index |
| mapping. Bump by one since we start counting at zero. */ |
| highest_shndx++; |
| amt = highest_shndx; |
| amt *= sizeof (int); |
| hppa_info->section_syms = (int *) bfd_malloc (amt); |
| |
| /* Now walk the local symbols again. If we find a section symbol, |
| record the index of the symbol into the section_syms array. */ |
| for (i = 0, isym = local_syms; isym < isymend; i++, isym++) |
| { |
| if (ELF_ST_TYPE (isym->st_info) == STT_SECTION) |
| hppa_info->section_syms[isym->st_shndx] = i; |
| } |
| |
| /* We are finished with the local symbols. */ |
| if (local_syms != NULL |
| && symtab_hdr->contents != (unsigned char *) local_syms) |
| { |
| if (! info->keep_memory) |
| free (local_syms); |
| else |
| { |
| /* Cache the symbols for elf_link_input_bfd. */ |
| symtab_hdr->contents = (unsigned char *) local_syms; |
| } |
| } |
| |
| /* Record which BFD we built the section_syms mapping for. */ |
| hppa_info->section_syms_bfd = abfd; |
| } |
| |
| /* Record the symbol index for this input section. We may need it for |
| relocations when building shared libraries. When not building shared |
| libraries this value is never really used, but assign it to zero to |
| prevent out of bounds memory accesses in other routines. */ |
| if (info->shared) |
| { |
| sec_symndx = _bfd_elf_section_from_bfd_section (abfd, sec); |
| |
| /* If we did not find a section symbol for this section, then |
| something went terribly wrong above. */ |
| if (sec_symndx == SHN_BAD) |
| return FALSE; |
| |
| if (sec_symndx < SHN_LORESERVE) |
| sec_symndx = hppa_info->section_syms[sec_symndx]; |
| else |
| sec_symndx = 0; |
| } |
| else |
| sec_symndx = 0; |
| |
| dlt = plt = stubs = NULL; |
| buf = NULL; |
| buf_len = 0; |
| |
| relend = relocs + sec->reloc_count; |
| for (rel = relocs; rel < relend; ++rel) |
| { |
| enum |
| { |
| NEED_DLT = 1, |
| NEED_PLT = 2, |
| NEED_STUB = 4, |
| NEED_OPD = 8, |
| NEED_DYNREL = 16, |
| }; |
| |
| struct elf_link_hash_entry *h = NULL; |
| unsigned long r_symndx = ELF64_R_SYM (rel->r_info); |
| struct elf64_hppa_dyn_hash_entry *dyn_h; |
| int need_entry; |
| const char *addr_name; |
| bfd_boolean maybe_dynamic; |
| int dynrel_type = R_PARISC_NONE; |
| static reloc_howto_type *howto; |
| |
| if (r_symndx >= symtab_hdr->sh_info) |
| { |
| /* We're dealing with a global symbol -- find its hash entry |
| and mark it as being referenced. */ |
| long indx = r_symndx - symtab_hdr->sh_info; |
| h = elf_sym_hashes (abfd)[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; |
| |
| h->ref_regular = 1; |
| } |
| |
| /* We can only get preliminary data on whether a symbol is |
| locally or externally defined, as not all of the input files |
| have yet been processed. Do something with what we know, as |
| this may help reduce memory usage and processing time later. */ |
| maybe_dynamic = FALSE; |
| if (h && ((info->shared |
| && (!info->symbolic |
| || info->unresolved_syms_in_shared_libs == RM_IGNORE)) |
| || !h->def_regular |
| || h->root.type == bfd_link_hash_defweak)) |
| maybe_dynamic = TRUE; |
| |
| howto = elf_hppa_howto_table + ELF64_R_TYPE (rel->r_info); |
| need_entry = 0; |
| switch (howto->type) |
| { |
| /* These are simple indirect references to symbols through the |
| DLT. We need to create a DLT entry for any symbols which |
| appears in a DLTIND relocation. */ |
| case R_PARISC_DLTIND21L: |
| case R_PARISC_DLTIND14R: |
| case R_PARISC_DLTIND14F: |
| case R_PARISC_DLTIND14WR: |
| case R_PARISC_DLTIND14DR: |
| need_entry = NEED_DLT; |
| break; |
| |
| /* ?!? These need a DLT entry. But I have no idea what to do with |
| the "link time TP value. */ |
| case R_PARISC_LTOFF_TP21L: |
| case R_PARISC_LTOFF_TP14R: |
| case R_PARISC_LTOFF_TP14F: |
| case R_PARISC_LTOFF_TP64: |
| case R_PARISC_LTOFF_TP14WR: |
| case R_PARISC_LTOFF_TP14DR: |
| case R_PARISC_LTOFF_TP16F: |
| case R_PARISC_LTOFF_TP16WF: |
| case R_PARISC_LTOFF_TP16DF: |
| need_entry = NEED_DLT; |
| break; |
| |
| /* These are function calls. Depending on their precise target we |
| may need to make a stub for them. The stub uses the PLT, so we |
| need to create PLT entries for these symbols too. */ |
| case R_PARISC_PCREL12F: |
| case R_PARISC_PCREL17F: |
| case R_PARISC_PCREL22F: |
| case R_PARISC_PCREL32: |
| case R_PARISC_PCREL64: |
| case R_PARISC_PCREL21L: |
| case R_PARISC_PCREL17R: |
| case R_PARISC_PCREL17C: |
| case R_PARISC_PCREL14R: |
| case R_PARISC_PCREL14F: |
| case R_PARISC_PCREL22C: |
| case R_PARISC_PCREL14WR: |
| case R_PARISC_PCREL14DR: |
| case R_PARISC_PCREL16F: |
| case R_PARISC_PCREL16WF: |
| case R_PARISC_PCREL16DF: |
| need_entry = (NEED_PLT | NEED_STUB); |
| break; |
| |
| case R_PARISC_PLTOFF21L: |
| case R_PARISC_PLTOFF14R: |
| case R_PARISC_PLTOFF14F: |
| case R_PARISC_PLTOFF14WR: |
| case R_PARISC_PLTOFF14DR: |
| case R_PARISC_PLTOFF16F: |
| case R_PARISC_PLTOFF16WF: |
| case R_PARISC_PLTOFF16DF: |
| need_entry = (NEED_PLT); |
| break; |
| |
| case R_PARISC_DIR64: |
| if (info->shared || maybe_dynamic) |
| need_entry = (NEED_DYNREL); |
| dynrel_type = R_PARISC_DIR64; |
| break; |
| |
| /* This is an indirect reference through the DLT to get the address |
| of a OPD descriptor. Thus we need to make a DLT entry that points |
| to an OPD entry. */ |
| case R_PARISC_LTOFF_FPTR21L: |
| case R_PARISC_LTOFF_FPTR14R: |
| case R_PARISC_LTOFF_FPTR14WR: |
| case R_PARISC_LTOFF_FPTR14DR: |
| case R_PARISC_LTOFF_FPTR32: |
| case R_PARISC_LTOFF_FPTR64: |
| case R_PARISC_LTOFF_FPTR16F: |
| case R_PARISC_LTOFF_FPTR16WF: |
| case R_PARISC_LTOFF_FPTR16DF: |
| if (info->shared || maybe_dynamic) |
| need_entry = (NEED_DLT | NEED_OPD); |
| else |
| need_entry = (NEED_DLT | NEED_OPD); |
| dynrel_type = R_PARISC_FPTR64; |
| break; |
| |
| /* This is a simple OPD entry. */ |
| case R_PARISC_FPTR64: |
| if (info->shared || maybe_dynamic) |
| need_entry = (NEED_OPD | NEED_DYNREL); |
| else |
| need_entry = (NEED_OPD); |
| dynrel_type = R_PARISC_FPTR64; |
| break; |
| |
| /* Add more cases as needed. */ |
| } |
| |
| if (!need_entry) |
| continue; |
| |
| /* Collect a canonical name for this address. */ |
| addr_name = get_dyn_name (abfd, h, rel, &buf, &buf_len); |
| |
| /* Collect the canonical entry data for this address. */ |
| dyn_h = elf64_hppa_dyn_hash_lookup (&hppa_info->dyn_hash_table, |
| addr_name, TRUE, TRUE); |
| BFD_ASSERT (dyn_h); |
| |
| /* Stash away enough information to be able to find this symbol |
| regardless of whether or not it is local or global. */ |
| dyn_h->h = h; |
| dyn_h->owner = abfd; |
| dyn_h->sym_indx = r_symndx; |
| |
| /* ?!? We may need to do some error checking in here. */ |
| /* Create what's needed. */ |
| if (need_entry & NEED_DLT) |
| { |
| if (! hppa_info->dlt_sec |
| && ! get_dlt (abfd, info, hppa_info)) |
| goto err_out; |
| dyn_h->want_dlt = 1; |
| } |
| |
| if (need_entry & NEED_PLT) |
| { |
| if (! hppa_info->plt_sec |
| && ! get_plt (abfd, info, hppa_info)) |
| goto err_out; |
| dyn_h->want_plt = 1; |
| } |
| |
| if (need_entry & NEED_STUB) |
| { |
| if (! hppa_info->stub_sec |
| && ! get_stub (abfd, info, hppa_info)) |
| goto err_out; |
| dyn_h->want_stub = 1; |
| } |
| |
| if (need_entry & NEED_OPD) |
| { |
| if (! hppa_info->opd_sec |
| && ! get_opd (abfd, info, hppa_info)) |
| goto err_out; |
| |
| dyn_h->want_opd = 1; |
| |
| /* FPTRs are not allocated by the dynamic linker for PA64, though |
| it is possible that will change in the future. */ |
| |
| /* This could be a local function that had its address taken, in |
| which case H will be NULL. */ |
| if (h) |
| h->needs_plt = 1; |
| } |
| |
| /* Add a new dynamic relocation to the chain of dynamic |
| relocations for this symbol. */ |
| if ((need_entry & NEED_DYNREL) && (sec->flags & SEC_ALLOC)) |
| { |
| if (! hppa_info->other_rel_sec |
| && ! get_reloc_section (abfd, hppa_info, sec)) |
| goto err_out; |
| |
| if (!count_dyn_reloc (abfd, dyn_h, dynrel_type, sec, |
| sec_symndx, rel->r_offset, rel->r_addend)) |
| goto err_out; |
| |
| /* If we are building a shared library and we just recorded |
| a dynamic R_PARISC_FPTR64 relocation, then make sure the |
| section symbol for this section ends up in the dynamic |
| symbol table. */ |
| if (info->shared && dynrel_type == R_PARISC_FPTR64 |
| && ! (bfd_elf_link_record_local_dynamic_symbol |
| (info, abfd, sec_symndx))) |
| return FALSE; |
| } |
| } |
| |
| if (buf) |
| free (buf); |
| return TRUE; |
| |
| err_out: |
| if (buf) |
| free (buf); |
| return FALSE; |
| } |
| |
| struct elf64_hppa_allocate_data |
| { |
| struct bfd_link_info *info; |
| bfd_size_type ofs; |
| }; |
| |
| /* Should we do dynamic things to this symbol? */ |
| |
| static bfd_boolean |
| elf64_hppa_dynamic_symbol_p (h, info) |
| struct elf_link_hash_entry *h; |
| struct bfd_link_info *info; |
| { |
| /* ??? What, if anything, needs to happen wrt STV_PROTECTED symbols |
| and relocations that retrieve a function descriptor? Assume the |
| worst for now. */ |
| if (_bfd_elf_dynamic_symbol_p (h, info, 1)) |
| { |
| /* ??? Why is this here and not elsewhere is_local_label_name. */ |
| if (h->root.root.string[0] == '$' && h->root.root.string[1] == '$') |
| return FALSE; |
| |
| return TRUE; |
| } |
| else |
| return FALSE; |
| } |
| |
| /* Mark all functions exported by this file so that we can later allocate |
| entries in .opd for them. */ |
| |
| static bfd_boolean |
| elf64_hppa_mark_exported_functions (h, data) |
| struct elf_link_hash_entry *h; |
| PTR data; |
| { |
| struct bfd_link_info *info = (struct bfd_link_info *)data; |
| struct elf64_hppa_link_hash_table *hppa_info; |
| |
| hppa_info = elf64_hppa_hash_table (info); |
| |
| if (h->root.type == bfd_link_hash_warning) |
| h = (struct elf_link_hash_entry *) h->root.u.i.link; |
| |
| if (h |
| && (h->root.type == bfd_link_hash_defined |
| || h->root.type == bfd_link_hash_defweak) |
| && h->root.u.def.section->output_section != NULL |
| && h->type == STT_FUNC) |
| { |
| struct elf64_hppa_dyn_hash_entry *dyn_h; |
| |
| /* Add this symbol to the PA64 linker hash table. */ |
| dyn_h = elf64_hppa_dyn_hash_lookup (&hppa_info->dyn_hash_table, |
| h->root.root.string, TRUE, TRUE); |
| BFD_ASSERT (dyn_h); |
| dyn_h->h = h; |
| |
| if (! hppa_info->opd_sec |
| && ! get_opd (hppa_info->root.dynobj, info, hppa_info)) |
| return FALSE; |
| |
| dyn_h->want_opd = 1; |
| /* Put a flag here for output_symbol_hook. */ |
| dyn_h->st_shndx = -1; |
| h->needs_plt = 1; |
| } |
| |
| return TRUE; |
| } |
| |
| /* Allocate space for a DLT entry. */ |
| |
| static bfd_boolean |
| allocate_global_data_dlt (dyn_h, data) |
| struct elf64_hppa_dyn_hash_entry *dyn_h; |
| PTR data; |
| { |
| struct elf64_hppa_allocate_data *x = (struct elf64_hppa_allocate_data *)data; |
| |
| if (dyn_h->want_dlt) |
| { |
| struct elf_link_hash_entry *h = dyn_h->h; |
| |
| if (x->info->shared) |
| { |
| /* Possibly add the symbol to the local dynamic symbol |
| table since we might need to create a dynamic relocation |
| against it. */ |
| if (! h |
| || (h->dynindx == -1 && h->type != STT_PARISC_MILLI)) |
| { |
| bfd *owner; |
| owner = (h ? h->root.u.def.section->owner : dyn_h->owner); |
| |
| if (! (bfd_elf_link_record_local_dynamic_symbol |
| (x->info, owner, dyn_h->sym_indx))) |
| return FALSE; |
| } |
| } |
| |
| dyn_h->dlt_offset = x->ofs; |
| x->ofs += DLT_ENTRY_SIZE; |
| } |
| return TRUE; |
| } |
| |
| /* Allocate space for a DLT.PLT entry. */ |
| |
| static bfd_boolean |
| allocate_global_data_plt (dyn_h, data) |
| struct elf64_hppa_dyn_hash_entry *dyn_h; |
| PTR data; |
| { |
| struct elf64_hppa_allocate_data *x = (struct elf64_hppa_allocate_data *)data; |
| |
| if (dyn_h->want_plt |
| && elf64_hppa_dynamic_symbol_p (dyn_h->h, x->info) |
| && !((dyn_h->h->root.type == bfd_link_hash_defined |
| || dyn_h->h->root.type == bfd_link_hash_defweak) |
| && dyn_h->h->root.u.def.section->output_section != NULL)) |
| { |
| dyn_h->plt_offset = x->ofs; |
| x->ofs += PLT_ENTRY_SIZE; |
| if (dyn_h->plt_offset < 0x2000) |
| elf64_hppa_hash_table (x->info)->gp_offset = dyn_h->plt_offset; |
| } |
| else |
| dyn_h->want_plt = 0; |
| |
| return TRUE; |
| } |
| |
| /* Allocate space for a STUB entry. */ |
| |
| static bfd_boolean |
| allocate_global_data_stub (dyn_h, data) |
| struct elf64_hppa_dyn_hash_entry *dyn_h; |
| PTR data; |
| { |
| struct elf64_hppa_allocate_data *x = (struct elf64_hppa_allocate_data *)data; |
| |
| if (dyn_h->want_stub |
| && elf64_hppa_dynamic_symbol_p (dyn_h->h, x->info) |
| && !((dyn_h->h->root.type == bfd_link_hash_defined |
| || dyn_h->h->root.type == bfd_link_hash_defweak) |
| && dyn_h->h->root.u.def.section->output_section != NULL)) |
| { |
| dyn_h->stub_offset = x->ofs; |
| x->ofs += sizeof (plt_stub); |
| } |
| else |
| dyn_h->want_stub = 0; |
| return TRUE; |
| } |
| |
| /* Allocate space for a FPTR entry. */ |
| |
| static bfd_boolean |
| allocate_global_data_opd (dyn_h, data) |
| struct elf64_hppa_dyn_hash_entry *dyn_h; |
| PTR data; |
| { |
| struct elf64_hppa_allocate_data *x = (struct elf64_hppa_allocate_data *)data; |
| |
| if (dyn_h->want_opd) |
| { |
| struct elf_link_hash_entry *h = dyn_h->h; |
| |
| if (h) |
| 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; |
| |
| /* We never need an opd entry for a symbol which is not |
| defined by this output file. */ |
| if (h && (h->root.type == bfd_link_hash_undefined |
| || h->root.type == bfd_link_hash_undefweak |
| || h->root.u.def.section->output_section == NULL)) |
| dyn_h->want_opd = 0; |
| |
| /* If we are creating a shared library, took the address of a local |
| function or might export this function from this object file, then |
| we have to create an opd descriptor. */ |
| else if (x->info->shared |
| || h == NULL |
| || (h->dynindx == -1 && h->type != STT_PARISC_MILLI) |
| || (h->root.type == bfd_link_hash_defined |
| || h->root.type == bfd_link_hash_defweak)) |
| { |
| /* If we are creating a shared library, then we will have to |
| create a runtime relocation for the symbol to properly |
| initialize the .opd entry. Make sure the symbol gets |
| added to the dynamic symbol table. */ |
| if (x->info->shared |
| && (h == NULL || (h->dynindx == -1))) |
| { |
| bfd *owner; |
| owner = (h ? h->root.u.def.section->owner : dyn_h->owner); |
| |
| if (!bfd_elf_link_record_local_dynamic_symbol |
| (x->info, owner, dyn_h->sym_indx)) |
| return FALSE; |
| } |
| |
| /* This may not be necessary or desirable anymore now that |
| we have some support for dealing with section symbols |
| in dynamic relocs. But name munging does make the result |
| much easier to debug. ie, the EPLT reloc will reference |
| a symbol like .foobar, instead of .text + offset. */ |
| if (x->info->shared && h) |
| { |
| char *new_name; |
| struct elf_link_hash_entry *nh; |
| |
| new_name = alloca (strlen (h->root.root.string) + 2); |
| new_name[0] = '.'; |
| strcpy (new_name + 1, h->root.root.string); |
| |
| nh = elf_link_hash_lookup (elf_hash_table (x->info), |
| new_name, TRUE, TRUE, TRUE); |
| |
| nh->root.type = h->root.type; |
| nh->root.u.def.value = h->root.u.def.value; |
| nh->root.u.def.section = h->root.u.def.section; |
| |
| if (! bfd_elf_link_record_dynamic_symbol (x->info, nh)) |
| return FALSE; |
| |
| } |
| dyn_h->opd_offset = x->ofs; |
| x->ofs += OPD_ENTRY_SIZE; |
| } |
| |
| /* Otherwise we do not need an opd entry. */ |
| else |
| dyn_h->want_opd = 0; |
| } |
| return TRUE; |
| } |
| |
| /* HP requires the EI_OSABI field to be filled in. The assignment to |
| EI_ABIVERSION may not be strictly necessary. */ |
| |
| static void |
| elf64_hppa_post_process_headers (abfd, link_info) |
| bfd * abfd; |
| struct bfd_link_info * link_info ATTRIBUTE_UNUSED; |
| { |
| Elf_Internal_Ehdr * i_ehdrp; |
| |
| i_ehdrp = elf_elfheader (abfd); |
| |
| i_ehdrp->e_ident[EI_OSABI] = get_elf_backend_data (abfd)->elf_osabi; |
| i_ehdrp->e_ident[EI_ABIVERSION] = 1; |
| } |
| |
| /* Create function descriptor section (.opd). This section is called .opd |
| because it contains "official procedure descriptors". The "official" |
| refers to the fact that these descriptors are used when taking the address |
| of a procedure, thus ensuring a unique address for each procedure. */ |
| |
| static bfd_boolean |
| get_opd (abfd, info, hppa_info) |
| bfd *abfd; |
| struct bfd_link_info *info ATTRIBUTE_UNUSED; |
| struct elf64_hppa_link_hash_table *hppa_info; |
| { |
| asection *opd; |
| bfd *dynobj; |
| |
| opd = hppa_info->opd_sec; |
| if (!opd) |
| { |
| dynobj = hppa_info->root.dynobj; |
| if (!dynobj) |
| hppa_info->root.dynobj = dynobj = abfd; |
| |
| opd = bfd_make_section_with_flags (dynobj, ".opd", |
| (SEC_ALLOC |
| | SEC_LOAD |
| | SEC_HAS_CONTENTS |
| | SEC_IN_MEMORY |
| | SEC_LINKER_CREATED)); |
| if (!opd |
| || !bfd_set_section_alignment (abfd, opd, 3)) |
| { |
| BFD_ASSERT (0); |
| return FALSE; |
| } |
| |
| hppa_info->opd_sec = opd; |
| } |
| |
| return TRUE; |
| } |
| |
| /* Create the PLT section. */ |
| |
| static bfd_boolean |
| get_plt (abfd, info, hppa_info) |
| bfd *abfd; |
| struct bfd_link_info *info ATTRIBUTE_UNUSED; |
| struct elf64_hppa_link_hash_table *hppa_info; |
| { |
| asection *plt; |
| bfd *dynobj; |
| |
| plt = hppa_info->plt_sec; |
| if (!plt) |
| { |
| dynobj = hppa_info->root.dynobj; |
| if (!dynobj) |
| hppa_info->root.dynobj = dynobj = abfd; |
| |
| plt = bfd_make_section_with_flags (dynobj, ".plt", |
| (SEC_ALLOC |
| | SEC_LOAD |
| | SEC_HAS_CONTENTS |
| | SEC_IN_MEMORY |
| | SEC_LINKER_CREATED)); |
| if (!plt |
| || !bfd_set_section_alignment (abfd, plt, 3)) |
| { |
| BFD_ASSERT (0); |
| return FALSE; |
| } |
| |
| hppa_info->plt_sec = plt; |
| } |
| |
| return TRUE; |
| } |
| |
| /* Create the DLT section. */ |
| |
| static bfd_boolean |
| get_dlt (abfd, info, hppa_info) |
| bfd *abfd; |
| struct bfd_link_info *info ATTRIBUTE_UNUSED; |
| struct elf64_hppa_link_hash_table *hppa_info; |
| { |
| asection *dlt; |
| bfd *dynobj; |
| |
| dlt = hppa_info->dlt_sec; |
| if (!dlt) |
| { |
| dynobj = hppa_info->root.dynobj; |
| if (!dynobj) |
| hppa_info->root.dynobj = dynobj = abfd; |
| |
| dlt = bfd_make_section_with_flags (dynobj, ".dlt", |
| (SEC_ALLOC |
| | SEC_LOAD |
| | SEC_HAS_CONTENTS |
| | SEC_IN_MEMORY |
| | SEC_LINKER_CREATED)); |
| if (!dlt |
| || !bfd_set_section_alignment (abfd, dlt, 3)) |
| { |
| BFD_ASSERT (0); |
| return FALSE; |
| } |
| |
| hppa_info->dlt_sec = dlt; |
| } |
| |
| return TRUE; |
| } |
| |
| /* Create the stubs section. */ |
| |
| static bfd_boolean |
| get_stub (abfd, info, hppa_info) |
| bfd *abfd; |
| struct bfd_link_info *info ATTRIBUTE_UNUSED; |
| struct elf64_hppa_link_hash_table *hppa_info; |
| { |
| asection *stub; |
| bfd *dynobj; |
| |
| stub = hppa_info->stub_sec; |
| if (!stub) |
| { |
| dynobj = hppa_info->root.dynobj; |
| if (!dynobj) |
| hppa_info->root.dynobj = dynobj = abfd; |
| |
| stub = bfd_make_section_with_flags (dynobj, ".stub", |
| (SEC_ALLOC | SEC_LOAD |
| | SEC_HAS_CONTENTS |
| | SEC_IN_MEMORY |
| | SEC_READONLY |
| | SEC_LINKER_CREATED)); |
| if (!stub |
| || !bfd_set_section_alignment (abfd, stub, 3)) |
| { |
| BFD_ASSERT (0); |
| return FALSE; |
| } |
| |
| hppa_info->stub_sec = stub; |
| } |
| |
| return TRUE; |
| } |
| |
| /* Create sections necessary for dynamic linking. This is only a rough |
| cut and will likely change as we learn more about the somewhat |
| unusual dynamic linking scheme HP uses. |
| |
| .stub: |
| Contains code to implement cross-space calls. The first time one |
| of the stubs is used it will call into the dynamic linker, later |
| calls will go straight to the target. |
| |
| The only stub we support right now looks like |
| |
| ldd OFFSET(%dp),%r1 |
| bve %r0(%r1) |
| ldd OFFSET+8(%dp),%dp |
| |
| Other stubs may be needed in the future. We may want the remove |
| the break/nop instruction. It is only used right now to keep the |
| offset of a .plt entry and a .stub entry in sync. |
| |
| .dlt: |
| This is what most people call the .got. HP used a different name. |
| Losers. |
| |
| .rela.dlt: |
| Relocations for the DLT. |
| |
| .plt: |
| Function pointers as address,gp pairs. |
| |
| .rela.plt: |
| Should contain dynamic IPLT (and EPLT?) relocations. |
| |
| .opd: |
| FPTRS |
| |
| .rela.opd: |
| EPLT relocations for symbols exported from shared libraries. */ |
| |
| static bfd_boolean |
| elf64_hppa_create_dynamic_sections (abfd, info) |
| bfd *abfd; |
| struct bfd_link_info *info; |
| { |
| asection *s; |
| |
| if (! get_stub (abfd, info, elf64_hppa_hash_table (info))) |
| return FALSE; |
| |
| if (! get_dlt (abfd, info, elf64_hppa_hash_table (info))) |
| return FALSE; |
| |
| if (! get_plt (abfd, info, elf64_hppa_hash_table (info))) |
| return FALSE; |
| |
| if (! get_opd (abfd, info, elf64_hppa_hash_table (info))) |
| return FALSE; |
| |
| s = bfd_make_section_with_flags (abfd, ".rela.dlt", |
| (SEC_ALLOC | SEC_LOAD |
| | SEC_HAS_CONTENTS |
| | SEC_IN_MEMORY |
| | SEC_READONLY |
| | SEC_LINKER_CREATED)); |
| if (s == NULL |
| || !bfd_set_section_alignment (abfd, s, 3)) |
| return FALSE; |
| elf64_hppa_hash_table (info)->dlt_rel_sec = s; |
| |
| s = bfd_make_section_with_flags (abfd, ".rela.plt", |
| (SEC_ALLOC | SEC_LOAD |
| | SEC_HAS_CONTENTS |
| | SEC_IN_MEMORY |
| | SEC_READONLY |
| | SEC_LINKER_CREATED)); |
| if (s == NULL |
| || !bfd_set_section_alignment (abfd, s, 3)) |
| return FALSE; |
| elf64_hppa_hash_table (info)->plt_rel_sec = s; |
| |
| s = bfd_make_section_with_flags (abfd, ".rela.data", |
| (SEC_ALLOC | SEC_LOAD |
| | SEC_HAS_CONTENTS |
| | SEC_IN_MEMORY |
| | SEC_READONLY |
| | SEC_LINKER_CREATED)); |
| if (s == NULL |
| || !bfd_set_section_alignment (abfd, s, 3)) |
| return FALSE; |
| elf64_hppa_hash_table (info)->other_rel_sec = s; |
| |
| s = bfd_make_section_with_flags (abfd, ".rela.opd", |
| (SEC_ALLOC | SEC_LOAD |
| | SEC_HAS_CONTENTS |
| | SEC_IN_MEMORY |
| | SEC_READONLY |
| | SEC_LINKER_CREATED)); |
| if (s == NULL |
| || !bfd_set_section_alignment (abfd, s, 3)) |
| return FALSE; |
| elf64_hppa_hash_table (info)->opd_rel_sec = s; |
| |
| return TRUE; |
| } |
| |
| /* Allocate dynamic relocations for those symbols that turned out |
| to be dynamic. */ |
| |
| static bfd_boolean |
| allocate_dynrel_entries (dyn_h, data) |
| struct elf64_hppa_dyn_hash_entry *dyn_h; |
| PTR data; |
| { |
| struct elf64_hppa_allocate_data *x = (struct elf64_hppa_allocate_data *)data; |
| struct elf64_hppa_link_hash_table *hppa_info; |
| struct elf64_hppa_dyn_reloc_entry *rent; |
| bfd_boolean dynamic_symbol, shared; |
| |
| hppa_info = elf64_hppa_hash_table (x->info); |
| dynamic_symbol = elf64_hppa_dynamic_symbol_p (dyn_h->h, x->info); |
| shared = x->info->shared; |
| |
| /* We may need to allocate relocations for a non-dynamic symbol |
| when creating a shared library. */ |
| if (!dynamic_symbol && !shared) |
| return TRUE; |
| |
| /* Take care of the normal data relocations. */ |
| |
| for (rent = dyn_h->reloc_entries; rent; rent = rent->next) |
| { |
| /* Allocate one iff we are building a shared library, the relocation |
| isn't a R_PARISC_FPTR64, or we don't want an opd entry. */ |
| if (!shared && rent->type == R_PARISC_FPTR64 && dyn_h->want_opd) |
| continue; |
| |
| hppa_info->other_rel_sec->size += sizeof (Elf64_External_Rela); |
| |
| /* Make sure this symbol gets into the dynamic symbol table if it is |
| not already recorded. ?!? This should not be in the loop since |
| the symbol need only be added once. */ |
| if (dyn_h->h == 0 |
| || (dyn_h->h->dynindx == -1 && dyn_h->h->type != STT_PARISC_MILLI)) |
| if (!bfd_elf_link_record_local_dynamic_symbol |
| (x->info, rent->sec->owner, dyn_h->sym_indx)) |
| return FALSE; |
| } |
| |
| /* Take care of the GOT and PLT relocations. */ |
| |
| if ((dynamic_symbol || shared) && dyn_h->want_dlt) |
| hppa_info->dlt_rel_sec->size += sizeof (Elf64_External_Rela); |
| |
| /* If we are building a shared library, then every symbol that has an |
| opd entry will need an EPLT relocation to relocate the symbol's address |
| and __gp value based on the runtime load address. */ |
| if (shared && dyn_h->want_opd) |
| hppa_info->opd_rel_sec->size += sizeof (Elf64_External_Rela); |
| |
| if (dyn_h->want_plt && dynamic_symbol) |
| { |
| bfd_size_type t = 0; |
| |
| /* Dynamic symbols get one IPLT relocation. Local symbols in |
| shared libraries get two REL relocations. Local symbols in |
| main applications get nothing. */ |
| if (dynamic_symbol) |
| t = sizeof (Elf64_External_Rela); |
| else if (shared) |
| t = 2 * sizeof (Elf64_External_Rela); |
| |
| hppa_info->plt_rel_sec->size += t; |
| } |
| |
| return TRUE; |
| } |
| |
| /* Adjust a symbol defined by a dynamic object and referenced by a |
| regular object. */ |
| |
| static bfd_boolean |
| elf64_hppa_adjust_dynamic_symbol (info, h) |
| struct bfd_link_info *info ATTRIBUTE_UNUSED; |
| struct elf_link_hash_entry *h; |
| { |
| /* ??? Undefined symbols with PLT entries should be re-defined |
| to be the PLT entry. */ |
| |
| /* If this is a weak symbol, and there is a real definition, the |
| processor independent code will have arranged for us to see the |
| real definition first, and we can just use the same value. */ |
| if (h->u.weakdef != NULL) |
| { |
| BFD_ASSERT (h->u.weakdef->root.type == bfd_link_hash_defined |
| || h->u.weakdef->root.type == bfd_link_hash_defweak); |
| h->root.u.def.section = h->u.weakdef->root.u.def.section; |
| h->root.u.def.value = h->u.weakdef->root.u.def.value; |
| return TRUE; |
| } |
| |
| /* If this is a reference to a symbol defined by a dynamic object which |
| is not a function, we might allocate the symbol in our .dynbss section |
| and allocate a COPY dynamic relocation. |
| |
| But PA64 code is canonically PIC, so as a rule we can avoid this sort |
| of hackery. */ |
| |
| return TRUE; |
| } |
| |
| /* This function is called via elf_link_hash_traverse to mark millicode |
| symbols with a dynindx of -1 and to remove the string table reference |
| from the dynamic symbol table. If the symbol is not a millicode symbol, |
| elf64_hppa_mark_exported_functions is called. */ |
| |
| static bfd_boolean |
| elf64_hppa_mark_milli_and_exported_functions (h, data) |
| struct elf_link_hash_entry *h; |
| PTR data; |
| { |
| struct bfd_link_info *info = (struct bfd_link_info *)data; |
| struct elf_link_hash_entry *elf = h; |
| |
| if (elf->root.type == bfd_link_hash_warning) |
| elf = (struct elf_link_hash_entry *) elf->root.u.i.link; |
| |
| if (elf->type == STT_PARISC_MILLI) |
| { |
| if (elf->dynindx != -1) |
| { |
| elf->dynindx = -1; |
| _bfd_elf_strtab_delref (elf_hash_table (info)->dynstr, |
| elf->dynstr_index); |
| } |
| return TRUE; |
| } |
| |
| return elf64_hppa_mark_exported_functions (h, data); |
| } |
| |
| /* Set the final sizes of the dynamic sections and allocate memory for |
| the contents of our special sections. */ |
| |
| static bfd_boolean |
| elf64_hppa_size_dynamic_sections (output_bfd, info) |
| bfd *output_bfd; |
| struct bfd_link_info *info; |
| { |
| bfd *dynobj; |
| asection *s; |
| bfd_boolean plt; |
| bfd_boolean relocs; |
| bfd_boolean reltext; |
| struct elf64_hppa_allocate_data data; |
| struct elf64_hppa_link_hash_table *hppa_info; |
| |
| hppa_info = elf64_hppa_hash_table (info); |
| |
| dynobj = elf_hash_table (info)->dynobj; |
| BFD_ASSERT (dynobj != NULL); |
| |
| /* Mark each function this program exports so that we will allocate |
| space in the .opd section for each function's FPTR. If we are |
| creating dynamic sections, change the dynamic index of millicode |
| symbols to -1 and remove them from the string table for .dynstr. |
| |
| We have to traverse the main linker hash table since we have to |
| find functions which may not have been mentioned in any relocs. */ |
| elf_link_hash_traverse (elf_hash_table (info), |
| (elf_hash_table (info)->dynamic_sections_created |
| ? elf64_hppa_mark_milli_and_exported_functions |
| : elf64_hppa_mark_exported_functions), |
| info); |
| |
| if (elf_hash_table (info)->dynamic_sections_created) |
| { |
| /* Set the contents of the .interp section to the interpreter. */ |
| if (info->executable) |
| { |
| s = bfd_get_section_by_name (dynobj, ".interp"); |
| BFD_ASSERT (s != NULL); |
| s->size = sizeof ELF_DYNAMIC_INTERPRETER; |
| s->contents = (unsigned char *) ELF_DYNAMIC_INTERPRETER; |
| } |
| } |
| else |
| { |
| /* We may have created entries in the .rela.got section. |
| However, if we are not creating the dynamic sections, we will |
| not actually use these entries. Reset the size of .rela.dlt, |
| which will cause it to get stripped from the output file |
| below. */ |
| s = bfd_get_section_by_name (dynobj, ".rela.dlt"); |
| if (s != NULL) |
| s->size = 0; |
| } |
| |
| /* Allocate the GOT entries. */ |
| |
| data.info = info; |
| if (elf64_hppa_hash_table (info)->dlt_sec) |
| { |
| data.ofs = 0x0; |
| elf64_hppa_dyn_hash_traverse (&hppa_info->dyn_hash_table, |
| allocate_global_data_dlt, &data); |
| hppa_info->dlt_sec->size = data.ofs; |
| |
| data.ofs = 0x0; |
| elf64_hppa_dyn_hash_traverse (&hppa_info->dyn_hash_table, |
| allocate_global_data_plt, &data); |
| hppa_info->plt_sec->size = data.ofs; |
| |
| data.ofs = 0x0; |
| elf64_hppa_dyn_hash_traverse (&hppa_info->dyn_hash_table, |
| allocate_global_data_stub, &data); |
| hppa_info->stub_sec->size = data.ofs; |
| } |
| |
| /* Allocate space for entries in the .opd section. */ |
| if (elf64_hppa_hash_table (info)->opd_sec) |
| { |
| data.ofs = 0; |
| elf64_hppa_dyn_hash_traverse (&hppa_info->dyn_hash_table, |
| allocate_global_data_opd, &data); |
| hppa_info->opd_sec->size = data.ofs; |
| } |
| |
| /* Now allocate space for dynamic relocations, if necessary. */ |
| if (hppa_info->root.dynamic_sections_created) |
| elf64_hppa_dyn_hash_traverse (&hppa_info->dyn_hash_table, |
| allocate_dynrel_entries, &data); |
| |
| /* The sizes of all the sections are set. Allocate memory for them. */ |
| plt = FALSE; |
| relocs = FALSE; |
| reltext = FALSE; |
| for (s = dynobj->sections; s != NULL; s = s->next) |
| { |
| const char *name; |
| |
| if ((s->flags & SEC_LINKER_CREATED) == 0) |
| continue; |
| |
| /* It's OK to base decisions on the section name, because none |
| of the dynobj section names depend upon the input files. */ |
| name = bfd_get_section_name (dynobj, s); |
| |
| if (strcmp (name, ".plt") == 0) |
| { |
| /* Remember whether there is a PLT. */ |
| plt = s->size != 0; |
| } |
| else if (strcmp (name, ".opd") == 0 |
| || CONST_STRNEQ (name, ".dlt") |
| || strcmp (name, ".stub") == 0 |
| || strcmp (name, ".got") == 0) |
| { |
| /* Strip this section if we don't need it; see the comment below. */ |
| } |
| else if (CONST_STRNEQ (name, ".rela")) |
| { |
| if (s->size != 0) |
| { |
| asection *target; |
| |
| /* Remember whether there are any reloc sections other |
| than .rela.plt. */ |
| if (strcmp (name, ".rela.plt") != 0) |
| { |
| const char *outname; |
| |
| relocs = TRUE; |
| |
| /* If this relocation section applies to a read only |
| section, then we probably need a DT_TEXTREL |
| entry. The entries in the .rela.plt section |
| really apply to the .got section, which we |
| created ourselves and so know is not readonly. */ |
| outname = bfd_get_section_name (output_bfd, |
| s->output_section); |
| target = bfd_get_section_by_name (output_bfd, outname + 4); |
| if (target != NULL |
| && (target->flags & SEC_READONLY) != 0 |
| && (target->flags & SEC_ALLOC) != 0) |
| reltext = TRUE; |
| } |
| |
| /* We use the reloc_count field as a counter if we need |
| to copy relocs into the output file. */ |
| s->reloc_count = 0; |
| } |
| } |
| else |
| { |
| /* It's not one of our sections, so don't allocate space. */ |
| continue; |
| } |
| |
| if (s->size == 0) |
| { |
| /* If we don't need this section, strip it from the |
| output file. This is mostly to handle .rela.bss and |
| .rela.plt. We must create both sections in |
| create_dynamic_sections, because they must be created |
| before the linker maps input sections to output |
| sections. The linker does that before |
| adjust_dynamic_symbol is called, and it is that |
| function which decides whether anything needs to go |
| into these sections. */ |
| s->flags |= SEC_EXCLUDE; |
| continue; |
| } |
| |
| if ((s->flags & SEC_HAS_CONTENTS) == 0) |
| continue; |
| |
| /* Allocate memory for the section contents if it has not |
| been allocated already. We use bfd_zalloc here in case |
| unused entries are not reclaimed before the section's |
| contents are written out. This should not happen, but this |
| way if it does, we get a R_PARISC_NONE reloc instead of |
| garbage. */ |
| if (s->contents == NULL) |
| { |
| s->contents = (bfd_byte *) bfd_zalloc (dynobj, s->size); |
| if (s->contents == NULL) |
| return FALSE; |
| } |
| } |
| |
| if (elf_hash_table (info)->dynamic_sections_created) |
| { |
| /* Always create a DT_PLTGOT. It actually has nothing to do with |
| the PLT, it is how we communicate the __gp value of a load |
| module to the dynamic linker. */ |
| #define add_dynamic_entry(TAG, VAL) \ |
| _bfd_elf_add_dynamic_entry (info, TAG, VAL) |
| |
| if (!add_dynamic_entry (DT_HP_DLD_FLAGS, 0) |
| || !add_dynamic_entry (DT_PLTGOT, 0)) |
| return FALSE; |
| |
| /* Add some entries to the .dynamic section. We fill in the |
| values later, in elf64_hppa_finish_dynamic_sections, but we |
| must add the entries now so that we get the correct size for |
| the .dynamic section. The DT_DEBUG entry is filled in by the |
| dynamic linker and used by the debugger. */ |
| if (! info->shared) |
| { |
| if (!add_dynamic_entry (DT_DEBUG, 0) |
| || !add_dynamic_entry (DT_HP_DLD_HOOK, 0) |
| || !add_dynamic_entry (DT_HP_LOAD_MAP, 0)) |
| return FALSE; |
| } |
| |
| /* Force DT_FLAGS to always be set. |
| Required by HPUX 11.00 patch PHSS_26559. */ |
| if (!add_dynamic_entry (DT_FLAGS, (info)->flags)) |
| return FALSE; |
| |
| if (plt) |
| { |
| if (!add_dynamic_entry (DT_PLTRELSZ, 0) |
| || !add_dynamic_entry (DT_PLTREL, DT_RELA) |
| || !add_dynamic_entry (DT_JMPREL, 0)) |
| return FALSE; |
| } |
| |
| if (relocs) |
| { |
| if (!add_dynamic_entry (DT_RELA, 0) |
| || !add_dynamic_entry (DT_RELASZ, 0) |
| || !add_dynamic_entry (DT_RELAENT, sizeof (Elf64_External_Rela))) |
| return FALSE; |
| } |
| |
| if (reltext) |
| { |
| if (!add_dynamic_entry (DT_TEXTREL, 0)) |
| return FALSE; |
| info->flags |= DF_TEXTREL; |
| } |
| } |
| #undef add_dynamic_entry |
| |
| return TRUE; |
| } |
| |
| /* Called after we have output the symbol into the dynamic symbol |
| table, but before we output the symbol into the normal symbol |
| table. |
| |
| For some symbols we had to change their address when outputting |
| the dynamic symbol table. We undo that change here so that |
| the symbols have their expected value in the normal symbol |
| table. Ick. */ |
| |
| static bfd_boolean |
| elf64_hppa_link_output_symbol_hook (info, name, sym, input_sec, h) |
| struct bfd_link_info *info; |
| const char *name; |
| Elf_Internal_Sym *sym; |
| asection *input_sec ATTRIBUTE_UNUSED; |
| struct elf_link_hash_entry *h; |
| { |
| struct elf64_hppa_link_hash_table *hppa_info; |
| struct elf64_hppa_dyn_hash_entry *dyn_h; |
| |
| /* We may be called with the file symbol or section symbols. |
| They never need munging, so it is safe to ignore them. */ |
| if (!name) |
| return TRUE; |
| |
| /* Get the PA dyn_symbol (if any) associated with NAME. */ |
| hppa_info = elf64_hppa_hash_table (info); |
| dyn_h = elf64_hppa_dyn_hash_lookup (&hppa_info->dyn_hash_table, |
| name, FALSE, FALSE); |
| if (!dyn_h || dyn_h->h != h) |
| return TRUE; |
| |
| /* Function symbols for which we created .opd entries *may* have been |
| munged by finish_dynamic_symbol and have to be un-munged here. |
| |
| Note that finish_dynamic_symbol sometimes turns dynamic symbols |
| into non-dynamic ones, so we initialize st_shndx to -1 in |
| mark_exported_functions and check to see if it was overwritten |
| here instead of just checking dyn_h->h->dynindx. */ |
| if (dyn_h->want_opd && dyn_h->st_shndx != -1) |
| { |
| /* Restore the saved value and section index. */ |
| sym->st_value = dyn_h->st_value; |
| sym->st_shndx = dyn_h->st_shndx; |
| } |
| |
| return TRUE; |
| } |
| |
| /* Finish up dynamic symbol handling. We set the contents of various |
| dynamic sections here. */ |
| |
| static bfd_boolean |
| elf64_hppa_finish_dynamic_symbol (output_bfd, info, h, sym) |
| bfd *output_bfd; |
| struct bfd_link_info *info; |
| struct elf_link_hash_entry *h; |
| Elf_Internal_Sym *sym; |
| { |
| asection *stub, *splt, *sdlt, *sopd, *spltrel, *sdltrel; |
| struct elf64_hppa_link_hash_table *hppa_info; |
| struct elf64_hppa_dyn_hash_entry *dyn_h; |
| |
| hppa_info = elf64_hppa_hash_table (info); |
| dyn_h = elf64_hppa_dyn_hash_lookup (&hppa_info->dyn_hash_table, |
| h->root.root.string, FALSE, FALSE); |
| |
| stub = hppa_info->stub_sec; |
| splt = hppa_info->plt_sec; |
| sdlt = hppa_info->dlt_sec; |
| sopd = hppa_info->opd_sec; |
| spltrel = hppa_info->plt_rel_sec; |
| sdltrel = hppa_info->dlt_rel_sec; |
| |
| /* Incredible. It is actually necessary to NOT use the symbol's real |
| value when building the dynamic symbol table for a shared library. |
| At least for symbols that refer to functions. |
| |
| We will store a new value and section index into the symbol long |
| enough to output it into the dynamic symbol table, then we restore |
| the original values (in elf64_hppa_link_output_symbol_hook). */ |
| if (dyn_h && dyn_h->want_opd) |
| { |
| BFD_ASSERT (sopd != NULL); |
| |
| /* Save away the original value and section index so that we |
| can restore them later. */ |
| dyn_h->st_value = sym->st_value; |
| dyn_h->st_shndx = sym->st_shndx; |
| |
| /* For the dynamic symbol table entry, we want the value to be |
| address of this symbol's entry within the .opd section. */ |
| sym->st_value = (dyn_h->opd_offset |
| + sopd->output_offset |
| + sopd->output_section->vma); |
| sym->st_shndx = _bfd_elf_section_from_bfd_section (output_bfd, |
| sopd->output_section); |
| } |
| |
| /* Initialize a .plt entry if requested. */ |
| if (dyn_h && dyn_h->want_plt |
| && elf64_hppa_dynamic_symbol_p (dyn_h->h, info)) |
| { |
| bfd_vma value; |
| Elf_Internal_Rela rel; |
| bfd_byte *loc; |
| |
| BFD_ASSERT (splt != NULL && spltrel != NULL); |
| |
| /* We do not actually care about the value in the PLT entry |
| if we are creating a shared library and the symbol is |
| still undefined, we create a dynamic relocation to fill |
| in the correct value. */ |
| if (info->shared && h->root.type == bfd_link_hash_undefined) |
| value = 0; |
| else |
| value = (h->root.u.def.value + h->root.u.def.section->vma); |
| |
| /* Fill in the entry in the procedure linkage table. |
| |
| The format of a plt entry is |
| <funcaddr> <__gp>. |
| |
| plt_offset is the offset within the PLT section at which to |
| install the PLT entry. |
| |
| We are modifying the in-memory PLT contents here, so we do not add |
| in the output_offset of the PLT section. */ |
| |
| bfd_put_64 (splt->owner, value, splt->contents + dyn_h->plt_offset); |
| value = _bfd_get_gp_value (splt->output_section->owner); |
| bfd_put_64 (splt->owner, value, splt->contents + dyn_h->plt_offset + 0x8); |
| |
| /* Create a dynamic IPLT relocation for this entry. |
| |
| We are creating a relocation in the output file's PLT section, |
| which is included within the DLT secton. So we do need to include |
| the PLT's output_offset in the computation of the relocation's |
| address. */ |
| rel.r_offset = (dyn_h->plt_offset + splt->output_offset |
| + splt->output_section->vma); |
| rel.r_info = ELF64_R_INFO (h->dynindx, R_PARISC_IPLT); |
| rel.r_addend = 0; |
| |
| loc = spltrel->contents; |
| loc += spltrel->reloc_count++ * sizeof (Elf64_External_Rela); |
| bfd_elf64_swap_reloca_out (splt->output_section->owner, &rel, loc); |
| } |
| |
| /* Initialize an external call stub entry if requested. */ |
| if (dyn_h && dyn_h->want_stub |
| && elf64_hppa_dynamic_symbol_p (dyn_h->h, info)) |
| { |
| bfd_vma value; |
| int insn; |
| unsigned int max_offset; |
| |
| BFD_ASSERT (stub != NULL); |
| |
| /* Install the generic stub template. |
| |
| We are modifying the contents of the stub section, so we do not |
| need to include the stub section's output_offset here. */ |
| memcpy (stub->contents + dyn_h->stub_offset, plt_stub, sizeof (plt_stub)); |
| |
| /* Fix up the first ldd instruction. |
| |
| We are modifying the contents of the STUB section in memory, |
| so we do not need to include its output offset in this computation. |
| |
| Note the plt_offset value is the value of the PLT entry relative to |
| the start of the PLT section. These instructions will reference |
| data relative to the value of __gp, which may not necessarily have |
| the same address as the start of the PLT section. |
| |
| gp_offset contains the offset of __gp within the PLT section. */ |
| value = dyn_h->plt_offset - hppa_info->gp_offset; |
| |
| insn = bfd_get_32 (stub->owner, stub->contents + dyn_h->stub_offset); |
| if (output_bfd->arch_info->mach >= 25) |
| { |
| /* Wide mode allows 16 bit offsets. */ |
| max_offset = 32768; |
| insn &= ~ 0xfff1; |
| insn |= re_assemble_16 ((int) value); |
| } |
| else |
| { |
| max_offset = 8192; |
| insn &= ~ 0x3ff1; |
| insn |= re_assemble_14 ((int) value); |
| } |
| |
| if ((value & 7) || value + max_offset >= 2*max_offset - 8) |
| { |
| (*_bfd_error_handler) (_("stub entry for %s cannot load .plt, dp offset = %ld"), |
| dyn_h->root.string, |
| (long) value); |
| return FALSE; |
| } |
| |
| bfd_put_32 (stub->owner, (bfd_vma) insn, |
| stub->contents + dyn_h->stub_offset); |
| |
| /* Fix up the second ldd instruction. */ |
| value += 8; |
| insn = bfd_get_32 (stub->owner, stub->contents + dyn_h->stub_offset + 8); |
| if (output_bfd->arch_info->mach >= 25) |
| { |
| insn &= ~ 0xfff1; |
| insn |= re_assemble_16 ((int) value); |
| } |
| else |
| { |
| insn &= ~ 0x3ff1; |
| insn |= re_assemble_14 ((int) value); |
| } |
| bfd_put_32 (stub->owner, (bfd_vma) insn, |
| stub->contents + dyn_h->stub_offset + 8); |
| } |
| |
| return TRUE; |
| } |
| |
| /* The .opd section contains FPTRs for each function this file |
| exports. Initialize the FPTR entries. */ |
| |
| static bfd_boolean |
| elf64_hppa_finalize_opd (dyn_h, data) |
| struct elf64_hppa_dyn_hash_entry *dyn_h; |
| PTR data; |
| { |
| struct bfd_link_info *info = (struct bfd_link_info *)data; |
| struct elf64_hppa_link_hash_table *hppa_info; |
| struct elf_link_hash_entry *h = dyn_h ? dyn_h->h : NULL; |
| asection *sopd; |
| asection *sopdrel; |
| |
| hppa_info = elf64_hppa_hash_table (info); |
| sopd = hppa_info->opd_sec; |
| sopdrel = hppa_info->opd_rel_sec; |
| |
| if (h && dyn_h->want_opd) |
| { |
| bfd_vma value; |
| |
| /* The first two words of an .opd entry are zero. |
| |
| We are modifying the contents of the OPD section in memory, so we |
| do not need to include its output offset in this computation. */ |
| memset (sopd->contents + dyn_h->opd_offset, 0, 16); |
| |
| value = (h->root.u.def.value |
| + h->root.u.def.section->output_section->vma |
| + h->root.u.def.section->output_offset); |
| |
| /* The next word is the address of the function. */ |
| bfd_put_64 (sopd->owner, value, sopd->contents + dyn_h->opd_offset + 16); |
| |
| /* The last word is our local __gp value. */ |
| value = _bfd_get_gp_value (sopd->output_section->owner); |
| bfd_put_64 (sopd->owner, value, sopd->contents + dyn_h->opd_offset + 24); |
| } |
| |
| /* If we are generating a shared library, we must generate EPLT relocations |
| for each entry in the .opd, even for static functions (they may have |
| had their address taken). */ |
| if (info->shared && dyn_h && dyn_h->want_opd) |
| { |
| Elf_Internal_Rela rel; |
| bfd_byte *loc; |
| int dynindx; |
| |
| /* We may need to do a relocation against a local symbol, in |
| which case we have to look up it's dynamic symbol index off |
| the local symbol hash table. */ |
| if (h && h->dynindx != -1) |
| dynindx = h->dynindx; |
| else |
| dynindx |
| = _bfd_elf_link_lookup_local_dynindx (info, dyn_h->owner, |
| dyn_h->sym_indx); |
| |
| /* The offset of this relocation is the absolute address of the |
| .opd entry for this symbol. */ |
| rel.r_offset = (dyn_h->opd_offset + sopd->output_offset |
| + sopd->output_section->vma); |
| |
| /* If H is non-null, then we have an external symbol. |
| |
| It is imperative that we use a different dynamic symbol for the |
| EPLT relocation if the symbol has global scope. |
| |
| In the dynamic symbol table, the function symbol will have a value |
| which is address of the function's .opd entry. |
| |
| Thus, we can not use that dynamic symbol for the EPLT relocation |
| (if we did, the data in the .opd would reference itself rather |
| than the actual address of the function). Instead we have to use |
| a new dynamic symbol which has the same value as the original global |
| function symbol. |
| |
| We prefix the original symbol with a "." and use the new symbol in |
| the EPLT relocation. This new symbol has already been recorded in |
| the symbol table, we just have to look it up and use it. |
| |
| We do not have such problems with static functions because we do |
| not make their addresses in the dynamic symbol table point to |
| the .opd entry. Ultimately this should be safe since a static |
| function can not be directly referenced outside of its shared |
| library. |
| |
| We do have to play similar games for FPTR relocations in shared |
| libraries, including those for static symbols. See the FPTR |
| handling in elf64_hppa_finalize_dynreloc. */ |
| if (h) |
| { |
| char *new_name; |
| struct elf_link_hash_entry *nh; |
| |
| new_name = alloca (strlen (h->root.root.string) + 2); |
| new_name[0] = '.'; |
| strcpy (new_name + 1, h->root.root.string); |
| |
| nh = elf_link_hash_lookup (elf_hash_table (info), |
| new_name, FALSE, FALSE, FALSE); |
| |
| /* All we really want from the new symbol is its dynamic |
| symbol index. */ |
| dynindx = nh->dynindx; |
| } |
| |
| rel.r_addend = 0; |
| rel.r_info = ELF64_R_INFO (dynindx, R_PARISC_EPLT); |
| |
| loc = sopdrel->contents; |
| loc += sopdrel->reloc_count++ * sizeof (Elf64_External_Rela); |
| bfd_elf64_swap_reloca_out (sopd->output_section->owner, &rel, loc); |
| } |
| return TRUE; |
| } |
| |
| /* The .dlt section contains addresses for items referenced through the |
| dlt. Note that we can have a DLTIND relocation for a local symbol, thus |
| we can not depend on finish_dynamic_symbol to initialize the .dlt. */ |
| |
| static bfd_boolean |
| elf64_hppa_finalize_dlt (dyn_h, data) |
| struct elf64_hppa_dyn_hash_entry *dyn_h; |
| PTR data; |
| { |
| struct bfd_link_info *info = (struct bfd_link_info *)data; |
| struct elf64_hppa_link_hash_table *hppa_info; |
| asection *sdlt, *sdltrel; |
| struct elf_link_hash_entry *h = dyn_h ? dyn_h->h : NULL; |
| |
| hppa_info = elf64_hppa_hash_table (info); |
| |
| sdlt = hppa_info->dlt_sec; |
| sdltrel = hppa_info->dlt_rel_sec; |
| |
| /* H/DYN_H may refer to a local variable and we know it's |
| address, so there is no need to create a relocation. Just install |
| the proper value into the DLT, note this shortcut can not be |
| skipped when building a shared library. */ |
| if (! info->shared && h && dyn_h->want_dlt) |
| { |
| bfd_vma value; |
| |
| /* If we had an LTOFF_FPTR style relocation we want the DLT entry |
| to point to the FPTR entry in the .opd section. |
| |
| We include the OPD's output offset in this computation as |
| we are referring to an absolute address in the resulting |
| object file. */ |
| if (dyn_h->want_opd) |
| { |
| value = (dyn_h->opd_offset |
| + hppa_info->opd_sec->output_offset |
| + hppa_info->opd_sec->output_section->vma); |
| } |
| else if ((h->root.type == bfd_link_hash_defined |
| || h->root.type == bfd_link_hash_defweak) |
| && h->root.u.def.section) |
| { |
| value = h->root.u.def.value + h->root.u.def.section->output_offset; |
| if (h->root.u.def.section->output_section) |
| value += h->root.u.def.section->output_section->vma; |
| else |
| value += h->root.u.def.section->vma; |
| } |
| else |
| /* We have an undefined function reference. */ |
| value = 0; |
| |
| /* We do not need to include the output offset of the DLT section |
| here because we are modifying the in-memory contents. */ |
| bfd_put_64 (sdlt->owner, value, sdlt->contents + dyn_h->dlt_offset); |
| } |
| |
| /* Create a relocation for the DLT entry associated with this symbol. |
| When building a shared library the symbol does not have to be dynamic. */ |
| if (dyn_h->want_dlt |
| && (elf64_hppa_dynamic_symbol_p (dyn_h->h, info) || info->shared)) |
| { |
| Elf_Internal_Rela rel; |
| bfd_byte *loc; |
| int dynindx; |
| |
| /* We may need to do a relocation against a local symbol, in |
| which case we have to look up it's dynamic symbol index off |
| the local symbol hash table. */ |
| if (h && h->dynindx != -1) |
| dynindx = h->dynindx; |
| else |
| dynindx |
| = _bfd_elf_link_lookup_local_dynindx (info, dyn_h->owner, |
| dyn_h->sym_indx); |
| |
| /* Create a dynamic relocation for this entry. Do include the output |
| offset of the DLT entry since we need an absolute address in the |
| resulting object file. */ |
| rel.r_offset = (dyn_h->dlt_offset + sdlt->output_offset |
| + sdlt->output_section->vma); |
| if (h && h->type == STT_FUNC) |
| rel.r_info = ELF64_R_INFO (dynindx, R_PARISC_FPTR64); |
| else |
| rel.r_info = ELF64_R_INFO (dynindx, R_PARISC_DIR64); |
| rel.r_addend = 0; |
| |
| loc = sdltrel->contents; |
| loc += sdltrel->reloc_count++ * sizeof (Elf64_External_Rela); |
| bfd_elf64_swap_reloca_out (sdlt->output_section->owner, &rel, loc); |
| } |
| return TRUE; |
| } |
| |
| /* Finalize the dynamic relocations. Specifically the FPTR relocations |
| for dynamic functions used to initialize static data. */ |
| |
| static bfd_boolean |
| elf64_hppa_finalize_dynreloc (dyn_h, data) |
| struct elf64_hppa_dyn_hash_entry *dyn_h; |
| PTR data; |
| { |
| struct bfd_link_info *info = (struct bfd_link_info *)data; |
| struct elf64_hppa_link_hash_table *hppa_info; |
| struct elf_link_hash_entry *h; |
| int dynamic_symbol; |
| |
| dynamic_symbol = elf64_hppa_dynamic_symbol_p (dyn_h->h, info); |
| |
| if (!dynamic_symbol && !info->shared) |
| return TRUE; |
| |
| if (dyn_h->reloc_entries) |
| { |
| struct elf64_hppa_dyn_reloc_entry *rent; |
| int dynindx; |
| |
| hppa_info = elf64_hppa_hash_table (info); |
| h = dyn_h->h; |
| |
| /* We may need to do a relocation against a local symbol, in |
| which case we have to look up it's dynamic symbol index off |
| the local symbol hash table. */ |
| if (h && h->dynindx != -1) |
| dynindx = h->dynindx; |
| else |
| dynindx |
| = _bfd_elf_link_lookup_local_dynindx (info, dyn_h->owner, |
| dyn_h->sym_indx); |
| |
| for (rent = dyn_h->reloc_entries; rent; rent = rent->next) |
| { |
| Elf_Internal_Rela rel; |
| bfd_byte *loc; |
| |
| /* Allocate one iff we are building a shared library, the relocation |
| isn't a R_PARISC_FPTR64, or we don't want an opd entry. */ |
| if (!info->shared && rent->type == R_PARISC_FPTR64 && dyn_h->want_opd) |
| continue; |
| |
| /* Create a dynamic relocation for this entry. |
| |
| We need the output offset for the reloc's section because |
| we are creating an absolute address in the resulting object |
| file. */ |
| rel.r_offset = (rent->offset + rent->sec->output_offset |
| + rent->sec->output_section->vma); |
| |
| /* An FPTR64 relocation implies that we took the address of |
| a function and that the function has an entry in the .opd |
| section. We want the FPTR64 relocation to reference the |
| entry in .opd. |
| |
| We could munge the symbol value in the dynamic symbol table |
| (in fact we already do for functions with global scope) to point |
| to the .opd entry. Then we could use that dynamic symbol in |
| this relocation. |
| |
| Or we could do something sensible, not munge the symbol's |
| address and instead just use a different symbol to reference |
| the .opd entry. At least that seems sensible until you |
| realize there's no local dynamic symbols we can use for that |
| purpose. Thus the hair in the check_relocs routine. |
| |
| We use a section symbol recorded by check_relocs as the |
| base symbol for the relocation. The addend is the difference |
| between the section symbol and the address of the .opd entry. */ |
| if (info->shared && rent->type == R_PARISC_FPTR64 && dyn_h->want_opd) |
| { |
| bfd_vma value, value2; |
| |
| /* First compute the address of the opd entry for this symbol. */ |
| value = (dyn_h->opd_offset |
| + hppa_info->opd_sec->output_section->vma |
| + hppa_info->opd_sec->output_offset); |
| |
| /* Compute the value of the start of the section with |
| the relocation. */ |
| value2 = (rent->sec->output_section->vma |
| + rent->sec->output_offset); |
| |
| /* Compute the difference between the start of the section |
| with the relocation and the opd entry. */ |
| value -= value2; |
| |
| /* The result becomes the addend of the relocation. */ |
| rel.r_addend = value; |
| |
| /* The section symbol becomes the symbol for the dynamic |
| relocation. */ |
| dynindx |
| = _bfd_elf_link_lookup_local_dynindx (info, |
| rent->sec->owner, |
| rent->sec_symndx); |
| } |
| else |
| rel.r_addend = rent->addend; |
| |
| rel.r_info = ELF64_R_INFO (dynindx, rent->type); |
| |
| loc = hppa_info->other_rel_sec->contents; |
| loc += (hppa_info->other_rel_sec->reloc_count++ |
| * sizeof (Elf64_External_Rela)); |
| bfd_elf64_swap_reloca_out (hppa_info->other_rel_sec->output_section->owner, |
| &rel, loc); |
| } |
| } |
| |
| return TRUE; |
| } |
| |
| /* Used to decide how to sort relocs in an optimal manner for the |
| dynamic linker, before writing them out. */ |
| |
| static enum elf_reloc_type_class |
| elf64_hppa_reloc_type_class (rela) |
| const Elf_Internal_Rela *rela; |
| { |
| if (ELF64_R_SYM (rela->r_info) == 0) |
| return reloc_class_relative; |
| |
| switch ((int) ELF64_R_TYPE (rela->r_info)) |
| { |
| case R_PARISC_IPLT: |
| return reloc_class_plt; |
| case R_PARISC_COPY: |
| return reloc_class_copy; |
| default: |
| return reloc_class_normal; |
| } |
| } |
| |
| /* Finish up the dynamic sections. */ |
| |
| static bfd_boolean |
| elf64_hppa_finish_dynamic_sections (output_bfd, info) |
| bfd *output_bfd; |
| struct bfd_link_info *info; |
| { |
| bfd *dynobj; |
| asection *sdyn; |
| struct elf64_hppa_link_hash_table *hppa_info; |
| |
| hppa_info = elf64_hppa_hash_table (info); |
| |
| /* Finalize the contents of the .opd section. */ |
| elf64_hppa_dyn_hash_traverse (&hppa_info->dyn_hash_table, |
| elf64_hppa_finalize_opd, |
| info); |
| |
| elf64_hppa_dyn_hash_traverse (&hppa_info->dyn_hash_table, |
| elf64_hppa_finalize_dynreloc, |
| info); |
| |
| /* Finalize the contents of the .dlt section. */ |
| dynobj = elf_hash_table (info)->dynobj; |
| /* Finalize the contents of the .dlt section. */ |
| elf64_hppa_dyn_hash_traverse (&hppa_info->dyn_hash_table, |
| elf64_hppa_finalize_dlt, |
| info); |
| |
| sdyn = bfd_get_section_by_name (dynobj, ".dynamic"); |
| |
| if (elf_hash_table (info)->dynamic_sections_created) |
| { |
| Elf64_External_Dyn *dyncon, *dynconend; |
| |
| BFD_ASSERT (sdyn != NULL); |
| |
| dyncon = (Elf64_External_Dyn *) sdyn->contents; |
| dynconend = (Elf64_External_Dyn *) (sdyn->contents + sdyn->size); |
| for (; dyncon < dynconend; dyncon++) |
| { |
| Elf_Internal_Dyn dyn; |
| asection *s; |
| |
| bfd_elf64_swap_dyn_in (dynobj, dyncon, &dyn); |
| |
| switch (dyn.d_tag) |
| { |
| default: |
| break; |
| |
| case DT_HP_LOAD_MAP: |
| /* Compute the absolute address of 16byte scratchpad area |
| for the dynamic linker. |
| |
| By convention the linker script will allocate the scratchpad |
| area at the start of the .data section. So all we have to |
| to is find the start of the .data section. */ |
| s = bfd_get_section_by_name (output_bfd, ".data"); |
| dyn.d_un.d_ptr = s->vma; |
| bfd_elf64_swap_dyn_out (output_bfd, &dyn, dyncon); |
| break; |
| |
| case DT_PLTGOT: |
| /* HP's use PLTGOT to set the GOT register. */ |
| dyn.d_un.d_ptr = _bfd_get_gp_value (output_bfd); |
| bfd_elf64_swap_dyn_out (output_bfd, &dyn, dyncon); |
| break; |
| |
| case DT_JMPREL: |
| s = hppa_info->plt_rel_sec; |
| dyn.d_un.d_ptr = s->output_section->vma + s->output_offset; |
| bfd_elf64_swap_dyn_out (output_bfd, &dyn, dyncon); |
| break; |
| |
| case DT_PLTRELSZ: |
| s = hppa_info->plt_rel_sec; |
| dyn.d_un.d_val = s->size; |
| bfd_elf64_swap_dyn_out (output_bfd, &dyn, dyncon); |
| break; |
| |
| case DT_RELA: |
| s = hppa_info->other_rel_sec; |
| if (! s || ! s->size) |
| s = hppa_info->dlt_rel_sec; |
| if (! s || ! s->size) |
| s = hppa_info->opd_rel_sec; |
| dyn.d_un.d_ptr = s->output_section->vma + s->output_offset; |
| bfd_elf64_swap_dyn_out (output_bfd, &dyn, dyncon); |
| break; |
| |
| case DT_RELASZ: |
| s = hppa_info->other_rel_sec; |
| dyn.d_un.d_val = s->size; |
| s = hppa_info->dlt_rel_sec; |
| dyn.d_un.d_val += s->size; |
| s = hppa_info->opd_rel_sec; |
| dyn.d_un.d_val += s->size; |
| /* There is some question about whether or not the size of |
| the PLT relocs should be included here. HP's tools do |
| it, so we'll emulate them. */ |
| s = hppa_info->plt_rel_sec; |
| dyn.d_un.d_val += s->size; |
| bfd_elf64_swap_dyn_out (output_bfd, &dyn, dyncon); |
| break; |
| |
| } |
| } |
| } |
| |
| return TRUE; |
| } |
| |
| /* Support for core dump NOTE sections. */ |
| |
| static bfd_boolean |
| elf64_hppa_grok_prstatus (bfd *abfd, Elf_Internal_Note *note) |
| { |
| int offset; |
| size_t size; |
| |
| switch (note->descsz) |
| { |
| default: |
| return FALSE; |
| |
| case 760: /* Linux/hppa */ |
| /* pr_cursig */ |
| elf_tdata (abfd)->core_signal = bfd_get_16 (abfd, note->descdata + 12); |
| |
| /* pr_pid */ |
| elf_tdata (abfd)->core_pid = bfd_get_32 (abfd, note->descdata + 32); |
| |
| /* pr_reg */ |
| offset = 112; |
| size = 640; |
| |
| break; |
| } |
| |
| /* Make a ".reg/999" section. */ |
| return _bfd_elfcore_make_pseudosection (abfd, ".reg", |
| size, note->descpos + offset); |
| } |
| |
| static bfd_boolean |
| elf64_hppa_grok_psinfo (bfd *abfd, Elf_Internal_Note *note) |
| { |
| char * command; |
| int n; |
| |
| switch (note->descsz) |
| { |
| default: |
| return FALSE; |
| |
| case 136: /* Linux/hppa elf_prpsinfo. */ |
| elf_tdata (abfd)->core_program |
| = _bfd_elfcore_strndup (abfd, note->descdata + 40, 16); |
| elf_tdata (abfd)->core_command |
| = _bfd_elfcore_strndup (abfd, note->descdata + 56, 80); |
| } |
| |
| /* Note that for some reason, a spurious space is tacked |
| onto the end of the args in some (at least one anyway) |
| implementations, so strip it off if it exists. */ |
| command = elf_tdata (abfd)->core_command; |
| n = strlen (command); |
| |
| if (0 < n && command[n - 1] == ' ') |
| command[n - 1] = '\0'; |
| |
| return TRUE; |
| } |
| |
| /* Return the number of additional phdrs we will need. |
| |
| The generic ELF code only creates PT_PHDRs for executables. The HP |
| dynamic linker requires PT_PHDRs for dynamic libraries too. |
| |
| This routine indicates that the backend needs one additional program |
| header for that case. |
| |
| Note we do not have access to the link info structure here, so we have |
| to guess whether or not we are building a shared library based on the |
| existence of a .interp section. */ |
| |
| static int |
| elf64_hppa_additional_program_headers (bfd *abfd, |
| struct bfd_link_info *info ATTRIBUTE_UNUSED) |
| { |
| asection *s; |
| |
| /* If we are creating a shared library, then we have to create a |
| PT_PHDR segment. HP's dynamic linker chokes without it. */ |
| s = bfd_get_section_by_name (abfd, ".interp"); |
| if (! s) |
| return 1; |
| return 0; |
| } |
| |
| /* Allocate and initialize any program headers required by this |
| specific backend. |
| |
| The generic ELF code only creates PT_PHDRs for executables. The HP |
| dynamic linker requires PT_PHDRs for dynamic libraries too. |
| |
| This allocates the PT_PHDR and initializes it in a manner suitable |
| for the HP linker. |
| |
| Note we do not have access to the link info structure here, so we have |
| to guess whether or not we are building a shared library based on the |
| existence of a .interp section. */ |
| |
| static bfd_boolean |
| elf64_hppa_modify_segment_map (bfd *abfd, |
| struct bfd_link_info *info ATTRIBUTE_UNUSED) |
| { |
| struct elf_segment_map *m; |
| asection *s; |
| |
| s = bfd_get_section_by_name (abfd, ".interp"); |
| if (! s) |
| { |
| for (m = elf_tdata (abfd)->segment_map; m != NULL; m = m->next) |
| if (m->p_type == PT_PHDR) |
| break; |
| if (m == NULL) |
| { |
| m = ((struct elf_segment_map *) |
| bfd_zalloc (abfd, (bfd_size_type) sizeof *m)); |
| if (m == NULL) |
| return FALSE; |
| |
| m->p_type = PT_PHDR; |
| m->p_flags = PF_R | PF_X; |
| m->p_flags_valid = 1; |
| m->p_paddr_valid = 1; |
| m->includes_phdrs = 1; |
| |
| m->next = elf_tdata (abfd)->segment_map; |
| elf_tdata (abfd)->segment_map = m; |
| } |
| } |
| |
| for (m = elf_tdata (abfd)->segment_map; m != NULL; m = m->next) |
| if (m->p_type == PT_LOAD) |
| { |
| unsigned int i; |
| |
| for (i = 0; i < m->count; i++) |
| { |
| /* The code "hint" is not really a hint. It is a requirement |
| for certain versions of the HP dynamic linker. Worse yet, |
| it must be set even if the shared library does not have |
| any code in its "text" segment (thus the check for .hash |
| to catch this situation). */ |
| if (m->sections[i]->flags & SEC_CODE |
| || (strcmp (m->sections[i]->name, ".hash") == 0)) |
| m->p_flags |= (PF_X | PF_HP_CODE); |
| } |
| } |
| |
| return TRUE; |
| } |
| |
| /* Called when writing out an object file to decide the type of a |
| symbol. */ |
| static int |
| elf64_hppa_elf_get_symbol_type (elf_sym, type) |
| Elf_Internal_Sym *elf_sym; |
| int type; |
| { |
| if (ELF_ST_TYPE (elf_sym->st_info) == STT_PARISC_MILLI) |
| return STT_PARISC_MILLI; |
| else |
| return type; |
| } |
| |
| /* Support HP specific sections for core files. */ |
| static bfd_boolean |
| elf64_hppa_section_from_phdr (bfd *abfd, Elf_Internal_Phdr *hdr, int index, |
| const char *typename) |
| { |
| if (hdr->p_type == PT_HP_CORE_KERNEL) |
| { |
| asection *sect; |
| |
| if (!_bfd_elf_make_section_from_phdr (abfd, hdr, index, typename)) |
| return FALSE; |
| |
| sect = bfd_make_section_anyway (abfd, ".kernel"); |
| if (sect == NULL) |
| return FALSE; |
| sect->size = hdr->p_filesz; |
| sect->filepos = hdr->p_offset; |
| sect->flags = SEC_HAS_CONTENTS | SEC_READONLY; |
| return TRUE; |
| } |
| |
| if (hdr->p_type == PT_HP_CORE_PROC) |
| { |
| int sig; |
| |
| if (bfd_seek (abfd, hdr->p_offset, SEEK_SET) != 0) |
| return FALSE; |
| if (bfd_bread (&sig, 4, abfd) != 4) |
| return FALSE; |
| |
| elf_tdata (abfd)->core_signal = sig; |
| |
| if (!_bfd_elf_make_section_from_phdr (abfd, hdr, index, typename)) |
| return FALSE; |
| |
| /* GDB uses the ".reg" section to read register contents. */ |
| return _bfd_elfcore_make_pseudosection (abfd, ".reg", hdr->p_filesz, |
| hdr->p_offset); |
| } |
| |
| if (hdr->p_type == PT_HP_CORE_LOADABLE |
| || hdr->p_type == PT_HP_CORE_STACK |
| || hdr->p_type == PT_HP_CORE_MMF) |
| hdr->p_type = PT_LOAD; |
| |
| return _bfd_elf_make_section_from_phdr (abfd, hdr, index, typename); |
| } |
| |
| static const struct bfd_elf_special_section elf64_hppa_special_sections[] = |
| { |
| { STRING_COMMA_LEN (".fini"), 0, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE }, |
| { STRING_COMMA_LEN (".init"), 0, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE }, |
| { STRING_COMMA_LEN (".plt"), 0, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE + SHF_PARISC_SHORT }, |
| { STRING_COMMA_LEN (".dlt"), 0, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE + SHF_PARISC_SHORT }, |
| { STRING_COMMA_LEN (".sdata"), 0, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE + SHF_PARISC_SHORT }, |
| { STRING_COMMA_LEN (".sbss"), 0, SHT_NOBITS, SHF_ALLOC + SHF_WRITE + SHF_PARISC_SHORT }, |
| { STRING_COMMA_LEN (".tbss"), 0, SHT_NOBITS, SHF_ALLOC + SHF_WRITE + SHF_HP_TLS }, |
| { NULL, 0, 0, 0, 0 } |
| }; |
| |
| /* The hash bucket size is the standard one, namely 4. */ |
| |
| const struct elf_size_info hppa64_elf_size_info = |
| { |
| sizeof (Elf64_External_Ehdr), |
| sizeof (Elf64_External_Phdr), |
| sizeof (Elf64_External_Shdr), |
| sizeof (Elf64_External_Rel), |
| sizeof (Elf64_External_Rela), |
| sizeof (Elf64_External_Sym), |
| sizeof (Elf64_External_Dyn), |
| sizeof (Elf_External_Note), |
| 4, |
| 1, |
| 64, 3, |
| ELFCLASS64, EV_CURRENT, |
| bfd_elf64_write_out_phdrs, |
| bfd_elf64_write_shdrs_and_ehdr, |
| bfd_elf64_checksum_contents, |
| bfd_elf64_write_relocs, |
| bfd_elf64_swap_symbol_in, |
| bfd_elf64_swap_symbol_out, |
| bfd_elf64_slurp_reloc_table, |
| bfd_elf64_slurp_symbol_table, |
| bfd_elf64_swap_dyn_in, |
| bfd_elf64_swap_dyn_out, |
| bfd_elf64_swap_reloc_in, |
| bfd_elf64_swap_reloc_out, |
| bfd_elf64_swap_reloca_in, |
| bfd_elf64_swap_reloca_out |
| }; |
| |
| #define TARGET_BIG_SYM bfd_elf64_hppa_vec |
| #define TARGET_BIG_NAME "elf64-hppa" |
| #define ELF_ARCH bfd_arch_hppa |
| #define ELF_MACHINE_CODE EM_PARISC |
| /* This is not strictly correct. The maximum page size for PA2.0 is |
| 64M. But everything still uses 4k. */ |
| #define ELF_MAXPAGESIZE 0x1000 |
| #define ELF_OSABI ELFOSABI_HPUX |
| |
| #define bfd_elf64_bfd_reloc_type_lookup elf_hppa_reloc_type_lookup |
| #define bfd_elf64_bfd_reloc_name_lookup elf_hppa_reloc_name_lookup |
| #define bfd_elf64_bfd_is_local_label_name elf_hppa_is_local_label_name |
| #define elf_info_to_howto elf_hppa_info_to_howto |
| #define elf_info_to_howto_rel elf_hppa_info_to_howto_rel |
| |
| #define elf_backend_section_from_shdr elf64_hppa_section_from_shdr |
| #define elf_backend_object_p elf64_hppa_object_p |
| #define elf_backend_final_write_processing \ |
| elf_hppa_final_write_processing |
| #define elf_backend_fake_sections elf_hppa_fake_sections |
| #define elf_backend_add_symbol_hook elf_hppa_add_symbol_hook |
| |
| #define elf_backend_relocate_section elf_hppa_relocate_section |
| |
| #define bfd_elf64_bfd_final_link elf_hppa_final_link |
| |
| #define elf_backend_create_dynamic_sections \ |
| elf64_hppa_create_dynamic_sections |
| #define elf_backend_post_process_headers elf64_hppa_post_process_headers |
| |
| #define elf_backend_omit_section_dynsym \ |
| ((bfd_boolean (*) (bfd *, struct bfd_link_info *, asection *)) bfd_true) |
| #define elf_backend_adjust_dynamic_symbol \ |
| elf64_hppa_adjust_dynamic_symbol |
| |
| #define elf_backend_size_dynamic_sections \ |
| elf64_hppa_size_dynamic_sections |
| |
| #define elf_backend_finish_dynamic_symbol \ |
| elf64_hppa_finish_dynamic_symbol |
| #define elf_backend_finish_dynamic_sections \ |
| elf64_hppa_finish_dynamic_sections |
| #define elf_backend_grok_prstatus elf64_hppa_grok_prstatus |
| #define elf_backend_grok_psinfo elf64_hppa_grok_psinfo |
| |
| /* Stuff for the BFD linker: */ |
| #define bfd_elf64_bfd_link_hash_table_create \ |
| elf64_hppa_hash_table_create |
| |
| #define elf_backend_check_relocs \ |
| elf64_hppa_check_relocs |
| |
| #define elf_backend_size_info \ |
| hppa64_elf_size_info |
| |
| #define elf_backend_additional_program_headers \ |
| elf64_hppa_additional_program_headers |
| |
| #define elf_backend_modify_segment_map \ |
| elf64_hppa_modify_segment_map |
| |
| #define elf_backend_link_output_symbol_hook \ |
| elf64_hppa_link_output_symbol_hook |
| |
| #define elf_backend_want_got_plt 0 |
| #define elf_backend_plt_readonly 0 |
| #define elf_backend_want_plt_sym 0 |
| #define elf_backend_got_header_size 0 |
| #define elf_backend_type_change_ok TRUE |
| #define elf_backend_get_symbol_type elf64_hppa_elf_get_symbol_type |
| #define elf_backend_reloc_type_class elf64_hppa_reloc_type_class |
| #define elf_backend_rela_normal 1 |
| #define elf_backend_special_sections elf64_hppa_special_sections |
| #define elf_backend_action_discarded elf_hppa_action_discarded |
| #define elf_backend_section_from_phdr elf64_hppa_section_from_phdr |
| |
| #define elf64_bed elf64_hppa_hpux_bed |
| |
| #include "elf64-target.h" |
| |
| #undef TARGET_BIG_SYM |
| #define TARGET_BIG_SYM bfd_elf64_hppa_linux_vec |
| #undef TARGET_BIG_NAME |
| #define TARGET_BIG_NAME "elf64-hppa-linux" |
| #undef ELF_OSABI |
| #define ELF_OSABI ELFOSABI_LINUX |
| #undef elf_backend_post_process_headers |
| #define elf_backend_post_process_headers _bfd_elf_set_osabi |
| #undef elf64_bed |
| #define elf64_bed elf64_hppa_linux_bed |
| |
| #include "elf64-target.h" |