diff options
author | Jeff Law <law@redhat.com> | 2000-04-24 08:17:58 +0000 |
---|---|---|
committer | Jeff Law <law@redhat.com> | 2000-04-24 08:17:58 +0000 |
commit | 15bda42549f5da68d8fdb3d91e70dae10f8fc78e (patch) | |
tree | c9590a8b9ef7ac5b8fe137eef3d2d4e2194bc719 /bfd/elf64-hppa.c | |
parent | 9fd9378ca0dcc59270194784dc76b124b249fb91 (diff) | |
download | gdb-15bda42549f5da68d8fdb3d91e70dae10f8fc78e.zip gdb-15bda42549f5da68d8fdb3d91e70dae10f8fc78e.tar.gz gdb-15bda42549f5da68d8fdb3d91e70dae10f8fc78e.tar.bz2 |
* elf64-hppa.c, elf64-hppa.h: New files with PA64 support.
Diffstat (limited to 'bfd/elf64-hppa.c')
-rw-r--r-- | bfd/elf64-hppa.c | 2633 |
1 files changed, 2633 insertions, 0 deletions
diff --git a/bfd/elf64-hppa.c b/bfd/elf64-hppa.c new file mode 100644 index 0000000..b5b9f7f --- /dev/null +++ b/bfd/elf64-hppa.c @@ -0,0 +1,2633 @@ +/* Generic support for 64-bit ELF + Copyright 1999, 2000 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 2 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "bfd.h" +#include "sysdep.h" +#include "libbfd.h" +#include "elf-bfd.h" +#include "elf/hppa.h" +#include "libhppa.h" +#include "elf64-hppa.h" +#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 derrived 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. */ + unsigned 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 boolean elf64_hppa_dyn_hash_table_init + PARAMS ((struct elf64_hppa_dyn_hash_table *ht, bfd *abfd, + new_hash_entry_func new)); +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, + boolean create, boolean copy)); +static void elf64_hppa_dyn_hash_traverse + PARAMS ((struct elf64_hppa_dyn_hash_table *table, + boolean (*func)(struct elf64_hppa_dyn_hash_entry *, PTR), + PTR info)); + +static const char *get_dyn_name + PARAMS ((bfd *abfd, struct elf_link_hash_entry *h, + const Elf_Internal_Rela *rel, char **pbuf, size_t *plen)); + + +/* This must follow the definitions of the various derived linker + hash tables and shared functions. */ +#include "elf-hppa.h" + + +static boolean elf64_hppa_object_p + PARAMS ((bfd *)); + +static boolean elf64_hppa_section_from_shdr + PARAMS ((bfd *, Elf64_Internal_Shdr *, char *)); + +static void elf64_hppa_post_process_headers + PARAMS ((bfd *, struct bfd_link_info *)); + +static boolean elf64_hppa_create_dynamic_sections + PARAMS ((bfd *, struct bfd_link_info *)); + +static boolean elf64_hppa_adjust_dynamic_symbol + PARAMS ((struct bfd_link_info *, struct elf_link_hash_entry *)); + +static boolean elf64_hppa_size_dynamic_sections + PARAMS ((bfd *, struct bfd_link_info *)); + +static boolean elf64_hppa_finish_dynamic_symbol + PARAMS ((bfd *, struct bfd_link_info *, + struct elf_link_hash_entry *, Elf_Internal_Sym *)); + +static boolean elf64_hppa_finish_dynamic_sections + PARAMS ((bfd *, struct bfd_link_info *)); + +static boolean elf64_hppa_check_relocs + PARAMS ((bfd *, struct bfd_link_info *, + asection *, const Elf_Internal_Rela *)); + +static boolean elf64_hppa_dynamic_symbol_p + PARAMS ((struct elf_link_hash_entry *, struct bfd_link_info *)); + +static boolean elf64_hppa_mark_exported_functions + PARAMS ((struct elf_link_hash_entry *, PTR)); + +static boolean elf64_hppa_finalize_opd + PARAMS ((struct elf64_hppa_dyn_hash_entry *, PTR)); + +static boolean elf64_hppa_finalize_dlt + PARAMS ((struct elf64_hppa_dyn_hash_entry *, PTR)); + +static boolean allocate_global_data_dlt + PARAMS ((struct elf64_hppa_dyn_hash_entry *, PTR)); + +static boolean allocate_global_data_plt + PARAMS ((struct elf64_hppa_dyn_hash_entry *, PTR)); + +static boolean allocate_global_data_stub + PARAMS ((struct elf64_hppa_dyn_hash_entry *, PTR)); + +static boolean allocate_global_data_opd + PARAMS ((struct elf64_hppa_dyn_hash_entry *, PTR)); + +static boolean get_reloc_section + PARAMS ((bfd *, struct elf64_hppa_link_hash_table *, asection *)); + +static boolean count_dyn_reloc + PARAMS ((bfd *, struct elf64_hppa_dyn_hash_entry *, + int, asection *, int, bfd_vma, bfd_vma)); + +static boolean allocate_dynrel_entries + PARAMS ((struct elf64_hppa_dyn_hash_entry *, PTR)); + +static boolean elf64_hppa_finalize_dynreloc + PARAMS ((struct elf64_hppa_dyn_hash_entry *, PTR)); + +static boolean get_opd + PARAMS ((bfd *, struct bfd_link_info *, struct elf64_hppa_link_hash_table *)); + +static boolean get_plt + PARAMS ((bfd *, struct bfd_link_info *, struct elf64_hppa_link_hash_table *)); + +static boolean get_dlt + PARAMS ((bfd *, struct bfd_link_info *, struct elf64_hppa_link_hash_table *)); + +static boolean get_stub + PARAMS ((bfd *, struct bfd_link_info *, struct elf64_hppa_link_hash_table *)); + +static boolean +elf64_hppa_dyn_hash_table_init (ht, abfd, new) + struct elf64_hppa_dyn_hash_table *ht; + bfd *abfd; + new_hash_entry_func new; +{ + memset (ht, 0, sizeof(*ht)); + return bfd_hash_table_init (&ht->root, new); +} + +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; + + /* Initialize our local data. All zeros, and definitely easier + than setting 8 bit fields. */ + memset (ret, 0, sizeof(*ret)); + + /* Call the allocation method of the superclass. */ + ret = ((struct elf64_hppa_dyn_hash_entry *) + bfd_hash_newfunc ((struct bfd_hash_entry *) ret, table, string)); + + 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, sizeof (*ret)); + if (!ret) + return 0; + if (!_bfd_elf_link_hash_table_init (&ret->root, abfd, + _bfd_elf_link_hash_newfunc)) + { + bfd_release (abfd, ret); + return 0; + } + + if (!elf64_hppa_dyn_hash_table_init (&ret->dyn_hash_table, abfd, + elf64_hppa_new_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; + 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; + boolean (*func) PARAMS ((struct elf64_hppa_dyn_hash_entry *, PTR)); + PTR info; +{ + (bfd_hash_traverse + (&table->root, + (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 boolean +elf64_hppa_object_p (abfd) + bfd *abfd; +{ + /* Set the right machine number for an HPPA ELF file. */ + return bfd_default_set_arch_mach (abfd, bfd_arch_hppa, 25); +} + +/* Given section type (hdr->sh_type), return a boolean indicating + whether or not the section is an elf64-hppa specific section. */ +static boolean +elf64_hppa_section_from_shdr (abfd, hdr, name) + bfd *abfd; + Elf64_Internal_Shdr *hdr; + char *name; +{ + 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)) + 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; +{ + 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 = sizeof(void*)*2 + 1 + sizeof(bfd_vma)*4 + 1 + 1; + nlen += 10; /* %p slop */ + } + tlen = nlen + 1 + 16 + 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); + sprintf_vma (buf + nlen, rel->r_addend); + } + else + { + nlen = sprintf (buf, "%p:%lx", abfd, 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 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 ((strncmp (srel_name, ".rela", 5) == 0 + && strcmp (bfd_get_section_name (abfd, sec), + srel_name+5) == 0) + || (strncmp (srel_name, ".rel", 4) == 0 + && 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 (dynobj, srel_name); + if (srel == NULL + || !bfd_set_section_flags (dynobj, srel, + (SEC_ALLOC + | SEC_LOAD + | SEC_HAS_CONTENTS + | SEC_IN_MEMORY + | SEC_LINKER_CREATED + | SEC_READONLY)) + || !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 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, 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 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; + int sec_symndx; + + if (info->relocateable) + 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_elf64_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. This is disgusting. */ + + if (info->shared && hppa_info->section_syms_bfd != abfd) + { + int i, highest_shndx; + asection *section; + Elf_Internal_Sym *local_syms, *isym; + Elf64_External_Sym *ext_syms, *esym; + + /* 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); + + /* Allocate memory for the internal and external symbols. */ + local_syms + = (Elf_Internal_Sym *) bfd_malloc (symtab_hdr->sh_info + * sizeof (Elf_Internal_Sym)); + if (local_syms == NULL) + return false; + + ext_syms + = (Elf64_External_Sym *) bfd_malloc (symtab_hdr->sh_info + * sizeof (Elf64_External_Sym)); + if (ext_syms == NULL) + { + free (local_syms); + return false; + } + + /* Read in the local symbols. */ + if (bfd_seek (abfd, symtab_hdr->sh_offset, SEEK_SET) != 0 + || bfd_read (ext_syms, 1, + (symtab_hdr->sh_info + * sizeof (Elf64_External_Sym)), abfd) + != (symtab_hdr->sh_info * sizeof (Elf64_External_Sym))) + { + free (local_syms); + free (ext_syms); + return false; + } + + /* Swap in the local symbols, also record the highest section index + referenced by the local symbols. */ + isym = local_syms; + esym = ext_syms; + highest_shndx = 0; + for (i = 0; i < symtab_hdr->sh_info; i++, esym++, isym++) + { + bfd_elf64_swap_symbol_in (abfd, esym, isym); + if (isym->st_shndx > highest_shndx) + highest_shndx = isym->st_shndx; + } + + /* Now we can free the external symbols. */ + free (ext_syms); + + /* Allocate an array to hold the section index to section symbol index + mapping. Bump by one since we start counting at zero. */ + highest_shndx++; + hppa_info->section_syms = (int *) bfd_malloc (highest_shndx + * sizeof (int)); + + /* Now walk the local symbols again. If we find a section symbol, + record the index of the symbol into the section_syms array. */ + for (isym = local_syms, i = 0; i < symtab_hdr->sh_info; 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. Get rid of them. */ + free (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 == -1) + return false; + + sec_symndx = hppa_info->section_syms[sec_symndx]; + } + 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; + 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->elf_link_hash_flags |= ELF_LINK_HASH_REF_REGULAR; + } + + /* 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) + || ! (h->elf_link_hash_flags & ELF_LINK_HASH_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_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->elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT; + } + + /* 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_elf64_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 boolean +elf64_hppa_dynamic_symbol_p (h, info) + struct elf_link_hash_entry *h; + struct bfd_link_info *info; +{ + if (h == NULL) + return false; + + 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->dynindx == -1) + return false; + + if (h->root.type == bfd_link_hash_undefweak + || h->root.type == bfd_link_hash_defweak) + return true; + + if (h->root.root.string[0] == '$' && h->root.root.string[1] == '$') + return false; + + if ((info->shared && !info->symbolic) + || ((h->elf_link_hash_flags + & (ELF_LINK_HASH_DEF_DYNAMIC | ELF_LINK_HASH_REF_REGULAR)) + == (ELF_LINK_HASH_DEF_DYNAMIC | ELF_LINK_HASH_REF_REGULAR))) + return true; + + return false; +} + +/* Mark all funtions exported by this file so that we can later allocate + entries in .opd for them. */ + +static 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 + && (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; + h->elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT; + } + + return true; +} + +/* Allocate space for a DLT entry. */ + +static 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 && h->dynindx == -1)) + { + bfd *owner; + owner = (h ? h->root.u.def.section->owner : dyn_h->owner); + + if (!_bfd_elf64_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 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 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 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) + 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->root.type == bfd_link_hash_defined + || h->root.type == bfd_link_hash_defweak) + && h->root.u.def.section->output_section != NULL)) + { + /* 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_elf64_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_elf64_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] = ELFOSABI_HPUX; + i_ehdrp->e_ident[EI_ABIVERSION] = 1; +} + +/* Create function descriptor section (.opd). This section is called .opd + because it contains "official prodecure 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 boolean +get_opd (abfd, info, hppa_info) + bfd *abfd; + struct bfd_link_info *info; + 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 (dynobj, ".opd"); + if (!opd + || !bfd_set_section_flags (dynobj, opd, + (SEC_ALLOC + | SEC_LOAD + | SEC_HAS_CONTENTS + | SEC_IN_MEMORY + | SEC_LINKER_CREATED)) + || !bfd_set_section_alignment (abfd, opd, 3)) + { + BFD_ASSERT (0); + return false; + } + + hppa_info->opd_sec = opd; + } + + return true; +} + +/* Create the PLT section. */ + +static boolean +get_plt (abfd, info, hppa_info) + bfd *abfd; + struct bfd_link_info *info; + 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 (dynobj, ".plt"); + if (!plt + || !bfd_set_section_flags (dynobj, plt, + (SEC_ALLOC + | SEC_LOAD + | SEC_HAS_CONTENTS + | SEC_IN_MEMORY + | SEC_LINKER_CREATED)) + || !bfd_set_section_alignment (abfd, plt, 3)) + { + BFD_ASSERT (0); + return false; + } + + hppa_info->plt_sec = plt; + } + + return true; +} + +/* Create the DLT section. */ + +static boolean +get_dlt (abfd, info, hppa_info) + bfd *abfd; + struct bfd_link_info *info; + 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 (dynobj, ".dlt"); + if (!dlt + || !bfd_set_section_flags (dynobj, dlt, + (SEC_ALLOC + | SEC_LOAD + | SEC_HAS_CONTENTS + | SEC_IN_MEMORY + | SEC_LINKER_CREATED)) + || !bfd_set_section_alignment (abfd, dlt, 3)) + { + BFD_ASSERT (0); + return false; + } + + hppa_info->dlt_sec = dlt; + } + + return true; +} + +/* Create the stubs section. */ + +static boolean +get_stub (abfd, info, hppa_info) + bfd *abfd; + struct bfd_link_info *info; + 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 (dynobj, ".stub"); + if (!stub + || !bfd_set_section_flags (dynobj, stub, + (SEC_ALLOC + | SEC_LOAD + | SEC_HAS_CONTENTS + | SEC_IN_MEMORY + | SEC_READONLY + | SEC_LINKER_CREATED)) + || !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 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(abfd, ".rela.dlt"); + if (s == NULL + || !bfd_set_section_flags (abfd, s, (SEC_ALLOC | SEC_LOAD + | SEC_HAS_CONTENTS + | SEC_IN_MEMORY + | SEC_READONLY + | SEC_LINKER_CREATED)) + || !bfd_set_section_alignment (abfd, s, 3)) + return false; + elf64_hppa_hash_table (info)->dlt_rel_sec = s; + + s = bfd_make_section(abfd, ".rela.plt"); + if (s == NULL + || !bfd_set_section_flags (abfd, s, (SEC_ALLOC | SEC_LOAD + | SEC_HAS_CONTENTS + | SEC_IN_MEMORY + | SEC_READONLY + | SEC_LINKER_CREATED)) + || !bfd_set_section_alignment (abfd, s, 3)) + return false; + elf64_hppa_hash_table (info)->plt_rel_sec = s; + + s = bfd_make_section(abfd, ".rela.data"); + if (s == NULL + || !bfd_set_section_flags (abfd, s, (SEC_ALLOC | SEC_LOAD + | SEC_HAS_CONTENTS + | SEC_IN_MEMORY + | SEC_READONLY + | SEC_LINKER_CREATED)) + || !bfd_set_section_alignment (abfd, s, 3)) + return false; + elf64_hppa_hash_table (info)->other_rel_sec = s; + + s = bfd_make_section(abfd, ".rela.opd"); + if (s == NULL + || !bfd_set_section_flags (abfd, s, (SEC_ALLOC | SEC_LOAD + | SEC_HAS_CONTENTS + | SEC_IN_MEMORY + | SEC_READONLY + | SEC_LINKER_CREATED)) + || !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 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; + 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) + { + switch (rent->type) + { + case R_PARISC_FPTR64: + /* Allocate one iff we are not building a shared library and + !want_opd, which by this point will be true only if we're + actually allocating one statically in the main executable. */ + if (!x->info->shared && dyn_h->want_opd) + continue; + break; + } + hppa_info->other_rel_sec->_raw_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) + if (!_bfd_elf64_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->_raw_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->_raw_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->_raw_size += t; + } + + return true; +} + +/* Adjust a symbol defined by a dynamic object and referenced by a + regular object. */ + +static boolean +elf64_hppa_adjust_dynamic_symbol (info, h) + struct bfd_link_info *info; + 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->weakdef != NULL) + { + BFD_ASSERT (h->weakdef->root.type == bfd_link_hash_defined + || h->weakdef->root.type == bfd_link_hash_defweak); + h->root.u.def.section = h->weakdef->root.u.def.section; + h->root.u.def.value = h->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; +} + +/* Set the final sizes of the dynamic sections and allocate memory for + the contents of our special sections. */ + +static boolean +elf64_hppa_size_dynamic_sections (output_bfd, info) + bfd *output_bfd; + struct bfd_link_info *info; +{ + bfd *dynobj; + asection *s; + boolean plt; + boolean relocs; + boolean reltext; + boolean stubs; + 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); + + if (elf_hash_table (info)->dynamic_sections_created) + { + /* Set the contents of the .interp section to the interpreter. */ + if (! info->shared) + { + s = bfd_get_section_by_name (dynobj, ".interp"); + BFD_ASSERT (s != NULL); + s->_raw_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->_raw_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->_raw_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->_raw_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->_raw_size = data.ofs; + } + + /* Mark each function this program exports so that we will allocate + space in the .opd section for each function's FPTR. + + 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), + elf64_hppa_mark_exported_functions, + info); + + /* 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->_raw_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; + boolean strip; + + 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); + + strip = 0; + + if (strcmp (name, ".plt") == 0) + { + if (s->_raw_size == 0) + { + /* Strip this section if we don't need it; see the + comment below. */ + strip = true; + } + else + { + /* Remember whether there is a PLT. */ + plt = true; + } + } + else if (strcmp (name, ".dlt") == 0) + { + if (s->_raw_size == 0) + { + /* Strip this section if we don't need it; see the + comment below. */ + strip = true; + } + } + else if (strcmp (name, ".opd") == 0) + { + if (s->_raw_size == 0) + { + /* Strip this section if we don't need it; see the + comment below. */ + strip = true; + } + } + else if (strncmp (name, ".rela", 4) == 0) + { + if (s->_raw_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. */ + strip = true; + } + else + { + 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 if (strncmp (name, ".dlt", 4) != 0 + && strcmp (name, ".stub") != 0 + && strcmp (name, ".got") != 0) + { + /* It's not one of our sections, so don't allocate space. */ + continue; + } + + if (strip) + { + _bfd_strip_section_from_output (info, s); + continue; + } + + /* Allocate memory for the section contents if it has not + been allocated already. */ + if (s->contents == NULL) + { + s->contents = (bfd_byte *) bfd_alloc (dynobj, s->_raw_size); + if (s->contents == NULL && s->_raw_size != 0) + 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. */ + if (! bfd_elf64_add_dynamic_entry (info, DT_HP_DLD_FLAGS, 0) + || ! bfd_elf64_add_dynamic_entry (info, 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 (! bfd_elf64_add_dynamic_entry (info, DT_DEBUG, 0) + || ! bfd_elf64_add_dynamic_entry (info, DT_HP_DLD_HOOK, 0) + || ! bfd_elf64_add_dynamic_entry (info, DT_HP_LOAD_MAP, 0)) + return false; + } + + if (plt) + { + if (! bfd_elf64_add_dynamic_entry (info, DT_PLTRELSZ, 0) + || ! bfd_elf64_add_dynamic_entry (info, DT_PLTREL, DT_RELA) + || ! bfd_elf64_add_dynamic_entry (info, DT_JMPREL, 0)) + return false; + } + + if (relocs) + { + if (! bfd_elf64_add_dynamic_entry (info, DT_RELA, 0) + || ! bfd_elf64_add_dynamic_entry (info, DT_RELASZ, 0) + || ! bfd_elf64_add_dynamic_entry (info, DT_RELAENT, + sizeof (Elf64_External_Rela))) + return false; + } + + if (reltext) + { + if (! bfd_elf64_add_dynamic_entry (info, DT_TEXTREL, 0)) + return false; + } + } + + 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 boolean +elf64_hppa_link_output_symbol_hook (abfd, info, name, sym, input_sec) + bfd *abfd; + struct bfd_link_info *info; + const char *name; + Elf_Internal_Sym *sym; + asection *input_sec; +{ + 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); + + /* Function symbols for which we created .opd entries were munged + by finish_dynamic_symbol and have to be un-munged here. */ + if (dyn_h && dyn_h->want_opd) + { + /* 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 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; + + BFD_ASSERT (stub != NULL && splt != NULL + && sopd != NULL && sdlt != NULL) + + /* 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) + { + /* 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; + + /* 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; + + bfd_elf64_swap_reloca_out (splt->output_section->owner, &rel, + (((Elf64_External_Rela *) + spltrel->contents) + + spltrel->reloc_count)); + spltrel->reloc_count++; + } + + /* 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; + + /* 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); + insn &= 0xffffc00e; + insn |= ((value & 0x2000) >> 13); + value &= 0x1ff8; + value <<= 1; + bfd_put_32 (stub->owner, (insn | value), + stub->contents + dyn_h->stub_offset); + + /* Fix up the second ldd instruction. */ + value = dyn_h->plt_offset - hppa_info->gp_offset + 8; + + insn = bfd_get_32 (stub->owner, stub->contents + dyn_h->stub_offset + 8); + insn &= 0xffffc00e; + insn |= ((value & 0x2000) >> 13); + value &= 0x1ff8; + value <<= 1; + bfd_put_32 (stub->owner, (insn | value), + stub->contents + dyn_h->stub_offset + 8); + } + + /* Millicode symbols should not be put in the dynamic + symbol table under any circumstances. */ + if (ELF_ST_TYPE (sym->st_info) == STT_PARISC_MILLI) + h->dynindx = -1; + + return true; +} + +/* The .opd section contains FPTRs for each function this file + exports. Initialize the FPTR entries. */ + +static 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->h; + 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 && 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) + { + Elf64_Internal_Rela rel; + bfd_vma value; + 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); + + bfd_elf64_swap_reloca_out (sopd->output_section->owner, &rel, + (((Elf64_External_Rela *) + sopdrel->contents) + + sopdrel->reloc_count)); + sopdrel->reloc_count++; + } + 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 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->h; + + 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 && 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 + { + 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; + } + + /* 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 assocated 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)) + { + Elf64_Internal_Rela rel; + 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; + + bfd_elf64_swap_reloca_out (sdlt->output_section->owner, &rel, + (((Elf64_External_Rela *) + sdltrel->contents) + + sdltrel->reloc_count)); + sdltrel->reloc_count++; + } + return true; +} + +/* Finalize the dynamic relocations. Specifically the FPTR relocations + for dynamic functions used to initialize static data. */ + +static 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) + { + Elf64_Internal_Rela rel; + + switch (rent->type) + { + case R_PARISC_FPTR64: + /* Allocate one iff we are not building a shared library and + !want_opd, which by this point will be true only if we're + actually allocating one statically in the main executable. */ + if (!info->shared && dyn_h->want_opd) + continue; + break; + } + + /* 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) + { + bfd_vma value, value2; + asymbol *sym; + int elf_index; + + /* 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); + + bfd_elf64_swap_reloca_out (hppa_info->other_rel_sec->output_section->owner, + &rel, + (((Elf64_External_Rela *) + hppa_info->other_rel_sec->contents) + + hppa_info->other_rel_sec->reloc_count)); + hppa_info->other_rel_sec->reloc_count++; + } + } + + return true; +} + +/* Finish up the dynamic sections. */ + +static 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; + struct elf_link_hash_entry *h; + + BFD_ASSERT (sdyn != NULL); + + dyncon = (Elf64_External_Dyn *) sdyn->contents; + dynconend = (Elf64_External_Dyn *) (sdyn->contents + sdyn->_raw_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->_raw_size; + bfd_elf64_swap_dyn_out (output_bfd, &dyn, dyncon); + break; + + case DT_RELA: + s = hppa_info->other_rel_sec; + if (! s) + s = hppa_info->dlt_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->_raw_size; + s = hppa_info->dlt_rel_sec; + dyn.d_un.d_val += s->_raw_size; + s = hppa_info->opd_rel_sec; + dyn.d_un.d_val += s->_raw_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->_raw_size; + bfd_elf64_swap_dyn_out (output_bfd, &dyn, dyncon); + break; + + } + } + } + + 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 (abfd) + bfd *abfd; +{ + 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 boolean +elf64_hppa_modify_segment_map (abfd) + bfd *abfd; +{ + struct elf_segment_map *m, **pm; + 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, 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) + { + 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; +} + +/* 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, 8, + ELFCLASS64, EV_CURRENT, + bfd_elf64_write_out_phdrs, + bfd_elf64_write_shdrs_and_ehdr, + bfd_elf64_write_relocs, + 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, + NULL, + NULL, + NULL, + NULL +}; + +#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 bfd_elf64_bfd_reloc_type_lookup elf_hppa_reloc_type_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_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 + +/* 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_plt_header_size 0 +#define elf_backend_type_change_ok true + +#include "elf64-target.h" |