| /* MIPS-specific support for ELF |
| Copyright (C) 1993-2024 Free Software Foundation, Inc. |
| |
| Most of the information added by Ian Lance Taylor, Cygnus Support, |
| <ian@cygnus.com>. |
| N32/64 ABI support added by Mark Mitchell, CodeSourcery, LLC. |
| <mark@codesourcery.com> |
| Traditional MIPS targets support added by Koundinya.K, Dansk Data |
| Elektronik & Operations Research Group. <kk@ddeorg.soft.net> |
| |
| This file is part of BFD, the Binary File Descriptor library. |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, write to the Free Software |
| Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, |
| MA 02110-1301, USA. */ |
| |
| |
| /* This file handles functionality common to the different MIPS ABI's. */ |
| |
| #include "sysdep.h" |
| #include "bfd.h" |
| #include "libbfd.h" |
| #include "libiberty.h" |
| #include "elf-bfd.h" |
| #include "ecoff-bfd.h" |
| #include "elfxx-mips.h" |
| #include "elf/mips.h" |
| #include "elf-vxworks.h" |
| #include "dwarf2.h" |
| |
| /* Get the ECOFF swapping routines. */ |
| #include "coff/sym.h" |
| #include "coff/symconst.h" |
| #include "coff/ecoff.h" |
| #include "coff/mips.h" |
| |
| #include "hashtab.h" |
| |
| /* Types of TLS GOT entry. */ |
| enum mips_got_tls_type { |
| GOT_TLS_NONE, |
| GOT_TLS_GD, |
| GOT_TLS_LDM, |
| GOT_TLS_IE |
| }; |
| |
| /* This structure is used to hold information about one GOT entry. |
| There are four types of entry: |
| |
| (1) an absolute address |
| requires: abfd == NULL |
| fields: d.address |
| |
| (2) a SYMBOL + OFFSET address, where SYMBOL is local to an input bfd |
| requires: abfd != NULL, symndx >= 0, tls_type != GOT_TLS_LDM |
| fields: abfd, symndx, d.addend, tls_type |
| |
| (3) a SYMBOL address, where SYMBOL is not local to an input bfd |
| requires: abfd != NULL, symndx == -1 |
| fields: d.h, tls_type |
| |
| (4) a TLS LDM slot |
| requires: abfd != NULL, symndx == 0, tls_type == GOT_TLS_LDM |
| fields: none; there's only one of these per GOT. */ |
| struct mips_got_entry |
| { |
| /* One input bfd that needs the GOT entry. */ |
| bfd *abfd; |
| /* The index of the symbol, as stored in the relocation r_info, if |
| we have a local symbol; -1 otherwise. */ |
| long symndx; |
| union |
| { |
| /* If abfd == NULL, an address that must be stored in the got. */ |
| bfd_vma address; |
| /* If abfd != NULL && symndx != -1, the addend of the relocation |
| that should be added to the symbol value. */ |
| bfd_vma addend; |
| /* If abfd != NULL && symndx == -1, the hash table entry |
| corresponding to a symbol in the GOT. The symbol's entry |
| is in the local area if h->global_got_area is GGA_NONE, |
| otherwise it is in the global area. */ |
| struct mips_elf_link_hash_entry *h; |
| } d; |
| |
| /* The TLS type of this GOT entry. An LDM GOT entry will be a local |
| symbol entry with r_symndx == 0. */ |
| unsigned char tls_type; |
| |
| /* True if we have filled in the GOT contents for a TLS entry, |
| and created the associated relocations. */ |
| unsigned char tls_initialized; |
| |
| /* The offset from the beginning of the .got section to the entry |
| corresponding to this symbol+addend. If it's a global symbol |
| whose offset is yet to be decided, it's going to be -1. */ |
| long gotidx; |
| }; |
| |
| /* This structure represents a GOT page reference from an input bfd. |
| Each instance represents a symbol + ADDEND, where the representation |
| of the symbol depends on whether it is local to the input bfd. |
| If it is, then SYMNDX >= 0, and the symbol has index SYMNDX in U.ABFD. |
| Otherwise, SYMNDX < 0 and U.H points to the symbol's hash table entry. |
| |
| Page references with SYMNDX >= 0 always become page references |
| in the output. Page references with SYMNDX < 0 only become page |
| references if the symbol binds locally; in other cases, the page |
| reference decays to a global GOT reference. */ |
| struct mips_got_page_ref |
| { |
| long symndx; |
| union |
| { |
| struct mips_elf_link_hash_entry *h; |
| bfd *abfd; |
| } u; |
| bfd_vma addend; |
| }; |
| |
| /* This structure describes a range of addends: [MIN_ADDEND, MAX_ADDEND]. |
| The structures form a non-overlapping list that is sorted by increasing |
| MIN_ADDEND. */ |
| struct mips_got_page_range |
| { |
| struct mips_got_page_range *next; |
| bfd_signed_vma min_addend; |
| bfd_signed_vma max_addend; |
| }; |
| |
| /* This structure describes the range of addends that are applied to page |
| relocations against a given section. */ |
| struct mips_got_page_entry |
| { |
| /* The section that these entries are based on. */ |
| asection *sec; |
| /* The ranges for this page entry. */ |
| struct mips_got_page_range *ranges; |
| /* The maximum number of page entries needed for RANGES. */ |
| bfd_vma num_pages; |
| }; |
| |
| /* This structure is used to hold .got information when linking. */ |
| |
| struct mips_got_info |
| { |
| /* The number of global .got entries. */ |
| unsigned int global_gotno; |
| /* The number of global .got entries that are in the GGA_RELOC_ONLY area. */ |
| unsigned int reloc_only_gotno; |
| /* The number of .got slots used for TLS. */ |
| unsigned int tls_gotno; |
| /* The first unused TLS .got entry. Used only during |
| mips_elf_initialize_tls_index. */ |
| unsigned int tls_assigned_gotno; |
| /* The number of local .got entries, eventually including page entries. */ |
| unsigned int local_gotno; |
| /* The maximum number of page entries needed. */ |
| unsigned int page_gotno; |
| /* The number of relocations needed for the GOT entries. */ |
| unsigned int relocs; |
| /* The first unused local .got entry. */ |
| unsigned int assigned_low_gotno; |
| /* The last unused local .got entry. */ |
| unsigned int assigned_high_gotno; |
| /* A hash table holding members of the got. */ |
| struct htab *got_entries; |
| /* A hash table holding mips_got_page_ref structures. */ |
| struct htab *got_page_refs; |
| /* A hash table of mips_got_page_entry structures. */ |
| struct htab *got_page_entries; |
| /* In multi-got links, a pointer to the next got (err, rather, most |
| of the time, it points to the previous got). */ |
| struct mips_got_info *next; |
| }; |
| |
| /* Structure passed when merging bfds' gots. */ |
| |
| struct mips_elf_got_per_bfd_arg |
| { |
| /* The output bfd. */ |
| bfd *obfd; |
| /* The link information. */ |
| struct bfd_link_info *info; |
| /* A pointer to the primary got, i.e., the one that's going to get |
| the implicit relocations from DT_MIPS_LOCAL_GOTNO and |
| DT_MIPS_GOTSYM. */ |
| struct mips_got_info *primary; |
| /* A non-primary got we're trying to merge with other input bfd's |
| gots. */ |
| struct mips_got_info *current; |
| /* The maximum number of got entries that can be addressed with a |
| 16-bit offset. */ |
| unsigned int max_count; |
| /* The maximum number of page entries needed by each got. */ |
| unsigned int max_pages; |
| /* The total number of global entries which will live in the |
| primary got and be automatically relocated. This includes |
| those not referenced by the primary GOT but included in |
| the "master" GOT. */ |
| unsigned int global_count; |
| }; |
| |
| /* A structure used to pass information to htab_traverse callbacks |
| when laying out the GOT. */ |
| |
| struct mips_elf_traverse_got_arg |
| { |
| struct bfd_link_info *info; |
| struct mips_got_info *g; |
| int value; |
| }; |
| |
| struct _mips_elf_section_data |
| { |
| struct bfd_elf_section_data elf; |
| union |
| { |
| bfd_byte *tdata; |
| } u; |
| }; |
| |
| #define mips_elf_section_data(sec) \ |
| ((struct _mips_elf_section_data *) elf_section_data (sec)) |
| |
| #define is_mips_elf(bfd) \ |
| (bfd_get_flavour (bfd) == bfd_target_elf_flavour \ |
| && elf_tdata (bfd) != NULL \ |
| && elf_object_id (bfd) == MIPS_ELF_DATA) |
| |
| /* The ABI says that every symbol used by dynamic relocations must have |
| a global GOT entry. Among other things, this provides the dynamic |
| linker with a free, directly-indexed cache. The GOT can therefore |
| contain symbols that are not referenced by GOT relocations themselves |
| (in other words, it may have symbols that are not referenced by things |
| like R_MIPS_GOT16 and R_MIPS_GOT_PAGE). |
| |
| GOT relocations are less likely to overflow if we put the associated |
| GOT entries towards the beginning. We therefore divide the global |
| GOT entries into two areas: "normal" and "reloc-only". Entries in |
| the first area can be used for both dynamic relocations and GP-relative |
| accesses, while those in the "reloc-only" area are for dynamic |
| relocations only. |
| |
| These GGA_* ("Global GOT Area") values are organised so that lower |
| values are more general than higher values. Also, non-GGA_NONE |
| values are ordered by the position of the area in the GOT. */ |
| #define GGA_NORMAL 0 |
| #define GGA_RELOC_ONLY 1 |
| #define GGA_NONE 2 |
| |
| /* Information about a non-PIC interface to a PIC function. There are |
| two ways of creating these interfaces. The first is to add: |
| |
| lui $25,%hi(func) |
| addiu $25,$25,%lo(func) |
| |
| immediately before a PIC function "func". The second is to add: |
| |
| lui $25,%hi(func) |
| j func |
| addiu $25,$25,%lo(func) |
| |
| to a separate trampoline section. |
| |
| Stubs of the first kind go in a new section immediately before the |
| target function. Stubs of the second kind go in a single section |
| pointed to by the hash table's "strampoline" field. */ |
| struct mips_elf_la25_stub { |
| /* The generated section that contains this stub. */ |
| asection *stub_section; |
| |
| /* The offset of the stub from the start of STUB_SECTION. */ |
| bfd_vma offset; |
| |
| /* One symbol for the original function. Its location is available |
| in H->root.root.u.def. */ |
| struct mips_elf_link_hash_entry *h; |
| }; |
| |
| /* Macros for populating a mips_elf_la25_stub. */ |
| |
| #define LA25_LUI(VAL) (0x3c190000 | (VAL)) /* lui t9,VAL */ |
| #define LA25_J(VAL) (0x08000000 | (((VAL) >> 2) & 0x3ffffff)) /* j VAL */ |
| #define LA25_BC(VAL) (0xc8000000 | (((VAL) >> 2) & 0x3ffffff)) /* bc VAL */ |
| #define LA25_ADDIU(VAL) (0x27390000 | (VAL)) /* addiu t9,t9,VAL */ |
| #define LA25_LUI_MICROMIPS(VAL) \ |
| (0x41b90000 | (VAL)) /* lui t9,VAL */ |
| #define LA25_J_MICROMIPS(VAL) \ |
| (0xd4000000 | (((VAL) >> 1) & 0x3ffffff)) /* j VAL */ |
| #define LA25_ADDIU_MICROMIPS(VAL) \ |
| (0x33390000 | (VAL)) /* addiu t9,t9,VAL */ |
| |
| /* This structure is passed to mips_elf_sort_hash_table_f when sorting |
| the dynamic symbols. */ |
| |
| struct mips_elf_hash_sort_data |
| { |
| /* The symbol in the global GOT with the lowest dynamic symbol table |
| index. */ |
| struct elf_link_hash_entry *low; |
| /* The least dynamic symbol table index corresponding to a non-TLS |
| symbol with a GOT entry. */ |
| bfd_size_type min_got_dynindx; |
| /* The greatest dynamic symbol table index corresponding to a symbol |
| with a GOT entry that is not referenced (e.g., a dynamic symbol |
| with dynamic relocations pointing to it from non-primary GOTs). */ |
| bfd_size_type max_unref_got_dynindx; |
| /* The greatest dynamic symbol table index corresponding to a local |
| symbol. */ |
| bfd_size_type max_local_dynindx; |
| /* The greatest dynamic symbol table index corresponding to an external |
| symbol without a GOT entry. */ |
| bfd_size_type max_non_got_dynindx; |
| /* If non-NULL, output BFD for .MIPS.xhash finalization. */ |
| bfd *output_bfd; |
| /* If non-NULL, pointer to contents of .MIPS.xhash for filling in |
| real final dynindx. */ |
| bfd_byte *mipsxhash; |
| }; |
| |
| /* We make up to two PLT entries if needed, one for standard MIPS code |
| and one for compressed code, either a MIPS16 or microMIPS one. We |
| keep a separate record of traditional lazy-binding stubs, for easier |
| processing. */ |
| |
| struct plt_entry |
| { |
| /* Traditional SVR4 stub offset, or -1 if none. */ |
| bfd_vma stub_offset; |
| |
| /* Standard PLT entry offset, or -1 if none. */ |
| bfd_vma mips_offset; |
| |
| /* Compressed PLT entry offset, or -1 if none. */ |
| bfd_vma comp_offset; |
| |
| /* The corresponding .got.plt index, or -1 if none. */ |
| bfd_vma gotplt_index; |
| |
| /* Whether we need a standard PLT entry. */ |
| unsigned int need_mips : 1; |
| |
| /* Whether we need a compressed PLT entry. */ |
| unsigned int need_comp : 1; |
| }; |
| |
| /* The MIPS ELF linker needs additional information for each symbol in |
| the global hash table. */ |
| |
| struct mips_elf_link_hash_entry |
| { |
| struct elf_link_hash_entry root; |
| |
| /* External symbol information. */ |
| EXTR esym; |
| |
| /* The la25 stub we have created for ths symbol, if any. */ |
| struct mips_elf_la25_stub *la25_stub; |
| |
| /* Number of R_MIPS_32, R_MIPS_REL32, or R_MIPS_64 relocs against |
| this symbol. */ |
| unsigned int possibly_dynamic_relocs; |
| |
| /* If there is a stub that 32 bit functions should use to call this |
| 16 bit function, this points to the section containing the stub. */ |
| asection *fn_stub; |
| |
| /* If there is a stub that 16 bit functions should use to call this |
| 32 bit function, this points to the section containing the stub. */ |
| asection *call_stub; |
| |
| /* This is like the call_stub field, but it is used if the function |
| being called returns a floating point value. */ |
| asection *call_fp_stub; |
| |
| /* If non-zero, location in .MIPS.xhash to write real final dynindx. */ |
| bfd_vma mipsxhash_loc; |
| |
| /* The highest GGA_* value that satisfies all references to this symbol. */ |
| unsigned int global_got_area : 2; |
| |
| /* True if all GOT relocations against this symbol are for calls. This is |
| a looser condition than no_fn_stub below, because there may be other |
| non-call non-GOT relocations against the symbol. */ |
| unsigned int got_only_for_calls : 1; |
| |
| /* True if one of the relocations described by possibly_dynamic_relocs |
| is against a readonly section. */ |
| unsigned int readonly_reloc : 1; |
| |
| /* True if there is a relocation against this symbol that must be |
| resolved by the static linker (in other words, if the relocation |
| cannot possibly be made dynamic). */ |
| unsigned int has_static_relocs : 1; |
| |
| /* True if we must not create a .MIPS.stubs entry for this symbol. |
| This is set, for example, if there are relocations related to |
| taking the function's address, i.e. any but R_MIPS_CALL*16 ones. |
| See "MIPS ABI Supplement, 3rd Edition", p. 4-20. */ |
| unsigned int no_fn_stub : 1; |
| |
| /* Whether we need the fn_stub; this is true if this symbol appears |
| in any relocs other than a 16 bit call. */ |
| unsigned int need_fn_stub : 1; |
| |
| /* True if this symbol is referenced by branch relocations from |
| any non-PIC input file. This is used to determine whether an |
| la25 stub is required. */ |
| unsigned int has_nonpic_branches : 1; |
| |
| /* Does this symbol need a traditional MIPS lazy-binding stub |
| (as opposed to a PLT entry)? */ |
| unsigned int needs_lazy_stub : 1; |
| |
| /* Does this symbol resolve to a PLT entry? */ |
| unsigned int use_plt_entry : 1; |
| }; |
| |
| /* MIPS ELF linker hash table. */ |
| |
| struct mips_elf_link_hash_table |
| { |
| struct elf_link_hash_table root; |
| |
| /* The number of .rtproc entries. */ |
| bfd_size_type procedure_count; |
| |
| /* The size of the .compact_rel section (if SGI_COMPAT). */ |
| bfd_size_type compact_rel_size; |
| |
| /* This flag indicates that the value of DT_MIPS_RLD_MAP dynamic entry |
| is set to the address of __rld_obj_head as in IRIX5 and IRIX6. */ |
| bool use_rld_obj_head; |
| |
| /* The __rld_map or __rld_obj_head symbol. */ |
| struct elf_link_hash_entry *rld_symbol; |
| |
| /* This is set if we see any mips16 stub sections. */ |
| bool mips16_stubs_seen; |
| |
| /* True if we can generate copy relocs and PLTs. */ |
| bool use_plts_and_copy_relocs; |
| |
| /* True if we can only use 32-bit microMIPS instructions. */ |
| bool insn32; |
| |
| /* True if we suppress checks for invalid branches between ISA modes. */ |
| bool ignore_branch_isa; |
| |
| /* True if we are targetting R6 compact branches. */ |
| bool compact_branches; |
| |
| /* True if we already reported the small-data section overflow. */ |
| bool small_data_overflow_reported; |
| |
| /* True if we use the special `__gnu_absolute_zero' symbol. */ |
| bool use_absolute_zero; |
| |
| /* True if we have been configured for a GNU target. */ |
| bool gnu_target; |
| |
| /* Shortcuts to some dynamic sections, or NULL if they are not |
| being used. */ |
| asection *srelplt2; |
| asection *sstubs; |
| |
| /* The master GOT information. */ |
| struct mips_got_info *got_info; |
| |
| /* The global symbol in the GOT with the lowest index in the dynamic |
| symbol table. */ |
| struct elf_link_hash_entry *global_gotsym; |
| |
| /* The size of the PLT header in bytes. */ |
| bfd_vma plt_header_size; |
| |
| /* The size of a standard PLT entry in bytes. */ |
| bfd_vma plt_mips_entry_size; |
| |
| /* The size of a compressed PLT entry in bytes. */ |
| bfd_vma plt_comp_entry_size; |
| |
| /* The offset of the next standard PLT entry to create. */ |
| bfd_vma plt_mips_offset; |
| |
| /* The offset of the next compressed PLT entry to create. */ |
| bfd_vma plt_comp_offset; |
| |
| /* The index of the next .got.plt entry to create. */ |
| bfd_vma plt_got_index; |
| |
| /* The number of functions that need a lazy-binding stub. */ |
| bfd_vma lazy_stub_count; |
| |
| /* The size of a function stub entry in bytes. */ |
| bfd_vma function_stub_size; |
| |
| /* The number of reserved entries at the beginning of the GOT. */ |
| unsigned int reserved_gotno; |
| |
| /* The section used for mips_elf_la25_stub trampolines. |
| See the comment above that structure for details. */ |
| asection *strampoline; |
| |
| /* A table of mips_elf_la25_stubs, indexed by (input_section, offset) |
| pairs. */ |
| htab_t la25_stubs; |
| |
| /* A function FN (NAME, IS, OS) that creates a new input section |
| called NAME and links it to output section OS. If IS is nonnull, |
| the new section should go immediately before it, otherwise it |
| should go at the (current) beginning of OS. |
| |
| The function returns the new section on success, otherwise it |
| returns null. */ |
| asection *(*add_stub_section) (const char *, asection *, asection *); |
| |
| /* Is the PLT header compressed? */ |
| unsigned int plt_header_is_comp : 1; |
| }; |
| |
| /* Get the MIPS ELF linker hash table from a link_info structure. */ |
| |
| #define mips_elf_hash_table(p) \ |
| ((is_elf_hash_table ((p)->hash) \ |
| && elf_hash_table_id (elf_hash_table (p)) == MIPS_ELF_DATA) \ |
| ? (struct mips_elf_link_hash_table *) (p)->hash : NULL) |
| |
| /* A structure used to communicate with htab_traverse callbacks. */ |
| struct mips_htab_traverse_info |
| { |
| /* The usual link-wide information. */ |
| struct bfd_link_info *info; |
| bfd *output_bfd; |
| |
| /* Starts off FALSE and is set to TRUE if the link should be aborted. */ |
| bool error; |
| }; |
| |
| /* Used to store a REL high-part relocation such as R_MIPS_HI16 or |
| R_MIPS_GOT16. REL is the relocation, INPUT_SECTION is the section |
| that contains the relocation field and DATA points to the start of |
| INPUT_SECTION. */ |
| |
| struct mips_hi16 |
| { |
| struct mips_hi16 *next; |
| bfd_byte *data; |
| asection *input_section; |
| arelent rel; |
| }; |
| |
| /* MIPS ELF private object data. */ |
| |
| struct mips_elf_obj_tdata |
| { |
| /* Generic ELF private object data. */ |
| struct elf_obj_tdata root; |
| |
| /* Input BFD providing Tag_GNU_MIPS_ABI_FP attribute for output. */ |
| bfd *abi_fp_bfd; |
| |
| /* Input BFD providing Tag_GNU_MIPS_ABI_MSA attribute for output. */ |
| bfd *abi_msa_bfd; |
| |
| /* The abiflags for this object. */ |
| Elf_Internal_ABIFlags_v0 abiflags; |
| bool abiflags_valid; |
| |
| /* The GOT requirements of input bfds. */ |
| struct mips_got_info *got; |
| |
| /* Used by _bfd_mips_elf_find_nearest_line. The structure could be |
| included directly in this one, but there's no point to wasting |
| the memory just for the infrequently called find_nearest_line. */ |
| struct mips_elf_find_line *find_line_info; |
| |
| /* An array of stub sections indexed by symbol number. */ |
| asection **local_stubs; |
| asection **local_call_stubs; |
| |
| /* The Irix 5 support uses two virtual sections, which represent |
| text/data symbols defined in dynamic objects. */ |
| asymbol *elf_data_symbol; |
| asymbol *elf_text_symbol; |
| asection *elf_data_section; |
| asection *elf_text_section; |
| |
| struct mips_hi16 *mips_hi16_list; |
| }; |
| |
| /* Get MIPS ELF private object data from BFD's tdata. */ |
| |
| #define mips_elf_tdata(bfd) \ |
| ((struct mips_elf_obj_tdata *) (bfd)->tdata.any) |
| |
| #define TLS_RELOC_P(r_type) \ |
| (r_type == R_MIPS_TLS_DTPMOD32 \ |
| || r_type == R_MIPS_TLS_DTPMOD64 \ |
| || r_type == R_MIPS_TLS_DTPREL32 \ |
| || r_type == R_MIPS_TLS_DTPREL64 \ |
| || r_type == R_MIPS_TLS_GD \ |
| || r_type == R_MIPS_TLS_LDM \ |
| || r_type == R_MIPS_TLS_DTPREL_HI16 \ |
| || r_type == R_MIPS_TLS_DTPREL_LO16 \ |
| || r_type == R_MIPS_TLS_GOTTPREL \ |
| || r_type == R_MIPS_TLS_TPREL32 \ |
| || r_type == R_MIPS_TLS_TPREL64 \ |
| || r_type == R_MIPS_TLS_TPREL_HI16 \ |
| || r_type == R_MIPS_TLS_TPREL_LO16 \ |
| || r_type == R_MIPS16_TLS_GD \ |
| || r_type == R_MIPS16_TLS_LDM \ |
| || r_type == R_MIPS16_TLS_DTPREL_HI16 \ |
| || r_type == R_MIPS16_TLS_DTPREL_LO16 \ |
| || r_type == R_MIPS16_TLS_GOTTPREL \ |
| || r_type == R_MIPS16_TLS_TPREL_HI16 \ |
| || r_type == R_MIPS16_TLS_TPREL_LO16 \ |
| || r_type == R_MICROMIPS_TLS_GD \ |
| || r_type == R_MICROMIPS_TLS_LDM \ |
| || r_type == R_MICROMIPS_TLS_DTPREL_HI16 \ |
| || r_type == R_MICROMIPS_TLS_DTPREL_LO16 \ |
| || r_type == R_MICROMIPS_TLS_GOTTPREL \ |
| || r_type == R_MICROMIPS_TLS_TPREL_HI16 \ |
| || r_type == R_MICROMIPS_TLS_TPREL_LO16) |
| |
| /* Structure used to pass information to mips_elf_output_extsym. */ |
| |
| struct extsym_info |
| { |
| bfd *abfd; |
| struct bfd_link_info *info; |
| struct ecoff_debug_info *debug; |
| const struct ecoff_debug_swap *swap; |
| bool failed; |
| }; |
| |
| /* The names of the runtime procedure table symbols used on IRIX5. */ |
| |
| static const char * const mips_elf_dynsym_rtproc_names[] = |
| { |
| "_procedure_table", |
| "_procedure_string_table", |
| "_procedure_table_size", |
| NULL |
| }; |
| |
| /* These structures are used to generate the .compact_rel section on |
| IRIX5. */ |
| |
| typedef struct |
| { |
| unsigned long id1; /* Always one? */ |
| unsigned long num; /* Number of compact relocation entries. */ |
| unsigned long id2; /* Always two? */ |
| unsigned long offset; /* The file offset of the first relocation. */ |
| unsigned long reserved0; /* Zero? */ |
| unsigned long reserved1; /* Zero? */ |
| } Elf32_compact_rel; |
| |
| typedef struct |
| { |
| bfd_byte id1[4]; |
| bfd_byte num[4]; |
| bfd_byte id2[4]; |
| bfd_byte offset[4]; |
| bfd_byte reserved0[4]; |
| bfd_byte reserved1[4]; |
| } Elf32_External_compact_rel; |
| |
| typedef struct |
| { |
| unsigned int ctype : 1; /* 1: long 0: short format. See below. */ |
| unsigned int rtype : 4; /* Relocation types. See below. */ |
| unsigned int dist2to : 8; |
| unsigned int relvaddr : 19; /* (VADDR - vaddr of the previous entry)/ 4 */ |
| unsigned long konst; /* KONST field. See below. */ |
| unsigned long vaddr; /* VADDR to be relocated. */ |
| } Elf32_crinfo; |
| |
| typedef struct |
| { |
| unsigned int ctype : 1; /* 1: long 0: short format. See below. */ |
| unsigned int rtype : 4; /* Relocation types. See below. */ |
| unsigned int dist2to : 8; |
| unsigned int relvaddr : 19; /* (VADDR - vaddr of the previous entry)/ 4 */ |
| unsigned long konst; /* KONST field. See below. */ |
| } Elf32_crinfo2; |
| |
| typedef struct |
| { |
| bfd_byte info[4]; |
| bfd_byte konst[4]; |
| bfd_byte vaddr[4]; |
| } Elf32_External_crinfo; |
| |
| typedef struct |
| { |
| bfd_byte info[4]; |
| bfd_byte konst[4]; |
| } Elf32_External_crinfo2; |
| |
| /* These are the constants used to swap the bitfields in a crinfo. */ |
| |
| #define CRINFO_CTYPE (0x1U) |
| #define CRINFO_CTYPE_SH (31) |
| #define CRINFO_RTYPE (0xfU) |
| #define CRINFO_RTYPE_SH (27) |
| #define CRINFO_DIST2TO (0xffU) |
| #define CRINFO_DIST2TO_SH (19) |
| #define CRINFO_RELVADDR (0x7ffffU) |
| #define CRINFO_RELVADDR_SH (0) |
| |
| /* A compact relocation info has long (3 words) or short (2 words) |
| formats. A short format doesn't have VADDR field and relvaddr |
| fields contains ((VADDR - vaddr of the previous entry) >> 2). */ |
| #define CRF_MIPS_LONG 1 |
| #define CRF_MIPS_SHORT 0 |
| |
| /* There are 4 types of compact relocation at least. The value KONST |
| has different meaning for each type: |
| |
| (type) (konst) |
| CT_MIPS_REL32 Address in data |
| CT_MIPS_WORD Address in word (XXX) |
| CT_MIPS_GPHI_LO GP - vaddr |
| CT_MIPS_JMPAD Address to jump |
| */ |
| |
| #define CRT_MIPS_REL32 0xa |
| #define CRT_MIPS_WORD 0xb |
| #define CRT_MIPS_GPHI_LO 0xc |
| #define CRT_MIPS_JMPAD 0xd |
| |
| #define mips_elf_set_cr_format(x,format) ((x).ctype = (format)) |
| #define mips_elf_set_cr_type(x,type) ((x).rtype = (type)) |
| #define mips_elf_set_cr_dist2to(x,v) ((x).dist2to = (v)) |
| #define mips_elf_set_cr_relvaddr(x,d) ((x).relvaddr = (d)<<2) |
| |
| /* The structure of the runtime procedure descriptor created by the |
| loader for use by the static exception system. */ |
| |
| typedef struct runtime_pdr { |
| bfd_vma adr; /* Memory address of start of procedure. */ |
| long regmask; /* Save register mask. */ |
| long regoffset; /* Save register offset. */ |
| long fregmask; /* Save floating point register mask. */ |
| long fregoffset; /* Save floating point register offset. */ |
| long frameoffset; /* Frame size. */ |
| short framereg; /* Frame pointer register. */ |
| short pcreg; /* Offset or reg of return pc. */ |
| long irpss; /* Index into the runtime string table. */ |
| long reserved; |
| struct exception_info *exception_info;/* Pointer to exception array. */ |
| } RPDR, *pRPDR; |
| #define cbRPDR sizeof (RPDR) |
| #define rpdNil ((pRPDR) 0) |
| |
| static struct mips_got_entry *mips_elf_create_local_got_entry |
| (bfd *, struct bfd_link_info *, bfd *, bfd_vma, unsigned long, |
| struct mips_elf_link_hash_entry *, int); |
| static bool mips_elf_sort_hash_table_f |
| (struct mips_elf_link_hash_entry *, void *); |
| static bfd_vma mips_elf_high |
| (bfd_vma); |
| static bool mips_elf_create_dynamic_relocation |
| (bfd *, struct bfd_link_info *, const Elf_Internal_Rela *, |
| struct mips_elf_link_hash_entry *, asection *, bfd_vma, |
| bfd_vma *, asection *); |
| static bfd_vma mips_elf_adjust_gp |
| (bfd *, struct mips_got_info *, bfd *); |
| |
| /* This will be used when we sort the dynamic relocation records. */ |
| static bfd *reldyn_sorting_bfd; |
| |
| /* True if ABFD is for CPUs with load interlocking that include |
| non-MIPS1 CPUs and R3900. */ |
| #define LOAD_INTERLOCKS_P(abfd) \ |
| ( ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH) != EF_MIPS_ARCH_1) \ |
| || ((elf_elfheader (abfd)->e_flags & EF_MIPS_MACH) == EF_MIPS_MACH_3900)) |
| |
| /* True if ABFD is for CPUs that are faster if JAL is converted to BAL. |
| This should be safe for all architectures. We enable this predicate |
| for RM9000 for now. */ |
| #define JAL_TO_BAL_P(abfd) \ |
| ((elf_elfheader (abfd)->e_flags & EF_MIPS_MACH) == EF_MIPS_MACH_9000) |
| |
| /* True if ABFD is for CPUs that are faster if JALR is converted to BAL. |
| This should be safe for all architectures. We enable this predicate for |
| all CPUs. */ |
| #define JALR_TO_BAL_P(abfd) 1 |
| |
| /* True if ABFD is for CPUs that are faster if JR is converted to B. |
| This should be safe for all architectures. We enable this predicate for |
| all CPUs. */ |
| #define JR_TO_B_P(abfd) 1 |
| |
| /* True if ABFD is a PIC object. */ |
| #define PIC_OBJECT_P(abfd) \ |
| ((elf_elfheader (abfd)->e_flags & EF_MIPS_PIC) != 0) |
| |
| /* Nonzero if ABFD is using the O32 ABI. */ |
| #define ABI_O32_P(abfd) \ |
| ((elf_elfheader (abfd)->e_flags & EF_MIPS_ABI) == EF_MIPS_ABI_O32) |
| |
| /* Nonzero if ABFD is using the N32 ABI. */ |
| #define ABI_N32_P(abfd) \ |
| ((elf_elfheader (abfd)->e_flags & EF_MIPS_ABI2) != 0) |
| |
| /* Nonzero if ABFD is using the N64 ABI. */ |
| #define ABI_64_P(abfd) \ |
| (get_elf_backend_data (abfd)->s->elfclass == ELFCLASS64) |
| |
| /* Nonzero if ABFD is using NewABI conventions. */ |
| #define NEWABI_P(abfd) (ABI_N32_P (abfd) || ABI_64_P (abfd)) |
| |
| /* Nonzero if ABFD has microMIPS code. */ |
| #define MICROMIPS_P(abfd) \ |
| ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH_ASE_MICROMIPS) != 0) |
| |
| /* Nonzero if ABFD is MIPS R6. */ |
| #define MIPSR6_P(abfd) \ |
| ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH) == EF_MIPS_ARCH_32R6 \ |
| || (elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH) == EF_MIPS_ARCH_64R6) |
| |
| /* The IRIX compatibility level we are striving for. */ |
| #define IRIX_COMPAT(abfd) \ |
| (get_elf_backend_data (abfd)->elf_backend_mips_irix_compat (abfd)) |
| |
| /* Whether we are trying to be compatible with IRIX at all. */ |
| #define SGI_COMPAT(abfd) \ |
| (IRIX_COMPAT (abfd) != ict_none) |
| |
| /* The name of the options section. */ |
| #define MIPS_ELF_OPTIONS_SECTION_NAME(abfd) \ |
| (NEWABI_P (abfd) ? ".MIPS.options" : ".options") |
| |
| /* True if NAME is the recognized name of any SHT_MIPS_OPTIONS section. |
| Some IRIX system files do not use MIPS_ELF_OPTIONS_SECTION_NAME. */ |
| #define MIPS_ELF_OPTIONS_SECTION_NAME_P(NAME) \ |
| (strcmp (NAME, ".MIPS.options") == 0 || strcmp (NAME, ".options") == 0) |
| |
| /* True if NAME is the recognized name of any SHT_MIPS_ABIFLAGS section. */ |
| #define MIPS_ELF_ABIFLAGS_SECTION_NAME_P(NAME) \ |
| (strcmp (NAME, ".MIPS.abiflags") == 0) |
| |
| /* Whether the section is readonly. */ |
| #define MIPS_ELF_READONLY_SECTION(sec) \ |
| ((sec->flags & (SEC_ALLOC | SEC_LOAD | SEC_READONLY)) \ |
| == (SEC_ALLOC | SEC_LOAD | SEC_READONLY)) |
| |
| /* The name of the stub section. */ |
| #define MIPS_ELF_STUB_SECTION_NAME(abfd) ".MIPS.stubs" |
| |
| /* The size of an external REL relocation. */ |
| #define MIPS_ELF_REL_SIZE(abfd) \ |
| (get_elf_backend_data (abfd)->s->sizeof_rel) |
| |
| /* The size of an external RELA relocation. */ |
| #define MIPS_ELF_RELA_SIZE(abfd) \ |
| (get_elf_backend_data (abfd)->s->sizeof_rela) |
| |
| /* The size of an external dynamic table entry. */ |
| #define MIPS_ELF_DYN_SIZE(abfd) \ |
| (get_elf_backend_data (abfd)->s->sizeof_dyn) |
| |
| /* The size of a GOT entry. */ |
| #define MIPS_ELF_GOT_SIZE(abfd) \ |
| (get_elf_backend_data (abfd)->s->arch_size / 8) |
| |
| /* The size of the .rld_map section. */ |
| #define MIPS_ELF_RLD_MAP_SIZE(abfd) \ |
| (get_elf_backend_data (abfd)->s->arch_size / 8) |
| |
| /* The size of a symbol-table entry. */ |
| #define MIPS_ELF_SYM_SIZE(abfd) \ |
| (get_elf_backend_data (abfd)->s->sizeof_sym) |
| |
| /* The default alignment for sections, as a power of two. */ |
| #define MIPS_ELF_LOG_FILE_ALIGN(abfd) \ |
| (get_elf_backend_data (abfd)->s->log_file_align) |
| |
| /* Get word-sized data. */ |
| #define MIPS_ELF_GET_WORD(abfd, ptr) \ |
| (ABI_64_P (abfd) ? bfd_get_64 (abfd, ptr) : bfd_get_32 (abfd, ptr)) |
| |
| /* Put out word-sized data. */ |
| #define MIPS_ELF_PUT_WORD(abfd, val, ptr) \ |
| (ABI_64_P (abfd) \ |
| ? bfd_put_64 (abfd, val, ptr) \ |
| : bfd_put_32 (abfd, val, ptr)) |
| |
| /* The opcode for word-sized loads (LW or LD). */ |
| #define MIPS_ELF_LOAD_WORD(abfd) \ |
| (ABI_64_P (abfd) ? 0xdc000000 : 0x8c000000) |
| |
| /* Add a dynamic symbol table-entry. */ |
| #define MIPS_ELF_ADD_DYNAMIC_ENTRY(info, tag, val) \ |
| _bfd_elf_add_dynamic_entry (info, tag, val) |
| |
| #define MIPS_ELF_RTYPE_TO_HOWTO(abfd, rtype, rela) \ |
| (get_elf_backend_data (abfd)->elf_backend_mips_rtype_to_howto (abfd, rtype, rela)) |
| |
| /* The name of the dynamic relocation section. */ |
| #define MIPS_ELF_REL_DYN_NAME(INFO) \ |
| (mips_elf_hash_table (INFO)->root.target_os == is_vxworks \ |
| ? ".rela.dyn" : ".rel.dyn") |
| |
| /* In case we're on a 32-bit machine, construct a 64-bit "-1" value |
| from smaller values. Start with zero, widen, *then* decrement. */ |
| #define MINUS_ONE (((bfd_vma)0) - 1) |
| #define MINUS_TWO (((bfd_vma)0) - 2) |
| |
| /* The value to write into got[1] for SVR4 targets, to identify it is |
| a GNU object. The dynamic linker can then use got[1] to store the |
| module pointer. */ |
| #define MIPS_ELF_GNU_GOT1_MASK(abfd) \ |
| ((bfd_vma) 1 << (ABI_64_P (abfd) ? 63 : 31)) |
| |
| /* The offset of $gp from the beginning of the .got section. */ |
| #define ELF_MIPS_GP_OFFSET(INFO) \ |
| (mips_elf_hash_table (INFO)->root.target_os == is_vxworks \ |
| ? 0x0 : 0x7ff0) |
| |
| /* The maximum size of the GOT for it to be addressable using 16-bit |
| offsets from $gp. */ |
| #define MIPS_ELF_GOT_MAX_SIZE(INFO) (ELF_MIPS_GP_OFFSET (INFO) + 0x7fff) |
| |
| /* Instructions which appear in a stub. */ |
| #define STUB_LW(abfd) \ |
| ((ABI_64_P (abfd) \ |
| ? 0xdf998010 /* ld t9,0x8010(gp) */ \ |
| : 0x8f998010)) /* lw t9,0x8010(gp) */ |
| #define STUB_MOVE 0x03e07825 /* or t7,ra,zero */ |
| #define STUB_LUI(VAL) (0x3c180000 + (VAL)) /* lui t8,VAL */ |
| #define STUB_JALR 0x0320f809 /* jalr ra,t9 */ |
| #define STUB_JALRC 0xf8190000 /* jalrc ra,t9 */ |
| #define STUB_ORI(VAL) (0x37180000 + (VAL)) /* ori t8,t8,VAL */ |
| #define STUB_LI16U(VAL) (0x34180000 + (VAL)) /* ori t8,zero,VAL unsigned */ |
| #define STUB_LI16S(abfd, VAL) \ |
| ((ABI_64_P (abfd) \ |
| ? (0x64180000 + (VAL)) /* daddiu t8,zero,VAL sign extended */ \ |
| : (0x24180000 + (VAL)))) /* addiu t8,zero,VAL sign extended */ |
| |
| /* Likewise for the microMIPS ASE. */ |
| #define STUB_LW_MICROMIPS(abfd) \ |
| (ABI_64_P (abfd) \ |
| ? 0xdf3c8010 /* ld t9,0x8010(gp) */ \ |
| : 0xff3c8010) /* lw t9,0x8010(gp) */ |
| #define STUB_MOVE_MICROMIPS 0x0dff /* move t7,ra */ |
| #define STUB_MOVE32_MICROMIPS 0x001f7a90 /* or t7,ra,zero */ |
| #define STUB_LUI_MICROMIPS(VAL) \ |
| (0x41b80000 + (VAL)) /* lui t8,VAL */ |
| #define STUB_JALR_MICROMIPS 0x45d9 /* jalr t9 */ |
| #define STUB_JALR32_MICROMIPS 0x03f90f3c /* jalr ra,t9 */ |
| #define STUB_ORI_MICROMIPS(VAL) \ |
| (0x53180000 + (VAL)) /* ori t8,t8,VAL */ |
| #define STUB_LI16U_MICROMIPS(VAL) \ |
| (0x53000000 + (VAL)) /* ori t8,zero,VAL unsigned */ |
| #define STUB_LI16S_MICROMIPS(abfd, VAL) \ |
| (ABI_64_P (abfd) \ |
| ? 0x5f000000 + (VAL) /* daddiu t8,zero,VAL sign extended */ \ |
| : 0x33000000 + (VAL)) /* addiu t8,zero,VAL sign extended */ |
| |
| #define MIPS_FUNCTION_STUB_NORMAL_SIZE 16 |
| #define MIPS_FUNCTION_STUB_BIG_SIZE 20 |
| #define MICROMIPS_FUNCTION_STUB_NORMAL_SIZE 12 |
| #define MICROMIPS_FUNCTION_STUB_BIG_SIZE 16 |
| #define MICROMIPS_INSN32_FUNCTION_STUB_NORMAL_SIZE 16 |
| #define MICROMIPS_INSN32_FUNCTION_STUB_BIG_SIZE 20 |
| |
| /* The name of the dynamic interpreter. This is put in the .interp |
| section. */ |
| |
| #define ELF_DYNAMIC_INTERPRETER(abfd) \ |
| (ABI_N32_P (abfd) ? "/usr/lib32/libc.so.1" \ |
| : ABI_64_P (abfd) ? "/usr/lib64/libc.so.1" \ |
| : "/usr/lib/libc.so.1") |
| |
| #ifdef BFD64 |
| #define MNAME(bfd,pre,pos) \ |
| (ABI_64_P (bfd) ? CONCAT4 (pre,64,_,pos) : CONCAT4 (pre,32,_,pos)) |
| #define ELF_R_SYM(bfd, i) \ |
| (ABI_64_P (bfd) ? ELF64_R_SYM (i) : ELF32_R_SYM (i)) |
| #define ELF_R_TYPE(bfd, i) \ |
| (ABI_64_P (bfd) ? ELF64_MIPS_R_TYPE (i) : ELF32_R_TYPE (i)) |
| #define ELF_R_INFO(bfd, s, t) \ |
| (ABI_64_P (bfd) ? ELF64_R_INFO (s, t) : ELF32_R_INFO (s, t)) |
| #else |
| #define MNAME(bfd,pre,pos) CONCAT4 (pre,32,_,pos) |
| #define ELF_R_SYM(bfd, i) \ |
| (ELF32_R_SYM (i)) |
| #define ELF_R_TYPE(bfd, i) \ |
| (ELF32_R_TYPE (i)) |
| #define ELF_R_INFO(bfd, s, t) \ |
| (ELF32_R_INFO (s, t)) |
| #endif |
| |
| /* The mips16 compiler uses a couple of special sections to handle |
| floating point arguments. |
| |
| Section names that look like .mips16.fn.FNNAME contain stubs that |
| copy floating point arguments from the fp regs to the gp regs and |
| then jump to FNNAME. If any 32 bit function calls FNNAME, the |
| call should be redirected to the stub instead. If no 32 bit |
| function calls FNNAME, the stub should be discarded. We need to |
| consider any reference to the function, not just a call, because |
| if the address of the function is taken we will need the stub, |
| since the address might be passed to a 32 bit function. |
| |
| Section names that look like .mips16.call.FNNAME contain stubs |
| that copy floating point arguments from the gp regs to the fp |
| regs and then jump to FNNAME. If FNNAME is a 32 bit function, |
| then any 16 bit function that calls FNNAME should be redirected |
| to the stub instead. If FNNAME is not a 32 bit function, the |
| stub should be discarded. |
| |
| .mips16.call.fp.FNNAME sections are similar, but contain stubs |
| which call FNNAME and then copy the return value from the fp regs |
| to the gp regs. These stubs store the return value in $18 while |
| calling FNNAME; any function which might call one of these stubs |
| must arrange to save $18 around the call. (This case is not |
| needed for 32 bit functions that call 16 bit functions, because |
| 16 bit functions always return floating point values in both |
| $f0/$f1 and $2/$3.) |
| |
| Note that in all cases FNNAME might be defined statically. |
| Therefore, FNNAME is not used literally. Instead, the relocation |
| information will indicate which symbol the section is for. |
| |
| We record any stubs that we find in the symbol table. */ |
| |
| #define FN_STUB ".mips16.fn." |
| #define CALL_STUB ".mips16.call." |
| #define CALL_FP_STUB ".mips16.call.fp." |
| |
| #define FN_STUB_P(name) startswith (name, FN_STUB) |
| #define CALL_STUB_P(name) startswith (name, CALL_STUB) |
| #define CALL_FP_STUB_P(name) startswith (name, CALL_FP_STUB) |
| |
| /* The format of the first PLT entry in an O32 executable. */ |
| static const bfd_vma mips_o32_exec_plt0_entry[] = |
| { |
| 0x3c1c0000, /* lui $28, %hi(&GOTPLT[0]) */ |
| 0x8f990000, /* lw $25, %lo(&GOTPLT[0])($28) */ |
| 0x279c0000, /* addiu $28, $28, %lo(&GOTPLT[0]) */ |
| 0x031cc023, /* subu $24, $24, $28 */ |
| 0x03e07825, /* or t7, ra, zero */ |
| 0x0018c082, /* srl $24, $24, 2 */ |
| 0x0320f809, /* jalr $25 */ |
| 0x2718fffe /* subu $24, $24, 2 */ |
| }; |
| |
| /* The format of the first PLT entry in an O32 executable using compact |
| jumps. */ |
| static const bfd_vma mipsr6_o32_exec_plt0_entry_compact[] = |
| { |
| 0x3c1c0000, /* lui $28, %hi(&GOTPLT[0]) */ |
| 0x8f990000, /* lw $25, %lo(&GOTPLT[0])($28) */ |
| 0x279c0000, /* addiu $28, $28, %lo(&GOTPLT[0]) */ |
| 0x031cc023, /* subu $24, $24, $28 */ |
| 0x03e07821, /* move $15, $31 # 32-bit move (addu) */ |
| 0x0018c082, /* srl $24, $24, 2 */ |
| 0x2718fffe, /* subu $24, $24, 2 */ |
| 0xf8190000 /* jalrc $25 */ |
| }; |
| |
| /* The format of the first PLT entry in an N32 executable. Different |
| because gp ($28) is not available; we use t2 ($14) instead. */ |
| static const bfd_vma mips_n32_exec_plt0_entry[] = |
| { |
| 0x3c0e0000, /* lui $14, %hi(&GOTPLT[0]) */ |
| 0x8dd90000, /* lw $25, %lo(&GOTPLT[0])($14) */ |
| 0x25ce0000, /* addiu $14, $14, %lo(&GOTPLT[0]) */ |
| 0x030ec023, /* subu $24, $24, $14 */ |
| 0x03e07825, /* or t7, ra, zero */ |
| 0x0018c082, /* srl $24, $24, 2 */ |
| 0x0320f809, /* jalr $25 */ |
| 0x2718fffe /* subu $24, $24, 2 */ |
| }; |
| |
| /* The format of the first PLT entry in an N32 executable using compact |
| jumps. Different because gp ($28) is not available; we use t2 ($14) |
| instead. */ |
| static const bfd_vma mipsr6_n32_exec_plt0_entry_compact[] = |
| { |
| 0x3c0e0000, /* lui $14, %hi(&GOTPLT[0]) */ |
| 0x8dd90000, /* lw $25, %lo(&GOTPLT[0])($14) */ |
| 0x25ce0000, /* addiu $14, $14, %lo(&GOTPLT[0]) */ |
| 0x030ec023, /* subu $24, $24, $14 */ |
| 0x03e07821, /* move $15, $31 # 32-bit move (addu) */ |
| 0x0018c082, /* srl $24, $24, 2 */ |
| 0x2718fffe, /* subu $24, $24, 2 */ |
| 0xf8190000 /* jalrc $25 */ |
| }; |
| |
| /* The format of the first PLT entry in an N64 executable. Different |
| from N32 because of the increased size of GOT entries. */ |
| static const bfd_vma mips_n64_exec_plt0_entry[] = |
| { |
| 0x3c0e0000, /* lui $14, %hi(&GOTPLT[0]) */ |
| 0xddd90000, /* ld $25, %lo(&GOTPLT[0])($14) */ |
| 0x25ce0000, /* addiu $14, $14, %lo(&GOTPLT[0]) */ |
| 0x030ec023, /* subu $24, $24, $14 */ |
| 0x03e07825, /* or t7, ra, zero */ |
| 0x0018c0c2, /* srl $24, $24, 3 */ |
| 0x0320f809, /* jalr $25 */ |
| 0x2718fffe /* subu $24, $24, 2 */ |
| }; |
| |
| /* The format of the first PLT entry in an N64 executable using compact |
| jumps. Different from N32 because of the increased size of GOT |
| entries. */ |
| static const bfd_vma mipsr6_n64_exec_plt0_entry_compact[] = |
| { |
| 0x3c0e0000, /* lui $14, %hi(&GOTPLT[0]) */ |
| 0xddd90000, /* ld $25, %lo(&GOTPLT[0])($14) */ |
| 0x25ce0000, /* addiu $14, $14, %lo(&GOTPLT[0]) */ |
| 0x030ec023, /* subu $24, $24, $14 */ |
| 0x03e0782d, /* move $15, $31 # 64-bit move (daddu) */ |
| 0x0018c0c2, /* srl $24, $24, 3 */ |
| 0x2718fffe, /* subu $24, $24, 2 */ |
| 0xf8190000 /* jalrc $25 */ |
| }; |
| |
| |
| /* The format of the microMIPS first PLT entry in an O32 executable. |
| We rely on v0 ($2) rather than t8 ($24) to contain the address |
| of the GOTPLT entry handled, so this stub may only be used when |
| all the subsequent PLT entries are microMIPS code too. |
| |
| The trailing NOP is for alignment and correct disassembly only. */ |
| static const bfd_vma micromips_o32_exec_plt0_entry[] = |
| { |
| 0x7980, 0x0000, /* addiupc $3, (&GOTPLT[0]) - . */ |
| 0xff23, 0x0000, /* lw $25, 0($3) */ |
| 0x0535, /* subu $2, $2, $3 */ |
| 0x2525, /* srl $2, $2, 2 */ |
| 0x3302, 0xfffe, /* subu $24, $2, 2 */ |
| 0x0dff, /* move $15, $31 */ |
| 0x45f9, /* jalrs $25 */ |
| 0x0f83, /* move $28, $3 */ |
| 0x0c00 /* nop */ |
| }; |
| |
| /* The format of the microMIPS first PLT entry in an O32 executable |
| in the insn32 mode. */ |
| static const bfd_vma micromips_insn32_o32_exec_plt0_entry[] = |
| { |
| 0x41bc, 0x0000, /* lui $28, %hi(&GOTPLT[0]) */ |
| 0xff3c, 0x0000, /* lw $25, %lo(&GOTPLT[0])($28) */ |
| 0x339c, 0x0000, /* addiu $28, $28, %lo(&GOTPLT[0]) */ |
| 0x0398, 0xc1d0, /* subu $24, $24, $28 */ |
| 0x001f, 0x7a90, /* or $15, $31, zero */ |
| 0x0318, 0x1040, /* srl $24, $24, 2 */ |
| 0x03f9, 0x0f3c, /* jalr $25 */ |
| 0x3318, 0xfffe /* subu $24, $24, 2 */ |
| }; |
| |
| /* The format of subsequent standard PLT entries. */ |
| static const bfd_vma mips_exec_plt_entry[] = |
| { |
| 0x3c0f0000, /* lui $15, %hi(.got.plt entry) */ |
| 0x01f90000, /* l[wd] $25, %lo(.got.plt entry)($15) */ |
| 0x25f80000, /* addiu $24, $15, %lo(.got.plt entry) */ |
| 0x03200008 /* jr $25 */ |
| }; |
| |
| static const bfd_vma mipsr6_exec_plt_entry[] = |
| { |
| 0x3c0f0000, /* lui $15, %hi(.got.plt entry) */ |
| 0x01f90000, /* l[wd] $25, %lo(.got.plt entry)($15) */ |
| 0x25f80000, /* addiu $24, $15, %lo(.got.plt entry) */ |
| 0x03200009 /* jr $25 */ |
| }; |
| |
| static const bfd_vma mipsr6_exec_plt_entry_compact[] = |
| { |
| 0x3c0f0000, /* lui $15, %hi(.got.plt entry) */ |
| 0x01f90000, /* l[wd] $25, %lo(.got.plt entry)($15) */ |
| 0x25f80000, /* addiu $24, $15, %lo(.got.plt entry) */ |
| 0xd8190000 /* jic $25, 0 */ |
| }; |
| |
| /* The format of subsequent MIPS16 o32 PLT entries. We use v0 ($2) |
| and v1 ($3) as temporaries because t8 ($24) and t9 ($25) are not |
| directly addressable. */ |
| static const bfd_vma mips16_o32_exec_plt_entry[] = |
| { |
| 0xb203, /* lw $2, 12($pc) */ |
| 0x9a60, /* lw $3, 0($2) */ |
| 0x651a, /* move $24, $2 */ |
| 0xeb00, /* jr $3 */ |
| 0x653b, /* move $25, $3 */ |
| 0x6500, /* nop */ |
| 0x0000, 0x0000 /* .word (.got.plt entry) */ |
| }; |
| |
| /* The format of subsequent microMIPS o32 PLT entries. We use v0 ($2) |
| as a temporary because t8 ($24) is not addressable with ADDIUPC. */ |
| static const bfd_vma micromips_o32_exec_plt_entry[] = |
| { |
| 0x7900, 0x0000, /* addiupc $2, (.got.plt entry) - . */ |
| 0xff22, 0x0000, /* lw $25, 0($2) */ |
| 0x4599, /* jr $25 */ |
| 0x0f02 /* move $24, $2 */ |
| }; |
| |
| /* The format of subsequent microMIPS o32 PLT entries in the insn32 mode. */ |
| static const bfd_vma micromips_insn32_o32_exec_plt_entry[] = |
| { |
| 0x41af, 0x0000, /* lui $15, %hi(.got.plt entry) */ |
| 0xff2f, 0x0000, /* lw $25, %lo(.got.plt entry)($15) */ |
| 0x0019, 0x0f3c, /* jr $25 */ |
| 0x330f, 0x0000 /* addiu $24, $15, %lo(.got.plt entry) */ |
| }; |
| |
| /* The format of the first PLT entry in a VxWorks executable. */ |
| static const bfd_vma mips_vxworks_exec_plt0_entry[] = |
| { |
| 0x3c190000, /* lui t9, %hi(_GLOBAL_OFFSET_TABLE_) */ |
| 0x27390000, /* addiu t9, t9, %lo(_GLOBAL_OFFSET_TABLE_) */ |
| 0x8f390008, /* lw t9, 8(t9) */ |
| 0x00000000, /* nop */ |
| 0x03200008, /* jr t9 */ |
| 0x00000000 /* nop */ |
| }; |
| |
| /* The format of subsequent PLT entries. */ |
| static const bfd_vma mips_vxworks_exec_plt_entry[] = |
| { |
| 0x10000000, /* b .PLT_resolver */ |
| 0x24180000, /* li t8, <pltindex> */ |
| 0x3c190000, /* lui t9, %hi(<.got.plt slot>) */ |
| 0x27390000, /* addiu t9, t9, %lo(<.got.plt slot>) */ |
| 0x8f390000, /* lw t9, 0(t9) */ |
| 0x00000000, /* nop */ |
| 0x03200008, /* jr t9 */ |
| 0x00000000 /* nop */ |
| }; |
| |
| /* The format of the first PLT entry in a VxWorks shared object. */ |
| static const bfd_vma mips_vxworks_shared_plt0_entry[] = |
| { |
| 0x8f990008, /* lw t9, 8(gp) */ |
| 0x00000000, /* nop */ |
| 0x03200008, /* jr t9 */ |
| 0x00000000, /* nop */ |
| 0x00000000, /* nop */ |
| 0x00000000 /* nop */ |
| }; |
| |
| /* The format of subsequent PLT entries. */ |
| static const bfd_vma mips_vxworks_shared_plt_entry[] = |
| { |
| 0x10000000, /* b .PLT_resolver */ |
| 0x24180000 /* li t8, <pltindex> */ |
| }; |
| |
| /* microMIPS 32-bit opcode helper installer. */ |
| |
| static void |
| bfd_put_micromips_32 (const bfd *abfd, bfd_vma opcode, bfd_byte *ptr) |
| { |
| bfd_put_16 (abfd, (opcode >> 16) & 0xffff, ptr); |
| bfd_put_16 (abfd, opcode & 0xffff, ptr + 2); |
| } |
| |
| /* microMIPS 32-bit opcode helper retriever. */ |
| |
| static bfd_vma |
| bfd_get_micromips_32 (const bfd *abfd, const bfd_byte *ptr) |
| { |
| return (bfd_get_16 (abfd, ptr) << 16) | bfd_get_16 (abfd, ptr + 2); |
| } |
| |
| /* Look up an entry in a MIPS ELF linker hash table. */ |
| |
| #define mips_elf_link_hash_lookup(table, string, create, copy, follow) \ |
| ((struct mips_elf_link_hash_entry *) \ |
| elf_link_hash_lookup (&(table)->root, (string), (create), \ |
| (copy), (follow))) |
| |
| /* Traverse a MIPS ELF linker hash table. */ |
| |
| #define mips_elf_link_hash_traverse(table, func, info) \ |
| (elf_link_hash_traverse \ |
| (&(table)->root, \ |
| (bool (*) (struct elf_link_hash_entry *, void *)) (func), \ |
| (info))) |
| |
| /* Find the base offsets for thread-local storage in this object, |
| for GD/LD and IE/LE respectively. */ |
| |
| #define TP_OFFSET 0x7000 |
| #define DTP_OFFSET 0x8000 |
| |
| static bfd_vma |
| dtprel_base (struct bfd_link_info *info) |
| { |
| /* If tls_sec is NULL, we should have signalled an error already. */ |
| if (elf_hash_table (info)->tls_sec == NULL) |
| return 0; |
| return elf_hash_table (info)->tls_sec->vma + DTP_OFFSET; |
| } |
| |
| static bfd_vma |
| tprel_base (struct bfd_link_info *info) |
| { |
| /* If tls_sec is NULL, we should have signalled an error already. */ |
| if (elf_hash_table (info)->tls_sec == NULL) |
| return 0; |
| return elf_hash_table (info)->tls_sec->vma + TP_OFFSET; |
| } |
| |
| /* Create an entry in a MIPS ELF linker hash table. */ |
| |
| static struct bfd_hash_entry * |
| mips_elf_link_hash_newfunc (struct bfd_hash_entry *entry, |
| struct bfd_hash_table *table, const char *string) |
| { |
| struct mips_elf_link_hash_entry *ret = |
| (struct mips_elf_link_hash_entry *) entry; |
| |
| /* Allocate the structure if it has not already been allocated by a |
| subclass. */ |
| if (ret == NULL) |
| ret = bfd_hash_allocate (table, sizeof (struct mips_elf_link_hash_entry)); |
| if (ret == NULL) |
| return (struct bfd_hash_entry *) ret; |
| |
| /* Call the allocation method of the superclass. */ |
| ret = ((struct mips_elf_link_hash_entry *) |
| _bfd_elf_link_hash_newfunc ((struct bfd_hash_entry *) ret, |
| table, string)); |
| if (ret != NULL) |
| { |
| /* Set local fields. */ |
| memset (&ret->esym, 0, sizeof (EXTR)); |
| /* We use -2 as a marker to indicate that the information has |
| not been set. -1 means there is no associated ifd. */ |
| ret->esym.ifd = -2; |
| ret->la25_stub = 0; |
| ret->possibly_dynamic_relocs = 0; |
| ret->fn_stub = NULL; |
| ret->call_stub = NULL; |
| ret->call_fp_stub = NULL; |
| ret->mipsxhash_loc = 0; |
| ret->global_got_area = GGA_NONE; |
| ret->got_only_for_calls = true; |
| ret->readonly_reloc = false; |
| ret->has_static_relocs = false; |
| ret->no_fn_stub = false; |
| ret->need_fn_stub = false; |
| ret->has_nonpic_branches = false; |
| ret->needs_lazy_stub = false; |
| ret->use_plt_entry = false; |
| } |
| |
| return (struct bfd_hash_entry *) ret; |
| } |
| |
| /* Allocate MIPS ELF private object data. */ |
| |
| bool |
| _bfd_mips_elf_mkobject (bfd *abfd) |
| { |
| return bfd_elf_allocate_object (abfd, sizeof (struct mips_elf_obj_tdata), |
| MIPS_ELF_DATA); |
| } |
| |
| /* MIPS ELF uses a special find_nearest_line routine in order the |
| handle the ECOFF debugging information. */ |
| |
| struct mips_elf_find_line |
| { |
| struct ecoff_debug_info d; |
| struct ecoff_find_line i; |
| }; |
| |
| bool |
| _bfd_mips_elf_free_cached_info (bfd *abfd) |
| { |
| struct mips_elf_obj_tdata *tdata; |
| |
| if ((bfd_get_format (abfd) == bfd_object |
| || bfd_get_format (abfd) == bfd_core) |
| && (tdata = mips_elf_tdata (abfd)) != NULL) |
| { |
| BFD_ASSERT (tdata->root.object_id == MIPS_ELF_DATA); |
| while (tdata->mips_hi16_list != NULL) |
| { |
| struct mips_hi16 *hi = tdata->mips_hi16_list; |
| tdata->mips_hi16_list = hi->next; |
| free (hi); |
| } |
| if (tdata->find_line_info != NULL) |
| _bfd_ecoff_free_ecoff_debug_info (&tdata->find_line_info->d); |
| } |
| return _bfd_elf_free_cached_info (abfd); |
| } |
| |
| bool |
| _bfd_mips_elf_new_section_hook (bfd *abfd, asection *sec) |
| { |
| if (!sec->used_by_bfd) |
| { |
| struct _mips_elf_section_data *sdata; |
| size_t amt = sizeof (*sdata); |
| |
| sdata = bfd_zalloc (abfd, amt); |
| if (sdata == NULL) |
| return false; |
| sec->used_by_bfd = sdata; |
| } |
| |
| return _bfd_elf_new_section_hook (abfd, sec); |
| } |
| |
| /* Read ECOFF debugging information from a .mdebug section into a |
| ecoff_debug_info structure. */ |
| |
| bool |
| _bfd_mips_elf_read_ecoff_info (bfd *abfd, asection *section, |
| struct ecoff_debug_info *debug) |
| { |
| HDRR *symhdr; |
| const struct ecoff_debug_swap *swap; |
| char *ext_hdr; |
| |
| swap = get_elf_backend_data (abfd)->elf_backend_ecoff_debug_swap; |
| memset (debug, 0, sizeof (*debug)); |
| |
| ext_hdr = bfd_malloc (swap->external_hdr_size); |
| if (ext_hdr == NULL && swap->external_hdr_size != 0) |
| goto error_return; |
| |
| if (! bfd_get_section_contents (abfd, section, ext_hdr, 0, |
| swap->external_hdr_size)) |
| goto error_return; |
| |
| symhdr = &debug->symbolic_header; |
| (*swap->swap_hdr_in) (abfd, ext_hdr, symhdr); |
| free (ext_hdr); |
| ext_hdr = NULL; |
| |
| /* The symbolic header contains absolute file offsets and sizes to |
| read. */ |
| #define READ(ptr, offset, count, size) \ |
| do \ |
| { \ |
| size_t amt; \ |
| debug->ptr = NULL; \ |
| if (symhdr->count == 0) \ |
| break; \ |
| if (_bfd_mul_overflow (size, symhdr->count, &amt)) \ |
| { \ |
| bfd_set_error (bfd_error_file_too_big); \ |
| goto error_return; \ |
| } \ |
| if (bfd_seek (abfd, symhdr->offset, SEEK_SET) != 0) \ |
| goto error_return; \ |
| debug->ptr = _bfd_malloc_and_read (abfd, amt + 1, amt); \ |
| if (debug->ptr == NULL) \ |
| goto error_return; \ |
| ((char *) debug->ptr)[amt] = 0; \ |
| } while (0) |
| |
| READ (line, cbLineOffset, cbLine, sizeof (unsigned char)); |
| READ (external_dnr, cbDnOffset, idnMax, swap->external_dnr_size); |
| READ (external_pdr, cbPdOffset, ipdMax, swap->external_pdr_size); |
| READ (external_sym, cbSymOffset, isymMax, swap->external_sym_size); |
| READ (external_opt, cbOptOffset, ioptMax, swap->external_opt_size); |
| READ (external_aux, cbAuxOffset, iauxMax, sizeof (union aux_ext)); |
| READ (ss, cbSsOffset, issMax, sizeof (char)); |
| READ (ssext, cbSsExtOffset, issExtMax, sizeof (char)); |
| READ (external_fdr, cbFdOffset, ifdMax, swap->external_fdr_size); |
| READ (external_rfd, cbRfdOffset, crfd, swap->external_rfd_size); |
| READ (external_ext, cbExtOffset, iextMax, swap->external_ext_size); |
| #undef READ |
| |
| return true; |
| |
| error_return: |
| free (ext_hdr); |
| _bfd_ecoff_free_ecoff_debug_info (debug); |
| return false; |
| } |
| |
| /* Swap RPDR (runtime procedure table entry) for output. */ |
| |
| static void |
| ecoff_swap_rpdr_out (bfd *abfd, const RPDR *in, struct rpdr_ext *ex) |
| { |
| H_PUT_S32 (abfd, in->adr, ex->p_adr); |
| H_PUT_32 (abfd, in->regmask, ex->p_regmask); |
| H_PUT_32 (abfd, in->regoffset, ex->p_regoffset); |
| H_PUT_32 (abfd, in->fregmask, ex->p_fregmask); |
| H_PUT_32 (abfd, in->fregoffset, ex->p_fregoffset); |
| H_PUT_32 (abfd, in->frameoffset, ex->p_frameoffset); |
| |
| H_PUT_16 (abfd, in->framereg, ex->p_framereg); |
| H_PUT_16 (abfd, in->pcreg, ex->p_pcreg); |
| |
| H_PUT_32 (abfd, in->irpss, ex->p_irpss); |
| } |
| |
| /* Create a runtime procedure table from the .mdebug section. */ |
| |
| static bool |
| mips_elf_create_procedure_table (void *handle, bfd *abfd, |
| struct bfd_link_info *info, asection *s, |
| struct ecoff_debug_info *debug) |
| { |
| const struct ecoff_debug_swap *swap; |
| HDRR *hdr = &debug->symbolic_header; |
| RPDR *rpdr, *rp; |
| struct rpdr_ext *erp; |
| void *rtproc; |
| struct pdr_ext *epdr; |
| struct sym_ext *esym; |
| char *ss, **sv; |
| char *str; |
| bfd_size_type size; |
| bfd_size_type count; |
| unsigned long sindex; |
| unsigned long i; |
| PDR pdr; |
| SYMR sym; |
| const char *no_name_func = _("static procedure (no name)"); |
| |
| epdr = NULL; |
| rpdr = NULL; |
| esym = NULL; |
| ss = NULL; |
| sv = NULL; |
| |
| swap = get_elf_backend_data (abfd)->elf_backend_ecoff_debug_swap; |
| |
| sindex = strlen (no_name_func) + 1; |
| count = hdr->ipdMax; |
| if (count > 0) |
| { |
| size = swap->external_pdr_size; |
| |
| epdr = bfd_malloc (size * count); |
| if (epdr == NULL) |
| goto error_return; |
| |
| if (! _bfd_ecoff_get_accumulated_pdr (handle, (bfd_byte *) epdr)) |
| goto error_return; |
| |
| size = sizeof (RPDR); |
| rp = rpdr = bfd_malloc (size * count); |
| if (rpdr == NULL) |
| goto error_return; |
| |
| size = sizeof (char *); |
| sv = bfd_malloc (size * count); |
| if (sv == NULL) |
| goto error_return; |
| |
| count = hdr->isymMax; |
| size = swap->external_sym_size; |
| esym = bfd_malloc (size * count); |
| if (esym == NULL) |
| goto error_return; |
| |
| if (! _bfd_ecoff_get_accumulated_sym (handle, (bfd_byte *) esym)) |
| goto error_return; |
| |
| count = hdr->issMax; |
| ss = bfd_malloc (count); |
| if (ss == NULL) |
| goto error_return; |
| if (! _bfd_ecoff_get_accumulated_ss (handle, (bfd_byte *) ss)) |
| goto error_return; |
| |
| count = hdr->ipdMax; |
| for (i = 0; i < (unsigned long) count; i++, rp++) |
| { |
| (*swap->swap_pdr_in) (abfd, epdr + i, &pdr); |
| (*swap->swap_sym_in) (abfd, &esym[pdr.isym], &sym); |
| rp->adr = sym.value; |
| rp->regmask = pdr.regmask; |
| rp->regoffset = pdr.regoffset; |
| rp->fregmask = pdr.fregmask; |
| rp->fregoffset = pdr.fregoffset; |
| rp->frameoffset = pdr.frameoffset; |
| rp->framereg = pdr.framereg; |
| rp->pcreg = pdr.pcreg; |
| rp->irpss = sindex; |
| sv[i] = ss + sym.iss; |
| sindex += strlen (sv[i]) + 1; |
| } |
| } |
| |
| size = sizeof (struct rpdr_ext) * (count + 2) + sindex; |
| size = BFD_ALIGN (size, 16); |
| rtproc = bfd_alloc (abfd, size); |
| if (rtproc == NULL) |
| { |
| mips_elf_hash_table (info)->procedure_count = 0; |
| goto error_return; |
| } |
| |
| mips_elf_hash_table (info)->procedure_count = count + 2; |
| |
| erp = rtproc; |
| memset (erp, 0, sizeof (struct rpdr_ext)); |
| erp++; |
| str = (char *) rtproc + sizeof (struct rpdr_ext) * (count + 2); |
| strcpy (str, no_name_func); |
| str += strlen (no_name_func) + 1; |
| for (i = 0; i < count; i++) |
| { |
| ecoff_swap_rpdr_out (abfd, rpdr + i, erp + i); |
| strcpy (str, sv[i]); |
| str += strlen (sv[i]) + 1; |
| } |
| H_PUT_S32 (abfd, -1, (erp + count)->p_adr); |
| |
| /* Set the size and contents of .rtproc section. */ |
| s->size = size; |
| s->contents = rtproc; |
| |
| /* Skip this section later on (I don't think this currently |
| matters, but someday it might). */ |
| s->map_head.link_order = NULL; |
| |
| free (epdr); |
| free (rpdr); |
| free (esym); |
| free (ss); |
| free (sv); |
| return true; |
| |
| error_return: |
| free (epdr); |
| free (rpdr); |
| free (esym); |
| free (ss); |
| free (sv); |
| return false; |
| } |
| |
| /* We're going to create a stub for H. Create a symbol for the stub's |
| value and size, to help make the disassembly easier to read. */ |
| |
| static bool |
| mips_elf_create_stub_symbol (struct bfd_link_info *info, |
| struct mips_elf_link_hash_entry *h, |
| const char *prefix, asection *s, bfd_vma value, |
| bfd_vma size) |
| { |
| bool micromips_p = ELF_ST_IS_MICROMIPS (h->root.other); |
| struct bfd_link_hash_entry *bh; |
| struct elf_link_hash_entry *elfh; |
| char *name; |
| bool res; |
| |
| if (micromips_p) |
| value |= 1; |
| |
| /* Create a new symbol. */ |
| name = concat (prefix, h->root.root.root.string, NULL); |
| bh = NULL; |
| res = _bfd_generic_link_add_one_symbol (info, s->owner, name, |
| BSF_LOCAL, s, value, NULL, |
| true, false, &bh); |
| free (name); |
| if (! res) |
| return false; |
| |
| /* Make it a local function. */ |
| elfh = (struct elf_link_hash_entry *) bh; |
| elfh->type = ELF_ST_INFO (STB_LOCAL, STT_FUNC); |
| elfh->size = size; |
| elfh->forced_local = 1; |
| if (micromips_p) |
| elfh->other = ELF_ST_SET_MICROMIPS (elfh->other); |
| return true; |
| } |
| |
| /* We're about to redefine H. Create a symbol to represent H's |
| current value and size, to help make the disassembly easier |
| to read. */ |
| |
| static bool |
| mips_elf_create_shadow_symbol (struct bfd_link_info *info, |
| struct mips_elf_link_hash_entry *h, |
| const char *prefix) |
| { |
| struct bfd_link_hash_entry *bh; |
| struct elf_link_hash_entry *elfh; |
| char *name; |
| asection *s; |
| bfd_vma value; |
| bool res; |
| |
| /* Read the symbol's value. */ |
| BFD_ASSERT (h->root.root.type == bfd_link_hash_defined |
| || h->root.root.type == bfd_link_hash_defweak); |
| s = h->root.root.u.def.section; |
| value = h->root.root.u.def.value; |
| |
| /* Create a new symbol. */ |
| name = concat (prefix, h->root.root.root.string, NULL); |
| bh = NULL; |
| res = _bfd_generic_link_add_one_symbol (info, s->owner, name, |
| BSF_LOCAL, s, value, NULL, |
| true, false, &bh); |
| free (name); |
| if (! res) |
| return false; |
| |
| /* Make it local and copy the other attributes from H. */ |
| elfh = (struct elf_link_hash_entry *) bh; |
| elfh->type = ELF_ST_INFO (STB_LOCAL, ELF_ST_TYPE (h->root.type)); |
| elfh->other = h->root.other; |
| elfh->size = h->root.size; |
| elfh->forced_local = 1; |
| return true; |
| } |
| |
| /* Return TRUE if relocations in SECTION can refer directly to a MIPS16 |
| function rather than to a hard-float stub. */ |
| |
| static bool |
| section_allows_mips16_refs_p (asection *section) |
| { |
| const char *name; |
| |
| name = bfd_section_name (section); |
| return (FN_STUB_P (name) |
| || CALL_STUB_P (name) |
| || CALL_FP_STUB_P (name) |
| || strcmp (name, ".pdr") == 0); |
| } |
| |
| /* [RELOCS, RELEND) are the relocations against SEC, which is a MIPS16 |
| stub section of some kind. Return the R_SYMNDX of the target |
| function, or 0 if we can't decide which function that is. */ |
| |
| static unsigned long |
| mips16_stub_symndx (const struct elf_backend_data *bed, |
| asection *sec ATTRIBUTE_UNUSED, |
| const Elf_Internal_Rela *relocs, |
| const Elf_Internal_Rela *relend) |
| { |
| int int_rels_per_ext_rel = bed->s->int_rels_per_ext_rel; |
| const Elf_Internal_Rela *rel; |
| |
| /* Trust the first R_MIPS_NONE relocation, if any, but not a subsequent |
| one in a compound relocation. */ |
| for (rel = relocs; rel < relend; rel += int_rels_per_ext_rel) |
| if (ELF_R_TYPE (sec->owner, rel->r_info) == R_MIPS_NONE) |
| return ELF_R_SYM (sec->owner, rel->r_info); |
| |
| /* Otherwise trust the first relocation, whatever its kind. This is |
| the traditional behavior. */ |
| if (relocs < relend) |
| return ELF_R_SYM (sec->owner, relocs->r_info); |
| |
| return 0; |
| } |
| |
| /* Check the mips16 stubs for a particular symbol, and see if we can |
| discard them. */ |
| |
| static void |
| mips_elf_check_mips16_stubs (struct bfd_link_info *info, |
| struct mips_elf_link_hash_entry *h) |
| { |
| /* Dynamic symbols must use the standard call interface, in case other |
| objects try to call them. */ |
| if (h->fn_stub != NULL |
| && h->root.dynindx != -1) |
| { |
| mips_elf_create_shadow_symbol (info, h, ".mips16."); |
| h->need_fn_stub = true; |
| } |
| |
| if (h->fn_stub != NULL |
| && ! h->need_fn_stub) |
| { |
| /* We don't need the fn_stub; the only references to this symbol |
| are 16 bit calls. Clobber the size to 0 to prevent it from |
| being included in the link. */ |
| h->fn_stub->size = 0; |
| h->fn_stub->flags &= ~SEC_RELOC; |
| h->fn_stub->reloc_count = 0; |
| h->fn_stub->flags |= SEC_EXCLUDE; |
| h->fn_stub->output_section = bfd_abs_section_ptr; |
| } |
| |
| if (h->call_stub != NULL |
| && ELF_ST_IS_MIPS16 (h->root.other)) |
| { |
| /* We don't need the call_stub; this is a 16 bit function, so |
| calls from other 16 bit functions are OK. Clobber the size |
| to 0 to prevent it from being included in the link. */ |
| h->call_stub->size = 0; |
| h->call_stub->flags &= ~SEC_RELOC; |
| h->call_stub->reloc_count = 0; |
| h->call_stub->flags |= SEC_EXCLUDE; |
| h->call_stub->output_section = bfd_abs_section_ptr; |
| } |
| |
| if (h->call_fp_stub != NULL |
| && ELF_ST_IS_MIPS16 (h->root.other)) |
| { |
| /* We don't need the call_stub; this is a 16 bit function, so |
| calls from other 16 bit functions are OK. Clobber the size |
| to 0 to prevent it from being included in the link. */ |
| h->call_fp_stub->size = 0; |
| h->call_fp_stub->flags &= ~SEC_RELOC; |
| h->call_fp_stub->reloc_count = 0; |
| h->call_fp_stub->flags |= SEC_EXCLUDE; |
| h->call_fp_stub->output_section = bfd_abs_section_ptr; |
| } |
| } |
| |
| /* Hashtable callbacks for mips_elf_la25_stubs. */ |
| |
| static hashval_t |
| mips_elf_la25_stub_hash (const void *entry_) |
| { |
| const struct mips_elf_la25_stub *entry; |
| |
| entry = (struct mips_elf_la25_stub *) entry_; |
| return entry->h->root.root.u.def.section->id |
| + entry->h->root.root.u.def.value; |
| } |
| |
| static int |
| mips_elf_la25_stub_eq (const void *entry1_, const void *entry2_) |
| { |
| const struct mips_elf_la25_stub *entry1, *entry2; |
| |
| entry1 = (struct mips_elf_la25_stub *) entry1_; |
| entry2 = (struct mips_elf_la25_stub *) entry2_; |
| return ((entry1->h->root.root.u.def.section |
| == entry2->h->root.root.u.def.section) |
| && (entry1->h->root.root.u.def.value |
| == entry2->h->root.root.u.def.value)); |
| } |
| |
| /* Called by the linker to set up the la25 stub-creation code. FN is |
| the linker's implementation of add_stub_function. Return true on |
| success. */ |
| |
| bool |
| _bfd_mips_elf_init_stubs (struct bfd_link_info *info, |
| asection *(*fn) (const char *, asection *, |
| asection *)) |
| { |
| struct mips_elf_link_hash_table *htab; |
| |
| htab = mips_elf_hash_table (info); |
| if (htab == NULL) |
| return false; |
| |
| htab->add_stub_section = fn; |
| htab->la25_stubs = htab_try_create (1, mips_elf_la25_stub_hash, |
| mips_elf_la25_stub_eq, NULL); |
| if (htab->la25_stubs == NULL) |
| return false; |
| |
| return true; |
| } |
| |
| /* Return true if H is a locally-defined PIC function, in the sense |
| that it or its fn_stub might need $25 to be valid on entry. |
| Note that MIPS16 functions set up $gp using PC-relative instructions, |
| so they themselves never need $25 to be valid. Only non-MIPS16 |
| entry points are of interest here. */ |
| |
| static bool |
| mips_elf_local_pic_function_p (struct mips_elf_link_hash_entry *h) |
| { |
| return ((h->root.root.type == bfd_link_hash_defined |
| || h->root.root.type == bfd_link_hash_defweak) |
| && h->root.def_regular |
| && !bfd_is_abs_section (h->root.root.u.def.section) |
| && !bfd_is_und_section (h->root.root.u.def.section) |
| && (!ELF_ST_IS_MIPS16 (h->root.other) |
| || (h->fn_stub && h->need_fn_stub)) |
| && (PIC_OBJECT_P (h->root.root.u.def.section->owner) |
| || ELF_ST_IS_MIPS_PIC (h->root.other))); |
| } |
| |
| /* Set *SEC to the input section that contains the target of STUB. |
| Return the offset of the target from the start of that section. */ |
| |
| static bfd_vma |
| mips_elf_get_la25_target (struct mips_elf_la25_stub *stub, |
| asection **sec) |
| { |
| if (ELF_ST_IS_MIPS16 (stub->h->root.other)) |
| { |
| BFD_ASSERT (stub->h->need_fn_stub); |
| *sec = stub->h->fn_stub; |
| return 0; |
| } |
| else |
| { |
| *sec = stub->h->root.root.u.def.section; |
| return stub->h->root.root.u.def.value; |
| } |
| } |
| |
| /* STUB describes an la25 stub that we have decided to implement |
| by inserting an LUI/ADDIU pair before the target function. |
| Create the section and redirect the function symbol to it. */ |
| |
| static bool |
| mips_elf_add_la25_intro (struct mips_elf_la25_stub *stub, |
| struct bfd_link_info *info) |
| { |
| struct mips_elf_link_hash_table *htab; |
| char *name; |
| asection *s, *input_section; |
| unsigned int align; |
| |
| htab = mips_elf_hash_table (info); |
| if (htab == NULL) |
| return false; |
| |
| /* Create a unique name for the new section. */ |
| name = bfd_malloc (11 + sizeof (".text.stub.")); |
| if (name == NULL) |
| return false; |
| sprintf (name, ".text.stub.%d", (int) htab_elements (htab->la25_stubs)); |
| |
| /* Create the section. */ |
| mips_elf_get_la25_target (stub, &input_section); |
| s = htab->add_stub_section (name, input_section, |
| input_section->output_section); |
| if (s == NULL) |
| return false; |
| |
| /* Make sure that any padding goes before the stub. */ |
| align = input_section->alignment_power; |
| if (!bfd_set_section_alignment (s, align)) |
| return false; |
| if (align > 3) |
| s->size = (1 << align) - 8; |
| |
| /* Create a symbol for the stub. */ |
| mips_elf_create_stub_symbol (info, stub->h, ".pic.", s, s->size, 8); |
| stub->stub_section = s; |
| stub->offset = s->size; |
| |
| /* Allocate room for it. */ |
| s->size += 8; |
| return true; |
| } |
| |
| /* STUB describes an la25 stub that we have decided to implement |
| with a separate trampoline. Allocate room for it and redirect |
| the function symbol to it. */ |
| |
| static bool |
| mips_elf_add_la25_trampoline (struct mips_elf_la25_stub *stub, |
| struct bfd_link_info *info) |
| { |
| struct mips_elf_link_hash_table *htab; |
| asection *s; |
| |
| htab = mips_elf_hash_table (info); |
| if (htab == NULL) |
| return false; |
| |
| /* Create a trampoline section, if we haven't already. */ |
| s = htab->strampoline; |
| if (s == NULL) |
| { |
| asection *input_section = stub->h->root.root.u.def.section; |
| s = htab->add_stub_section (".text", NULL, |
| input_section->output_section); |
| if (s == NULL || !bfd_set_section_alignment (s, 4)) |
| return false; |
| htab->strampoline = s; |
| } |
| |
| /* Create a symbol for the stub. */ |
| mips_elf_create_stub_symbol (info, stub->h, ".pic.", s, s->size, 16); |
| stub->stub_section = s; |
| stub->offset = s->size; |
| |
| /* Allocate room for it. */ |
| s->size += 16; |
| return true; |
| } |
| |
| /* H describes a symbol that needs an la25 stub. Make sure that an |
| appropriate stub exists and point H at it. */ |
| |
| static bool |
| mips_elf_add_la25_stub (struct bfd_link_info *info, |
| struct mips_elf_link_hash_entry *h) |
| { |
| struct mips_elf_link_hash_table *htab; |
| struct mips_elf_la25_stub search, *stub; |
| bool use_trampoline_p; |
| asection *s; |
| bfd_vma value; |
| void **slot; |
| |
| /* Describe the stub we want. */ |
| search.stub_section = NULL; |
| search.offset = 0; |
| search.h = h; |
| |
| /* See if we've already created an equivalent stub. */ |
| htab = mips_elf_hash_table (info); |
| if (htab == NULL) |
| return false; |
| |
| slot = htab_find_slot (htab->la25_stubs, &search, INSERT); |
| if (slot == NULL) |
| return false; |
| |
| stub = (struct mips_elf_la25_stub *) *slot; |
| if (stub != NULL) |
| { |
| /* We can reuse the existing stub. */ |
| h->la25_stub = stub; |
| return true; |
| } |
| |
| /* Create a permanent copy of ENTRY and add it to the hash table. */ |
| stub = bfd_malloc (sizeof (search)); |
| if (stub == NULL) |
| return false; |
| *stub = search; |
| *slot = stub; |
| |
| /* Prefer to use LUI/ADDIU stubs if the function is at the beginning |
| of the section and if we would need no more than 2 nops. */ |
| value = mips_elf_get_la25_target (stub, &s); |
| if (ELF_ST_IS_MICROMIPS (stub->h->root.other)) |
| value &= ~1; |
| use_trampoline_p = (value != 0 || s->alignment_power > 4); |
| |
| h->la25_stub = stub; |
| return (use_trampoline_p |
| ? mips_elf_add_la25_trampoline (stub, info) |
| : mips_elf_add_la25_intro (stub, info)); |
| } |
| |
| /* A mips_elf_link_hash_traverse callback that is called before sizing |
| sections. DATA points to a mips_htab_traverse_info structure. */ |
| |
| static bool |
| mips_elf_check_symbols (struct mips_elf_link_hash_entry *h, void *data) |
| { |
| struct mips_htab_traverse_info *hti; |
| |
| hti = (struct mips_htab_traverse_info *) data; |
| if (!bfd_link_relocatable (hti->info)) |
| mips_elf_check_mips16_stubs (hti->info, h); |
| |
| if (mips_elf_local_pic_function_p (h)) |
| { |
| /* PR 12845: If H is in a section that has been garbage |
| collected it will have its output section set to *ABS*. */ |
| if (bfd_is_abs_section (h->root.root.u.def.section->output_section)) |
| return true; |
| |
| /* H is a function that might need $25 to be valid on entry. |
| If we're creating a non-PIC relocatable object, mark H as |
| being PIC. If we're creating a non-relocatable object with |
| non-PIC branches and jumps to H, make sure that H has an la25 |
| stub. */ |
| if (bfd_link_relocatable (hti->info)) |
| { |
| if (!PIC_OBJECT_P (hti->output_bfd)) |
| h->root.other = ELF_ST_SET_MIPS_PIC (h->root.other); |
| } |
| else if (h->has_nonpic_branches && !mips_elf_add_la25_stub (hti->info, h)) |
| { |
| hti->error = true; |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /* R_MIPS16_26 is used for the mips16 jal and jalx instructions. |
| Most mips16 instructions are 16 bits, but these instructions |
| are 32 bits. |
| |
| The format of these instructions is: |
| |
| +--------------+--------------------------------+ |
| | JALX | X| Imm 20:16 | Imm 25:21 | |
| +--------------+--------------------------------+ |
| | Immediate 15:0 | |
| +-----------------------------------------------+ |
| |
| JALX is the 5-bit value 00011. X is 0 for jal, 1 for jalx. |
| Note that the immediate value in the first word is swapped. |
| |
| When producing a relocatable object file, R_MIPS16_26 is |
| handled mostly like R_MIPS_26. In particular, the addend is |
| stored as a straight 26-bit value in a 32-bit instruction. |
| (gas makes life simpler for itself by never adjusting a |
| R_MIPS16_26 reloc to be against a section, so the addend is |
| always zero). However, the 32 bit instruction is stored as 2 |
| 16-bit values, rather than a single 32-bit value. In a |
| big-endian file, the result is the same; in a little-endian |
| file, the two 16-bit halves of the 32 bit value are swapped. |
| This is so that a disassembler can recognize the jal |
| instruction. |
| |
| When doing a final link, R_MIPS16_26 is treated as a 32 bit |
| instruction stored as two 16-bit values. The addend A is the |
| contents of the targ26 field. The calculation is the same as |
| R_MIPS_26. When storing the calculated value, reorder the |
| immediate value as shown above, and don't forget to store the |
| value as two 16-bit values. |
| |
| To put it in MIPS ABI terms, the relocation field is T-targ26-16, |
| defined as |
| |
| big-endian: |
| +--------+----------------------+ |
| | | | |
| | | targ26-16 | |
| |31 26|25 0| |
| +--------+----------------------+ |
| |
| little-endian: |
| +----------+------+-------------+ |
| | | | | |
| | sub1 | | sub2 | |
| |0 9|10 15|16 31| |
| +----------+--------------------+ |
| where targ26-16 is sub1 followed by sub2 (i.e., the addend field A is |
| ((sub1 << 16) | sub2)). |
| |
| When producing a relocatable object file, the calculation is |
| (((A < 2) | ((P + 4) & 0xf0000000) + S) >> 2) |
| When producing a fully linked file, the calculation is |
| let R = (((A < 2) | ((P + 4) & 0xf0000000) + S) >> 2) |
| ((R & 0x1f0000) << 5) | ((R & 0x3e00000) >> 5) | (R & 0xffff) |
| |
| The table below lists the other MIPS16 instruction relocations. |
| Each one is calculated in the same way as the non-MIPS16 relocation |
| given on the right, but using the extended MIPS16 layout of 16-bit |
| immediate fields: |
| |
| R_MIPS16_GPREL R_MIPS_GPREL16 |
| R_MIPS16_GOT16 R_MIPS_GOT16 |
| R_MIPS16_CALL16 R_MIPS_CALL16 |
| R_MIPS16_HI16 R_MIPS_HI16 |
| R_MIPS16_LO16 R_MIPS_LO16 |
| |
| A typical instruction will have a format like this: |
| |
| +--------------+--------------------------------+ |
| | EXTEND | Imm 10:5 | Imm 15:11 | |
| +--------------+--------------------------------+ |
| | Major | rx | ry | Imm 4:0 | |
| +--------------+--------------------------------+ |
| |
| EXTEND is the five bit value 11110. Major is the instruction |
| opcode. |
| |
| All we need to do here is shuffle the bits appropriately. |
| As above, the two 16-bit halves must be swapped on a |
| little-endian system. |
| |
| Finally R_MIPS16_PC16_S1 corresponds to R_MIPS_PC16, however the |
| relocatable field is shifted by 1 rather than 2 and the same bit |
| shuffling is done as with the relocations above. */ |
| |
| static inline bool |
| mips16_reloc_p (int r_type) |
| { |
| switch (r_type) |
| { |
| case R_MIPS16_26: |
| case R_MIPS16_GPREL: |
| case R_MIPS16_GOT16: |
| case R_MIPS16_CALL16: |
| case R_MIPS16_HI16: |
| case R_MIPS16_LO16: |
| case R_MIPS16_TLS_GD: |
| case R_MIPS16_TLS_LDM: |
| case R_MIPS16_TLS_DTPREL_HI16: |
| case R_MIPS16_TLS_DTPREL_LO16: |
| case R_MIPS16_TLS_GOTTPREL: |
| case R_MIPS16_TLS_TPREL_HI16: |
| case R_MIPS16_TLS_TPREL_LO16: |
| case R_MIPS16_PC16_S1: |
| return true; |
| |
| default: |
| return false; |
| } |
| } |
| |
| /* Check if a microMIPS reloc. */ |
| |
| static inline bool |
| micromips_reloc_p (unsigned int r_type) |
| { |
| return r_type >= R_MICROMIPS_min && r_type < R_MICROMIPS_max; |
| } |
| |
| /* Similar to MIPS16, the two 16-bit halves in microMIPS must be swapped |
| on a little-endian system. This does not apply to R_MICROMIPS_PC7_S1, |
| R_MICROMIPS_PC10_S1 and R_MICROMIPS_GPREL7_S2 relocs that apply to |
| 16-bit instructions. */ |
| |
| static inline bool |
| micromips_reloc_shuffle_p (unsigned int r_type) |
| { |
| return (micromips_reloc_p (r_type) |
| && r_type != R_MICROMIPS_PC7_S1 |
| && r_type != R_MICROMIPS_PC10_S1 |
| && r_type != R_MICROMIPS_GPREL7_S2); |
| } |
| |
| static inline bool |
| got16_reloc_p (int r_type) |
| { |
| return (r_type == R_MIPS_GOT16 |
| || r_type == R_MIPS16_GOT16 |
| || r_type == R_MICROMIPS_GOT16); |
| } |
| |
| static inline bool |
| call16_reloc_p (int r_type) |
| { |
| return (r_type == R_MIPS_CALL16 |
| || r_type == R_MIPS16_CALL16 |
| || r_type == R_MICROMIPS_CALL16); |
| } |
| |
| static inline bool |
| got_disp_reloc_p (unsigned int r_type) |
| { |
| return r_type == R_MIPS_GOT_DISP || r_type == R_MICROMIPS_GOT_DISP; |
| } |
| |
| static inline bool |
| got_page_reloc_p (unsigned int r_type) |
| { |
| return r_type == R_MIPS_GOT_PAGE || r_type == R_MICROMIPS_GOT_PAGE; |
| } |
| |
| static inline bool |
| got_lo16_reloc_p (unsigned int r_type) |
| { |
| return r_type == R_MIPS_GOT_LO16 || r_type == R_MICROMIPS_GOT_LO16; |
| } |
| |
| static inline bool |
| call_hi16_reloc_p (unsigned int r_type) |
| { |
| return r_type == R_MIPS_CALL_HI16 || r_type == R_MICROMIPS_CALL_HI16; |
| } |
| |
| static inline bool |
| call_lo16_reloc_p (unsigned int r_type) |
| { |
| return r_type == R_MIPS_CALL_LO16 || r_type == R_MICROMIPS_CALL_LO16; |
| } |
| |
| static inline bool |
| hi16_reloc_p (int r_type) |
| { |
| return (r_type == R_MIPS_HI16 |
| || r_type == R_MIPS16_HI16 |
| || r_type == R_MICROMIPS_HI16 |
| || r_type == R_MIPS_PCHI16); |
| } |
| |
| static inline bool |
| lo16_reloc_p (int r_type) |
| { |
| return (r_type == R_MIPS_LO16 |
| || r_type == R_MIPS16_LO16 |
| || r_type == R_MICROMIPS_LO16 |
| || r_type == R_MIPS_PCLO16); |
| } |
| |
| static inline bool |
| mips16_call_reloc_p (int r_type) |
| { |
| return r_type == R_MIPS16_26 || r_type == R_MIPS16_CALL16; |
| } |
| |
| static inline bool |
| jal_reloc_p (int r_type) |
| { |
| return (r_type == R_MIPS_26 |
| || r_type == R_MIPS16_26 |
| || r_type == R_MICROMIPS_26_S1); |
| } |
| |
| static inline bool |
| b_reloc_p (int r_type) |
| { |
| return (r_type == R_MIPS_PC26_S2 |
| || r_type == R_MIPS_PC21_S2 |
| || r_type == R_MIPS_PC16 |
| || r_type == R_MIPS_GNU_REL16_S2 |
| || r_type == R_MIPS16_PC16_S1 |
| || r_type == R_MICROMIPS_PC16_S1 |
| || r_type == R_MICROMIPS_PC10_S1 |
| || r_type == R_MICROMIPS_PC7_S1); |
| } |
| |
| static inline bool |
| aligned_pcrel_reloc_p (int r_type) |
| { |
| return (r_type == R_MIPS_PC18_S3 |
| || r_type == R_MIPS_PC19_S2); |
| } |
| |
| static inline bool |
| branch_reloc_p (int r_type) |
| { |
| return (r_type == R_MIPS_26 |
| || r_type == R_MIPS_PC26_S2 |
| || r_type == R_MIPS_PC21_S2 |
| || r_type == R_MIPS_PC16 |
| || r_type == R_MIPS_GNU_REL16_S2); |
| } |
| |
| static inline bool |
| mips16_branch_reloc_p (int r_type) |
| { |
| return (r_type == R_MIPS16_26 |
| || r_type == R_MIPS16_PC16_S1); |
| } |
| |
| static inline bool |
| micromips_branch_reloc_p (int r_type) |
| { |
| return (r_type == R_MICROMIPS_26_S1 |
| || r_type == R_MICROMIPS_PC16_S1 |
| || r_type == R_MICROMIPS_PC10_S1 |
| || r_type == R_MICROMIPS_PC7_S1); |
| } |
| |
| static inline bool |
| tls_gd_reloc_p (unsigned int r_type) |
| { |
| return (r_type == R_MIPS_TLS_GD |
| || r_type == R_MIPS16_TLS_GD |
| || r_type == R_MICROMIPS_TLS_GD); |
| } |
| |
| static inline bool |
| tls_ldm_reloc_p (unsigned int r_type) |
| { |
| return (r_type == R_MIPS_TLS_LDM |
| || r_type == R_MIPS16_TLS_LDM |
| || r_type == R_MICROMIPS_TLS_LDM); |
| } |
| |
| static inline bool |
| tls_gottprel_reloc_p (unsigned int r_type) |
| { |
| return (r_type == R_MIPS_TLS_GOTTPREL |
| || r_type == R_MIPS16_TLS_GOTTPREL |
| || r_type == R_MICROMIPS_TLS_GOTTPREL); |
| } |
| |
| static inline bool |
| needs_shuffle (int r_type) |
| { |
| return mips16_reloc_p (r_type) || micromips_reloc_shuffle_p (r_type); |
| } |
| |
| void |
| _bfd_mips_elf_reloc_unshuffle (bfd *abfd, int r_type, |
| bool jal_shuffle, bfd_byte *data) |
| { |
| bfd_vma first, second, val; |
| |
| if (!needs_shuffle (r_type)) |
| return; |
| |
| /* Pick up the first and second halfwords of the instruction. */ |
| first = bfd_get_16 (abfd, data); |
| second = bfd_get_16 (abfd, data + 2); |
| if (micromips_reloc_p (r_type) || (r_type == R_MIPS16_26 && !jal_shuffle)) |
| val = first << 16 | second; |
| else if (r_type != R_MIPS16_26) |
| val = (((first & 0xf800) << 16) | ((second & 0xffe0) << 11) |
| | ((first & 0x1f) << 11) | (first & 0x7e0) | (second & 0x1f)); |
| else |
| val = (((first & 0xfc00) << 16) | ((first & 0x3e0) << 11) |
| | ((first & 0x1f) << 21) | second); |
| bfd_put_32 (abfd, val, data); |
| } |
| |
| void |
| _bfd_mips_elf_reloc_shuffle (bfd *abfd, int r_type, |
| bool jal_shuffle, bfd_byte *data) |
| { |
| bfd_vma first, second, val; |
| |
| if (!needs_shuffle (r_type)) |
| return; |
| |
| val = bfd_get_32 (abfd, data); |
| if (micromips_reloc_p (r_type) || (r_type == R_MIPS16_26 && !jal_shuffle)) |
| { |
| second = val & 0xffff; |
| first = val >> 16; |
| } |
| else if (r_type != R_MIPS16_26) |
| { |
| second = ((val >> 11) & 0xffe0) | (val & 0x1f); |
| first = ((val >> 16) & 0xf800) | ((val >> 11) & 0x1f) | (val & 0x7e0); |
| } |
| else |
| { |
| second = val & 0xffff; |
| first = ((val >> 16) & 0xfc00) | ((val >> 11) & 0x3e0) |
| | ((val >> 21) & 0x1f); |
| } |
| bfd_put_16 (abfd, second, data + 2); |
| bfd_put_16 (abfd, first, data); |
| } |
| |
| /* Perform reloc offset checking. |
| We can only use bfd_reloc_offset_in_range, which takes into account |
| the size of the field being relocated, when section contents will |
| be accessed because mips object files may use relocations that seem |
| to access beyond section limits. |
| gas/testsuite/gas/mips/dla-reloc.s is an example that puts |
| R_MIPS_SUB, a 64-bit relocation, on the last instruction in the |
| section. The R_MIPS_SUB applies to the addend for the next reloc |
| rather than the section contents. |
| |
| CHECK is CHECK_STD for the standard bfd_reloc_offset_in_range check, |
| CHECK_INPLACE to only check partial_inplace relocs, and |
| CHECK_SHUFFLE to only check relocs that shuffle/unshuffle. */ |
| |
| bool |
| _bfd_mips_reloc_offset_in_range (bfd *abfd, asection *input_section, |
| arelent *reloc_entry, enum reloc_check check) |
| { |
| if (check == check_inplace && !reloc_entry->howto->partial_inplace) |
| return true; |
| if (check == check_shuffle && !needs_shuffle (reloc_entry->howto->type)) |
| return true; |
| return bfd_reloc_offset_in_range (reloc_entry->howto, abfd, |
| input_section, reloc_entry->address); |
| } |
| |
| bfd_reloc_status_type |
| _bfd_mips_elf_gprel16_with_gp (bfd *abfd, asymbol *symbol, |
| arelent *reloc_entry, asection *input_section, |
| bool relocatable, void *data, bfd_vma gp) |
| { |
| bfd_vma relocation; |
| bfd_signed_vma val; |
| bfd_reloc_status_type status; |
| |
| if (bfd_is_com_section (symbol->section)) |
| relocation = 0; |
| else |
| relocation = symbol->value; |
| |
| if (symbol->section->output_section != NULL) |
| { |
| relocation += symbol->section->output_section->vma; |
| relocation += symbol->section->output_offset; |
| } |
| |
| /* Set val to the offset into the section or symbol. */ |
| val = reloc_entry->addend; |
| |
| _bfd_mips_elf_sign_extend (val, 16); |
| |
| /* Adjust val for the final section location and GP value. If we |
| are producing relocatable output, we don't want to do this for |
| an external symbol. */ |
| if (! relocatable |
| || (symbol->flags & BSF_SECTION_SYM) != 0) |
| val += relocation - gp; |
| |
| if (reloc_entry->howto->partial_inplace) |
| { |
| if (!bfd_reloc_offset_in_range (reloc_entry->howto, abfd, input_section, |
| reloc_entry->address)) |
| return bfd_reloc_outofrange; |
| |
| status = _bfd_relocate_contents (reloc_entry->howto, abfd, val, |
| (bfd_byte *) data |
| + reloc_entry->address); |
| if (status != bfd_reloc_ok) |
| return status; |
| } |
| else |
| reloc_entry->addend = val; |
| |
| if (relocatable) |
| reloc_entry->address += input_section->output_offset; |
| |
| return bfd_reloc_ok; |
| } |
| |
| /* A howto special_function for REL *HI16 relocations. We can only |
| calculate the correct value once we've seen the partnering |
| *LO16 relocation, so just save the information for later. |
| |
| The ABI requires that the *LO16 immediately follow the *HI16. |
| However, as a GNU extension, we permit an arbitrary number of |
| *HI16s to be associated with a single *LO16. This significantly |
| simplies the relocation handling in gcc. */ |
| |
| bfd_reloc_status_type |
| _bfd_mips_elf_hi16_reloc (bfd *abfd, arelent *reloc_entry, |
| asymbol *symbol ATTRIBUTE_UNUSED, void *data, |
| asection *input_section, bfd *output_bfd, |
| char **error_message ATTRIBUTE_UNUSED) |
| { |
| struct mips_hi16 *n; |
| struct mips_elf_obj_tdata *tdata; |
| |
| if (reloc_entry->address > bfd_get_section_limit (abfd, input_section)) |
| return bfd_reloc_outofrange; |
| |
| n = bfd_malloc (sizeof *n); |
| if (n == NULL) |
| return bfd_reloc_outofrange; |
| |
| tdata = mips_elf_tdata (abfd); |
| n->next = tdata->mips_hi16_list; |
| n->data = data; |
| n->input_section = input_section; |
| n->rel = *reloc_entry; |
| tdata->mips_hi16_list = n; |
| |
| if (output_bfd != NULL) |
| reloc_entry->address += input_section->output_offset; |
| |
| return bfd_reloc_ok; |
| } |
| |
| /* A howto special_function for REL R_MIPS*_GOT16 relocations. This is just |
| like any other 16-bit relocation when applied to global symbols, but is |
| treated in the same as R_MIPS_HI16 when applied to local symbols. */ |
| |
| bfd_reloc_status_type |
| _bfd_mips_elf_got16_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol, |
| void *data, asection *input_section, |
| bfd *output_bfd, char **error_message) |
| { |
| if ((symbol->flags & (BSF_GLOBAL | BSF_WEAK)) != 0 |
| || bfd_is_und_section (bfd_asymbol_section (symbol)) |
| || bfd_is_com_section (bfd_asymbol_section (symbol))) |
| /* The relocation is against a global symbol. */ |
| return _bfd_mips_elf_generic_reloc (abfd, reloc_entry, symbol, data, |
| input_section, output_bfd, |
| error_message); |
| |
| return _bfd_mips_elf_hi16_reloc (abfd, reloc_entry, symbol, data, |
| input_section, output_bfd, error_message); |
| } |
| |
| /* A howto special_function for REL *LO16 relocations. The *LO16 itself |
| is a straightforward 16 bit inplace relocation, but we must deal with |
| any partnering high-part relocations as well. */ |
| |
| bfd_reloc_status_type |
| _bfd_mips_elf_lo16_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol, |
| void *data, asection *input_section, |
| bfd *output_bfd, char **error_message) |
| { |
| bfd_vma vallo; |
| bfd_byte *location = (bfd_byte *) data + reloc_entry->address; |
| struct mips_elf_obj_tdata *tdata; |
| |
| if (!bfd_reloc_offset_in_range (reloc_entry->howto, abfd, input_section, |
| reloc_entry->address)) |
| return bfd_reloc_outofrange; |
| |
| _bfd_mips_elf_reloc_unshuffle (abfd, reloc_entry->howto->type, false, |
| location); |
| /* The high 16 bits of the addend are stored in the high insn, the |
| low 16 bits in the low insn, but there is a catch: You can't |
| just concatenate the high and low parts. The high part of the |
| addend is adjusted for the fact that the low part is sign |
| extended. For example, an addend of 0x38000 would have 0x0004 in |
| the high part and 0x8000 (=0xff..f8000) in the low part. |
| To extract the actual addend, calculate (a) |
| ((hi & 0xffff) << 16) + ((lo & 0xffff) ^ 0x8000) - 0x8000. |
| We will be applying (symbol + addend) & 0xffff to the low insn, |
| and we want to apply (b) (symbol + addend + 0x8000) >> 16 to the |
| high insn (the +0x8000 adjusting for when the applied low part is |
| negative). Substituting (a) into (b) and recognising that |
| (hi & 0xffff) is already in the high insn gives a high part |
| addend adjustment of (lo & 0xffff) ^ 0x8000. */ |
| vallo = (bfd_get_32 (abfd, location) & 0xffff) ^ 0x8000; |
| _bfd_mips_elf_reloc_shuffle (abfd, reloc_entry->howto->type, false, |
| location); |
| |
| tdata = mips_elf_tdata (abfd); |
| while (tdata->mips_hi16_list != NULL) |
| { |
| bfd_reloc_status_type ret; |
| struct mips_hi16 *hi; |
| |
| hi = tdata->mips_hi16_list; |
| |
| /* R_MIPS*_GOT16 relocations are something of a special case. We |
| want to install the addend in the same way as for a R_MIPS*_HI16 |
| relocation (with a rightshift of 16). However, since GOT16 |
| relocations can also be used with global symbols, their howto |
| has a rightshift of 0. */ |
| if (hi->rel.howto->type == R_MIPS_GOT16) |
| hi->rel.howto = MIPS_ELF_RTYPE_TO_HOWTO (abfd, R_MIPS_HI16, false); |
| else if (hi->rel.howto->type == R_MIPS16_GOT16) |
| hi->rel.howto = MIPS_ELF_RTYPE_TO_HOWTO (abfd, R_MIPS16_HI16, false); |
| else if (hi->rel.howto->type == R_MICROMIPS_GOT16) |
| hi->rel.howto = MIPS_ELF_RTYPE_TO_HOWTO (abfd, R_MICROMIPS_HI16, false); |
| |
| hi->rel.addend += vallo; |
| |
| ret = _bfd_mips_elf_generic_reloc (abfd, &hi->rel, symbol, hi->data, |
| hi->input_section, output_bfd, |
| error_message); |
| if (ret != bfd_reloc_ok) |
| return ret; |
| |
| tdata->mips_hi16_list = hi->next; |
| free (hi); |
| } |
| |
| return _bfd_mips_elf_generic_reloc (abfd, reloc_entry, symbol, data, |
| input_section, output_bfd, |
| error_message); |
| } |
| |
| /* A generic howto special_function. This calculates and installs the |
| relocation itself, thus avoiding the oft-discussed problems in |
| bfd_perform_relocation and bfd_install_relocation. */ |
| |
| bfd_reloc_status_type |
| _bfd_mips_elf_generic_reloc (bfd *abfd ATTRIBUTE_UNUSED, arelent *reloc_entry, |
| asymbol *symbol, void *data ATTRIBUTE_UNUSED, |
| asection *input_section, bfd *output_bfd, |
| char **error_message ATTRIBUTE_UNUSED) |
| { |
| bfd_signed_vma val; |
| bfd_reloc_status_type status; |
| bool relocatable; |
| |
| relocatable = (output_bfd != NULL); |
| |
| if (!_bfd_mips_reloc_offset_in_range (abfd, input_section, reloc_entry, |
| (relocatable |
| ? check_inplace : check_std))) |
| return bfd_reloc_outofrange; |
| |
| /* Build up the field adjustment in VAL. */ |
| val = 0; |
| if ((!relocatable || (symbol->flags & BSF_SECTION_SYM) != 0) |
| && symbol->section->output_section != NULL) |
| { |
| /* Either we're calculating the final field value or we have a |
| relocation against a section symbol. Add in the section's |
| offset or address. */ |
| val += symbol->section->output_section->vma; |
| val += symbol->section->output_offset; |
| } |
| |
| if (!relocatable) |
| { |
| /* We're calculating the final field value. Add in the symbol's value |
| and, if pc-relative, subtract the address of the field itself. */ |
| val += symbol->value; |
| if (reloc_entry->howto->pc_relative) |
| { |
| val -= input_section->output_section->vma; |
| val -= input_section->output_offset; |
| val -= reloc_entry->address; |
| } |
| } |
| |
| /* VAL is now the final adjustment. If we're keeping this relocation |
| in the output file, and if the relocation uses a separate addend, |
| we just need to add VAL to that addend. Otherwise we need to add |
| VAL to the relocation field itself. */ |
| if (relocatable && !reloc_entry->howto->partial_inplace) |
| reloc_entry->addend += val; |
| else |
| { |
| bfd_byte *location = (bfd_byte *) data + reloc_entry->address; |
| |
| /* Add in the separate addend, if any. */ |
| val += reloc_entry->addend; |
| |
| /* Add VAL to the relocation field. */ |
| _bfd_mips_elf_reloc_unshuffle (abfd, reloc_entry->howto->type, false, |
| location); |
| status = _bfd_relocate_contents (reloc_entry->howto, abfd, val, |
| location); |
| _bfd_mips_elf_reloc_shuffle (abfd, reloc_entry->howto->type, false, |
| location); |
| |
| if (status != bfd_reloc_ok) |
| return status; |
| } |
| |
| if (relocatable) |
| reloc_entry->address += input_section->output_offset; |
| |
| return bfd_reloc_ok; |
| } |
| |
| /* Swap an entry in a .gptab section. Note that these routines rely |
| on the equivalence of the two elements of the union. */ |
| |
| static void |
| bfd_mips_elf32_swap_gptab_in (bfd *abfd, const Elf32_External_gptab *ex, |
| Elf32_gptab *in) |
| { |
| in->gt_entry.gt_g_value = H_GET_32 (abfd, ex->gt_entry.gt_g_value); |
| in->gt_entry.gt_bytes = H_GET_32 (abfd, ex->gt_entry.gt_bytes); |
| } |
| |
| static void |
| bfd_mips_elf32_swap_gptab_out (bfd *abfd, const Elf32_gptab *in, |
| Elf32_External_gptab *ex) |
| { |
| H_PUT_32 (abfd, in->gt_entry.gt_g_value, ex->gt_entry.gt_g_value); |
| H_PUT_32 (abfd, in->gt_entry.gt_bytes, ex->gt_entry.gt_bytes); |
| } |
| |
| static void |
| bfd_elf32_swap_compact_rel_out (bfd *abfd, const Elf32_compact_rel *in, |
| Elf32_External_compact_rel *ex) |
| { |
| H_PUT_32 (abfd, in->id1, ex->id1); |
| H_PUT_32 (abfd, in->num, ex->num); |
| H_PUT_32 (abfd, in->id2, ex->id2); |
| H_PUT_32 (abfd, in->offset, ex->offset); |
| H_PUT_32 (abfd, in->reserved0, ex->reserved0); |
| H_PUT_32 (abfd, in->reserved1, ex->reserved1); |
| } |
| |
| static void |
| bfd_elf32_swap_crinfo_out (bfd *abfd, const Elf32_crinfo *in, |
| Elf32_External_crinfo *ex) |
| { |
| unsigned long l; |
| |
| l = (((in->ctype & CRINFO_CTYPE) << CRINFO_CTYPE_SH) |
| | ((in->rtype & CRINFO_RTYPE) << CRINFO_RTYPE_SH) |
| | ((in->dist2to & CRINFO_DIST2TO) << CRINFO_DIST2TO_SH) |
| | ((in->relvaddr & CRINFO_RELVADDR) << CRINFO_RELVADDR_SH)); |
| H_PUT_32 (abfd, l, ex->info); |
| H_PUT_32 (abfd, in->konst, ex->konst); |
| H_PUT_32 (abfd, in->vaddr, ex->vaddr); |
| } |
| |
| /* A .reginfo section holds a single Elf32_RegInfo structure. These |
| routines swap this structure in and out. They are used outside of |
| BFD, so they are globally visible. */ |
| |
| void |
| bfd_mips_elf32_swap_reginfo_in (bfd *abfd, const Elf32_External_RegInfo *ex, |
| Elf32_RegInfo *in) |
| { |
| in->ri_gprmask = H_GET_32 (abfd, ex->ri_gprmask); |
| in->ri_cprmask[0] = H_GET_32 (abfd, ex->ri_cprmask[0]); |
| in->ri_cprmask[1] = H_GET_32 (abfd, ex->ri_cprmask[1]); |
| in->ri_cprmask[2] = H_GET_32 (abfd, ex->ri_cprmask[2]); |
| in->ri_cprmask[3] = H_GET_32 (abfd, ex->ri_cprmask[3]); |
| in->ri_gp_value = H_GET_32 (abfd, ex->ri_gp_value); |
| } |
| |
| void |
| bfd_mips_elf32_swap_reginfo_out (bfd *abfd, const Elf32_RegInfo *in, |
| Elf32_External_RegInfo *ex) |
| { |
| H_PUT_32 (abfd, in->ri_gprmask, ex->ri_gprmask); |
| H_PUT_32 (abfd, in->ri_cprmask[0], ex->ri_cprmask[0]); |
| H_PUT_32 (abfd, in->ri_cprmask[1], ex->ri_cprmask[1]); |
| H_PUT_32 (abfd, in->ri_cprmask[2], ex->ri_cprmask[2]); |
| H_PUT_32 (abfd, in->ri_cprmask[3], ex->ri_cprmask[3]); |
| H_PUT_32 (abfd, in->ri_gp_value, ex->ri_gp_value); |
| } |
| |
| /* In the 64 bit ABI, the .MIPS.options section holds register |
| information in an Elf64_Reginfo structure. These routines swap |
| them in and out. They are globally visible because they are used |
| outside of BFD. These routines are here so that gas can call them |
| without worrying about whether the 64 bit ABI has been included. */ |
| |
| void |
| bfd_mips_elf64_swap_reginfo_in (bfd *abfd, const Elf64_External_RegInfo *ex, |
| Elf64_Internal_RegInfo *in) |
| { |
| in->ri_gprmask = H_GET_32 (abfd, ex->ri_gprmask); |
| in->ri_pad = H_GET_32 (abfd, ex->ri_pad); |
| in->ri_cprmask[0] = H_GET_32 (abfd, ex->ri_cprmask[0]); |
| in->ri_cprmask[1] = H_GET_32 (abfd, ex->ri_cprmask[1]); |
| in->ri_cprmask[2] = H_GET_32 (abfd, ex->ri_cprmask[2]); |
| in->ri_cprmask[3] = H_GET_32 (abfd, ex->ri_cprmask[3]); |
| in->ri_gp_value = H_GET_64 (abfd, ex->ri_gp_value); |
| } |
| |
| void |
| bfd_mips_elf64_swap_reginfo_out (bfd *abfd, const Elf64_Internal_RegInfo *in, |
| Elf64_External_RegInfo *ex) |
| { |
| H_PUT_32 (abfd, in->ri_gprmask, ex->ri_gprmask); |
| H_PUT_32 (abfd, in->ri_pad, ex->ri_pad); |
| H_PUT_32 (abfd, in->ri_cprmask[0], ex->ri_cprmask[0]); |
| H_PUT_32 (abfd, in->ri_cprmask[1], ex->ri_cprmask[1]); |
| H_PUT_32 (abfd, in->ri_cprmask[2], ex->ri_cprmask[2]); |
| H_PUT_32 (abfd, in->ri_cprmask[3], ex->ri_cprmask[3]); |
| H_PUT_64 (abfd, in->ri_gp_value, ex->ri_gp_value); |
| } |
| |
| /* Swap in an options header. */ |
| |
| void |
| bfd_mips_elf_swap_options_in (bfd *abfd, const Elf_External_Options *ex, |
| Elf_Internal_Options *in) |
| { |
| in->kind = H_GET_8 (abfd, ex->kind); |
| in->size = H_GET_8 (abfd, ex->size); |
| in->section = H_GET_16 (abfd, ex->section); |
| in->info = H_GET_32 (abfd, ex->info); |
| } |
| |
| /* Swap out an options header. */ |
| |
| void |
| bfd_mips_elf_swap_options_out (bfd *abfd, const Elf_Internal_Options *in, |
| Elf_External_Options *ex) |
| { |
| H_PUT_8 (abfd, in->kind, ex->kind); |
| H_PUT_8 (abfd, in->size, ex->size); |
| H_PUT_16 (abfd, in->section, ex->section); |
| H_PUT_32 (abfd, in->info, ex->info); |
| } |
| |
| /* Swap in an abiflags structure. */ |
| |
| void |
| bfd_mips_elf_swap_abiflags_v0_in (bfd *abfd, |
| const Elf_External_ABIFlags_v0 *ex, |
| Elf_Internal_ABIFlags_v0 *in) |
| { |
| in->version = H_GET_16 (abfd, ex->version); |
| in->isa_level = H_GET_8 (abfd, ex->isa_level); |
| in->isa_rev = H_GET_8 (abfd, ex->isa_rev); |
| in->gpr_size = H_GET_8 (abfd, ex->gpr_size); |
| in->cpr1_size = H_GET_8 (abfd, ex->cpr1_size); |
| in->cpr2_size = H_GET_8 (abfd, ex->cpr2_size); |
| in->fp_abi = H_GET_8 (abfd, ex->fp_abi); |
| in->isa_ext = H_GET_32 (abfd, ex->isa_ext); |
| in->ases = H_GET_32 (abfd, ex->ases); |
| in->flags1 = H_GET_32 (abfd, ex->flags1); |
| in->flags2 = H_GET_32 (abfd, ex->flags2); |
| } |
| |
| /* Swap out an abiflags structure. */ |
| |
| void |
| bfd_mips_elf_swap_abiflags_v0_out (bfd *abfd, |
| const Elf_Internal_ABIFlags_v0 *in, |
| Elf_External_ABIFlags_v0 *ex) |
| { |
| H_PUT_16 (abfd, in->version, ex->version); |
| H_PUT_8 (abfd, in->isa_level, ex->isa_level); |
| H_PUT_8 (abfd, in->isa_rev, ex->isa_rev); |
| H_PUT_8 (abfd, in->gpr_size, ex->gpr_size); |
| H_PUT_8 (abfd, in->cpr1_size, ex->cpr1_size); |
| H_PUT_8 (abfd, in->cpr2_size, ex->cpr2_size); |
| H_PUT_8 (abfd, in->fp_abi, ex->fp_abi); |
| H_PUT_32 (abfd, in->isa_ext, ex->isa_ext); |
| H_PUT_32 (abfd, in->ases, ex->ases); |
| H_PUT_32 (abfd, in->flags1, ex->flags1); |
| H_PUT_32 (abfd, in->flags2, ex->flags2); |
| } |
| |
| /* This function is called via qsort() to sort the dynamic relocation |
| entries by increasing r_symndx value. */ |
| |
| static int |
| sort_dynamic_relocs (const void *arg1, const void *arg2) |
| { |
| Elf_Internal_Rela int_reloc1; |
| Elf_Internal_Rela int_reloc2; |
| int diff; |
| |
| bfd_elf32_swap_reloc_in (reldyn_sorting_bfd, arg1, &int_reloc1); |
| bfd_elf32_swap_reloc_in (reldyn_sorting_bfd, arg2, &int_reloc2); |
| |
| diff = ELF32_R_SYM (int_reloc1.r_info) - ELF32_R_SYM (int_reloc2.r_info); |
| if (diff != 0) |
| return diff; |
| |
| if (int_reloc1.r_offset < int_reloc2.r_offset) |
| return -1; |
| if (int_reloc1.r_offset > int_reloc2.r_offset) |
| return 1; |
| return 0; |
| } |
| |
| /* Like sort_dynamic_relocs, but used for elf64 relocations. */ |
| |
| static int |
| sort_dynamic_relocs_64 (const void *arg1 ATTRIBUTE_UNUSED, |
| const void *arg2 ATTRIBUTE_UNUSED) |
| { |
| #ifdef BFD64 |
| Elf_Internal_Rela int_reloc1[3]; |
| Elf_Internal_Rela int_reloc2[3]; |
| |
| (*get_elf_backend_data (reldyn_sorting_bfd)->s->swap_reloc_in) |
| (reldyn_sorting_bfd, arg1, int_reloc1); |
| (*get_elf_backend_data (reldyn_sorting_bfd)->s->swap_reloc_in) |
| (reldyn_sorting_bfd, arg2, int_reloc2); |
| |
| if (ELF64_R_SYM (int_reloc1[0].r_info) < ELF64_R_SYM (int_reloc2[0].r_info)) |
| return -1; |
| if (ELF64_R_SYM (int_reloc1[0].r_info) > ELF64_R_SYM (int_reloc2[0].r_info)) |
| return 1; |
| |
| if (int_reloc1[0].r_offset < int_reloc2[0].r_offset) |
| return -1; |
| if (int_reloc1[0].r_offset > int_reloc2[0].r_offset) |
| return 1; |
| return 0; |
| #else |
| abort (); |
| #endif |
| } |
| |
| |
| /* This routine is used to write out ECOFF debugging external symbol |
| information. It is called via mips_elf_link_hash_traverse. The |
| ECOFF external symbol information must match the ELF external |
| symbol information. Unfortunately, at this point we don't know |
| whether a symbol is required by reloc information, so the two |
| tables may wind up being different. We must sort out the external |
| symbol information before we can set the final size of the .mdebug |
| section, and we must set the size of the .mdebug section before we |
| can relocate any sections, and we can't know which symbols are |
| required by relocation until we relocate the sections. |
| Fortunately, it is relatively unlikely that any symbol will be |
| stripped but required by a reloc. In particular, it can not happen |
| when generating a final executable. */ |
| |
| static bool |
| mips_elf_output_extsym (struct mips_elf_link_hash_entry *h, void *data) |
| { |
| struct extsym_info *einfo = data; |
| bool strip; |
| asection *sec, *output_section; |
| |
| if (h->root.indx == -2) |
| strip = false; |
| else if ((h->root.def_dynamic |
| || h->root.ref_dynamic |
| || h->root.type == bfd_link_hash_new) |
| && !h->root.def_regular |
| && !h->root.ref_regular) |
| strip = true; |
| else if (einfo->info->strip == strip_all |
| || (einfo->info->strip == strip_some |
| && bfd_hash_lookup (einfo->info->keep_hash, |
| h->root.root.root.string, |
| false, false) == NULL)) |
| strip = true; |
| else |
| strip = false; |
| |
| if (strip) |
| return true; |
| |
| if (h->esym.ifd == -2) |
| { |
| h->esym.jmptbl = 0; |
| h->esym.cobol_main = 0; |
| h->esym.weakext = 0; |
| h->esym.reserved = 0; |
| h->esym.ifd = ifdNil; |
| h->esym.asym.value = 0; |
| h->esym.asym.st = stGlobal; |
| |
| if (h->root.root.type == bfd_link_hash_undefined |
| || h->root.root.type == bfd_link_hash_undefweak) |
| { |
| const char *name; |
| |
| /* Use undefined class. Also, set class and type for some |
| special symbols. */ |
| name = h->root.root.root.string; |
| if (strcmp (name, mips_elf_dynsym_rtproc_names[0]) == 0 |
| || strcmp (name, mips_elf_dynsym_rtproc_names[1]) == 0) |
| { |
| h->esym.asym.sc = scData; |
| h->esym.asym.st = stLabel; |
| h->esym.asym.value = 0; |
| } |
| else if (strcmp (name, mips_elf_dynsym_rtproc_names[2]) == 0) |
| { |
| h->esym.asym.sc = scAbs; |
| h->esym.asym.st = stLabel; |
| h->esym.asym.value = |
| mips_elf_hash_table (einfo->info)->procedure_count; |
| } |
| else |
| h->esym.asym.sc = scUndefined; |
| } |
| else if (h->root.root.type != bfd_link_hash_defined |
| && h->root.root.type != bfd_link_hash_defweak) |
| h->esym.asym.sc = scAbs; |
| else |
| { |
| const char *name; |
| |
| sec = h->root.root.u.def.section; |
| output_section = sec->output_section; |
| |
| /* When making a shared library and symbol h is the one from |
| the another shared library, OUTPUT_SECTION may be null. */ |
| if (output_section == NULL) |
| h->esym.asym.sc = scUndefined; |
| else |
| { |
| name = bfd_section_name (output_section); |
| |
| if (strcmp (name, ".text") == 0) |
| h->esym.asym.sc = scText; |
| else if (strcmp (name, ".data") == 0) |
| h->esym.asym.sc = scData; |
| else if (strcmp (name, ".sdata") == 0) |
| h->esym.asym.sc = scSData; |
| else if (strcmp (name, ".rodata") == 0 |
| || strcmp (name, ".rdata") == 0) |
| h->esym.asym.sc = scRData; |
| else if (strcmp (name, ".bss") == 0) |
| h->esym.asym.sc = scBss; |
| else if (strcmp (name, ".sbss") == 0) |
| h->esym.asym.sc = scSBss; |
| else if (strcmp (name, ".init") == 0) |
| h->esym.asym.sc = scInit; |
| else if (strcmp (name, ".fini") == 0) |
| h->esym.asym.sc = scFini; |
| else |
| h->esym.asym.sc = scAbs; |
| } |
| } |
| |
| h->esym.asym.reserved = 0; |
| h->esym.asym.index = indexNil; |
| } |
| |
| if (h->root.root.type == bfd_link_hash_common) |
| h->esym.asym.value = h->root.root.u.c.size; |
| else if (h->root.root.type == bfd_link_hash_defined |
| || h->root.root.type == bfd_link_hash_defweak) |
| { |
| if (h->esym.asym.sc == scCommon) |
| h->esym.asym.sc = scBss; |
| else if (h->esym.asym.sc == scSCommon) |
| h->esym.asym.sc = scSBss; |
| |
| sec = h->root.root.u.def.section; |
| output_section = sec->output_section; |
| if (output_section != NULL) |
| h->esym.asym.value = (h->root.root.u.def.value |
| + sec->output_offset |
| + output_section->vma); |
| else |
| h->esym.asym.value = 0; |
| } |
| else |
| { |
| struct mips_elf_link_hash_entry *hd = h; |
| |
| while (hd->root.root.type == bfd_link_hash_indirect) |
| hd = (struct mips_elf_link_hash_entry *)h->root.root.u.i.link; |
| |
| if (hd->needs_lazy_stub) |
| { |
| BFD_ASSERT (hd->root.plt.plist != NULL); |
| BFD_ASSERT (hd->root.plt.plist->stub_offset != MINUS_ONE); |
| /* Set type and value for a symbol with a function stub. */ |
| h->esym.asym.st = stProc; |
| sec = hd->root.root.u.def.section; |
| if (sec == NULL) |
| h->esym.asym.value = 0; |
| else |
| { |
| output_section = sec->output_section; |
| if (output_section != NULL) |
| h->esym.asym.value = (hd->root.plt.plist->stub_offset |
| + sec->output_offset |
| + output_section->vma); |
| else |
| h->esym.asym.value = 0; |
| } |
| } |
| } |
| |
| if (! bfd_ecoff_debug_one_external (einfo->abfd, einfo->debug, einfo->swap, |
| h->root.root.root.string, |
| &h->esym)) |
| { |
|