X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=bfd%2Felf32-xtensa.c;h=473a9d76f289e077ff317540584fa0da190e699f;hb=4b3ecb3b91b1b6154a6444efdcbadb90854a6654;hp=0bcd36438304fba756fc7bc0442572c6b250d5a8;hpb=c456f0829fef883581694547d21b7183cdbaa3d6;p=deliverable%2Fbinutils-gdb.git diff --git a/bfd/elf32-xtensa.c b/bfd/elf32-xtensa.c index 0bcd364383..473a9d76f2 100644 --- a/bfd/elf32-xtensa.c +++ b/bfd/elf32-xtensa.c @@ -1,11 +1,11 @@ /* Xtensa-specific support for 32-bit ELF. - Copyright 2003, 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 2003-2020 Free Software Foundation, Inc. This file is part of BFD, the Binary File Descriptor library. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the + 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 @@ -18,8 +18,8 @@ Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -#include "bfd.h" #include "sysdep.h" +#include "bfd.h" #include #include @@ -28,15 +28,19 @@ #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 /* Local helper functions. */ -static bfd_boolean add_extra_plt_sections (bfd *, int); -static char *build_encoding_error_message (xtensa_opcode, bfd_vma); +static bfd_boolean 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 bfd_boolean do_fix_for_relocatable_link @@ -62,6 +66,8 @@ static bfd_boolean is_alt_relocation (int); static bfd_boolean 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 bfd_boolean check_branch_target_aligned @@ -94,8 +100,8 @@ static Elf_Internal_Sym *retrieve_local_syms (bfd *); /* Miscellaneous utility functions. */ -static asection *elf_xtensa_get_plt_section (bfd *, int); -static asection *elf_xtensa_get_gotplt_section (bfd *, int); +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); @@ -103,10 +109,12 @@ static bfd_vma get_elf_r_symndx_offset (bfd *, unsigned long); static bfd_boolean is_reloc_sym_weak (bfd *, Elf_Internal_Rela *); static bfd_boolean pcrel_reloc_fits (xtensa_opcode, int, bfd_vma, bfd_vma); static bfd_boolean xtensa_is_property_section (asection *); +static bfd_boolean xtensa_is_insntable_section (asection *); static bfd_boolean xtensa_is_littable_section (asection *); +static bfd_boolean xtensa_is_proptable_section (asection *); static int internal_reloc_compare (const void *, const void *); static int internal_reloc_matches (const void *, const void *); -extern char *xtensa_get_property_section_name (asection *, const char *); +static asection *xtensa_get_property_section (asection *, const char *); static flagword xtensa_get_property_predef_flags (asection *); /* Other functions called directly by the linker. */ @@ -132,16 +140,6 @@ int elf32xtensa_size_opt; typedef struct xtensa_relax_info_struct xtensa_relax_info; -/* 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. */ - -static int plt_reloc_count = 0; - - /* 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 @@ -161,168 +159,172 @@ static bfd_boolean relaxing_section = FALSE; int elf32xtensa_no_literal_movement = 1; +/* Place property records for a section into individual property section + with xt.prop. prefix. */ + +bfd_boolean elf32xtensa_separate_props = FALSE; + +/* 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, 0, 0, FALSE, 0, complain_overflow_dont, + HOWTO (R_XTENSA_NONE, 0, 3, 0, FALSE, 0, complain_overflow_dont, bfd_elf_xtensa_reloc, "R_XTENSA_NONE", - FALSE, 0x00000000, 0x00000000, FALSE), + 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, 0x00000000, 0x00000000, FALSE), + 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, 0xffffffff, 0xffffffff, FALSE), + 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, 0xffffffff, 0xffffffff, FALSE), + FALSE, 0, 0xffffffff, FALSE), HOWTO (R_XTENSA_RELATIVE, 0, 2, 32, FALSE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_XTENSA_RELATIVE", - FALSE, 0xffffffff, 0xffffffff, FALSE), + FALSE, 0, 0xffffffff, FALSE), HOWTO (R_XTENSA_PLT, 0, 2, 32, FALSE, 0, complain_overflow_bitfield, bfd_elf_xtensa_reloc, "R_XTENSA_PLT", - FALSE, 0xffffffff, 0xffffffff, FALSE), + 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, 0x00000000, 0x00000000, TRUE), + 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, 0x00000000, 0x00000000, TRUE), + 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, 0x00000000, 0x00000000, TRUE), + 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, 0x00000000, 0x00000000, FALSE), + 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, 0x00000000, 0x00000000, TRUE), + bfd_elf_xtensa_reloc, "R_XTENSA_ASM_SIMPLIFY", FALSE, 0, 0, TRUE), + EMPTY_HOWTO (13), - EMPTY_HOWTO (14), + + 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, 0x00000000, 0x00000000, FALSE), + 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, 0x00000000, 0x00000000, FALSE), + _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_bitfield, - bfd_elf_xtensa_reloc, "R_XTENSA_DIFF8", - FALSE, 0xffffffff, 0xffffffff, FALSE), - HOWTO (R_XTENSA_DIFF16, 0, 1, 16, FALSE, 0, complain_overflow_bitfield, - bfd_elf_xtensa_reloc, "R_XTENSA_DIFF16", - FALSE, 0xffffffff, 0xffffffff, FALSE), - HOWTO (R_XTENSA_DIFF32, 0, 2, 32, FALSE, 0, complain_overflow_bitfield, - bfd_elf_xtensa_reloc, "R_XTENSA_DIFF32", - FALSE, 0xffffffff, 0xffffffff, FALSE), + 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, 0x00000000, 0x00000000, TRUE), + 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, 0x00000000, 0x00000000, TRUE), + 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, 0x00000000, 0x00000000, TRUE), + 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, 0x00000000, 0x00000000, TRUE), + 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, 0x00000000, 0x00000000, TRUE), + 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, 0x00000000, 0x00000000, TRUE), + 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, 0x00000000, 0x00000000, TRUE), + 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, 0x00000000, 0x00000000, TRUE), + 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, 0x00000000, 0x00000000, TRUE), + 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, 0x00000000, 0x00000000, TRUE), + 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, 0x00000000, 0x00000000, TRUE), + 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, 0x00000000, 0x00000000, TRUE), + 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, 0x00000000, 0x00000000, TRUE), + 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, 0x00000000, 0x00000000, TRUE), + 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, 0x00000000, 0x00000000, TRUE), + 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, 0x00000000, 0x00000000, TRUE), + 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, 0x00000000, 0x00000000, TRUE), + 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, 0x00000000, 0x00000000, TRUE), + 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, 0x00000000, 0x00000000, TRUE), + 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, 0x00000000, 0x00000000, TRUE), + 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, 0x00000000, 0x00000000, TRUE), + 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, 0x00000000, 0x00000000, TRUE), + 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, 0x00000000, 0x00000000, TRUE), + 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, 0x00000000, 0x00000000, TRUE), + 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, 0x00000000, 0x00000000, TRUE), + 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, 0x00000000, 0x00000000, TRUE), + 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, 0x00000000, 0x00000000, TRUE), + 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, 0x00000000, 0x00000000, TRUE), + 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, 0x00000000, 0x00000000, TRUE), + 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, 0x00000000, 0x00000000, TRUE) + 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), }; #if DEBUG_GEN_RELOC @@ -346,6 +348,10 @@ elf_xtensa_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED, 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 ]; @@ -406,6 +412,34 @@ elf_xtensa_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED, 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) @@ -426,23 +460,48 @@ elf_xtensa_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED, 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 void -elf_xtensa_info_to_howto_rela (bfd *abfd ATTRIBUTE_UNUSED, +static bfd_boolean +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); - BFD_ASSERT (r_type < (unsigned int) R_XTENSA_max); + 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; } @@ -476,29 +535,214 @@ elf_xtensa_info_to_howto_rela (bfd *abfd ATTRIBUTE_UNUSED, 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] = +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 { - 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 */ + struct elf_obj_tdata root; + + /* tls_type for each local got entry. */ + char *local_got_tls_type; + + bfd_signed_vma *local_tlsfunc_refcounts; }; -static const bfd_byte elf_xtensa_le_plt_entry[PLT_ENTRY_SIZE] = +#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 bfd_boolean +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 { - 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 */ + 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) \ + (elf_hash_table_id ((struct elf_link_hash_table *) ((p)->hash)) \ + == 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->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 bfd_boolean -xtensa_elf_dynamic_symbol_p (struct elf_link_hash_entry *h, +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 @@ -531,7 +775,7 @@ property_table_compare (const void *ap, const void *bp) != 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) @@ -563,7 +807,7 @@ property_table_matches (const void *ap, const void *bp) section. Sets TABLE_P and returns the number of entries. On error, returns a negative value. */ -static int +int xtensa_read_table_entries (bfd *abfd, asection *section, property_table_entry **table_p, @@ -571,16 +815,15 @@ xtensa_read_table_entries (bfd *abfd, bfd_boolean output_addr) { asection *table_section; - char *table_section_name; 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; - bfd_vma section_addr; + Elf_Internal_Rela *internal_relocs, *irel, *rel_end; + bfd_vma section_addr, off; flagword predef_flags; - bfd_size_type table_entry_size; + bfd_size_type table_entry_size, section_limit; if (!section || !(section->flags & SEC_ALLOC) @@ -590,13 +833,11 @@ xtensa_read_table_entries (bfd *abfd, return 0; } - table_section_name = xtensa_get_property_section_name (section, sec_name); - table_section = bfd_get_section_by_name (abfd, table_section_name); - free (table_section_name); + table_section = xtensa_get_property_section (section, sec_name); if (table_section) table_size = table_section->size; - if (table_size == 0) + if (table_size == 0) { *table_p = NULL; return 0; @@ -618,68 +859,63 @@ xtensa_read_table_entries (bfd *abfd, else section_addr = section->vma; - /* If the file has not yet been relocated, process the relocations - and sort out the table entries that apply to the specified section. */ internal_relocs = retrieve_internal_relocs (abfd, table_section, TRUE); if (internal_relocs && !table_section->reloc_done) { - unsigned i; - - for (i = 0; i < table_section->reloc_count; i++) - { - Elf_Internal_Rela *rel = &internal_relocs[i]; - unsigned long r_symndx; + qsort (internal_relocs, table_section->reloc_count, + sizeof (Elf_Internal_Rela), internal_reloc_compare); + irel = internal_relocs; + } + else + irel = NULL; - if (ELF32_R_TYPE (rel->r_info) == R_XTENSA_NONE) - continue; + section_limit = bfd_get_section_limit (abfd, section); + rel_end = internal_relocs + table_section->reloc_count; - BFD_ASSERT (ELF32_R_TYPE (rel->r_info) == R_XTENSA_32); - r_symndx = ELF32_R_SYM (rel->r_info); + for (off = 0; off < table_size; off += table_entry_size) + { + bfd_vma address = bfd_get_32 (abfd, table_data + off); - if (get_elf_r_symndx_section (abfd, r_symndx) == section) - { - bfd_vma sym_off = get_elf_r_symndx_offset (abfd, r_symndx); - BFD_ASSERT (sym_off == 0); - BFD_ASSERT (rel->r_addend == 0); - blocks[block_count].address = - (section_addr + sym_off + rel->r_addend - + bfd_get_32 (abfd, table_data + rel->r_offset)); - blocks[block_count].size = - bfd_get_32 (abfd, table_data + rel->r_offset + 4); - if (predef_flags) - blocks[block_count].flags = predef_flags; - else - blocks[block_count].flags = - bfd_get_32 (abfd, table_data + rel->r_offset + 8); - block_count++; - } + /* 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; } - } - else - { - /* The file has already been relocated and the addresses are - already in the table. */ - bfd_vma off; - bfd_size_type section_limit = bfd_get_section_limit (abfd, section); - for (off = 0; off < table_size; off += table_entry_size) + if (irel && irel->r_offset == off) { - bfd_vma address = bfd_get_32 (abfd, table_data + 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 (address >= section_addr - && address < section_addr + section_limit) - { - 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++; - } + 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); @@ -692,7 +928,7 @@ xtensa_read_table_entries (bfd *abfd, property_table_compare); /* Check that the table contents are valid. Problems may occur, - for example, if an unrelocated object file is stripped. */ + for example, if an unrelocated object file is stripped. */ for (blk = 1; blk < block_count; blk++) { /* The only circumstance where two entries may legitimately @@ -702,8 +938,9 @@ xtensa_read_table_entries (bfd *abfd, if (blocks[blk - 1].address == blocks[blk].address && blocks[blk - 1].size != 0) { - (*_bfd_error_handler) (_("%B(%A): invalid property table"), - abfd, section); + /* xgettext:c-format */ + _bfd_error_handler (_("%pB(%pA): invalid property table"), + abfd, section); bfd_set_error (bfd_error_bad_value); free (blocks); return -1; @@ -758,14 +995,21 @@ elf_xtensa_check_relocs (bfd *abfd, 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 (info->relocatable) + if (bfd_link_relocatable (info) || (sec->flags & SEC_ALLOC) == 0) 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); @@ -773,51 +1017,108 @@ elf_xtensa_check_relocs (bfd *abfd, for (rel = relocs; rel < rel_end; rel++) { unsigned int r_type; - unsigned long r_symndx; - struct elf_link_hash_entry *h; + unsigned r_symndx; + struct elf_link_hash_entry *h = NULL; + struct elf_xtensa_link_hash_entry *eh; + int tls_type, old_tls_type; + bfd_boolean is_got = FALSE; + bfd_boolean is_plt = FALSE; + bfd_boolean 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)) { - (*_bfd_error_handler) (_("%B: bad symbol index: %d"), - abfd, r_symndx); + /* xgettext:c-format */ + _bfd_error_handler (_("%pB: bad symbol index: %d"), + abfd, r_symndx); return FALSE; } - if (r_symndx < symtab_hdr->sh_info) - h = NULL; - else + 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_32: - if (h == NULL) - goto local_literal; + 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; - if ((sec->flags & SEC_ALLOC) != 0) + case R_XTENSA_TLSDESC_ARG: + if (bfd_link_pic (info)) { - if (h->got.refcount <= 0) - h->got.refcount = 1; - else - h->got.refcount += 1; + 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: - /* If this relocation is against a local symbol, then it's - exactly the same as a normal local GOT entry. */ - if (h == NULL) - goto local_literal; + 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 ((sec->flags & SEC_ALLOC) != 0) + if (h) + { + if (is_plt) { if (h->plt.refcount <= 0) { @@ -830,97 +1131,91 @@ elf_xtensa_check_relocs (bfd *abfd, /* Keep track of the total PLT relocation count even if we don't yet know whether the dynamic sections will be created. */ - plt_reloc_count += 1; + htab->plt_reloc_count += 1; if (elf_hash_table (info)->dynamic_sections_created) { - if (!add_extra_plt_sections (elf_hash_table (info)->dynobj, - plt_reloc_count)) + if (! add_extra_plt_sections (info, htab->plt_reloc_count)) return FALSE; } } - break; + else if (is_got) + { + if (h->got.refcount <= 0) + h->got.refcount = 1; + else + h->got.refcount += 1; + } - local_literal: - if ((sec->flags & SEC_ALLOC) != 0) + 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_signed_vma *local_got_refcounts; + bfd_size_type size = symtab_hdr->sh_info; + void *mem; - /* This is a global offset table entry for a local symbol. */ - local_got_refcounts = elf_local_got_refcounts (abfd); - if (local_got_refcounts == NULL) - { - bfd_size_type size; + mem = bfd_zalloc (abfd, size * sizeof (bfd_signed_vma)); + if (mem == NULL) + return FALSE; + elf_local_got_refcounts (abfd) = (bfd_signed_vma *) mem; - size = symtab_hdr->sh_info; - size *= sizeof (bfd_signed_vma); - local_got_refcounts = - (bfd_signed_vma *) bfd_zalloc (abfd, size); - if (local_got_refcounts == NULL) - return FALSE; - elf_local_got_refcounts (abfd) = local_got_refcounts; - } - local_got_refcounts[r_symndx] += 1; + 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; } - break; - case R_XTENSA_OP0: - case R_XTENSA_OP1: - case R_XTENSA_OP2: - case R_XTENSA_SLOT0_OP: - case R_XTENSA_SLOT1_OP: - case R_XTENSA_SLOT2_OP: - case R_XTENSA_SLOT3_OP: - case R_XTENSA_SLOT4_OP: - case R_XTENSA_SLOT5_OP: - case R_XTENSA_SLOT6_OP: - case R_XTENSA_SLOT7_OP: - case R_XTENSA_SLOT8_OP: - case R_XTENSA_SLOT9_OP: - case R_XTENSA_SLOT10_OP: - case R_XTENSA_SLOT11_OP: - case R_XTENSA_SLOT12_OP: - case R_XTENSA_SLOT13_OP: - case R_XTENSA_SLOT14_OP: - case R_XTENSA_SLOT0_ALT: - case R_XTENSA_SLOT1_ALT: - case R_XTENSA_SLOT2_ALT: - case R_XTENSA_SLOT3_ALT: - case R_XTENSA_SLOT4_ALT: - case R_XTENSA_SLOT5_ALT: - case R_XTENSA_SLOT6_ALT: - case R_XTENSA_SLOT7_ALT: - case R_XTENSA_SLOT8_ALT: - case R_XTENSA_SLOT9_ALT: - case R_XTENSA_SLOT10_ALT: - case R_XTENSA_SLOT11_ALT: - case R_XTENSA_SLOT12_ALT: - case R_XTENSA_SLOT13_ALT: - case R_XTENSA_SLOT14_ALT: - case R_XTENSA_ASM_EXPAND: - case R_XTENSA_ASM_SIMPLIFY: - case R_XTENSA_DIFF8: - case R_XTENSA_DIFF16: - case R_XTENSA_DIFF32: - /* Nothing to do for these. */ - break; + /* This is a global offset table entry for a local symbol. */ + if (is_got || is_plt) + elf_local_got_refcounts (abfd) [r_symndx] += 1; - 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; - break; + if (is_tlsfunc) + elf_xtensa_local_tlsfunc_refcounts (abfd) [r_symndx] += 1; - 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; - break; + old_tls_type = elf_xtensa_local_got_tls_type (abfd) [r_symndx]; + } - default: - break; + 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 : ""); + 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; } } @@ -932,11 +1227,12 @@ static void elf_xtensa_make_sym_local (struct bfd_link_info *info, struct elf_link_hash_entry *h) { - if (info->shared) + if (bfd_link_pic (info)) { if (h->plt.refcount > 0) { - /* Will use RELATIVE relocs instead of JMP_SLOT relocs. */ + /* 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; @@ -970,116 +1266,47 @@ elf_xtensa_hide_symbol (struct bfd_link_info *info, static asection * elf_xtensa_gc_mark_hook (asection *sec, - struct bfd_link_info *info ATTRIBUTE_UNUSED, + struct bfd_link_info *info, Elf_Internal_Rela *rel, struct elf_link_hash_entry *h, Elf_Internal_Sym *sym) { - if (h) - { - switch (ELF32_R_TYPE (rel->r_info)) - { - case R_XTENSA_GNU_VTINHERIT: - case R_XTENSA_GNU_VTENTRY: - break; - - default: - switch (h->root.type) - { - case bfd_link_hash_defined: - case bfd_link_hash_defweak: - return h->root.u.def.section; - - case bfd_link_hash_common: - return h->root.u.c.p->section; + /* 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; - default: - break; - } - } - } - else - return bfd_section_from_elf_index (sec->owner, sym->st_shndx); + if (h != NULL) + switch (ELF32_R_TYPE (rel->r_info)) + { + case R_XTENSA_GNU_VTINHERIT: + case R_XTENSA_GNU_VTENTRY: + return NULL; + } - return NULL; + return _bfd_elf_gc_mark_hook (sec, info, rel, h, sym); } -/* Update the GOT & PLT entry reference counts - for the section being removed. */ +/* Create all the dynamic sections. */ static bfd_boolean -elf_xtensa_gc_sweep_hook (bfd *abfd, - struct bfd_link_info *info ATTRIBUTE_UNUSED, - asection *sec, - const Elf_Internal_Rela *relocs) -{ - Elf_Internal_Shdr *symtab_hdr; - struct elf_link_hash_entry **sym_hashes; - bfd_signed_vma *local_got_refcounts; - const Elf_Internal_Rela *rel, *relend; - - if ((sec->flags & SEC_ALLOC) == 0) - return TRUE; - - symtab_hdr = &elf_tdata (abfd)->symtab_hdr; - sym_hashes = elf_sym_hashes (abfd); - local_got_refcounts = elf_local_got_refcounts (abfd); - - relend = relocs + sec->reloc_count; - for (rel = relocs; rel < relend; rel++) - { - unsigned long r_symndx; - unsigned int r_type; - struct elf_link_hash_entry *h = NULL; - - r_symndx = ELF32_R_SYM (rel->r_info); - 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; - } - - r_type = ELF32_R_TYPE (rel->r_info); - switch (r_type) - { - case R_XTENSA_32: - if (h == NULL) - goto local_literal; - if (h->got.refcount > 0) - h->got.refcount--; - break; - - case R_XTENSA_PLT: - if (h == NULL) - goto local_literal; - if (h->plt.refcount > 0) - h->plt.refcount--; - break; - - local_literal: - if (local_got_refcounts[r_symndx] > 0) - local_got_refcounts[r_symndx] -= 1; - break; - - default: - break; - } - } - - return TRUE; -} - - -/* Create all the dynamic sections. */ - -static bfd_boolean -elf_xtensa_create_dynamic_sections (bfd *dynobj, struct bfd_link_info *info) +elf_xtensa_create_dynamic_sections (bfd *dynobj, struct bfd_link_info *info) { + struct elf_xtensa_link_hash_table *htab; flagword flags, noalloc_flags; - asection *s; + + 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)) @@ -1087,7 +1314,7 @@ elf_xtensa_create_dynamic_sections (bfd *dynobj, struct bfd_link_info *info) /* 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 (dynobj, plt_reloc_count)) + if (! add_extra_plt_sections (info, htab->plt_reloc_count)) return FALSE; noalloc_flags = (SEC_HAS_CONTENTS | SEC_IN_MEMORY @@ -1095,28 +1322,22 @@ elf_xtensa_create_dynamic_sections (bfd *dynobj, struct bfd_link_info *info) flags = noalloc_flags | SEC_ALLOC | SEC_LOAD; /* Mark the ".got.plt" section READONLY. */ - s = bfd_get_section_by_name (dynobj, ".got.plt"); - if (s == NULL - || ! bfd_set_section_flags (dynobj, s, flags)) - return FALSE; - - /* Create ".rela.got". */ - s = bfd_make_section_with_flags (dynobj, ".rela.got", flags); - if (s == NULL - || ! bfd_set_section_alignment (dynobj, s, 2)) + 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). */ - s = bfd_make_section_with_flags (dynobj, ".got.loc", flags); - if (s == NULL - || ! bfd_set_section_alignment (dynobj, s, 2)) + 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*"). */ - s = bfd_make_section_with_flags (dynobj, ".xt.lit.plt", - noalloc_flags); - if (s == NULL - || ! bfd_set_section_alignment (dynobj, s, 2)) + 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; @@ -1124,8 +1345,9 @@ elf_xtensa_create_dynamic_sections (bfd *dynobj, struct bfd_link_info *info) static bfd_boolean -add_extra_plt_sections (bfd *dynobj, int count) +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 @@ -1137,7 +1359,7 @@ add_extra_plt_sections (bfd *dynobj, int count) asection *s; /* Stop when we find a section has already been created. */ - if (elf_xtensa_get_plt_section (dynobj, chunk)) + if (elf_xtensa_get_plt_section (info, chunk)) break; flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY @@ -1145,17 +1367,16 @@ add_extra_plt_sections (bfd *dynobj, int count) sname = (char *) bfd_malloc (10); sprintf (sname, ".plt.%u", chunk); - s = bfd_make_section_with_flags (dynobj, sname, - flags | SEC_CODE); + s = bfd_make_section_anyway_with_flags (dynobj, sname, flags | SEC_CODE); if (s == NULL - || ! bfd_set_section_alignment (dynobj, s, 2)) + || !bfd_set_section_alignment (s, 2)) return FALSE; sname = (char *) bfd_malloc (14); sprintf (sname, ".got.plt.%u", chunk); - s = bfd_make_section_with_flags (dynobj, sname, flags); + s = bfd_make_section_anyway_with_flags (dynobj, sname, flags); if (s == NULL - || ! bfd_set_section_alignment (dynobj, s, 2)) + || !bfd_set_section_alignment (s, 2)) return FALSE; } @@ -1176,12 +1397,12 @@ elf_xtensa_adjust_dynamic_symbol (struct bfd_link_info *info ATTRIBUTE_UNUSED, /* If this is a weak symbol, and there is a real definition, the processor independent code will have arranged for us to see the real definition first, and we can just use the same value. */ - if (h->u.weakdef) + if (h->is_weakalias) { - BFD_ASSERT (h->u.weakdef->root.type == bfd_link_hash_defined - || h->u.weakdef->root.type == bfd_link_hash_defweak); - h->root.u.def.section = h->u.weakdef->root.u.def.section; - h->root.u.def.value = h->u.weakdef->root.u.def.value; + 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; } @@ -1194,57 +1415,56 @@ elf_xtensa_adjust_dynamic_symbol (struct bfd_link_info *info ATTRIBUTE_UNUSED, static bfd_boolean -elf_xtensa_fix_refcounts (struct elf_link_hash_entry *h, void *arg) +elf_xtensa_allocate_dynrelocs (struct elf_link_hash_entry *h, void *arg) { - struct bfd_link_info *info = (struct bfd_link_info *) arg; - - if (h->root.type == bfd_link_hash_warning) - h = (struct elf_link_hash_entry *) h->root.u.i.link; + 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 (! xtensa_elf_dynamic_symbol_p (h, info)) - elf_xtensa_make_sym_local (info, h); + if (h->root.type == bfd_link_hash_indirect) + return TRUE; - 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; + } -static bfd_boolean -elf_xtensa_allocate_plt_size (struct elf_link_hash_entry *h, void *arg) -{ - asection *srelplt = (asection *) arg; + if (! elf_xtensa_dynamic_symbol_p (h, info)) + elf_xtensa_make_sym_local (info, h); - if (h->root.type == bfd_link_hash_warning) - h = (struct elf_link_hash_entry *) h->root.u.i.link; + if (! elf_xtensa_dynamic_symbol_p (h, info) + && h->root.type == bfd_link_hash_undefweak) + return TRUE; if (h->plt.refcount > 0) - srelplt->size += (h->plt.refcount * sizeof (Elf32_External_Rela)); - - return TRUE; -} - - -static bfd_boolean -elf_xtensa_allocate_got_size (struct elf_link_hash_entry *h, void *arg) -{ - asection *srelgot = (asection *) arg; - - if (h->root.type == bfd_link_hash_warning) - h = (struct elf_link_hash_entry *) h->root.u.i.link; + htab->elf.srelplt->size += (h->plt.refcount * sizeof (Elf32_External_Rela)); if (h->got.refcount > 0) - srelgot->size += (h->got.refcount * sizeof (Elf32_External_Rela)); + 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, - asection *srelgot) +elf_xtensa_allocate_local_got_size (struct bfd_link_info *info) { + struct elf_xtensa_link_hash_table *htab; bfd *i; - for (i = info->input_bfds; i; i = i->link_next) + 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; @@ -1259,9 +1479,19 @@ elf_xtensa_allocate_local_got_size (struct bfd_link_info *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) - srelgot->size += (local_got_refcounts[j] - * sizeof (Elf32_External_Rela)); + htab->elf.srelgot->size += (local_got_refcounts[j] + * sizeof (Elf32_External_Rela)); } } } @@ -1273,6 +1503,7 @@ static bfd_boolean 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; bfd_boolean relplt, relgot; @@ -1280,18 +1511,29 @@ elf_xtensa_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, plt_entries = 0; plt_chunks = 0; - srelgot = 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 (info->executable) + if (bfd_link_executable (info) && !info->nointerp) { - s = bfd_get_section_by_name (dynobj, ".interp"); + s = bfd_get_linker_section (dynobj, ".interp"); if (s == NULL) abort (); s->size = sizeof ELF_DYNAMIC_INTERPRETER; @@ -1299,48 +1541,27 @@ elf_xtensa_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, } /* Allocate room for one word in ".got". */ - s = bfd_get_section_by_name (dynobj, ".got"); - if (s == NULL) - abort (); - s->size = 4; + htab->elf.sgot->size = 4; - /* Adjust refcounts for symbols that we now know are not "dynamic". */ + /* 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_fix_refcounts, + elf_xtensa_allocate_dynrelocs, (void *) info); - /* Allocate space in ".rela.got" for literals that reference - global symbols. */ - srelgot = bfd_get_section_by_name (dynobj, ".rela.got"); - if (srelgot == NULL) - abort (); - elf_link_hash_traverse (elf_hash_table (info), - elf_xtensa_allocate_got_size, - (void *) srelgot); - /* 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 (info->shared) - elf_xtensa_allocate_local_got_size (info, srelgot); - - /* Allocate space in ".rela.plt" for literals that have PLT entries. */ - srelplt = bfd_get_section_by_name (dynobj, ".rela.plt"); - if (srelplt == NULL) - abort (); - elf_link_hash_traverse (elf_hash_table (info), - elf_xtensa_allocate_plt_size, - (void *) srelplt); + 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 = bfd_get_section_by_name (dynobj, ".xt.lit.plt"); - if (spltlittbl == NULL) - abort (); - + spltlittbl = htab->spltlittbl; plt_entries = srelplt->size / sizeof (Elf32_External_Rela); plt_chunks = (plt_entries + PLT_ENTRIES_PER_CHUNK - 1) / PLT_ENTRIES_PER_CHUNK; @@ -1349,14 +1570,13 @@ elf_xtensa_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, created earlier because the initial count of PLT relocations was an overestimate. */ for (chunk = 0; - (splt = elf_xtensa_get_plt_section (dynobj, chunk)) != NULL; + (splt = elf_xtensa_get_plt_section (info, chunk)) != NULL; chunk++) { int chunk_entries; - sgotplt = elf_xtensa_get_gotplt_section (dynobj, chunk); - if (sgotplt == NULL) - abort (); + sgotplt = elf_xtensa_get_gotplt_section (info, chunk); + BFD_ASSERT (sgotplt != NULL); if (chunk < plt_chunks - 1) chunk_entries = PLT_ENTRIES_PER_CHUNK; @@ -1381,17 +1601,15 @@ elf_xtensa_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, /* Allocate space in ".got.loc" to match the total size of all the literal tables. */ - sgotloc = bfd_get_section_by_name (dynobj, ".got.loc"); - if (sgotloc == NULL) - abort (); + sgotloc = htab->sgotloc; sgotloc->size = spltlittbl->size; - for (abfd = info->input_bfds; abfd != NULL; abfd = abfd->link_next) + 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 (! elf_discarded_section (s) + if (! discarded_section (s) && xtensa_is_littable_section (s) && s != spltlittbl) sgotloc->size += s->size; @@ -1411,9 +1629,9 @@ elf_xtensa_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, /* It's OK to base decisions on the section name, because none of the dynobj section names depend upon the input files. */ - name = bfd_get_section_name (dynobj, s); + name = bfd_section_name (s); - if (strncmp (name, ".rela", 5) == 0) + if (CONST_STRNEQ (name, ".rela")) { if (s->size != 0) { @@ -1427,8 +1645,8 @@ elf_xtensa_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, s->reloc_count = 0; } } - else if (strncmp (name, ".plt.", 5) != 0 - && strncmp (name, ".got.plt.", 9) != 0 + else if (! CONST_STRNEQ (name, ".plt.") + && ! CONST_STRNEQ (name, ".got.plt.") && strcmp (name, ".got") != 0 && strcmp (name, ".plt") != 0 && strcmp (name, ".got.plt") != 0 @@ -1466,8 +1684,6 @@ elf_xtensa_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, /* 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. */ - if (srelgot == NULL) - abort (); for (chunk = 0; chunk < plt_chunks; chunk++) { Elf_Internal_Rela irela; @@ -1493,7 +1709,7 @@ elf_xtensa_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, #define add_dynamic_entry(TAG, VAL) \ _bfd_elf_add_dynamic_entry (info, TAG, VAL) - if (! info->shared) + if (bfd_link_executable (info)) { if (!add_dynamic_entry (DT_DEBUG, 0)) return FALSE; @@ -1501,8 +1717,7 @@ elf_xtensa_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, if (relplt) { - if (!add_dynamic_entry (DT_PLTGOT, 0) - || !add_dynamic_entry (DT_PLTRELSZ, 0) + if (!add_dynamic_entry (DT_PLTRELSZ, 0) || !add_dynamic_entry (DT_PLTREL, DT_RELA) || !add_dynamic_entry (DT_JMPREL, 0)) return FALSE; @@ -1516,7 +1731,8 @@ elf_xtensa_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, return FALSE; } - if (!add_dynamic_entry (DT_XTENSA_GOT_LOC_OFF, 0) + if (!add_dynamic_entry (DT_PLTGOT, 0) + || !add_dynamic_entry (DT_XTENSA_GOT_LOC_OFF, 0) || !add_dynamic_entry (DT_XTENSA_GOT_LOC_SZ, 0)) return FALSE; } @@ -1525,32 +1741,69 @@ elf_xtensa_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, return TRUE; } - -/* Remove any PT_LOAD segments with no allocated sections. Prior to - binutils 2.13, this function used to remove the non-SEC_ALLOC - sections from PT_LOAD segments, but that task has now been moved - into elf.c. We still need this function to remove any empty - segments that result, but there's nothing Xtensa-specific about - this and it probably ought to be moved into elf.c as well. */ - static bfd_boolean -elf_xtensa_modify_segment_map (bfd *abfd, - struct bfd_link_info *info ATTRIBUTE_UNUSED) +elf_xtensa_always_size_sections (bfd *output_bfd, + struct bfd_link_info *info) { - struct elf_segment_map **m_p; + 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; - m_p = &elf_tdata (abfd)->segment_map; - while (*m_p) + if (tls_sec && (htab->tlsbase->tls_type & GOT_TLS_ANY) != 0) { - if ((*m_p)->p_type == PT_LOAD && (*m_p)->count == 0) - *m_p = (*m_p)->next; - else - m_p = &(*m_p)->next; + 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 @@ -1574,7 +1827,7 @@ elf_xtensa_do_reloc (reloc_howto_type *howto, xtensa_isa isa = xtensa_default_isa; static xtensa_insnbuf ibuff = NULL; static xtensa_insnbuf sbuff = NULL; - bfd_vma self_address = 0; + bfd_vma self_address; bfd_size_type input_size; int opnd, slot; uint32 newval; @@ -1587,28 +1840,32 @@ elf_xtensa_do_reloc (reloc_howto_type *howto, 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_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. */ - xtensa_opcode opcode = - get_expanded_call_opcode (contents + address, - input_size - address, 0); + opcode = get_expanded_call_opcode (contents + address, + input_size - address, 0); if (is_windowed_call_opcode (opcode)) { - self_address = (input_section->output_section->vma - + input_section->output_offset - + address); if ((self_address >> CALL_SEGMENT_BITS) - != (relocation >> CALL_SEGMENT_BITS)) + != (relocation >> CALL_SEGMENT_BITS)) { *error_message = "windowed longcall crosses 1GB boundary; " "return may fail"; @@ -1620,7 +1877,7 @@ elf_xtensa_do_reloc (reloc_howto_type *howto, case R_XTENSA_ASM_SIMPLIFY: { - /* Convert the L32R/CALLX to CALL. */ + /* Convert the L32R/CALLX to CALL. */ bfd_reloc_status_type retval = elf_xtensa_do_asm_simplify (contents, address, input_size, error_message); @@ -1629,12 +1886,12 @@ elf_xtensa_do_reloc (reloc_howto_type *howto, /* 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: - case R_XTENSA_PLT: { bfd_vma x; x = bfd_get_32 (abfd, contents + address); @@ -1642,6 +1899,18 @@ elf_xtensa_do_reloc (reloc_howto_type *howto, 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.... */ @@ -1691,8 +1960,9 @@ elf_xtensa_do_reloc (reloc_howto_type *howto, } else if (opcode == get_const16_opcode ()) { - /* ALT used for high 16 bits. */ - newval = relocation >> 16; + /* ALT used for high 16 bits. + Ignore 32-bit overflow. */ + newval = (relocation >> 16) & 0xffff; opnd = 1; } else @@ -1727,11 +1997,6 @@ elf_xtensa_do_reloc (reloc_howto_type *howto, return bfd_reloc_dangerous; } - /* Calculate the PC address for this instruction. */ - self_address = (input_section->output_section->vma - + input_section->output_offset - + address); - newval = relocation; } } @@ -1742,7 +2007,30 @@ elf_xtensa_do_reloc (reloc_howto_type *howto, || xtensa_operand_set_field (isa, opcode, opnd, fmt, slot, sbuff, newval)) { - *error_message = build_encoding_error_message (opcode, relocation); + 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; } @@ -1751,7 +2039,7 @@ elf_xtensa_do_reloc (reloc_howto_type *howto, && is_windowed_call_opcode (opcode)) { if ((self_address >> CALL_SEGMENT_BITS) - != (relocation >> CALL_SEGMENT_BITS)) + != (relocation >> CALL_SEGMENT_BITS)) { *error_message = "windowed call crosses 1GB boundary; return may fail"; @@ -1767,7 +2055,7 @@ elf_xtensa_do_reloc (reloc_howto_type *howto, } -static char * ATTRIBUTE_PRINTF(2,4) +static char * vsprint_msg (const char *origmsg, const char *fmt, int arglen, ...) { /* To reduce the size of the memory leak, @@ -1776,50 +2064,27 @@ vsprint_msg (const char *origmsg, const char *fmt, int arglen, ...) static char *message = NULL; bfd_size_type orig_len, len = 0; bfd_boolean is_append; + va_list ap; - VA_OPEN (ap, arglen); - VA_FIXEDARG (ap, const char *, origmsg); - - is_append = (origmsg == message); + 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 (message, len); + message = (char *) bfd_realloc_or_free (message, len); alloc_size = len; } - if (!is_append) - memcpy (message, origmsg, orig_len); - vsprintf (message + orig_len, fmt, ap); - VA_CLOSE (ap); - return message; -} - - -static char * -build_encoding_error_message (xtensa_opcode opcode, bfd_vma target_address) -{ - const char *opname = xtensa_opcode_name (xtensa_default_isa, opcode); - const char *msg; - - msg = "cannot encode"; - if (is_direct_call_opcode (opcode)) - { - if ((target_address & 0x3) != 0) - msg = "misaligned call target"; - else - msg = "call target out of range"; - } - else if (opcode == get_l32r_opcode ()) + if (message != NULL) { - if ((target_address & 0x3) != 0) - msg = "misaligned literal target"; - else - msg = "literal target out of range"; + if (!is_append) + memcpy (message, origmsg, orig_len); + vsprintf (message + orig_len, fmt, ap); } - - return vsprint_msg (opname, ": %s", strlen (msg) + 2, msg); + va_end (ap); + return message; } @@ -1841,7 +2106,8 @@ bfd_elf_xtensa_reloc (bfd *abfd, { bfd_vma relocation; bfd_reloc_status_type flag; - bfd_size_type octets = reloc_entry->address * bfd_octets_per_byte (abfd); + 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; @@ -1930,7 +2196,8 @@ bfd_elf_xtensa_reloc (bfd *abfd, *error_message = ""; *error_message = vsprint_msg (*error_message, ": (%s + 0x%lx)", strlen (symbol->name) + 17, - symbol->name, reloc_entry->addend); + symbol->name, + (unsigned long) reloc_entry->addend); } return flag; @@ -1940,18 +2207,18 @@ bfd_elf_xtensa_reloc (bfd *abfd, /* Set up an entry in the procedure linkage table. */ static bfd_vma -elf_xtensa_create_plt_entry (bfd *dynobj, +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; + bfd_vma code_offset, lit_offset, abi_offset; int chunk; chunk = reloc_index / PLT_ENTRIES_PER_CHUNK; - splt = elf_xtensa_get_plt_section (dynobj, chunk); - sgotplt = elf_xtensa_get_gotplt_section (dynobj, 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; @@ -1968,23 +2235,206 @@ elf_xtensa_create_plt_entry (bfd *dynobj, /* Fill in the entry in the procedure linkage table. */ memcpy (splt->contents + code_offset, (bfd_big_endian (output_bfd) - ? elf_xtensa_be_plt_entry - : elf_xtensa_le_plt_entry), + ? elf_xtensa_be_plt_entry[XSHAL_ABI != XTHAL_ABI_WINDOWED] + : elf_xtensa_le_plt_entry[XSHAL_ABI != XTHAL_ABI_WINDOWED]), PLT_ENTRY_SIZE); + abi_offset = XSHAL_ABI == XTHAL_ABI_WINDOWED ? 3 : 0; bfd_put_16 (output_bfd, l32r_offset (got_base + 0, - plt_base + code_offset + 3), - splt->contents + code_offset + 4); + 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 + 6), - splt->contents + code_offset + 7); + 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 + 9), - splt->contents + code_offset + 10); + plt_base + code_offset + abi_offset + 6), + splt->contents + code_offset + abi_offset + 7); return plt_base + code_offset; } +static bfd_boolean get_indirect_call_dest_reg (xtensa_opcode, unsigned *); + +static bfd_boolean +replace_tls_insn (Elf_Internal_Rela *rel, + bfd *abfd, + asection *input_section, + bfd_byte *contents, + bfd_boolean 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. */ @@ -1998,32 +2448,35 @@ elf_xtensa_relocate_section (bfd *output_bfd, 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; - asection *srelgot, *srelplt; - bfd *dynobj; 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); - dynobj = elf_hash_table (info)->dynobj; - symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr; - sym_hashes = elf_sym_hashes (input_bfd); - - srelgot = NULL; - srelplt = NULL; - if (dynobj) + if (!is_xtensa_elf (input_bfd)) { - srelgot = bfd_get_section_by_name (dynobj, ".rela.got");; - srelplt = bfd_get_section_by_name (dynobj, ".rela.plt"); + 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, @@ -2044,12 +2497,15 @@ elf_xtensa_relocate_section (bfd *output_bfd, 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; bfd_boolean is_weak_undef; bfd_boolean unresolved_reloc; bfd_boolean warned; + bfd_boolean dynamic_symbol; r_type = ELF32_R_TYPE (rel->r_info); if (r_type == (int) R_XTENSA_GNU_VTINHERIT @@ -2065,8 +2521,56 @@ elf_xtensa_relocate_section (bfd *output_bfd, r_symndx = ELF32_R_SYM (rel->r_info); - if (info->relocatable) + 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 + { + bfd_boolean 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. @@ -2081,23 +2585,23 @@ elf_xtensa_relocate_section (bfd *output_bfd, if (!do_fix_for_relocatable_link (rel, input_bfd, input_section, contents)) return FALSE; - r_type = ELF32_R_TYPE (rel->r_info); } + 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) { - char *error_message = NULL; + 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) - { - if (!((*info->callbacks->reloc_dangerous) - (info, error_message, input_bfd, input_section, - rel->r_offset))) - return FALSE; - } + (*info->callbacks->reloc_dangerous) + (info, error_message, + input_bfd, input_section, rel->r_offset); + r_type = ELF32_R_TYPE (rel->r_info); } @@ -2120,25 +2624,38 @@ elf_xtensa_relocate_section (bfd *output_bfd, 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.... */ - if (rel->r_addend) + 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 { - howto = &elf_howto_table[r_type]; - if (howto->partial_inplace) + /* 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, - rel->r_addend, contents, + dest_addr, contents, rel->r_offset, FALSE, &error_message); - if (r != bfd_reloc_ok) - { - if (!((*info->callbacks->reloc_dangerous) - (info, error_message, input_bfd, input_section, - rel->r_offset))) - return FALSE; - } - rel->r_addend = 0; } } + 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; @@ -2146,94 +2663,81 @@ elf_xtensa_relocate_section (bfd *output_bfd, /* This is a final link. */ - h = NULL; - sym = NULL; - sec = NULL; - is_weak_undef = FALSE; - unresolved_reloc = FALSE; - warned = FALSE; - - if (howto->partial_inplace) - { - /* 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; - sec = local_sections[r_symndx]; - relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel); - } - else - { - RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel, - r_symndx, symtab_hdr, sym_hashes, - h, sec, relocation, - unresolved_reloc, warned); - - if (relocation == 0 - && !unresolved_reloc - && h->root.type == bfd_link_hash_undefweak) - is_weak_undef = TRUE; - } - 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); - - /* Update some already cached values. */ - r_type = ELF32_R_TYPE (rel->r_info); - howto = &elf_howto_table[r_type]; } /* Sanity check the address. */ if (rel->r_offset >= input_size && ELF32_R_TYPE (rel->r_info) != R_XTENSA_NONE) { - (*_bfd_error_handler) - (_("%B(%A+0x%lx): relocation offset out of range (size=0x%x)"), - input_bfd, input_section, rel->r_offset, input_size); + _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; } - /* Generate dynamic relocations. */ - if (elf_hash_table (info)->dynamic_sections_created) + if (h != NULL) + name = h->root.root.string; + else { - bfd_boolean dynamic_symbol = xtensa_elf_dynamic_symbol_p (h, info); + 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 (dynamic_symbol && is_operand_relocation (r_type)) - { - /* This is an error. The symbol's real value won't be known - until runtime and it's likely to be out of range anyway. */ - const char *name = h->root.root.string; - error_message = vsprint_msg ("invalid relocation for dynamic " - "symbol", ": %s", - strlen (name) + 2, name); - if (!((*info->callbacks->reloc_dangerous) - (info, error_message, input_bfd, input_section, - rel->r_offset))) - return FALSE; - } - else if ((r_type == R_XTENSA_32 || r_type == R_XTENSA_PLT) - && (input_section->flags & SEC_ALLOC) != 0 - && (dynamic_symbol || info->shared)) + 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 = srelplt; + srel = htab->elf.srelplt; else - srel = srelgot; + srel = htab->elf.srelgot; BFD_ASSERT (srel != NULL); @@ -2256,10 +2760,9 @@ elf_xtensa_relocate_section (bfd *output_bfd, { error_message = _("dynamic relocation in read-only section"); - if (!((*info->callbacks->reloc_dangerous) - (info, error_message, input_bfd, input_section, - rel->r_offset))) - return FALSE; + (*info->callbacks->reloc_dangerous) + (info, error_message, + input_bfd, input_section, rel->r_offset); } if (dynamic_symbol) @@ -2282,17 +2785,21 @@ elf_xtensa_relocate_section (bfd *output_bfd, contents of the literal entry to the address of the PLT entry. */ relocation = - elf_xtensa_create_plt_entry (dynobj, output_bfd, + elf_xtensa_create_plt_entry (info, output_bfd, srel->reloc_count); } unresolved_reloc = FALSE; } - else + 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 @@ -2301,6 +2808,151 @@ elf_xtensa_relocate_section (bfd *output_bfd, 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) + { + bfd_boolean is_ld_model = + (h && elf_xtensa_hash_entry (h) == htab->tlsbase); + if (! replace_tls_insn (rel, input_bfd, input_section, contents, + is_ld_model, &error_message)) + (*info->callbacks->reloc_dangerous) + (info, error_message, + input_bfd, input_section, rel->r_offset); + + if (r_type != R_XTENSA_TLS_ARG || is_ld_model) + { + /* Skip subsequent relocations on the same instruction. */ + while (rel + 1 < relend && rel[1].r_offset == rel->r_offset) + rel++; + } + } + continue; + + default: + if (elf_hash_table (info)->dynamic_sections_created + && dynamic_symbol && (is_operand_relocation (r_type) + || r_type == R_XTENSA_32_PCREL)) + { + error_message = + vsprint_msg ("invalid relocation for dynamic symbol", ": %s", + strlen (name) + 2, name); + (*info->callbacks->reloc_dangerous) + (info, error_message, input_bfd, input_section, rel->r_offset); + continue; + } + break; } /* Dynamic relocs are not propagated for SEC_DEBUGGING sections @@ -2308,13 +2960,24 @@ elf_xtensa_relocate_section (bfd *output_bfd, not process them. */ if (unresolved_reloc && !((input_section->flags & SEC_DEBUGGING) != 0 - && h->def_dynamic)) - (*_bfd_error_handler) - (_("%B(%A+0x%lx): unresolvable relocation against symbol `%s'"), - input_bfd, - input_section, - (long) rel->r_offset, - h->root.root.string); + && h->def_dynamic) + && _bfd_elf_section_offset (output_bfd, info, input_section, + rel->r_offset) != (bfd_vma) -1) + { + _bfd_error_handler + /* xgettext:c-format */ + (_("%pB(%pA+%#" PRIx64 "): " + "unresolvable %s relocation against symbol `%s'"), + input_bfd, + input_section, + (uint64_t) rel->r_offset, + howto->name, + name); + return FALSE; + } + + /* TLS optimizations may have changed r_type; update "howto". */ + howto = &elf_howto_table[r_type]; /* There's no point in calling bfd_perform_relocation here. Just go directly to our "special function". */ @@ -2325,35 +2988,19 @@ elf_xtensa_relocate_section (bfd *output_bfd, if (r != bfd_reloc_ok && !warned) { - const char *name; - BFD_ASSERT (r == bfd_reloc_dangerous || r == bfd_reloc_other); BFD_ASSERT (error_message != NULL); - if (h) - name = h->root.root.string; + if (rel->r_addend == 0) + error_message = vsprint_msg (error_message, ": %s", + strlen (name) + 2, name); else - { - name = bfd_elf_string_from_elf_section - (input_bfd, symtab_hdr->sh_link, sym->st_name); - if (name && *name == '\0') - name = bfd_section_name (input_bfd, sec); - } - if (name) - { - if (rel->r_addend == 0) - error_message = vsprint_msg (error_message, ": %s", - strlen (name) + 2, name); - else - error_message = vsprint_msg (error_message, ": (%s+0x%x)", - strlen (name) + 22, - name, (int)rel->r_addend); - } + error_message = vsprint_msg (error_message, ": (%s+0x%x)", + strlen (name) + 22, + name, (int) rel->r_addend); - if (!((*info->callbacks->reloc_dangerous) - (info, error_message, input_bfd, input_section, - rel->r_offset))) - return FALSE; + (*info->callbacks->reloc_dangerous) + (info, error_message, input_bfd, input_section, rel->r_offset); } } @@ -2375,17 +3022,22 @@ elf_xtensa_finish_dynamic_symbol (bfd *output_bfd ATTRIBUTE_UNUSED, struct elf_link_hash_entry *h, Elf_Internal_Sym *sym) { - if (h->needs_plt - && !h->def_regular) + if (h->needs_plt && !h->def_regular) { /* Mark the symbol as undefined, rather than as defined in the .plt section. Leave the value alone. */ sym->st_shndx = SHN_UNDEF; + /* If the symbol is weak, we do need to clear the value. + Otherwise, the PLT entry would provide a definition for + the symbol even if the symbol wasn't defined anywhere, + and so the symbol would never be NULL. */ + if (!h->ref_regular_nonweak) + sym->st_value = 0; } /* Mark _DYNAMIC and _GLOBAL_OFFSET_TABLE_ as absolute. */ - if (strcmp (h->root.root.string, "_DYNAMIC") == 0 - || strcmp (h->root.root.string, "_GLOBAL_OFFSET_TABLE_") == 0) + if (h == elf_hash_table (info)->hdynamic + || h == elf_hash_table (info)->hgot) sym->st_shndx = SHN_ABS; return TRUE; @@ -2418,7 +3070,7 @@ elf_xtensa_combine_prop_entries (bfd *output_bfd, sgotloc_size = sgotloc->size; if (sgotloc_size != section_size) { - (*_bfd_error_handler) + _bfd_error_handler (_("internal inconsistency in size of .got.loc section")); return -1; } @@ -2455,18 +3107,18 @@ elf_xtensa_combine_prop_entries (bfd *output_bfd, for (n = 0; n < num; n++) { - bfd_boolean remove = FALSE; + bfd_boolean remove_entry = FALSE; if (table[n].size == 0) - remove = TRUE; - else if (n > 0 && - (table[n-1].address + table[n-1].size == table[n].address)) + remove_entry = TRUE; + else if (n > 0 + && (table[n-1].address + table[n-1].size == table[n].address)) { table[n-1].size += table[n].size; - remove = TRUE; + remove_entry = TRUE; } - if (remove) + if (remove_entry) { for (m = n; m < num - 1; m++) { @@ -2511,21 +3163,26 @@ static bfd_boolean elf_xtensa_finish_dynamic_sections (bfd *output_bfd, struct bfd_link_info *info) { + struct elf_xtensa_link_hash_table *htab; bfd *dynobj; - asection *sdyn, *srelplt, *sgot, *sxtlit, *sgotloc; + asection *sdyn, *srelplt, *srelgot, *sgot, *sxtlit, *sgotloc; Elf32_External_Dyn *dyncon, *dynconend; - int num_xtlit_entries; + int num_xtlit_entries = 0; if (! elf_hash_table (info)->dynamic_sections_created) return TRUE; + htab = elf_xtensa_hash_table (info); + if (htab == NULL) + return FALSE; + dynobj = elf_hash_table (info)->dynobj; - sdyn = bfd_get_section_by_name (dynobj, ".dynamic"); + sdyn = bfd_get_linker_section (dynobj, ".dynamic"); BFD_ASSERT (sdyn != NULL); /* Set the first entry in the global offset table to the address of the dynamic section. */ - sgot = bfd_get_section_by_name (dynobj, ".got"); + sgot = htab->elf.sgot; if (sgot) { BFD_ASSERT (sgot->size == 4); @@ -2537,20 +3194,18 @@ elf_xtensa_finish_dynamic_sections (bfd *output_bfd, sgot->contents); } - srelplt = bfd_get_section_by_name (dynobj, ".rela.plt"); + srelplt = htab->elf.srelplt; + srelgot = htab->elf.srelgot; if (srelplt && srelplt->size != 0) { - asection *sgotplt, *srelgot, *spltlittbl; + asection *sgotplt, *spltlittbl; int chunk, plt_chunks, plt_entries; Elf_Internal_Rela irela; bfd_byte *loc; unsigned rtld_reloc; - srelgot = bfd_get_section_by_name (dynobj, ".rela.got");; - BFD_ASSERT (srelgot != NULL); - - spltlittbl = bfd_get_section_by_name (dynobj, ".xt.lit.plt"); - BFD_ASSERT (spltlittbl != NULL); + spltlittbl = htab->spltlittbl; + BFD_ASSERT (srelgot != NULL && spltlittbl != NULL); /* Find the first XTENSA_RTLD relocation. Presumably the rest of them follow immediately after.... */ @@ -2571,7 +3226,7 @@ elf_xtensa_finish_dynamic_sections (bfd *output_bfd, { int chunk_entries = 0; - sgotplt = elf_xtensa_get_gotplt_section (dynobj, chunk); + sgotplt = elf_xtensa_get_gotplt_section (info, chunk); BFD_ASSERT (sgotplt != NULL); /* Emit special RTLD relocations for the first two entries in @@ -2614,14 +3269,6 @@ elf_xtensa_finish_dynamic_sections (bfd *output_bfd, spltlittbl->contents + (chunk * 8) + 4); } - /* All the dynamic relocations have been emitted at this point. - Make sure the relocation sections are the correct size. */ - if (srelgot->size != (sizeof (Elf32_External_Rela) - * srelgot->reloc_count) - || srelplt->size != (sizeof (Elf32_External_Rela) - * srelplt->reloc_count)) - abort (); - /* The .xt.lit.plt section has just been modified. This must happen before the code below which combines adjacent literal table entries, and the .xt.lit.plt contents have to be forced to @@ -2636,23 +3283,32 @@ elf_xtensa_finish_dynamic_sections (bfd *output_bfd, spltlittbl->flags &= ~SEC_HAS_CONTENTS; } + /* All the dynamic relocations have been emitted at this point. + Make sure the relocation sections are the correct size. */ + if ((srelgot && srelgot->size != (sizeof (Elf32_External_Rela) + * srelgot->reloc_count)) + || (srelplt && srelplt->size != (sizeof (Elf32_External_Rela) + * srelplt->reloc_count))) + abort (); + /* Combine adjacent literal table entries. */ - BFD_ASSERT (! info->relocatable); + BFD_ASSERT (! bfd_link_relocatable (info)); sxtlit = bfd_get_section_by_name (output_bfd, ".xt.lit"); - sgotloc = bfd_get_section_by_name (dynobj, ".got.loc"); - BFD_ASSERT (sxtlit && sgotloc); - num_xtlit_entries = - elf_xtensa_combine_prop_entries (output_bfd, sxtlit, sgotloc); - if (num_xtlit_entries < 0) - return FALSE; + sgotloc = htab->sgotloc; + BFD_ASSERT (sgotloc); + if (sxtlit) + { + num_xtlit_entries = + elf_xtensa_combine_prop_entries (output_bfd, sxtlit, sgotloc); + if (num_xtlit_entries < 0) + return FALSE; + } dyncon = (Elf32_External_Dyn *) sdyn->contents; dynconend = (Elf32_External_Dyn *) (sdyn->contents + sdyn->size); for (; dyncon < dynconend; dyncon++) { Elf_Internal_Dyn dyn; - const char *name; - asection *s; bfd_elf32_swap_dyn_in (dynobj, dyncon, &dyn); @@ -2666,35 +3322,22 @@ elf_xtensa_finish_dynamic_sections (bfd *output_bfd, break; case DT_XTENSA_GOT_LOC_OFF: - name = ".got.loc"; - goto get_vma; + dyn.d_un.d_ptr = (htab->sgotloc->output_section->vma + + htab->sgotloc->output_offset); + break; + case DT_PLTGOT: - name = ".got"; - goto get_vma; - case DT_JMPREL: - name = ".rela.plt"; - get_vma: - s = bfd_get_section_by_name (output_bfd, name); - BFD_ASSERT (s); - dyn.d_un.d_ptr = s->vma; + dyn.d_un.d_ptr = (htab->elf.sgot->output_section->vma + + htab->elf.sgot->output_offset); break; - case DT_PLTRELSZ: - s = bfd_get_section_by_name (output_bfd, ".rela.plt"); - BFD_ASSERT (s); - dyn.d_un.d_val = s->size; + case DT_JMPREL: + dyn.d_un.d_ptr = (htab->elf.srelplt->output_section->vma + + htab->elf.srelplt->output_offset); break; - case DT_RELASZ: - /* Adjust RELASZ to not include JMPREL. This matches what - glibc expects and what is done for several other ELF - targets (e.g., i386, alpha), but the "correct" behavior - seems to be unresolved. Since the linker script arranges - for .rela.plt to follow all other relocation sections, we - don't have to worry about changing the DT_RELA entry. */ - s = bfd_get_section_by_name (output_bfd, ".rela.plt"); - if (s) - dyn.d_un.d_val -= s->size; + case DT_PLTRELSZ: + dyn.d_un.d_val = htab->elf.srelplt->size; break; } @@ -2711,13 +3354,14 @@ elf_xtensa_finish_dynamic_sections (bfd *output_bfd, object file when linking. */ static bfd_boolean -elf_xtensa_merge_private_bfd_data (bfd *ibfd, bfd *obfd) +elf_xtensa_merge_private_bfd_data (bfd *ibfd, struct bfd_link_info *info) { + bfd *obfd = info->output_bfd; unsigned out_mach, in_mach; flagword out_flag, in_flag; - /* Check if we have the same endianess. */ - if (!_bfd_generic_verify_endian_match (ibfd, obfd)) + /* Check if we have the same endianness. */ + if (!_bfd_generic_verify_endian_match (ibfd, info)) return FALSE; /* Don't even pretend to support mixed-format linking. */ @@ -2732,8 +3376,9 @@ elf_xtensa_merge_private_bfd_data (bfd *ibfd, bfd *obfd) in_mach = in_flag & EF_XTENSA_MACH; if (out_mach != in_mach) { - (*_bfd_error_handler) - (_("%B: incompatible machine type. Output is 0x%x. Input is 0x%x"), + _bfd_error_handler + /* xgettext:c-format */ + (_("%pB: incompatible machine type; output is 0x%x; input is 0x%x"), ibfd, out_mach, in_mach); bfd_set_error (bfd_error_wrong_format); return FALSE; @@ -2752,10 +3397,10 @@ elf_xtensa_merge_private_bfd_data (bfd *ibfd, bfd *obfd) return TRUE; } - if ((out_flag & EF_XTENSA_XT_INSN) != (in_flag & EF_XTENSA_XT_INSN)) + if ((out_flag & EF_XTENSA_XT_INSN) != (in_flag & EF_XTENSA_XT_INSN)) elf_elfheader (obfd)->e_flags &= (~ EF_XTENSA_XT_INSN); - if ((out_flag & EF_XTENSA_XT_LIT) != (in_flag & EF_XTENSA_XT_LIT)) + if ((out_flag & EF_XTENSA_XT_LIT) != (in_flag & EF_XTENSA_XT_LIT)) elf_elfheader (obfd)->e_flags &= (~ EF_XTENSA_XT_LIT); return TRUE; @@ -2823,12 +3468,11 @@ elf_xtensa_object_p (bfd *abfd) file. This gets the Xtensa architecture right based on the machine number. */ -static void -elf_xtensa_final_write_processing (bfd *abfd, - bfd_boolean linker ATTRIBUTE_UNUSED) +static bfd_boolean +elf_xtensa_final_write_processing (bfd *abfd) { int mach; - unsigned long val; + unsigned long val = elf_elfheader (abfd)->e_flags & EF_XTENSA_MACH; switch (mach = bfd_get_mach (abfd)) { @@ -2836,16 +3480,19 @@ elf_xtensa_final_write_processing (bfd *abfd, val = E_XTENSA_MACH; break; default: - return; + break; } - elf_elfheader (abfd)->e_flags &= (~ EF_XTENSA_MACH); + elf_elfheader (abfd)->e_flags &= ~EF_XTENSA_MACH; elf_elfheader (abfd)->e_flags |= val; + return _bfd_elf_final_write_processing (abfd); } static enum elf_reloc_type_class -elf_xtensa_reloc_type_class (const Elf_Internal_Rela *rela) +elf_xtensa_reloc_type_class (const struct bfd_link_info *info ATTRIBUTE_UNUSED, + const asection *rel_sec ATTRIBUTE_UNUSED, + const Elf_Internal_Rela *rela) { switch ((int) ELF32_R_TYPE (rela->r_info)) { @@ -2866,18 +3513,22 @@ elf_xtensa_discard_info_for_section (bfd *abfd, asection *sec) { bfd_byte *contents; - bfd_vma section_size; bfd_vma offset, actual_offset; - size_t removed_bytes = 0; - - section_size = sec->size; - if (section_size == 0 || section_size % 8 != 0) - return FALSE; + bfd_size_type removed_bytes = 0; + bfd_size_type entry_size; if (sec->output_section && bfd_is_abs_section (sec->output_section)) return FALSE; + if (xtensa_is_proptable_section (sec)) + entry_size = 12; + else + entry_size = 8; + + if (sec->size == 0 || sec->size % entry_size != 0) + return FALSE; + contents = retrieve_contents (abfd, sec, info->keep_memory); if (!contents) return FALSE; @@ -2889,10 +3540,15 @@ elf_xtensa_discard_info_for_section (bfd *abfd, return FALSE; } + /* Sort the relocations. They should already be in order when + relaxation is enabled, but it might not be. */ + qsort (cookie->rels, sec->reloc_count, sizeof (Elf_Internal_Rela), + internal_reloc_compare); + cookie->rel = cookie->rels; cookie->relend = cookie->rels + sec->reloc_count; - for (offset = 0; offset < section_size; offset += 8) + for (offset = 0; offset < sec->size; offset += entry_size) { actual_offset = offset - removed_bytes; @@ -2916,11 +3572,11 @@ elf_xtensa_discard_info_for_section (bfd *abfd, if (ELF32_R_TYPE (cookie->rel->r_info) != R_XTENSA_NONE) { /* Shift the contents up. */ - if (offset + 8 < section_size) + if (offset + entry_size < sec->size) memmove (&contents[actual_offset], - &contents[actual_offset+8], - section_size - offset - 8); - removed_bytes += 8; + &contents[actual_offset + entry_size], + sec->size - offset - entry_size); + removed_bytes += entry_size; } /* Remove this relocation. */ @@ -2953,24 +3609,21 @@ elf_xtensa_discard_info_for_section (bfd *abfd, } /* Clear the removed bytes. */ - memset (&contents[section_size - removed_bytes], 0, removed_bytes); + memset (&contents[sec->size - removed_bytes], 0, removed_bytes); pin_contents (sec, contents); pin_internal_relocs (sec, cookie->rels); /* Shrink size. */ - sec->size = section_size - removed_bytes; + if (sec->rawsize == 0) + sec->rawsize = sec->size; + sec->size -= removed_bytes; if (xtensa_is_littable_section (sec)) { - bfd *dynobj = elf_hash_table (info)->dynobj; - if (dynobj) - { - asection *sgotloc = - bfd_get_section_by_name (dynobj, ".got.loc"); - if (sgotloc) - sgotloc->size -= removed_bytes; - } + asection *sgotloc = elf_xtensa_hash_table (info)->sgotloc; + if (sgotloc) + sgotloc->size -= removed_bytes; } } else @@ -3010,6 +3663,19 @@ elf_xtensa_ignore_discarded_relocs (asection *sec) return xtensa_is_property_section (sec); } + +static unsigned int +elf_xtensa_action_discarded (asection *sec) +{ + if (strcmp (".xt_except_table", sec->name) == 0) + return 0; + + if (strcmp (".xt_except_desc", sec->name) == 0) + return 0; + + return _bfd_elf_default_action_discarded (sec); +} + /* Support for core dump NOTE sections. */ @@ -3023,10 +3689,10 @@ elf_xtensa_grok_prstatus (bfd *abfd, Elf_Internal_Note *note) based on the size. Just assume this is GNU/Linux. */ /* pr_cursig */ - elf_tdata (abfd)->core_signal = bfd_get_16 (abfd, note->descdata + 12); + elf_tdata (abfd)->core->signal = bfd_get_16 (abfd, note->descdata + 12); /* pr_pid */ - elf_tdata (abfd)->core_pid = bfd_get_32 (abfd, note->descdata + 24); + elf_tdata (abfd)->core->lwpid = bfd_get_32 (abfd, note->descdata + 24); /* pr_reg */ offset = 72; @@ -3047,9 +3713,9 @@ elf_xtensa_grok_psinfo (bfd *abfd, Elf_Internal_Note *note) return FALSE; case 128: /* GNU/Linux elf_prpsinfo */ - elf_tdata (abfd)->core_program + elf_tdata (abfd)->core->program = _bfd_elfcore_strndup (abfd, note->descdata + 32, 16); - elf_tdata (abfd)->core_command + elf_tdata (abfd)->core->command = _bfd_elfcore_strndup (abfd, note->descdata + 48, 80); } @@ -3058,7 +3724,7 @@ elf_xtensa_grok_psinfo (bfd *abfd, Elf_Internal_Note *note) implementations, so strip it off if it exists. */ { - char *command = elf_tdata (abfd)->core_command; + char *command = elf_tdata (abfd)->core->command; int n = strlen (command); if (0 < n && command[n - 1] == ' ') @@ -3132,6 +3798,29 @@ is_windowed_call_opcode (xtensa_opcode opcode) } +static bfd_boolean +get_indirect_call_dest_reg (xtensa_opcode opcode, unsigned *pdst) +{ + unsigned dst = (unsigned) -1; + + init_call_opcodes (); + if (opcode == callx0_op) + dst = 0; + else if (opcode == callx4_op) + dst = 4; + else if (opcode == callx8_op) + dst = 8; + else if (opcode == callx12_op) + dst = 12; + + if (dst == (unsigned) -1) + return FALSE; + + *pdst = dst; + return TRUE; +} + + static xtensa_opcode get_const16_opcode (void) { @@ -3174,6 +3863,33 @@ l32r_offset (bfd_vma addr, bfd_vma pc) } +static xtensa_opcode +get_rsr_lend_opcode (void) +{ + static xtensa_opcode rsr_lend_opcode = XTENSA_UNDEFINED; + static bfd_boolean done_lookup = FALSE; + if (!done_lookup) + { + rsr_lend_opcode = xtensa_opcode_lookup (xtensa_default_isa, "rsr.lend"); + done_lookup = TRUE; + } + return rsr_lend_opcode; +} + +static xtensa_opcode +get_wsr_lbeg_opcode (void) +{ + static xtensa_opcode wsr_lbeg_opcode = XTENSA_UNDEFINED; + static bfd_boolean done_lookup = FALSE; + if (!done_lookup) + { + wsr_lbeg_opcode = xtensa_opcode_lookup (xtensa_default_isa, "wsr.lbeg"); + done_lookup = TRUE; + } + return wsr_lbeg_opcode; +} + + static int get_relocation_opnd (xtensa_opcode opcode, int r_type) { @@ -3304,7 +4020,7 @@ get_asm_simplify_size (bfd_byte *contents, return 0; size += insnlen; - + insnlen = insn_decode_len (contents, content_len, offset + size); if (insnlen == 0) return 0; @@ -3343,7 +4059,7 @@ is_operand_relocation (int r_type) return FALSE; } - + #define MIN_INSN_LENGTH 2 /* Return 0 if it fails to decode. */ @@ -3374,6 +4090,28 @@ insn_decode_len (bfd_byte *contents, return insn_len; } +int +insn_num_slots (bfd_byte *contents, + bfd_size_type content_len, + bfd_size_type offset) +{ + xtensa_isa isa = xtensa_default_isa; + xtensa_format fmt; + static xtensa_insnbuf ibuff = NULL; + + if (offset + MIN_INSN_LENGTH > content_len) + return XTENSA_UNDEFINED; + + if (ibuff == NULL) + ibuff = xtensa_insnbuf_alloc (isa); + xtensa_insnbuf_from_chars (isa, ibuff, &contents[offset], + content_len - offset); + fmt = xtensa_format_decode (isa, ibuff); + if (fmt == XTENSA_UNDEFINED) + return XTENSA_UNDEFINED; + return xtensa_format_num_slots (isa, fmt); +} + /* Decode the opcode for a single slot instruction. Return 0 if it fails to decode or the instruction is multi-slot. */ @@ -3435,25 +4173,38 @@ check_loop_aligned (bfd_byte *contents, bfd_vma address) { bfd_size_type loop_len, insn_len; - xtensa_opcode opcode = - insn_decode_opcode (contents, content_length, offset, 0); - BFD_ASSERT (opcode != XTENSA_UNDEFINED); - if (opcode != XTENSA_UNDEFINED) - return FALSE; - BFD_ASSERT (xtensa_opcode_is_loop (xtensa_default_isa, opcode)); - if (!xtensa_opcode_is_loop (xtensa_default_isa, opcode)) - return FALSE; + xtensa_opcode opcode; - loop_len = insn_decode_len (contents, content_length, offset); - BFD_ASSERT (loop_len != 0); - if (loop_len == 0) - return FALSE; + opcode = insn_decode_opcode (contents, content_length, offset, 0); + if (opcode == XTENSA_UNDEFINED + || xtensa_opcode_is_loop (xtensa_default_isa, opcode) != 1) + { + BFD_ASSERT (FALSE); + return FALSE; + } + loop_len = insn_decode_len (contents, content_length, offset); insn_len = insn_decode_len (contents, content_length, offset + loop_len); - BFD_ASSERT (insn_len != 0); - if (insn_len == 0) - return FALSE; + if (loop_len == 0 || insn_len == 0) + { + BFD_ASSERT (FALSE); + return FALSE; + } + /* If this is relaxed loop, analyze first instruction of the actual loop + body. It must be at offset 27 from the loop instruction address. */ + if (insn_len == 3 + && insn_num_slots (contents, content_length, offset + loop_len) == 1 + && insn_decode_opcode (contents, content_length, + offset + loop_len, 0) == get_rsr_lend_opcode() + && insn_decode_len (contents, content_length, offset + loop_len + 3) == 3 + && insn_num_slots (contents, content_length, offset + loop_len + 3) == 1 + && insn_decode_opcode (contents, content_length, + offset + loop_len + 3, 0) == get_wsr_lbeg_opcode()) + { + loop_len = 27; + insn_len = insn_decode_len (contents, content_length, offset + loop_len); + } return check_branch_target_aligned_address (address + loop_len, insn_len); } @@ -3564,60 +4315,31 @@ struct string_pair widenable[] = }; -/* Attempt to narrow an instruction. Return true if the narrowing is - valid. If the do_it parameter is non-zero, then perform the action - in-place directly into the contents. Otherwise, do not modify the - contents. The set of valid narrowing are specified by a string table +/* Check if an instruction can be "narrowed", i.e., changed from a standard + 3-byte instruction to a 2-byte "density" instruction. If it is valid, + return the instruction buffer holding the narrow instruction. Otherwise, + return 0. The set of valid narrowing are specified by a string table but require some special case operand checks in some cases. */ -static bfd_boolean -narrow_instruction (bfd_byte *contents, - bfd_size_type content_length, - bfd_size_type offset, - bfd_boolean do_it) +static xtensa_insnbuf +can_narrow_instruction (xtensa_insnbuf slotbuf, + xtensa_format fmt, + xtensa_opcode opcode) { - xtensa_opcode opcode; - bfd_size_type insn_len, opi; xtensa_isa isa = xtensa_default_isa; - xtensa_format fmt, o_fmt; + xtensa_format o_fmt; + unsigned opi; - static xtensa_insnbuf insnbuf = NULL; - static xtensa_insnbuf slotbuf = NULL; static xtensa_insnbuf o_insnbuf = NULL; static xtensa_insnbuf o_slotbuf = NULL; - if (insnbuf == NULL) + if (o_insnbuf == NULL) { - insnbuf = xtensa_insnbuf_alloc (isa); - slotbuf = xtensa_insnbuf_alloc (isa); o_insnbuf = xtensa_insnbuf_alloc (isa); o_slotbuf = xtensa_insnbuf_alloc (isa); } - BFD_ASSERT (offset < content_length); - - if (content_length < 2) - return FALSE; - - /* We will hand-code a few of these for a little while. - These have all been specified in the assembler aleady. */ - xtensa_insnbuf_from_chars (isa, insnbuf, &contents[offset], - content_length - offset); - fmt = xtensa_format_decode (isa, insnbuf); - if (xtensa_format_num_slots (isa, fmt) != 1) - return FALSE; - - if (xtensa_format_get_slot (isa, fmt, 0, insnbuf, slotbuf) != 0) - return FALSE; - - opcode = xtensa_opcode_decode (isa, fmt, 0, slotbuf); - if (opcode == XTENSA_UNDEFINED) - return FALSE; - insn_len = xtensa_format_length (isa, fmt); - if (insn_len > content_length) - return FALSE; - - for (opi = 0; opi < (sizeof (narrowable)/sizeof (struct string_pair)); ++opi) + for (opi = 0; opi < (sizeof (narrowable)/sizeof (struct string_pair)); opi++) { bfd_boolean is_or = (strcmp ("or", narrowable[opi].wide) == 0); @@ -3633,47 +4355,41 @@ narrow_instruction (bfd_byte *contents, o_opcode = xtensa_opcode_lookup (isa, narrowable[opi].narrow); if (o_opcode == XTENSA_UNDEFINED) - return FALSE; + return 0; o_fmt = get_single_format (o_opcode); if (o_fmt == XTENSA_UNDEFINED) - return FALSE; + return 0; if (xtensa_format_length (isa, fmt) != 3 || xtensa_format_length (isa, o_fmt) != 2) - return FALSE; + return 0; xtensa_format_encode (isa, o_fmt, o_insnbuf); operand_count = xtensa_opcode_num_operands (isa, opcode); o_operand_count = xtensa_opcode_num_operands (isa, o_opcode); if (xtensa_opcode_encode (isa, o_fmt, 0, o_slotbuf, o_opcode) != 0) - return FALSE; + return 0; if (!is_or) { if (xtensa_opcode_num_operands (isa, o_opcode) != operand_count) - return FALSE; + return 0; } else { uint32 rawval0, rawval1, rawval2; - if (o_operand_count + 1 != operand_count) - return FALSE; - if (xtensa_operand_get_field (isa, opcode, 0, - fmt, 0, slotbuf, &rawval0) != 0) - return FALSE; - if (xtensa_operand_get_field (isa, opcode, 1, - fmt, 0, slotbuf, &rawval1) != 0) - return FALSE; - if (xtensa_operand_get_field (isa, opcode, 2, - fmt, 0, slotbuf, &rawval2) != 0) - return FALSE; - - if (rawval1 != rawval2) - return FALSE; - if (rawval0 == rawval1) /* it is a nop */ - return FALSE; + if (o_operand_count + 1 != operand_count + || xtensa_operand_get_field (isa, opcode, 0, + fmt, 0, slotbuf, &rawval0) != 0 + || xtensa_operand_get_field (isa, opcode, 1, + fmt, 0, slotbuf, &rawval1) != 0 + || xtensa_operand_get_field (isa, opcode, 2, + fmt, 0, slotbuf, &rawval2) != 0 + || rawval1 != rawval2 + || rawval0 == rawval1 /* it is a nop */) + return 0; } for (i = 0; i < o_operand_count; ++i) @@ -3681,7 +4397,7 @@ narrow_instruction (bfd_byte *contents, if (xtensa_operand_get_field (isa, opcode, i, fmt, 0, slotbuf, &value) || xtensa_operand_decode (isa, opcode, i, &value)) - return FALSE; + return 0; /* PC-relative branches need adjustment, but the PC-rel operand will always have a relocation. */ @@ -3691,52 +4407,41 @@ narrow_instruction (bfd_byte *contents, || xtensa_operand_encode (isa, o_opcode, i, &newval) || xtensa_operand_set_field (isa, o_opcode, i, o_fmt, 0, o_slotbuf, newval)) - return FALSE; + return 0; } - if (xtensa_format_set_slot (isa, o_fmt, 0, - o_insnbuf, o_slotbuf) != 0) - return FALSE; + if (xtensa_format_set_slot (isa, o_fmt, 0, o_insnbuf, o_slotbuf)) + return 0; - if (do_it) - xtensa_insnbuf_to_chars (isa, o_insnbuf, contents + offset, - content_length - offset); - return TRUE; + return o_insnbuf; } } - return FALSE; + return 0; } -/* Attempt to widen an instruction. Return true if the widening is - valid. If the do_it parameter is non-zero, then the action should - be performed inplace into the contents. Otherwise, do not modify - the contents. The set of valid widenings are specified by a string - table but require some special case operand checks in some - cases. */ +/* Attempt to narrow an instruction. If the narrowing is valid, perform + the action in-place directly into the contents and return TRUE. Otherwise, + the return value is FALSE and the contents are not modified. */ static bfd_boolean -widen_instruction (bfd_byte *contents, - bfd_size_type content_length, - bfd_size_type offset, - bfd_boolean do_it) +narrow_instruction (bfd_byte *contents, + bfd_size_type content_length, + bfd_size_type offset) { xtensa_opcode opcode; - bfd_size_type insn_len, opi; + bfd_size_type insn_len; xtensa_isa isa = xtensa_default_isa; - xtensa_format fmt, o_fmt; + xtensa_format fmt; + xtensa_insnbuf o_insnbuf; static xtensa_insnbuf insnbuf = NULL; static xtensa_insnbuf slotbuf = NULL; - static xtensa_insnbuf o_insnbuf = NULL; - static xtensa_insnbuf o_slotbuf = NULL; if (insnbuf == NULL) { insnbuf = xtensa_insnbuf_alloc (isa); slotbuf = xtensa_insnbuf_alloc (isa); - o_insnbuf = xtensa_insnbuf_alloc (isa); - o_slotbuf = xtensa_insnbuf_alloc (isa); } BFD_ASSERT (offset < content_length); @@ -3744,7 +4449,7 @@ widen_instruction (bfd_byte *contents, if (content_length < 2) return FALSE; - /* We will hand code a few of these for a little while. + /* We will hand-code a few of these for a little while. These have all been specified in the assembler aleady. */ xtensa_insnbuf_from_chars (isa, insnbuf, &contents[offset], content_length - offset); @@ -3762,7 +4467,43 @@ widen_instruction (bfd_byte *contents, if (insn_len > content_length) return FALSE; - for (opi = 0; opi < (sizeof (widenable)/sizeof (struct string_pair)); ++opi) + o_insnbuf = can_narrow_instruction (slotbuf, fmt, opcode); + if (o_insnbuf) + { + xtensa_insnbuf_to_chars (isa, o_insnbuf, contents + offset, + content_length - offset); + return TRUE; + } + + return FALSE; +} + + +/* Check if an instruction can be "widened", i.e., changed from a 2-byte + "density" instruction to a standard 3-byte instruction. If it is valid, + return the instruction buffer holding the wide instruction. Otherwise, + return 0. The set of valid widenings are specified by a string table + but require some special case operand checks in some cases. */ + +static xtensa_insnbuf +can_widen_instruction (xtensa_insnbuf slotbuf, + xtensa_format fmt, + xtensa_opcode opcode) +{ + xtensa_isa isa = xtensa_default_isa; + xtensa_format o_fmt; + unsigned opi; + + static xtensa_insnbuf o_insnbuf = NULL; + static xtensa_insnbuf o_slotbuf = NULL; + + if (o_insnbuf == NULL) + { + o_insnbuf = xtensa_insnbuf_alloc (isa); + o_slotbuf = xtensa_insnbuf_alloc (isa); + } + + for (opi = 0; opi < (sizeof (widenable)/sizeof (struct string_pair)); opi++) { bfd_boolean is_or = (strcmp ("or", widenable[opi].wide) == 0); bfd_boolean is_branch = (strcmp ("beqz", widenable[opi].wide) == 0 @@ -3780,14 +4521,14 @@ widen_instruction (bfd_byte *contents, o_opcode = xtensa_opcode_lookup (isa, widenable[opi].wide); if (o_opcode == XTENSA_UNDEFINED) - return FALSE; + return 0; o_fmt = get_single_format (o_opcode); if (o_fmt == XTENSA_UNDEFINED) - return FALSE; + return 0; if (xtensa_format_length (isa, fmt) != 2 || xtensa_format_length (isa, o_fmt) != 3) - return FALSE; + return 0; xtensa_format_encode (isa, o_fmt, o_insnbuf); operand_count = xtensa_opcode_num_operands (isa, opcode); @@ -3795,32 +4536,29 @@ widen_instruction (bfd_byte *contents, check_operand_count = o_operand_count; if (xtensa_opcode_encode (isa, o_fmt, 0, o_slotbuf, o_opcode) != 0) - return FALSE; + return 0; if (!is_or) { if (xtensa_opcode_num_operands (isa, o_opcode) != operand_count) - return FALSE; + return 0; } else { uint32 rawval0, rawval1; - if (o_operand_count != operand_count + 1) - return FALSE; - if (xtensa_operand_get_field (isa, opcode, 0, - fmt, 0, slotbuf, &rawval0) != 0) - return FALSE; - if (xtensa_operand_get_field (isa, opcode, 1, - fmt, 0, slotbuf, &rawval1) != 0) - return FALSE; - if (rawval0 == rawval1) /* it is a nop */ - return FALSE; + if (o_operand_count != operand_count + 1 + || xtensa_operand_get_field (isa, opcode, 0, + fmt, 0, slotbuf, &rawval0) != 0 + || xtensa_operand_get_field (isa, opcode, 1, + fmt, 0, slotbuf, &rawval1) != 0 + || rawval0 == rawval1 /* it is a nop */) + return 0; } if (is_branch) check_operand_count--; - for (i = 0; i < check_operand_count; ++i) + for (i = 0; i < check_operand_count; i++) { int new_i = i; if (is_or && i == o_operand_count - 1) @@ -3828,7 +4566,7 @@ widen_instruction (bfd_byte *contents, if (xtensa_operand_get_field (isa, opcode, new_i, fmt, 0, slotbuf, &value) || xtensa_operand_decode (isa, opcode, new_i, &value)) - return FALSE; + return 0; /* PC-relative branches need adjustment, but the PC-rel operand will always have a relocation. */ @@ -3838,18 +4576,73 @@ widen_instruction (bfd_byte *contents, || xtensa_operand_encode (isa, o_opcode, i, &newval) || xtensa_operand_set_field (isa, o_opcode, i, o_fmt, 0, o_slotbuf, newval)) - return FALSE; + return 0; } if (xtensa_format_set_slot (isa, o_fmt, 0, o_insnbuf, o_slotbuf)) - return FALSE; + return 0; - if (do_it) - xtensa_insnbuf_to_chars (isa, o_insnbuf, contents + offset, - content_length - offset); - return TRUE; + return o_insnbuf; } } + return 0; +} + + +/* Attempt to widen an instruction. If the widening is valid, perform + the action in-place directly into the contents and return TRUE. Otherwise, + the return value is FALSE and the contents are not modified. */ + +static bfd_boolean +widen_instruction (bfd_byte *contents, + bfd_size_type content_length, + bfd_size_type offset) +{ + xtensa_opcode opcode; + bfd_size_type insn_len; + xtensa_isa isa = xtensa_default_isa; + xtensa_format fmt; + xtensa_insnbuf o_insnbuf; + + static xtensa_insnbuf insnbuf = NULL; + static xtensa_insnbuf slotbuf = NULL; + + if (insnbuf == NULL) + { + insnbuf = xtensa_insnbuf_alloc (isa); + slotbuf = xtensa_insnbuf_alloc (isa); + } + + BFD_ASSERT (offset < content_length); + + if (content_length < 2) + return FALSE; + + /* We will hand-code a few of these for a little while. + These have all been specified in the assembler aleady. */ + xtensa_insnbuf_from_chars (isa, insnbuf, &contents[offset], + content_length - offset); + fmt = xtensa_format_decode (isa, insnbuf); + if (xtensa_format_num_slots (isa, fmt) != 1) + return FALSE; + + if (xtensa_format_get_slot (isa, fmt, 0, insnbuf, slotbuf) != 0) + return FALSE; + + opcode = xtensa_opcode_decode (isa, fmt, 0, slotbuf); + if (opcode == XTENSA_UNDEFINED) + return FALSE; + insn_len = xtensa_format_length (isa, fmt); + if (insn_len > content_length) + return FALSE; + + o_insnbuf = can_widen_instruction (slotbuf, fmt, opcode); + if (o_insnbuf) + { + xtensa_insnbuf_to_chars (isa, o_insnbuf, contents + offset, + content_length - offset); + return TRUE; + } return FALSE; } @@ -3879,7 +4672,7 @@ elf_xtensa_do_asm_simplify (bfd_byte *contents, if (content_length < address) { - *error_message = _("Attempt to convert L32R/CALLX to CALL failed"); + *error_message = _("attempt to convert L32R/CALLX to CALL failed"); return bfd_reloc_other; } @@ -3887,15 +4680,15 @@ elf_xtensa_do_asm_simplify (bfd_byte *contents, direct_call_opcode = swap_callx_for_call_opcode (opcode); if (direct_call_opcode == XTENSA_UNDEFINED) { - *error_message = _("Attempt to convert L32R/CALLX to CALL failed"); + *error_message = _("attempt to convert L32R/CALLX to CALL failed"); return bfd_reloc_other; } - + /* Assemble a NOP ("or a1, a1, a1") into the 0 byte offset. */ core_format = xtensa_format_lookup (isa, "x24"); opcode = xtensa_opcode_lookup (isa, "or"); xtensa_opcode_encode (isa, core_format, 0, slotbuf, opcode); - for (opn = 0; opn < 3; opn++) + for (opn = 0; opn < 3; opn++) { uint32 regno = 1; xtensa_operand_encode (isa, opcode, opn, ®no); @@ -3963,7 +4756,7 @@ swap_callx_for_call_opcode (xtensa_opcode opcode) #define CONST16_TARGET_REG_OPERAND 0 #define CALLN_SOURCE_OPERAND 0 -static xtensa_opcode +static xtensa_opcode get_expanded_call_opcode (bfd_byte *buf, int bufsize, bfd_boolean *p_uses_l32r) { static xtensa_insnbuf insnbuf = NULL; @@ -4039,7 +4832,7 @@ get_expanded_call_opcode (bfd_byte *buf, int bufsize, bfd_boolean *p_uses_l32r) || xtensa_format_get_slot (isa, fmt, 0, insnbuf, slotbuf)) return XTENSA_UNDEFINED; opcode = xtensa_opcode_decode (isa, fmt, 0, slotbuf); - if (opcode == XTENSA_UNDEFINED + if (opcode == XTENSA_UNDEFINED || !is_indirect_call_opcode (opcode)) return XTENSA_UNDEFINED; @@ -4070,7 +4863,7 @@ get_expanded_call_opcode (bfd_byte *buf, int bufsize, bfd_boolean *p_uses_l32r) For efficiency, an r_reloc also contains a "target_offset" field to cache the target-section-relative offset value that is represented by the relocation. - + The r_reloc also contains a virtual offset that allows multiple inserted literals to be placed at the same "address" with different offsets. */ @@ -4197,7 +4990,7 @@ print_r_reloc (FILE *fp, const r_reloc *r_rel) fprintf (fp, " + "); fprintf_vma (fp, r_rel->virtual_offset); } - + fprintf (fp, ")"); } @@ -4317,7 +5110,7 @@ typedef struct value_map_hash_table_struct value_map_hash_table; struct literal_value_struct { - r_reloc r_rel; + r_reloc r_rel; unsigned long value; bfd_boolean is_abs_literal; }; @@ -4358,7 +5151,7 @@ literal_value_equal (const literal_value *src1, { struct elf_link_hash_entry *h1, *h2; - if (r_reloc_is_const (&src1->r_rel) != r_reloc_is_const (&src2->r_rel)) + if (r_reloc_is_const (&src1->r_rel) != r_reloc_is_const (&src2->r_rel)) return FALSE; if (r_reloc_is_const (&src1->r_rel)) @@ -4370,13 +5163,13 @@ literal_value_equal (const literal_value *src1, if (src1->r_rel.target_offset != src2->r_rel.target_offset) return FALSE; - + if (src1->r_rel.virtual_offset != src2->r_rel.virtual_offset) return FALSE; if (src1->value != src2->value) return FALSE; - + /* Now check for the same section (if defined) or the same elf_hash (if undefined or weak). */ h1 = r_reloc_get_hash_entry (&src1->r_rel); @@ -4418,7 +5211,7 @@ value_map_hash_table_init (void) values->count = 0; values->buckets = (value_map **) bfd_zmalloc (sizeof (value_map *) * values->bucket_count); - if (values->buckets == NULL) + if (values->buckets == NULL) { free (values); return NULL; @@ -4457,7 +5250,7 @@ literal_value_hash (const literal_value *src) hash_val += hash_bfd_vma (src->is_abs_literal * 1000); hash_val += hash_bfd_vma (src->r_rel.target_offset); hash_val += hash_bfd_vma (src->r_rel.virtual_offset); - + /* Now check for the same section and the same elf_hash. */ if (r_reloc_is_defined (&src->r_rel)) sec_or_hash = r_reloc_get_section (&src->r_rel); @@ -4523,7 +5316,7 @@ add_value_map (value_map_hash_table *map, *bucket_p = val_e; map->count++; /* FIXME: Consider resizing the hash table if we get too many entries. */ - + return val_e; } @@ -4533,23 +5326,23 @@ add_value_map (value_map_hash_table *map, /* The following text actions are generated: - "ta_remove_insn" remove an instruction or instructions - "ta_remove_longcall" convert longcall to call + "ta_remove_insn" remove an instruction or instructions + "ta_remove_longcall" convert longcall to call "ta_convert_longcall" convert longcall to nop/call - "ta_narrow_insn" narrow a wide instruction - "ta_widen" widen a narrow instruction - "ta_fill" add fill or remove fill + "ta_narrow_insn" narrow a wide instruction + "ta_widen" widen a narrow instruction + "ta_fill" add fill or remove fill removed < 0 is a fill; branches to the fill address will be changed to address + fill size (e.g., address - removed) removed >= 0 branches to the fill address will stay unchanged - "ta_remove_literal" remove a literal; this action is + "ta_remove_literal" remove a literal; this action is indicated when a literal is removed - or replaced. - "ta_add_literal" insert a new literal; this action is - indicated when a literal has been moved. - It may use a virtual_offset because + or replaced. + "ta_add_literal" insert a new literal; this action is + indicated when a literal has been moved. + It may use a virtual_offset because multiple literals can be placed at the - same location. + same location. For each of these text actions, we also record the number of bytes removed by performing the text action. In the case of a "ta_widen" @@ -4562,12 +5355,12 @@ typedef enum text_action_enum_t text_action_t; enum text_action_enum_t { ta_none, - ta_remove_insn, /* removed = -size */ - ta_remove_longcall, /* removed = -size */ - ta_convert_longcall, /* removed = 0 */ - ta_narrow_insn, /* removed = -1 */ - ta_widen_insn, /* removed = +1 */ - ta_fill, /* removed = +size */ + ta_remove_insn, /* removed = -size */ + ta_remove_longcall, /* removed = -size */ + ta_convert_longcall, /* removed = 0 */ + ta_narrow_insn, /* removed = -1 */ + ta_widen_insn, /* removed = +1 */ + ta_fill, /* removed = +size */ ta_remove_literal, ta_add_literal }; @@ -4582,35 +5375,49 @@ struct text_action_struct bfd_vma virtual_offset; /* Zero except for adding literals. */ int removed_bytes; literal_value value; /* Only valid when adding literals. */ +}; - text_action *next; +struct removal_by_action_entry_struct +{ + bfd_vma offset; + int removed; + int eq_removed; + int eq_removed_before_fill; +}; +typedef struct removal_by_action_entry_struct removal_by_action_entry; + +struct removal_by_action_map_struct +{ + unsigned n_entries; + removal_by_action_entry *entry; }; +typedef struct removal_by_action_map_struct removal_by_action_map; /* List of all of the actions taken on a text section. */ struct text_action_list_struct { - text_action *head; + unsigned count; + splay_tree tree; + removal_by_action_map map; }; static text_action * find_fill_action (text_action_list *l, asection *sec, bfd_vma offset) { - text_action **m_p; + text_action a; /* It is not necessary to fill at the end of a section. */ if (sec->size == offset) return NULL; - for (m_p = &l->head; *m_p && (*m_p)->offset <= offset; m_p = &(*m_p)->next) - { - text_action *t = *m_p; - /* When the action is another fill at the same address, - just increase the size. */ - if (t->offset == offset && t->action == ta_fill) - return t; - } + a.offset = offset; + a.action = ta_fill; + + splay_tree_node node = splay_tree_lookup (l->tree, (splay_tree_key)&a); + if (node) + return (text_action *)node->value; return NULL; } @@ -4658,20 +5465,63 @@ adjust_fill_action (text_action *ta, int fill_diff) } +static int +text_action_compare (splay_tree_key a, splay_tree_key b) +{ + text_action *pa = (text_action *)a; + text_action *pb = (text_action *)b; + static const int action_priority[] = + { + [ta_fill] = 0, + [ta_none] = 1, + [ta_convert_longcall] = 2, + [ta_narrow_insn] = 3, + [ta_remove_insn] = 4, + [ta_remove_longcall] = 5, + [ta_remove_literal] = 6, + [ta_widen_insn] = 7, + [ta_add_literal] = 8, + }; + + if (pa->offset == pb->offset) + { + if (pa->action == pb->action) + return 0; + return action_priority[pa->action] - action_priority[pb->action]; + } + else + return pa->offset < pb->offset ? -1 : 1; +} + +static text_action * +action_first (text_action_list *action_list) +{ + splay_tree_node node = splay_tree_min (action_list->tree); + return node ? (text_action *)node->value : NULL; +} + +static text_action * +action_next (text_action_list *action_list, text_action *action) +{ + splay_tree_node node = splay_tree_successor (action_list->tree, + (splay_tree_key)action); + return node ? (text_action *)node->value : NULL; +} + /* Add a modification action to the text. For the case of adding or removing space, modify any current fill and assume that "unreachable_space" bytes can be freely contracted. Note that a negative removed value is a fill. */ -static void +static void text_action_add (text_action_list *l, text_action_t action, asection *sec, bfd_vma offset, int removed) { - text_action **m_p; text_action *ta; + text_action a; /* It is not necessary to fill at the end of a section. */ if (action == ta_fill && sec->size == offset) @@ -4681,26 +5531,30 @@ text_action_add (text_action_list *l, if (action == ta_fill && removed == 0) return; - for (m_p = &l->head; *m_p && (*m_p)->offset <= offset; m_p = &(*m_p)->next) + a.action = action; + a.offset = offset; + + if (action == ta_fill) { - text_action *t = *m_p; - /* When the action is another fill at the same address, - just increase the size. */ - if (t->offset == offset && t->action == ta_fill && action == ta_fill) + splay_tree_node node = splay_tree_lookup (l->tree, (splay_tree_key)&a); + + if (node) { - t->removed_bytes += removed; + ta = (text_action *)node->value; + ta->removed_bytes += removed; return; } } + else + BFD_ASSERT (splay_tree_lookup (l->tree, (splay_tree_key)&a) == NULL); - /* Create a new record and fill it up. */ ta = (text_action *) bfd_zmalloc (sizeof (text_action)); ta->action = action; ta->sec = sec; ta->offset = offset; ta->removed_bytes = removed; - ta->next = (*m_p); - *m_p = ta; + splay_tree_insert (l->tree, (splay_tree_key)ta, (splay_tree_value)ta); + ++l->count; } @@ -4711,7 +5565,6 @@ text_action_add_literal (text_action_list *l, const literal_value *value, int removed) { - text_action **m_p; text_action *ta; asection *sec = r_reloc_get_section (loc); bfd_vma offset = loc->target_offset; @@ -4719,14 +5572,6 @@ text_action_add_literal (text_action_list *l, BFD_ASSERT (action == ta_add_literal); - for (m_p = &l->head; *m_p != NULL; m_p = &(*m_p)->next) - { - if ((*m_p)->offset > offset - && ((*m_p)->offset != offset - || (*m_p)->virtual_offset > virtual_offset)) - break; - } - /* Create a new record and fill it up. */ ta = (text_action *) bfd_zmalloc (sizeof (text_action)); ta->action = action; @@ -4735,111 +5580,254 @@ text_action_add_literal (text_action_list *l, ta->virtual_offset = virtual_offset; ta->value = *value; ta->removed_bytes = removed; - ta->next = (*m_p); - *m_p = ta; + + BFD_ASSERT (splay_tree_lookup (l->tree, (splay_tree_key)ta) == NULL); + splay_tree_insert (l->tree, (splay_tree_key)ta, (splay_tree_value)ta); + ++l->count; } -static bfd_vma -offset_with_removed_text (text_action_list *action_list, bfd_vma offset) +/* Find the total offset adjustment for the relaxations specified by + text_actions, beginning from a particular starting action. This is + typically used from offset_with_removed_text to search an entire list of + actions, but it may also be called directly when adjusting adjacent offsets + so that each search may begin where the previous one left off. */ + +static int +removed_by_actions (text_action_list *action_list, + text_action **p_start_action, + bfd_vma offset, + bfd_boolean before_fill) { text_action *r; int removed = 0; - for (r = action_list->head; r && r->offset <= offset; r = r->next) + r = *p_start_action; + if (r) + { + splay_tree_node node = splay_tree_lookup (action_list->tree, + (splay_tree_key)r); + BFD_ASSERT (node != NULL && r == (text_action *)node->value); + } + + while (r) { - if (r->offset < offset - || (r->action == ta_fill && r->removed_bytes < 0)) - removed += r->removed_bytes; + if (r->offset > offset) + break; + + if (r->offset == offset + && (before_fill || r->action != ta_fill || r->removed_bytes >= 0)) + break; + + removed += r->removed_bytes; + + r = action_next (action_list, r); } - return (offset - removed); + *p_start_action = r; + return removed; } static bfd_vma -offset_with_removed_text_before_fill (text_action_list *action_list, - bfd_vma offset) +offset_with_removed_text (text_action_list *action_list, bfd_vma offset) { - text_action *r; - int removed = 0; + text_action *r = action_first (action_list); - for (r = action_list->head; r && r->offset < offset; r = r->next) - removed += r->removed_bytes; - - return (offset - removed); + return offset - removed_by_actions (action_list, &r, offset, FALSE); } -/* The find_insn_action routine will only find non-fill actions. */ +static unsigned +action_list_count (text_action_list *action_list) +{ + return action_list->count; +} -static text_action * -find_insn_action (text_action_list *action_list, bfd_vma offset) +typedef struct map_action_fn_context_struct map_action_fn_context; +struct map_action_fn_context_struct +{ + int removed; + removal_by_action_map map; + bfd_boolean eq_complete; +}; + +static int +map_action_fn (splay_tree_node node, void *p) { - text_action *t; - for (t = action_list->head; t; t = t->next) + map_action_fn_context *ctx = p; + text_action *r = (text_action *)node->value; + removal_by_action_entry *ientry = ctx->map.entry + ctx->map.n_entries; + + if (ctx->map.n_entries && (ientry - 1)->offset == r->offset) { - if (t->offset == offset) + --ientry; + } + else + { + ++ctx->map.n_entries; + ctx->eq_complete = FALSE; + ientry->offset = r->offset; + ientry->eq_removed_before_fill = ctx->removed; + } + + if (!ctx->eq_complete) + { + if (r->action != ta_fill || r->removed_bytes >= 0) { - switch (t->action) - { - case ta_none: - case ta_fill: - break; - case ta_remove_insn: - case ta_remove_longcall: - case ta_convert_longcall: - case ta_narrow_insn: - case ta_widen_insn: - return t; - case ta_remove_literal: - case ta_add_literal: - BFD_ASSERT (0); - break; - } + ientry->eq_removed = ctx->removed; + ctx->eq_complete = TRUE; } + else + ientry->eq_removed = ctx->removed + r->removed_bytes; } - return NULL; + + ctx->removed += r->removed_bytes; + ientry->removed = ctx->removed; + return 0; } +static void +map_removal_by_action (text_action_list *action_list) +{ + map_action_fn_context ctx; -#if DEBUG + ctx.removed = 0; + ctx.map.n_entries = 0; + ctx.map.entry = bfd_malloc (action_list_count (action_list) * + sizeof (removal_by_action_entry)); + ctx.eq_complete = FALSE; -static void -print_action_list (FILE *fp, text_action_list *action_list) + splay_tree_foreach (action_list->tree, map_action_fn, &ctx); + action_list->map = ctx.map; +} + +static int +removed_by_actions_map (text_action_list *action_list, bfd_vma offset, + bfd_boolean before_fill) { - text_action *r; + unsigned a, b; - fprintf (fp, "Text Action\n"); - for (r = action_list->head; r != NULL; r = r->next) + if (!action_list->map.entry) + map_removal_by_action (action_list); + + if (!action_list->map.n_entries) + return 0; + + a = 0; + b = action_list->map.n_entries; + + while (b - a > 1) { - const char *t = "unknown"; - switch (r->action) - { - case ta_remove_insn: - t = "remove_insn"; break; - case ta_remove_longcall: - t = "remove_longcall"; break; - case ta_convert_longcall: - t = "remove_longcall"; break; - case ta_narrow_insn: - t = "narrow_insn"; break; - case ta_widen_insn: - t = "widen_insn"; break; - case ta_fill: - t = "fill"; break; - case ta_none: - t = "none"; break; - case ta_remove_literal: - t = "remove_literal"; break; - case ta_add_literal: - t = "add_literal"; break; - } + unsigned c = (a + b) / 2; + + if (action_list->map.entry[c].offset <= offset) + a = c; + else + b = c; + } - fprintf (fp, "%s: %s[0x%lx] \"%s\" %d\n", - r->sec->owner->filename, - r->sec->name, r->offset, t, r->removed_bytes); + if (action_list->map.entry[a].offset < offset) + { + return action_list->map.entry[a].removed; } + else if (action_list->map.entry[a].offset == offset) + { + return before_fill ? + action_list->map.entry[a].eq_removed_before_fill : + action_list->map.entry[a].eq_removed; + } + else + { + return 0; + } +} + +static bfd_vma +offset_with_removed_text_map (text_action_list *action_list, bfd_vma offset) +{ + int removed = removed_by_actions_map (action_list, offset, FALSE); + return offset - removed; +} + + +/* The find_insn_action routine will only find non-fill actions. */ + +static text_action * +find_insn_action (text_action_list *action_list, bfd_vma offset) +{ + static const text_action_t action[] = + { + ta_convert_longcall, + ta_remove_longcall, + ta_widen_insn, + ta_narrow_insn, + ta_remove_insn, + }; + text_action a; + unsigned i; + + a.offset = offset; + for (i = 0; i < sizeof (action) / sizeof (*action); ++i) + { + splay_tree_node node; + + a.action = action[i]; + node = splay_tree_lookup (action_list->tree, (splay_tree_key)&a); + if (node) + return (text_action *)node->value; + } + return NULL; +} + + +#if DEBUG + +static void +print_action (FILE *fp, text_action *r) +{ + const char *t = "unknown"; + switch (r->action) + { + case ta_remove_insn: + t = "remove_insn"; break; + case ta_remove_longcall: + t = "remove_longcall"; break; + case ta_convert_longcall: + t = "convert_longcall"; break; + case ta_narrow_insn: + t = "narrow_insn"; break; + case ta_widen_insn: + t = "widen_insn"; break; + case ta_fill: + t = "fill"; break; + case ta_none: + t = "none"; break; + case ta_remove_literal: + t = "remove_literal"; break; + case ta_add_literal: + t = "add_literal"; break; + } + + fprintf (fp, "%s: %s[0x%lx] \"%s\" %d\n", + r->sec->owner->filename, + r->sec->name, (unsigned long) r->offset, t, r->removed_bytes); +} + +static int +print_action_list_fn (splay_tree_node node, void *p) +{ + text_action *r = (text_action *)node->value; + + print_action (p, r); + return 0; +} + +static void +print_action_list (FILE *fp, text_action_list *action_list) +{ + fprintf (fp, "Text Action\n"); + splay_tree_foreach (action_list->tree, print_action_list_fn, fp); } #endif /* DEBUG */ @@ -4854,6 +5842,7 @@ print_action_list (FILE *fp, text_action_list *action_list) by the "from" offset field. */ typedef struct removed_literal_struct removed_literal; +typedef struct removed_literal_map_entry_struct removed_literal_map_entry; typedef struct removed_literal_list_struct removed_literal_list; struct removed_literal_struct @@ -4863,10 +5852,19 @@ struct removed_literal_struct removed_literal *next; }; +struct removed_literal_map_entry_struct +{ + bfd_vma addr; + removed_literal *literal; +}; + struct removed_literal_list_struct { removed_literal *head; removed_literal *tail; + + unsigned n_map; + removed_literal_map_entry *map; }; @@ -4888,9 +5886,9 @@ add_removed_literal (removed_literal_list *removed_list, else new_r->to.abfd = NULL; new_r->next = NULL; - + r = removed_list->head; - if (r == NULL) + if (r == NULL) { removed_list->head = new_r; removed_list->tail = new_r; @@ -4903,7 +5901,7 @@ add_removed_literal (removed_literal_list *removed_list, } else { - while (r->from.target_offset < from->target_offset && r->next) + while (r->from.target_offset < from->target_offset && r->next) { r = r->next; } @@ -4915,6 +5913,39 @@ add_removed_literal (removed_literal_list *removed_list, } } +static void +map_removed_literal (removed_literal_list *removed_list) +{ + unsigned n_map = 0; + unsigned i; + removed_literal_map_entry *map = NULL; + removed_literal *r = removed_list->head; + + for (i = 0; r; ++i, r = r->next) + { + if (i == n_map) + { + n_map = (n_map * 2) + 2; + map = bfd_realloc (map, n_map * sizeof (*map)); + } + map[i].addr = r->from.target_offset; + map[i].literal = r; + } + removed_list->map = map; + removed_list->n_map = i; +} + +static int +removed_literal_compare (const void *a, const void *b) +{ + const removed_literal_map_entry *pa = a; + const removed_literal_map_entry *pb = b; + + if (pa->addr == pb->addr) + return 0; + else + return pa->addr < pb->addr ? -1 : 1; +} /* Check if the list of removed literals contains an entry for the given address. Return the entry if found. */ @@ -4922,12 +5953,21 @@ add_removed_literal (removed_literal_list *removed_list, static removed_literal * find_removed_literal (removed_literal_list *removed_list, bfd_vma addr) { - removed_literal *r = removed_list->head; - while (r && r->from.target_offset < addr) - r = r->next; - if (r && r->from.target_offset == addr) - return r; - return NULL; + removed_literal_map_entry *p; + removed_literal *r = NULL; + + if (removed_list->map == NULL) + map_removed_literal (removed_list); + + p = bsearch (&addr, removed_list->map, removed_list->n_map, + sizeof (*removed_list->map), removed_literal_compare); + if (p) + { + while (p != removed_list->map && (p - 1)->addr == addr) + --p; + r = p->literal; + } + return r; } @@ -4981,7 +6021,7 @@ struct xtensa_relax_info_struct reallocated, the newly allocated relocations will be referenced here along with the actual size allocated. The relocation count will always be found in the section structure. */ - Elf_Internal_Rela *allocated_relocs; + Elf_Internal_Rela *allocated_relocs; unsigned relocs_count; unsigned allocated_relocs_count; }; @@ -4996,13 +6036,16 @@ struct elf_xtensa_section_data static bfd_boolean elf_xtensa_new_section_hook (bfd *abfd, asection *sec) { - struct elf_xtensa_section_data *sdata; - bfd_size_type amt = sizeof (*sdata); + if (!sec->used_by_bfd) + { + struct elf_xtensa_section_data *sdata; + size_t amt = sizeof (*sdata); - sdata = (struct elf_xtensa_section_data *) bfd_zalloc (abfd, amt); - if (sdata == NULL) - return FALSE; - sec->used_by_bfd = (void *) sdata; + sdata = bfd_zalloc (abfd, amt); + if (sdata == NULL) + return FALSE; + sec->used_by_bfd = sdata; + } return _bfd_elf_new_section_hook (abfd, sec); } @@ -5038,13 +6081,16 @@ init_xtensa_relax_info (asection *sec) relax_info->removed_list.head = NULL; relax_info->removed_list.tail = NULL; - relax_info->action_list.head = NULL; + relax_info->action_list.tree = splay_tree_new (text_action_compare, + NULL, NULL); + relax_info->action_list.map.n_entries = 0; + relax_info->action_list.map.entry = NULL; relax_info->fix_list = NULL; relax_info->fix_array = NULL; relax_info->fix_array_count = 0; - relax_info->allocated_relocs = NULL; + relax_info->allocated_relocs = NULL; relax_info->relocs_count = 0; relax_info->allocated_relocs_count = 0; } @@ -5064,12 +6110,11 @@ struct reloc_bfd_fix_struct asection *src_sec; bfd_vma src_offset; unsigned src_type; /* Relocation type. */ - - bfd *target_abfd; + asection *target_sec; bfd_vma target_offset; bfd_boolean translated; - + reloc_bfd_fix *next; }; @@ -5078,7 +6123,6 @@ static reloc_bfd_fix * reloc_bfd_fix_init (asection *src_sec, bfd_vma src_offset, unsigned src_type, - bfd *target_abfd, asection *target_sec, bfd_vma target_offset, bfd_boolean translated) @@ -5089,7 +6133,6 @@ reloc_bfd_fix_init (asection *src_sec, fix->src_sec = src_sec; fix->src_offset = src_offset; fix->src_type = src_type; - fix->target_abfd = target_abfd; fix->target_sec = target_sec; fix->target_offset = target_offset; fix->translated = translated; @@ -5202,7 +6245,7 @@ init_section_cache (section_cache_t *sec_cache) static void -clear_section_cache (section_cache_t *sec_cache) +free_section_cache (section_cache_t *sec_cache) { if (sec_cache->sec) { @@ -5210,7 +6253,6 @@ clear_section_cache (section_cache_t *sec_cache) release_internal_relocs (sec_cache->sec, sec_cache->relocs); if (sec_cache->ptbl) free (sec_cache->ptbl); - memset (sec_cache, 0, sizeof (sec_cache)); } } @@ -5251,8 +6293,8 @@ section_cache_section (section_cache_t *sec_cache, goto err; /* Fill in the new section cache. */ - clear_section_cache (sec_cache); - memset (sec_cache, 0, sizeof (sec_cache)); + free_section_cache (sec_cache); + init_section_cache (sec_cache); sec_cache->sec = sec; sec_cache->contents = contents; @@ -5464,9 +6506,12 @@ extend_ebb_bounds_forward (ebb_t *ebb) entry_end - ebb->end_offset); if (insn_block_len != (entry_end - ebb->end_offset)) { - (*_bfd_error_handler) - (_("%B(%A+0x%lx): could not decode instruction; possible configuration mismatch"), - ebb->sec->owner, ebb->sec, ebb->end_offset + insn_block_len); + _bfd_error_handler + /* xgettext:c-format */ + (_("%pB(%pA+%#" PRIx64 "): could not decode instruction; " + "possible configuration mismatch"), + ebb->sec->owner, ebb->sec, + (uint64_t) (ebb->end_offset + insn_block_len)); return FALSE; } ebb->end_offset += insn_block_len; @@ -5487,7 +6532,7 @@ extend_ebb_bounds_forward (ebb_t *ebb) new_entry = &ebb->ptbl[ebb->end_ptbl_idx + 1]; if (((new_entry->flags & XTENSA_PROP_INSN) == 0) - || ((new_entry->flags & XTENSA_PROP_INSN_NO_TRANSFORM) != 0) + || ((new_entry->flags & XTENSA_PROP_NO_TRANSFORM) != 0) || ((the_entry->flags & XTENSA_PROP_ALIGN) != 0)) break; @@ -5540,9 +6585,12 @@ extend_ebb_bounds_backward (ebb_t *ebb) ebb->start_offset - block_begin); if (insn_block_len != ebb->start_offset - block_begin) { - (*_bfd_error_handler) - (_("%B(%A+0x%lx): could not decode instruction; possible configuration mismatch"), - ebb->sec->owner, ebb->sec, ebb->end_offset + insn_block_len); + _bfd_error_handler + /* xgettext:c-format */ + (_("%pB(%pA+%#" PRIx64 "): could not decode instruction; " + "possible configuration mismatch"), + ebb->sec->owner, ebb->sec, + (uint64_t) (ebb->end_offset + insn_block_len)); return FALSE; } ebb->start_offset -= insn_block_len; @@ -5560,7 +6608,7 @@ extend_ebb_bounds_backward (ebb_t *ebb) new_entry = &ebb->ptbl[ebb->start_ptbl_idx - 1]; if ((new_entry->flags & XTENSA_PROP_INSN) == 0 - || ((new_entry->flags & XTENSA_PROP_INSN_NO_TRANSFORM) != 0) + || ((new_entry->flags & XTENSA_PROP_NO_TRANSFORM) != 0) || ((new_entry->flags & XTENSA_PROP_ALIGN) != 0)) return TRUE; if (new_entry->address + new_entry->size != the_entry->address) @@ -5683,7 +6731,7 @@ retrieve_contents (bfd *abfd, asection *sec, bfd_boolean keep_memory) sec_size = bfd_get_section_limit (abfd, sec); contents = elf_section_data (sec)->this_hdr.contents; - + if (contents == NULL && sec_size != 0) { if (!bfd_malloc_and_get_section (abfd, sec, &contents)) @@ -5692,7 +6740,7 @@ retrieve_contents (bfd *abfd, asection *sec, bfd_boolean keep_memory) free (contents); return NULL; } - if (keep_memory) + if (keep_memory) elf_section_data (sec)->this_hdr.contents = contents; } return contents; @@ -5754,23 +6802,26 @@ static bfd_boolean compute_text_actions (bfd *, asection *, struct bfd_link_info *); static bfd_boolean compute_ebb_proposed_actions (ebb_constraint *); static bfd_boolean compute_ebb_actions (ebb_constraint *); +typedef struct reloc_range_list_struct reloc_range_list; static bfd_boolean check_section_ebb_pcrels_fit - (bfd *, asection *, bfd_byte *, Elf_Internal_Rela *, const ebb_constraint *); + (bfd *, asection *, bfd_byte *, Elf_Internal_Rela *, + reloc_range_list *, const ebb_constraint *, + const xtensa_opcode *); static bfd_boolean check_section_ebb_reduces (const ebb_constraint *); static void text_action_add_proposed (text_action_list *, const ebb_constraint *, asection *); -static int compute_fill_extra_space (property_table_entry *); /* First pass: */ static bfd_boolean compute_removed_literals (bfd *, asection *, struct bfd_link_info *, value_map_hash_table *); static Elf_Internal_Rela *get_irel_at_offset (asection *, Elf_Internal_Rela *, bfd_vma); -static bfd_boolean is_removable_literal - (const source_reloc *, int, const source_reloc *, int); +static bfd_boolean is_removable_literal + (const source_reloc *, int, const source_reloc *, int, asection *, + property_table_entry *, int); static bfd_boolean remove_dead_literal (bfd *, asection *, struct bfd_link_info *, Elf_Internal_Rela *, - Elf_Internal_Rela *, source_reloc *, property_table_entry *, int); + Elf_Internal_Rela *, source_reloc *, property_table_entry *, int); static bfd_boolean identify_literal_placement (bfd *, asection *, bfd_byte *, struct bfd_link_info *, value_map_hash_table *, bfd_boolean *, Elf_Internal_Rela *, int, @@ -5787,7 +6838,7 @@ static bfd_boolean move_shared_literal static bfd_boolean relax_section (bfd *, asection *, struct bfd_link_info *); static bfd_boolean translate_section_fixes (asection *); static bfd_boolean translate_reloc_bfd_fix (reloc_bfd_fix *); -static void translate_reloc (const r_reloc *, r_reloc *); +static asection *translate_reloc (const r_reloc *, r_reloc *, asection *); static void shrink_dynamic_reloc_sections (struct bfd_link_info *, bfd *, asection *, Elf_Internal_Rela *); static bfd_boolean move_literal @@ -5800,7 +6851,7 @@ static bfd_boolean relax_property_section static bfd_boolean relax_section_symbols (bfd *, asection *); -static bfd_boolean +static bfd_boolean elf_xtensa_relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info, @@ -5883,14 +6934,14 @@ analyze_relocations (struct bfd_link_info *link_info) bfd_boolean is_relaxable = FALSE; /* Initialize the per-section relaxation info. */ - for (abfd = link_info->input_bfds; abfd != NULL; abfd = abfd->link_next) + for (abfd = link_info->input_bfds; abfd != NULL; abfd = abfd->link.next) for (sec = abfd->sections; sec != NULL; sec = sec->next) { init_xtensa_relax_info (sec); } /* Mark relaxable sections (and count relocations against each one). */ - for (abfd = link_info->input_bfds; abfd != NULL; abfd = abfd->link_next) + for (abfd = link_info->input_bfds; abfd != NULL; abfd = abfd->link.next) for (sec = abfd->sections; sec != NULL; sec = sec->next) { if (!find_relaxable_sections (abfd, sec, link_info, &is_relaxable)) @@ -5902,7 +6953,7 @@ analyze_relocations (struct bfd_link_info *link_info) return TRUE; /* Allocate space for source_relocs. */ - for (abfd = link_info->input_bfds; abfd != NULL; abfd = abfd->link_next) + for (abfd = link_info->input_bfds; abfd != NULL; abfd = abfd->link.next) for (sec = abfd->sections; sec != NULL; sec = sec->next) { xtensa_relax_info *relax_info; @@ -5914,10 +6965,12 @@ analyze_relocations (struct bfd_link_info *link_info) relax_info->src_relocs = (source_reloc *) bfd_malloc (relax_info->src_count * sizeof (source_reloc)); } + else + relax_info->src_count = 0; } /* Collect info on relocations against each relaxable section. */ - for (abfd = link_info->input_bfds; abfd != NULL; abfd = abfd->link_next) + for (abfd = link_info->input_bfds; abfd != NULL; abfd = abfd->link.next) for (sec = abfd->sections; sec != NULL; sec = sec->next) { if (!collect_source_relocs (abfd, sec, link_info)) @@ -5925,7 +6978,7 @@ analyze_relocations (struct bfd_link_info *link_info) } /* Compute the text actions. */ - for (abfd = link_info->input_bfds; abfd != NULL; abfd = abfd->link_next) + for (abfd = link_info->input_bfds; abfd != NULL; abfd = abfd->link.next) for (sec = abfd->sections; sec != NULL; sec = sec->next) { if (!compute_text_actions (abfd, sec, link_info)) @@ -5957,10 +7010,11 @@ find_relaxable_sections (bfd *abfd, bfd_boolean ok = TRUE; unsigned i; xtensa_relax_info *source_relax_info; + bfd_boolean is_l32r_reloc; internal_relocs = retrieve_internal_relocs (abfd, sec, link_info->keep_memory); - if (internal_relocs == NULL) + if (internal_relocs == NULL) return ok; contents = retrieve_contents (abfd, sec, link_info->keep_memory); @@ -5971,7 +7025,7 @@ find_relaxable_sections (bfd *abfd, } source_relax_info = get_xtensa_relax_info (sec); - for (i = 0; i < sec->reloc_count; i++) + for (i = 0; i < sec->reloc_count; i++) { Elf_Internal_Rela *irel = &internal_relocs[i]; r_reloc r_rel; @@ -6005,15 +7059,23 @@ find_relaxable_sections (bfd *abfd, continue; /* Count PC-relative operand relocations against the target section. - Note: The conditions tested here must match the conditions under + Note: The conditions tested here must match the conditions under which init_source_reloc is called in collect_source_relocs(). */ - if (is_operand_relocation (ELF32_R_TYPE (irel->r_info)) - && (!is_alt_relocation (ELF32_R_TYPE (irel->r_info)) - || is_l32r_relocation (abfd, sec, contents, irel))) - target_relax_info->src_count++; + is_l32r_reloc = FALSE; + if (is_operand_relocation (ELF32_R_TYPE (irel->r_info))) + { + xtensa_opcode opcode = + get_relocation_opcode (abfd, sec, contents, irel); + if (opcode != XTENSA_UNDEFINED) + { + is_l32r_reloc = (opcode == get_l32r_opcode ()); + if (!is_alt_relocation (ELF32_R_TYPE (irel->r_info)) + || is_l32r_reloc) + target_relax_info->src_count++; + } + } - if (is_l32r_relocation (abfd, sec, contents, irel) - && r_reloc_is_defined (&r_rel)) + if (is_l32r_reloc && r_reloc_is_defined (&r_rel)) { /* Mark the target section as relaxable. */ target_relax_info->is_relaxable_literal_section = TRUE; @@ -6043,9 +7105,9 @@ collect_source_relocs (bfd *abfd, unsigned i; bfd_size_type sec_size; - internal_relocs = retrieve_internal_relocs (abfd, sec, + internal_relocs = retrieve_internal_relocs (abfd, sec, link_info->keep_memory); - if (internal_relocs == NULL) + if (internal_relocs == NULL) return ok; sec_size = bfd_get_section_limit (abfd, sec); @@ -6057,7 +7119,7 @@ collect_source_relocs (bfd *abfd, } /* Record relocations against relaxable literal sections. */ - for (i = 0; i < sec->reloc_count; i++) + for (i = 0; i < sec->reloc_count; i++) { Elf_Internal_Rela *irel = &internal_relocs[i]; r_reloc r_rel; @@ -6115,7 +7177,7 @@ collect_source_relocs (bfd *abfd, relocations associated with ASM_EXPANDs because they were just added in the preceding loop over the relocations. */ - for (i = 0; i < sec->reloc_count; i++) + for (i = 0; i < sec->reloc_count; i++) { Elf_Internal_Rela *irel = &internal_relocs[i]; bfd_boolean is_reachable; @@ -6154,7 +7216,7 @@ collect_source_relocs (bfd *abfd, the l32r_irel. Note: The src_relocs array is not yet sorted, but it wouldn't matter anyway because we're searching by source offset instead of target offset. */ - s_reloc = find_source_reloc (target_relax_info->src_relocs, + s_reloc = find_source_reloc (target_relax_info->src_relocs, target_relax_info->src_next, sec, l32r_irel); BFD_ASSERT (s_reloc); @@ -6200,6 +7262,11 @@ is_resolvable_asm_expansion (bfd *abfd, bfd_boolean *is_reachable_p) { asection *target_sec; + asection *s; + bfd_vma first_vma; + bfd_vma last_vma; + unsigned int first_align; + unsigned int adjust; bfd_vma target_offset; r_reloc r_rel; xtensa_opcode opcode, direct_call_opcode; @@ -6213,7 +7280,7 @@ is_resolvable_asm_expansion (bfd *abfd, if (contents == NULL) return FALSE; - if (ELF32_R_TYPE (irel->r_info) != R_XTENSA_ASM_EXPAND) + if (ELF32_R_TYPE (irel->r_info) != R_XTENSA_ASM_EXPAND) return FALSE; sec_size = bfd_get_section_limit (abfd, sec); @@ -6222,7 +7289,7 @@ is_resolvable_asm_expansion (bfd *abfd, /* Optimization of longcalls that use CONST16 is not yet implemented. */ if (!uses_l32r) return FALSE; - + direct_call_opcode = swap_callx_for_call_opcode (opcode); if (direct_call_opcode == XTENSA_UNDEFINED) return FALSE; @@ -6241,20 +7308,103 @@ is_resolvable_asm_expansion (bfd *abfd, shouldn't crash regardless. */ if (!target_sec->output_section) return FALSE; - + /* For relocatable sections, we can only simplify when the output section of the target is the same as the output section of the source. */ - if (link_info->relocatable + if (bfd_link_relocatable (link_info) && (target_sec->output_section != sec->output_section || is_reloc_sym_weak (abfd, irel))) return FALSE; - self_address = (sec->output_section->vma - + sec->output_offset + irel->r_offset + 3); - dest_address = (target_sec->output_section->vma - + target_sec->output_offset + target_offset); - + if (target_sec->output_section != sec->output_section) + { + /* If the two sections are sufficiently far away that relaxation + might take the call out of range, we can't simplify. For + example, a positive displacement call into another memory + could get moved to a lower address due to literal removal, + but the destination won't move, and so the displacment might + get larger. + + If the displacement is negative, assume the destination could + move as far back as the start of the output section. The + self_address will be at least as far into the output section + as it is prior to relaxation. + + If the displacement is postive, assume the destination will be in + it's pre-relaxed location (because relaxation only makes sections + smaller). The self_address could go all the way to the beginning + of the output section. */ + + dest_address = target_sec->output_section->vma; + self_address = sec->output_section->vma; + + if (sec->output_section->vma > target_sec->output_section->vma) + self_address += sec->output_offset + irel->r_offset + 3; + else + dest_address += bfd_get_section_limit (abfd, target_sec->output_section); + /* Call targets should be four-byte aligned. */ + dest_address = (dest_address + 3) & ~3; + } + else + { + + self_address = (sec->output_section->vma + + sec->output_offset + irel->r_offset + 3); + dest_address = (target_sec->output_section->vma + + target_sec->output_offset + target_offset); + } + + /* Adjust addresses with alignments for the worst case to see if call insn + can fit. Don't relax l32r + callx to call if the target can be out of + range due to alignment. + Caller and target addresses are highest and lowest address. + Search all sections between caller and target, looking for max alignment. + The adjustment is max alignment bytes. If the alignment at the lowest + address is less than the adjustment, apply the adjustment to highest + address. */ + + /* Start from lowest address. + Lowest address aligmnet is from input section. + Initial alignment (adjust) is from input section. */ + if (dest_address > self_address) + { + s = sec->output_section; + last_vma = dest_address; + first_align = sec->alignment_power; + adjust = target_sec->alignment_power; + } + else + { + s = target_sec->output_section; + last_vma = self_address; + first_align = target_sec->alignment_power; + adjust = sec->alignment_power; + } + + first_vma = s->vma; + + /* Find the largest alignment in output section list. */ + for (; s && s->vma >= first_vma && s->vma <= last_vma ; s = s->next) + { + if (s->alignment_power > adjust) + adjust = s->alignment_power; + } + + if (adjust > first_align) + { + /* Alignment may enlarge the range, adjust highest address. */ + adjust = 1 << adjust; + if (dest_address > self_address) + { + dest_address += adjust; + } + else + { + self_address += adjust; + } + } + *is_reachable_p = pcrel_reloc_fits (direct_call_opcode, 0, self_address, dest_address); @@ -6275,7 +7425,7 @@ find_associated_l32r_irel (bfd *abfd, { unsigned i; - for (i = 0; i < sec->reloc_count; i++) + for (i = 0; i < sec->reloc_count; i++) { Elf_Internal_Rela *irel = &internal_relocs[i]; @@ -6291,6 +7441,239 @@ find_associated_l32r_irel (bfd *abfd, } +static xtensa_opcode * +build_reloc_opcodes (bfd *abfd, + asection *sec, + bfd_byte *contents, + Elf_Internal_Rela *internal_relocs) +{ + unsigned i; + xtensa_opcode *reloc_opcodes = + (xtensa_opcode *) bfd_malloc (sizeof (xtensa_opcode) * sec->reloc_count); + for (i = 0; i < sec->reloc_count; i++) + { + Elf_Internal_Rela *irel = &internal_relocs[i]; + reloc_opcodes[i] = get_relocation_opcode (abfd, sec, contents, irel); + } + return reloc_opcodes; +} + +struct reloc_range_struct +{ + bfd_vma addr; + bfd_boolean add; /* TRUE if start of a range, FALSE otherwise. */ + /* Original irel index in the array of relocations for a section. */ + unsigned irel_index; +}; +typedef struct reloc_range_struct reloc_range; + +typedef struct reloc_range_list_entry_struct reloc_range_list_entry; +struct reloc_range_list_entry_struct +{ + reloc_range_list_entry *next; + reloc_range_list_entry *prev; + Elf_Internal_Rela *irel; + xtensa_opcode opcode; + int opnum; +}; + +struct reloc_range_list_struct +{ + /* The rest of the structure is only meaningful when ok is TRUE. */ + bfd_boolean ok; + + unsigned n_range; /* Number of range markers. */ + reloc_range *range; /* Sorted range markers. */ + + unsigned first; /* Index of a first range element in the list. */ + unsigned last; /* One past index of a last range element in the list. */ + + unsigned n_list; /* Number of list elements. */ + reloc_range_list_entry *reloc; /* */ + reloc_range_list_entry list_root; +}; + +static int +reloc_range_compare (const void *a, const void *b) +{ + const reloc_range *ra = a; + const reloc_range *rb = b; + + if (ra->addr != rb->addr) + return ra->addr < rb->addr ? -1 : 1; + if (ra->add != rb->add) + return ra->add ? -1 : 1; + return 0; +} + +static void +build_reloc_ranges (bfd *abfd, asection *sec, + bfd_byte *contents, + Elf_Internal_Rela *internal_relocs, + xtensa_opcode *reloc_opcodes, + reloc_range_list *list) +{ + unsigned i; + size_t n = 0; + size_t max_n = 0; + reloc_range *ranges = NULL; + reloc_range_list_entry *reloc = + bfd_malloc (sec->reloc_count * sizeof (*reloc)); + + memset (list, 0, sizeof (*list)); + list->ok = TRUE; + + for (i = 0; i < sec->reloc_count; i++) + { + Elf_Internal_Rela *irel = &internal_relocs[i]; + int r_type = ELF32_R_TYPE (irel->r_info); + reloc_howto_type *howto = &elf_howto_table[r_type]; + r_reloc r_rel; + + if (r_type == R_XTENSA_ASM_SIMPLIFY + || r_type == R_XTENSA_32_PCREL + || !howto->pc_relative) + continue; + + r_reloc_init (&r_rel, abfd, irel, contents, + bfd_get_section_limit (abfd, sec)); + + if (r_reloc_get_section (&r_rel) != sec) + continue; + + if (n + 2 > max_n) + { + max_n = (max_n + 2) * 2; + ranges = bfd_realloc (ranges, max_n * sizeof (*ranges)); + } + + ranges[n].addr = irel->r_offset; + ranges[n + 1].addr = r_rel.target_offset; + + ranges[n].add = ranges[n].addr < ranges[n + 1].addr; + ranges[n + 1].add = !ranges[n].add; + + ranges[n].irel_index = i; + ranges[n + 1].irel_index = i; + + n += 2; + + reloc[i].irel = irel; + + /* Every relocation won't possibly be checked in the optimized version of + check_section_ebb_pcrels_fit, so this needs to be done here. */ + if (is_alt_relocation (ELF32_R_TYPE (irel->r_info))) + { + /* None of the current alternate relocs are PC-relative, + and only PC-relative relocs matter here. */ + } + else + { + xtensa_opcode opcode; + int opnum; + + if (reloc_opcodes) + opcode = reloc_opcodes[i]; + else + opcode = get_relocation_opcode (abfd, sec, contents, irel); + + if (opcode == XTENSA_UNDEFINED) + { + list->ok = FALSE; + break; + } + + opnum = get_relocation_opnd (opcode, ELF32_R_TYPE (irel->r_info)); + if (opnum == XTENSA_UNDEFINED) + { + list->ok = FALSE; + break; + } + + /* Record relocation opcode and opnum as we've calculated them + anyway and they won't change. */ + reloc[i].opcode = opcode; + reloc[i].opnum = opnum; + } + } + + if (list->ok) + { + ranges = bfd_realloc (ranges, n * sizeof (*ranges)); + qsort (ranges, n, sizeof (*ranges), reloc_range_compare); + + list->n_range = n; + list->range = ranges; + list->reloc = reloc; + list->list_root.prev = &list->list_root; + list->list_root.next = &list->list_root; + } + else + { + free (ranges); + free (reloc); + } +} + +static void reloc_range_list_append (reloc_range_list *list, + unsigned irel_index) +{ + reloc_range_list_entry *entry = list->reloc + irel_index; + + entry->prev = list->list_root.prev; + entry->next = &list->list_root; + entry->prev->next = entry; + entry->next->prev = entry; + ++list->n_list; +} + +static void reloc_range_list_remove (reloc_range_list *list, + unsigned irel_index) +{ + reloc_range_list_entry *entry = list->reloc + irel_index; + + entry->next->prev = entry->prev; + entry->prev->next = entry->next; + --list->n_list; +} + +/* Update relocation list object so that it lists all relocations that cross + [first; last] range. Range bounds should not decrease with successive + invocations. */ +static void reloc_range_list_update_range (reloc_range_list *list, + bfd_vma first, bfd_vma last) +{ + /* This should not happen: EBBs are iterated from lower addresses to higher. + But even if that happens there's no need to break: just flush current list + and start from scratch. */ + if ((list->last > 0 && list->range[list->last - 1].addr > last) || + (list->first > 0 && list->range[list->first - 1].addr >= first)) + { + list->first = 0; + list->last = 0; + list->n_list = 0; + list->list_root.next = &list->list_root; + list->list_root.prev = &list->list_root; + fprintf (stderr, "%s: move backwards requested\n", __func__); + } + + for (; list->last < list->n_range && + list->range[list->last].addr <= last; ++list->last) + if (list->range[list->last].add) + reloc_range_list_append (list, list->range[list->last].irel_index); + + for (; list->first < list->n_range && + list->range[list->first].addr < first; ++list->first) + if (!list->range[list->first].add) + reloc_range_list_remove (list, list->range[list->first].irel_index); +} + +static void free_reloc_range_list (reloc_range_list *list) +{ + free (list->range); + free (list->reloc); +} + /* The compute_text_actions function will build a list of potential transformation actions for code in the extended basic block of each longcall that is optimized to a direct call. From this list we @@ -6307,6 +7690,7 @@ compute_text_actions (bfd *abfd, asection *sec, struct bfd_link_info *link_info) { + xtensa_opcode *reloc_opcodes = NULL; xtensa_relax_info *relax_info; bfd_byte *contents; Elf_Internal_Rela *internal_relocs; @@ -6315,14 +7699,13 @@ compute_text_actions (bfd *abfd, property_table_entry *prop_table = 0; int ptblsize = 0; bfd_size_type sec_size; - static bfd_boolean no_insn_move = FALSE; + reloc_range_list relevant_relocs; - if (no_insn_move) - return ok; - - /* Do nothing if the section contains no optimized longcalls. */ relax_info = get_xtensa_relax_info (sec); BFD_ASSERT (relax_info); + BFD_ASSERT (relax_info->src_next == relax_info->src_count); + + /* Do nothing if the section contains no optimized longcalls. */ if (!relax_info->is_relaxable_asm_section) return ok; @@ -6349,6 +7732,12 @@ compute_text_actions (bfd *abfd, goto error_return; } + /* Precompute the opcode for each relocation. */ + reloc_opcodes = build_reloc_opcodes (abfd, sec, contents, internal_relocs); + + build_reloc_ranges (abfd, sec, contents, internal_relocs, reloc_opcodes, + &relevant_relocs); + for (i = 0; i < sec->reloc_count; i++) { Elf_Internal_Rela *irel = &internal_relocs[i]; @@ -6366,9 +7755,12 @@ compute_text_actions (bfd *abfd, simplify_size = get_asm_simplify_size (contents, sec_size, r_offset); if (simplify_size == 0) { - (*_bfd_error_handler) - (_("%B(%A+0x%lx): could not decode instruction for XTENSA_ASM_SIMPLIFY relocation; possible configuration mismatch"), - sec->owner, sec, r_offset); + _bfd_error_handler + /* xgettext:c-format */ + (_("%pB(%pA+%#" PRIx64 "): could not decode instruction for " + "XTENSA_ASM_SIMPLIFY relocation; " + "possible configuration mismatch"), + sec->owner, sec, (uint64_t) r_offset); continue; } @@ -6397,7 +7789,7 @@ compute_text_actions (bfd *abfd, the_entry++; } - if (the_entry->flags & XTENSA_PROP_INSN_NO_TRANSFORM) + if (the_entry->flags & XTENSA_PROP_NO_TRANSFORM) /* NO_REORDER is OK */ continue; @@ -6416,7 +7808,9 @@ compute_text_actions (bfd *abfd, || !compute_ebb_proposed_actions (&ebb_table) || !compute_ebb_actions (&ebb_table) || !check_section_ebb_pcrels_fit (abfd, sec, contents, - internal_relocs, &ebb_table) + internal_relocs, + &relevant_relocs, + &ebb_table, reloc_opcodes) || !check_section_ebb_reduces (&ebb_table)) { /* If anything goes wrong or we get unlucky and something does @@ -6438,21 +7832,42 @@ compute_text_actions (bfd *abfd, free_ebb_constraint (&ebb_table); } + free_reloc_range_list (&relevant_relocs); + #if DEBUG - if (relax_info->action_list.head) + if (action_list_count (&relax_info->action_list)) print_action_list (stderr, &relax_info->action_list); #endif -error_return: + error_return: release_contents (sec, contents); release_internal_relocs (sec, internal_relocs); if (prop_table) free (prop_table); + if (reloc_opcodes) + free (reloc_opcodes); return ok; } +/* Do not widen an instruction if it is preceeded by a + loop opcode. It might cause misalignment. */ + +static bfd_boolean +prev_instr_is_a_loop (bfd_byte *contents, + bfd_size_type content_length, + bfd_size_type offset) +{ + xtensa_opcode prev_opcode; + + if (offset < 3) + return FALSE; + prev_opcode = insn_decode_opcode (contents, content_length, offset-3, 0); + return (xtensa_opcode_is_loop (xtensa_default_isa, prev_opcode) == 1); +} + + /* Find all of the possible actions for an extended basic block. */ bfd_boolean @@ -6461,13 +7876,24 @@ compute_ebb_proposed_actions (ebb_constraint *ebb_table) const ebb_t *ebb = &ebb_table->ebb; unsigned rel_idx = ebb->start_reloc_idx; property_table_entry *entry, *start_entry, *end_entry; + bfd_vma offset = 0; + xtensa_isa isa = xtensa_default_isa; + xtensa_format fmt; + static xtensa_insnbuf insnbuf = NULL; + static xtensa_insnbuf slotbuf = NULL; + + if (insnbuf == NULL) + { + insnbuf = xtensa_insnbuf_alloc (isa); + slotbuf = xtensa_insnbuf_alloc (isa); + } start_entry = &ebb->ptbl[ebb->start_ptbl_idx]; end_entry = &ebb->ptbl[ebb->end_ptbl_idx]; for (entry = start_entry; entry <= end_entry; entry++) { - bfd_vma offset, start_offset, end_offset; + bfd_vma start_offset, end_offset; bfd_size_type insn_len; start_offset = entry->address - ebb->sec->vma; @@ -6489,15 +7915,9 @@ compute_ebb_proposed_actions (ebb_constraint *ebb_table) insn_len = insn_decode_len (ebb->contents, ebb->content_length, offset); + if (insn_len == 0) + goto decode_error; - /* Propose no actions for a section with an undecodable offset. */ - if (insn_len == 0) - { - (*_bfd_error_handler) - (_("%B(%A+0x%lx): could not decode instruction; possible configuration mismatch"), - ebb->sec->owner, ebb->sec, offset); - return FALSE; - } if (check_branch_target_aligned_address (offset, insn_len)) align_type = EBB_REQUIRE_TGT_ALIGN; @@ -6524,65 +7944,63 @@ compute_ebb_proposed_actions (ebb_constraint *ebb_table) { bfd_size_type simplify_size; - simplify_size = get_asm_simplify_size (ebb->contents, + simplify_size = get_asm_simplify_size (ebb->contents, ebb->content_length, irel->r_offset); if (simplify_size == 0) - { - (*_bfd_error_handler) - (_("%B(%A+0x%lx): could not decode instruction for XTENSA_ASM_SIMPLIFY relocation; possible configuration mismatch"), - ebb->sec->owner, ebb->sec, offset); - return FALSE; - } + goto decode_error; ebb_propose_action (ebb_table, EBB_NO_ALIGN, 0, ta_convert_longcall, offset, 0, TRUE); - + offset += simplify_size; continue; } - insn_len = insn_decode_len (ebb->contents, ebb->content_length, - offset); - /* If the instruction is undecodable, then report an error. */ - if (insn_len == 0) + if (offset + MIN_INSN_LENGTH > ebb->content_length) + goto decode_error; + xtensa_insnbuf_from_chars (isa, insnbuf, &ebb->contents[offset], + ebb->content_length - offset); + fmt = xtensa_format_decode (isa, insnbuf); + if (fmt == XTENSA_UNDEFINED) + goto decode_error; + insn_len = xtensa_format_length (isa, fmt); + if (insn_len == (bfd_size_type) XTENSA_UNDEFINED) + goto decode_error; + + if (xtensa_format_num_slots (isa, fmt) != 1) { - (*_bfd_error_handler) - (_("%B(%A+0x%lx): could not decode instruction; possible configuration mismatch"), - ebb->sec->owner, ebb->sec, offset); - return FALSE; + offset += insn_len; + continue; } - + + xtensa_format_get_slot (isa, fmt, 0, insnbuf, slotbuf); + opcode = xtensa_opcode_decode (isa, fmt, 0, slotbuf); + if (opcode == XTENSA_UNDEFINED) + goto decode_error; + if ((entry->flags & XTENSA_PROP_INSN_NO_DENSITY) == 0 - && (entry->flags & XTENSA_PROP_INSN_NO_TRANSFORM) == 0 - && narrow_instruction (ebb->contents, ebb->content_length, - offset, FALSE)) + && (entry->flags & XTENSA_PROP_NO_TRANSFORM) == 0 + && can_narrow_instruction (slotbuf, fmt, opcode) != 0) { /* Add an instruction narrow action. */ ebb_propose_action (ebb_table, EBB_NO_ALIGN, 0, ta_narrow_insn, offset, 0, FALSE); - offset += insn_len; - continue; } - if ((entry->flags & XTENSA_PROP_INSN_NO_TRANSFORM) == 0 - && widen_instruction (ebb->contents, ebb->content_length, - offset, FALSE)) + else if ((entry->flags & XTENSA_PROP_NO_TRANSFORM) == 0 + && can_widen_instruction (slotbuf, fmt, opcode) != 0 + && ! prev_instr_is_a_loop (ebb->contents, + ebb->content_length, offset)) { /* Add an instruction widen action. */ ebb_propose_action (ebb_table, EBB_NO_ALIGN, 0, ta_widen_insn, offset, 0, FALSE); - offset += insn_len; - continue; } - opcode = insn_decode_opcode (ebb->contents, ebb->content_length, - offset, 0); - if (xtensa_opcode_is_loop (xtensa_default_isa, opcode)) + else if (xtensa_opcode_is_loop (xtensa_default_isa, opcode) == 1) { /* Check for branch targets. */ ebb_propose_action (ebb_table, EBB_REQUIRE_LOOP_ALIGN, 0, ta_none, offset, 0, TRUE); - offset += insn_len; - continue; } offset += insn_len; @@ -6596,6 +8014,14 @@ compute_ebb_proposed_actions (ebb_constraint *ebb_table) } return TRUE; + + decode_error: + _bfd_error_handler + /* xgettext:c-format */ + (_("%pB(%pA+%#" PRIx64 "): could not decode instruction; " + "possible configuration mismatch"), + ebb->sec->owner, ebb->sec, (uint64_t) offset); + return FALSE; } @@ -6808,6 +8234,8 @@ compute_ebb_actions (ebb_constraint *ebb_table) bad_alignment = FALSE; break; } + if (new_action->do_action) + removed_bytes += new_action->removed_bytes; } if (!bad_alignment) { @@ -6842,7 +8270,7 @@ compute_ebb_actions (ebb_constraint *ebb_table) BFD_ASSERT (action->action == ta_fill); BFD_ASSERT (ebb->ends_unreachable->flags & XTENSA_PROP_UNREACHABLE); - extra_space = compute_fill_extra_space (ebb->ends_unreachable); + extra_space = xtensa_compute_fill_extra_space (ebb->ends_unreachable); br = action->removed_bytes + removed_bytes + extra_space; br = br & ((1 << ebb->sec->alignment_power ) - 1); @@ -6852,6 +8280,178 @@ compute_ebb_actions (ebb_constraint *ebb_table) } +/* The xlate_map is a sorted array of address mappings designed to + answer the offset_with_removed_text() query with a binary search instead + of a linear search through the section's action_list. */ + +typedef struct xlate_map_entry xlate_map_entry_t; +typedef struct xlate_map xlate_map_t; + +struct xlate_map_entry +{ + bfd_vma orig_address; + bfd_vma new_address; + unsigned size; +}; + +struct xlate_map +{ + unsigned entry_count; + xlate_map_entry_t *entry; +}; + + +static int +xlate_compare (const void *a_v, const void *b_v) +{ + const xlate_map_entry_t *a = (const xlate_map_entry_t *) a_v; + const xlate_map_entry_t *b = (const xlate_map_entry_t *) b_v; + if (a->orig_address < b->orig_address) + return -1; + if (a->orig_address > (b->orig_address + b->size - 1)) + return 1; + return 0; +} + + +static bfd_vma +xlate_offset_with_removed_text (const xlate_map_t *map, + text_action_list *action_list, + bfd_vma offset) +{ + void *r; + xlate_map_entry_t *e; + struct xlate_map_entry se; + + if (map == NULL) + return offset_with_removed_text (action_list, offset); + + if (map->entry_count == 0) + return offset; + + se.orig_address = offset; + r = bsearch (&se, map->entry, map->entry_count, + sizeof (xlate_map_entry_t), &xlate_compare); + e = (xlate_map_entry_t *) r; + + /* There could be a jump past the end of the section, + allow it using the last xlate map entry to translate its address. */ + if (e == NULL) + { + e = map->entry + map->entry_count - 1; + if (xlate_compare (&se, e) <= 0) + e = NULL; + } + BFD_ASSERT (e != NULL); + if (e == NULL) + return offset; + return e->new_address - e->orig_address + offset; +} + +typedef struct xlate_map_context_struct xlate_map_context; +struct xlate_map_context_struct +{ + xlate_map_t *map; + xlate_map_entry_t *current_entry; + int removed; +}; + +static int +xlate_map_fn (splay_tree_node node, void *p) +{ + text_action *r = (text_action *)node->value; + xlate_map_context *ctx = p; + unsigned orig_size = 0; + + switch (r->action) + { + case ta_none: + case ta_remove_insn: + case ta_convert_longcall: + case ta_remove_literal: + case ta_add_literal: + break; + case ta_remove_longcall: + orig_size = 6; + break; + case ta_narrow_insn: + orig_size = 3; + break; + case ta_widen_insn: + orig_size = 2; + break; + case ta_fill: + break; + } + ctx->current_entry->size = + r->offset + orig_size - ctx->current_entry->orig_address; + if (ctx->current_entry->size != 0) + { + ctx->current_entry++; + ctx->map->entry_count++; + } + ctx->current_entry->orig_address = r->offset + orig_size; + ctx->removed += r->removed_bytes; + ctx->current_entry->new_address = r->offset + orig_size - ctx->removed; + ctx->current_entry->size = 0; + return 0; +} + +/* Build a binary searchable offset translation map from a section's + action list. */ + +static xlate_map_t * +build_xlate_map (asection *sec, xtensa_relax_info *relax_info) +{ + text_action_list *action_list = &relax_info->action_list; + unsigned num_actions = 0; + xlate_map_context ctx; + + ctx.map = (xlate_map_t *) bfd_malloc (sizeof (xlate_map_t)); + + if (ctx.map == NULL) + return NULL; + + num_actions = action_list_count (action_list); + ctx.map->entry = (xlate_map_entry_t *) + bfd_malloc (sizeof (xlate_map_entry_t) * (num_actions + 1)); + if (ctx.map->entry == NULL) + { + free (ctx.map); + return NULL; + } + ctx.map->entry_count = 0; + + ctx.removed = 0; + ctx.current_entry = &ctx.map->entry[0]; + + ctx.current_entry->orig_address = 0; + ctx.current_entry->new_address = 0; + ctx.current_entry->size = 0; + + splay_tree_foreach (action_list->tree, xlate_map_fn, &ctx); + + ctx.current_entry->size = (bfd_get_section_limit (sec->owner, sec) + - ctx.current_entry->orig_address); + if (ctx.current_entry->size != 0) + ctx.map->entry_count++; + + return ctx.map; +} + + +/* Free an offset translation map. */ + +static void +free_xlate_map (xlate_map_t *map) +{ + if (map && map->entry) + free (map->entry); + if (map) + free (map); +} + + /* Use check_section_ebb_pcrels_fit to make sure that all of the relocations in a section will fit if a proposed set of actions are performed. */ @@ -6861,15 +8461,61 @@ check_section_ebb_pcrels_fit (bfd *abfd, asection *sec, bfd_byte *contents, Elf_Internal_Rela *internal_relocs, - const ebb_constraint *constraint) + reloc_range_list *relevant_relocs, + const ebb_constraint *constraint, + const xtensa_opcode *reloc_opcodes) { unsigned i, j; + unsigned n = sec->reloc_count; Elf_Internal_Rela *irel; + xlate_map_t *xmap = NULL; + bfd_boolean ok = TRUE; xtensa_relax_info *relax_info; + reloc_range_list_entry *entry = NULL; relax_info = get_xtensa_relax_info (sec); - for (i = 0; i < sec->reloc_count; i++) + if (relax_info && sec->reloc_count > 100) + { + xmap = build_xlate_map (sec, relax_info); + /* NULL indicates out of memory, but the slow version + can still be used. */ + } + + if (relevant_relocs && constraint->action_count) + { + if (!relevant_relocs->ok) + { + ok = FALSE; + n = 0; + } + else + { + bfd_vma min_offset, max_offset; + min_offset = max_offset = constraint->actions[0].offset; + + for (i = 1; i < constraint->action_count; ++i) + { + proposed_action *action = &constraint->actions[i]; + bfd_vma offset = action->offset; + + if (offset < min_offset) + min_offset = offset; + if (offset > max_offset) + max_offset = offset; + } + reloc_range_list_update_range (relevant_relocs, min_offset, + max_offset); + n = relevant_relocs->n_list; + entry = &relevant_relocs->list_root; + } + } + else + { + relevant_relocs = NULL; + } + + for (i = 0; i < n; i++) { r_reloc r_rel; bfd_vma orig_self_offset, orig_target_offset; @@ -6878,7 +8524,15 @@ check_section_ebb_pcrels_fit (bfd *abfd, reloc_howto_type *howto; int self_removed_bytes, target_removed_bytes; - irel = &internal_relocs[i]; + if (relevant_relocs) + { + entry = entry->next; + irel = entry->irel; + } + else + { + irel = internal_relocs + i; + } r_type = ELF32_R_TYPE (irel->r_info); howto = &elf_howto_table[r_type]; @@ -6886,7 +8540,8 @@ check_section_ebb_pcrels_fit (bfd *abfd, that fit before linking must fit after linking. Thus we only need to deal with relocations to the same section that are PC-relative. */ - if (ELF32_R_TYPE (irel->r_info) == R_XTENSA_ASM_SIMPLIFY + if (r_type == R_XTENSA_ASM_SIMPLIFY + || r_type == R_XTENSA_32_PCREL || !howto->pc_relative) continue; @@ -6904,10 +8559,12 @@ check_section_ebb_pcrels_fit (bfd *abfd, if (relax_info) { - self_offset = offset_with_removed_text (&relax_info->action_list, - orig_self_offset); - target_offset = offset_with_removed_text (&relax_info->action_list, - orig_target_offset); + self_offset = + xlate_offset_with_removed_text (xmap, &relax_info->action_list, + orig_self_offset); + target_offset = + xlate_offset_with_removed_text (xmap, &relax_info->action_list, + orig_target_offset); } self_removed_bytes = 0; @@ -6941,20 +8598,44 @@ check_section_ebb_pcrels_fit (bfd *abfd, xtensa_opcode opcode; int opnum; - opcode = get_relocation_opcode (abfd, sec, contents, irel); - if (opcode == XTENSA_UNDEFINED) - return FALSE; + if (relevant_relocs) + { + opcode = entry->opcode; + opnum = entry->opnum; + } + else + { + if (reloc_opcodes) + opcode = reloc_opcodes[relevant_relocs ? + (unsigned)(entry - relevant_relocs->reloc) : i]; + else + opcode = get_relocation_opcode (abfd, sec, contents, irel); + if (opcode == XTENSA_UNDEFINED) + { + ok = FALSE; + break; + } - opnum = get_relocation_opnd (opcode, ELF32_R_TYPE (irel->r_info)); - if (opnum == XTENSA_UNDEFINED) - return FALSE; + opnum = get_relocation_opnd (opcode, ELF32_R_TYPE (irel->r_info)); + if (opnum == XTENSA_UNDEFINED) + { + ok = FALSE; + break; + } + } if (!pcrel_reloc_fits (opcode, opnum, self_offset, target_offset)) - return FALSE; + { + ok = FALSE; + break; + } } } - return TRUE; + if (xmap) + free_xlate_map (xmap); + + return ok; } @@ -7013,7 +8694,7 @@ text_action_add_proposed (text_action_list *l, int -compute_fill_extra_space (property_table_entry *entry) +xtensa_compute_fill_extra_space (property_table_entry *entry) { int fill_extra_space; @@ -7046,7 +8727,7 @@ compute_fill_extra_space (property_table_entry *entry) add an entry to the per-section list of removed literals. The actual changes are deferred until the next pass. */ -static bfd_boolean +static bfd_boolean compute_removed_literals (bfd *abfd, asection *sec, struct bfd_link_info *link_info, @@ -7073,7 +8754,7 @@ compute_removed_literals (bfd *abfd, if (!relax_info->is_relaxable_literal_section) return ok; - internal_relocs = retrieve_internal_relocs (abfd, sec, + internal_relocs = retrieve_internal_relocs (abfd, sec, link_info->keep_memory); sec_size = bfd_get_section_limit (abfd, sec); @@ -7125,14 +8806,15 @@ compute_removed_literals (bfd *abfd, continue; prev_i = i; - if (last_loc_is_prev && + if (last_loc_is_prev && last_target_offset + 4 != rel->r_rel.target_offset) last_loc_is_prev = FALSE; /* Check if the relocation was from an L32R that is being removed because a CALLX was converted to a direct CALL, and check if there are no other relocations to the literal. */ - if (is_removable_literal (rel, i, src_relocs, relax_info->src_count)) + if (is_removable_literal (rel, i, src_relocs, relax_info->src_count, + sec, prop_table, ptblsize)) { if (!remove_dead_literal (abfd, sec, link_info, internal_relocs, irel, rel, prop_table, ptblsize)) @@ -7145,8 +8827,8 @@ compute_removed_literals (bfd *abfd, } if (!identify_literal_placement (abfd, sec, contents, link_info, - values, - &last_loc_is_prev, irel, + values, + &last_loc_is_prev, irel, relax_info->src_count - i, rel, prop_table, ptblsize, &target_sec_cache, rel->is_abs_literal)) @@ -7162,9 +8844,10 @@ compute_removed_literals (bfd *abfd, print_action_list (stderr, &relax_info->action_list); #endif /* DEBUG */ -error_return: - if (prop_table) free (prop_table); - clear_section_cache (&target_sec_cache); + error_return: + if (prop_table) + free (prop_table); + free_section_cache (&target_sec_cache); release_contents (sec, contents); release_internal_relocs (sec, internal_relocs); @@ -7182,7 +8865,7 @@ get_irel_at_offset (asection *sec, unsigned r_type; Elf_Internal_Rela key; - if (!internal_relocs) + if (!internal_relocs) return NULL; key.r_offset = offset; @@ -7216,12 +8899,22 @@ bfd_boolean is_removable_literal (const source_reloc *rel, int i, const source_reloc *src_relocs, - int src_count) + int src_count, + asection *sec, + property_table_entry *prop_table, + int ptblsize) { const source_reloc *curr_rel; + property_table_entry *entry; + if (!rel->is_null) return FALSE; - + + entry = elf_xtensa_find_property_entry (prop_table, ptblsize, + sec->vma + rel->r_rel.target_offset); + if (entry && (entry->flags & XTENSA_PROP_NO_TRANSFORM)) + return FALSE; + for (++i; i < src_count; ++i) { curr_rel = &src_relocs[i]; @@ -7238,7 +8931,7 @@ is_removable_literal (const source_reloc *rel, } -bfd_boolean +bfd_boolean remove_dead_literal (bfd *abfd, asection *sec, struct bfd_link_info *link_info, @@ -7265,7 +8958,7 @@ remove_dead_literal (bfd *abfd, ta_remove_literal, sec, rel->r_rel.target_offset, 4); /* If the section is 4-byte aligned, do not add fill. */ - if (sec->alignment_power > 2) + if (sec->alignment_power > 2) { int fill_extra_space; bfd_vma entry_sec_offset; @@ -7282,7 +8975,7 @@ remove_dead_literal (bfd *abfd, do not add fill. */ the_add_entry = elf_xtensa_find_property_entry (prop_table, ptblsize, entry_sec_offset); - fill_extra_space = compute_fill_extra_space (the_add_entry); + fill_extra_space = xtensa_compute_fill_extra_space (the_add_entry); fa = find_fill_action (&relax_info->action_list, sec, entry_sec_offset); removed_diff = compute_removed_action_diff (fa, sec, entry_sec_offset, @@ -7309,7 +9002,7 @@ remove_dead_literal (bfd *abfd, } -bfd_boolean +bfd_boolean identify_literal_placement (bfd *abfd, asection *sec, bfd_byte *contents, @@ -7340,7 +9033,7 @@ identify_literal_placement (bfd *abfd, sec_size = bfd_get_section_limit (abfd, sec); final_static_link = - (!link_info->relocatable + (!bfd_link_relocatable (link_info) && !elf_hash_table (link_info)->dynamic_sections_created); /* The placement algorithm first checks to see if the literal is @@ -7379,7 +9072,7 @@ identify_literal_placement (bfd *abfd, /* For relocatable links, do not try to move literals. To do it correctly might increase the number of relocations in an input section making the default relocatable linking fail. */ - if (!link_info->relocatable && !literal_placed + if (!bfd_link_relocatable (link_info) && !literal_placed && values->has_last_loc && !(*last_loc_is_prev_p)) { asection *target_sec = r_reloc_get_section (&values->last_loc); @@ -7392,7 +9085,7 @@ identify_literal_placement (bfd *abfd, /* There is a last loc that was in the same output section. */ if (relocations_reach (rel, remaining_src_rels, &try_loc) && move_shared_literal (sec, link_info, rel, - prop_table, ptblsize, + prop_table, ptblsize, &try_loc, &val, target_sec_cache)) { values->last_loc.virtual_offset += 4; @@ -7458,11 +9151,16 @@ relocations_reach (source_reloc *reloc, continue; /* The original and new output section for these must be the same - in order to coalesce. */ + in order to coalesce. */ if (r_reloc_get_section (&reloc[i].r_rel)->output_section != sec->output_section) return FALSE; + /* Absolute literals in the same output section can always be + combined. */ + if (reloc[i].is_abs_literal) + continue; + /* A literal with no PC-relative relocations can be moved anywhere. */ if (reloc[i].opnd != -1) { @@ -7487,7 +9185,7 @@ relocations_reach (source_reloc *reloc, /* Move a literal to another literal location because it is the same as the other literal value. */ -static bfd_boolean +static bfd_boolean coalesce_shared_literal (asection *sec, source_reloc *rel, property_table_entry *prop_table, @@ -7506,7 +9204,7 @@ coalesce_shared_literal (asection *sec, entry = elf_xtensa_find_property_entry (prop_table, ptblsize, sec->vma + rel->r_rel.target_offset); - if (entry && (entry->flags & XTENSA_PROP_INSN_NO_TRANSFORM)) + if (entry && (entry->flags & XTENSA_PROP_NO_TRANSFORM)) return TRUE; /* Mark that the literal will be coalesced. */ @@ -7516,7 +9214,7 @@ coalesce_shared_literal (asection *sec, ta_remove_literal, sec, rel->r_rel.target_offset, 4); /* If the section is 4-byte aligned, do not add fill. */ - if (sec->alignment_power > 2) + if (sec->alignment_power > 2) { int fill_extra_space; bfd_vma entry_sec_offset; @@ -7552,7 +9250,7 @@ coalesce_shared_literal (asection *sec, total amount of space used because of alignments so we need to do this carefully. Also, it may make a branch go out of range. */ -static bfd_boolean +static bfd_boolean move_shared_literal (asection *sec, struct bfd_link_info *link_info, source_reloc *rel, @@ -7595,7 +9293,7 @@ move_shared_literal (asection *sec, return FALSE; target_entry = elf_xtensa_find_property_entry - (target_sec_cache->ptbl, target_sec_cache->pte_count, + (target_sec_cache->ptbl, target_sec_cache->pte_count, target_sec->vma + target_loc->target_offset); if (!target_entry) @@ -7606,7 +9304,7 @@ move_shared_literal (asection *sec, init_ebb_constraint (&ebb_table); ebb = &ebb_table.ebb; - init_ebb (ebb, target_sec_cache->sec, target_sec_cache->contents, + init_ebb (ebb, target_sec_cache->sec, target_sec_cache->contents, target_sec_cache->content_length, target_sec_cache->ptbl, target_sec_cache->pte_count, target_sec_cache->relocs, target_sec_cache->reloc_count); @@ -7618,24 +9316,24 @@ move_shared_literal (asection *sec, -4 - (1 << target_sec->alignment_power), TRUE); /* Check all of the PC-relative relocations to make sure they still fit. */ - relocs_fit = check_section_ebb_pcrels_fit (target_sec->owner, target_sec, + relocs_fit = check_section_ebb_pcrels_fit (target_sec->owner, target_sec, target_sec_cache->contents, - target_sec_cache->relocs, - &ebb_table); + target_sec_cache->relocs, NULL, + &ebb_table, NULL); - if (!relocs_fit) + if (!relocs_fit) return FALSE; text_action_add_literal (&target_relax_info->action_list, ta_add_literal, target_loc, lit_value, -4); - if (target_sec->alignment_power > 2 && target_entry != src_entry) + if (target_sec->alignment_power > 2 && target_entry != src_entry) { /* May need to add or remove some fill to maintain alignment. */ int fill_extra_space; bfd_vma entry_sec_offset; - entry_sec_offset = + entry_sec_offset = target_entry->address - target_sec->vma + target_entry->size; /* If the literal range is at the end of the section, @@ -7668,7 +9366,7 @@ move_shared_literal (asection *sec, ta_remove_literal, sec, rel->r_rel.target_offset, 4); /* If the section is 4-byte aligned, do not add fill. */ - if (sec->alignment_power > 2 && target_entry != src_entry) + if (sec->alignment_power > 2 && target_entry != src_entry) { int fill_extra_space; bfd_vma entry_sec_offset; @@ -7702,6 +9400,16 @@ move_shared_literal (asection *sec, /* Second relaxation pass. */ +static int +action_remove_bytes_fn (splay_tree_node node, void *p) +{ + bfd_size_type *final_size = p; + text_action *action = (text_action *)node->value; + + *final_size -= action->removed_bytes; + return 0; +} + /* Modify all of the relocations to point to the right spot, and if this is a relaxable section, delete the unwanted literals and fix the section size. */ @@ -7732,8 +9440,11 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info) return relax_property_section (abfd, sec, link_info); } - internal_relocs = retrieve_internal_relocs (abfd, sec, + internal_relocs = retrieve_internal_relocs (abfd, sec, link_info->keep_memory); + if (!internal_relocs && !action_list_count (&relax_info->action_list)) + return TRUE; + contents = retrieve_contents (abfd, sec, link_info->keep_memory); if (contents == NULL && sec_size != 0) { @@ -7772,6 +9483,8 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info) if (relax_info->is_relaxable_literal_section || relax_info->is_relaxable_asm_section) { + pin_internal_relocs (sec, internal_relocs); + if (r_type != R_XTENSA_NONE && find_removed_literal (&relax_info->removed_list, irel->r_offset)) @@ -7780,9 +9493,8 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info) if (elf_hash_table (link_info)->dynamic_sections_created) shrink_dynamic_reloc_sections (link_info, abfd, sec, irel); irel->r_info = ELF32_R_INFO (0, R_XTENSA_NONE); - irel->r_offset = offset_with_removed_text + irel->r_offset = offset_with_removed_text_map (&relax_info->action_list, irel->r_offset); - pin_internal_relocs (sec, internal_relocs); continue; } @@ -7808,6 +9520,12 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info) } /* Update the action so that the code that moves the contents will do the right thing. */ + /* ta_remove_longcall and ta_remove_insn actions are + grouped together in the tree as well as + ta_convert_longcall and ta_none, so that changes below + can be done w/o removing and reinserting action into + the tree. */ + if (action->action == ta_remove_longcall) action->action = ta_remove_insn; else @@ -7818,7 +9536,7 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info) } } - source_offset = offset_with_removed_text + source_offset = offset_with_removed_text_map (&relax_info->action_list, irel->r_offset); irel->r_offset = source_offset; } @@ -7827,23 +9545,69 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info) we may need to change the relocation's target offset. */ target_sec = r_reloc_get_section (&r_rel); - target_relax_info = get_xtensa_relax_info (target_sec); + /* For a reference to a discarded section from a DWARF section, + i.e., where action_discarded is PRETEND, the symbol will + eventually be modified to refer to the kept section (at least if + the kept and discarded sections are the same size). Anticipate + that here and adjust things accordingly. */ + if (! elf_xtensa_ignore_discarded_relocs (sec) + && elf_xtensa_action_discarded (sec) == PRETEND + && sec->sec_info_type != SEC_INFO_TYPE_STABS + && target_sec != NULL + && discarded_section (target_sec)) + { + /* It would be natural to call _bfd_elf_check_kept_section + here, but it's not exported from elflink.c. It's also a + fairly expensive check. Adjusting the relocations to the + discarded section is fairly harmless; it will only adjust + some addends and difference values. If it turns out that + _bfd_elf_check_kept_section fails later, it won't matter, + so just compare the section names to find the right group + member. */ + asection *kept = target_sec->kept_section; + if (kept != NULL) + { + if ((kept->flags & SEC_GROUP) != 0) + { + asection *first = elf_next_in_group (kept); + asection *s = first; + + kept = NULL; + while (s != NULL) + { + if (strcmp (s->name, target_sec->name) == 0) + { + kept = s; + break; + } + s = elf_next_in_group (s); + if (s == first) + break; + } + } + } + if (kept != NULL + && ((target_sec->rawsize != 0 + ? target_sec->rawsize : target_sec->size) + == (kept->rawsize != 0 ? kept->rawsize : kept->size))) + target_sec = kept; + } + + target_relax_info = get_xtensa_relax_info (target_sec); if (target_relax_info && (target_relax_info->is_relaxable_literal_section || target_relax_info->is_relaxable_asm_section)) { r_reloc new_reloc; - reloc_bfd_fix *fix; - bfd_vma addend_displacement; - - translate_reloc (&r_rel, &new_reloc); + target_sec = translate_reloc (&r_rel, &new_reloc, target_sec); if (r_type == R_XTENSA_DIFF8 || r_type == R_XTENSA_DIFF16 || r_type == R_XTENSA_DIFF32) { - bfd_vma diff_value = 0, new_end_offset, diff_mask = 0; + bfd_signed_vma diff_value = 0; + bfd_vma new_end_offset, diff_mask = 0; if (bfd_get_section_limit (abfd, sec) < old_source_offset) { @@ -7857,19 +9621,19 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info) { case R_XTENSA_DIFF8: diff_value = - bfd_get_8 (abfd, &contents[old_source_offset]); + bfd_get_signed_8 (abfd, &contents[old_source_offset]); break; case R_XTENSA_DIFF16: diff_value = - bfd_get_16 (abfd, &contents[old_source_offset]); + bfd_get_signed_16 (abfd, &contents[old_source_offset]); break; case R_XTENSA_DIFF32: diff_value = - bfd_get_32 (abfd, &contents[old_source_offset]); + bfd_get_signed_32 (abfd, &contents[old_source_offset]); break; } - new_end_offset = offset_with_removed_text + new_end_offset = offset_with_removed_text_map (&target_relax_info->action_list, r_rel.target_offset + diff_value); diff_value = new_end_offset - new_reloc.target_offset; @@ -7877,24 +9641,25 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info) switch (r_type) { case R_XTENSA_DIFF8: - diff_mask = 0xff; - bfd_put_8 (abfd, diff_value, + diff_mask = 0x7f; + bfd_put_signed_8 (abfd, diff_value, &contents[old_source_offset]); break; case R_XTENSA_DIFF16: - diff_mask = 0xffff; - bfd_put_16 (abfd, diff_value, + diff_mask = 0x7fff; + bfd_put_signed_16 (abfd, diff_value, &contents[old_source_offset]); break; case R_XTENSA_DIFF32: - diff_mask = 0xffffffff; - bfd_put_32 (abfd, diff_value, + diff_mask = 0x7fffffff; + bfd_put_signed_32 (abfd, diff_value, &contents[old_source_offset]); break; } - /* Check for overflow. */ - if ((diff_value & ~diff_mask) != 0) + /* Check for overflow. Sign bits must be all zeroes or all ones */ + if ((diff_value & ~diff_mask) != 0 && + (diff_value & ~diff_mask) != (-1 & ~diff_mask)) { (*link_info->callbacks->reloc_dangerous) (link_info, _("overflow after relaxation"), @@ -7905,52 +9670,56 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info) pin_contents (sec, contents); } - /* FIXME: If the relocation still references a section in - the same input file, the relocation should be modified - directly instead of adding a "fix" record. */ - - addend_displacement = - new_reloc.target_offset + new_reloc.virtual_offset; - - fix = reloc_bfd_fix_init (sec, source_offset, r_type, 0, - r_reloc_get_section (&new_reloc), - addend_displacement, TRUE); - add_fix (sec, fix); + /* If the relocation still references a section in the same + input file, modify the relocation directly instead of + adding a "fix" record. */ + if (target_sec->owner == abfd) + { + unsigned r_symndx = ELF32_R_SYM (new_reloc.rela.r_info); + irel->r_info = ELF32_R_INFO (r_symndx, r_type); + irel->r_addend = new_reloc.rela.r_addend; + pin_internal_relocs (sec, internal_relocs); + } + else + { + bfd_vma addend_displacement; + reloc_bfd_fix *fix; + + addend_displacement = + new_reloc.target_offset + new_reloc.virtual_offset; + fix = reloc_bfd_fix_init (sec, source_offset, r_type, + target_sec, + addend_displacement, TRUE); + add_fix (sec, fix); + } } - - pin_internal_relocs (sec, internal_relocs); } } if ((relax_info->is_relaxable_literal_section || relax_info->is_relaxable_asm_section) - && relax_info->action_list.head) + && action_list_count (&relax_info->action_list)) { /* Walk through the planned actions and build up a table of move, copy and fill records. Use the move, copy and fill records to perform the actions once. */ - bfd_size_type size = sec->size; - int removed = 0; bfd_size_type final_size, copy_size, orig_insn_size; bfd_byte *scratch = NULL; bfd_byte *dup_contents = NULL; - bfd_size_type orig_size = size; + bfd_size_type orig_size = sec->size; bfd_vma orig_dot = 0; bfd_vma orig_dot_copied = 0; /* Byte copied already from orig dot in physical memory. */ bfd_vma orig_dot_vo = 0; /* Virtual offset from orig_dot. */ bfd_vma dup_dot = 0; - text_action *action = relax_info->action_list.head; + text_action *action; final_size = sec->size; - for (action = relax_info->action_list.head; action; - action = action->next) - { - final_size -= action->removed_bytes; - } + splay_tree_foreach (relax_info->action_list.tree, + action_remove_bytes_fn, &final_size); scratch = (bfd_byte *) bfd_zmalloc (final_size); dup_contents = (bfd_byte *) bfd_zmalloc (final_size); @@ -7959,8 +9728,8 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info) print_action_list (stderr, &relax_info->action_list); #endif - for (action = relax_info->action_list.head; action; - action = action->next) + for (action = action_first (&relax_info->action_list); action; + action = action_next (&relax_info->action_list, action)) { virtual_action = FALSE; if (action->offset > orig_dot) @@ -8005,7 +9774,7 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info) dup_dot += copy_size; } virtual_action = TRUE; - } + } else BFD_ASSERT (action->virtual_offset <= orig_dot_vo); } @@ -8022,7 +9791,7 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info) copy_size = 2; memmove (scratch, &contents[orig_dot], orig_insn_size); BFD_ASSERT (action->removed_bytes == 1); - rv = narrow_instruction (scratch, final_size, 0, TRUE); + rv = narrow_instruction (scratch, final_size, 0); BFD_ASSERT (rv); memmove (&dup_contents[dup_dot], scratch, copy_size); orig_dot += orig_insn_size; @@ -8055,7 +9824,7 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info) copy_size = 3; memmove (scratch, &contents[orig_dot], orig_insn_size); BFD_ASSERT (action->removed_bytes == -1); - rv = widen_instruction (scratch, final_size, 0, TRUE); + rv = widen_instruction (scratch, final_size, 0); BFD_ASSERT (rv); memmove (&dup_contents[dup_dot], scratch, copy_size); orig_dot += orig_insn_size; @@ -8076,7 +9845,7 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info) relax_info, &internal_relocs, &action->value)) goto error_return; - if (virtual_action) + if (virtual_action) orig_dot_vo += copy_size; orig_dot += orig_insn_size; @@ -8089,8 +9858,6 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info) break; } - size -= action->removed_bytes; - removed += action->removed_bytes; BFD_ASSERT (dup_dot <= final_size); BFD_ASSERT (orig_dot <= orig_size); } @@ -8130,6 +9897,8 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info) free (scratch); pin_contents (sec, contents); + if (sec->rawsize == 0) + sec->rawsize = sec->size; sec->size = final_size; } @@ -8140,7 +9909,7 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info) } -static bfd_boolean +static bfd_boolean translate_section_fixes (asection *sec) { xtensa_relax_info *relax_info; @@ -8161,7 +9930,7 @@ translate_section_fixes (asection *sec) /* Translate a fix given the mapping in the relax info for the target section. If it has already been translated, no work is required. */ -static bfd_boolean +static bfd_boolean translate_reloc_bfd_fix (reloc_bfd_fix *fix) { reloc_bfd_fix new_fix; @@ -8207,7 +9976,7 @@ translate_reloc_bfd_fix (reloc_bfd_fix *fix) target_offset); } - if (removed) + if (removed) { asection *new_sec; @@ -8217,11 +9986,11 @@ translate_reloc_bfd_fix (reloc_bfd_fix *fix) /* This was moved to some other address (possibly another section). */ new_sec = r_reloc_get_section (&removed->to); - if (new_sec != sec) + if (new_sec != sec) { sec = new_sec; relax_info = get_xtensa_relax_info (sec); - if (!relax_info || + if (!relax_info || (!relax_info->is_relaxable_literal_section && !relax_info->is_relaxable_asm_section)) { @@ -8251,26 +10020,21 @@ translate_reloc_bfd_fix (reloc_bfd_fix *fix) /* Fix up a relocation to take account of removed literals. */ -static void -translate_reloc (const r_reloc *orig_rel, r_reloc *new_rel) +static asection * +translate_reloc (const r_reloc *orig_rel, r_reloc *new_rel, asection *sec) { - asection *sec; xtensa_relax_info *relax_info; removed_literal *removed; - bfd_vma new_offset, target_offset, removed_bytes; + bfd_vma target_offset, base_offset; *new_rel = *orig_rel; if (!r_reloc_is_defined (orig_rel)) - return; - sec = r_reloc_get_section (orig_rel); + return sec ; relax_info = get_xtensa_relax_info (sec); - BFD_ASSERT (relax_info); - - if (!relax_info->is_relaxable_literal_section - && !relax_info->is_relaxable_asm_section) - return; + BFD_ASSERT (relax_info && (relax_info->is_relaxable_literal_section + || relax_info->is_relaxable_asm_section)); target_offset = orig_rel->target_offset; @@ -8301,19 +10065,44 @@ translate_reloc (const r_reloc *orig_rel, r_reloc *new_rel) if (!relax_info || (!relax_info->is_relaxable_literal_section && !relax_info->is_relaxable_asm_section)) - return; + return sec; } target_offset = new_rel->target_offset; } - /* ...and the target address may have been moved within its section. */ - new_offset = offset_with_removed_text (&relax_info->action_list, - target_offset); + /* Find the base offset of the reloc symbol, excluding any addend from the + reloc or from the section contents (for a partial_inplace reloc). Then + find the adjusted values of the offsets due to relaxation. The base + offset is needed to determine the change to the reloc's addend; the reloc + addend should not be adjusted due to relaxations located before the base + offset. */ + + base_offset = r_reloc_get_target_offset (new_rel) - new_rel->rela.r_addend; + if (base_offset <= target_offset) + { + int base_removed = removed_by_actions_map (&relax_info->action_list, + base_offset, FALSE); + int addend_removed = removed_by_actions_map (&relax_info->action_list, + target_offset, FALSE) - + base_removed; + + new_rel->target_offset = target_offset - base_removed - addend_removed; + new_rel->rela.r_addend -= addend_removed; + } + else + { + /* Handle a negative addend. The base offset comes first. */ + int tgt_removed = removed_by_actions_map (&relax_info->action_list, + target_offset, FALSE); + int addend_removed = removed_by_actions_map (&relax_info->action_list, + base_offset, FALSE) - + tgt_removed; + + new_rel->target_offset = target_offset - tgt_removed; + new_rel->rela.r_addend += addend_removed; + } - /* Modify the offset and addend. */ - removed_bytes = target_offset - new_offset; - new_rel->target_offset = new_offset; - new_rel->rela.r_addend -= removed_bytes; + return sec; } @@ -8332,6 +10121,7 @@ shrink_dynamic_reloc_sections (struct bfd_link_info *info, asection *input_section, Elf_Internal_Rela *rel) { + struct elf_xtensa_link_hash_table *htab; Elf_Internal_Shdr *symtab_hdr; struct elf_link_hash_entry **sym_hashes; unsigned long r_symndx; @@ -8339,6 +10129,10 @@ shrink_dynamic_reloc_sections (struct bfd_link_info *info, struct elf_link_hash_entry *h; bfd_boolean dynamic_symbol; + htab = elf_xtensa_hash_table (info); + if (htab == NULL) + return; + symtab_hdr = &elf_tdata (abfd)->symtab_hdr; sym_hashes = elf_sym_hashes (abfd); @@ -8350,30 +10144,26 @@ shrink_dynamic_reloc_sections (struct bfd_link_info *info, else h = sym_hashes[r_symndx - symtab_hdr->sh_info]; - dynamic_symbol = xtensa_elf_dynamic_symbol_p (h, info); + dynamic_symbol = elf_xtensa_dynamic_symbol_p (h, info); if ((r_type == R_XTENSA_32 || r_type == R_XTENSA_PLT) && (input_section->flags & SEC_ALLOC) != 0 - && (dynamic_symbol || info->shared)) + && (dynamic_symbol + || (bfd_link_pic (info) + && (!h || h->root.type != bfd_link_hash_undefweak)))) { - bfd *dynobj; - const char *srel_name; asection *srel; bfd_boolean is_plt = FALSE; - dynobj = elf_hash_table (info)->dynobj; - BFD_ASSERT (dynobj != NULL); - if (dynamic_symbol && r_type == R_XTENSA_PLT) { - srel_name = ".rela.plt"; + srel = htab->elf.srelplt; is_plt = TRUE; } else - srel_name = ".rela.got"; + srel = htab->elf.srelgot; /* Reduce size of the .rela.* section by one reloc. */ - srel = bfd_get_section_by_name (dynobj, srel_name); BFD_ASSERT (srel != NULL); BFD_ASSERT (srel->size >= sizeof (Elf32_External_Rela)); srel->size -= sizeof (Elf32_External_Rela); @@ -8392,15 +10182,15 @@ shrink_dynamic_reloc_sections (struct bfd_link_info *info, reloc_index = srel->size / sizeof (Elf32_External_Rela); chunk = reloc_index / PLT_ENTRIES_PER_CHUNK; - splt = elf_xtensa_get_plt_section (dynobj, chunk); - sgotplt = elf_xtensa_get_gotplt_section (dynobj, chunk); + splt = elf_xtensa_get_plt_section (info, chunk); + sgotplt = elf_xtensa_get_gotplt_section (info, chunk); BFD_ASSERT (splt != NULL && sgotplt != NULL); /* Check if an entire PLT chunk has just been eliminated. */ if (reloc_index % PLT_ENTRIES_PER_CHUNK == 0) { /* The two magic GOT entries for that chunk can go away. */ - srelgot = bfd_get_section_by_name (dynobj, ".rela.got"); + srelgot = htab->elf.srelgot; BFD_ASSERT (srelgot != NULL); srelgot->reloc_count -= 2; srelgot->size -= 2 * sizeof (Elf32_External_Rela); @@ -8453,12 +10243,10 @@ move_literal (bfd *abfd, { int r_type; unsigned i; - asection *target_sec; reloc_bfd_fix *fix; unsigned insert_at; r_type = ELF32_R_TYPE (r_rel->rela.r_info); - target_sec = r_reloc_get_section (r_rel); /* This is the difficult case. We have to create a fix up. */ this_rela.r_offset = offset; @@ -8468,8 +10256,8 @@ move_literal (bfd *abfd, bfd_put_32 (abfd, lit->value, contents + offset); /* Currently, we cannot move relocations during a relocatable link. */ - BFD_ASSERT (!link_info->relocatable); - fix = reloc_bfd_fix_init (sec, offset, r_type, r_rel->abfd, + BFD_ASSERT (!bfd_link_relocatable (link_info)); + fix = reloc_bfd_fix_init (sec, offset, r_type, r_reloc_get_section (r_rel), r_rel->target_offset + r_rel->virtual_offset, FALSE); @@ -8499,7 +10287,7 @@ move_literal (bfd *abfd, BFD_ASSERT (relax_info->allocated_relocs == NULL || sec->reloc_count == relax_info->relocs_count); - if (relax_info->allocated_relocs_count == 0) + if (relax_info->allocated_relocs_count == 0) new_relocs_count = (sec->reloc_count + 2) * 2; else new_relocs_count = (relax_info->allocated_relocs_count + 2) * 2; @@ -8519,7 +10307,7 @@ move_literal (bfd *abfd, if (insert_at != sec->reloc_count) memcpy (new_relocs + insert_at + 1, (*internal_relocs_p) + insert_at, - (sec->reloc_count - insert_at) + (sec->reloc_count - insert_at) * sizeof (Elf_Internal_Rela)); if (*internal_relocs_p != relax_info->allocated_relocs) @@ -8569,15 +10357,16 @@ relax_property_section (bfd *abfd, { Elf_Internal_Rela *internal_relocs; bfd_byte *contents; - unsigned i, nexti; + unsigned i; bfd_boolean ok = TRUE; bfd_boolean is_full_prop_section; size_t last_zfill_target_offset = 0; asection *last_zfill_target_sec = NULL; bfd_size_type sec_size; + bfd_size_type entry_size; sec_size = bfd_get_section_limit (abfd, sec); - internal_relocs = retrieve_internal_relocs (abfd, sec, + internal_relocs = retrieve_internal_relocs (abfd, sec, link_info->keep_memory); contents = retrieve_contents (abfd, sec, link_info->keep_memory); if (contents == NULL && sec_size != 0) @@ -8586,10 +10375,11 @@ relax_property_section (bfd *abfd, goto error_return; } - is_full_prop_section = - ((strcmp (sec->name, XTENSA_PROP_SEC_NAME) == 0) - || (strncmp (sec->name, ".gnu.linkonce.prop.", - sizeof ".gnu.linkonce.prop." - 1) == 0)); + is_full_prop_section = xtensa_is_proptable_section (sec); + if (is_full_prop_section) + entry_size = 12; + else + entry_size = 8; if (internal_relocs) { @@ -8618,12 +10408,8 @@ relax_property_section (bfd *abfd, size_p = &contents[irel->r_offset + 4]; flags_p = NULL; if (is_full_prop_section) - { - flags_p = &contents[irel->r_offset + 8]; - BFD_ASSERT (irel->r_offset + 12 <= sec_size); - } - else - BFD_ASSERT (irel->r_offset + 8 <= sec_size); + flags_p = &contents[irel->r_offset + 8]; + BFD_ASSERT (irel->r_offset + entry_size <= sec_size); target_sec = r_reloc_get_section (&val.r_rel); target_relax_info = get_xtensa_relax_info (target_sec); @@ -8633,14 +10419,17 @@ relax_property_section (bfd *abfd, || target_relax_info->is_relaxable_asm_section )) { /* Translate the relocation's destination. */ - bfd_vma new_offset, new_end_offset; + bfd_vma old_offset = val.r_rel.target_offset; + bfd_vma new_offset; long old_size, new_size; - - new_offset = offset_with_removed_text - (&target_relax_info->action_list, val.r_rel.target_offset); + int removed_by_old_offset = + removed_by_actions_map (&target_relax_info->action_list, + old_offset, FALSE); + new_offset = old_offset - removed_by_old_offset; /* Assert that we are not out of bounds. */ old_size = bfd_get_32 (abfd, size_p); + new_size = old_size; if (old_size == 0) { @@ -8652,49 +10441,49 @@ relax_property_section (bfd *abfd, offset before or after the fill address depending on whether the expanding unreachable entry preceeds it. */ - if (last_zfill_target_sec - && last_zfill_target_sec == target_sec - && last_zfill_target_offset == val.r_rel.target_offset) - new_end_offset = new_offset; - else + if (last_zfill_target_sec == 0 + || last_zfill_target_sec != target_sec + || last_zfill_target_offset != old_offset) { - new_end_offset = new_offset; - new_offset = offset_with_removed_text_before_fill - (&target_relax_info->action_list, - val.r_rel.target_offset); + bfd_vma new_end_offset = new_offset; + + /* Recompute the new_offset, but this time don't + include any fill inserted by relaxation. */ + removed_by_old_offset = + removed_by_actions_map (&target_relax_info->action_list, + old_offset, TRUE); + new_offset = old_offset - removed_by_old_offset; /* If it is not unreachable and we have not yet seen an unreachable at this address, place it before the fill address. */ - if (!flags_p - || (bfd_get_32 (abfd, flags_p) - & XTENSA_PROP_UNREACHABLE) == 0) - new_end_offset = new_offset; - else + if (flags_p && (bfd_get_32 (abfd, flags_p) + & XTENSA_PROP_UNREACHABLE) != 0) { + new_size = new_end_offset - new_offset; + last_zfill_target_sec = target_sec; - last_zfill_target_offset = val.r_rel.target_offset; + last_zfill_target_offset = old_offset; } } } else { - new_end_offset = offset_with_removed_text_before_fill - (&target_relax_info->action_list, - val.r_rel.target_offset + old_size); + int removed_by_old_offset_size = + removed_by_actions_map (&target_relax_info->action_list, + old_offset + old_size, TRUE); + new_size -= removed_by_old_offset_size - removed_by_old_offset; } - new_size = new_end_offset - new_offset; - if (new_size != old_size) { bfd_put_32 (abfd, new_size, size_p); pin_contents (sec, contents); } - if (new_offset != val.r_rel.target_offset) + if (new_offset != old_offset) { - bfd_vma diff = new_offset - val.r_rel.target_offset; + bfd_vma diff = new_offset - old_offset; irel->r_addend += diff; pin_internal_relocs (sec, internal_relocs); } @@ -8706,197 +10495,196 @@ relax_property_section (bfd *abfd, finish_dynamic_sections() but at that point it's too late to reclaim the space in the output section, so we do this twice. */ - if (internal_relocs && (!link_info->relocatable - || strcmp (sec->name, XTENSA_LIT_SEC_NAME) == 0)) + if (internal_relocs && (!bfd_link_relocatable (link_info) + || xtensa_is_littable_section (sec))) { Elf_Internal_Rela *last_irel = NULL; + Elf_Internal_Rela *irel, *next_rel, *rel_end; int removed_bytes = 0; - bfd_vma offset, last_irel_offset; - bfd_vma section_size; - bfd_size_type entry_size; + bfd_vma offset; flagword predef_flags; - if (is_full_prop_section) - entry_size = 12; - else - entry_size = 8; - predef_flags = xtensa_get_property_predef_flags (sec); - /* Walk over memory and irels at the same time. - This REQUIRES that the internal_relocs be sorted by offset. */ + /* Walk over memory and relocations at the same time. + This REQUIRES that the internal_relocs be sorted by offset. */ qsort (internal_relocs, sec->reloc_count, sizeof (Elf_Internal_Rela), internal_reloc_compare); - nexti = 0; /* Index into internal_relocs. */ pin_internal_relocs (sec, internal_relocs); pin_contents (sec, contents); - last_irel_offset = (bfd_vma) -1; - section_size = sec->size; - BFD_ASSERT (section_size % entry_size == 0); + next_rel = internal_relocs; + rel_end = internal_relocs + sec->reloc_count; - for (offset = 0; offset < section_size; offset += entry_size) + BFD_ASSERT (sec->size % entry_size == 0); + + for (offset = 0; offset < sec->size; offset += entry_size) { - Elf_Internal_Rela *irel, *next_irel; + Elf_Internal_Rela *offset_rel, *extra_rel; bfd_vma bytes_to_remove, size, actual_offset; - bfd_boolean remove_this_irel; + bfd_boolean remove_this_rel; flagword flags; - irel = NULL; - next_irel = NULL; - - /* Find the next two relocations (if there are that many left), - skipping over any R_XTENSA_NONE relocs. On entry, "nexti" is - the starting reloc index. After these two loops, "i" - is the index of the first non-NONE reloc past that starting - index, and "nexti" is the index for the next non-NONE reloc - after "i". */ + /* Find the first relocation for the entry at the current offset. + Adjust the offsets of any extra relocations for the previous + entry. */ + offset_rel = NULL; + if (next_rel) + { + for (irel = next_rel; irel < rel_end; irel++) + { + if ((irel->r_offset == offset + && ELF32_R_TYPE (irel->r_info) != R_XTENSA_NONE) + || irel->r_offset > offset) + { + offset_rel = irel; + break; + } + irel->r_offset -= removed_bytes; + } + } - for (i = nexti; i < sec->reloc_count; i++) + /* Find the next relocation (if there are any left). */ + extra_rel = NULL; + if (offset_rel) { - if (ELF32_R_TYPE (internal_relocs[i].r_info) != R_XTENSA_NONE) + for (irel = offset_rel + 1; irel < rel_end; irel++) { - irel = &internal_relocs[i]; - break; + if (ELF32_R_TYPE (irel->r_info) != R_XTENSA_NONE) + { + extra_rel = irel; + break; + } } - internal_relocs[i].r_offset -= removed_bytes; } - for (nexti = i + 1; nexti < sec->reloc_count; nexti++) + /* Check if there are relocations on the current entry. There + should usually be a relocation on the offset field. If there + are relocations on the size or flags, then we can't optimize + this entry. Also, find the next relocation to examine on the + next iteration. */ + if (offset_rel) { - if (ELF32_R_TYPE (internal_relocs[nexti].r_info) - != R_XTENSA_NONE) + if (offset_rel->r_offset >= offset + entry_size) { - next_irel = &internal_relocs[nexti]; - break; + next_rel = offset_rel; + /* There are no relocations on the current entry, but we + might still be able to remove it if the size is zero. */ + offset_rel = NULL; + } + else if (offset_rel->r_offset > offset + || (extra_rel + && extra_rel->r_offset < offset + entry_size)) + { + /* There is a relocation on the size or flags, so we can't + do anything with this entry. Continue with the next. */ + next_rel = offset_rel; + continue; + } + else + { + BFD_ASSERT (offset_rel->r_offset == offset); + offset_rel->r_offset -= removed_bytes; + next_rel = offset_rel + 1; } - internal_relocs[nexti].r_offset -= removed_bytes; } + else + next_rel = NULL; - remove_this_irel = FALSE; + remove_this_rel = FALSE; bytes_to_remove = 0; actual_offset = offset - removed_bytes; size = bfd_get_32 (abfd, &contents[actual_offset + 4]); - if (is_full_prop_section) + if (is_full_prop_section) flags = bfd_get_32 (abfd, &contents[actual_offset + 8]); else flags = predef_flags; - /* Check that the irels are sorted by offset, - with only one per address. */ - BFD_ASSERT (!irel || (int) irel->r_offset > (int) last_irel_offset); - BFD_ASSERT (!next_irel || next_irel->r_offset > irel->r_offset); - - /* Make sure there aren't relocs on the size or flag fields. */ - if ((irel && irel->r_offset == offset + 4) - || (is_full_prop_section - && irel && irel->r_offset == offset + 8)) - { - irel->r_offset -= removed_bytes; - last_irel_offset = irel->r_offset; - } - else if (next_irel && (next_irel->r_offset == offset + 4 - || (is_full_prop_section - && next_irel->r_offset == offset + 8))) - { - nexti += 1; - irel->r_offset -= removed_bytes; - next_irel->r_offset -= removed_bytes; - last_irel_offset = next_irel->r_offset; - } - else if (size == 0 && (flags & XTENSA_PROP_ALIGN) == 0 - && (flags & XTENSA_PROP_UNREACHABLE) == 0) + if (size == 0 + && (flags & XTENSA_PROP_ALIGN) == 0 + && (flags & XTENSA_PROP_UNREACHABLE) == 0) { /* Always remove entries with zero size and no alignment. */ bytes_to_remove = entry_size; - if (irel && irel->r_offset == offset) - { - remove_this_irel = TRUE; - - irel->r_offset -= removed_bytes; - last_irel_offset = irel->r_offset; - } + if (offset_rel) + remove_this_rel = TRUE; } - else if (irel && irel->r_offset == offset) + else if (offset_rel + && ELF32_R_TYPE (offset_rel->r_info) == R_XTENSA_32) { - if (ELF32_R_TYPE (irel->r_info) == R_XTENSA_32) + if (last_irel) { - if (last_irel) + flagword old_flags; + bfd_vma old_size = + bfd_get_32 (abfd, &contents[last_irel->r_offset + 4]); + bfd_vma old_address = + (last_irel->r_addend + + bfd_get_32 (abfd, &contents[last_irel->r_offset])); + bfd_vma new_address = + (offset_rel->r_addend + + bfd_get_32 (abfd, &contents[actual_offset])); + if (is_full_prop_section) + old_flags = bfd_get_32 + (abfd, &contents[last_irel->r_offset + 8]); + else + old_flags = predef_flags; + + if ((ELF32_R_SYM (offset_rel->r_info) + == ELF32_R_SYM (last_irel->r_info)) + && old_address + old_size == new_address + && old_flags == flags + && (old_flags & XTENSA_PROP_INSN_BRANCH_TARGET) == 0 + && (old_flags & XTENSA_PROP_INSN_LOOP_TARGET) == 0) { - flagword old_flags; - bfd_vma old_size = - bfd_get_32 (abfd, &contents[last_irel->r_offset + 4]); - bfd_vma old_address = - (last_irel->r_addend - + bfd_get_32 (abfd, &contents[last_irel->r_offset])); - bfd_vma new_address = - (irel->r_addend - + bfd_get_32 (abfd, &contents[actual_offset])); - if (is_full_prop_section) - old_flags = bfd_get_32 - (abfd, &contents[last_irel->r_offset + 8]); - else - old_flags = predef_flags; - - if ((ELF32_R_SYM (irel->r_info) - == ELF32_R_SYM (last_irel->r_info)) - && old_address + old_size == new_address - && old_flags == flags - && (old_flags & XTENSA_PROP_INSN_BRANCH_TARGET) == 0 - && (old_flags & XTENSA_PROP_INSN_LOOP_TARGET) == 0) - { - /* Fix the old size. */ - bfd_put_32 (abfd, old_size + size, - &contents[last_irel->r_offset + 4]); - bytes_to_remove = entry_size; - remove_this_irel = TRUE; - } - else - last_irel = irel; + /* Fix the old size. */ + bfd_put_32 (abfd, old_size + size, + &contents[last_irel->r_offset + 4]); + bytes_to_remove = entry_size; + remove_this_rel = TRUE; } else - last_irel = irel; + last_irel = offset_rel; } - - irel->r_offset -= removed_bytes; - last_irel_offset = irel->r_offset; + else + last_irel = offset_rel; } - if (remove_this_irel) + if (remove_this_rel) { - irel->r_info = ELF32_R_INFO (0, R_XTENSA_NONE); - irel->r_offset -= bytes_to_remove; + offset_rel->r_info = ELF32_R_INFO (0, R_XTENSA_NONE); + offset_rel->r_offset = 0; } if (bytes_to_remove != 0) { removed_bytes += bytes_to_remove; - if (offset + bytes_to_remove < section_size) + if (offset + bytes_to_remove < sec->size) memmove (&contents[actual_offset], &contents[actual_offset + bytes_to_remove], - section_size - offset - bytes_to_remove); + sec->size - offset - bytes_to_remove); } } if (removed_bytes) { + /* Fix up any extra relocations on the last entry. */ + for (irel = next_rel; irel < rel_end; irel++) + irel->r_offset -= removed_bytes; + /* Clear the removed bytes. */ - memset (&contents[section_size - removed_bytes], 0, removed_bytes); + memset (&contents[sec->size - removed_bytes], 0, removed_bytes); - sec->size = section_size - removed_bytes; + if (sec->rawsize == 0) + sec->rawsize = sec->size; + sec->size -= removed_bytes; if (xtensa_is_littable_section (sec)) { - bfd *dynobj = elf_hash_table (link_info)->dynobj; - if (dynobj) - { - asection *sgotloc = - bfd_get_section_by_name (dynobj, ".got.loc"); - if (sgotloc) - sgotloc->size -= removed_bytes; - } + asection *sgotloc = elf_xtensa_hash_table (link_info)->sgotloc; + if (sgotloc) + sgotloc->size -= removed_bytes; } } } @@ -8943,19 +10731,16 @@ relax_section_symbols (bfd *abfd, asection *sec) if (isym->st_shndx == sec_shndx) { - bfd_vma new_address = offset_with_removed_text - (&relax_info->action_list, isym->st_value); - bfd_vma new_size = isym->st_size; + bfd_vma orig_addr = isym->st_value; + int removed = removed_by_actions_map (&relax_info->action_list, + orig_addr, FALSE); + isym->st_value -= removed; if (ELF32_ST_TYPE (isym->st_info) == STT_FUNC) - { - bfd_vma new_end = offset_with_removed_text - (&relax_info->action_list, isym->st_value + isym->st_size); - new_size = new_end - new_address; - } - - isym->st_value = new_address; - isym->st_size = new_size; + isym->st_size -= + removed_by_actions_map (&relax_info->action_list, + orig_addr + isym->st_size, FALSE) - + removed; } } @@ -8973,20 +10758,17 @@ relax_section_symbols (bfd *abfd, asection *sec) || sym_hash->root.type == bfd_link_hash_defweak) && sym_hash->root.u.def.section == sec) { - bfd_vma new_address = offset_with_removed_text - (&relax_info->action_list, sym_hash->root.u.def.value); - bfd_vma new_size = sym_hash->size; + bfd_vma orig_addr = sym_hash->root.u.def.value; + int removed = removed_by_actions_map (&relax_info->action_list, + orig_addr, FALSE); - if (sym_hash->type == STT_FUNC) - { - bfd_vma new_end = offset_with_removed_text - (&relax_info->action_list, - sym_hash->root.u.def.value + sym_hash->size); - new_size = new_end - new_address; - } + sym_hash->root.u.def.value -= removed; - sym_hash->root.u.def.value = new_address; - sym_hash->size = new_size; + if (sym_hash->type == STT_FUNC) + sym_hash->size -= + removed_by_actions_map (&relax_info->action_list, + orig_addr + sym_hash->size, FALSE) - + removed; } } @@ -9024,9 +10806,10 @@ do_fix_for_relocatable_link (Elf_Internal_Rela *rel, { if (r_type != R_XTENSA_ASM_EXPAND) { - (*_bfd_error_handler) - (_("%B(%A+0x%lx): unexpected fix for %s relocation"), - input_bfd, input_section, rel->r_offset, + _bfd_error_handler + /* xgettext:c-format */ + (_("%pB(%pA+%#" PRIx64 "): unexpected fix for %s relocation"), + input_bfd, input_section, (uint64_t) rel->r_offset, elf_howto_table[r_type].name); return FALSE; } @@ -9082,28 +10865,32 @@ do_fix_for_final_link (Elf_Internal_Rela *rel, /* Miscellaneous utility functions.... */ static asection * -elf_xtensa_get_plt_section (bfd *dynobj, int chunk) +elf_xtensa_get_plt_section (struct bfd_link_info *info, int chunk) { - char plt_name[10]; + bfd *dynobj; + char plt_name[17]; if (chunk == 0) - return bfd_get_section_by_name (dynobj, ".plt"); + return elf_hash_table (info)->splt; + dynobj = elf_hash_table (info)->dynobj; sprintf (plt_name, ".plt.%u", chunk); - return bfd_get_section_by_name (dynobj, plt_name); + return bfd_get_linker_section (dynobj, plt_name); } static asection * -elf_xtensa_get_gotplt_section (bfd *dynobj, int chunk) +elf_xtensa_get_gotplt_section (struct bfd_link_info *info, int chunk) { - char got_name[14]; + bfd *dynobj; + char got_name[21]; if (chunk == 0) - return bfd_get_section_by_name (dynobj, ".got.plt"); + return elf_hash_table (info)->sgotplt; + dynobj = elf_hash_table (info)->dynobj; sprintf (got_name, ".got.plt.%u", chunk); - return bfd_get_section_by_name (dynobj, got_name); + return bfd_get_linker_section (dynobj, got_name); } @@ -9130,15 +10917,12 @@ get_elf_r_symndx_section (bfd *abfd, unsigned long r_symndx) if (section_index == SHN_UNDEF) target_sec = bfd_und_section_ptr; - else if (section_index > 0 && section_index < SHN_LORESERVE) - target_sec = bfd_section_from_elf_index (abfd, section_index); else if (section_index == SHN_ABS) target_sec = bfd_abs_section_ptr; else if (section_index == SHN_COMMON) target_sec = bfd_com_section_ptr; else - /* Who knows? */ - target_sec = NULL; + target_sec = bfd_section_from_elf_index (abfd, section_index); } else { @@ -9146,8 +10930,8 @@ get_elf_r_symndx_section (bfd *abfd, unsigned long r_symndx) struct elf_link_hash_entry *h = elf_sym_hashes (abfd)[indx]; while (h->root.type == bfd_link_hash_indirect - || h->root.type == bfd_link_hash_warning) - h = (struct elf_link_hash_entry *) h->root.u.i.link; + || h->root.type == bfd_link_hash_warning) + h = (struct elf_link_hash_entry *) h->root.u.i.link; switch (h->root.type) { @@ -9211,10 +10995,10 @@ get_elf_r_symndx_offset (bfd *abfd, unsigned long r_symndx) elf_sym_hashes (abfd)[indx]; while (h->root.type == bfd_link_hash_indirect - || h->root.type == bfd_link_hash_warning) + || h->root.type == bfd_link_hash_warning) h = (struct elf_link_hash_entry *) h->root.u.i.link; if (h->root.type == bfd_link_hash_defined - || h->root.type == bfd_link_hash_defweak) + || h->root.type == bfd_link_hash_defweak) offset = h->root.u.def.value; } return offset; @@ -9249,39 +11033,45 @@ pcrel_reloc_fits (xtensa_opcode opc, } -static int linkonce_len = sizeof (".gnu.linkonce.") - 1; -static int insn_sec_len = sizeof (XTENSA_INSN_SEC_NAME) - 1; -static int lit_sec_len = sizeof (XTENSA_LIT_SEC_NAME) - 1; -static int prop_sec_len = sizeof (XTENSA_PROP_SEC_NAME) - 1; - - -static bfd_boolean +static bfd_boolean xtensa_is_property_section (asection *sec) { - if (strncmp (XTENSA_INSN_SEC_NAME, sec->name, insn_sec_len) == 0 - || strncmp (XTENSA_LIT_SEC_NAME, sec->name, lit_sec_len) == 0 - || strncmp (XTENSA_PROP_SEC_NAME, sec->name, prop_sec_len) == 0) + if (xtensa_is_insntable_section (sec) + || xtensa_is_littable_section (sec) + || xtensa_is_proptable_section (sec)) return TRUE; - if (strncmp (".gnu.linkonce.", sec->name, linkonce_len) == 0 - && (strncmp (&sec->name[linkonce_len], "x.", 2) == 0 - || strncmp (&sec->name[linkonce_len], "p.", 2) == 0 - || strncmp (&sec->name[linkonce_len], "prop.", 5) == 0)) + return FALSE; +} + + +static bfd_boolean +xtensa_is_insntable_section (asection *sec) +{ + if (CONST_STRNEQ (sec->name, XTENSA_INSN_SEC_NAME) + || CONST_STRNEQ (sec->name, ".gnu.linkonce.x.")) return TRUE; return FALSE; } -static bfd_boolean +static bfd_boolean xtensa_is_littable_section (asection *sec) { - if (strncmp (XTENSA_LIT_SEC_NAME, sec->name, lit_sec_len) == 0) + if (CONST_STRNEQ (sec->name, XTENSA_LIT_SEC_NAME) + || CONST_STRNEQ (sec->name, ".gnu.linkonce.p.")) return TRUE; - if (strncmp (".gnu.linkonce.", sec->name, linkonce_len) == 0 - && sec->name[linkonce_len] == 'p' - && sec->name[linkonce_len + 1] == '.') + return FALSE; +} + + +static bfd_boolean +xtensa_is_proptable_section (asection *sec) +{ + if (CONST_STRNEQ (sec->name, XTENSA_PROP_SEC_NAME) + || CONST_STRNEQ (sec->name, ".gnu.linkonce.prop.")) return TRUE; return FALSE; @@ -9323,18 +11113,64 @@ internal_reloc_matches (const void *ap, const void *bp) } -char * -xtensa_get_property_section_name (asection *sec, const char *base_name) +/* Predicate function used to look up a section in a particular group. */ + +static bfd_boolean +match_section_group (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *inf) +{ + const char *gname = inf; + const char *group_name = elf_group_name (sec); + + return (group_name == gname + || (group_name != NULL + && gname != NULL + && strcmp (group_name, gname) == 0)); +} + + +static char * +xtensa_add_names (const char *base, const char *suffix) +{ + if (suffix) + { + size_t base_len = strlen (base); + size_t suffix_len = strlen (suffix); + char *str = bfd_malloc (base_len + suffix_len + 1); + + memcpy (str, base, base_len); + memcpy (str + base_len, suffix, suffix_len + 1); + return str; + } + else + { + return strdup (base); + } +} + +static int linkonce_len = sizeof (".gnu.linkonce.") - 1; + +static char * +xtensa_property_section_name (asection *sec, const char *base_name, + bfd_boolean separate_sections) { - if (strncmp (sec->name, ".gnu.linkonce.", linkonce_len) == 0) + const char *suffix, *group_name; + char *prop_sec_name; + + group_name = elf_group_name (sec); + if (group_name) + { + suffix = strrchr (sec->name, '.'); + if (suffix == sec->name) + suffix = 0; + prop_sec_name = xtensa_add_names (base_name, suffix); + } + else if (strncmp (sec->name, ".gnu.linkonce.", linkonce_len) == 0) { - char *prop_sec_name; - const char *suffix; char *linkonce_kind = 0; - if (strcmp (base_name, XTENSA_INSN_SEC_NAME) == 0) + if (strcmp (base_name, XTENSA_INSN_SEC_NAME) == 0) linkonce_kind = "x."; - else if (strcmp (base_name, XTENSA_LIT_SEC_NAME) == 0) + else if (strcmp (base_name, XTENSA_LIT_SEC_NAME) == 0) linkonce_kind = "p."; else if (strcmp (base_name, XTENSA_PROP_SEC_NAME) == 0) linkonce_kind = "prop."; @@ -9348,31 +11184,96 @@ xtensa_get_property_section_name (asection *sec, const char *base_name) suffix = sec->name + linkonce_len; /* For backward compatibility, replace "t." instead of inserting - the new linkonce_kind (but not for "prop" sections). */ - if (strncmp (suffix, "t.", 2) == 0 && linkonce_kind[1] == '.') - suffix += 2; + the new linkonce_kind (but not for "prop" sections). */ + if (CONST_STRNEQ (suffix, "t.") && linkonce_kind[1] == '.') + suffix += 2; strcat (prop_sec_name + linkonce_len, suffix); + } + else + { + prop_sec_name = xtensa_add_names (base_name, + separate_sections ? sec->name : NULL); + } + + return prop_sec_name; +} + + +static asection * +xtensa_get_separate_property_section (asection *sec, const char *base_name, + bfd_boolean separate_section) +{ + char *prop_sec_name; + asection *prop_sec; + + prop_sec_name = xtensa_property_section_name (sec, base_name, + separate_section); + prop_sec = bfd_get_section_by_name_if (sec->owner, prop_sec_name, + match_section_group, + (void *) elf_group_name (sec)); + free (prop_sec_name); + return prop_sec; +} + +static asection * +xtensa_get_property_section (asection *sec, const char *base_name) +{ + asection *prop_sec; + + /* Try individual property section first. */ + prop_sec = xtensa_get_separate_property_section (sec, base_name, TRUE); + + /* Refer to a common property section if individual is not present. */ + if (!prop_sec) + prop_sec = xtensa_get_separate_property_section (sec, base_name, FALSE); + + return prop_sec; +} + + +asection * +xtensa_make_property_section (asection *sec, const char *base_name) +{ + char *prop_sec_name; + asection *prop_sec; - return prop_sec_name; + /* Check if the section already exists. */ + prop_sec_name = xtensa_property_section_name (sec, base_name, + elf32xtensa_separate_props); + prop_sec = bfd_get_section_by_name_if (sec->owner, prop_sec_name, + match_section_group, + (void *) elf_group_name (sec)); + /* If not, create it. */ + if (! prop_sec) + { + flagword flags = (SEC_RELOC | SEC_HAS_CONTENTS | SEC_READONLY); + flags |= (bfd_section_flags (sec) + & (SEC_LINK_ONCE | SEC_LINK_DUPLICATES)); + + prop_sec = bfd_make_section_anyway_with_flags + (sec->owner, strdup (prop_sec_name), flags); + if (! prop_sec) + return 0; + + elf_group_name (prop_sec) = elf_group_name (sec); } - return strdup (base_name); + free (prop_sec_name); + return prop_sec; } flagword xtensa_get_property_predef_flags (asection *sec) { - if (strcmp (sec->name, XTENSA_INSN_SEC_NAME) == 0 - || strncmp (sec->name, ".gnu.linkonce.x.", - sizeof ".gnu.linkonce.x." - 1) == 0) + if (xtensa_is_insntable_section (sec)) return (XTENSA_PROP_INSN - | XTENSA_PROP_INSN_NO_TRANSFORM + | XTENSA_PROP_NO_TRANSFORM | XTENSA_PROP_INSN_NO_REORDER); if (xtensa_is_littable_section (sec)) return (XTENSA_PROP_LITERAL - | XTENSA_PROP_INSN_NO_TRANSFORM + | XTENSA_PROP_NO_TRANSFORM | XTENSA_PROP_INSN_NO_REORDER); return 0; @@ -9399,13 +11300,13 @@ xtensa_callback_required_dependence (bfd *abfd, /* ".plt*" sections have no explicit relocations but they contain L32R instructions that reference the corresponding ".got.plt*" sections. */ if ((sec->flags & SEC_LINKER_CREATED) != 0 - && strncmp (sec->name, ".plt", 4) == 0) + && CONST_STRNEQ (sec->name, ".plt")) { asection *sgotplt; /* Find the corresponding ".got.plt*" section. */ if (sec->name[4] == '\0') - sgotplt = bfd_get_section_by_name (sec->owner, ".got.plt"); + sgotplt = elf_hash_table (link_info)->sgotplt; else { char got_name[14]; @@ -9415,7 +11316,7 @@ xtensa_callback_required_dependence (bfd *abfd, chunk = strtol (&sec->name[5], NULL, 10); sprintf (got_name, ".got.plt.%u", chunk); - sgotplt = bfd_get_section_by_name (sec->owner, got_name); + sgotplt = bfd_get_linker_section (sec->owner, got_name); } BFD_ASSERT (sgotplt); @@ -9425,7 +11326,12 @@ xtensa_callback_required_dependence (bfd *abfd, (*callback) (sec, sec_size, sgotplt, 0, closure); } - internal_relocs = retrieve_internal_relocs (abfd, sec, + /* Only ELF files are supported for Xtensa. Check here to avoid a segfault + when building uclibc, which runs "ld -b binary /dev/null". */ + if (bfd_get_flavour (abfd) != bfd_target_elf_flavour) + return ok; + + internal_relocs = retrieve_internal_relocs (abfd, sec, link_info->keep_memory); if (internal_relocs == NULL || sec->reloc_count == 0) @@ -9476,33 +11382,25 @@ xtensa_callback_required_dependence (bfd *abfd, module loader so that the literals are not placed after the text. */ static const struct bfd_elf_special_section elf_xtensa_special_sections[] = { - { ".fini.literal", 13, 0, SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR }, - { ".init.literal", 13, 0, SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR }, - { ".literal", 8, 0, SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR }, - { NULL, 0, 0, 0, 0 } + { STRING_COMMA_LEN (".fini.literal"), 0, SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR }, + { STRING_COMMA_LEN (".init.literal"), 0, SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR }, + { STRING_COMMA_LEN (".literal"), 0, SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR }, + { STRING_COMMA_LEN (".xtensa.info"), 0, SHT_NOTE, 0 }, + { NULL, 0, 0, 0, 0 } }; +#define ELF_TARGET_ID XTENSA_ELF_DATA #ifndef ELF_ARCH -#define TARGET_LITTLE_SYM bfd_elf32_xtensa_le_vec +#define TARGET_LITTLE_SYM xtensa_elf32_le_vec #define TARGET_LITTLE_NAME "elf32-xtensa-le" -#define TARGET_BIG_SYM bfd_elf32_xtensa_be_vec +#define TARGET_BIG_SYM xtensa_elf32_be_vec #define TARGET_BIG_NAME "elf32-xtensa-be" #define ELF_ARCH bfd_arch_xtensa -/* The new EM_XTENSA value will be recognized beginning in the Xtensa T1040 - release. However, we still have to generate files with the EM_XTENSA_OLD - value so that pre-T1040 tools can read the files. As soon as we stop - caring about pre-T1040 tools, the following two values should be - swapped. At the same time, any other code that uses EM_XTENSA_OLD - should be changed to use EM_XTENSA. */ -#define ELF_MACHINE_CODE EM_XTENSA_OLD -#define ELF_MACHINE_ALT1 EM_XTENSA - -#if XCHAL_HAVE_MMU -#define ELF_MAXPAGESIZE (1 << XCHAL_MMU_MIN_PTE_PAGE_SIZE) -#else /* !XCHAL_HAVE_MMU */ -#define ELF_MAXPAGESIZE 1 -#endif /* !XCHAL_HAVE_MMU */ +#define ELF_MACHINE_CODE EM_XTENSA +#define ELF_MACHINE_ALT1 EM_XTENSA_OLD + +#define ELF_MAXPAGESIZE 0x1000 #endif /* ELF_ARCH */ #define elf_backend_can_gc_sections 1 @@ -9511,15 +11409,21 @@ static const struct bfd_elf_special_section elf_xtensa_special_sections[] = #define elf_backend_got_header_size 4 #define elf_backend_want_dynbss 0 #define elf_backend_want_got_plt 1 +#define elf_backend_dtrel_excludes_plt 1 #define elf_info_to_howto elf_xtensa_info_to_howto_rela +#define bfd_elf32_mkobject elf_xtensa_mkobject + #define bfd_elf32_bfd_merge_private_bfd_data elf_xtensa_merge_private_bfd_data #define bfd_elf32_new_section_hook elf_xtensa_new_section_hook #define bfd_elf32_bfd_print_private_bfd_data elf_xtensa_print_private_bfd_data #define bfd_elf32_bfd_relax_section elf_xtensa_relax_section #define bfd_elf32_bfd_reloc_type_lookup elf_xtensa_reloc_type_lookup +#define bfd_elf32_bfd_reloc_name_lookup \ + elf_xtensa_reloc_name_lookup #define bfd_elf32_bfd_set_private_flags elf_xtensa_set_private_flags +#define bfd_elf32_bfd_link_hash_table_create elf_xtensa_link_hash_table_create #define elf_backend_adjust_dynamic_symbol elf_xtensa_adjust_dynamic_symbol #define elf_backend_check_relocs elf_xtensa_check_relocs @@ -9530,15 +11434,17 @@ static const struct bfd_elf_special_section elf_xtensa_special_sections[] = #define elf_backend_finish_dynamic_sections elf_xtensa_finish_dynamic_sections #define elf_backend_finish_dynamic_symbol elf_xtensa_finish_dynamic_symbol #define elf_backend_gc_mark_hook elf_xtensa_gc_mark_hook -#define elf_backend_gc_sweep_hook elf_xtensa_gc_sweep_hook #define elf_backend_grok_prstatus elf_xtensa_grok_prstatus #define elf_backend_grok_psinfo elf_xtensa_grok_psinfo #define elf_backend_hide_symbol elf_xtensa_hide_symbol -#define elf_backend_modify_segment_map elf_xtensa_modify_segment_map #define elf_backend_object_p elf_xtensa_object_p #define elf_backend_reloc_type_class elf_xtensa_reloc_type_class #define elf_backend_relocate_section elf_xtensa_relocate_section #define elf_backend_size_dynamic_sections elf_xtensa_size_dynamic_sections +#define elf_backend_always_size_sections elf_xtensa_always_size_sections +#define elf_backend_omit_section_dynsym _bfd_elf_omit_section_dynsym_all #define elf_backend_special_sections elf_xtensa_special_sections +#define elf_backend_action_discarded elf_xtensa_action_discarded +#define elf_backend_copy_indirect_symbol elf_xtensa_copy_indirect_symbol #include "elf32-target.h"