From 30667bf3fc37affeac424dc5c7e422afd8880e97 Mon Sep 17 00:00:00 2001 From: Alan Modra Date: Tue, 5 Sep 2000 05:03:43 +0000 Subject: [PATCH] Shared lib support. --- bfd/ChangeLog | 88 + bfd/elf32-hppa.c | 4004 ++++++++++++++++++++++++++++++++++++---------- bfd/elf32-hppa.h | 7 +- 3 files changed, 3283 insertions(+), 816 deletions(-) diff --git a/bfd/ChangeLog b/bfd/ChangeLog index ab44781912..f7c186cf94 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,5 +1,93 @@ 2000-09-05 Alan Modra + Shared library and PIC support. + * elf32-hppa.c (PLT_ENTRY_SIZE): New. + (GOT_ENTRY_SIZE): New. + (ELF_DYNAMIC_INTERPRETER): New. + (STUB_SUFFIX): Define. + (LONG_BRANCH_PIC_IN_SHLIB): Define. + (RELATIVE_DYNAMIC_RELOCS): Define. + (enum elf32_hppa_stub_type): New. + (struct elf32_hppa_stub_hash_entry): Rename offset to + stub_offset. Add a number of new fields. + (struct elf32_hppa_link_hash_entry): New. + (struct elf32_hppa_link_hash_table): Add numerous fields. Remove + global_value. + (elf32_hppa_hash_table): Rename to hppa_link_hash_table. + (elf32_hppa_stub_hash_lookup): Rename to hppa_stub_hash_lookup. + (elf32_hppa_stub_hash_newfunc): Rename to stub_hash_newfunc. Init + new fields. + (hppa_link_hash_newfunc): New function. + (elf32_hppa_link_hash_table_create): Use above function. Init new + fields. + (elf32_hppa_stub_name): Rename to hppa_stub_name. Pass in reloc + instead of addend, and remove sym_name from args. Don't use + symbol name for local syms, instead use sym index. + (elf32_hppa_size_of_stub): Rename to hppa_type_of_stub, and return + stub type rather than size. Pass in hash and handle import stub + case. Also pass in reloc instead of offset so we can calculate + PCREL22F and PCREL12F branches properly. + (elf32_hppa_build_one_stub): Rename to hppa_build_one_stub. Build + import and export stubs too. + (elf32_hppa_size_one_stub): Rename to hppa_size_one_stub. Handle + import and export stub sizing. + (elf32_hppa_check_relocs): New function. + (elf32_hppa_adjust_dynamic_symbol): New function. + (hppa_discard_copies): New function. + (elf32_hppa_size_dynamic_sections): New function. + (elf_backend_size_dynamic_sections): Define. + (elf32_hppa_finish_dynamic_symbol): New function. + (elf_backend_finish_dynamic_symbol): Define. + (elf32_hppa_size_stubs): Stash params in link hash table, and move + some local vars into the link hash table too. For shared links, + trundle over function syms, generating export stubs. Handle + PCREL22F branches. Break out stub creation code from here.. + (hppa_add_stub): .. to here. New function. + (elf32_hppa_final_link): Rename to elf32_hppa_set_gp, and don't + call the bfd linker. Use elf_gp to record global pointer. + Calculate a value from sections if $global$ is missing. + (bfd_elf32_bfd_final_link): Define as _bfd_elf32_gc_common_final_link. + (elf32_hppa_gc_mark_hook): New function. + (elf_backend_gc_mark_hook): Define. + (elf32_hppa_gc_sweep_hook): New function. + (elf_backend_gc_sweep_hook): Define. + (elf32_hppa_bfd_final_link_relocate): Rename to final_link_relocate. + Add rel to args, and remove howto, input_bfd, offset, addend, + sym_name as we can recalculate these locally. Handle calls to + dynamic objects, extra PIC relocs, PCREL22F branches. Change + handling of undefined weak syms. Check that stubs are in range. + Only look for import stubs on PCREL17F and PCREL22F relocs. Add + message on hitting a DPREL21L reloc that needs fixing. Subtract + off PC for PCREL14F. Break out code that does a stub lookup from + here.. + (hppa_get_stub_entry): ..to here. New function. + (elf32_hppa_relocate_insn): Merge into final_link_relocate. + (elf32_hppa_relocate_section): Handle got and plt relocs, dynamic + relocs, etc. etc. + (elf32_hppa_finish_dynamic_sections): New function. + (elf_backend_finish_dynamic_sections): Define. + (elf_backend_final_write_processing) Define. + (hppa_handle_PIC_calls): New function. + (elf32_hppa_build_stubs): Call it. Pass link_info to + hppa_build_one_stub. + (elf32_hppa_create_dynamic_sections): New function + to create .plt and .got then set .plt flags correctly. + (elf_backend_create_dynamic_sections): Define. + (elf32_hppa_object_p): New function. + (elf_backend_object_p): Define. + (elf32_hppa_elf_get_symbol_type): New function. + (elf_backend_get_symbol_type): Define. + (elf_backend_can_gc_sections): Define. + (elf_backend_want_got_plt): Define. + (elf_backend_plt_alignment): Set to 2. + (elf_backend_plt_readonly): Define. + (elf_backend_want_plt_sym): Define. + (elf_backend_got_header_size): Reserve one entry. + + * elf32-hppa.h (elf32_hppa_build_stubs): Don't pass stub bfd. + (elf32_hppa_size_stubs): Pass in multi_subspace. + (elf32_hppa_set_gp): Declare. + * section.c (SEC_HAS_GOT_REF): Define new flag for asection. (bfd_get_unique_section_name): New function. * bfd_in2.h: Regenerate. diff --git a/bfd/elf32-hppa.c b/bfd/elf32-hppa.c index f57911a4b4..b645b5fdb2 100644 --- a/bfd/elf32-hppa.c +++ b/bfd/elf32-hppa.c @@ -2,11 +2,11 @@ Copyright (C) 1990, 91, 92, 93, 94, 95, 96, 97, 98, 99, 2000 Free Software Foundation, Inc. - Written by - + Original code by Center for Software Science Department of Computer Science University of Utah + Largely rewritten by Alan Modra This file is part of BFD, the Binary File Descriptor library. @@ -44,88 +44,309 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ The second is the stub hash table which is derived from the base BFD hash table. The stub hash table holds the information - necessary to build the linker stubs during a link. */ + necessary to build the linker stubs during a link. -/* Hash table for linker stubs. */ + There are a number of different stubs generated by the linker. + + Long branch stub: + : ldil LR'X,%r1 + : be,n RR'X(%sr4,%r1) + + PIC long branch stub: + : b,l .+8,%r1 + : addil L'X - ($PIC_pcrel$0 - 4),%r1 + : be,n R'X - ($PIC_pcrel$0 - 8)(%sr4,%r1) + + Import stub to call shared library routine from normal object file + (single sub-space version) + : addil L'lt_ptr+ltoff,%dp ; get procedure entry point + : ldw R'lt_ptr+ltoff(%r1),%r21 + : bv %r0(%r21) + : ldw R'lt_ptr+ltoff+4(%r1),%r19 ; get new dlt value. + + Import stub to call shared library routine from shared library + (single sub-space version) + : addil L'ltoff,%r19 ; get procedure entry point + : ldw R'ltoff(%r1),%r21 + : bv %r0(%r21) + : ldw R'ltoff+4(%r1),%r19 ; get new dlt value. + + Import stub to call shared library routine from normal object file + (multiple sub-space support) + : addil L'lt_ptr+ltoff,%dp ; get procedure entry point + : ldw R'lt_ptr+ltoff(%r1),%r21 + : ldw R'lt_ptr+ltoff+4(%r1),%r19 ; get new dlt value. + : ldsid (%r21),%r1 + : mtsp %r1,%sr0 + : be 0(%sr0,%r21) ; branch to target + : stw %rp,-24(%sp) ; save rp + + Import stub to call shared library routine from shared library + (multiple sub-space support) + : addil L'ltoff,%r19 ; get procedure entry point + : ldw R'ltoff(%r1),%r21 + : ldw R'ltoff+4(%r1),%r19 ; get new dlt value. + : ldsid (%r21),%r1 + : mtsp %r1,%sr0 + : be 0(%sr0,%r21) ; branch to target + : stw %rp,-24(%sp) ; save rp + + Export stub to return from shared lib routine (multiple sub-space support) + One of these is created for each exported procedure in a shared + library (and stored in the shared lib). Shared lib routines are + called via the first instruction in the export stub so that we can + do an inter-space return. Not required for single sub-space. + : bl,n X,%rp ; trap the return + : nop + : ldw -24(%sp),%rp ; restore the original rp + : ldsid (%rp),%r1 + : mtsp %r1,%sr0 + : be,n 0(%sr0,%rp) ; inter-space return +*/ + +#define PLT_ENTRY_SIZE 8 +#define GOT_ENTRY_SIZE 4 +#define ELF_DYNAMIC_INTERPRETER "/lib/ld.so.1" + +/* Section name for stubs is the associated section name plus this + string. */ +#define STUB_SUFFIX ".stub" + +/* Setting the following non-zero makes all long branch stubs + generated during a shared link of the PIC variety. This saves on + relocs, but costs one extra instruction per stub. */ +#ifndef LONG_BRANCH_PIC_IN_SHLIB +#define LONG_BRANCH_PIC_IN_SHLIB 1 +#endif + +/* We don't need to copy any PC- or GP-relative dynamic relocs into a + shared object's dynamic section. */ +#ifndef RELATIVE_DYNAMIC_RELOCS +#define RELATIVE_DYNAMIC_RELOCS 0 +#endif + + +enum elf32_hppa_stub_type { + hppa_stub_long_branch, + hppa_stub_long_branch_shared, + hppa_stub_import, + hppa_stub_import_shared, + hppa_stub_export, + hppa_stub_none +}; + + +struct elf32_hppa_stub_hash_entry { -struct elf32_hppa_stub_hash_entry -{ /* Base hash table entry structure. */ struct bfd_hash_entry root; /* The stub section. */ asection *stub_sec; +#if ! LONG_BRANCH_PIC_IN_SHLIB + /* It's associated reloc section. */ + asection *reloc_sec; +#endif + /* Offset within stub_sec of the beginning of this stub. */ - bfd_vma offset; + bfd_vma stub_offset; /* Given the symbol's value and its section we can determine its final value when building the stubs (so the stub knows where to jump. */ - symvalue target_value; + bfd_vma target_value; asection *target_section; + + enum elf32_hppa_stub_type stub_type; + + /* The symbol table entry, if any, that this was derived from. */ + struct elf32_hppa_link_hash_entry *h; + + /* Where this stub is being called from. */ + asection *input_section; }; -struct elf32_hppa_link_hash_table -{ + +struct elf32_hppa_link_hash_entry { + + struct elf_link_hash_entry elf; + + /* A pointer to the most recently used stub hash entry against this + symbol. */ + struct elf32_hppa_stub_hash_entry *stub_cache; + +#if ! LONG_BRANCH_PIC_IN_SHLIB + /* Used to track whether we have allocated space for a long branch + stub relocation for this symbol in the given section. */ + asection *stub_reloc_sec; +#endif + +#if ! LONG_BRANCH_PIC_IN_SHLIB || RELATIVE_DYNAMIC_RELOCS + /* Used to count relocations for delayed sizing of relocation + sections. */ + struct elf32_hppa_dyn_reloc_entry { + + /* Next relocation in the chain. */ + struct elf32_hppa_dyn_reloc_entry *next; + + /* The section in dynobj. */ + asection *section; + + /* Number of relocs copied in this section. */ + bfd_size_type count; + } *reloc_entries; +#endif + + /* Set during a static link if we detect a function is PIC. */ + boolean pic_call; +}; + + +struct elf32_hppa_link_hash_table { + /* The main hash table. */ struct elf_link_hash_table root; /* The stub hash table. */ struct bfd_hash_table stub_hash_table; + /* Linker stub bfd. */ + bfd *stub_bfd; + + /* Whether we support multiple sub-spaces for shared libs. */ + boolean multi_subspace; + + /* Linker call-backs. */ + asection * (*add_stub_section) PARAMS ((const char *, asection *)); + void (*layout_sections_again) PARAMS ((void)); + + /* Arrays to keep track of which stub sections have been created. */ + asection **stub_section_created; +#if ! LONG_BRANCH_PIC_IN_SHLIB + asection **reloc_section_created; +#endif + int first_init_sec; + int first_fini_sec; + /* Current offsets in the stub sections. */ bfd_vma *offset; - /* Global data pointer. */ - bfd_vma global_value; + /* Short-cuts to get to dynamic linker sections. */ + asection *sgot; + asection *srelgot; + asection *splt; + asection *srelplt; + asection *sdynbss; + asection *srelbss; }; -/* For linker stub hash tables. */ +/* Functions named elf32_hppa_* are called by external routines, other + functions are only called locally. elf32_hppa_* functions appear + in this file more or less in the order in which they are called + from external routines. eg. elf32_hppa_check_relocs is called + early in the link process, elf32_hppa_finish_dynamic_sections is + one of the last functions. */ -#define elf32_hppa_stub_hash_lookup(table, string, create, copy) \ + +/* Various hash macros and functions. */ +#define hppa_link_hash_table(p) \ + ((struct elf32_hppa_link_hash_table *) ((p)->hash)) + +#define hppa_stub_hash_lookup(table, string, create, copy) \ ((struct elf32_hppa_stub_hash_entry *) \ bfd_hash_lookup ((table), (string), (create), (copy))) -/* Get the PA ELF linker hash table from a link_info structure. */ +static struct bfd_hash_entry *stub_hash_newfunc + PARAMS ((struct bfd_hash_entry *, struct bfd_hash_table *, const char *)); -#define elf32_hppa_hash_table(p) \ - ((struct elf32_hppa_link_hash_table *) ((p)->hash)) - - -static struct bfd_hash_entry *elf32_hppa_stub_hash_newfunc +static struct bfd_hash_entry *hppa_link_hash_newfunc PARAMS ((struct bfd_hash_entry *, struct bfd_hash_table *, const char *)); static struct bfd_link_hash_table *elf32_hppa_link_hash_table_create PARAMS ((bfd *)); -static char *elf32_hppa_stub_name - PARAMS ((const char *, const asection *, const asection *, - bfd_vma, const struct elf_link_hash_entry *)); -static int elf32_hppa_relocate_insn - PARAMS ((int, bfd_vma, bfd_signed_vma, unsigned int, int, - enum hppa_reloc_field_selector_type_alt)); +/* Stub handling functions. */ +static char *hppa_stub_name + PARAMS ((const asection *, const asection *, + const struct elf32_hppa_link_hash_entry *, + const Elf_Internal_Rela *)); -static bfd_reloc_status_type elf32_hppa_bfd_final_link_relocate - PARAMS ((reloc_howto_type *, bfd *, asection *, - bfd_byte *, bfd_vma, bfd_vma, bfd_signed_vma, - struct bfd_link_info *, asection *, const char *, - struct elf_link_hash_entry *)); +static struct elf32_hppa_stub_hash_entry *hppa_get_stub_entry + PARAMS ((const asection *, const asection *, + struct elf32_hppa_link_hash_entry *, + const Elf_Internal_Rela *, struct bfd_link_info *)); -static boolean elf32_hppa_relocate_section - PARAMS ((bfd *, struct bfd_link_info *, bfd *, asection *, - bfd_byte *, Elf_Internal_Rela *, Elf_Internal_Sym *, asection **)); +static struct elf32_hppa_stub_hash_entry *hppa_add_stub + PARAMS ((const char *, asection *, unsigned int, + struct bfd_link_info *)); + +static enum elf32_hppa_stub_type hppa_type_of_stub + PARAMS ((asection *, const Elf_Internal_Rela *, + struct elf32_hppa_link_hash_entry *, bfd_vma)); + +static boolean hppa_build_one_stub + PARAMS ((struct bfd_hash_entry *, PTR)); + +static boolean hppa_size_one_stub + PARAMS ((struct bfd_hash_entry *, PTR)); + + +/* BFD and elf backend functions. */ +static boolean elf32_hppa_object_p PARAMS ((bfd *)); static boolean elf32_hppa_add_symbol_hook PARAMS ((bfd *, struct bfd_link_info *, const Elf_Internal_Sym *, const char **, flagword *, asection **, bfd_vma *)); -static unsigned int elf32_hppa_size_of_stub - PARAMS ((asection *, bfd_vma, bfd_vma)); +static boolean elf32_hppa_create_dynamic_sections + PARAMS ((bfd *, struct bfd_link_info *)); -static boolean elf32_hppa_build_one_stub - PARAMS ((struct bfd_hash_entry *, PTR)); +static boolean elf32_hppa_check_relocs + PARAMS ((bfd *, struct bfd_link_info *, + asection *, const Elf_Internal_Rela *)); + +static asection *elf32_hppa_gc_mark_hook + PARAMS ((bfd *, struct bfd_link_info *, Elf_Internal_Rela *, + struct elf_link_hash_entry *, Elf_Internal_Sym *)); + +static boolean elf32_hppa_gc_sweep_hook + PARAMS ((bfd *, struct bfd_link_info *, + asection *, const Elf_Internal_Rela *)); + +static boolean elf32_hppa_adjust_dynamic_symbol + PARAMS ((struct bfd_link_info *, struct elf_link_hash_entry *)); + +static boolean hppa_handle_PIC_calls + PARAMS ((struct elf_link_hash_entry *, PTR)); + +#if ! LONG_BRANCH_PIC_IN_SHLIB || RELATIVE_DYNAMIC_RELOCS +static boolean hppa_discard_copies + PARAMS ((struct elf_link_hash_entry *, PTR)); +#endif + +static boolean elf32_hppa_size_dynamic_sections + PARAMS ((bfd *, struct bfd_link_info *)); + +static bfd_reloc_status_type final_link_relocate + PARAMS ((asection *, bfd_byte *, const Elf_Internal_Rela *, + bfd_vma, struct bfd_link_info *, asection *, + struct elf32_hppa_link_hash_entry *)); + +static boolean elf32_hppa_relocate_section + PARAMS ((bfd *, struct bfd_link_info *, bfd *, asection *, + bfd_byte *, Elf_Internal_Rela *, Elf_Internal_Sym *, asection **)); + +static boolean elf32_hppa_finish_dynamic_symbol + PARAMS ((bfd *, struct bfd_link_info *, + struct elf_link_hash_entry *, Elf_Internal_Sym *)); + +static boolean elf32_hppa_finish_dynamic_sections + PARAMS ((bfd *, struct bfd_link_info *)); + +static int elf32_hppa_elf_get_symbol_type + PARAMS ((Elf_Internal_Sym *, int)); /* Assorted hash table functions. */ @@ -133,7 +354,7 @@ static boolean elf32_hppa_build_one_stub /* Initialize an entry in the stub hash table. */ static struct bfd_hash_entry * -elf32_hppa_stub_hash_newfunc (entry, table, string) +stub_hash_newfunc (entry, table, string) struct bfd_hash_entry *entry; struct bfd_hash_table *table; const char *string; @@ -145,11 +366,13 @@ elf32_hppa_stub_hash_newfunc (entry, table, string) /* Allocate the structure if it has not already been allocated by a subclass. */ if (ret == NULL) - ret = ((struct elf32_hppa_stub_hash_entry *) - bfd_hash_allocate (table, - sizeof (struct elf32_hppa_stub_hash_entry))); - if (ret == NULL) - return NULL; + { + ret = ((struct elf32_hppa_stub_hash_entry *) + bfd_hash_allocate (table, + sizeof (struct elf32_hppa_stub_hash_entry))); + if (ret == NULL) + return NULL; + } /* Call the allocation method of the superclass. */ ret = ((struct elf32_hppa_stub_hash_entry *) @@ -159,14 +382,66 @@ elf32_hppa_stub_hash_newfunc (entry, table, string) { /* Initialize the local fields. */ ret->stub_sec = NULL; - ret->offset = 0; +#if ! LONG_BRANCH_PIC_IN_SHLIB + ret->reloc_sec = NULL; +#endif + ret->stub_offset = 0; ret->target_value = 0; ret->target_section = NULL; + ret->stub_type = hppa_stub_long_branch; + ret->h = NULL; + ret->input_section = NULL; } return (struct bfd_hash_entry *) ret; } + +/* Initialize an entry in the link hash table. */ + +static struct bfd_hash_entry * +hppa_link_hash_newfunc (entry, table, string) + struct bfd_hash_entry *entry; + struct bfd_hash_table *table; + const char *string; +{ + struct elf32_hppa_link_hash_entry *ret; + + ret = (struct elf32_hppa_link_hash_entry *) entry; + + /* Allocate the structure if it has not already been allocated by a + subclass. */ + if (ret == NULL) + { + ret = ((struct elf32_hppa_link_hash_entry *) + bfd_hash_allocate (table, + sizeof (struct elf32_hppa_link_hash_entry))); + if (ret == NULL) + return NULL; + } + + /* Call the allocation method of the superclass. */ + ret = ((struct elf32_hppa_link_hash_entry *) + _bfd_elf_link_hash_newfunc ((struct bfd_hash_entry *) ret, + table, string)); + + if (ret) + { + /* Initialize the local fields. */ +#if ! LONG_BRANCH_PIC_IN_SHLIB + ret->stub_reloc_sec = NULL; +#endif + ret->stub_cache = NULL; +#if ! LONG_BRANCH_PIC_IN_SHLIB || RELATIVE_DYNAMIC_RELOCS + ret->reloc_entries = NULL; +#endif + ret->pic_call = 0; + } + + return (struct bfd_hash_entry *) ret; +} + + /* Create the derived linker hash table. The PA ELF port uses the derived hash table to keep information specific to the PA ELF linker (without using static variables). */ @@ -181,574 +456,685 @@ elf32_hppa_link_hash_table_create (abfd) if (ret == NULL) return NULL; - if (!_bfd_elf_link_hash_table_init (&ret->root, abfd, - _bfd_elf_link_hash_newfunc)) + if (!_bfd_elf_link_hash_table_init (&ret->root, abfd, hppa_link_hash_newfunc)) { bfd_release (abfd, ret); return NULL; } /* Init the stub hash table too. */ - if (!bfd_hash_table_init (&ret->stub_hash_table, - elf32_hppa_stub_hash_newfunc)) + if (!bfd_hash_table_init (&ret->stub_hash_table, stub_hash_newfunc)) return NULL; + ret->stub_bfd = NULL; + ret->multi_subspace = 0; + ret->add_stub_section = NULL; + ret->layout_sections_again = NULL; + ret->stub_section_created = NULL; +#if ! LONG_BRANCH_PIC_IN_SHLIB + ret->reloc_section_created = NULL; +#endif + ret->first_init_sec = 0; + ret->first_fini_sec = 0; ret->offset = NULL; - ret->global_value = 0; + ret->sgot = NULL; + ret->srelgot = NULL; + ret->splt = NULL; + ret->srelplt = NULL; + ret->sdynbss = NULL; + ret->srelbss = NULL; return &ret->root.root; } -/* Build a name for a long branch stub. */ + +/* Build a name for an entry in the stub hash table. */ + static char * -elf32_hppa_stub_name (sym_name, sym_sec, input_section, addend, hash) - const char *sym_name; - const asection *sym_sec; +hppa_stub_name (input_section, sym_sec, hash, rel) const asection *input_section; - bfd_vma addend; - const struct elf_link_hash_entry *hash; + const asection *sym_sec; + const struct elf32_hppa_link_hash_entry *hash; + const Elf_Internal_Rela *rel; { char *stub_name; - int len; + unsigned int len; - len = strlen (sym_name) + 19; - if (hash == NULL) - len += 9; - - stub_name = bfd_malloc (len); - if (stub_name != NULL) + if (hash) { - sprintf (stub_name, "%08x_%08x_%s", - input_section->id & 0xffffffff, - (int) addend & 0xffffffff, - sym_name); - - /* Tack on an ID so we can uniquely identify - this local symbol in the stub hash tables. */ - if (hash == NULL) - sprintf (stub_name + len - 10, "_%08x", - sym_sec->id & 0xffffffff); + len = 8 + 1 + strlen (hash->elf.root.root.string) + 1 + 8 + 1; + stub_name = bfd_malloc (len); + if (stub_name != NULL) + { + sprintf (stub_name, "%08x_%s+%x", + input_section->id & 0xffffffff, + hash->elf.root.root.string, + (int) rel->r_addend & 0xffffffff); + } + } + else + { + len = 8 + 1 + 8 + 1 + 8 + 1 + 8 + 1; + stub_name = bfd_malloc (len); + if (stub_name != NULL) + { + sprintf (stub_name, "%08x_%x:%x+%x", + input_section->id & 0xffffffff, + sym_sec->id & 0xffffffff, + (int) ELF32_R_SYM (rel->r_info) & 0xffffffff, + (int) rel->r_addend & 0xffffffff); + } } return stub_name; } -/* Relocate the given INSN given the various input parameters. */ -static int -elf32_hppa_relocate_insn (insn, sym_value, r_addend, r_type, r_format, r_field) - int insn; - bfd_vma sym_value; - bfd_signed_vma r_addend; - unsigned int r_type; - int r_format; - enum hppa_reloc_field_selector_type_alt r_field; -{ - int value; -#ifdef ELF_ARG_RELOC -#ifndef ELF_ARG_RELOC_INSN - /* Ick. Who would want to support this? */ - int imm; +/* Look up an entry in the stub hash. Stub entries are cached because + creating the stub name takes a bit of time. */ - switch (r_type) - { - /* The offset is partly stored in the instruction for cases - where the top ten bits of the addend are used for arg_reloc. - This is a little tricky, because the immediate value in the - instruction not only needs to be pieced together from - multiple bit fields, but also needs to be shifted left to - restore the original quantity. Which bits of the offset - we can retrieve from the instruction depend on exactly which - instruction we are dealing with. */ - case R_PARISC_PCREL17R: - case R_PARISC_PCREL17F: - case R_PARISC_PCREL17C: - case R_PARISC_DIR17R: - case R_PARISC_DIR17F: - /* For these relocs, we choose to use the low 10 bits from the - instruction and store the high 22 bits in the reloc addend. - It doesn't matter that the bottom 2 bits of the value are - always zero, as branches must be to a location which is a - multiple of 4. */ -#if 0 - /* It isn't necessary to retrieve the whole immediate, but - this documents what we have in the instruction. */ - imm = (((insn & 0x1f0000) >> 5) - | ((insn & 0x0004) << 8) - | ((insn & 0x1ff8) >> 3)) - ((insn & 1) << 17); - imm <<= 2; - imm = imm & 0x3ff; -#else - imm = (insn & 0x7f8) >> 1; -#endif - r_addend = (r_addend << (BFD_ARCH_SIZE-22)) >> (BFD_ARCH_SIZE-32); - r_addend = r_addend | imm; - break; - - case R_PARISC_PCREL21L: - case R_PARISC_DIR21L: - /* In this case, the instruction stores the high 21 bits of the - value, so we pick off the top 10 bits, and use the reloc - addend to store the low 22 bits. */ -#if 0 - /* It isn't necessary to retrieve the whole immediate, but - this documents what we have in the instruction. */ - imm = (( (insn & 0x000ffe) << 8) - | ((insn & 0x00c000) >> 7) - | ((insn & 0x1f0000) >> 14) - | ((insn & 0x003000) >> 12)) - ((insn & 1) << 20); - imm <<= 11; - imm = imm & ~ 0x3fffff; -#else - /* Just pick off the 10 needed bits, ensuring we sign extend. */ - imm = ((insn & 0x000ff8) << 19) - ((insn & 1) << 31); -#endif - r_addend = imm | (r_addend & 0x3fffff); - break; - - default: - break; - } -#endif -#endif - - switch (r_type) - { - case R_PARISC_PCREL21L: - case R_PARISC_PCREL17C: - case R_PARISC_PCREL17F: - case R_PARISC_PCREL17R: - case R_PARISC_PCREL14R: - /* Adjust PC relative offset. */ - r_addend -= 8; - break; - default: - break; - } - value = hppa_field_adjust (sym_value, r_addend, r_field); - - switch (r_type) - { - case R_PARISC_PCREL17C: - case R_PARISC_PCREL17F: - case R_PARISC_PCREL17R: - case R_PARISC_DIR17F: - case R_PARISC_DIR17R: - /* This is a branch. Divide the offset by four. - Note that we need to decide whether it's a branch or - otherwise by inspecting the reloc. Inspecting insn won't - work as insn might be from a .word directive. */ - value >>= 2; - break; - - default: - break; - } - - return hppa_rebuild_insn (insn, value, r_format); -} - -/* Actually perform a relocation as part of a final link. This can get - rather hairy when linker stubs are needed. */ - -static bfd_reloc_status_type -elf32_hppa_bfd_final_link_relocate (howto, input_bfd, input_section, - contents, offset, value, addend, - info, sym_sec, sym_name, h) - reloc_howto_type *howto; - bfd *input_bfd; - asection *input_section; - bfd_byte *contents; - bfd_vma offset; - bfd_vma value; - bfd_signed_vma addend; +static struct elf32_hppa_stub_hash_entry * +hppa_get_stub_entry (input_section, sym_sec, hash, rel, info) + const asection *input_section; + const asection *sym_sec; + struct elf32_hppa_link_hash_entry *hash; + const Elf_Internal_Rela *rel; struct bfd_link_info *info; - asection *sym_sec; - const char *sym_name; - struct elf_link_hash_entry *h; { - int insn; - unsigned int r_type = howto->type; - int r_format = howto->bitsize; - enum hppa_reloc_field_selector_type_alt r_field = e_fsel; - bfd_byte *hit_data = contents + offset; - bfd_vma location; + struct elf32_hppa_stub_hash_entry *stub_entry; - if (r_type == R_PARISC_NONE) - return bfd_reloc_ok; - - insn = bfd_get_32 (input_bfd, hit_data); - - /* Find out where we are and where we're going. */ - location = (offset + - input_section->output_offset + - input_section->output_section->vma); - - switch (r_type) + if (hash != NULL && hash->stub_cache != NULL + && hash->stub_cache->h == hash + && hash->stub_cache->input_section == input_section) { - case R_PARISC_PCREL21L: - case R_PARISC_PCREL17C: - case R_PARISC_PCREL17F: - case R_PARISC_PCREL17R: - case R_PARISC_PCREL14R: - /* Make it a pc relative offset. */ - value -= location; - break; - default: - break; + stub_entry = hash->stub_cache; } - - switch (r_type) + else { - case R_PARISC_DIR32: - case R_PARISC_DIR17F: - case R_PARISC_PCREL17C: - r_field = e_fsel; - break; + struct bfd_hash_table *stub_hash_table; + char *stub_name; - case R_PARISC_DIR21L: - case R_PARISC_PCREL21L: - r_field = e_lrsel; - break; + stub_name = hppa_stub_name (input_section, sym_sec, hash, rel); + if (stub_name == NULL) + return NULL; - case R_PARISC_DIR17R: - case R_PARISC_PCREL17R: - case R_PARISC_DIR14R: - case R_PARISC_PCREL14R: - r_field = e_rrsel; - break; + stub_hash_table = &hppa_link_hash_table (info)->stub_hash_table; - /* For all the DP relative relocations, we need to examine the symbol's - section. If it's a code section, then "data pointer relative" makes - no sense. In that case we don't adjust the "value", and for 21 bit - addil instructions, we change the source addend register from %dp to - %r0. */ - case R_PARISC_DPREL21L: - r_field = e_lrsel; - if (sym_sec != NULL) + stub_entry = hppa_stub_hash_lookup (stub_hash_table, stub_name, + false, false); + if (stub_entry == NULL) { - if ((sym_sec->flags & SEC_CODE) != 0) - { - if ((insn & ((0x3f << 26) | (0x1f << 21))) - == (((int) OP_ADDIL << 26) | (27 << 21))) - { - insn &= ~ (0x1f << 21); - } - } - else - value -= elf32_hppa_hash_table (info)->global_value; + if (hash == NULL || hash->elf.root.type != bfd_link_hash_undefweak) + (*_bfd_error_handler) (_("%s(%s+0x%lx): cannot find stub entry %s"), + bfd_get_filename (input_section->owner), + input_section->name, + (long) rel->r_offset, + stub_name); } - break; - - case R_PARISC_DPREL14R: - r_field = e_rrsel; - if (sym_sec != NULL && (sym_sec->flags & SEC_CODE) == 0) - value -= elf32_hppa_hash_table (info)->global_value; - break; - - case R_PARISC_DPREL14F: - r_field = e_fsel; - if (sym_sec != NULL && (sym_sec->flags & SEC_CODE) == 0) - value -= elf32_hppa_hash_table (info)->global_value; - break; - - case R_PARISC_PLABEL32: - r_field = e_fsel; - break; - - case R_PARISC_PLABEL21L: - r_field = e_lrsel; - break; - - case R_PARISC_PLABEL14R: - r_field = e_rrsel; - break; - - /* This case is separate as it may involve a lot more work - to deal with linker stubs. */ - case R_PARISC_PCREL17F: - r_field = e_fsel; - - /* bfd_link_hash_undefweak symbols have sym_sec == NULL. */ - if (sym_sec == NULL) - break; - - /* Any kind of linker stub needed? */ - /* bfd_vma value is unsigned, so this is testing for offsets - outside the range -0x40000 to +0x3ffff */ - if (value + addend - 8 + 0x40000 >= 0x80000) + else { - struct bfd_hash_table *stub_hash_table; - struct elf32_hppa_stub_hash_entry *stub_entry; - char *stub_name; - - stub_name = elf32_hppa_stub_name (sym_name, sym_sec, - input_section, addend, h); - if (!stub_name) - { - (*_bfd_error_handler) ("%s: %s", - bfd_get_filename (input_bfd), - bfd_errmsg (bfd_get_error ())); - return bfd_reloc_notsupported; - } - - stub_hash_table = &elf32_hppa_hash_table (info)->stub_hash_table; - - stub_entry = elf32_hppa_stub_hash_lookup (stub_hash_table, - stub_name, - false, - false); - if (stub_entry == NULL) - { - (*_bfd_error_handler) - (_("%s: cannot find stub entry %s"), - bfd_get_filename (input_bfd), - stub_name); - free (stub_name); - return bfd_reloc_notsupported; - } - - /* Munge up the value and addend for elf32_hppa_relocate_insn. */ - value = (stub_entry->offset - + stub_entry->stub_sec->output_offset - + stub_entry->stub_sec->output_section->vma - - location); - addend = 0; - - if (value + addend - 8 + 0x40000 >= 0x80000) - { - (*_bfd_error_handler) - (_("%s: cannot reach stub %s, recompile with -ffunction-sections"), - bfd_get_filename (input_bfd), - stub_name); - free (stub_name); - return bfd_reloc_notsupported; - } - - free (stub_name); + if (hash != NULL) + hash->stub_cache = stub_entry; } - break; - /* Something we don't know how to handle. */ - default: - return bfd_reloc_notsupported; + free (stub_name); } - /* bfd_link_hash_undefweak symbols have sym_sec == NULL. */ - if (sym_sec == NULL) - { - BFD_ASSERT (h != NULL && h->root.type == bfd_link_hash_undefweak); - value = 0; - } - - insn = elf32_hppa_relocate_insn (insn, value, addend, - r_type, r_format, r_field); - - /* Update the instruction word. */ - bfd_put_32 (input_bfd, insn, hit_data); - return bfd_reloc_ok; + return stub_entry; } -/* Relocate an HPPA ELF section. */ + +/* Add a new stub entry to the stub hash. Not all fields of the new + stub entry are initialised. */ + +static struct elf32_hppa_stub_hash_entry * +hppa_add_stub (stub_name, section, sec_count, info) + const char *stub_name; + asection *section; + unsigned int sec_count; + struct bfd_link_info *info; +{ + asection *stub_sec; +#if ! LONG_BRANCH_PIC_IN_SHLIB + asection *reloc_sec; +#endif + struct elf32_hppa_stub_hash_entry *stub_entry; + struct elf32_hppa_link_hash_table *hplink; + + hplink = hppa_link_hash_table (info); + stub_sec = hplink->stub_section_created[sec_count]; + if (stub_sec == NULL) + { + int special_sec = 0; + + /* We only want one stub for .init and .fini because glibc + splits the _init and _fini functions into two parts. We + don't want to put a stub in the middle of a function. + It would be better to merge all the stub sections for an + output section if the output section + stubs is small enough. + This would fix the .init and .fini case and also allow stubs + to be merged. It's more linker work though. */ + if (strncmp (section->name, ".init", 5) == 0) + { + stub_sec = hplink->stub_section_created[hplink->first_init_sec]; + special_sec = 1; + } + else if (strncmp (section->name, ".fini", 5) == 0) + { + stub_sec = hplink->stub_section_created[hplink->first_fini_sec]; + special_sec = 2; + } + if (stub_sec == NULL) + { + int len; + char *s_name; + + len = strlen (section->name) + sizeof (STUB_SUFFIX); + s_name = bfd_alloc (hplink->stub_bfd, len); + if (s_name == NULL) + return NULL; + + strcpy (s_name, section->name); + strcpy (s_name + len - sizeof (STUB_SUFFIX), STUB_SUFFIX); + stub_sec = (*hplink->add_stub_section) (s_name, section); + if (stub_sec == NULL) + return NULL; + + if (special_sec != 0) + { + if (special_sec == 1) + hplink->first_init_sec = sec_count; + else + hplink->first_fini_sec = sec_count; + } + } + hplink->stub_section_created[sec_count] = stub_sec; + } + +#if ! LONG_BRANCH_PIC_IN_SHLIB + reloc_sec = hplink->reloc_section_created[sec_count]; + if (reloc_sec == NULL && info->shared) + { + char *name; + + name = bfd_malloc (sizeof ".rela" + strlen (stub_sec->name)); + if (name == NULL) + return NULL; + strcpy (name, ".rela"); + strcpy (name + sizeof ".rela" - 1, stub_sec->name); + reloc_sec = bfd_get_section_by_name (elf_hash_table (info)->dynobj, + name); + hplink->reloc_section_created[sec_count] = reloc_sec; + free (name); + } +#endif + + /* Enter this entry into the linker stub hash table. */ + stub_entry = hppa_stub_hash_lookup (&hplink->stub_hash_table, stub_name, + true, false); + if (stub_entry == NULL) + { + (*_bfd_error_handler) (_("%s: cannot create stub entry %s"), + bfd_get_filename (section->owner), + stub_name); + return NULL; + } + + stub_entry->stub_sec = stub_sec; +#if ! LONG_BRANCH_PIC_IN_SHLIB + stub_entry->reloc_sec = reloc_sec; +#endif + stub_entry->stub_offset = 0; + stub_entry->input_section = section; + return stub_entry; +} + + +/* Determine the type of stub needed, if any, for a call. */ + +static enum elf32_hppa_stub_type +hppa_type_of_stub (input_sec, rel, hash, destination) + asection *input_sec; + const Elf_Internal_Rela *rel; + struct elf32_hppa_link_hash_entry *hash; + bfd_vma destination; +{ + bfd_vma location; + bfd_vma branch_offset; + bfd_vma max_branch_offset; + unsigned int r_type; + + if (hash != NULL + && (((hash->elf.root.type == bfd_link_hash_defined + || hash->elf.root.type == bfd_link_hash_defweak) + && hash->elf.root.u.def.section->output_section == NULL) + || hash->elf.root.type == bfd_link_hash_undefweak + || hash->elf.root.type == bfd_link_hash_undefined + || hash->pic_call)) + { + /* If output_section is NULL, then it's a symbol defined in a + shared library. We will need an import stub. Decide between + hppa_stub_import and hppa_stub_import_shared later. + For shared links we need stubs for undefined syms too; They + will presumably be resolved by the dynamic linker. */ + return hppa_stub_import; + } + + /* Determine where the call point is. */ + location = (input_sec->output_offset + + input_sec->output_section->vma + + rel->r_offset); + + branch_offset = destination - location - 8; + r_type = ELF32_R_TYPE (rel->r_info); + + /* Determine if a long branch stub is needed. parisc branch offsets + are relative to the second instruction past the branch, ie. +8 + bytes on from the branch instruction location. The offset is + signed and counts in units of 4 bytes. */ + if (r_type == (unsigned int) R_PARISC_PCREL17F) + { + max_branch_offset = (1 << (17-1)) << 2; + } + else if (r_type == (unsigned int) R_PARISC_PCREL12F) + { + max_branch_offset = (1 << (12-1)) << 2; + } + else /* R_PARISC_PCREL22F */ + { + max_branch_offset = (1 << (22-1)) << 2; + } + + if (branch_offset + max_branch_offset >= 2*max_branch_offset) + { +#if 0 + if (hash != NULL + && hash->elf.dynindx != -1 + && hash->elf.plt.offset != (bfd_vma) -1) + { + /* If we are doing a shared link and find we need a long + branch stub, then go via the .plt if possible. */ + return hppa_stub_import; + } + else +#endif + return hppa_stub_long_branch; + } + return hppa_stub_none; +} + + +/* Build one linker stub as defined by the stub hash table entry GEN_ENTRY. + IN_ARG contains the link info pointer. */ + +#define LDIL_R1 0x20200000 /* ldil LR'XXX,%r1 */ +#define BE_SR4_R1 0xe0202002 /* be,n RR'XXX(%sr4,%r1) */ + +#define BL_R1 0xe8200000 /* b,l .+8,%r1 */ +#define ADDIL_R1 0x28200000 /* addil L'XXX,%r1,%r1 */ +#define DEPI_R1 0xd4201c1e /* depi 0,31,2,%r1 */ + +#define ADDIL_DP 0x2b600000 /* addil L'XXX,%dp,%r1 */ +#define LDW_R1_R21 0x48350000 /* ldw R'XXX(%sr0,%r1),%r21 */ +#define BV_R0_R21 0xeaa0c000 /* bv %r0(%r21) */ +#define LDW_R1_R19 0x48330000 /* ldw R'XXX(%sr0,%r1),%r19 */ + +#define ADDIL_R19 0x2a600000 /* addil L'XXX,%r19,%r1 */ +#define LDW_R1_DP 0x483b0000 /* ldw R'XXX(%sr0,%r1),%dp */ + +#define LDSID_R21_R1 0x02a010a1 /* ldsid (%sr0,%r21),%r1 */ +#define MTSP_R1 0x00011820 /* mtsp %r1,%sr0 */ +#define BE_SR0_R21 0xe2a00000 /* be 0(%sr0,%r21) */ +#define STW_RP 0x6bc23fd1 /* stw %rp,-24(%sr0,%sp) */ + +#define BL_RP 0xe8400002 /* b,l,n XXX,%rp */ +#define NOP 0x08000240 /* nop */ +#define LDW_RP 0x4bc23fd1 /* ldw -24(%sr0,%sp),%rp */ +#define LDSID_RP_R1 0x004010a1 /* ldsid (%sr0,%rp),%r1 */ +#define BE_SR0_RP 0xe0400002 /* be,n 0(%sr0,%rp) */ + +#ifndef R19_STUBS +#define R19_STUBS 1 +#endif + +#if R19_STUBS +#define LDW_R1_DLT LDW_R1_R19 +#else +#define LDW_R1_DLT LDW_R1_DP +#endif static boolean -elf32_hppa_relocate_section (output_bfd, info, input_bfd, input_section, - contents, relocs, local_syms, local_sections) - bfd *output_bfd ATTRIBUTE_UNUSED; - struct bfd_link_info *info; - bfd *input_bfd; - asection *input_section; - bfd_byte *contents; - Elf_Internal_Rela *relocs; - Elf_Internal_Sym *local_syms; - asection **local_sections; +hppa_build_one_stub (gen_entry, in_arg) + struct bfd_hash_entry *gen_entry; + PTR in_arg; { - Elf_Internal_Shdr *symtab_hdr; - Elf_Internal_Rela *rel; - Elf_Internal_Rela *relend; + struct elf32_hppa_stub_hash_entry *stub_entry; + struct bfd_link_info *info; + struct elf32_hppa_link_hash_table *hplink; + asection *stub_sec; + bfd *stub_bfd; + bfd_byte *loc; + bfd_vma sym_value; + unsigned int insn; + int size; - symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr; + /* Massage our args to the form they really have. */ + stub_entry = (struct elf32_hppa_stub_hash_entry *) gen_entry; + info = (struct bfd_link_info *) in_arg; - rel = relocs; - relend = relocs + input_section->reloc_count; - for (; rel < relend; rel++) + hplink = hppa_link_hash_table (info); + stub_sec = stub_entry->stub_sec; + + /* Make a note of the offset within the stubs for this entry. */ + stub_entry->stub_offset = hplink->offset[stub_sec->index]; + loc = stub_sec->contents + stub_entry->stub_offset; + + stub_bfd = stub_sec->owner; + + switch (stub_entry->stub_type) { - unsigned int r_type; - reloc_howto_type *howto; - unsigned int r_symndx; - struct elf_link_hash_entry *h; - Elf_Internal_Sym *sym; - asection *sym_sec; - bfd_vma relocation; - bfd_reloc_status_type r; - const char *sym_name; + case hppa_stub_long_branch: + /* Create the long branch. A long branch is formed with "ldil" + loading the upper bits of the target address into a register, + then branching with "be" which adds in the lower bits. + The "be" has its delay slot nullified. */ + sym_value = (stub_entry->target_value + + stub_entry->target_section->output_offset + + stub_entry->target_section->output_section->vma); - r_type = ELF32_R_TYPE (rel->r_info); - if (r_type >= (unsigned int) R_PARISC_UNIMPLEMENTED) + insn = hppa_rebuild_insn ((int) LDIL_R1, + hppa_field_adjust (sym_value, 0, e_lrsel), + 21); + bfd_put_32 (stub_bfd, insn, loc); + + insn = hppa_rebuild_insn ((int) BE_SR4_R1, + hppa_field_adjust (sym_value, 0, e_rrsel) >> 2, + 17); + bfd_put_32 (stub_bfd, insn, loc + 4); + +#if ! LONG_BRANCH_PIC_IN_SHLIB + if (info->shared) { + /* Output a dynamic relocation for this stub. We only + output one PCREL21L reloc per stub, trusting that the + dynamic linker will also fix the implied PCREL17R for the + second instruction. PCREL21L dynamic relocs had better + never be emitted for some other purpose... */ + asection *srel; + Elf_Internal_Rela outrel; + + if (stub_entry->h == NULL) + { + (*_bfd_error_handler) + (_("%s(%s+0x%lx): cannot relocate %s, recompile with -ffunction-sections"), + bfd_get_filename (stub_entry->target_section->owner), + stub_sec->name, + (long) stub_entry->stub_offset, + stub_entry->root.string); + bfd_set_error (bfd_error_bad_value); + return false; + } + + srel = stub_entry->reloc_sec; + if (srel == NULL) + { + (*_bfd_error_handler) + (_("Could not find relocation section for %s"), + stub_sec->name); + bfd_set_error (bfd_error_bad_value); + return false; + } + + outrel.r_offset = (stub_entry->stub_offset + + stub_sec->output_offset + + stub_sec->output_section->vma); + outrel.r_info = ELF32_R_INFO (0, R_PARISC_PCREL21L); + outrel.r_addend = sym_value; + bfd_elf32_swap_reloca_out (stub_sec->output_section->owner, + &outrel, + ((Elf32_External_Rela *) + srel->contents + srel->reloc_count)); + ++srel->reloc_count; + } +#endif + size = 8; + break; + + case hppa_stub_long_branch_shared: + /* Branches are relative. This is where we are going to. */ + sym_value = (stub_entry->target_value + + stub_entry->target_section->output_offset + + stub_entry->target_section->output_section->vma); + + /* And this is where we are coming from, more or less. */ + sym_value -= (stub_entry->stub_offset + + stub_sec->output_offset + + stub_sec->output_section->vma); + + bfd_put_32 (stub_bfd, (unsigned int) BL_R1, loc); + insn = hppa_rebuild_insn ((int) ADDIL_R1, + hppa_field_adjust (sym_value, -8, e_lsel), + 21); + bfd_put_32 (stub_bfd, insn, loc + 4); + + insn = hppa_rebuild_insn ((int) BE_SR4_R1, + hppa_field_adjust (sym_value, -8, e_rsel) >> 2, + 17); + bfd_put_32 (stub_bfd, insn, loc + 8); + size = 12; + break; + + case hppa_stub_import: + case hppa_stub_import_shared: + sym_value = (stub_entry->h->elf.plt.offset + + hplink->splt->output_offset + + hplink->splt->output_section->vma + - elf_gp (hplink->splt->output_section->owner)); + + insn = ADDIL_DP; +#if R19_STUBS + if (stub_entry->stub_type == hppa_stub_import_shared) + insn = ADDIL_R19; +#endif + insn = hppa_rebuild_insn ((int) insn, + hppa_field_adjust (sym_value, 0, e_lsel), + 21); + bfd_put_32 (stub_bfd, insn, loc); + + insn = hppa_rebuild_insn ((int) LDW_R1_R21, + hppa_field_adjust (sym_value, 0, e_rsel), + 14); + bfd_put_32 (stub_bfd, insn, loc + 4); + + if (hplink->multi_subspace) + { + insn = hppa_rebuild_insn ((int) LDW_R1_DLT, + hppa_field_adjust (sym_value, 4, e_rsel), + 14); + bfd_put_32 (stub_bfd, insn, loc + 8); + + bfd_put_32 (stub_bfd, (unsigned int) LDSID_R21_R1, loc + 12); + bfd_put_32 (stub_bfd, (unsigned int) MTSP_R1, loc + 16); + bfd_put_32 (stub_bfd, (unsigned int) BE_SR0_R21, loc + 20); + bfd_put_32 (stub_bfd, (unsigned int) STW_RP, loc + 24); + + size = 28; + } + else + { + bfd_put_32 (stub_bfd, (unsigned int) BV_R0_R21, loc + 8); + insn = hppa_rebuild_insn ((int) LDW_R1_DLT, + hppa_field_adjust (sym_value, 4, e_rsel), + 14); + bfd_put_32 (stub_bfd, insn, loc + 12); + + size = 16; + } + + if (!info->shared + && stub_entry->h != NULL + && stub_entry->h->pic_call) + { + /* Build the .plt entry needed to call a PIC function from + statically linked code. We don't need any relocs. */ + bfd *dynobj; + struct elf32_hppa_link_hash_entry *eh; + bfd_vma value; + + dynobj = elf_hash_table (info)->dynobj; + eh = (struct elf32_hppa_link_hash_entry *) stub_entry->h; + + BFD_ASSERT (eh->elf.root.type == bfd_link_hash_defined + || eh->elf.root.type == bfd_link_hash_defweak); + + value = (eh->elf.root.u.def.value + + eh->elf.root.u.def.section->output_offset + + eh->elf.root.u.def.section->output_section->vma); + + /* Fill in the entry in the procedure linkage table. + + The format of a plt entry is + <__gp>. */ + + bfd_put_32 (hplink->splt->owner, value, + hplink->splt->contents + eh->elf.plt.offset); + value = elf_gp (hplink->splt->output_section->owner); + bfd_put_32 (hplink->splt->owner, value, + hplink->splt->contents + eh->elf.plt.offset + 4); + } + break; + + case hppa_stub_export: + /* Branches are relative. This is where we are going to. */ + sym_value = (stub_entry->target_value + + stub_entry->target_section->output_offset + + stub_entry->target_section->output_section->vma); + + /* And this is where we are coming from. */ + sym_value -= (stub_entry->stub_offset + + stub_sec->output_offset + + stub_sec->output_section->vma); + + if (sym_value - 8 + 0x40000 >= 0x80000) + { + (*_bfd_error_handler) + (_("%s(%s+0x%lx): cannot reach %s, recompile with -ffunction-sections"), + bfd_get_filename (stub_entry->target_section->owner), + stub_sec->name, + (long) stub_entry->stub_offset, + stub_entry->root.string); bfd_set_error (bfd_error_bad_value); return false; } - howto = elf_hppa_howto_table + r_type; - r_symndx = ELF32_R_SYM (rel->r_info); + insn = hppa_rebuild_insn ((int) BL_RP, + hppa_field_adjust (sym_value, -8, e_fsel) >> 2, + 17); + bfd_put_32 (stub_bfd, insn, loc); - if (info->relocateable) - { - /* This is a relocateable link. We don't have to change - anything, unless the reloc is against a section symbol, - in which case we have to adjust according to where the - section symbol winds up in the output section. */ - if (r_symndx < symtab_hdr->sh_info) - { - sym = local_syms + r_symndx; - if (ELF_ST_TYPE (sym->st_info) == STT_SECTION) - { - sym_sec = local_sections[r_symndx]; - rel->r_addend += sym_sec->output_offset; - } - } + bfd_put_32 (stub_bfd, (unsigned int) NOP, loc + 4); + bfd_put_32 (stub_bfd, (unsigned int) LDW_RP, loc + 8); + bfd_put_32 (stub_bfd, (unsigned int) LDSID_RP_R1, loc + 12); + bfd_put_32 (stub_bfd, (unsigned int) MTSP_R1, loc + 16); + bfd_put_32 (stub_bfd, (unsigned int) BE_SR0_RP, loc + 20); - continue; - } + /* Point the function symbol at the stub. */ + stub_entry->h->elf.root.u.def.section = stub_sec; + stub_entry->h->elf.root.u.def.value = hplink->offset[stub_sec->index]; - /* This is a final link. */ - h = NULL; - sym = NULL; - sym_sec = NULL; - if (r_symndx < symtab_hdr->sh_info) - { - sym = local_syms + r_symndx; - sym_sec = local_sections[r_symndx]; - relocation = ((ELF_ST_TYPE (sym->st_info) == STT_SECTION - ? 0 : sym->st_value) - + sym_sec->output_offset - + sym_sec->output_section->vma); - } - else - { - int indx; + size = 24; + break; - indx = r_symndx - symtab_hdr->sh_info; - h = elf_sym_hashes (input_bfd)[indx]; - while (h->root.type == bfd_link_hash_indirect - || h->root.type == bfd_link_hash_warning) - h = (struct elf_link_hash_entry *) h->root.u.i.link; - if (h->root.type == bfd_link_hash_defined - || h->root.type == bfd_link_hash_defweak) - { - sym_sec = h->root.u.def.section; - relocation = (h->root.u.def.value - + sym_sec->output_offset - + sym_sec->output_section->vma); - } - else if (h->root.type == bfd_link_hash_undefweak) - relocation = 0; - else - { - if (!((*info->callbacks->undefined_symbol) - (info, h->root.root.string, input_bfd, - input_section, rel->r_offset, true))) - return false; - break; - } - } - - if (h != NULL) - sym_name = h->root.root.string; - else - { - sym_name = bfd_elf_string_from_elf_section (input_bfd, - symtab_hdr->sh_link, - sym->st_name); - if (sym_name == NULL) - return false; - if (*sym_name == '\0') - sym_name = bfd_section_name (input_bfd, sym_sec); - } - - r = elf32_hppa_bfd_final_link_relocate (howto, input_bfd, - input_section, contents, - rel->r_offset, relocation, - rel->r_addend, info, sym_sec, - sym_name, h); - - switch (r) - { - case bfd_reloc_ok: - break; - - case bfd_reloc_undefined: - case bfd_reloc_notsupported: - (*_bfd_error_handler) - (_("%s: cannot handle relocation %s for %s at 0x%x in %s"), - bfd_get_filename (input_bfd), - howto->name, - sym_name, - rel->r_offset, - input_section->name); - return false; - - default: - case bfd_reloc_outofrange: - case bfd_reloc_overflow: - { - if (!((*info->callbacks->reloc_overflow) - (info, sym_name, howto->name, (bfd_vma) 0, - input_bfd, input_section, rel->r_offset))) - return false; - } - break; - } + default: + BFD_FAIL (); + return false; } + hplink->offset[stub_sec->index] += size; return true; } -/* Called after we have seen all the input files/sections, but before - final symbol resolution and section placement has been determined. +#undef LDIL_R1 +#undef BE_SR4_R1 +#undef BL_R1 +#undef ADDIL_R1 +#undef DEPI_R1 +#undef ADDIL_DP +#undef LDW_R1_R21 +#undef LDW_R1_DLT +#undef LDW_R1_R19 +#undef ADDIL_R19 +#undef LDW_R1_DP +#undef LDSID_R21_R1 +#undef MTSP_R1 +#undef BE_SR0_R21 +#undef STW_RP +#undef BV_R0_R21 +#undef BL_RP +#undef NOP +#undef LDW_RP +#undef LDSID_RP_R1 +#undef BE_SR0_RP - We use this hook to (possibly) provide a value for __gp, then we - fall back to the generic ELF final link routine. */ -boolean -elf32_hppa_final_link (abfd, info) - bfd *abfd; - struct bfd_link_info *info; +/* As above, but don't actually build the stub. Just bump offset so + we know stub section sizes. */ + +static boolean +hppa_size_one_stub (gen_entry, in_arg) + struct bfd_hash_entry *gen_entry; + PTR in_arg; { - if (!info->relocateable) + struct elf32_hppa_stub_hash_entry *stub_entry; + struct elf32_hppa_link_hash_table *hplink; + int size; + + /* Massage our args to the form they really have. */ + stub_entry = (struct elf32_hppa_stub_hash_entry *) gen_entry; + hplink = (struct elf32_hppa_link_hash_table *) in_arg; + + if (stub_entry->stub_type == hppa_stub_long_branch) + size = 8; + else if (stub_entry->stub_type == hppa_stub_long_branch_shared) + size = 12; + else if (stub_entry->stub_type == hppa_stub_export) + size = 24; + else /* hppa_stub_import or hppa_stub_import_shared */ { - struct elf_link_hash_entry *h; - asection *sec; - bfd_vma gp_val; - - h = elf_link_hash_lookup (elf_hash_table (info), "$global$", - false, false, false); - - if (h != NULL - && h->root.type == bfd_link_hash_defined) - { - gp_val = h->root.u.def.value; - sec = h->root.u.def.section; - } + if (hplink->multi_subspace) + size = 28; else - { - /* If $global$ isn't defined, we could make one up ourselves - from the start of .plt, .dlt, or .data For the time - being, just bomb. */ - (*info->callbacks->undefined_symbol) - (info, "$global$", abfd, NULL, 0, true); - return false; - } - - elf32_hppa_hash_table (info)->global_value = (gp_val - + sec->output_section->vma - + sec->output_offset); + size = 16; } - /* Invoke the standard linker. */ - return bfd_elf_bfd_final_link (abfd, info); + hplink->offset[stub_entry->stub_sec->index] += size; + return true; } + +/* Return nonzero if ABFD represents an HPPA ELF32 file. + Additionally we set the default architecture and machine. */ + +static boolean +elf32_hppa_object_p (abfd) + bfd *abfd; +{ + unsigned int flags = elf_elfheader (abfd)->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: + 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); + } + return true; +} + + /* Undo the generic ELF code's subtraction of section->vma from the value of each external symbol. */ @@ -766,175 +1152,1117 @@ elf32_hppa_add_symbol_hook (abfd, info, sym, namep, flagsp, secp, valp) return true; } -/* Compute the size of the stub needed to call from INPUT_SEC (OFFSET) - to DESTINATION. Return zero if no stub is needed to perform such a - call. */ -static unsigned int -elf32_hppa_size_of_stub (input_sec, offset, destination) - asection *input_sec; - bfd_vma offset; - bfd_vma destination; -{ - bfd_vma location; - - /* Determine where the call point is. */ - location = (input_sec->output_offset - + input_sec->output_section->vma - + offset); - - /* Determine if a long branch stub is needed. parisc branch offsets - are relative to the second instruction past the branch, ie. +8 - bytes on from the branch instruction location. The offset is - signed, 17 bits wide, and counts in units of 4 bytes. - bfd_vma is unsigned, so this is testing for offsets outside the - range -0x40000 to +0x3ffff */ - if (destination - location - 8 + 0x40000 >= 0x80000) - return 8; - return 0; -} - -/* Build one linker stub as defined by the stub hash table entry GEN_ENTRY. - IN_ARG contains the link info pointer. */ - -#define LDIL_R1 0x20200000 /* ldil LR'XXX,%r1 */ -#define BE_SR4_R1 0xe0202002 /* be,n RR'XXX(%sr4,%r1) */ +/* Create the .plt and .got sections, and set up our hash table + short-cuts to various dynamic sections. */ static boolean -elf32_hppa_build_one_stub (gen_entry, in_arg) - struct bfd_hash_entry *gen_entry; - PTR in_arg; +elf32_hppa_create_dynamic_sections (abfd, info) + bfd *abfd; + struct bfd_link_info *info; { - struct elf32_hppa_stub_hash_entry *stub_entry; - struct elf32_hppa_link_hash_table *hppa_link_hash; - asection *stub_sec; - bfd *stub_bfd; - bfd_byte *loc; - symvalue sym_value; - int insn; + flagword flags; + asection *s; + struct elf32_hppa_link_hash_table *hplink; - /* Massage our args to the form they really have. */ - stub_entry = (struct elf32_hppa_stub_hash_entry *) gen_entry; - hppa_link_hash = (struct elf32_hppa_link_hash_table *) in_arg; + /* Don't try to create the .plt and .got twice. */ + hplink = hppa_link_hash_table (info); + if (hplink->splt != NULL) + return true; - stub_sec = stub_entry->stub_sec; + /* Call the generic code to do most of the work. */ + if (! _bfd_elf_create_dynamic_sections (abfd, info)) + return false; - /* Make a note of the offset within the stubs for this entry. */ - stub_entry->offset = hppa_link_hash->offset[stub_sec->index]; - loc = stub_sec->contents + stub_entry->offset; + /* Our .plt just contains pointers. I suppose we should be using + .plt.got but .plt.got doesn't make too much sense without a .plt + section. Set the flags to say the .plt isn't executable. */ + s = bfd_get_section_by_name (abfd, ".plt"); + flags = bfd_get_section_flags (abfd, s); + if (! bfd_set_section_flags (abfd, s, flags & ~SEC_CODE)) + return false; + hplink->splt = s; + hplink->srelplt = bfd_get_section_by_name (abfd, ".rela.plt"); - sym_value = (stub_entry->target_value - + stub_entry->target_section->output_offset - + stub_entry->target_section->output_section->vma); + hplink->sgot = bfd_get_section_by_name (abfd, ".got"); + hplink->srelgot = bfd_make_section (abfd, ".rela.got"); + if (hplink->srelgot == NULL + || ! bfd_set_section_flags (abfd, hplink->srelgot, + (SEC_ALLOC + | SEC_LOAD + | SEC_HAS_CONTENTS + | SEC_IN_MEMORY + | SEC_LINKER_CREATED + | SEC_READONLY)) + || ! bfd_set_section_alignment (abfd, hplink->srelgot, 2)) + return false; - stub_bfd = stub_sec->owner; - - /* Create the long branch. A long branch is formed with "ldil" - loading the upper bits of the target address into a register, - then branching with "be" which adds in the lower bits. - The "be" has its delay slot nullified. */ - insn = hppa_rebuild_insn (LDIL_R1, - hppa_field_adjust (sym_value, 0, e_lrsel), - 21); - bfd_put_32 (stub_bfd, insn, loc); - - insn = hppa_rebuild_insn (BE_SR4_R1, - hppa_field_adjust (sym_value, 0, e_rrsel) >> 2, - 17); - bfd_put_32 (stub_bfd, insn, loc + 4); - - hppa_link_hash->offset[stub_sec->index] += 8; + hplink->sdynbss = bfd_get_section_by_name (abfd, ".dynbss"); + hplink->srelbss = bfd_get_section_by_name (abfd, ".rela.bss"); return true; } -/* As above, but don't actually build the stub. Just bump offset so - we know stub section sizes. */ + +/* Look through the relocs for a section during the first phase, and + allocate space in the global offset table or procedure linkage + table. At this point we haven't necessarily read all the input + files. */ static boolean -elf32_hppa_size_one_stub (gen_entry, in_arg) - struct bfd_hash_entry *gen_entry; - PTR in_arg; +elf32_hppa_check_relocs (abfd, info, sec, relocs) + bfd *abfd; + struct bfd_link_info *info; + asection *sec; + const Elf_Internal_Rela *relocs; { - struct elf32_hppa_stub_hash_entry *stub_entry; - struct elf32_hppa_link_hash_table *hppa_link_hash; + bfd *dynobj; + Elf_Internal_Shdr *symtab_hdr; + struct elf_link_hash_entry **sym_hashes; + bfd_signed_vma *local_got_refcounts; + const Elf_Internal_Rela *rel; + const Elf_Internal_Rela *rel_end; + struct elf32_hppa_link_hash_table *hplink; + asection *sreloc; + asection *stubreloc; - /* Massage our args to the form they really have. */ - stub_entry = (struct elf32_hppa_stub_hash_entry *) gen_entry; - hppa_link_hash = (struct elf32_hppa_link_hash_table *) in_arg; + if (info->relocateable) + return true; - hppa_link_hash->offset[stub_entry->stub_sec->index] += 8; + hplink = hppa_link_hash_table (info); + dynobj = elf_hash_table (info)->dynobj; + symtab_hdr = &elf_tdata (abfd)->symtab_hdr; + sym_hashes = elf_sym_hashes (abfd); + local_got_refcounts = elf_local_got_refcounts (abfd); + sreloc = NULL; + stubreloc = NULL; - return true; -} - -/* External entry points for sizing and building linker stubs. */ - -/* Build all the stubs associated with the current output file. The - stubs are kept in a hash table attached to the main linker hash - table. This is called via hppaelf_finish in the linker. */ - -boolean -elf32_hppa_build_stubs (stub_bfd, link_info) - bfd *stub_bfd; - struct bfd_link_info *link_info; -{ - asection *stub_sec; - struct bfd_hash_table *table; - struct elf32_hppa_link_hash_table *hppa_link_hash; - - for (stub_sec = stub_bfd->sections; stub_sec; stub_sec = stub_sec->next) + rel_end = relocs + sec->reloc_count; + for (rel = relocs; rel < rel_end; rel++) { - unsigned int size; + enum { + NEED_GOT = 1, + NEED_PLT = 2, + NEED_DYNREL = 4, +#if LONG_BRANCH_PIC_IN_SHLIB + NEED_STUBREL = 0 /* We won't be needing them in this case. */ +#else + NEED_STUBREL = 8 +#endif + }; - /* Allocate memory to hold the linker stubs. */ - size = bfd_section_size (stub_bfd, stub_sec); - stub_sec->contents = (unsigned char *) bfd_zalloc (stub_bfd, size); - if (stub_sec->contents == NULL && size != 0) + unsigned int r_symndx, r_type; + struct elf32_hppa_link_hash_entry *h; + int need_entry; + + r_symndx = ELF32_R_SYM (rel->r_info); + + if (r_symndx < symtab_hdr->sh_info) + h = NULL; + else + h = ((struct elf32_hppa_link_hash_entry *) + sym_hashes[r_symndx - symtab_hdr->sh_info]); + + r_type = ELF32_R_TYPE (rel->r_info); + + switch (r_type) + { + case R_PARISC_DLTIND14F: + case R_PARISC_DLTIND14R: + case R_PARISC_DLTIND21L: + /* This symbol requires a global offset table entry. */ + need_entry = NEED_GOT; + + /* Mark this section as containing PIC code. */ + sec->flags |= SEC_HAS_GOT_REF; + break; + + case R_PARISC_PLABEL14R: /* "Official" procedure labels. */ + case R_PARISC_PLABEL21L: + case R_PARISC_PLABEL32: + if (h == NULL) + { + /* If this is a local symbol we do not need to create a + PLT entry, but if we are creating a shared object we + have to output a relocation for the PLABEL itself. */ + need_entry = NEED_DYNREL; + } + else + { + /* If it is a global symbol, then we do need to create a + PLT entry, and additionally, if we are creating a + shared object, we need to output a dynamic relocation + pointing to that PLT entry. */ + need_entry = NEED_PLT | NEED_DYNREL; + } + break; + + case R_PARISC_PCREL12F: + case R_PARISC_PCREL17C: + case R_PARISC_PCREL17F: + case R_PARISC_PCREL22F: + /* Handle calls, and function pointers as they might need to + go through the .plt, and might require long branch stubs. */ + if (h == NULL) + { + /* We know local syms won't need a .plt entry, and if + they need a long branch stub we can't guarantee that + we can reach the stub. So just flag an error later + if we're doing a shared link and find we need a long + branch stub. */ + continue; + } + else + { + /* Global symbols will need a .plt entry if they remain + global, and in most cases won't need a long branch + stub. Unfortunately, we have to cater for the case + where a symbol is forced local by versioning, or due + to symbolic linking, and we lose the .plt entry. */ + need_entry = NEED_PLT | NEED_STUBREL; + } + break; + + case R_PARISC_SEGBASE: /* Used to set segment base. */ + case R_PARISC_SEGREL32: /* Relative reloc. */ + case R_PARISC_PCREL14F: /* PC relative load/store. */ + case R_PARISC_PCREL14R: + case R_PARISC_PCREL17R: /* External branches. */ + case R_PARISC_PCREL21L: /* As above, and for load/store too. */ + /* We don't need to propagate the relocation if linking a + shared object since these are section relative. */ + continue; + + case R_PARISC_DPREL14F: /* Used for gp rel data load/store. */ + case R_PARISC_DPREL14R: + case R_PARISC_DPREL21L: + if (info->shared) + { + (*_bfd_error_handler) + (_("%s: relocation %s can not be used when making a shared object; recompile with -fPIC"), + bfd_get_filename (abfd), + elf_hppa_howto_table[r_type].name); + bfd_set_error (bfd_error_bad_value); + return false; + } + /* Fall through. */ + + case R_PARISC_DIR17F: /* Used for external branches. */ + case R_PARISC_DIR17R: + case R_PARISC_DIR14R: /* Used for load/store from absolute locn. */ + case R_PARISC_DIR21L: /* As above, and for ext branches too. */ +#if 1 + /* Help debug shared library creation. Any of the above + relocs can be used in shared libs, but they may cause + pages to become unshared. */ + if (info->shared) + { + (*_bfd_error_handler) + (_("%s: relocation %s should not be used when making a shared object; recompile with -fPIC"), + bfd_get_filename (abfd), + elf_hppa_howto_table[r_type].name); + } + /* Fall through. */ +#endif + + case R_PARISC_DIR32: /* .word, PARISC.unwind relocs. */ + /* We may want to output a dynamic relocation later. */ + need_entry = NEED_DYNREL; + break; + + /* This relocation describes the C++ object vtable hierarchy. + Reconstruct it for later use during GC. */ + case R_PARISC_GNU_VTINHERIT: + if (!_bfd_elf32_gc_record_vtinherit (abfd, sec, + &h->elf, rel->r_offset)) + return false; + continue; + + /* This relocation describes which C++ vtable entries are actually + used. Record for later use during GC. */ + case R_PARISC_GNU_VTENTRY: + if (!_bfd_elf32_gc_record_vtentry (abfd, sec, + &h->elf, rel->r_offset)) + return false; + continue; + + default: + continue; + } + + /* Now carry out our orders. */ + if (need_entry & NEED_GOT) + { + /* Allocate space for a GOT entry, as well as a dynamic + relocation for this entry. */ + if (dynobj == NULL) + elf_hash_table (info)->dynobj = dynobj = abfd; + + if (hplink->sgot == NULL) + { + if (! elf32_hppa_create_dynamic_sections (dynobj, info)) + return false; + } + + if (h != NULL) + { + if (h->elf.got.refcount == -1) + { + h->elf.got.refcount = 1; + + /* Make sure this symbol is output as a dynamic symbol. */ + if (h->elf.dynindx == -1) + { + if (! bfd_elf32_link_record_dynamic_symbol (info, + &h->elf)) + return false; + } + + hplink->sgot->_raw_size += GOT_ENTRY_SIZE; + hplink->srelgot->_raw_size += sizeof (Elf32_External_Rela); + } + else + h->elf.got.refcount += 1; + } + else + { + /* This is a global offset table entry for a local symbol. */ + if (local_got_refcounts == NULL) + { + size_t size; + + size = symtab_hdr->sh_info * sizeof (bfd_signed_vma); + local_got_refcounts = ((bfd_signed_vma *) + bfd_alloc (abfd, size)); + if (local_got_refcounts == NULL) + return false; + elf_local_got_refcounts (abfd) = local_got_refcounts; + memset (local_got_refcounts, -1, size); + } + if (local_got_refcounts[r_symndx] == -1) + { + local_got_refcounts[r_symndx] = 1; + + hplink->sgot->_raw_size += GOT_ENTRY_SIZE; + if (info->shared) + { + /* If we are generating a shared object, we need to + output a reloc so that the dynamic linker can + adjust this GOT entry (because the address + the shared library is loaded at is not fixed). */ + hplink->srelgot->_raw_size += + sizeof (Elf32_External_Rela); + } + } + else + local_got_refcounts[r_symndx] += 1; + } + } + + if (need_entry & NEED_PLT) + { + /* If we are creating a shared library, and this is a reloc + against a weak symbol or a global symbol in a dynamic + object, then we will be creating an import stub and a + .plt entry for the symbol. Similarly, on a normal link + to symbols defined in a dynamic object we'll need the + import stub and a .plt entry. We don't know yet whether + the symbol is defined or not, so make an entry anyway and + clean up later in adjust_dynamic_symbol. */ + if ((sec->flags & SEC_ALLOC) != 0) + { + if (h->elf.plt.refcount == -1) + { + h->elf.plt.refcount = 1; + h->elf.elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT; + } + else + h->elf.plt.refcount += 1; + } + } + + if (need_entry & (NEED_DYNREL | NEED_STUBREL)) + { + /* Flag this symbol as having a non-got, non-plt reference + so that we generate copy relocs if it turns out to be + dynamic. */ + if (h != NULL) + h->elf.elf_link_hash_flags |= ELF_LINK_NON_GOT_REF; + + /* If we are creating a shared library then we need to copy + the reloc into the shared library. However, if we are + linking with -Bsymbolic, we need only copy absolute + relocs or relocs against symbols that are not defined in + an object we are including in the link. PC- or DP- or + DLT-relative relocs against any local sym or global sym + with DEF_REGULAR set, can be discarded. At this point we + have not seen all the input files, so it is possible that + DEF_REGULAR is not set now but will be set later (it is + never cleared). We account for that possibility below by + storing information in the reloc_entries field of the + hash table entry. + + A similar situation to the -Bsymbolic case occurs when + creating shared libraries and symbol visibility changes + render the symbol local. + + As it turns out, all the relocs we will be creating here + are absolute, so we cannot remove them on -Bsymbolic + links or visibility changes anyway. A STUB_REL reloc + is absolute too, as in that case it is the reloc in the + stub we will be creating, rather than copying the PCREL + reloc in the branch. */ + if ((sec->flags & SEC_ALLOC) != 0 + && info->shared +#if RELATIVE_DYNAMIC_RELOCS + && (!info->symbolic + || is_absolute_reloc (r_type) + || (h != NULL + && ((h->elf.elf_link_hash_flags + & ELF_LINK_HASH_DEF_REGULAR) == 0 + || h->elf.root.type == bfd_link_hash_defweak))) +#endif + ) + { + boolean doit; + asection *srel; + + srel = sreloc; + if ((need_entry & NEED_STUBREL)) + srel = stubreloc; + + /* Create a reloc section in dynobj and make room for + this reloc. */ + if (srel == NULL) + { + char *name; + + if (dynobj == NULL) + elf_hash_table (info)->dynobj = dynobj = abfd; + + name = bfd_elf_string_from_elf_section + (abfd, + elf_elfheader (abfd)->e_shstrndx, + elf_section_data (sec)->rel_hdr.sh_name); + if (name == NULL) + { + (*_bfd_error_handler) + (_("Could not find relocation section for %s"), + sec->name); + bfd_set_error (bfd_error_bad_value); + return false; + } + + if ((need_entry & NEED_STUBREL)) + { + int len = strlen (name) + sizeof (STUB_SUFFIX); + char *newname = bfd_malloc (len); + + if (newname == NULL) + return false; + strcpy (newname, name); + strcpy (newname + len - sizeof (STUB_SUFFIX), + STUB_SUFFIX); + name = newname; + } + + srel = bfd_get_section_by_name (dynobj, name); + if (srel == NULL) + { + flagword flags; + + srel = bfd_make_section (dynobj, name); + flags = (SEC_HAS_CONTENTS | SEC_READONLY + | SEC_IN_MEMORY | SEC_LINKER_CREATED); + if ((sec->flags & SEC_ALLOC) != 0) + flags |= SEC_ALLOC | SEC_LOAD; + if (srel == NULL + || !bfd_set_section_flags (dynobj, srel, flags) + || !bfd_set_section_alignment (dynobj, srel, 2)) + return false; + } + else if ((need_entry & NEED_STUBREL)) + free (name); + + if ((need_entry & NEED_STUBREL)) + stubreloc = srel; + else + sreloc = srel; + } + +#if ! LONG_BRANCH_PIC_IN_SHLIB + /* If this is a function call, we only need one dynamic + reloc for the stub as all calls to a particular + function will go through the same stub. Actually, a + long branch stub needs two relocations, but we count + on some intelligence on the part of the dynamic + linker. */ + if ((need_entry & NEED_STUBREL)) + { + doit = h->stub_reloc_sec != stubreloc; + h->stub_reloc_sec = stubreloc; + } + else +#endif + doit = 1; + + if (doit) + { + srel->_raw_size += sizeof (Elf32_External_Rela); + +#if ! LONG_BRANCH_PIC_IN_SHLIB || RELATIVE_DYNAMIC_RELOCS + /* Keep track of relocations we have entered for + this global symbol, so that we can discard them + later if necessary. */ + if (h != NULL + && (0 +#if RELATIVE_DYNAMIC_RELOCS + || ! is_absolute_reloc (rtype) +#endif + || (need_entry & NEED_STUBREL))) + { + struct elf32_hppa_dyn_reloc_entry *p; + + for (p = h->reloc_entries; p != NULL; p = p->next) + if (p->section == srel) + break; + + if (p == NULL) + { + p = ((struct elf32_hppa_dyn_reloc_entry *) + bfd_alloc (dynobj, sizeof *p)); + if (p == NULL) + return false; + p->next = h->reloc_entries; + h->reloc_entries = p; + p->section = srel; + p->count = 0; + } + + /* NEED_STUBREL and NEED_DYNREL are never both + set. Leave the count at zero for the + NEED_STUBREL case as we only ever have one + stub reloc per section per symbol, and this + simplifies code in hppa_discard_copies. */ + if (! (need_entry & NEED_STUBREL)) + ++p->count; + } +#endif + } + } + } + } + + return true; +} + + +/* Return the section that should be marked against garbage collection + for a given relocation. */ + +static asection * +elf32_hppa_gc_mark_hook (abfd, info, rel, h, sym) + bfd *abfd; + struct bfd_link_info *info ATTRIBUTE_UNUSED; + Elf_Internal_Rela *rel; + struct elf_link_hash_entry *h; + Elf_Internal_Sym *sym; +{ + if (h != NULL) + { + switch ((unsigned int) ELF32_R_TYPE (rel->r_info)) + { + case R_PARISC_GNU_VTINHERIT: + case R_PARISC_GNU_VTENTRY: + break; + + default: + switch (h->root.type) + { + case bfd_link_hash_defined: + case bfd_link_hash_defweak: + return h->root.u.def.section; + + case bfd_link_hash_common: + return h->root.u.c.p->section; + + default: + break; + } + } + } + else + { + if (!(elf_bad_symtab (abfd) + && ELF_ST_BIND (sym->st_info) != STB_LOCAL) + && ! ((sym->st_shndx <= 0 || sym->st_shndx >= SHN_LORESERVE) + && sym->st_shndx != SHN_COMMON)) + { + return bfd_section_from_elf_index (abfd, sym->st_shndx); + } + } + + return NULL; +} + + +/* Update the got and plt entry reference counts for the section being + removed. */ + +static boolean +elf32_hppa_gc_sweep_hook (abfd, info, sec, relocs) + bfd *abfd; + struct bfd_link_info *info ATTRIBUTE_UNUSED; + asection *sec; + const Elf_Internal_Rela *relocs; +{ + Elf_Internal_Shdr *symtab_hdr; + struct elf_link_hash_entry **sym_hashes; + bfd_signed_vma *local_got_refcounts; + const Elf_Internal_Rela *rel, *relend; + unsigned long r_symndx; + struct elf_link_hash_entry *h; + bfd *dynobj; + asection *sgot; + asection *srelgot; + + symtab_hdr = &elf_tdata (abfd)->symtab_hdr; + sym_hashes = elf_sym_hashes (abfd); + local_got_refcounts = elf_local_got_refcounts (abfd); + + dynobj = elf_hash_table (info)->dynobj; + if (dynobj == NULL) + return true; + + sgot = hppa_link_hash_table (info)->sgot; + srelgot = hppa_link_hash_table (info)->srelgot; + + relend = relocs + sec->reloc_count; + for (rel = relocs; rel < relend; rel++) + switch ((unsigned int) ELF32_R_TYPE (rel->r_info)) + { + case R_PARISC_DLTIND14F: + case R_PARISC_DLTIND14R: + case R_PARISC_DLTIND21L: + r_symndx = ELF32_R_SYM (rel->r_info); + if (r_symndx >= symtab_hdr->sh_info) + { + h = sym_hashes[r_symndx - symtab_hdr->sh_info]; + if (h->got.refcount > 0) + { + h->got.refcount -= 1; + if (h->got.refcount == 0) + { + sgot->_raw_size -= 4; + srelgot->_raw_size -= sizeof (Elf32_External_Rela); + } + } + } + else if (local_got_refcounts != NULL) + { + if (local_got_refcounts[r_symndx] > 0) + { + local_got_refcounts[r_symndx] -= 1; + if (local_got_refcounts[r_symndx] == 0) + { + sgot->_raw_size -= 4; + if (info->shared) + srelgot->_raw_size -= sizeof (Elf32_External_Rela); + } + } + } + break; + + case R_PARISC_PLABEL14R: + case R_PARISC_PLABEL21L: + case R_PARISC_PLABEL32: + case R_PARISC_PCREL12F: + case R_PARISC_PCREL17C: + case R_PARISC_PCREL17F: + case R_PARISC_PCREL22F: + r_symndx = ELF32_R_SYM (rel->r_info); + if (r_symndx >= symtab_hdr->sh_info) + { + h = sym_hashes[r_symndx - symtab_hdr->sh_info]; + if (h->plt.refcount > 0) + h->plt.refcount -= 1; + } + break; + + default: + break; + } + + return true; +} + + +/* Adjust a symbol defined by a dynamic object and referenced by a + regular object. The current definition is in some section of the + dynamic object, but we're not including those sections. We have to + change the definition to something the rest of the link can + understand. */ + +static boolean +elf32_hppa_adjust_dynamic_symbol (info, h) + struct bfd_link_info *info; + struct elf_link_hash_entry *h; +{ + bfd *dynobj; + struct elf32_hppa_link_hash_table *hplink; + asection *s; + + dynobj = elf_hash_table (info)->dynobj; + hplink = hppa_link_hash_table (info); + + /* If this is a function, put it in the procedure linkage table. We + will fill in the contents of the procedure linkage table later, + when we know the address of the .got section. */ + if (h->type == STT_FUNC + || (h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT) != 0) + { + if (h->plt.refcount <= 0 + || ((h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) != 0 + && h->root.type != bfd_link_hash_defweak + && (!info->shared || info->symbolic))) + { + /* The .plt entry is not needed when: + a) Garbage collection has removed all references to the + symbol, or + b) We know for certain the symbol is defined in this + object, and it's not a weak definition. Either this + object is the application or we are doing a shared + symbolic link. As a special sop to the hppa ABI, we + keep a .plt entry for functions in sections containing + PIC code. */ + if (!info->shared + && h->plt.refcount > 0 + && (h->root.type == bfd_link_hash_defined + || h->root.type == bfd_link_hash_defweak) + && (h->root.u.def.section->flags & SEC_HAS_GOT_REF) != 0) + { + ((struct elf32_hppa_link_hash_entry *) h)->pic_call = 1; + } + else + { + h->plt.offset = (bfd_vma) -1; + h->elf_link_hash_flags &= ~ELF_LINK_HASH_NEEDS_PLT; + return true; + } + } + + /* Make an entry in the .plt section. */ + s = hplink->splt; + h->plt.offset = s->_raw_size; + s->_raw_size += PLT_ENTRY_SIZE; + + if (! ((struct elf32_hppa_link_hash_entry *) h)->pic_call) + { + /* Make sure this symbol is output as a dynamic symbol. */ + if (h->dynindx == -1) + { + if (! bfd_elf32_link_record_dynamic_symbol (info, h)) + return false; + } + + /* We also need to make an entry in the .rela.plt section. */ + s = hplink->srelplt; + s->_raw_size += sizeof (Elf32_External_Rela); + } + return true; + } + + /* 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; + } + + /* This is a reference to a symbol defined by a dynamic object which + is not a function. */ + + /* If we are creating a shared library, we must presume that the + only references to the symbol are via the global offset table. + For such cases we need not do anything here; the relocations will + be handled correctly by relocate_section. */ + if (info->shared) + return true; + + /* If there are no references to this symbol that do not use the + GOT, we don't need to generate a copy reloc. */ + if ((h->elf_link_hash_flags & ELF_LINK_NON_GOT_REF) == 0) + return true; + + /* We must allocate the symbol in our .dynbss section, which will + become part of the .bss section of the executable. There will be + an entry for this symbol in the .dynsym section. The dynamic + object will contain position independent code, so all references + from the dynamic object to this symbol will go through the global + offset table. The dynamic linker will use the .dynsym entry to + determine the address it must put in the global offset table, so + both the dynamic object and the regular object will refer to the + same memory location for the variable. */ + + s = hplink->sdynbss; + + /* We must generate a COPY reloc to tell the dynamic linker to + copy the initial value out of the dynamic object and into the + runtime process image. We need to remember the offset into the + .rela.bss section we are going to use. */ + if ((h->root.u.def.section->flags & SEC_ALLOC) != 0) + { + asection *srel; + + srel = hplink->srelbss; + srel->_raw_size += sizeof (Elf32_External_Rela); + h->elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_COPY; + } + + { + /* We need to figure out the alignment required for this symbol. I + have no idea how other ELF linkers handle this. */ + unsigned int power_of_two; + + power_of_two = bfd_log2 (h->size); + if (power_of_two > 3) + power_of_two = 3; + + /* Apply the required alignment. */ + s->_raw_size = BFD_ALIGN (s->_raw_size, + (bfd_size_type) (1 << power_of_two)); + if (power_of_two > bfd_get_section_alignment (dynobj, s)) + { + if (! bfd_set_section_alignment (dynobj, s, power_of_two)) + return false; + } + } + /* Define the symbol as being at this point in the section. */ + h->root.u.def.section = s; + h->root.u.def.value = s->_raw_size; + + /* Increment the section size to make room for the symbol. */ + s->_raw_size += h->size; + + return true; +} + + +/* Called via elf_link_hash_traverse to create .plt entries for an + application that uses statically linked PIC functions. Similar to + the first part of elf32_hppa_adjust_dynamic_symbol. */ + +static boolean +hppa_handle_PIC_calls (h, inf) + struct elf_link_hash_entry *h; + PTR inf; +{ + struct bfd_link_info *info; + bfd *dynobj; + struct elf32_hppa_link_hash_table *hplink; + asection *s; + + if (! (h->plt.refcount > 0 + && (h->root.type == bfd_link_hash_defined + || h->root.type == bfd_link_hash_defweak) + && (h->root.u.def.section->flags & SEC_HAS_GOT_REF) != 0)) + { + h->plt.offset = (bfd_vma) -1; + h->elf_link_hash_flags &= ~ELF_LINK_HASH_NEEDS_PLT; + return true; + } + + ((struct elf32_hppa_link_hash_entry *) h)->pic_call = 1; + + info = (struct bfd_link_info *) inf; + dynobj = elf_hash_table (info)->dynobj; + hplink = hppa_link_hash_table (info); + + /* Make an entry in the .plt section. */ + s = hplink->splt; + h->plt.offset = s->_raw_size; + s->_raw_size += PLT_ENTRY_SIZE; + + return true; +} + + +#if ! LONG_BRANCH_PIC_IN_SHLIB || RELATIVE_DYNAMIC_RELOCS +/* This function is called via elf_link_hash_traverse to discard space + we allocated for relocs that it turned out we didn't need. */ + +static boolean +hppa_discard_copies (h, inf) + struct elf_link_hash_entry *h; + PTR inf; +{ + struct elf32_hppa_dyn_reloc_entry *s; + struct elf32_hppa_link_hash_entry *eh; + struct bfd_link_info *info; + + eh = (struct elf32_hppa_link_hash_entry *) h; + info = (struct bfd_link_info *) inf; + +#if ! LONG_BRANCH_PIC_IN_SHLIB + /* Handle the stub reloc case. If we have a plt entry for the + function, we won't be needing long branch stubs. s->count will + only be zero for stub relocs, which provides a handy way of + flagging these relocs, and means we need do nothing special for + the forced local and symbolic link case. */ + if (eh->stub_reloc_sec != NULL + && eh->elf.plt.offset != (bfd_vma) -1) + { + for (s = eh->reloc_entries; s != NULL; s = s->next) + if (s->count == 0) + s->section->_raw_size -= sizeof (Elf32_External_Rela); + } +#endif + + /* If a symbol has been forced local or we have found a regular + definition for the symbolic link case, then we won't be needing + any relocs. */ +#if RELATIVE_DYNAMIC_RELOCS + if (eh->elf.dynindx == -1 + || ((eh->elf.elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) != 0 + && eh->elf.root.type != bfd_link_hash_defweak + && info->symbolic)) + { + for (s = eh->reloc_entries; s != NULL; s = s->next) + s->section->_raw_size -= s->count * sizeof (Elf32_External_Rela); + } +#endif + + return true; +} +#endif + + +/* Set the sizes of the dynamic sections. */ + +static boolean +elf32_hppa_size_dynamic_sections (output_bfd, info) + bfd *output_bfd; + struct bfd_link_info *info; +{ + struct elf32_hppa_link_hash_table *hplink; + bfd *dynobj; + asection *s; + boolean relocs; + boolean reltext; + + hplink = hppa_link_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 + { + /* Run through the function symbols, looking for any that are + PIC, and allocate space for the necessary .plt entries so + that %r19 will be set up. */ + if (! info->shared) + elf_link_hash_traverse (&hplink->root, + hppa_handle_PIC_calls, + info); + + /* 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.got, + which will cause it to get stripped from the output file + below. */ + hplink->srelgot->_raw_size = 0; + } + +#if ! LONG_BRANCH_PIC_IN_SHLIB || RELATIVE_DYNAMIC_RELOCS + /* If this is a -Bsymbolic shared link, then we need to discard all + relocs against symbols defined in a regular object. We also need + to lose relocs we've allocated for long branch stubs if we know + we won't be generating a stub. */ + if (info->shared) + elf_link_hash_traverse (&hplink->root, + hppa_discard_copies, + info); +#endif + + /* The check_relocs and adjust_dynamic_symbol entry points have + determined the sizes of the various dynamic sections. Allocate + memory for them. */ + 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 (strncmp (name, ".rela", 5) == 0) + { + if (s->_raw_size != 0) + { + asection *target; + + /* Remember whether there are any reloc sections other + than .rela.plt. */ + if (strcmp (name+5, ".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 + 5); + 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 (strcmp (name, ".plt") == 0) + ; + else if (strcmp (name, ".got") == 0) + ; + else + { + /* It's not one of our sections, so don't allocate space. */ + continue; + } + + 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. */ + _bfd_strip_section_from_output (info, s); + continue; + } + + /* Allocate memory for the section contents. Zero it, because + we may not fill in all the reloc sections. */ + s->contents = (bfd_byte *) bfd_zalloc (dynobj, s->_raw_size); + if (s->contents == NULL && s->_raw_size != 0) return false; } - /* Build the stubs as directed by the stub hash table. */ - hppa_link_hash = elf32_hppa_hash_table (link_info); - memset (hppa_link_hash->offset, 0, - stub_bfd->section_count * sizeof (bfd_vma)); + if (elf_hash_table (info)->dynamic_sections_created) + { + /* Like IA-64 and HPPA64, always create a DT_PLTGOT. It + actually has nothing to do with the PLT, it is how we + communicate the LTP value of a load module to the dynamic + linker. */ + if (! bfd_elf32_add_dynamic_entry (info, DT_PLTGOT, 0)) + return false; - table = &hppa_link_hash->stub_hash_table; - bfd_hash_traverse (table, elf32_hppa_build_one_stub, hppa_link_hash); + /* Add some entries to the .dynamic section. We fill in the + values later, in elf32_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_elf32_add_dynamic_entry (info, DT_DEBUG, 0)) + return false; + } + + if (hplink->srelplt->_raw_size != 0) + { + if (! bfd_elf32_add_dynamic_entry (info, DT_PLTRELSZ, 0) + || ! bfd_elf32_add_dynamic_entry (info, DT_PLTREL, DT_RELA) + || ! bfd_elf32_add_dynamic_entry (info, DT_JMPREL, 0)) + return false; + } + + if (relocs) + { + if (! bfd_elf32_add_dynamic_entry (info, DT_RELA, 0) + || ! bfd_elf32_add_dynamic_entry (info, DT_RELASZ, 0) + || ! bfd_elf32_add_dynamic_entry (info, DT_RELAENT, + sizeof (Elf32_External_Rela))) + return false; + } + + if (reltext) + { + if (! bfd_elf32_add_dynamic_entry (info, DT_TEXTREL, 0)) + return false; + info->flags |= DF_TEXTREL; + } + } return true; } + +/* External entry points for sizing and building linker stubs. */ + /* Determine and set the size of the stub section for a final link. The basic idea here is to examine all the relocations looking for PC-relative calls to a target that is unreachable with a "bl" - instruction or calls where the caller and callee disagree on the - location of their arguments or return value. Currently, we don't - support elf arg relocs. */ + instruction. */ boolean -elf32_hppa_size_stubs (stub_bfd, link_info, +elf32_hppa_size_stubs (stub_bfd, multi_subspace, info, add_stub_section, layout_sections_again) bfd *stub_bfd; - struct bfd_link_info *link_info; + boolean multi_subspace; + struct bfd_link_info *info; asection * (*add_stub_section) PARAMS ((const char *, asection *)); void (*layout_sections_again) PARAMS ((void)); { bfd *input_bfd; asection *section; Elf_Internal_Sym *local_syms, **all_local_syms; - asection **stub_section_created; unsigned int i, indx, bfd_count, sec_count; - asection *stub_sec; - asection *first_init_sec = NULL; - asection *first_fini_sec = NULL; - struct elf32_hppa_link_hash_table *hppa_link_hash; - struct bfd_hash_table *stub_hash_table; - boolean stub_changed; + struct elf32_hppa_link_hash_table *hplink; + boolean stub_changed = 0; + + hplink = hppa_link_hash_table (info); + + /* Stash our params away. */ + hplink->stub_bfd = stub_bfd; + hplink->multi_subspace = multi_subspace; + hplink->add_stub_section = add_stub_section; + hplink->layout_sections_again = layout_sections_again; /* Count the number of input BFDs and the total number of input sections. */ - for (input_bfd = link_info->input_bfds, bfd_count = 0, sec_count = 0; + for (input_bfd = info->input_bfds, bfd_count = 0, sec_count = 0; input_bfd != NULL; input_bfd = input_bfd->link_next) { @@ -942,11 +2270,18 @@ elf32_hppa_size_stubs (stub_bfd, link_info, sec_count += input_bfd->section_count; } - stub_section_created + hplink->stub_section_created = (asection **) bfd_zmalloc (sizeof (asection *) * sec_count); - if (stub_section_created == NULL) + if (hplink->stub_section_created == NULL) return false; +#if ! LONG_BRANCH_PIC_IN_SHLIB + hplink->reloc_section_created + = (asection **) bfd_zmalloc (sizeof (asection *) * sec_count); + if (hplink->reloc_section_created == NULL) + goto error_ret_free_stub; +#endif + /* We want to read in symbol extension records only once. To do this we need to read in the local symbols in parallel and save them for later use; so hold pointers to the local symbols in an array. */ @@ -954,11 +2289,12 @@ elf32_hppa_size_stubs (stub_bfd, link_info, = (Elf_Internal_Sym **) bfd_zmalloc (sizeof (Elf_Internal_Sym *) * bfd_count); if (all_local_syms == NULL) - goto error_ret_free_stub; + goto error_ret_free_reloc; - /* Walk over all the input BFDs adding entries to the args hash table - for all the external functions. */ - for (input_bfd = link_info->input_bfds, indx = 0; + /* Walk over all the input BFDs, swapping in local symbols. + If we are creating a shared library, create hash entries for the + export stubs. */ + for (input_bfd = info->input_bfds, indx = 0, sec_count = 0; input_bfd != NULL; input_bfd = input_bfd->link_next, indx++) { @@ -988,7 +2324,7 @@ elf32_hppa_size_stubs (stub_bfd, link_info, } if (bfd_seek (input_bfd, symtab_hdr->sh_offset, SEEK_SET) != 0 - || (bfd_read (ext_syms, 1, + || (bfd_read (ext_syms, 1, (symtab_hdr->sh_info * sizeof (Elf32_External_Sym)), input_bfd) != (symtab_hdr->sh_info * sizeof (Elf32_External_Sym)))) @@ -1005,17 +2341,82 @@ elf32_hppa_size_stubs (stub_bfd, link_info, /* Now we can free the external symbols. */ free (ext_syms); - } - stub_hash_table = &elf32_hppa_hash_table (link_info)->stub_hash_table; + if (info->shared && hplink->multi_subspace) + { + unsigned int symndx; + unsigned int symcount; + + symcount = (symtab_hdr->sh_size / sizeof (Elf32_External_Sym) + - symtab_hdr->sh_info); + + /* Look through the global syms for functions; We need to + build export stubs for all globally visible functions. */ + for (symndx = 0; symndx < symcount; symndx++) + { + struct elf32_hppa_link_hash_entry *hash; + + hash = ((struct elf32_hppa_link_hash_entry *) + elf_sym_hashes (input_bfd)[symndx]); + + while (hash->elf.root.type == bfd_link_hash_indirect + || hash->elf.root.type == bfd_link_hash_warning) + hash = ((struct elf32_hppa_link_hash_entry *) + hash->elf.root.u.i.link); + + /* At this point in the link, undefined syms have been + resolved, so we need to check that the symbol was + defined in this BFD. */ + if ((hash->elf.root.type == bfd_link_hash_defined + || hash->elf.root.type == bfd_link_hash_defweak) + && hash->elf.type == STT_FUNC + && hash->elf.root.u.def.section->output_section != NULL + && hash->elf.root.u.def.section->owner == input_bfd + && (hash->elf.elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) + && !(hash->elf.elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) + && ELF_ST_VISIBILITY (hash->elf.other) == STV_DEFAULT) + { + asection *sec; + const char *stub_name; + struct elf32_hppa_stub_hash_entry *stub_entry; + + sec = hash->elf.root.u.def.section; + stub_name = hash->elf.root.root.string; + stub_entry = hppa_stub_hash_lookup (&hplink->stub_hash_table, + stub_name, + false, false); + if (stub_entry == NULL) + { + stub_entry = hppa_add_stub (stub_name, + sec, + sec_count + sec->index, + info); + if (!stub_entry) + goto error_ret_free_local; + + stub_entry->target_value = hash->elf.root.u.def.value; + stub_entry->target_section = hash->elf.root.u.def.section; + stub_entry->stub_type = hppa_stub_export; + stub_entry->h = hash; + stub_changed = 1; + } + else + { + (*_bfd_error_handler) (_("%s: duplicate export stub %s"), + bfd_get_filename (input_bfd), + stub_name); + } + } + } + sec_count += input_bfd->section_count; + } + } while (1) { - stub_changed = 0; + asection *stub_sec; - /* Now that we have argument location information for all the - global functions we can start looking for stubs. */ - for (input_bfd = link_info->input_bfds, indx = 0, sec_count = 0; + for (input_bfd = info->input_bfds, indx = 0, sec_count = 0; input_bfd != NULL; input_bfd = input_bfd->link_next, indx++) { @@ -1037,7 +2438,8 @@ elf32_hppa_size_stubs (stub_bfd, link_info, Elf32_External_Rela *external_relocs, *erelaend, *erela; Elf_Internal_Rela *internal_relocs, *irelaend, *irela; - /* If there aren't any relocs, then there's nothing to do. */ + /* If there aren't any relocs, then there's nothing more + to do. */ if ((section->flags & SEC_RELOC) == 0 || section->reloc_count == 0) continue; @@ -1090,13 +2492,13 @@ elf32_hppa_size_stubs (stub_bfd, link_info, irelaend = irela + section->reloc_count; for (; irela < irelaend; irela++) { - unsigned int r_type, r_indx, size_of_stub; + unsigned int r_type, r_indx; + enum elf32_hppa_stub_type stub_type; struct elf32_hppa_stub_hash_entry *stub_entry; asection *sym_sec; - const char *sym_name; - symvalue sym_value; + bfd_vma sym_value; bfd_vma destination; - struct elf_link_hash_entry *hash; + struct elf32_hppa_link_hash_entry *hash; char *stub_name; r_type = ELF32_R_TYPE (irela->r_info); @@ -1109,12 +2511,16 @@ elf32_hppa_size_stubs (stub_bfd, link_info, } /* Only look for stubs on call instructions. */ - if (r_type != (unsigned int) R_PARISC_PCREL17F) + if (r_type != (unsigned int) R_PARISC_PCREL12F + && r_type != (unsigned int) R_PARISC_PCREL17F + && r_type != (unsigned int) R_PARISC_PCREL22F) continue; - /* Now determine the call target, its name, value, section - and argument relocation bits. */ + /* Now determine the call target, its name, value, + section. */ sym_sec = NULL; + sym_value = 0; + destination = 0; hash = NULL; if (r_indx < symtab_hdr->sh_info) { @@ -1125,12 +2531,8 @@ elf32_hppa_size_stubs (stub_bfd, link_info, sym = local_syms + r_indx; hdr = elf_elfsections (input_bfd)[sym->st_shndx]; sym_sec = hdr->bfd_section; - sym_name = - bfd_elf_string_from_elf_section (input_bfd, - symtab_hdr->sh_link, - sym->st_name); - sym_value = (ELF_ST_TYPE (sym->st_info) == STT_SECTION - ? 0 : sym->st_value); + if (ELF_ST_TYPE (sym->st_info) != STT_SECTION) + sym_value = sym->st_value; destination = (sym_value + irela->r_addend + sym_sec->output_offset + sym_sec->output_section->vma); @@ -1141,26 +2543,36 @@ elf32_hppa_size_stubs (stub_bfd, link_info, int e_indx; e_indx = r_indx - symtab_hdr->sh_info; - hash = elf_sym_hashes (input_bfd)[e_indx]; + hash = ((struct elf32_hppa_link_hash_entry *) + elf_sym_hashes (input_bfd)[e_indx]); - while (hash->root.type == bfd_link_hash_indirect - || hash->root.type == bfd_link_hash_warning) - hash = (struct elf_link_hash_entry *) - hash->root.u.i.link; + while (hash->elf.root.type == bfd_link_hash_indirect + || hash->elf.root.type == bfd_link_hash_warning) + hash = ((struct elf32_hppa_link_hash_entry *) + hash->elf.root.u.i.link); - if (hash->root.type == bfd_link_hash_undefined - || hash->root.type == bfd_link_hash_undefweak) - continue; - - if (hash->root.type == bfd_link_hash_defined - || hash->root.type == bfd_link_hash_defweak) + if (hash->elf.root.type == bfd_link_hash_defined + || hash->elf.root.type == bfd_link_hash_defweak) { - sym_sec = hash->root.u.def.section; - sym_name = hash->root.root.string; - sym_value = hash->root.u.def.value; - destination = (sym_value + irela->r_addend - + sym_sec->output_offset - + sym_sec->output_section->vma); + sym_sec = hash->elf.root.u.def.section; + sym_value = hash->elf.root.u.def.value; + if (sym_sec->output_section != NULL) + destination = (sym_value + irela->r_addend + + sym_sec->output_offset + + sym_sec->output_section->vma); + } + else if (hash->elf.root.type == bfd_link_hash_undefweak) + { + if (! info->shared) + continue; + } + else if (hash->elf.root.type == bfd_link_hash_undefined) + { + if (! (info->shared + && !info->no_undefined + && (ELF_ST_VISIBILITY (hash->elf.other) + == STV_DEFAULT))) + continue; } else { @@ -1169,27 +2581,20 @@ elf32_hppa_size_stubs (stub_bfd, link_info, } } - /* Determine what (if any) linker stub is needed and its - size (in bytes). */ - size_of_stub = elf32_hppa_size_of_stub (section, - irela->r_offset, - destination); - if (size_of_stub == 0) + /* Determine what (if any) linker stub is needed. */ + stub_type = hppa_type_of_stub (section, irela, hash, + destination); + if (stub_type == hppa_stub_none) continue; /* Get the name of this stub. */ - stub_name = elf32_hppa_stub_name (sym_name, - sym_sec, - section, - irela->r_addend, - hash); + stub_name = hppa_stub_name (section, sym_sec, hash, irela); if (!stub_name) goto error_ret_free_internal; - stub_entry = elf32_hppa_stub_hash_lookup (stub_hash_table, - stub_name, - false, - false); + stub_entry = hppa_stub_hash_lookup (&hplink->stub_hash_table, + stub_name, + false, false); if (stub_entry != NULL) { /* The proper stub has already been created. */ @@ -1197,78 +2602,29 @@ elf32_hppa_size_stubs (stub_bfd, link_info, continue; } - stub_sec = stub_section_created[sec_count]; - if (stub_sec == NULL) - { - char *s_name; - int nstub; - int special_sec = 0; - - /* We only want one stub for .init and .fini - because glibc splits the _init and _fini - functions into two parts. We don't want to - put a stub in the middle of a function. */ - if (strncmp (section->name, ".init", 5) == 0) - { - stub_sec = first_init_sec; - special_sec = 1; - } - else if (strncmp (section->name, ".fini", 5) == 0) - { - stub_sec = first_fini_sec; - special_sec = 2; - } - if (stub_sec == NULL) - { - s_name = bfd_alloc (stub_bfd, 16); - if (s_name == NULL) - { - free (stub_name); - goto error_ret_free_internal; - } - nstub = stub_bfd->section_count; - sprintf (s_name, ".stub_%x", nstub); - stub_sec = (*add_stub_section) (s_name, section); - if (stub_sec == NULL) - { - free (stub_name); - goto error_ret_free_internal; - } - if (special_sec != 0) - { - if (special_sec == 1) - first_init_sec = stub_sec; - else - first_fini_sec = stub_sec; - } - } - stub_section_created[sec_count] = stub_sec; - } - - /* Enter this entry into the linker stub - hash table. */ - stub_entry = elf32_hppa_stub_hash_lookup (stub_hash_table, - stub_name, - true, - false); + stub_entry = hppa_add_stub (stub_name, section, + sec_count, info); if (stub_entry == NULL) { - (*_bfd_error_handler) - (_("%s: cannot find stub entry %s"), - bfd_get_filename (section->owner), - stub_name); free (stub_name); - goto error_ret_free_internal; + goto error_ret_free_local; } - /* We'll need these to determine the address - that the stub will branch to. */ - stub_entry->stub_sec = stub_sec; - stub_entry->offset = 0; stub_entry->target_value = sym_value; stub_entry->target_section = sym_sec; + stub_entry->stub_type = stub_type; + if (info->shared) + { + if (stub_type == hppa_stub_import) + stub_entry->stub_type = hppa_stub_import_shared; + else if (stub_type == hppa_stub_long_branch + && (LONG_BRANCH_PIC_IN_SHLIB || hash == NULL)) + stub_entry->stub_type = hppa_stub_long_branch_shared; + } + stub_entry->h = hash; stub_changed = 1; } + /* We're done with the internal relocs, free them. */ free (internal_relocs); } @@ -1279,29 +2635,29 @@ elf32_hppa_size_stubs (stub_bfd, link_info, /* OK, we've added some stubs. Find out the new size of the stub sections. */ - hppa_link_hash = elf32_hppa_hash_table (link_info); - hppa_link_hash->offset = (bfd_vma *) - bfd_realloc (hppa_link_hash->offset, - stub_bfd->section_count * sizeof (bfd_vma)); - if (hppa_link_hash->offset == NULL) + hplink->offset = (bfd_vma *) + bfd_realloc (hplink->offset, + hplink->stub_bfd->section_count * sizeof (bfd_vma)); + if (hplink->offset == NULL) goto error_ret_free_local; - memset (hppa_link_hash->offset, 0, - stub_bfd->section_count * sizeof (bfd_vma)); + memset (hplink->offset, 0, + hplink->stub_bfd->section_count * sizeof (bfd_vma)); - bfd_hash_traverse (stub_hash_table, - elf32_hppa_size_one_stub, - hppa_link_hash); + bfd_hash_traverse (&hplink->stub_hash_table, + hppa_size_one_stub, + hplink); - for (stub_sec = stub_bfd->sections; + for (stub_sec = hplink->stub_bfd->sections; stub_sec != NULL; stub_sec = stub_sec->next) { - bfd_set_section_size (stub_bfd, stub_sec, - hppa_link_hash->offset[stub_sec->index]); + bfd_set_section_size (hplink->stub_bfd, stub_sec, + hplink->offset[stub_sec->index]); } /* Ask the linker to do its stuff. */ - (*layout_sections_again) (); + (*hplink->layout_sections_again) (); + stub_changed = 0; } /* We're done with the local symbols, free them. */ @@ -1309,7 +2665,10 @@ elf32_hppa_size_stubs (stub_bfd, link_info, if (all_local_syms[i]) free (all_local_syms[i]); free (all_local_syms); - free (stub_section_created); +#if ! LONG_BRANCH_PIC_IN_SHLIB + free (hplink->reloc_section_created); +#endif + free (hplink->stub_section_created); return true; error_ret_free_local: @@ -1318,28 +2677,1045 @@ elf32_hppa_size_stubs (stub_bfd, link_info, free (all_local_syms[i]); free (all_local_syms); + error_ret_free_reloc: +#if ! LONG_BRANCH_PIC_IN_SHLIB + free (hplink->reloc_section_created); error_ret_free_stub: - free (stub_section_created); +#endif + free (hplink->stub_section_created); return false; } + +/* For a final link, this function is called after we have sized the + stubs to provide a value for __gp. */ + +boolean +elf32_hppa_set_gp (abfd, info) + bfd *abfd; + struct bfd_link_info *info; +{ + struct elf_link_hash_entry *h; + asection *sec; + bfd_vma gp_val; + + h = elf_link_hash_lookup (elf_hash_table (info), "$global$", + false, false, false); + + if (h != NULL && h->root.type == bfd_link_hash_defined) + { + gp_val = h->root.u.def.value; + sec = h->root.u.def.section; + } + else + { + /* If $global$ isn't defined, we make one up ourselves + from the start of .data, .plt, or .got. */ + struct elf32_hppa_link_hash_table *hplink; + + hplink = hppa_link_hash_table (info); + gp_val = 0; + sec = bfd_get_section_by_name (abfd, ".data"); + if (sec == NULL) + sec = hplink->splt; + if (sec == NULL) + sec = hplink->sgot; + if (sec == NULL) + { + (*info->callbacks->undefined_symbol) + (info, "$global$", abfd, NULL, 0, true); + return false; + } + } + + elf_gp (abfd) = (gp_val + + sec->output_section->vma + + sec->output_offset); + return true; +} + + +/* Build all the stubs associated with the current output file. The + stubs are kept in a hash table attached to the main linker hash + table. We also set up the .plt entries for statically linked PIC + functions here. This function is called via hppaelf_finish in the + linker. */ + +boolean +elf32_hppa_build_stubs (info) + struct bfd_link_info *info; +{ + asection *stub_sec; + struct bfd_hash_table *table; + struct elf32_hppa_link_hash_table *hplink; + + hplink = hppa_link_hash_table (info); + + for (stub_sec = hplink->stub_bfd->sections; + stub_sec != NULL; + stub_sec = stub_sec->next) + { + unsigned int size; + + /* Allocate memory to hold the linker stubs. */ + size = bfd_section_size (hplink->stub_bfd, stub_sec); + stub_sec->contents = (unsigned char *) bfd_zalloc (hplink->stub_bfd, + size); + if (stub_sec->contents == NULL && size != 0) + return false; + } + + /* Build the stubs as directed by the stub hash table. */ + memset (hplink->offset, 0, + hplink->stub_bfd->section_count * sizeof (bfd_vma)); + + table = &hplink->stub_hash_table; + bfd_hash_traverse (table, hppa_build_one_stub, info); + + return true; +} + + +/* Perform a relocation as part of a final link. */ + +static bfd_reloc_status_type +final_link_relocate (input_section, contents, rel, value, info, sym_sec, h) + asection *input_section; + bfd_byte *contents; + const Elf_Internal_Rela *rel; + bfd_vma value; + struct bfd_link_info *info; + asection *sym_sec; + struct elf32_hppa_link_hash_entry *h; +{ + int insn; + unsigned int r_type = ELF32_R_TYPE (rel->r_info); + reloc_howto_type *howto = elf_hppa_howto_table + r_type; + int r_format = howto->bitsize; + enum hppa_reloc_field_selector_type_alt r_field; + bfd *input_bfd = input_section->owner; + bfd_vma offset = rel->r_offset; + bfd_vma max_branch_offset = 0; + bfd_byte *hit_data = contents + offset; + bfd_signed_vma addend = rel->r_addend; + bfd_vma location; + struct elf32_hppa_stub_hash_entry *stub_entry = NULL; + int val; + + if (r_type == R_PARISC_NONE) + return bfd_reloc_ok; + + insn = bfd_get_32 (input_bfd, hit_data); + + /* Find out where we are and where we're going. */ + location = (offset + + input_section->output_offset + + input_section->output_section->vma); + + switch (r_type) + { + case R_PARISC_PCREL12F: + case R_PARISC_PCREL17F: + case R_PARISC_PCREL22F: + /* If this is a call to a function defined in another dynamic + library, or if it is a call to a PIC function in the same + object, then find the import stub in the stub hash. */ + if (sym_sec == NULL + || sym_sec->output_section == NULL + || (h != NULL && h->pic_call)) + { + stub_entry = hppa_get_stub_entry (input_section, sym_sec, + h, rel, info); + if (stub_entry != NULL) + { + value = (stub_entry->stub_offset + + stub_entry->stub_sec->output_offset + + stub_entry->stub_sec->output_section->vma); + addend = 0; + } + else if (sym_sec == NULL && h != NULL + && h->elf.root.type == bfd_link_hash_undefweak) + { + /* It's OK if undefined weak. Make undefined weak + branches go nowhere. */ + value = location; + addend = 0; + } + else + return bfd_reloc_notsupported; + } + /* Fall thru. */ + + case R_PARISC_PCREL21L: + case R_PARISC_PCREL17C: + case R_PARISC_PCREL17R: + case R_PARISC_PCREL14R: + case R_PARISC_PCREL14F: + /* Make it a pc relative offset. */ + value -= location; + addend -= 8; + break; + + case R_PARISC_DPREL21L: + case R_PARISC_DPREL14R: + case R_PARISC_DPREL14F: + /* For all the DP relative relocations, we need to examine the symbol's + section. If it's a code section, then "data pointer relative" makes + no sense. In that case we don't adjust the "value", and for 21 bit + addil instructions, we change the source addend register from %dp to + %r0. This situation commonly arises when a variable's "constness" + is declared differently from the way the variable is defined. For + instance: "extern int foo" with foo defined as "const int foo". */ + if (sym_sec == NULL) + break; + if ((sym_sec->flags & SEC_CODE) != 0) + { + if ((insn & ((0x3f << 26) | (0x1f << 21))) + == (((int) OP_ADDIL << 26) | (27 << 21))) + { + insn &= ~ (0x1f << 21); +#if 1 /* debug them */ + (*_bfd_error_handler) + (_("%s(%s+0x%lx): fixing %s"), + bfd_get_filename (input_bfd), + input_section->name, + (long) rel->r_offset, + howto->name); +#endif + } + /* Now try to make things easy for the dynamic linker. */ + + break; + } + /* Fall thru */ + + case R_PARISC_DLTIND21L: + case R_PARISC_DLTIND14R: + case R_PARISC_DLTIND14F: + value -= elf_gp (input_section->output_section->owner); + break; + + default: + break; + } + + switch (r_type) + { + case R_PARISC_DIR32: + case R_PARISC_DIR17F: + case R_PARISC_PCREL17C: + case R_PARISC_PCREL14F: + case R_PARISC_DPREL14F: + case R_PARISC_PLABEL32: + case R_PARISC_DLTIND14F: + case R_PARISC_SEGBASE: + case R_PARISC_SEGREL32: + r_field = e_fsel; + break; + + case R_PARISC_DIR21L: + case R_PARISC_PCREL21L: + case R_PARISC_DPREL21L: + case R_PARISC_PLABEL21L: + case R_PARISC_DLTIND21L: + r_field = e_lrsel; + break; + + case R_PARISC_DIR17R: + case R_PARISC_PCREL17R: + case R_PARISC_DIR14R: + case R_PARISC_PCREL14R: + case R_PARISC_DPREL14R: + case R_PARISC_PLABEL14R: + case R_PARISC_DLTIND14R: + r_field = e_rrsel; + break; + + case R_PARISC_PCREL12F: + case R_PARISC_PCREL17F: + case R_PARISC_PCREL22F: + r_field = e_fsel; + + if (r_type == (unsigned int) R_PARISC_PCREL17F) + { + max_branch_offset = (1 << (17-1)) << 2; + } + else if (r_type == (unsigned int) R_PARISC_PCREL12F) + { + max_branch_offset = (1 << (12-1)) << 2; + } + else + { + max_branch_offset = (1 << (22-1)) << 2; + } + + /* sym_sec is NULL on undefined weak syms or when shared on + undefined syms. We've already checked for a stub for the + shared undefined case. */ + if (sym_sec == NULL) + break; + + /* If the branch is out of reach, then redirect the + call to the local stub for this function. */ + if (value + addend + max_branch_offset >= 2*max_branch_offset) + { + stub_entry = hppa_get_stub_entry (input_section, sym_sec, + h, rel, info); + if (stub_entry == NULL) + return bfd_reloc_notsupported; + + /* Munge up the value and addend so that we call the stub + rather than the procedure directly. */ + value = (stub_entry->stub_offset + + stub_entry->stub_sec->output_offset + + stub_entry->stub_sec->output_section->vma + - location); + addend = -8; + } + break; + + /* Something we don't know how to handle. */ + default: + return bfd_reloc_notsupported; + } + + /* Make sure we can reach the stub. */ + if (max_branch_offset != 0 + && value + addend + max_branch_offset >= 2*max_branch_offset) + { + (*_bfd_error_handler) + (_("%s(%s+0x%lx): cannot reach %s, recompile with -ffunction-sections"), + bfd_get_filename (input_bfd), + input_section->name, + (long) rel->r_offset, + stub_entry->root.string); + return bfd_reloc_notsupported; + } + + val = hppa_field_adjust (value, addend, r_field); + + switch (r_type) + { + case R_PARISC_PCREL12F: + case R_PARISC_PCREL17C: + case R_PARISC_PCREL17F: + case R_PARISC_PCREL17R: + case R_PARISC_PCREL22F: + case R_PARISC_DIR17F: + case R_PARISC_DIR17R: + /* This is a branch. Divide the offset by four. + Note that we need to decide whether it's a branch or + otherwise by inspecting the reloc. Inspecting insn won't + work as insn might be from a .word directive. */ + val >>= 2; + break; + + default: + break; + } + + insn = hppa_rebuild_insn (insn, val, r_format); + + /* Update the instruction word. */ + bfd_put_32 (input_bfd, (unsigned int) insn, hit_data); + return bfd_reloc_ok; +} + + +/* Relocate an HPPA ELF section. */ + +static boolean +elf32_hppa_relocate_section (output_bfd, info, input_bfd, input_section, + contents, relocs, local_syms, local_sections) + bfd *output_bfd; + struct bfd_link_info *info; + bfd *input_bfd; + asection *input_section; + bfd_byte *contents; + Elf_Internal_Rela *relocs; + Elf_Internal_Sym *local_syms; + asection **local_sections; +{ + bfd *dynobj; + bfd_vma *local_got_offsets; + struct elf32_hppa_link_hash_table *hplink; + Elf_Internal_Shdr *symtab_hdr; + Elf_Internal_Rela *rel; + Elf_Internal_Rela *relend; + asection *sreloc; + + symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr; + + dynobj = elf_hash_table (info)->dynobj; + local_got_offsets = elf_local_got_offsets (input_bfd); + hplink = hppa_link_hash_table (info); + sreloc = NULL; + + rel = relocs; + relend = relocs + input_section->reloc_count; + for (; rel < relend; rel++) + { + unsigned int r_type; + reloc_howto_type *howto; + unsigned int r_symndx; + struct elf32_hppa_link_hash_entry *h; + Elf_Internal_Sym *sym; + asection *sym_sec; + bfd_vma relocation; + bfd_reloc_status_type r; + const char *sym_name; + boolean pltrel; + + r_type = ELF32_R_TYPE (rel->r_info); + if (r_type >= (unsigned int) R_PARISC_UNIMPLEMENTED) + { + bfd_set_error (bfd_error_bad_value); + return false; + } + if (r_type == (unsigned int) R_PARISC_GNU_VTENTRY + || r_type == (unsigned int) R_PARISC_GNU_VTINHERIT) + continue; + + r_symndx = ELF32_R_SYM (rel->r_info); + + if (info->relocateable) + { + /* This is a relocateable link. We don't have to change + anything, unless the reloc is against a section symbol, + in which case we have to adjust according to where the + section symbol winds up in the output section. */ + if (r_symndx < symtab_hdr->sh_info) + { + sym = local_syms + r_symndx; + if (ELF_ST_TYPE (sym->st_info) == STT_SECTION) + { + sym_sec = local_sections[r_symndx]; + rel->r_addend += sym_sec->output_offset; + } + } + continue; + } + + /* This is a final link. */ + h = NULL; + sym = NULL; + sym_sec = NULL; + if (r_symndx < symtab_hdr->sh_info) + { + /* This is a local symbol, h defaults to NULL. */ + sym = local_syms + r_symndx; + sym_sec = local_sections[r_symndx]; + relocation = ((ELF_ST_TYPE (sym->st_info) == STT_SECTION + ? 0 : sym->st_value) + + sym_sec->output_offset + + sym_sec->output_section->vma); + } + else + { + int indx; + + /* It's a global; Find its entry in the link hash. */ + indx = r_symndx - symtab_hdr->sh_info; + h = ((struct elf32_hppa_link_hash_entry *) + elf_sym_hashes (input_bfd)[indx]); + while (h->elf.root.type == bfd_link_hash_indirect + || h->elf.root.type == bfd_link_hash_warning) + h = (struct elf32_hppa_link_hash_entry *) h->elf.root.u.i.link; + + relocation = 0; + if (h->elf.root.type == bfd_link_hash_defined + || h->elf.root.type == bfd_link_hash_defweak) + { + sym_sec = h->elf.root.u.def.section; + /* If sym_sec->output_section is NULL, then it's a + symbol defined in a shared library. */ + if (sym_sec->output_section != NULL) + relocation = (h->elf.root.u.def.value + + sym_sec->output_offset + + sym_sec->output_section->vma); + } + else if (h->elf.root.type == bfd_link_hash_undefweak) + ; + else if (info->shared && !info->no_undefined + && ELF_ST_VISIBILITY (h->elf.other) == STV_DEFAULT) + { + if (info->symbolic) + if (!((*info->callbacks->undefined_symbol) + (info, h->elf.root.root.string, input_bfd, + input_section, rel->r_offset, false))) + return false; + } + else + { + if (!((*info->callbacks->undefined_symbol) + (info, h->elf.root.root.string, input_bfd, + input_section, rel->r_offset, true))) + return false; + } + } + + /* Do any required modifications to the relocation value, and + determine what types of dynamic info we need to output, if + any. */ + pltrel = 0; + switch (r_type) + { + case R_PARISC_DLTIND14F: + case R_PARISC_DLTIND14R: + case R_PARISC_DLTIND21L: + /* Relocation is to the entry for this symbol in the global + offset table. */ + if (h != NULL) + { + bfd_vma off; + + off = h->elf.got.offset; + BFD_ASSERT (off != (bfd_vma) -1); + + if (! elf_hash_table (info)->dynamic_sections_created + || (info->shared + && (info->symbolic || h->elf.dynindx == -1) + && (h->elf.elf_link_hash_flags + & ELF_LINK_HASH_DEF_REGULAR) != 0)) + { + /* This is actually a static link, or it is a + -Bsymbolic link and the symbol is defined + locally, or the symbol was forced to be local + because of a version file. We must initialize + this entry in the global offset table. Since the + offset must always be a multiple of 4, we use the + least significant bit to record whether we have + initialized it already. + + When doing a dynamic link, we create a .rela.got + relocation entry to initialize the value. This + is done in the finish_dynamic_symbol routine. */ + if ((off & 1) != 0) + off &= ~1; + else + { + bfd_put_32 (output_bfd, relocation, + hplink->sgot->contents + off); + h->elf.got.offset |= 1; + } + } + + relocation = off; + } + else + { + /* Local symbol case. */ + bfd_vma off; + + BFD_ASSERT (local_got_offsets != NULL + && local_got_offsets[r_symndx] != (bfd_vma) -1); + + off = local_got_offsets[r_symndx]; + + /* The offset must always be a multiple of 4. We use + the least significant bit to record whether we have + already generated the necessary reloc. */ + if ((off & 1) != 0) + off &= ~1; + else + { + bfd_put_32 (output_bfd, relocation, + hplink->sgot->contents + off); + + if (info->shared) + { + /* Output a dynamic SEGREL32 relocation for this + GOT entry. In this case it is relative to + the base of the object because the symbol + index is zero. */ + Elf_Internal_Rela outrel; + asection *srelgot = hplink->srelgot; + + outrel.r_offset = (off + + hplink->sgot->output_offset + + hplink->sgot->output_section->vma); + outrel.r_info = ELF32_R_INFO (0, R_PARISC_SEGREL32); + outrel.r_addend = relocation; + bfd_elf32_swap_reloca_out (output_bfd, &outrel, + ((Elf32_External_Rela *) + srelgot->contents + + srelgot->reloc_count)); + ++srelgot->reloc_count; + } + + local_got_offsets[r_symndx] |= 1; + } + + relocation = off; + } + + /* Add the base of the GOT to the relocation value. */ + relocation += (hplink->sgot->output_offset + + hplink->sgot->output_section->vma); + break; + + case R_PARISC_PLABEL14R: + case R_PARISC_PLABEL21L: + case R_PARISC_PLABEL32: + /* If we have a global symbol with a PLT slot, then redirect + this relocation to it. */ + if (h != NULL + && h->elf.dynindx != -1 + && h->elf.plt.offset != (bfd_vma) -1) + { + /* PLABELs contain function pointers. Relocation is + to the entry for the function in the .plt. The + magic +2 offset signals to $$dyncall that the + function pointer is in the .plt and thus has a gp + pointer too. */ + relocation = (h->elf.plt.offset + + hplink->splt->output_offset + + hplink->splt->output_section->vma + + 2); + pltrel = 1; + } + /* Fall through and possibly emit a dynamic relocation. */ + + case R_PARISC_DIR17F: + case R_PARISC_DIR17R: + case R_PARISC_DIR14R: + case R_PARISC_DIR21L: + case R_PARISC_DPREL14F: + case R_PARISC_DPREL14R: + case R_PARISC_DPREL21L: + case R_PARISC_DIR32: + /* The reloc types handled here and this conditional + expression must match the code in check_relocs and + hppa_discard_copies. ie. We need exactly the same + condition as in check_relocs, with some extra conditions + (dynindx test in this case) to cater for relocs removed + by hppa_discard_copies. */ + if ((input_section->flags & SEC_ALLOC) != 0 + && info->shared +#if RELATIVE_DYNAMIC_RELOCS + && (is_absolute_reloc (r_type) + || ((!info->symbolic + || (h != NULL + && ((h->elf.elf_link_hash_flags + & ELF_LINK_HASH_DEF_REGULAR) == 0 + || h->elf.root.type == bfd_link_hash_defweak))) + && (h == NULL || h->elf.dynindx != -1))) +#endif + ) + { + Elf_Internal_Rela outrel; + boolean skip; + + /* When generating a shared object, these relocations + are copied into the output file to be resolved at run + time. */ + + if (sreloc == NULL) + { + const char *name; + + name = (bfd_elf_string_from_elf_section + (input_bfd, + elf_elfheader (input_bfd)->e_shstrndx, + elf_section_data (input_section)->rel_hdr.sh_name)); + if (name == NULL) + return false; + sreloc = bfd_get_section_by_name (dynobj, name); + BFD_ASSERT (sreloc != NULL); + } + + outrel.r_offset = rel->r_offset; + outrel.r_addend = rel->r_addend; + skip = false; + if (elf_section_data (input_section)->stab_info != NULL) + { + bfd_vma off; + + off = (_bfd_stab_section_offset + (output_bfd, &elf_hash_table (info)->stab_info, + input_section, + &elf_section_data (input_section)->stab_info, + rel->r_offset)); + if (off == (bfd_vma) -1) + skip = true; + outrel.r_offset = off; + } + + outrel.r_offset += (input_section->output_offset + + input_section->output_section->vma); + + if (skip) + { + memset (&outrel, 0, sizeof (outrel)); + } + else if (!pltrel + && h != NULL + && ((!info->symbolic && h->elf.dynindx != -1) + || (h->elf.elf_link_hash_flags + & ELF_LINK_HASH_DEF_REGULAR) == 0)) + { + outrel.r_info = ELF32_R_INFO (h->elf.dynindx, r_type); + } + else /* It's a local symbol, or one marked to become local. */ + { + int indx = 0; + + /* Add the absolute offset of the symbol. */ + outrel.r_addend += relocation; + + if (sym_sec != NULL + && sym_sec->output_section != NULL + && ! bfd_is_abs_section (sym_sec)) + { + indx = elf_section_data (sym_sec->output_section)->dynindx; + /* We are turning this relocation into one + against a section symbol, so subtract out the + output section's address but not the offset + of the input section in the output section. */ + outrel.r_addend -= sym_sec->output_section->vma; + } + + outrel.r_info = ELF32_R_INFO (indx, r_type); + } + + bfd_elf32_swap_reloca_out (output_bfd, &outrel, + ((Elf32_External_Rela *) + sreloc->contents + + sreloc->reloc_count)); + ++sreloc->reloc_count; + } + break; + + default: + break; + } + + r = final_link_relocate (input_section, contents, rel, relocation, + info, sym_sec, h); + + if (r == bfd_reloc_ok) + continue; + + if (h != NULL) + sym_name = h->elf.root.root.string; + else + { + sym_name = bfd_elf_string_from_elf_section (input_bfd, + symtab_hdr->sh_link, + sym->st_name); + if (sym_name == NULL) + return false; + if (*sym_name == '\0') + sym_name = bfd_section_name (input_bfd, sym_sec); + } + + howto = elf_hppa_howto_table + r_type; + + if (r == bfd_reloc_undefined || r == bfd_reloc_notsupported) + { + (*_bfd_error_handler) + (_("%s(%s+0x%lx): cannot handle %s for %s"), + bfd_get_filename (input_bfd), + input_section->name, + (long) rel->r_offset, + howto->name, + sym_name); + } + else + { + if (!((*info->callbacks->reloc_overflow) + (info, sym_name, howto->name, (bfd_vma) 0, + input_bfd, input_section, rel->r_offset))) + return false; + } + } + + return true; +} + + +/* Finish up dynamic symbol handling. We set the contents of various + dynamic sections here. */ + +static boolean +elf32_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; +{ + struct elf32_hppa_link_hash_table *hplink; + bfd *dynobj; + + hplink = hppa_link_hash_table (info); + dynobj = elf_hash_table (info)->dynobj; + + /* 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; + + if (h->plt.offset != (bfd_vma) -1) + { + bfd_vma value; + Elf_Internal_Rela rel; + + /* This symbol has an entry in the procedure linkage table. Set + it up. + + The format of a plt entry is + <__gp>. */ + + /* 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. */ + value = 0; + if (h->root.type == bfd_link_hash_defined + || h->root.type == bfd_link_hash_defweak) + { + value = h->root.u.def.value; + if (h->root.u.def.section->output_section != NULL) + value += (h->root.u.def.section->output_offset + + h->root.u.def.section->output_section->vma); + } + + bfd_put_32 (hplink->splt->owner, value, + hplink->splt->contents + h->plt.offset); + value = elf_gp (hplink->splt->output_section->owner); + bfd_put_32 (hplink->splt->owner, value, + hplink->splt->contents + h->plt.offset + 4); + + if (! ((struct elf32_hppa_link_hash_entry *) h)->pic_call + && h->dynindx != -1) + { + /* Create a dynamic IPLT relocation for this entry. */ + rel.r_offset = (h->plt.offset + + hplink->splt->output_offset + + hplink->splt->output_section->vma); + rel.r_info = ELF32_R_INFO (h->dynindx, R_PARISC_IPLT); + rel.r_addend = 0; + + bfd_elf32_swap_reloca_out (hplink->splt->output_section->owner, + &rel, + ((Elf32_External_Rela *) + hplink->srelplt->contents + + hplink->srelplt->reloc_count)); + hplink->srelplt->reloc_count++; + } + + if ((h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0) + { + /* Mark the symbol as undefined, rather than as defined in + the .plt section. Leave the value alone. */ + sym->st_shndx = SHN_UNDEF; + } + } + + if (h->got.offset != (bfd_vma) -1) + { + Elf_Internal_Rela rel; + + /* This symbol has an entry in the global offset table. Set it + up. */ + + rel.r_offset = ((h->got.offset &~ (bfd_vma) 1) + + hplink->sgot->output_offset + + hplink->sgot->output_section->vma); + + /* If this is a static link, or it is a -Bsymbolic link and the + symbol is defined locally or was forced to be local because + of a version file, we just want to emit a RELATIVE reloc. + The entry in the global offset table will already have been + initialized in the relocate_section function. */ + if (! elf_hash_table (info)->dynamic_sections_created + || (info->shared + && (info->symbolic || h->dynindx == -1) + && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR))) + { + rel.r_info = ELF32_R_INFO (0, R_PARISC_SEGREL32); + rel.r_addend = (h->root.u.def.value + + h->root.u.def.section->output_offset + + h->root.u.def.section->output_section->vma); + } + else + { + BFD_ASSERT((h->got.offset & 1) == 0); + bfd_put_32 (output_bfd, (bfd_vma) 0, + hplink->sgot->contents + h->got.offset); + rel.r_info = ELF32_R_INFO (h->dynindx, R_PARISC_DIR32); + rel.r_addend = 0; + } + + bfd_elf32_swap_reloca_out (output_bfd, &rel, + ((Elf32_External_Rela *) + hplink->srelgot->contents + + hplink->srelgot->reloc_count)); + ++hplink->srelgot->reloc_count; + } + + if ((h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_COPY) != 0) + { + asection *s; + Elf_Internal_Rela rel; + + /* This symbol needs a copy reloc. Set it up. */ + + BFD_ASSERT (h->dynindx != -1 + && (h->root.type == bfd_link_hash_defined + || h->root.type == bfd_link_hash_defweak)); + + s = hplink->srelbss; + + rel.r_offset = (h->root.u.def.value + + h->root.u.def.section->output_offset + + h->root.u.def.section->output_section->vma); + rel.r_addend = 0; + rel.r_info = ELF32_R_INFO (h->dynindx, R_PARISC_COPY); + bfd_elf32_swap_reloca_out (output_bfd, &rel, + ((Elf32_External_Rela *) s->contents + + s->reloc_count)); + ++s->reloc_count; + } + + /* Mark _DYNAMIC and _GLOBAL_OFFSET_TABLE_ as absolute. */ + if (h->root.root.string[0] == '_' + && (strcmp (h->root.root.string, "_DYNAMIC") == 0 + || strcmp (h->root.root.string, "_GLOBAL_OFFSET_TABLE_") == 0)) + { + sym->st_shndx = SHN_ABS; + } + + return true; +} + + +/* Finish up the dynamic sections. */ + +static boolean +elf32_hppa_finish_dynamic_sections (output_bfd, info) + bfd *output_bfd; + struct bfd_link_info *info; +{ + bfd *dynobj; + struct elf32_hppa_link_hash_table *hplink; + asection *sdyn; + + dynobj = elf_hash_table (info)->dynobj; + hplink = hppa_link_hash_table (info); + + sdyn = bfd_get_section_by_name (dynobj, ".dynamic"); + + if (elf_hash_table (info)->dynamic_sections_created) + { + Elf32_External_Dyn *dyncon, *dynconend; + + BFD_ASSERT (sdyn != NULL); + + dyncon = (Elf32_External_Dyn *) sdyn->contents; + dynconend = (Elf32_External_Dyn *) (sdyn->contents + sdyn->_raw_size); + for (; dyncon < dynconend; dyncon++) + { + Elf_Internal_Dyn dyn; + asection *s; + + bfd_elf32_swap_dyn_in (dynobj, dyncon, &dyn); + + switch (dyn.d_tag) + { + default: + break; + + case DT_PLTGOT: + /* Use PLTGOT to set the GOT register. */ + dyn.d_un.d_ptr = elf_gp (output_bfd); + bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); + break; + + case DT_JMPREL: + s = hplink->srelplt; + dyn.d_un.d_ptr = s->output_section->vma + s->output_offset; + bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); + break; + + case DT_PLTRELSZ: + s = hplink->srelplt; + if (s->_cooked_size != 0) + dyn.d_un.d_val = s->_cooked_size; + else + dyn.d_un.d_val = s->_raw_size; + bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); + break; + } + } + } + + /* Fill in the first entry in the global offset table. + We use it to point to our dynamic section, if we have one. */ + if (hplink->sgot->_raw_size != 0) + { + bfd_put_32 (output_bfd, + (sdyn != NULL + ? sdyn->output_section->vma + sdyn->output_offset + : (bfd_vma) 0), + hplink->sgot->contents); + + /* Set .got entry size. */ + elf_section_data (hplink->sgot->output_section)->this_hdr.sh_entsize = 4; + } + + /* Set plt entry size. */ + if (hplink->splt->_raw_size != 0) + elf_section_data (hplink->splt->output_section)->this_hdr.sh_entsize = 8; + + return true; +} + + +/* Called when writing out an object file to decide the type of a + symbol. */ +static int +elf32_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; +} + + /* Misc BFD support code. */ -#define bfd_elf32_bfd_reloc_type_lookup elf_hppa_reloc_type_lookup -#define bfd_elf32_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 bfd_elf32_bfd_is_local_label_name elf_hppa_is_local_label_name +#define bfd_elf32_bfd_reloc_type_lookup elf_hppa_reloc_type_lookup +#define elf_info_to_howto elf_hppa_info_to_howto +#define elf_info_to_howto_rel elf_hppa_info_to_howto_rel /* Stuff for the BFD linker. */ -#define elf_backend_relocate_section elf32_hppa_relocate_section -#define elf_backend_add_symbol_hook elf32_hppa_add_symbol_hook -#define bfd_elf32_bfd_final_link elf32_hppa_final_link -#if 0 -#define elf_backend_check_relocs elf32_hppa_check_relocs -#endif -#define bfd_elf32_bfd_link_hash_table_create \ - elf32_hppa_link_hash_table_create -#define elf_backend_fake_sections elf_hppa_fake_sections +#define bfd_elf32_bfd_final_link _bfd_elf32_gc_common_final_link +#define bfd_elf32_bfd_link_hash_table_create elf32_hppa_link_hash_table_create +#define elf_backend_add_symbol_hook elf32_hppa_add_symbol_hook +#define elf_backend_adjust_dynamic_symbol elf32_hppa_adjust_dynamic_symbol +#define elf_backend_check_relocs elf32_hppa_check_relocs +#define elf_backend_create_dynamic_sections elf32_hppa_create_dynamic_sections +#define elf_backend_fake_sections elf_hppa_fake_sections +#define elf_backend_relocate_section elf32_hppa_relocate_section +#define elf_backend_finish_dynamic_symbol elf32_hppa_finish_dynamic_symbol +#define elf_backend_finish_dynamic_sections elf32_hppa_finish_dynamic_sections +#define elf_backend_size_dynamic_sections elf32_hppa_size_dynamic_sections +#define elf_backend_gc_mark_hook elf32_hppa_gc_mark_hook +#define elf_backend_gc_sweep_hook elf32_hppa_gc_sweep_hook +#define elf_backend_object_p elf32_hppa_object_p +#define elf_backend_final_write_processing elf_hppa_final_write_processing +#define elf_backend_get_symbol_type elf32_hppa_elf_get_symbol_type +#define elf_backend_can_gc_sections 1 +#define elf_backend_plt_alignment 2 +#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 4 #define TARGET_BIG_SYM bfd_elf32_hppa_vec #define TARGET_BIG_NAME "elf32-hppa" diff --git a/bfd/elf32-hppa.h b/bfd/elf32-hppa.h index 127170c25f..acbdb9c951 100644 --- a/bfd/elf32-hppa.h +++ b/bfd/elf32-hppa.h @@ -37,13 +37,16 @@ #include "elf/hppa.h" boolean elf32_hppa_size_stubs - PARAMS ((bfd *, struct bfd_link_info *, + PARAMS ((bfd *, boolean, struct bfd_link_info *, asection * (*) PARAMS ((const char *, asection *)), void (*) PARAMS ((void)))); -boolean elf32_hppa_build_stubs +boolean elf32_hppa_set_gp PARAMS ((bfd *, struct bfd_link_info *)); +boolean elf32_hppa_build_stubs + PARAMS ((struct bfd_link_info *)); + extern elf_hppa_reloc_type ** _bfd_elf32_hppa_gen_reloc_type PARAMS ((bfd *, elf_hppa_reloc_type, int, unsigned int, int, asymbol *));