| /* Xtensa-specific support for 32-bit ELF. |
| Copyright (C) 2003-2021 Free Software Foundation, Inc. |
| |
| This file is part of BFD, the Binary File Descriptor library. |
| |
| This program is free software; you can redistribute it and/or |
| modify it under the terms of the GNU General Public License as |
| published by the Free Software Foundation; either version 3 of the |
| License, or (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, write to the Free Software |
| Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA |
| 02110-1301, USA. */ |
| |
| #include "sysdep.h" |
| #include "bfd.h" |
| |
| #include <stdarg.h> |
| #include <strings.h> |
| |
| #include "bfdlink.h" |
| #include "libbfd.h" |
| #include "elf-bfd.h" |
| #include "elf/xtensa.h" |
| #include "splay-tree.h" |
| #include "xtensa-isa.h" |
| #include "xtensa-config.h" |
| |
| /* All users of this file have bfd_octets_per_byte (abfd, sec) == 1. */ |
| #define OCTETS_PER_BYTE(ABFD, SEC) 1 |
| |
| #define XTENSA_NO_NOP_REMOVAL 0 |
| |
| #ifndef XSHAL_ABI |
| #define XSHAL_ABI 0 |
| #endif |
| |
| #ifndef XTHAL_ABI_UNDEFINED |
| #define XTHAL_ABI_UNDEFINED -1 |
| #endif |
| |
| #ifndef XTHAL_ABI_WINDOWED |
| #define XTHAL_ABI_WINDOWED 0 |
| #endif |
| |
| #ifndef XTHAL_ABI_CALL0 |
| #define XTHAL_ABI_CALL0 1 |
| #endif |
| |
| /* Local helper functions. */ |
| |
| static bool add_extra_plt_sections (struct bfd_link_info *, int); |
| static char *vsprint_msg (const char *, const char *, int, ...) ATTRIBUTE_PRINTF(2,4); |
| static bfd_reloc_status_type bfd_elf_xtensa_reloc |
| (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); |
| static bool do_fix_for_relocatable_link |
| (Elf_Internal_Rela *, bfd *, asection *, bfd_byte *); |
| static void do_fix_for_final_link |
| (Elf_Internal_Rela *, bfd *, asection *, bfd_byte *, bfd_vma *); |
| |
| /* Local functions to handle Xtensa configurability. */ |
| |
| static bool is_indirect_call_opcode (xtensa_opcode); |
| static bool is_direct_call_opcode (xtensa_opcode); |
| static bool is_windowed_call_opcode (xtensa_opcode); |
| static xtensa_opcode get_const16_opcode (void); |
| static xtensa_opcode get_l32r_opcode (void); |
| static bfd_vma l32r_offset (bfd_vma, bfd_vma); |
| static int get_relocation_opnd (xtensa_opcode, int); |
| static int get_relocation_slot (int); |
| static xtensa_opcode get_relocation_opcode |
| (bfd *, asection *, bfd_byte *, Elf_Internal_Rela *); |
| static bool is_l32r_relocation |
| (bfd *, asection *, bfd_byte *, Elf_Internal_Rela *); |
| static bool is_alt_relocation (int); |
| static bool is_operand_relocation (int); |
| static bfd_size_type insn_decode_len |
| (bfd_byte *, bfd_size_type, bfd_size_type); |
| static int insn_num_slots |
| (bfd_byte *, bfd_size_type, bfd_size_type); |
| static xtensa_opcode insn_decode_opcode |
| (bfd_byte *, bfd_size_type, bfd_size_type, int); |
| static bool check_branch_target_aligned |
| (bfd_byte *, bfd_size_type, bfd_vma, bfd_vma); |
| static bool check_loop_aligned |
| (bfd_byte *, bfd_size_type, bfd_vma, bfd_vma); |
| static bool check_branch_target_aligned_address (bfd_vma, int); |
| static bfd_size_type get_asm_simplify_size |
| (bfd_byte *, bfd_size_type, bfd_size_type); |
| |
| /* Functions for link-time code simplifications. */ |
| |
| static bfd_reloc_status_type elf_xtensa_do_asm_simplify |
| (bfd_byte *, bfd_vma, bfd_vma, char **); |
| static bfd_reloc_status_type contract_asm_expansion |
| (bfd_byte *, bfd_vma, Elf_Internal_Rela *, char **); |
| static xtensa_opcode swap_callx_for_call_opcode (xtensa_opcode); |
| static xtensa_opcode get_expanded_call_opcode (bfd_byte *, int, bool *); |
| |
| /* Access to internal relocations, section contents and symbols. */ |
| |
| static Elf_Internal_Rela *retrieve_internal_relocs |
| (bfd *, asection *, bool); |
| static void pin_internal_relocs (asection *, Elf_Internal_Rela *); |
| static void release_internal_relocs (asection *, Elf_Internal_Rela *); |
| static bfd_byte *retrieve_contents (bfd *, asection *, bool); |
| static void pin_contents (asection *, bfd_byte *); |
| static void release_contents (asection *, bfd_byte *); |
| static Elf_Internal_Sym *retrieve_local_syms (bfd *); |
| |
| /* Miscellaneous utility functions. */ |
| |
| static asection *elf_xtensa_get_plt_section (struct bfd_link_info *, int); |
| static asection *elf_xtensa_get_gotplt_section (struct bfd_link_info *, int); |
| static asection *get_elf_r_symndx_section (bfd *, unsigned long); |
| static struct elf_link_hash_entry *get_elf_r_symndx_hash_entry |
| (bfd *, unsigned long); |
| static bfd_vma get_elf_r_symndx_offset (bfd *, unsigned long); |
| static bool is_reloc_sym_weak (bfd *, Elf_Internal_Rela *); |
| static bool pcrel_reloc_fits (xtensa_opcode, int, bfd_vma, bfd_vma); |
| static bool xtensa_is_property_section (asection *); |
| static bool xtensa_is_insntable_section (asection *); |
| static bool xtensa_is_littable_section (asection *); |
| static bool xtensa_is_proptable_section (asection *); |
| static int internal_reloc_compare (const void *, const void *); |
| static int internal_reloc_matches (const void *, const void *); |
| static asection *xtensa_get_property_section (asection *, const char *); |
| static flagword xtensa_get_property_predef_flags (asection *); |
| |
| /* Other functions called directly by the linker. */ |
| |
| typedef void (*deps_callback_t) |
| (asection *, bfd_vma, asection *, bfd_vma, void *); |
| extern bool xtensa_callback_required_dependence |
| (bfd *, asection *, struct bfd_link_info *, deps_callback_t, void *); |
| |
| |
| /* Globally visible flag for choosing size optimization of NOP removal |
| instead of branch-target-aware minimization for NOP removal. |
| When nonzero, narrow all instructions and remove all NOPs possible |
| around longcall expansions. */ |
| |
| int elf32xtensa_size_opt; |
| |
| |
| /* The "new_section_hook" is used to set up a per-section |
| "xtensa_relax_info" data structure with additional information used |
| during relaxation. */ |
| |
| typedef struct xtensa_relax_info_struct xtensa_relax_info; |
| |
| |
| /* The GNU tools do not easily allow extending interfaces to pass around |
| the pointer to the Xtensa ISA information, so instead we add a global |
| variable here (in BFD) that can be used by any of the tools that need |
| this information. */ |
| |
| xtensa_isa xtensa_default_isa; |
| |
| |
| /* When this is true, relocations may have been modified to refer to |
| symbols from other input files. The per-section list of "fix" |
| records needs to be checked when resolving relocations. */ |
| |
| static bool relaxing_section = false; |
| |
| /* When this is true, during final links, literals that cannot be |
| coalesced and their relocations may be moved to other sections. */ |
| |
| int elf32xtensa_no_literal_movement = 1; |
| |
| /* Place property records for a section into individual property section |
| with xt.prop. prefix. */ |
| |
| bool elf32xtensa_separate_props = false; |
| |
| /* Xtensa ABI. It affects PLT entry code. */ |
| |
| int elf32xtensa_abi = XTHAL_ABI_UNDEFINED; |
| |
| /* Rename one of the generic section flags to better document how it |
| is used here. */ |
| /* Whether relocations have been processed. */ |
| #define reloc_done sec_flg0 |
| |
| static reloc_howto_type elf_howto_table[] = |
| { |
| HOWTO (R_XTENSA_NONE, 0, 3, 0, false, 0, complain_overflow_dont, |
| bfd_elf_xtensa_reloc, "R_XTENSA_NONE", |
| false, 0, 0, false), |
| HOWTO (R_XTENSA_32, 0, 2, 32, false, 0, complain_overflow_bitfield, |
| bfd_elf_xtensa_reloc, "R_XTENSA_32", |
| true, 0xffffffff, 0xffffffff, false), |
| |
| /* Replace a 32-bit value with a value from the runtime linker (only |
| used by linker-generated stub functions). The r_addend value is |
| special: 1 means to substitute a pointer to the runtime linker's |
| dynamic resolver function; 2 means to substitute the link map for |
| the shared object. */ |
| HOWTO (R_XTENSA_RTLD, 0, 2, 32, false, 0, complain_overflow_dont, |
| NULL, "R_XTENSA_RTLD", false, 0, 0, false), |
| |
| HOWTO (R_XTENSA_GLOB_DAT, 0, 2, 32, false, 0, complain_overflow_bitfield, |
| bfd_elf_generic_reloc, "R_XTENSA_GLOB_DAT", |
| false, 0, 0xffffffff, false), |
| HOWTO (R_XTENSA_JMP_SLOT, 0, 2, 32, false, 0, complain_overflow_bitfield, |
| bfd_elf_generic_reloc, "R_XTENSA_JMP_SLOT", |
| false, 0, 0xffffffff, false), |
| HOWTO (R_XTENSA_RELATIVE, 0, 2, 32, false, 0, complain_overflow_bitfield, |
| bfd_elf_generic_reloc, "R_XTENSA_RELATIVE", |
| false, 0, 0xffffffff, false), |
| HOWTO (R_XTENSA_PLT, 0, 2, 32, false, 0, complain_overflow_bitfield, |
| bfd_elf_xtensa_reloc, "R_XTENSA_PLT", |
| false, 0, 0xffffffff, false), |
| |
| EMPTY_HOWTO (7), |
| |
| /* Old relocations for backward compatibility. */ |
| HOWTO (R_XTENSA_OP0, 0, 0, 0, true, 0, complain_overflow_dont, |
| bfd_elf_xtensa_reloc, "R_XTENSA_OP0", false, 0, 0, true), |
| HOWTO (R_XTENSA_OP1, 0, 0, 0, true, 0, complain_overflow_dont, |
| bfd_elf_xtensa_reloc, "R_XTENSA_OP1", false, 0, 0, true), |
| HOWTO (R_XTENSA_OP2, 0, 0, 0, true, 0, complain_overflow_dont, |
| bfd_elf_xtensa_reloc, "R_XTENSA_OP2", false, 0, 0, true), |
| |
| /* Assembly auto-expansion. */ |
| HOWTO (R_XTENSA_ASM_EXPAND, 0, 0, 0, true, 0, complain_overflow_dont, |
| bfd_elf_xtensa_reloc, "R_XTENSA_ASM_EXPAND", false, 0, 0, true), |
| /* Relax assembly auto-expansion. */ |
| HOWTO (R_XTENSA_ASM_SIMPLIFY, 0, 0, 0, true, 0, complain_overflow_dont, |
| bfd_elf_xtensa_reloc, "R_XTENSA_ASM_SIMPLIFY", false, 0, 0, true), |
| |
| EMPTY_HOWTO (13), |
| |
| HOWTO (R_XTENSA_32_PCREL, 0, 2, 32, true, 0, complain_overflow_bitfield, |
| bfd_elf_xtensa_reloc, "R_XTENSA_32_PCREL", |
| false, 0, 0xffffffff, true), |
| |
| /* GNU extension to record C++ vtable hierarchy. */ |
| HOWTO (R_XTENSA_GNU_VTINHERIT, 0, 2, 0, false, 0, complain_overflow_dont, |
| NULL, "R_XTENSA_GNU_VTINHERIT", |
| false, 0, 0, false), |
| /* GNU extension to record C++ vtable member usage. */ |
| HOWTO (R_XTENSA_GNU_VTENTRY, 0, 2, 0, false, 0, complain_overflow_dont, |
| _bfd_elf_rel_vtable_reloc_fn, "R_XTENSA_GNU_VTENTRY", |
| false, 0, 0, false), |
| |
| /* Relocations for supporting difference of symbols. */ |
| HOWTO (R_XTENSA_DIFF8, 0, 0, 8, false, 0, complain_overflow_signed, |
| bfd_elf_xtensa_reloc, "R_XTENSA_DIFF8", false, 0, 0xff, false), |
| HOWTO (R_XTENSA_DIFF16, 0, 1, 16, false, 0, complain_overflow_signed, |
| bfd_elf_xtensa_reloc, "R_XTENSA_DIFF16", false, 0, 0xffff, false), |
| HOWTO (R_XTENSA_DIFF32, 0, 2, 32, false, 0, complain_overflow_signed, |
| bfd_elf_xtensa_reloc, "R_XTENSA_DIFF32", false, 0, 0xffffffff, false), |
| |
| /* General immediate operand relocations. */ |
| HOWTO (R_XTENSA_SLOT0_OP, 0, 0, 0, true, 0, complain_overflow_dont, |
| bfd_elf_xtensa_reloc, "R_XTENSA_SLOT0_OP", false, 0, 0, true), |
| HOWTO (R_XTENSA_SLOT1_OP, 0, 0, 0, true, 0, complain_overflow_dont, |
| bfd_elf_xtensa_reloc, "R_XTENSA_SLOT1_OP", false, 0, 0, true), |
| HOWTO (R_XTENSA_SLOT2_OP, 0, 0, 0, true, 0, complain_overflow_dont, |
| bfd_elf_xtensa_reloc, "R_XTENSA_SLOT2_OP", false, 0, 0, true), |
| HOWTO (R_XTENSA_SLOT3_OP, 0, 0, 0, true, 0, complain_overflow_dont, |
| bfd_elf_xtensa_reloc, "R_XTENSA_SLOT3_OP", false, 0, 0, true), |
| HOWTO (R_XTENSA_SLOT4_OP, 0, 0, 0, true, 0, complain_overflow_dont, |
| bfd_elf_xtensa_reloc, "R_XTENSA_SLOT4_OP", false, 0, 0, true), |
| HOWTO (R_XTENSA_SLOT5_OP, 0, 0, 0, true, 0, complain_overflow_dont, |
| bfd_elf_xtensa_reloc, "R_XTENSA_SLOT5_OP", false, 0, 0, true), |
| HOWTO (R_XTENSA_SLOT6_OP, 0, 0, 0, true, 0, complain_overflow_dont, |
| bfd_elf_xtensa_reloc, "R_XTENSA_SLOT6_OP", false, 0, 0, true), |
| HOWTO (R_XTENSA_SLOT7_OP, 0, 0, 0, true, 0, complain_overflow_dont, |
| bfd_elf_xtensa_reloc, "R_XTENSA_SLOT7_OP", false, 0, 0, true), |
| HOWTO (R_XTENSA_SLOT8_OP, 0, 0, 0, true, 0, complain_overflow_dont, |
| bfd_elf_xtensa_reloc, "R_XTENSA_SLOT8_OP", false, 0, 0, true), |
| HOWTO (R_XTENSA_SLOT9_OP, 0, 0, 0, true, 0, complain_overflow_dont, |
| bfd_elf_xtensa_reloc, "R_XTENSA_SLOT9_OP", false, 0, 0, true), |
| HOWTO (R_XTENSA_SLOT10_OP, 0, 0, 0, true, 0, complain_overflow_dont, |
| bfd_elf_xtensa_reloc, "R_XTENSA_SLOT10_OP", false, 0, 0, true), |
| HOWTO (R_XTENSA_SLOT11_OP, 0, 0, 0, true, 0, complain_overflow_dont, |
| bfd_elf_xtensa_reloc, "R_XTENSA_SLOT11_OP", false, 0, 0, true), |
| HOWTO (R_XTENSA_SLOT12_OP, 0, 0, 0, true, 0, complain_overflow_dont, |
| bfd_elf_xtensa_reloc, "R_XTENSA_SLOT12_OP", false, 0, 0, true), |
| HOWTO (R_XTENSA_SLOT13_OP, 0, 0, 0, true, 0, complain_overflow_dont, |
| bfd_elf_xtensa_reloc, "R_XTENSA_SLOT13_OP", false, 0, 0, true), |
| HOWTO (R_XTENSA_SLOT14_OP, 0, 0, 0, true, 0, complain_overflow_dont, |
| bfd_elf_xtensa_reloc, "R_XTENSA_SLOT14_OP", false, 0, 0, true), |
| |
| /* "Alternate" relocations. The meaning of these is opcode-specific. */ |
| HOWTO (R_XTENSA_SLOT0_ALT, 0, 0, 0, true, 0, complain_overflow_dont, |
| bfd_elf_xtensa_reloc, "R_XTENSA_SLOT0_ALT", false, 0, 0, true), |
| HOWTO (R_XTENSA_SLOT1_ALT, 0, 0, 0, true, 0, complain_overflow_dont, |
| bfd_elf_xtensa_reloc, "R_XTENSA_SLOT1_ALT", false, 0, 0, true), |
| HOWTO (R_XTENSA_SLOT2_ALT, 0, 0, 0, true, 0, complain_overflow_dont, |
| bfd_elf_xtensa_reloc, "R_XTENSA_SLOT2_ALT", false, 0, 0, true), |
| HOWTO (R_XTENSA_SLOT3_ALT, 0, 0, 0, true, 0, complain_overflow_dont, |
| bfd_elf_xtensa_reloc, "R_XTENSA_SLOT3_ALT", false, 0, 0, true), |
| HOWTO (R_XTENSA_SLOT4_ALT, 0, 0, 0, true, 0, complain_overflow_dont, |
| bfd_elf_xtensa_reloc, "R_XTENSA_SLOT4_ALT", false, 0, 0, true), |
| HOWTO (R_XTENSA_SLOT5_ALT, 0, 0, 0, true, 0, complain_overflow_dont, |
| bfd_elf_xtensa_reloc, "R_XTENSA_SLOT5_ALT", false, 0, 0, true), |
| HOWTO (R_XTENSA_SLOT6_ALT, 0, 0, 0, true, 0, complain_overflow_dont, |
| bfd_elf_xtensa_reloc, "R_XTENSA_SLOT6_ALT", false, 0, 0, true), |
| HOWTO (R_XTENSA_SLOT7_ALT, 0, 0, 0, true, 0, complain_overflow_dont, |
| bfd_elf_xtensa_reloc, "R_XTENSA_SLOT7_ALT", false, 0, 0, true), |
| HOWTO (R_XTENSA_SLOT8_ALT, 0, 0, 0, true, 0, complain_overflow_dont, |
| bfd_elf_xtensa_reloc, "R_XTENSA_SLOT8_ALT", false, 0, 0, true), |
| HOWTO (R_XTENSA_SLOT9_ALT, 0, 0, 0, true, 0, complain_overflow_dont, |
| bfd_elf_xtensa_reloc, "R_XTENSA_SLOT9_ALT", false, 0, 0, true), |
| HOWTO (R_XTENSA_SLOT10_ALT, 0, 0, 0, true, 0, complain_overflow_dont, |
| bfd_elf_xtensa_reloc, "R_XTENSA_SLOT10_ALT", false, 0, 0, true), |
| HOWTO (R_XTENSA_SLOT11_ALT, 0, 0, 0, true, 0, complain_overflow_dont, |
| bfd_elf_xtensa_reloc, "R_XTENSA_SLOT11_ALT", false, 0, 0, true), |
| HOWTO (R_XTENSA_SLOT12_ALT, 0, 0, 0, true, 0, complain_overflow_dont, |
| bfd_elf_xtensa_reloc, "R_XTENSA_SLOT12_ALT", false, 0, 0, true), |
| HOWTO (R_XTENSA_SLOT13_ALT, 0, 0, 0, true, 0, complain_overflow_dont, |
| bfd_elf_xtensa_reloc, "R_XTENSA_SLOT13_ALT", false, 0, 0, true), |
| HOWTO (R_XTENSA_SLOT14_ALT, 0, 0, 0, true, 0, complain_overflow_dont, |
| bfd_elf_xtensa_reloc, "R_XTENSA_SLOT14_ALT", false, 0, 0, true), |
| |
| /* TLS relocations. */ |
| HOWTO (R_XTENSA_TLSDESC_FN, 0, 2, 32, false, 0, complain_overflow_dont, |
| bfd_elf_xtensa_reloc, "R_XTENSA_TLSDESC_FN", |
| false, 0, 0xffffffff, false), |
| HOWTO (R_XTENSA_TLSDESC_ARG, 0, 2, 32, false, 0, complain_overflow_dont, |
| bfd_elf_xtensa_reloc, "R_XTENSA_TLSDESC_ARG", |
| false, 0, 0xffffffff, false), |
| HOWTO (R_XTENSA_TLS_DTPOFF, 0, 2, 32, false, 0, complain_overflow_dont, |
| bfd_elf_xtensa_reloc, "R_XTENSA_TLS_DTPOFF", |
| false, 0, 0xffffffff, false), |
| HOWTO (R_XTENSA_TLS_TPOFF, 0, 2, 32, false, 0, complain_overflow_dont, |
| bfd_elf_xtensa_reloc, "R_XTENSA_TLS_TPOFF", |
| false, 0, 0xffffffff, false), |
| HOWTO (R_XTENSA_TLS_FUNC, 0, 0, 0, false, 0, complain_overflow_dont, |
| bfd_elf_xtensa_reloc, "R_XTENSA_TLS_FUNC", |
| false, 0, 0, false), |
| HOWTO (R_XTENSA_TLS_ARG, 0, 0, 0, false, 0, complain_overflow_dont, |
| bfd_elf_xtensa_reloc, "R_XTENSA_TLS_ARG", |
| false, 0, 0, false), |
| HOWTO (R_XTENSA_TLS_CALL, 0, 0, 0, false, 0, complain_overflow_dont, |
| bfd_elf_xtensa_reloc, "R_XTENSA_TLS_CALL", |
| false, 0, 0, false), |
| |
| HOWTO (R_XTENSA_PDIFF8, 0, 0, 8, false, 0, complain_overflow_bitfield, |
| bfd_elf_xtensa_reloc, "R_XTENSA_PDIFF8", false, 0, 0xff, false), |
| HOWTO (R_XTENSA_PDIFF16, 0, 1, 16, false, 0, complain_overflow_bitfield, |
| bfd_elf_xtensa_reloc, "R_XTENSA_PDIFF16", false, 0, 0xffff, false), |
| HOWTO (R_XTENSA_PDIFF32, 0, 2, 32, false, 0, complain_overflow_bitfield, |
| bfd_elf_xtensa_reloc, "R_XTENSA_PDIFF32", false, 0, 0xffffffff, false), |
| |
| HOWTO (R_XTENSA_NDIFF8, 0, 0, 8, false, 0, complain_overflow_bitfield, |
| bfd_elf_xtensa_reloc, "R_XTENSA_NDIFF8", false, 0, 0xff, false), |
| HOWTO (R_XTENSA_NDIFF16, 0, 1, 16, false, 0, complain_overflow_bitfield, |
| bfd_elf_xtensa_reloc, "R_XTENSA_NDIFF16", false, 0, 0xffff, false), |
| HOWTO (R_XTENSA_NDIFF32, 0, 2, 32, false, 0, complain_overflow_bitfield, |
| bfd_elf_xtensa_reloc, "R_XTENSA_NDIFF32", false, 0, 0xffffffff, false), |
| }; |
| |
| #if DEBUG_GEN_RELOC |
| #define TRACE(str) \ |
| fprintf (stderr, "Xtensa bfd reloc lookup %d (%s)\n", code, str) |
| #else |
| #define TRACE(str) |
| #endif |
| |
| static reloc_howto_type * |
| elf_xtensa_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED, |
| bfd_reloc_code_real_type code) |
| { |
| switch (code) |
| { |
| case BFD_RELOC_NONE: |
| TRACE ("BFD_RELOC_NONE"); |
| return &elf_howto_table[(unsigned) R_XTENSA_NONE ]; |
| |
| case BFD_RELOC_32: |
| TRACE ("BFD_RELOC_32"); |
| return &elf_howto_table[(unsigned) R_XTENSA_32 ]; |
| |
| case BFD_RELOC_32_PCREL: |
| TRACE ("BFD_RELOC_32_PCREL"); |
| return &elf_howto_table[(unsigned) R_XTENSA_32_PCREL ]; |
| |
| case BFD_RELOC_XTENSA_DIFF8: |
| TRACE ("BFD_RELOC_XTENSA_DIFF8"); |
| return &elf_howto_table[(unsigned) R_XTENSA_DIFF8 ]; |
| |
| case BFD_RELOC_XTENSA_DIFF16: |
| TRACE ("BFD_RELOC_XTENSA_DIFF16"); |
| return &elf_howto_table[(unsigned) R_XTENSA_DIFF16 ]; |
| |
| case BFD_RELOC_XTENSA_DIFF32: |
| TRACE ("BFD_RELOC_XTENSA_DIFF32"); |
| return &elf_howto_table[(unsigned) R_XTENSA_DIFF32 ]; |
| |
| case BFD_RELOC_XTENSA_PDIFF8: |
| TRACE ("BFD_RELOC_XTENSA_PDIFF8"); |
| return &elf_howto_table[(unsigned) R_XTENSA_PDIFF8 ]; |
| |
| case BFD_RELOC_XTENSA_PDIFF16: |
| TRACE ("BFD_RELOC_XTENSA_PDIFF16"); |
| return &elf_howto_table[(unsigned) R_XTENSA_PDIFF16 ]; |
| |
| case BFD_RELOC_XTENSA_PDIFF32: |
| TRACE ("BFD_RELOC_XTENSA_PDIFF32"); |
| return &elf_howto_table[(unsigned) R_XTENSA_PDIFF32 ]; |
| |
| case BFD_RELOC_XTENSA_NDIFF8: |
| TRACE ("BFD_RELOC_XTENSA_NDIFF8"); |
| return &elf_howto_table[(unsigned) R_XTENSA_NDIFF8 ]; |
| |
| case BFD_RELOC_XTENSA_NDIFF16: |
| TRACE ("BFD_RELOC_XTENSA_NDIFF16"); |
| return &elf_howto_table[(unsigned) R_XTENSA_NDIFF16 ]; |
| |
| case BFD_RELOC_XTENSA_NDIFF32: |
| TRACE ("BFD_RELOC_XTENSA_NDIFF32"); |
| return &elf_howto_table[(unsigned) R_XTENSA_NDIFF32 ]; |
| |
| case BFD_RELOC_XTENSA_RTLD: |
| TRACE ("BFD_RELOC_XTENSA_RTLD"); |
| return &elf_howto_table[(unsigned) R_XTENSA_RTLD ]; |
| |
| case BFD_RELOC_XTENSA_GLOB_DAT: |
| TRACE ("BFD_RELOC_XTENSA_GLOB_DAT"); |
| return &elf_howto_table[(unsigned) R_XTENSA_GLOB_DAT ]; |
| |
| case BFD_RELOC_XTENSA_JMP_SLOT: |
| TRACE ("BFD_RELOC_XTENSA_JMP_SLOT"); |
| return &elf_howto_table[(unsigned) R_XTENSA_JMP_SLOT ]; |
| |
| case BFD_RELOC_XTENSA_RELATIVE: |
| TRACE ("BFD_RELOC_XTENSA_RELATIVE"); |
| return &elf_howto_table[(unsigned) R_XTENSA_RELATIVE ]; |
| |
| case BFD_RELOC_XTENSA_PLT: |
| TRACE ("BFD_RELOC_XTENSA_PLT"); |
| return &elf_howto_table[(unsigned) R_XTENSA_PLT ]; |
| |
| case BFD_RELOC_XTENSA_OP0: |
| TRACE ("BFD_RELOC_XTENSA_OP0"); |
| return &elf_howto_table[(unsigned) R_XTENSA_OP0 ]; |
| |
| case BFD_RELOC_XTENSA_OP1: |
| TRACE ("BFD_RELOC_XTENSA_OP1"); |
| return &elf_howto_table[(unsigned) R_XTENSA_OP1 ]; |
| |
| case BFD_RELOC_XTENSA_OP2: |
| TRACE ("BFD_RELOC_XTENSA_OP2"); |
| return &elf_howto_table[(unsigned) R_XTENSA_OP2 ]; |
| |
| case BFD_RELOC_XTENSA_ASM_EXPAND: |
| TRACE ("BFD_RELOC_XTENSA_ASM_EXPAND"); |
| return &elf_howto_table[(unsigned) R_XTENSA_ASM_EXPAND ]; |
| |
| case BFD_RELOC_XTENSA_ASM_SIMPLIFY: |
| TRACE ("BFD_RELOC_XTENSA_ASM_SIMPLIFY"); |
| return &elf_howto_table[(unsigned) R_XTENSA_ASM_SIMPLIFY ]; |
| |
| case BFD_RELOC_VTABLE_INHERIT: |
| TRACE ("BFD_RELOC_VTABLE_INHERIT"); |
| return &elf_howto_table[(unsigned) R_XTENSA_GNU_VTINHERIT ]; |
| |
| case BFD_RELOC_VTABLE_ENTRY: |
| TRACE ("BFD_RELOC_VTABLE_ENTRY"); |
| return &elf_howto_table[(unsigned) R_XTENSA_GNU_VTENTRY ]; |
| |
| case BFD_RELOC_XTENSA_TLSDESC_FN: |
| TRACE ("BFD_RELOC_XTENSA_TLSDESC_FN"); |
| return &elf_howto_table[(unsigned) R_XTENSA_TLSDESC_FN ]; |
| |
| case BFD_RELOC_XTENSA_TLSDESC_ARG: |
| TRACE ("BFD_RELOC_XTENSA_TLSDESC_ARG"); |
| return &elf_howto_table[(unsigned) R_XTENSA_TLSDESC_ARG ]; |
| |
| case BFD_RELOC_XTENSA_TLS_DTPOFF: |
| TRACE ("BFD_RELOC_XTENSA_TLS_DTPOFF"); |
| return &elf_howto_table[(unsigned) R_XTENSA_TLS_DTPOFF ]; |
| |
| case BFD_RELOC_XTENSA_TLS_TPOFF: |
| TRACE ("BFD_RELOC_XTENSA_TLS_TPOFF"); |
| return &elf_howto_table[(unsigned) R_XTENSA_TLS_TPOFF ]; |
| |
| case BFD_RELOC_XTENSA_TLS_FUNC: |
| TRACE ("BFD_RELOC_XTENSA_TLS_FUNC"); |
| return &elf_howto_table[(unsigned) R_XTENSA_TLS_FUNC ]; |
| |
| case BFD_RELOC_XTENSA_TLS_ARG: |
| TRACE ("BFD_RELOC_XTENSA_TLS_ARG"); |
| return &elf_howto_table[(unsigned) R_XTENSA_TLS_ARG ]; |
| |
| case BFD_RELOC_XTENSA_TLS_CALL: |
| TRACE ("BFD_RELOC_XTENSA_TLS_CALL"); |
| return &elf_howto_table[(unsigned) R_XTENSA_TLS_CALL ]; |
| |
| default: |
| if (code >= BFD_RELOC_XTENSA_SLOT0_OP |
| && code <= BFD_RELOC_XTENSA_SLOT14_OP) |
| { |
| unsigned n = (R_XTENSA_SLOT0_OP + |
| (code - BFD_RELOC_XTENSA_SLOT0_OP)); |
| return &elf_howto_table[n]; |
| } |
| |
| if (code >= BFD_RELOC_XTENSA_SLOT0_ALT |
| && code <= BFD_RELOC_XTENSA_SLOT14_ALT) |
| { |
| unsigned n = (R_XTENSA_SLOT0_ALT + |
| (code - BFD_RELOC_XTENSA_SLOT0_ALT)); |
| return &elf_howto_table[n]; |
| } |
| |
| break; |
| } |
| |
| /* xgettext:c-format */ |
| _bfd_error_handler (_("%pB: unsupported relocation type %#x"), abfd, (int) code); |
| bfd_set_error (bfd_error_bad_value); |
| TRACE ("Unknown"); |
| return NULL; |
| } |
| |
| static reloc_howto_type * |
| elf_xtensa_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED, |
| const char *r_name) |
| { |
| unsigned int i; |
| |
| for (i = 0; i < sizeof (elf_howto_table) / sizeof (elf_howto_table[0]); i++) |
| if (elf_howto_table[i].name != NULL |
| && strcasecmp (elf_howto_table[i].name, r_name) == 0) |
| return &elf_howto_table[i]; |
| |
| return NULL; |
| } |
| |
| |
| /* Given an ELF "rela" relocation, find the corresponding howto and record |
| it in the BFD internal arelent representation of the relocation. */ |
| |
| static bool |
| elf_xtensa_info_to_howto_rela (bfd *abfd, |
| arelent *cache_ptr, |
| Elf_Internal_Rela *dst) |
| { |
| unsigned int r_type = ELF32_R_TYPE (dst->r_info); |
| |
| if (r_type >= (unsigned int) R_XTENSA_max) |
| { |
| /* xgettext:c-format */ |
| _bfd_error_handler (_("%pB: unsupported relocation type %#x"), |
| abfd, r_type); |
| bfd_set_error (bfd_error_bad_value); |
| return false; |
| } |
| cache_ptr->howto = &elf_howto_table[r_type]; |
| return true; |
| } |
| |
| |
| /* Functions for the Xtensa ELF linker. */ |
| |
| /* The name of the dynamic interpreter. This is put in the .interp |
| section. */ |
| |
| #define ELF_DYNAMIC_INTERPRETER "/lib/ld.so" |
| |
| /* The size in bytes of an entry in the procedure linkage table. |
| (This does _not_ include the space for the literals associated with |
| the PLT entry.) */ |
| |
| #define PLT_ENTRY_SIZE 16 |
| |
| /* For _really_ large PLTs, we may need to alternate between literals |
| and code to keep the literals within the 256K range of the L32R |
| instructions in the code. It's unlikely that anyone would ever need |
| such a big PLT, but an arbitrary limit on the PLT size would be bad. |
| Thus, we split the PLT into chunks. Since there's very little |
| overhead (2 extra literals) for each chunk, the chunk size is kept |
| small so that the code for handling multiple chunks get used and |
| tested regularly. With 254 entries, there are 1K of literals for |
| each chunk, and that seems like a nice round number. */ |
| |
| #define PLT_ENTRIES_PER_CHUNK 254 |
| |
| /* PLT entries are actually used as stub functions for lazy symbol |
| resolution. Once the symbol is resolved, the stub function is never |
| invoked. Note: the 32-byte frame size used here cannot be changed |
| without a corresponding change in the runtime linker. */ |
| |
| static const bfd_byte elf_xtensa_be_plt_entry[][PLT_ENTRY_SIZE] = |
| { |
| { |
| 0x6c, 0x10, 0x04, /* entry sp, 32 */ |
| 0x18, 0x00, 0x00, /* l32r a8, [got entry for rtld's resolver] */ |
| 0x1a, 0x00, 0x00, /* l32r a10, [got entry for rtld's link map] */ |
| 0x1b, 0x00, 0x00, /* l32r a11, [literal for reloc index] */ |
| 0x0a, 0x80, 0x00, /* jx a8 */ |
| 0 /* unused */ |
| }, |
| { |
| 0x18, 0x00, 0x00, /* l32r a8, [got entry for rtld's resolver] */ |
| 0x1a, 0x00, 0x00, /* l32r a10, [got entry for rtld's link map] */ |
| 0x1b, 0x00, 0x00, /* l32r a11, [literal for reloc index] */ |
| 0x0a, 0x80, 0x00, /* jx a8 */ |
| 0 /* unused */ |
| } |
| }; |
| |
| static const bfd_byte elf_xtensa_le_plt_entry[][PLT_ENTRY_SIZE] = |
| { |
| { |
| 0x36, 0x41, 0x00, /* entry sp, 32 */ |
| 0x81, 0x00, 0x00, /* l32r a8, [got entry for rtld's resolver] */ |
| 0xa1, 0x00, 0x00, /* l32r a10, [got entry for rtld's link map] */ |
| 0xb1, 0x00, 0x00, /* l32r a11, [literal for reloc index] */ |
| 0xa0, 0x08, 0x00, /* jx a8 */ |
| 0 /* unused */ |
| }, |
| { |
| 0x81, 0x00, 0x00, /* l32r a8, [got entry for rtld's resolver] */ |
| 0xa1, 0x00, 0x00, /* l32r a10, [got entry for rtld's link map] */ |
| 0xb1, 0x00, 0x00, /* l32r a11, [literal for reloc index] */ |
| 0xa0, 0x08, 0x00, /* jx a8 */ |
| 0 /* unused */ |
| } |
| }; |
| |
| /* The size of the thread control block. */ |
| #define TCB_SIZE 8 |
| |
| struct elf_xtensa_link_hash_entry |
| { |
| struct elf_link_hash_entry elf; |
| |
| bfd_signed_vma tlsfunc_refcount; |
| |
| #define GOT_UNKNOWN 0 |
| #define GOT_NORMAL 1 |
| #define GOT_TLS_GD 2 /* global or local dynamic */ |
| #define GOT_TLS_IE 4 /* initial or local exec */ |
| #define GOT_TLS_ANY (GOT_TLS_GD | GOT_TLS_IE) |
| unsigned char tls_type; |
| }; |
| |
| #define elf_xtensa_hash_entry(ent) ((struct elf_xtensa_link_hash_entry *)(ent)) |
| |
| struct elf_xtensa_obj_tdata |
| { |
| struct elf_obj_tdata root; |
| |
| /* tls_type for each local got entry. */ |
| char *local_got_tls_type; |
| |
| bfd_signed_vma *local_tlsfunc_refcounts; |
| }; |
| |
| #define elf_xtensa_tdata(abfd) \ |
| ((struct elf_xtensa_obj_tdata *) (abfd)->tdata.any) |
| |
| #define elf_xtensa_local_got_tls_type(abfd) \ |
| (elf_xtensa_tdata (abfd)->local_got_tls_type) |
| |
| #define elf_xtensa_local_tlsfunc_refcounts(abfd) \ |
| (elf_xtensa_tdata (abfd)->local_tlsfunc_refcounts) |
| |
| #define is_xtensa_elf(bfd) \ |
| (bfd_get_flavour (bfd) == bfd_target_elf_flavour \ |
| && elf_tdata (bfd) != NULL \ |
| && elf_object_id (bfd) == XTENSA_ELF_DATA) |
| |
| static bool |
| elf_xtensa_mkobject (bfd *abfd) |
| { |
| return bfd_elf_allocate_object (abfd, sizeof (struct elf_xtensa_obj_tdata), |
| XTENSA_ELF_DATA); |
| } |
| |
| /* Xtensa ELF linker hash table. */ |
| |
| struct elf_xtensa_link_hash_table |
| { |
| struct elf_link_hash_table elf; |
| |
| /* Short-cuts to get to dynamic linker sections. */ |
| asection *sgotloc; |
| asection *spltlittbl; |
| |
| /* Total count of PLT relocations seen during check_relocs. |
| The actual PLT code must be split into multiple sections and all |
| the sections have to be created before size_dynamic_sections, |
| where we figure out the exact number of PLT entries that will be |
| needed. It is OK if this count is an overestimate, e.g., some |
| relocations may be removed by GC. */ |
| int plt_reloc_count; |
| |
| struct elf_xtensa_link_hash_entry *tlsbase; |
| }; |
| |
| /* Get the Xtensa ELF linker hash table from a link_info structure. */ |
| |
| #define elf_xtensa_hash_table(p) \ |
| ((is_elf_hash_table ((p)->hash) \ |
| && elf_hash_table_id (elf_hash_table (p)) == XTENSA_ELF_DATA) \ |
| ? (struct elf_xtensa_link_hash_table *) (p)->hash : NULL) |
| |
| /* Create an entry in an Xtensa ELF linker hash table. */ |
| |
| static struct bfd_hash_entry * |
| elf_xtensa_link_hash_newfunc (struct bfd_hash_entry *entry, |
| struct bfd_hash_table *table, |
| const char *string) |
| { |
| /* Allocate the structure if it has not already been allocated by a |
| subclass. */ |
| if (entry == NULL) |
| { |
| entry = bfd_hash_allocate (table, |
| sizeof (struct elf_xtensa_link_hash_entry)); |
| if (entry == NULL) |
| return entry; |
| } |
| |
| /* Call the allocation method of the superclass. */ |
| entry = _bfd_elf_link_hash_newfunc (entry, table, string); |
| if (entry != NULL) |
| { |
| struct elf_xtensa_link_hash_entry *eh = elf_xtensa_hash_entry (entry); |
| eh->tlsfunc_refcount = 0; |
| eh->tls_type = GOT_UNKNOWN; |
| } |
| |
| return entry; |
| } |
| |
| /* Create an Xtensa ELF linker hash table. */ |
| |
| static struct bfd_link_hash_table * |
| elf_xtensa_link_hash_table_create (bfd *abfd) |
| { |
| struct elf_link_hash_entry *tlsbase; |
| struct elf_xtensa_link_hash_table *ret; |
| size_t amt = sizeof (struct elf_xtensa_link_hash_table); |
| |
| ret = bfd_zmalloc (amt); |
| if (ret == NULL) |
| return NULL; |
| |
| if (!_bfd_elf_link_hash_table_init (&ret->elf, abfd, |
| elf_xtensa_link_hash_newfunc, |
| sizeof (struct elf_xtensa_link_hash_entry), |
| XTENSA_ELF_DATA)) |
| { |
| free (ret); |
| return NULL; |
| } |
| |
| /* Create a hash entry for "_TLS_MODULE_BASE_" to speed up checking |
| for it later. */ |
| tlsbase = elf_link_hash_lookup (&ret->elf, "_TLS_MODULE_BASE_", |
| true, false, false); |
| tlsbase->root.type = bfd_link_hash_new; |
| tlsbase->root.u.undef.abfd = NULL; |
| tlsbase->non_elf = 0; |
| ret->elf.dt_pltgot_required = true; |
| ret->tlsbase = elf_xtensa_hash_entry (tlsbase); |
| ret->tlsbase->tls_type = GOT_UNKNOWN; |
| |
| return &ret->elf.root; |
| } |
| |
| /* Copy the extra info we tack onto an elf_link_hash_entry. */ |
| |
| static void |
| elf_xtensa_copy_indirect_symbol (struct bfd_link_info *info, |
| struct elf_link_hash_entry *dir, |
| struct elf_link_hash_entry *ind) |
| { |
| struct elf_xtensa_link_hash_entry *edir, *eind; |
| |
| edir = elf_xtensa_hash_entry (dir); |
| eind = elf_xtensa_hash_entry (ind); |
| |
| if (ind->root.type == bfd_link_hash_indirect) |
| { |
| edir->tlsfunc_refcount += eind->tlsfunc_refcount; |
| eind->tlsfunc_refcount = 0; |
| |
| if (dir->got.refcount <= 0) |
| { |
| edir->tls_type = eind->tls_type; |
| eind->tls_type = GOT_UNKNOWN; |
| } |
| } |
| |
| _bfd_elf_link_hash_copy_indirect (info, dir, ind); |
| } |
| |
| static inline bool |
| elf_xtensa_dynamic_symbol_p (struct elf_link_hash_entry *h, |
| struct bfd_link_info *info) |
| { |
| /* Check if we should do dynamic things to this symbol. The |
| "ignore_protected" argument need not be set, because Xtensa code |
| does not require special handling of STV_PROTECTED to make function |
| pointer comparisons work properly. The PLT addresses are never |
| used for function pointers. */ |
| |
| return _bfd_elf_dynamic_symbol_p (h, info, 0); |
| } |
| |
| |
| static int |
| property_table_compare (const void *ap, const void *bp) |
| { |
| const property_table_entry *a = (const property_table_entry *) ap; |
| const property_table_entry *b = (const property_table_entry *) bp; |
| |
| if (a->address == b->address) |
| { |
| if (a->size != b->size) |
| return (a->size - b->size); |
| |
| if ((a->flags & XTENSA_PROP_ALIGN) != (b->flags & XTENSA_PROP_ALIGN)) |
| return ((b->flags & XTENSA_PROP_ALIGN) |
| - (a->flags & XTENSA_PROP_ALIGN)); |
| |
| if ((a->flags & XTENSA_PROP_ALIGN) |
| && (GET_XTENSA_PROP_ALIGNMENT (a->flags) |
| != GET_XTENSA_PROP_ALIGNMENT (b->flags))) |
| return (GET_XTENSA_PROP_ALIGNMENT (a->flags) |
| - GET_XTENSA_PROP_ALIGNMENT (b->flags)); |
| |
| if ((a->flags & XTENSA_PROP_UNREACHABLE) |
| != (b->flags & XTENSA_PROP_UNREACHABLE)) |
| return ((b->flags & XTENSA_PROP_UNREACHABLE) |
| - (a->flags & XTENSA_PROP_UNREACHABLE)); |
| |
| return (a->flags - b->flags); |
| } |
| |
| return (a->address - b->address); |
| } |
| |
| |
| static int |
| property_table_matches (const void *ap, const void *bp) |
| { |
| const property_table_entry *a = (const property_table_entry *) ap; |
| const property_table_entry *b = (const property_table_entry *) bp; |
| |
| /* Check if one entry overlaps with the other. */ |
| if ((b->address >= a->address && b->address < (a->address + a->size)) |
| || (a->address >= b->address && a->address < (b->address + b->size))) |
| return 0; |
| |
| return (a->address - b->address); |
| } |
| |
| |
| /* Get the literal table or property table entries for the given |
| section. Sets TABLE_P and returns the number of entries. On |
| error, returns a negative value. */ |
| |
| int |
| xtensa_read_table_entries (bfd *abfd, |
| asection *section, |
| property_table_entry **table_p, |
| const char *sec_name, |
| bool output_addr) |
| { |
| asection *table_section; |
| bfd_size_type table_size = 0; |
| bfd_byte *table_data; |
| property_table_entry *blocks; |
| int blk, block_count; |
| bfd_size_type num_records; |
| Elf_Internal_Rela *internal_relocs, *irel, *rel_end; |
| bfd_vma section_addr, off; |
| flagword predef_flags; |
| bfd_size_type table_entry_size, section_limit; |
| |
| if (bfd_get_flavour (abfd) != bfd_target_elf_flavour |
| || !section |
| || !(section->flags & SEC_ALLOC) |
| || (section->flags & SEC_DEBUGGING)) |
| { |
| *table_p = NULL; |
| return 0; |
| } |
| |
| table_section = xtensa_get_property_section (section, sec_name); |
| if (table_section) |
| table_size = table_section->size; |
| |
| if (table_size == 0) |
| { |
| *table_p = NULL; |
| return 0; |
| } |
| |
| predef_flags = xtensa_get_property_predef_flags (table_section); |
| table_entry_size = 12; |
| if (predef_flags) |
| table_entry_size -= 4; |
| |
| num_records = table_size / table_entry_size; |
| |
| table_data = retrieve_contents (abfd, table_section, true); |
| if (table_data == NULL) |
| { |
| *table_p = NULL; |
| return 0; |
| } |
| |
| blocks = (property_table_entry *) |
| bfd_malloc (num_records * sizeof (property_table_entry)); |
| block_count = 0; |
| |
| if (output_addr) |
| section_addr = section->output_section->vma + section->output_offset; |
| else |
| section_addr = section->vma; |
| |
| internal_relocs = retrieve_internal_relocs (abfd, table_section, true); |
| if (internal_relocs && !table_section->reloc_done) |
| { |
| qsort (internal_relocs, table_section->reloc_count, |
| sizeof (Elf_Internal_Rela), internal_reloc_compare); |
| irel = internal_relocs; |
| } |
| else |
| irel = NULL; |
| |
| section_limit = bfd_get_section_limit (abfd, section); |
| rel_end = internal_relocs + table_section->reloc_count; |
| |
| for (off = 0; off < table_size; off += table_entry_size) |
| { |
| bfd_vma address = bfd_get_32 (abfd, table_data + off); |
| |
| /* Skip any relocations before the current offset. This should help |
| avoid confusion caused by unexpected relocations for the preceding |
| table entry. */ |
| while (irel && |
| (irel->r_offset < off |
| || (irel->r_offset == off |
| && ELF32_R_TYPE (irel->r_info) == R_XTENSA_NONE))) |
| { |
| irel += 1; |
| if (irel >= rel_end) |
| irel = 0; |
| } |
| |
| if (irel && irel->r_offset == off) |
| { |
| bfd_vma sym_off; |
| unsigned long r_symndx = ELF32_R_SYM (irel->r_info); |
| BFD_ASSERT (ELF32_R_TYPE (irel->r_info) == R_XTENSA_32); |
| |
| if (get_elf_r_symndx_section (abfd, r_symndx) != section) |
| continue; |
| |
| sym_off = get_elf_r_symndx_offset (abfd, r_symndx); |
| BFD_ASSERT (sym_off == 0); |
| address += (section_addr + sym_off + irel->r_addend); |
| } |
| else |
| { |
| if (address < section_addr |
| || address >= section_addr + section_limit) |
| continue; |
| } |
| |
| blocks[block_count].address = address; |
| blocks[block_count].size = bfd_get_32 (abfd, table_data + off + 4); |
| if (predef_flags) |
| blocks[block_count].flags = predef_flags; |
| else |
| blocks[block_count].flags = bfd_get_32 (abfd, table_data + off + 8); |
| block_count++; |
| } |
| |
| release_contents (table_section, table_data); |
| release_internal_relocs (table_section, internal_relocs); |
| |
| if (block_count > 0) |
| { |
| /* Now sort them into address order for easy reference. */ |
| qsort (blocks, block_count, sizeof (property_table_entry), |
| property_table_compare); |
| |
| /* Check that the table contents are valid. Problems may occur, |
| for example, if an unrelocated object file is stripped. */ |
| for (blk = 1; blk < block_count; blk++) |
| { |
| /* The only circumstance where two entries may legitimately |
| have the same address is when one of them is a zero-size |
| placeholder to mark a place where fill can be inserted. |
| The zero-size entry should come first. */ |
| if (blocks[blk - 1].address == blocks[blk].address && |
| blocks[blk - 1].size != 0) |
| { |
| /* xgettext:c-format */ |
| _bfd_error_handler (_("%pB(%pA): invalid property table"), |
| abfd, section); |
| bfd_set_error (bfd_error_bad_value); |
| free (blocks); |
| return -1; |
| } |
| } |
| } |
| |
| *table_p = blocks; |
| return block_count; |
| } |
| |
| |
| static property_table_entry * |
| elf_xtensa_find_property_entry (property_table_entry *property_table, |
| int property_table_size, |
| bfd_vma addr) |
| { |
| property_table_entry entry; |
| property_table_entry *rv; |
| |
| if (property_table_size == 0) |
| return NULL; |
| |
| entry.address = addr; |
| entry.size = 1; |
| entry.flags = 0; |
| |
| rv = bsearch (&entry, property_table, property_table_size, |
| sizeof (property_table_entry), property_table_matches); |
| return rv; |
| } |
| |
| |
| static bool |
| elf_xtensa_in_literal_pool (property_table_entry *lit_table, |
| int lit_table_size, |
| bfd_vma addr) |
| { |
| if (elf_xtensa_find_property_entry (lit_table, lit_table_size, addr)) |
| return true; |
| |
| return false; |
| } |
| |
| |
| /* Look through the relocs for a section during the first phase, and |
| calculate needed space in the dynamic reloc sections. */ |
| |
| static bool |
| elf_xtensa_check_relocs (bfd *abfd, |
| struct bfd_link_info *info, |
| asection *sec, |
| const Elf_Internal_Rela *relocs) |
| { |
| struct elf_xtensa_link_hash_table *htab; |
| Elf_Internal_Shdr *symtab_hdr; |
| struct elf_link_hash_entry **sym_hashes; |
| const Elf_Internal_Rela *rel; |
| const Elf_Internal_Rela *rel_end; |
| |
| if (bfd_link_relocatable (info)) |
| return true; |
| |
| BFD_ASSERT (is_xtensa_elf (abfd)); |
| |
| htab = elf_xtensa_hash_table (info); |
| if (htab == NULL) |
| return false; |
| |
| symtab_hdr = &elf_tdata (abfd)->symtab_hdr; |
| sym_hashes = elf_sym_hashes (abfd); |
| |
| rel_end = relocs + sec->reloc_count; |
| for (rel = relocs; rel < rel_end; rel++) |
| { |
| unsigned int r_type; |
| unsigned r_symndx; |
| struct elf_link_hash_entry *h = NULL; |
| struct elf_xtensa_link_hash_entry *eh; |
| int tls_type, old_tls_type; |
| bool is_got = false; |
| bool is_plt = false; |
| bool is_tlsfunc = false; |
| |
| r_symndx = ELF32_R_SYM (rel->r_info); |
| r_type = ELF32_R_TYPE (rel->r_info); |
| |
| if (r_symndx >= NUM_SHDR_ENTRIES (symtab_hdr)) |
| { |
| /* xgettext:c-format */ |
| _bfd_error_handler (_("%pB: bad symbol index: %d"), |
| abfd, r_symndx); |
| return false; |
| } |
| |
| if (r_symndx >= symtab_hdr->sh_info) |
| { |
| h = sym_hashes[r_symndx - symtab_hdr->sh_info]; |
| while (h->root.type == bfd_link_hash_indirect |
| || h->root.type == bfd_link_hash_warning) |
| h = (struct elf_link_hash_entry *) h->root.u.i.link; |
| } |
| eh = elf_xtensa_hash_entry (h); |
| |
| switch (r_type) |
| { |
| case R_XTENSA_TLSDESC_FN: |
| if (bfd_link_pic (info)) |
| { |
| tls_type = GOT_TLS_GD; |
| is_got = true; |
| is_tlsfunc = true; |
| } |
| else |
| tls_type = GOT_TLS_IE; |
| break; |
| |
| case R_XTENSA_TLSDESC_ARG: |
| if (bfd_link_pic (info)) |
| { |
| tls_type = GOT_TLS_GD; |
| is_got = true; |
| } |
| else |
| { |
| tls_type = GOT_TLS_IE; |
| if (h && elf_xtensa_hash_entry (h) != htab->tlsbase) |
| is_got = true; |
| } |
| break; |
| |
| case R_XTENSA_TLS_DTPOFF: |
| if (bfd_link_pic (info)) |
| tls_type = GOT_TLS_GD; |
| else |
| tls_type = GOT_TLS_IE; |
| break; |
| |
| case R_XTENSA_TLS_TPOFF: |
| tls_type = GOT_TLS_IE; |
| if (bfd_link_pic (info)) |
| info->flags |= DF_STATIC_TLS; |
| if (bfd_link_pic (info) || h) |
| is_got = true; |
| break; |
| |
| case R_XTENSA_32: |
| tls_type = GOT_NORMAL; |
| is_got = true; |
| break; |
| |
| case R_XTENSA_PLT: |
| tls_type = GOT_NORMAL; |
| is_plt = true; |
| break; |
| |
| case R_XTENSA_GNU_VTINHERIT: |
| /* This relocation describes the C++ object vtable hierarchy. |
| Reconstruct it for later use during GC. */ |
| if (!bfd_elf_gc_record_vtinherit (abfd, sec, h, rel->r_offset)) |
| return false; |
| continue; |
| |
| case R_XTENSA_GNU_VTENTRY: |
| /* This relocation describes which C++ vtable entries are actually |
| used. Record for later use during GC. */ |
| if (!bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend)) |
| return false; |
| continue; |
| |
| default: |
| /* Nothing to do for any other relocations. */ |
| continue; |
| } |
| |
| if (h) |
| { |
| if (is_plt) |
| { |
| if (h->plt.refcount <= 0) |
| { |
| h->needs_plt = 1; |
| h->plt.refcount = 1; |
| } |
| else |
| h->plt.refcount += 1; |
| |
| /* Keep track of the total PLT relocation count even if we |
| don't yet know whether the dynamic sections will be |
| created. */ |
| htab->plt_reloc_count += 1; |
| |
| if (elf_hash_table (info)->dynamic_sections_created) |
| { |
| if (! add_extra_plt_sections (info, htab->plt_reloc_count)) |
| return false; |
| } |
| } |
| else if (is_got) |
| { |
| if (h->got.refcount <= 0) |
| h->got.refcount = 1; |
| else |
| h->got.refcount += 1; |
| } |
| |
| if (is_tlsfunc) |
| eh->tlsfunc_refcount += 1; |
| |
| old_tls_type = eh->tls_type; |
| } |
| else |
| { |
| /* Allocate storage the first time. */ |
| if (elf_local_got_refcounts (abfd) == NULL) |
| { |
| bfd_size_type size = symtab_hdr->sh_info; |
| void *mem; |
| |
| mem = bfd_zalloc (abfd, size * sizeof (bfd_signed_vma)); |
| if (mem == NULL) |
| return false; |
| elf_local_got_refcounts (abfd) = (bfd_signed_vma *) mem; |
| |
| mem = bfd_zalloc (abfd, size); |
| if (mem == NULL) |
| return false; |
| elf_xtensa_local_got_tls_type (abfd) = (char *) mem; |
| |
| mem = bfd_zalloc (abfd, size * sizeof (bfd_signed_vma)); |
| if (mem == NULL) |
| return false; |
| elf_xtensa_local_tlsfunc_refcounts (abfd) |
| = (bfd_signed_vma *) mem; |
| } |
| |
| /* This is a global offset table entry for a local symbol. */ |
| if (is_got || is_plt) |
| elf_local_got_refcounts (abfd) [r_symndx] += 1; |
| |
| if (is_tlsfunc) |
| elf_xtensa_local_tlsfunc_refcounts (abfd) [r_symndx] += 1; |
| |
| old_tls_type = elf_xtensa_local_got_tls_type (abfd) [r_symndx]; |
| } |
| |
| if ((old_tls_type & GOT_TLS_IE) && (tls_type & GOT_TLS_IE)) |
| tls_type |= old_tls_type; |
| /* If a TLS symbol is accessed using IE at least once, |
| there is no point to use a dynamic model for it. */ |
| else if (old_tls_type != tls_type && old_tls_type != GOT_UNKNOWN |
| && ((old_tls_type & GOT_TLS_GD) == 0 |
| || (tls_type & GOT_TLS_IE) == 0)) |
| { |
| if ((old_tls_type & GOT_TLS_IE) && (tls_type & GOT_TLS_GD)) |
| tls_type = old_tls_type; |
| else if ((old_tls_type & GOT_TLS_GD) && (tls_type & GOT_TLS_GD)) |
| tls_type |= old_tls_type; |
| else |
| { |
| _bfd_error_handler |
| /* xgettext:c-format */ |
| (_("%pB: `%s' accessed both as normal and thread local symbol"), |
| abfd, |
| h ? h->root.root.string : "<local>"); |
| return false; |
| } |
| } |
| |
| if (old_tls_type != tls_type) |
| { |
| if (eh) |
| eh->tls_type = tls_type; |
| else |
| elf_xtensa_local_got_tls_type (abfd) [r_symndx] = tls_type; |
| } |
| } |
| |
| return true; |
| } |
| |
| |
| static void |
| elf_xtensa_make_sym_local (struct bfd_link_info *info, |
| struct elf_link_hash_entry *h) |
| { |
| if (bfd_link_pic (info)) |
| { |
| if (h->plt.refcount > 0) |
| { |
| /* For shared objects, there's no need for PLT entries for local |
| symbols (use RELATIVE relocs instead of JMP_SLOT relocs). */ |
| if (h->got.refcount < 0) |
| h->got.refcount = 0; |
| h->got.refcount += h->plt.refcount; |
| h->plt.refcount = 0; |
| } |
| } |
| else |
| { |
| /* Don't need any dynamic relocations at all. */ |
| h->plt.refcount = 0; |
| h->got.refcount = 0; |
| } |
| } |
| |
| |
| static void |
| elf_xtensa_hide_symbol (struct bfd_link_info *info, |
| struct elf_link_hash_entry *h, |
| bool force_local) |
| { |
| /* For a shared link, move the plt refcount to the got refcount to leave |
| space for RELATIVE relocs. */ |
| elf_xtensa_make_sym_local (info, h); |
| |
| _bfd_elf_link_hash_hide_symbol (info, h, force_local); |
| } |
| |
| |
| /* Return the section that should be marked against GC for a given |
| relocation. */ |
| |
| static asection * |
| elf_xtensa_gc_mark_hook (asection *sec, |
| struct bfd_link_info *info, |
| Elf_Internal_Rela *rel, |
| struct elf_link_hash_entry *h, |
| Elf_Internal_Sym *sym) |
| { |
| /* Property sections are marked "KEEP" in the linker scripts, but they |
| should not cause other sections to be marked. (This approach relies |
| on elf_xtensa_discard_info to remove property table entries that |
| describe discarded sections. Alternatively, it might be more |
| efficient to avoid using "KEEP" in the linker scripts and instead use |
| the gc_mark_extra_sections hook to mark only the property sections |
| that describe marked sections. That alternative does not work well |
| with the current property table sections, which do not correspond |
| one-to-one with the sections they describe, but that should be fixed |
| someday.) */ |
| if (xtensa_is_property_section (sec)) |
| return NULL; |
| |
| if (h != NULL) |
| switch (ELF32_R_TYPE (rel->r_info)) |
| { |
| case R_XTENSA_GNU_VTINHERIT: |
| case R_XTENSA_GNU_VTENTRY: |
| return NULL; |
| } |
| |
| return _bfd_elf_gc_mark_hook (sec, info, rel, h, sym); |
| } |
| |
| |
| /* Create all the dynamic sections. */ |
| |
| static bool |
| elf_xtensa_create_dynamic_sections (bfd *dynobj, struct bfd_link_info *info) |
| { |
| struct elf_xtensa_link_hash_table *htab; |
| flagword flags, noalloc_flags; |
| |
| htab = elf_xtensa_hash_table (info); |
| if (htab == NULL) |
| return false; |
| |
| /* First do all the standard stuff. */ |
| if (! _bfd_elf_create_dynamic_sections (dynobj, info)) |
| return false; |
| |
| /* Create any extra PLT sections in case check_relocs has already |
| been called on all the non-dynamic input files. */ |
| if (! add_extra_plt_sections (info, htab->plt_reloc_count)) |
| return false; |
| |
| noalloc_flags = (SEC_HAS_CONTENTS | SEC_IN_MEMORY |
| | SEC_LINKER_CREATED | SEC_READONLY); |
| flags = noalloc_flags | SEC_ALLOC | SEC_LOAD; |
| |
| /* Mark the ".got.plt" section READONLY. */ |
| if (htab->elf.sgotplt == NULL |
| || !bfd_set_section_flags (htab->elf.sgotplt, flags)) |
| return false; |
| |
| /* Create ".got.loc" (literal tables for use by dynamic linker). */ |
| htab->sgotloc = bfd_make_section_anyway_with_flags (dynobj, ".got.loc", |
| flags); |
| if (htab->sgotloc == NULL |
| || !bfd_set_section_alignment (htab->sgotloc, 2)) |
| return false; |
| |
| /* Create ".xt.lit.plt" (literal table for ".got.plt*"). */ |
| htab->spltlittbl = bfd_make_section_anyway_with_flags (dynobj, ".xt.lit.plt", |
| noalloc_flags); |
| if (htab->spltlittbl == NULL |
| || !bfd_set_section_alignment (htab->spltlittbl, 2)) |
| return false; |
| |
| return true; |
| } |
| |
| |
| static bool |
| add_extra_plt_sections (struct bfd_link_info *info, int count) |
| { |
| bfd *dynobj = elf_hash_table (info)->dynobj; |
| int chunk; |
| |
| /* Iterate over all chunks except 0 which uses the standard ".plt" and |
| ".got.plt" sections. */ |
| for (chunk = count / PLT_ENTRIES_PER_CHUNK; chunk > 0; chunk--) |
| { |
| char *sname; |
| flagword flags; |
| asection *s; |
| |
| /* Stop when we find a section has already been created. */ |
| if (elf_xtensa_get_plt_section (info, chunk)) |
| break; |
| |
| flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY |
| | SEC_LINKER_CREATED | SEC_READONLY); |
| |
| sname = (char *) bfd_malloc (10); |
| sprintf (sname, ".plt.%u", chunk); |
| s = bfd_make_section_anyway_with_flags (dynobj, sname, flags | SEC_CODE); |
| if (s == NULL |
| || !bfd_set_section_alignment (s, 2)) |
| return false; |
| |
| sname = (char *) bfd_malloc (14); |
| sprintf (sname, ".got.plt.%u", chunk); |
| s = bfd_make_section_anyway_with_flags (dynobj, sname, flags); |
| if (s == NULL |
| || !bfd_set_section_alignment (s, 2)) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| |
| /* Adjust a symbol defined by a dynamic object and referenced by a |
| regular object. The current definition is in some section of the |
| dynamic object, but we're not including those sections. We have to |
| change the definition to something the rest of the link can |
| understand. */ |
| |
| static bool |
| elf_xtensa_adjust_dynamic_symbol (struct bfd_link_info *info ATTRIBUTE_UNUSED, |
| struct elf_link_hash_entry *h) |
| { |
| /* If this is a weak symbol, and there is a real definition, the |
| processor independent code will have arranged for us to see the |
| real definition first, and we can just use the same value. */ |
| if (h->is_weakalias) |
| { |
| struct elf_link_hash_entry *def = weakdef (h); |
| BFD_ASSERT (def->root.type == bfd_link_hash_defined); |
| h->root.u.def.section = def->root.u.def.section; |
| h->root.u.def.value = def->root.u.def.value; |
| return true; |
| } |
| |
| /* This is a reference to a symbol defined by a dynamic object. The |
| reference must go through the GOT, so there's no need for COPY relocs, |
| .dynbss, etc. */ |
| |
| return true; |
| } |
| |
| |
| static bool |
| elf_xtensa_allocate_dynrelocs (struct elf_link_hash_entry *h, void *arg) |
| { |
| struct bfd_link_info *info; |
| struct elf_xtensa_link_hash_table *htab; |
| struct elf_xtensa_link_hash_entry *eh = elf_xtensa_hash_entry (h); |
| |
| if (h->root.type == bfd_link_hash_indirect) |
| return true; |
| |
| info = (struct bfd_link_info *) arg; |
| htab = elf_xtensa_hash_table (info); |
| if (htab == NULL) |
| return false; |
| |
| /* If we saw any use of an IE model for this symbol, we can then optimize |
| away GOT entries for any TLSDESC_FN relocs. */ |
| if ((eh->tls_type & GOT_TLS_IE) != 0) |
| { |
| BFD_ASSERT (h->got.refcount >= eh->tlsfunc_refcount); |
| h->got.refcount -= eh->tlsfunc_refcount; |
| } |
| |
| if (! elf_xtensa_dynamic_symbol_p (h, info)) |
| elf_xtensa_make_sym_local (info, h); |
| |
| if (! elf_xtensa_dynamic_symbol_p (h, info) |
| && h->root.type == bfd_link_hash_undefweak) |
| return true; |
| |
| if (h->plt.refcount > 0) |
| htab->elf.srelplt->size += (h->plt.refcount * sizeof (Elf32_External_Rela)); |
| |
| if (h->got.refcount > 0) |
| htab->elf.srelgot->size += (h->got.refcount * sizeof (Elf32_External_Rela)); |
| |
| return true; |
| } |
| |
| |
| static void |
| elf_xtensa_allocate_local_got_size (struct bfd_link_info *info) |
| { |
| struct elf_xtensa_link_hash_table *htab; |
| bfd *i; |
| |
| htab = elf_xtensa_hash_table (info); |
| if (htab == NULL) |
| return; |
| |
| for (i = info->input_bfds; i; i = i->link.next) |
| { |
| bfd_signed_vma *local_got_refcounts; |
| bfd_size_type j, cnt; |
| Elf_Internal_Shdr *symtab_hdr; |
| |
| local_got_refcounts = elf_local_got_refcounts (i); |
| if (!local_got_refcounts) |
| continue; |
| |
| symtab_hdr = &elf_tdata (i)->symtab_hdr; |
| cnt = symtab_hdr->sh_info; |
| |
| for (j = 0; j < cnt; ++j) |
| { |
| /* If we saw any use of an IE model for this symbol, we can |
| then optimize away GOT entries for any TLSDESC_FN relocs. */ |
| if ((elf_xtensa_local_got_tls_type (i) [j] & GOT_TLS_IE) != 0) |
| { |
| bfd_signed_vma *tlsfunc_refcount |
| = &elf_xtensa_local_tlsfunc_refcounts (i) [j]; |
| BFD_ASSERT (local_got_refcounts[j] >= *tlsfunc_refcount); |
| local_got_refcounts[j] -= *tlsfunc_refcount; |
| } |
| |
| if (local_got_refcounts[j] > 0) |
| htab->elf.srelgot->size += (local_got_refcounts[j] |
| * sizeof (Elf32_External_Rela)); |
| } |
| } |
| } |
| |
| |
| /* Set the sizes of the dynamic sections. */ |
| |
| static bool |
| elf_xtensa_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, |
| struct bfd_link_info *info) |
| { |
| struct elf_xtensa_link_hash_table *htab; |
| bfd *dynobj, *abfd; |
| asection *s, *srelplt, *splt, *sgotplt, *srelgot, *spltlittbl, *sgotloc; |
| bool relplt, relgot; |
| int plt_entries, plt_chunks, chunk; |
| |
| plt_entries = 0; |
| plt_chunks = 0; |
| |
| htab = elf_xtensa_hash_table (info); |
| if (htab == NULL) |
| return false; |
| |
| dynobj = elf_hash_table (info)->dynobj; |
| if (dynobj == NULL) |
| abort (); |
| srelgot = htab->elf.srelgot; |
| srelplt = htab->elf.srelplt; |
| |
| if (elf_hash_table (info)->dynamic_sections_created) |
| { |
| BFD_ASSERT (htab->elf.srelgot != NULL |
| && htab->elf.srelplt != NULL |
| && htab->elf.sgot != NULL |
| && htab->spltlittbl != NULL |
| && htab->sgotloc != NULL); |
| |
| /* Set the contents of the .interp section to the interpreter. */ |
| if (bfd_link_executable (info) && !info->nointerp) |
| { |
| s = bfd_get_linker_section (dynobj, ".interp"); |
| if (s == NULL) |
| abort (); |
| s->size = sizeof ELF_DYNAMIC_INTERPRETER; |
| s->contents = (unsigned char *) ELF_DYNAMIC_INTERPRETER; |
| } |
| |
| /* Allocate room for one word in ".got". */ |
| htab->elf.sgot->size = 4; |
| |
| /* Allocate space in ".rela.got" for literals that reference global |
| symbols and space in ".rela.plt" for literals that have PLT |
| entries. */ |
| elf_link_hash_traverse (elf_hash_table (info), |
| elf_xtensa_allocate_dynrelocs, |
| (void *) info); |
| |
| /* If we are generating a shared object, we also need space in |
| ".rela.got" for R_XTENSA_RELATIVE relocs for literals that |
| reference local symbols. */ |
| if (bfd_link_pic (info)) |
| elf_xtensa_allocate_local_got_size (info); |
| |
| /* Allocate space in ".plt" to match the size of ".rela.plt". For |
| each PLT entry, we need the PLT code plus a 4-byte literal. |
| For each chunk of ".plt", we also need two more 4-byte |
| literals, two corresponding entries in ".rela.got", and an |
| 8-byte entry in ".xt.lit.plt". */ |
| spltlittbl = htab->spltlittbl; |
| plt_entries = srelplt->size / sizeof (Elf32_External_Rela); |
| plt_chunks = |
| (plt_entries + PLT_ENTRIES_PER_CHUNK - 1) / PLT_ENTRIES_PER_CHUNK; |
| |
| /* Iterate over all the PLT chunks, including any extra sections |
| created earlier because the initial count of PLT relocations |
| was an overestimate. */ |
| for (chunk = 0; |
| (splt = elf_xtensa_get_plt_section (info, chunk)) != NULL; |
| chunk++) |
| { |
| int chunk_entries; |
| |
| sgotplt = elf_xtensa_get_gotplt_section (info, chunk); |
| BFD_ASSERT (sgotplt != NULL); |
| |
| if (chunk < plt_chunks - 1) |
| chunk_entries = PLT_ENTRIES_PER_CHUNK; |
| else if (chunk == plt_chunks - 1) |
| chunk_entries = plt_entries - (chunk * PLT_ENTRIES_PER_CHUNK); |
| else |
| chunk_entries = 0; |
| |
| if (chunk_entries != 0) |
| { |
| sgotplt->size = 4 * (chunk_entries + 2); |
| splt->size = PLT_ENTRY_SIZE * chunk_entries; |
| srelgot->size += 2 * sizeof (Elf32_External_Rela); |
| spltlittbl->size += 8; |
| } |
| else |
| { |
| sgotplt->size = 0; |
| splt->size = 0; |
| } |
| } |
| |
| /* Allocate space in ".got.loc" to match the total size of all the |
| literal tables. */ |
| sgotloc = htab->sgotloc; |
| sgotloc->size = spltlittbl->size; |
| for (abfd = info->input_bfds; abfd != NULL; abfd = abfd->link.next) |
| { |
| if (abfd->flags & DYNAMIC) |
| continue; |
| for (s = abfd->sections; s != NULL; s = s->next) |
| { |
| if (! discarded_section (s) |
| && xtensa_is_littable_section (s) |
| && s != spltlittbl) |
| sgotloc->size += s->size; |
| } |
| } |
| } |
| |
| /* Allocate memory for dynamic sections. */ |
| relplt = false; |
| relgot = false; |
| for (s = dynobj->sections; s != NULL; s = s->next) |
| { |
| const char *name; |
| |
| if ((s->flags & SEC_LINKER_CREATED) == 0) |
| continue; |
| |
| /* It's OK to base decisions on the section name, because none |
| of the dynobj section names depend upon the input files. */ |
| name = bfd_section_name (s); |
| |
| if (startswith (name, ".rela")) |
| { |
| if (s->size != 0) |
| { |
| if (strcmp (name, ".rela.plt") == 0) |
| relplt = true; |
| else if (strcmp (name, ".rela.got") == 0) |
| relgot = true; |
| |
| /* We use the reloc_count field as a counter if we need |
| to copy relocs into the output file. */ |
| s->reloc_count = 0; |
| } |
| } |
| else if (! startswith (name, ".plt.") |
| && ! startswith (name, ".got.plt.") |
| && strcmp (name, ".got") != 0 |
| && strcmp (name, ".plt") != 0 |
| && strcmp (name, ".got.plt") != 0 |
| && strcmp (name, ".xt.lit.plt") != 0 |
| && strcmp (name, ".got.loc") != 0) |
| { |
| /* It's not one of our sections, so don't allocate space. */ |
| continue; |
| } |
| |
| if (s->size == 0) |
| { |
| /* If we don't need this section, strip it from the output |
| file. We must create the ".plt*" and ".got.plt*" |
| sections in create_dynamic_sections and/or check_relocs |
| based on a conservative estimate of the PLT relocation |
| count, because the sections must be created before the |
| linker maps input sections to output sections. The |
| linker does that before size_dynamic_sections, where we |
| compute the exact size of the PLT, so there may be more |
| of these sections than are actually needed. */ |
| s->flags |= SEC_EXCLUDE; |
| } |
| else if ((s->flags & SEC_HAS_CONTENTS) != 0) |
| { |
| /* Allocate memory for the section contents. */ |
| s->contents = (bfd_byte *) bfd_zalloc (dynobj, s->size); |
| if (s->contents == NULL) |
| return false; |
| } |
| } |
| |
| if (elf_hash_table (info)->dynamic_sections_created) |
| { |
| /* Add the special XTENSA_RTLD relocations now. The offsets won't be |
| known until finish_dynamic_sections, but we need to get the relocs |
| in place before they are sorted. */ |
| for (chunk = 0; chunk < plt_chunks; chunk++) |
| { |
| Elf_Internal_Rela irela; |
| bfd_byte *loc; |
| |
| irela.r_offset = 0; |
| irela.r_info = ELF32_R_INFO (0, R_XTENSA_RTLD); |
| irela.r_addend = 0; |
| |
| loc = (srelgot->contents |
| + srelgot->reloc_count * sizeof (Elf32_External_Rela)); |
| bfd_elf32_swap_reloca_out (output_bfd, &irela, loc); |
| bfd_elf32_swap_reloca_out (output_bfd, &irela, |
| loc + sizeof (Elf32_External_Rela)); |
| srelgot->reloc_count += 2; |
| } |
| |
| /* Add some entries to the .dynamic section. We fill in the |
| values later, in elf_xtensa_finish_dynamic_sections, but we |
| must add the entries now so that we get the correct size for |
| the .dynamic section. The DT_DEBUG entry is filled in by the |
| dynamic linker and used by the debugger. */ |
| #define add_dynamic_entry(TAG, VAL) \ |
| _bfd_elf_add_dynamic_entry (info, TAG, VAL) |
| |
| if (!_bfd_elf_add_dynamic_tags (output_bfd, info, |
| relplt || relgot)) |
| return false; |
| |
| if (!add_dynamic_entry (DT_XTENSA_GOT_LOC_OFF, 0) |
| || !add_dynamic_entry (DT_XTENSA_GOT_LOC_SZ, 0)) |
| return false; |
| } |
| #undef add_dynamic_entry |
| |
| return true; |
| } |
| |
| static bool |
| elf_xtensa_always_size_sections (bfd *output_bfd, |
| struct bfd_link_info *info) |
| { |
| struct elf_xtensa_link_hash_table *htab; |
| asection *tls_sec; |
| |
| htab = elf_xtensa_hash_table (info); |
| if (htab == NULL) |
| return false; |
| |
| tls_sec = htab->elf.tls_sec; |
| |
| if (tls_sec && (htab->tlsbase->tls_type & GOT_TLS_ANY) != 0) |
| { |
| struct elf_link_hash_entry *tlsbase = &htab->tlsbase->elf; |
| struct bfd_link_hash_entry *bh = &tlsbase->root; |
| const struct elf_backend_data *bed = get_elf_backend_data (output_bfd); |
| |
| tlsbase->type = STT_TLS; |
| if (!(_bfd_generic_link_add_one_symbol |
| (info, output_bfd, "_TLS_MODULE_BASE_", BSF_LOCAL, |
| tls_sec, 0, NULL, false, |
| bed->collect, &bh))) |
| return false; |
| tlsbase->def_regular = 1; |
| tlsbase->other = STV_HIDDEN; |
| (*bed->elf_backend_hide_symbol) (info, tlsbase, true); |
| } |
| |
| return true; |
| } |
| |
| |
| /* Return the base VMA address which should be subtracted from real addresses |
| when resolving @dtpoff relocation. |
| This is PT_TLS segment p_vaddr. */ |
| |
| static bfd_vma |
| dtpoff_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; |
| } |
| |
| /* Return the relocation value for @tpoff relocation |
| if STT_TLS virtual address is ADDRESS. */ |
| |
| static bfd_vma |
| tpoff (struct bfd_link_info *info, bfd_vma address) |
| { |
| struct elf_link_hash_table *htab = elf_hash_table (info); |
| bfd_vma base; |
| |
| /* If tls_sec is NULL, we should have signalled an error already. */ |
| if (htab->tls_sec == NULL) |
| return 0; |
| base = align_power ((bfd_vma) TCB_SIZE, htab->tls_sec->alignment_power); |
| return address - htab->tls_sec->vma + base; |
| } |
| |
| /* Perform the specified relocation. The instruction at (contents + address) |
| is modified to set one operand to represent the value in "relocation". The |
| operand position is determined by the relocation type recorded in the |
| howto. */ |
| |
| #define CALL_SEGMENT_BITS (30) |
| #define CALL_SEGMENT_SIZE (1 << CALL_SEGMENT_BITS) |
| |
| static bfd_reloc_status_type |
| elf_xtensa_do_reloc (reloc_howto_type *howto, |
| bfd *abfd, |
| asection *input_section, |
| bfd_vma relocation, |
| bfd_byte *contents, |
| bfd_vma address, |
| bool is_weak_undef, |
| char **error_message) |
| { |
| xtensa_format fmt; |
| xtensa_opcode opcode; |
| xtensa_isa isa = xtensa_default_isa; |
| static xtensa_insnbuf ibuff = NULL; |
| static xtensa_insnbuf sbuff = NULL; |
| bfd_vma self_address; |
| bfd_size_type input_size; |
| int opnd, slot; |
| uint32 newval; |
| |
| if (!ibuff) |
| { |
| ibuff = xtensa_insnbuf_alloc (isa); |
| sbuff = xtensa_insnbuf_alloc (isa); |
| } |
| |
| input_size = bfd_get_section_limit (abfd, input_section); |
| |
| /* Calculate the PC address for this instruction. */ |
| self_address = (input_section->output_section->vma |
| + input_section->output_offset |
| + address); |
| |
| switch (howto->type) |
| { |
| case R_XTENSA_NONE: |
| case R_XTENSA_DIFF8: |
| case R_XTENSA_DIFF16: |
| case R_XTENSA_DIFF32: |
| case R_XTENSA_PDIFF8: |
| case R_XTENSA_PDIFF16: |
| case R_XTENSA_PDIFF32: |
| case R_XTENSA_NDIFF8: |
| case R_XTENSA_NDIFF16: |
| case R_XTENSA_NDIFF32: |
| case R_XTENSA_TLS_FUNC: |
| case R_XTENSA_TLS_ARG: |
| case R_XTENSA_TLS_CALL: |
| return bfd_reloc_ok; |
| |
| case R_XTENSA_ASM_EXPAND: |
| if (!is_weak_undef) |
| { |
| /* Check for windowed CALL across a 1GB boundary. */ |
| opcode = get_expanded_call_opcode (contents + address, |
| input_size - address, 0); |
| if (is_windowed_call_opcode (opcode)) |
| { |
| if ((self_address >> CALL_SEGMENT_BITS) |
| != (relocation >> CALL_SEGMENT_BITS)) |
| { |
| *error_message = "windowed longcall crosses 1GB boundary; " |
| "return may fail"; |
| return bfd_reloc_dangerous; |
| } |
| } |
| } |
| return bfd_reloc_ok; |
| |
| case R_XTENSA_ASM_SIMPLIFY: |
| { |
| /* Convert the L32R/CALLX to CALL. */ |
| bfd_reloc_status_type retval = |
| elf_xtensa_do_asm_simplify (contents, address, input_size, |
| error_message); |
| if (retval != bfd_reloc_ok) |
| return bfd_reloc_dangerous; |
| |
| /* The CALL needs to be relocated. Continue below for that part. */ |
| address += 3; |
| self_address += 3; |
| howto = &elf_howto_table[(unsigned) R_XTENSA_SLOT0_OP ]; |
| } |
| break; |
| |
| case R_XTENSA_32: |
| { |
| bfd_vma x; |
| x = bfd_get_32 (abfd, contents + address); |
| x = x + relocation; |
| bfd_put_32 (abfd, x, contents + address); |
| } |
| return bfd_reloc_ok; |
| |
| case R_XTENSA_32_PCREL: |
| bfd_put_32 (abfd, relocation - self_address, contents + address); |
| return bfd_reloc_ok; |
| |
| case R_XTENSA_PLT: |
| case R_XTENSA_TLSDESC_FN: |
| case R_XTENSA_TLSDESC_ARG: |
| case R_XTENSA_TLS_DTPOFF: |
| case R_XTENSA_TLS_TPOFF: |
| bfd_put_32 (abfd, relocation, contents + address); |
| return bfd_reloc_ok; |
| } |
| |
| /* Only instruction slot-specific relocations handled below.... */ |
| slot = get_relocation_slot (howto->type); |
| if (slot == XTENSA_UNDEFINED) |
| { |
| *error_message = "unexpected relocation"; |
| return bfd_reloc_dangerous; |
| } |
| |
| if (input_size <= address) |
| return bfd_reloc_outofrange; |
| /* Read the instruction into a buffer and decode the opcode. */ |
| xtensa_insnbuf_from_chars (isa, ibuff, contents + address, |
| input_size - address); |
| fmt = xtensa_format_decode (isa, ibuff); |
| if (fmt == XTENSA_UNDEFINED) |
| { |
| *error_message = "cannot decode instruction format"; |
| return bfd_reloc_dangerous; |
| } |
| |
| xtensa_format_get_slot (isa, fmt, slot, ibuff, sbuff); |
| |
| opcode = xtensa_opcode_decode (isa, fmt, slot, sbuff); |
| if (opcode == XTENSA_UNDEFINED) |
| { |
| *error_message = "cannot decode instruction opcode"; |
| return bfd_reloc_dangerous; |
| } |
| |
| /* Check for opcode-specific "alternate" relocations. */ |
| if (is_alt_relocation (howto->type)) |
| { |
| if (opcode == get_l32r_opcode ()) |
| { |
| /* Handle the special-case of non-PC-relative L32R instructions. */ |
| bfd *output_bfd = input_section->output_section->owner; |
| asection *lit4_sec = bfd_get_section_by_name (output_bfd, ".lit4"); |
| if (!lit4_sec) |
| { |
| *error_message = "relocation references missing .lit4 section"; |
| return bfd_reloc_dangerous; |
| } |
| self_address = ((lit4_sec->vma & ~0xfff) |
| + 0x40000 - 3); /* -3 to compensate for do_reloc */ |
| newval = relocation; |
| opnd = 1; |
| } |
| else if (opcode == get_const16_opcode ()) |
| { |
| /* ALT used for high 16 bits. |
| Ignore 32-bit overflow. */ |
| newval = (relocation >> 16) & 0xffff; |
| opnd = 1; |
| } |
| else |
| { |
| /* No other "alternate" relocations currently defined. */ |
| *error_message = "unexpected relocation"; |
| return bfd_reloc_dangerous; |
| } |
| } |
| else /* Not an "alternate" relocation.... */ |
| { |
| if (opcode == get_const16_opcode ()) |
| { |
| newval = relocation & 0xffff; |
| opnd = 1; |
| } |
| else |
| { |
| /* ...normal PC-relative relocation.... */ |
| |
| /* Determine which operand is being relocated. */ |
| opnd = get_relocation_opnd (opcode, howto->type); |
| if (opnd == XTENSA_UNDEFINED) |
| { |
| *error_message = "unexpected relocation"; |
| return bfd_reloc_dangerous; |
| } |
| |
| if (!howto->pc_relative) |
| { |
| *error_message = "expected PC-relative relocation"; |
| return bfd_reloc_dangerous; |
| } |
| |
| newval = relocation; |
| } |
| } |
| |
| /* Apply the relocation. */ |
| if (xtensa_operand_do_reloc (isa, opcode, opnd, &newval, self_address) |
| || xtensa_operand_encode (isa, opcode, opnd, &newval) |
| || xtensa_operand_set_field (isa, opcode, opnd, fmt, slot, |
| sbuff, newval)) |
| { |
| const char *opname = xtensa_opcode_name (isa, opcode); |
| const char *msg; |
| |
| msg = "cannot encode"; |
| if (is_direct_call_opcode (opcode)) |
| { |
| if ((relocation & 0x3) != 0) |
| msg = "misaligned call target"; |
| else |
| msg = "call target out of range"; |
| } |
| else if (opcode == get_l32r_opcode ()) |
| { |
| if ((relocation & 0x3) != 0) |
| msg = "misaligned literal target"; |
| else if (is_alt_relocation (howto->type)) |
| msg = "literal target out of range (too many literals)"; |
| else if (self_address > relocation) |
| msg = "literal target out of range (try using text-section-literals)"; |
| else |
| msg = "literal placed after use"; |
| } |
| |
| *error_message = vsprint_msg (opname, ": %s", strlen (msg) + 2, msg); |
| return bfd_reloc_dangerous; |
| } |
| |
| /* Check for calls across 1GB boundaries. */ |
| if (is_direct_call_opcode (opcode) |
| && is_windowed_call_opcode (opcode)) |
| { |
| if ((self_address >> CALL_SEGMENT_BITS) |
| != (relocation >> CALL_SEGMENT_BITS)) |
| { |
| *error_message = |
| "windowed call crosses 1GB boundary; return may fail"; |
| return bfd_reloc_dangerous; |
| } |
| } |
| |
| /* Write the modified instruction back out of the buffer. */ |
| xtensa_format_set_slot (isa, fmt, slot, ibuff, sbuff); |
| xtensa_insnbuf_to_chars (isa, ibuff, contents + address, |
| input_size - address); |
| return bfd_reloc_ok; |
| } |
| |
| |
| static char * |
| vsprint_msg (const char *origmsg, const char *fmt, int arglen, ...) |
| { |
| /* To reduce the size of the memory leak, |
| we only use a single message buffer. */ |
| static bfd_size_type alloc_size = 0; |
| static char *message = NULL; |
| bfd_size_type orig_len, len = 0; |
| bool is_append; |
| va_list ap; |
| |
| va_start (ap, arglen); |
| |
| is_append = (origmsg == message); |
| |
| orig_len = strlen (origmsg); |
| len = orig_len + strlen (fmt) + arglen + 20; |
| if (len > alloc_size) |
| { |
| message = (char *) bfd_realloc_or_free (message, len); |
| alloc_size = len; |
| } |
| if (message != NULL) |
| { |
| if (!is_append) |
| memcpy (message, origmsg, orig_len); |
| vsprintf (message + orig_len, fmt, ap); |
| } |
| va_end (ap); |
| return message; |
| } |
| |
| |
| /* This function is registered as the "special_function" in the |
| Xtensa howto for handling simplify operations. |
| bfd_perform_relocation / bfd_install_relocation use it to |
| perform (install) the specified relocation. Since this replaces the code |
| in bfd_perform_relocation, it is basically an Xtensa-specific, |
| stripped-down version of bfd_perform_relocation. */ |
| |
| static bfd_reloc_status_type |
| bfd_elf_xtensa_reloc (bfd *abfd, |
| arelent *reloc_entry, |
| asymbol *symbol, |
| void *data, |
| asection *input_section, |
| bfd *output_bfd, |
| char **error_message) |
| { |
| bfd_vma relocation; |
| bfd_reloc_status_type flag; |
| bfd_size_type octets = (reloc_entry->address |
| * OCTETS_PER_BYTE (abfd, input_section)); |
| bfd_vma output_base = 0; |
| reloc_howto_type *howto = reloc_entry->howto; |
| asection *reloc_target_output_section; |
| bool is_weak_undef; |
| |
| if (!xtensa_default_isa) |
| xtensa_default_isa = xtensa_isa_init (0, 0); |
| |
| /* ELF relocs are against symbols. If we are producing relocatable |
| output, and the reloc is against an external symbol, the resulting |
| reloc will also be against the same symbol. In such a case, we |
| don't want to change anything about the way the reloc is handled, |
| since it will all be done at final link time. This test is similar |
| to what bfd_elf_generic_reloc does except that it lets relocs with |
| howto->partial_inplace go through even if the addend is non-zero. |
| (The real problem is that partial_inplace is set for XTENSA_32 |
| relocs to begin with, but that's a long story and there's little we |
| can do about it now....) */ |
| |
| if (output_bfd && (symbol->flags & BSF_SECTION_SYM) == 0) |
| { |
| reloc_entry->address += input_section->output_offset; |
| return bfd_reloc_ok; |
| } |
| |
| /* Is the address of the relocation really within the section? */ |
| if (reloc_entry->address > bfd_get_section_limit (abfd, input_section)) |
| return bfd_reloc_outofrange; |
| |
| /* Work out which section the relocation is targeted at and the |
| initial relocation command value. */ |
| |
| /* Get symbol value. (Common symbols are special.) */ |
| if (bfd_is_com_section (symbol->section)) |
| relocation = 0; |
| else |
| relocation = symbol->value; |
| |
| reloc_target_output_section = symbol->section->output_section; |
| |
| /* Convert input-section-relative symbol value to absolute. */ |
| if ((output_bfd && !howto->partial_inplace) |
| || reloc_target_output_section == NULL) |
| output_base = 0; |
| else |
| output_base = reloc_target_output_section->vma; |
| |
| relocation += output_base + symbol->section->output_offset; |
| |
| /* Add in supplied addend. */ |
| relocation += reloc_entry->addend; |
| |
| /* Here the variable relocation holds the final address of the |
| symbol we are relocating against, plus any addend. */ |
| if (output_bfd) |
| { |
| if (!howto->partial_inplace) |
| { |
| /* This is a partial relocation, and we want to apply the relocation |
| to the reloc entry rather than the raw data. Everything except |
| relocations against section symbols has already been handled |
| above. */ |
| |
| BFD_ASSERT (symbol->flags & BSF_SECTION_SYM); |
| reloc_entry->addend = relocation; |
| reloc_entry->address += input_section->output_offset; |
| return bfd_reloc_ok; |
| } |
| else |
| { |
| reloc_entry->address += input_section->output_offset; |
| reloc_entry->addend = 0; |
| } |
| } |
| |
| is_weak_undef = (bfd_is_und_section (symbol->section) |
| && (symbol->flags & BSF_WEAK) != 0); |
| flag = elf_xtensa_do_reloc (howto, abfd, input_section, relocation, |
| (bfd_byte *) data, (bfd_vma) octets, |
| is_weak_undef, error_message); |
| |
| if (flag == bfd_reloc_dangerous) |
| { |
| /* Add the symbol name to the error message. */ |
| if (! *error_message) |
| *error_message = ""; |
| *error_message = vsprint_msg (*error_message, ": (%s + 0x%lx)", |
| strlen (symbol->name) + 17, |
| symbol->name, |
| (unsigned long) reloc_entry->addend); |
| } |
| |
| return flag; |
| } |
| |
| int xtensa_abi_choice (void) |
| { |
| if (elf32xtensa_abi == XTHAL_ABI_UNDEFINED) |
| return XSHAL_ABI; |
| else |
| return elf32xtensa_abi; |
| } |
| |
| /* Set up an entry in the procedure linkage table. */ |
| |
| static bfd_vma |
| elf_xtensa_create_plt_entry (struct bfd_link_info *info, |
| bfd *output_bfd, |
| unsigned reloc_index) |
| { |
| asection *splt, *sgotplt; |
| bfd_vma plt_base, got_base; |
| bfd_vma code_offset, lit_offset, abi_offset; |
| int chunk; |
| int abi = xtensa_abi_choice (); |
| |
| chunk = reloc_index / PLT_ENTRIES_PER_CHUNK; |
| splt = elf_xtensa_get_plt_section (info, chunk); |
| sgotplt = elf_xtensa_get_gotplt_section (info, chunk); |
| BFD_ASSERT (splt != NULL && sgotplt != NULL); |
| |
| plt_base = splt->output_section->vma + splt->output_offset; |
| got_base = sgotplt->output_section->vma + sgotplt->output_offset; |
| |
| lit_offset = 8 + (reloc_index % PLT_ENTRIES_PER_CHUNK) * 4; |
| code_offset = (reloc_index % PLT_ENTRIES_PER_CHUNK) * PLT_ENTRY_SIZE; |
| |
| /* Fill in the literal entry. This is the offset of the dynamic |
| relocation entry. */ |
| bfd_put_32 (output_bfd, reloc_index * sizeof (Elf32_External_Rela), |
| sgotplt->contents + lit_offset); |
| |
| /* Fill in the entry in the procedure linkage table. */ |
| memcpy (splt->contents + code_offset, |
| (bfd_big_endian (output_bfd) |
| ? elf_xtensa_be_plt_entry[abi != XTHAL_ABI_WINDOWED] |
| : elf_xtensa_le_plt_entry[abi != XTHAL_ABI_WINDOWED]), |
| PLT_ENTRY_SIZE); |
| abi_offset = abi == XTHAL_ABI_WINDOWED ? 3 : 0; |
| bfd_put_16 (output_bfd, l32r_offset (got_base + 0, |
| plt_base + code_offset + abi_offset), |
| splt->contents + code_offset + abi_offset + 1); |
| bfd_put_16 (output_bfd, l32r_offset (got_base + 4, |
| plt_base + code_offset + abi_offset + 3), |
| splt->contents + code_offset + abi_offset + 4); |
| bfd_put_16 (output_bfd, l32r_offset (got_base + lit_offset, |
| plt_base + code_offset + abi_offset + 6), |
| splt->contents + code_offset + abi_offset + 7); |
| |
| return plt_base + code_offset; |
| } |
| |
| |
| static bool get_indirect_call_dest_reg (xtensa_opcode, unsigned *); |
| |
| static bool |
| replace_tls_insn (Elf_Internal_Rela *rel, |
| bfd *abfd, |
| asection *input_section, |
| bfd_byte *contents, |
| bool is_ld_model, |
| char **error_message) |
| { |
| static xtensa_insnbuf ibuff = NULL; |
| static xtensa_insnbuf sbuff = NULL; |
| xtensa_isa isa = xtensa_default_isa; |
| xtensa_format fmt; |
| xtensa_opcode old_op, new_op; |
| bfd_size_type input_size; |
| int r_type; |
| unsigned dest_reg, src_reg; |
| |
| if (ibuff == NULL) |
| { |
| ibuff = xtensa_insnbuf_alloc (isa); |
| sbuff = xtensa_insnbuf_alloc (isa); |
| } |
| |
| input_size = bfd_get_section_limit (abfd, input_section); |
| |
| /* Read the instruction into a buffer and decode the opcode. */ |
| xtensa_insnbuf_from_chars (isa, ibuff, contents + rel->r_offset, |
| input_size - rel->r_offset); |
| fmt = xtensa_format_decode (isa, ibuff); |
| if (fmt == XTENSA_UNDEFINED) |
| { |
| *error_message = "cannot decode instruction format"; |
| return false; |
| } |
| |
| BFD_ASSERT (xtensa_format_num_slots (isa, fmt) == 1); |
| xtensa_format_get_slot (isa, fmt, 0, ibuff, sbuff); |
| |
| old_op = xtensa_opcode_decode (isa, fmt, 0, sbuff); |
| if (old_op == XTENSA_UNDEFINED) |
| { |
| *error_message = "cannot decode instruction opcode"; |
| return false; |
| } |
| |
| r_type = ELF32_R_TYPE (rel->r_info); |
| switch (r_type) |
| { |
| case R_XTENSA_TLS_FUNC: |
| case R_XTENSA_TLS_ARG: |
| if (old_op != get_l32r_opcode () |
| || xtensa_operand_get_field (isa, old_op, 0, fmt, 0, |
| sbuff, &dest_reg) != 0) |
| { |
| *error_message = "cannot extract L32R destination for TLS access"; |
| return false; |
| } |
| break; |
| |
| case R_XTENSA_TLS_CALL: |
| if (! get_indirect_call_dest_reg (old_op, &dest_reg) |
| || xtensa_operand_get_field (isa, old_op, 0, fmt, 0, |
| sbuff, &src_reg) != 0) |
| { |
| *error_message = "cannot extract CALLXn operands for TLS access"; |
| return false; |
| } |
| break; |
| |
| default: |
| abort (); |
| } |
| |
| if (is_ld_model) |
| { |
| switch (r_type) |
| { |
| case R_XTENSA_TLS_FUNC: |
| case R_XTENSA_TLS_ARG: |
| /* Change the instruction to a NOP (or "OR a1, a1, a1" for older |
| versions of Xtensa). */ |
| new_op = xtensa_opcode_lookup (isa, "nop"); |
| if (new_op == XTENSA_UNDEFINED) |
| { |
| new_op = xtensa_opcode_lookup (isa, "or"); |
| if (new_op == XTENSA_UNDEFINED |
| || xtensa_opcode_encode (isa, fmt, 0, sbuff, new_op) != 0 |
| || xtensa_operand_set_field (isa, new_op, 0, fmt, 0, |
| sbuff, 1) != 0 |
| || xtensa_operand_set_field (isa, new_op, 1, fmt, 0, |
| sbuff, 1) != 0 |
| || xtensa_operand_set_field (isa, new_op, 2, fmt, 0, |
| sbuff, 1) != 0) |
| { |
| *error_message = "cannot encode OR for TLS access"; |
| return false; |
| } |
| } |
| else |
| { |
| if (xtensa_opcode_encode (isa, fmt, 0, sbuff, new_op) != 0) |
| { |
| *error_message = "cannot encode NOP for TLS access"; |
| return false; |
| } |
| } |
| break; |
| |
| case R_XTENSA_TLS_CALL: |
| /* Read THREADPTR into the CALLX's return value register. */ |
| new_op = xtensa_opcode_lookup (isa, "rur.threadptr"); |
| if (new_op == XTENSA_UNDEFINED |
| || xtensa_opcode_encode (isa, fmt, 0, sbuff, new_op) != 0 |
| || xtensa_operand_set_field (isa, new_op, 0, fmt, 0, |
| sbuff, dest_reg + 2) != 0) |
| { |
| *error_message = "cannot encode RUR.THREADPTR for TLS access"; |
| return false; |
| } |
| break; |
| } |
| } |
| else |
| { |
| switch (r_type) |
| { |
| case R_XTENSA_TLS_FUNC: |
| new_op = xtensa_opcode_lookup (isa, "rur.threadptr"); |
| if (new_op == XTENSA_UNDEFINED |
| || xtensa_opcode_encode (isa, fmt, 0, sbuff, new_op) != 0 |
| || xtensa_operand_set_field (isa, new_op, 0, fmt, 0, |
| sbuff, dest_reg) != 0) |
| { |
| *error_message = "cannot encode RUR.THREADPTR for TLS access"; |
| return false; |
| } |
| break; |
| |
| case R_XTENSA_TLS_ARG: |
| /* Nothing to do. Keep the original L32R instruction. */ |
| return true; |
| |
| case R_XTENSA_TLS_CALL: |
| /* Add the CALLX's src register (holding the THREADPTR value) |
| to the first argument register (holding the offset) and put |
| the result in the CALLX's return value register. */ |
| new_op = xtensa_opcode_lookup (isa, "add"); |
| if (new_op == XTENSA_UNDEFINED |
| || xtensa_opcode_encode (isa, fmt, 0, sbuff, new_op) != 0 |
| || xtensa_operand_set_field (isa, new_op, 0, fmt, 0, |
| sbuff, dest_reg + 2) != 0 |
| || xtensa_operand_set_field (isa, new_op, 1, fmt, 0, |
| sbuff, dest_reg + 2) != 0 |
| || xtensa_operand_set_field (isa, new_op, 2, fmt, 0, |
| sbuff, src_reg) != 0) |
| { |
| *error_message = "cannot encode ADD for TLS access"; |
| return false; |
| } |
| break; |
| } |
| } |
| |
| xtensa_format_set_slot (isa, fmt, 0, ibuff, sbuff); |
| xtensa_insnbuf_to_chars (isa, ibuff, contents + rel->r_offset, |
| input_size - rel->r_offset); |
| |
| return true; |
| } |
| |
| |
| #define IS_XTENSA_TLS_RELOC(R_TYPE) \ |
| ((R_TYPE) == R_XTENSA_TLSDESC_FN \ |
| || (R_TYPE) == R_XTENSA_TLSDESC_ARG \ |
| || (R_TYPE) == R_XTENSA_TLS_DTPOFF \ |
| || (R_TYPE) == R_XTENSA_TLS_TPOFF \ |
| || (R_TYPE) == R_XTENSA_TLS_FUNC \ |
| || (R_TYPE) == R_XTENSA_TLS_ARG \ |
| || (R_TYPE) == R_XTENSA_TLS_CALL) |
| |
| /* Relocate an Xtensa ELF section. This is invoked by the linker for |
| both relocatable and final links. */ |
| |
| static int |
| elf_xtensa_relocate_section (bfd *output_bfd, |
| struct bfd_link_info *info, |
| bfd *input_bfd, |
| asection *input_section, |
| bfd_byte *contents, |
| Elf_Internal_Rela *relocs, |
| Elf_Internal_Sym *local_syms, |
| asection **local_sections) |
| { |
| struct elf_xtensa_link_hash_table *htab; |
| Elf_Internal_Shdr *symtab_hdr; |
| Elf_Internal_Rela *rel; |
| Elf_Internal_Rela *relend; |
| struct elf_link_hash_entry **sym_hashes; |
| property_table_entry *lit_table = 0; |
| int ltblsize = 0; |
| char *local_got_tls_types; |
| char *error_message = NULL; |
| bfd_size_type input_size; |
| int tls_type; |
| |
| if (!xtensa_default_isa) |
| xtensa_default_isa = xtensa_isa_init (0, 0); |
| |
| if (!is_xtensa_elf (input_bfd)) |
| { |
| bfd_set_error (bfd_error_wrong_format); |
| return false; |
| } |
| |
| htab = elf_xtensa_hash_table (info); |
| if (htab == NULL) |
| return false; |
| |
| symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr; |
| sym_hashes = elf_sym_hashes (input_bfd); |
| local_got_tls_types = elf_xtensa_local_got_tls_type (input_bfd); |
| |
| if (elf_hash_table (info)->dynamic_sections_created) |
| { |
| ltblsize = xtensa_read_table_entries (input_bfd, input_section, |
| &lit_table, XTENSA_LIT_SEC_NAME, |
| true); |
| if (ltblsize < 0) |
| return false; |
| } |
| |
| input_size = bfd_get_section_limit (input_bfd, input_section); |
| |
| rel = relocs; |
| relend = relocs + input_section->reloc_count; |
| for (; rel < relend; rel++) |
| { |
| int r_type; |
| reloc_howto_type *howto; |
| unsigned long r_symndx; |
| struct elf_link_hash_entry *h; |
| Elf_Internal_Sym *sym; |
| char sym_type; |
| const char *name; |
| asection *sec; |
| bfd_vma relocation; |
| bfd_reloc_status_type r; |
| bool is_weak_undef; |
| bool unresolved_reloc; |
| bool warned; |
| bool dynamic_symbol; |
| |
| r_type = ELF32_R_TYPE (rel->r_info); |
| if (r_type == (int) R_XTENSA_GNU_VTINHERIT |
| || r_type == (int) R_XTENSA_GNU_VTENTRY) |
| continue; |
| |
| if (r_type < 0 || r_type >= (int) R_XTENSA_max) |
| { |
| bfd_set_error (bfd_error_bad_value); |
| return false; |
| } |
| howto = &elf_howto_table[r_type]; |
| |
| r_symndx = ELF32_R_SYM (rel->r_info); |
| |
| h = NULL; |
| sym = NULL; |
| sec = NULL; |
| is_weak_undef = false; |
| unresolved_reloc = false; |
| warned = false; |
| |
| if (howto->partial_inplace && !bfd_link_relocatable (info)) |
| { |
| /* Because R_XTENSA_32 was made partial_inplace to fix some |
| problems with DWARF info in partial links, there may be |
| an addend stored in the contents. Take it out of there |
| and move it back into the addend field of the reloc. */ |
| rel->r_addend += bfd_get_32 (input_bfd, contents + rel->r_offset); |
| bfd_put_32 (input_bfd, 0, contents + rel->r_offset); |
| } |
| |
| if (r_symndx < symtab_hdr->sh_info) |
| { |
| sym = local_syms + r_symndx; |
| sym_type = ELF32_ST_TYPE (sym->st_info); |
| sec = local_sections[r_symndx]; |
| relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel); |
| } |
| else |
| { |
| bool ignored; |
| |
| RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel, |
| r_symndx, symtab_hdr, sym_hashes, |
| h, sec, relocation, |
| unresolved_reloc, warned, ignored); |
| |
| if (relocation == 0 |
| && !unresolved_reloc |
| && h->root.type == bfd_link_hash_undefweak) |
| is_weak_undef = true; |
| |
| sym_type = h->type; |
| } |
| |
| if (sec != NULL && discarded_section (sec)) |
| RELOC_AGAINST_DISCARDED_SECTION (info, input_bfd, input_section, |
| rel, 1, relend, howto, 0, contents); |
| |
| if (bfd_link_relocatable (info)) |
| { |
| bfd_vma dest_addr; |
| asection * sym_sec = get_elf_r_symndx_section (input_bfd, r_symndx); |
| |
| /* This is a relocatable link. |
| 1) If the reloc is against a section symbol, adjust |
| according to the output section. |
| 2) If there is a new target for this relocation, |
| the new target will be in the same output section. |
| We adjust the relocation by the output section |
| difference. */ |
| |
| if (relaxing_section) |
| { |
| /* Check if this references a section in another input file. */ |
| if (!do_fix_for_relocatable_link (rel, input_bfd, input_section, |
| contents)) |
| return false; |
| } |
| |
| dest_addr = sym_sec->output_section->vma + sym_sec->output_offset |
| + get_elf_r_symndx_offset (input_bfd, r_symndx) + rel->r_addend; |
| |
| if (r_type == R_XTENSA_ASM_SIMPLIFY) |
| { |
| error_message = NULL; |
| /* Convert ASM_SIMPLIFY into the simpler relocation |
| so that they never escape a relaxing link. */ |
| r = contract_asm_expansion (contents, input_size, rel, |
| &error_message); |
| if (r != bfd_reloc_ok) |
| (*info->callbacks->reloc_dangerous) |
| (info, error_message, |
| input_bfd, input_section, rel->r_offset); |
| |
| r_type = ELF32_R_TYPE (rel->r_info); |
| } |
| |
| /* This is a relocatable link, so we don't have to change |
| anything unless the reloc is against a section symbol, |
| in which case we have to adjust according to where the |
| section symbol winds up in the output section. */ |
| if (r_symndx < symtab_hdr->sh_info) |
| { |
| sym = local_syms + r_symndx; |
| if (ELF_ST_TYPE (sym->st_info) == STT_SECTION) |
| { |
| sec = local_sections[r_symndx]; |
| rel->r_addend += sec->output_offset + sym->st_value; |
| } |
| } |
| |
| /* If there is an addend with a partial_inplace howto, |
| then move the addend to the contents. This is a hack |
| to work around problems with DWARF in relocatable links |
| with some previous version of BFD. Now we can't easily get |
| rid of the hack without breaking backward compatibility.... */ |
| r = bfd_reloc_ok; |
| howto = &elf_howto_table[r_type]; |
| if (howto->partial_inplace && rel->r_addend) |
| { |
| r = elf_xtensa_do_reloc (howto, input_bfd, input_section, |
| rel->r_addend, contents, |
| rel->r_offset, false, |
| &error_message); |
| rel->r_addend = 0; |
| } |
| else |
| { |
| /* Put the correct bits in the target instruction, even |
| though the relocation will still be present in the output |
| file. This makes disassembly clearer, as well as |
| allowing loadable kernel modules to work without needing |
| relocations on anything other than calls and l32r's. */ |
| |
| /* If it is not in the same section, there is nothing we can do. */ |
| if (r_type >= R_XTENSA_SLOT0_OP && r_type <= R_XTENSA_SLOT14_OP && |
| sym_sec->output_section == input_section->output_section) |
| { |
| r = elf_xtensa_do_reloc (howto, input_bfd, input_section, |
| dest_addr, contents, |
| rel->r_offset, false, |
| &error_message); |
| } |
| } |
| if (r != bfd_reloc_ok) |
| (*info->callbacks->reloc_dangerous) |
| (info, error_message, |
| input_bfd, input_section, rel->r_offset); |
| |
| /* Done with work for relocatable link; continue with next reloc. */ |
| continue; |
| } |
| |
| /* This is a final link. */ |
| |
| if (relaxing_section) |
| { |
| /* Check if this references a section in another input file. */ |
| do_fix_for_final_link (rel, input_bfd, input_section, contents, |
| &relocation); |
| } |
| |
| /* Sanity check the address. */ |
| if (rel->r_offset >= input_size |
| && ELF32_R_TYPE (rel->r_info) != R_XTENSA_NONE) |
| { |
| _bfd_error_handler |
| /* xgettext:c-format */ |
| (_("%pB(%pA+%#" PRIx64 "): " |
| "relocation offset out of range (size=%#" PRIx64 ")"), |
| input_bfd, input_section, (uint64_t) rel->r_offset, |
| (uint64_t) input_size); |
| bfd_set_error (bfd_error_bad_value); |
| return false; |
| } |
| |
| if (h != NULL) |
| name = h->root.root.string; |
| else |
| { |
| name = (bfd_elf_string_from_elf_section |
| (input_bfd, symtab_hdr->sh_link, sym->st_name)); |
| if (name == NULL || *name == '\0') |
| name = bfd_section_name (sec); |
| } |
| |
| if (r_symndx != STN_UNDEF |
| && r_type != R_XTENSA_NONE |
| && (h == NULL |
| || h->root.type == bfd_link_hash_defined |
| || h->root.type == bfd_link_hash_defweak) |
| && IS_XTENSA_TLS_RELOC (r_type) != (sym_type == STT_TLS)) |
| { |
| _bfd_error_handler |
| ((sym_type == STT_TLS |
| /* xgettext:c-format */ |
| ? _("%pB(%pA+%#" PRIx64 "): %s used with TLS symbol %s") |
| /* xgettext:c-format */ |
| : _("%pB(%pA+%#" PRIx64 "): %s used with non-TLS symbol %s")), |
| input_bfd, |
| input_section, |
| (uint64_t) rel->r_offset, |
| howto->name, |
| name); |
| } |
| |
| dynamic_symbol = elf_xtensa_dynamic_symbol_p (h, info); |
| |
| tls_type = GOT_UNKNOWN; |
| if (h) |
| tls_type = elf_xtensa_hash_entry (h)->tls_type; |
| else if (local_got_tls_types) |
| tls_type = local_got_tls_types [r_symndx]; |
| |
| switch (r_type) |
| { |
| case R_XTENSA_32: |
| case R_XTENSA_PLT: |
| if (elf_hash_table (info)->dynamic_sections_created |
| && (input_section->flags & SEC_ALLOC) != 0 |
| && (dynamic_symbol || bfd_link_pic (info))) |
| { |
| Elf_Internal_Rela outrel; |
| bfd_byte *loc; |
| asection *srel; |
| |
| if (dynamic_symbol && r_type == R_XTENSA_PLT) |
| srel = htab->elf.srelplt; |
| else |
| srel = htab->elf.srelgot; |
| |
| BFD_ASSERT (srel != NULL); |
| |
| outrel.r_offset = |
| _bfd_elf_section_offset (output_bfd, info, |
| input_section, rel->r_offset); |
| |
| if ((outrel.r_offset | 1) == (bfd_vma) -1) |
| memset (&outrel, 0, sizeof outrel); |
| else |
| { |
| outrel.r_offset += (input_section->output_section->vma |
| + input_section->output_offset); |
| |
| /* Complain if the relocation is in a read-only section |
| and not in a literal pool. */ |
| if ((input_section->flags & SEC_READONLY) != 0 |
| && !elf_xtensa_in_literal_pool (lit_table, ltblsize, |
| outrel.r_offset)) |
| { |
| error_message = |
| _("dynamic relocation in read-only section"); |
| (*info->callbacks->reloc_dangerous) |
| (info, error_message, |
| input_bfd, input_section, rel->r_offset); |
| } |
| |
| if (dynamic_symbol) |
| { |
| outrel.r_addend = rel->r_addend; |
| rel->r_addend = 0; |
| |
| if (r_type == R_XTENSA_32) |
| { |
| outrel.r_info = |
| ELF32_R_INFO (h->dynindx, R_XTENSA_GLOB_DAT); |
| relocation = 0; |
| } |
| else /* r_type == R_XTENSA_PLT */ |
| { |
| outrel.r_info = |
| ELF32_R_INFO (h->dynindx, R_XTENSA_JMP_SLOT); |
| |
| /* Create the PLT entry and set the initial |
| contents of the literal entry to the address of |
| the PLT entry. */ |
| relocation = |
| elf_xtensa_create_plt_entry (info, output_bfd, |
| srel->reloc_count); |
| } |
| unresolved_reloc = false; |
| } |
| else if (!is_weak_undef) |
| { |
| /* Generate a RELATIVE relocation. */ |
| outrel.r_info = ELF32_R_INFO (0, R_XTENSA_RELATIVE); |
| outrel.r_addend = 0; |
| } |
| else |
| { |
| continue; |
| } |
| } |
| |
| loc = (srel->contents |
| + srel->reloc_count++ * sizeof (Elf32_External_Rela)); |
| bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc); |
| BFD_ASSERT (sizeof (Elf32_External_Rela) * srel->reloc_count |
| <= srel->size); |
| } |
| else if (r_type == R_XTENSA_ASM_EXPAND && dynamic_symbol) |
| { |
| /* This should only happen for non-PIC code, which is not |
| supposed to be used on systems with dynamic linking. |
| Just ignore these relocations. */ |
| continue; |
| } |
| break; |
| |
| case R_XTENSA_TLS_TPOFF: |
| /* Switch to LE model for local symbols in an executable. */ |
| if (! bfd_link_pic (info) && ! dynamic_symbol) |
| { |
| relocation = tpoff (info, relocation); |
| break; |
| } |
| /* fall through */ |
| |
| case R_XTENSA_TLSDESC_FN: |
| case R_XTENSA_TLSDESC_ARG: |
| { |
| if (r_type == R_XTENSA_TLSDESC_FN) |
| { |
| if (! bfd_link_pic (info) || (tls_type & GOT_TLS_IE) != 0) |
| r_type = R_XTENSA_NONE; |
| } |
| else if (r_type == R_XTENSA_TLSDESC_ARG) |
| { |
| if (bfd_link_pic (info)) |
| { |
| if ((tls_type & GOT_TLS_IE) != 0) |
| r_type = R_XTENSA_TLS_TPOFF; |
| } |
| else |
| { |
| r_type = R_XTENSA_TLS_TPOFF; |
| if (! dynamic_symbol) |
| { |
| relocation = tpoff (info, relocation); |
| break; |
| } |
| } |
| } |
| |
| if (r_type == R_XTENSA_NONE) |
| /* Nothing to do here; skip to the next reloc. */ |
| continue; |
| |
| if (! elf_hash_table (info)->dynamic_sections_created) |
| { |
| error_message = |
| _("TLS relocation invalid without dynamic sections"); |
| (*info->callbacks->reloc_dangerous) |
| (info, error_message, |
| input_bfd, input_section, rel->r_offset); |
| } |
| else |
| { |
| Elf_Internal_Rela outrel; |
| bfd_byte *loc; |
| asection *srel = htab->elf.srelgot; |
| int indx; |
| |
| outrel.r_offset = (input_section->output_section->vma |
| + input_section->output_offset |
| + rel->r_offset); |
| |
| /* Complain if the relocation is in a read-only section |
| and not in a literal pool. */ |
| if ((input_section->flags & SEC_READONLY) != 0 |
| && ! elf_xtensa_in_literal_pool (lit_table, ltblsize, |
| outrel.r_offset)) |
| { |
| error_message = |
| _("dynamic relocation in read-only section"); |
| (*info->callbacks->reloc_dangerous) |
| (info, error_message, |
| input_bfd, input_section, rel->r_offset); |
| } |
| |
| indx = h && h->dynindx != -1 ? h->dynindx : 0; |
| if (indx == 0) |
| outrel.r_addend = relocation - dtpoff_base (info); |
| else |
| outrel.r_addend = 0; |
| rel->r_addend = 0; |
| |
| outrel.r_info = ELF32_R_INFO (indx, r_type); |
| relocation = 0; |
| unresolved_reloc = false; |
| |
| BFD_ASSERT (srel); |
| loc = (srel->contents |
| + srel->reloc_count++ * sizeof (Elf32_External_Rela)); |
| bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc); |
| BFD_ASSERT (sizeof (Elf32_External_Rela) * srel->reloc_count |
| <= srel->size); |
| } |
| } |
| break; |
| |
| case R_XTENSA_TLS_DTPOFF: |
| if (! bfd_link_pic (info)) |
| /* Switch from LD model to LE model. */ |
| relocation = tpoff (info, relocation); |
| else |
| relocation -= dtpoff_base (info); |
| break; |
| |
| case R_XTENSA_TLS_FUNC: |
| case R_XTENSA_TLS_ARG: |
| case R_XTENSA_TLS_CALL: |
| /* Check if optimizing to IE or LE model. */ |
| if ((tls_type & GOT_TLS_IE) != 0) |
| { |
| bool is_ld_model = |
| (h && elf_xtensa_hash_entry (h) == htab->tlsbase); |
|