X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=bfd%2Felf32-xtensa.c;h=c6c9c12ccf12c93481bfe2847754f339703d864a;hb=545fd46b6bb24535905e4bc69b91537fee065bf3;hp=fc235deeb53bd5457d575fc421b4ec1ac04e0265;hpb=843fe662b9589a111d4ac7cb8f94e79a7326b0e8;p=deliverable%2Fbinutils-gdb.git diff --git a/bfd/elf32-xtensa.c b/bfd/elf32-xtensa.c index fc235deeb5..c6c9c12ccf 100644 --- a/bfd/elf32-xtensa.c +++ b/bfd/elf32-xtensa.c @@ -1,11 +1,12 @@ /* Xtensa-specific support for 32-bit ELF. - Copyright 2003, 2004, 2005 Free Software Foundation, Inc. + Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 + 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 +19,8 @@ Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -#include "bfd.h" #include "sysdep.h" +#include "bfd.h" #include #include @@ -35,7 +36,7 @@ /* Local helper functions. */ -static bfd_boolean add_extra_plt_sections (bfd *, int); +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 **); @@ -94,8 +95,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 +104,13 @@ 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 *); +extern asection *xtensa_make_property_section (asection *, const char *); static flagword xtensa_get_property_predef_flags (asection *); /* Other functions called directly by the linker. */ @@ -132,16 +136,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 +155,167 @@ static bfd_boolean relaxing_section = FALSE; int elf32xtensa_no_literal_movement = 1; +/* 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, 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), + 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), + 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), + bfd_elf_xtensa_reloc, "R_XTENSA_DIFF8", FALSE, 0, 0xff, FALSE), HOWTO (R_XTENSA_DIFF16, 0, 1, 16, FALSE, 0, complain_overflow_bitfield, - bfd_elf_xtensa_reloc, "R_XTENSA_DIFF16", - FALSE, 0xffffffff, 0xffffffff, FALSE), + bfd_elf_xtensa_reloc, "R_XTENSA_DIFF16", FALSE, 0, 0xffff, FALSE), HOWTO (R_XTENSA_DIFF32, 0, 2, 32, FALSE, 0, complain_overflow_bitfield, - bfd_elf_xtensa_reloc, "R_XTENSA_DIFF32", - FALSE, 0xffffffff, 0xffffffff, FALSE), + 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 +339,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 +403,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) @@ -430,6 +455,20 @@ elf_xtensa_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED, 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. */ @@ -496,9 +535,191 @@ static const bfd_byte elf_xtensa_le_plt_entry[PLT_ENTRY_SIZE] = 0 /* unused */ }; +/* The size of the thread control block. */ +#define TCB_SIZE 8 + +struct elf_xtensa_link_hash_entry +{ + struct elf_link_hash_entry elf; + + bfd_signed_vma tlsfunc_refcount; + +#define GOT_UNKNOWN 0 +#define GOT_NORMAL 1 +#define GOT_TLS_GD 2 /* global or local dynamic */ +#define GOT_TLS_IE 4 /* initial or local exec */ +#define GOT_TLS_ANY (GOT_TLS_GD | GOT_TLS_IE) + unsigned char tls_type; +}; + +#define elf_xtensa_hash_entry(ent) ((struct elf_xtensa_link_hash_entry *)(ent)) + +struct elf_xtensa_obj_tdata +{ + struct elf_obj_tdata root; + + /* tls_type for each local got entry. */ + char *local_got_tls_type; + + bfd_signed_vma *local_tlsfunc_refcounts; +}; + +#define elf_xtensa_tdata(abfd) \ + ((struct elf_xtensa_obj_tdata *) (abfd)->tdata.any) + +#define elf_xtensa_local_got_tls_type(abfd) \ + (elf_xtensa_tdata (abfd)->local_got_tls_type) + +#define elf_xtensa_local_tlsfunc_refcounts(abfd) \ + (elf_xtensa_tdata (abfd)->local_tlsfunc_refcounts) + +#define is_xtensa_elf(bfd) \ + (bfd_get_flavour (bfd) == bfd_target_elf_flavour \ + && elf_tdata (bfd) != NULL \ + && elf_object_id (bfd) == XTENSA_ELF_DATA) + +static 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 +{ + struct elf_link_hash_table elf; + + /* Short-cuts to get to dynamic linker sections. */ + asection *sgot; + asection *sgotplt; + asection *srelgot; + asection *splt; + asection *srelplt; + 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; + bfd_size_type amt = sizeof (struct elf_xtensa_link_hash_table); + + ret = bfd_malloc (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; + } + + ret->sgot = NULL; + ret->sgotplt = NULL; + ret->srelgot = NULL; + ret->splt = NULL; + ret->srelplt = NULL; + ret->sgotloc = NULL; + ret->spltlittbl = NULL; + + ret->plt_reloc_count = 0; + + /* 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 @@ -571,16 +792,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,9 +810,7 @@ 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; @@ -618,68 +836,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; + qsort (internal_relocs, table_section->reloc_count, + sizeof (Elf_Internal_Rela), internal_reloc_compare); + irel = internal_relocs; + } + else + irel = NULL; - for (i = 0; i < table_section->reloc_count; i++) - { - Elf_Internal_Rela *rel = &internal_relocs[i]; - unsigned long r_symndx; + section_limit = bfd_get_section_limit (abfd, section); + rel_end = internal_relocs + table_section->reloc_count; - if (ELF32_R_TYPE (rel->r_info) == R_XTENSA_NONE) - continue; - - 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); @@ -758,14 +971,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 (info->relocatable || (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); @@ -774,7 +994,12 @@ elf_xtensa_check_relocs (bfd *abfd, { unsigned int r_type; unsigned long r_symndx; - struct elf_link_hash_entry *h; + 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); @@ -786,38 +1011,91 @@ elf_xtensa_check_relocs (bfd *abfd, 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 (info->shared) + { + 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 (info->shared) { - 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 (info->shared) + tls_type = GOT_TLS_GD; + else + tls_type = GOT_TLS_IE; + break; + + case R_XTENSA_TLS_TPOFF: + tls_type = GOT_TLS_IE; + if (info->shared) + info->flags |= DF_STATIC_TLS; + if (info->shared || 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; - if ((sec->flags & SEC_ALLOC) != 0) + 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. */ + BFD_ASSERT (h != NULL); + if (h != NULL + && !bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend)) + return FALSE; + continue; + + default: + /* Nothing to do for any other relocations. */ + continue; + } + + if (h) + { + if (is_plt) { if (h->plt.refcount <= 0) { @@ -830,97 +1108,90 @@ 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) + (_("%B: `%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; } } @@ -930,18 +1201,19 @@ elf_xtensa_check_relocs (bfd *abfd, static void elf_xtensa_make_sym_local (struct bfd_link_info *info, - struct elf_link_hash_entry *h) + struct elf_link_hash_entry *h) { if (info->shared) { if (h->plt.refcount > 0) - { - /* Will use RELATIVE relocs instead of JMP_SLOT relocs. */ - if (h->got.refcount < 0) - h->got.refcount = 0; - h->got.refcount += h->plt.refcount; - h->plt.refcount = 0; - } + { + /* For shared objects, there's no need for PLT entries for local + symbols (use RELATIVE relocs instead of JMP_SLOT relocs). */ + if (h->got.refcount < 0) + h->got.refcount = 0; + h->got.refcount += h->plt.refcount; + h->plt.refcount = 0; + } } else { @@ -954,8 +1226,8 @@ elf_xtensa_make_sym_local (struct bfd_link_info *info, static void elf_xtensa_hide_symbol (struct bfd_link_info *info, - struct elf_link_hash_entry *h, - bfd_boolean force_local) + struct elf_link_hash_entry *h, + bfd_boolean force_local) { /* For a shared link, move the plt refcount to the got refcount to leave space for RELATIVE relocs. */ @@ -970,38 +1242,33 @@ 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); } @@ -1010,21 +1277,27 @@ elf_xtensa_gc_mark_hook (asection *sec, static bfd_boolean elf_xtensa_gc_sweep_hook (bfd *abfd, - struct bfd_link_info *info ATTRIBUTE_UNUSED, + struct bfd_link_info *info, 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; + struct elf_xtensa_link_hash_table *htab; + + htab = elf_xtensa_hash_table (info); + if (htab == NULL) + return FALSE; + + if (info->relocatable) + return TRUE; 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++) @@ -1032,6 +1305,10 @@ elf_xtensa_gc_sweep_hook (bfd *abfd, unsigned long r_symndx; unsigned int r_type; struct elf_link_hash_entry *h = NULL; + struct elf_xtensa_link_hash_entry *eh; + bfd_boolean is_got = FALSE; + bfd_boolean is_plt = FALSE; + bfd_boolean is_tlsfunc = FALSE; r_symndx = ELF32_R_SYM (rel->r_info); if (r_symndx >= symtab_hdr->sh_info) @@ -1041,31 +1318,80 @@ elf_xtensa_gc_sweep_hook (bfd *abfd, || h->root.type == bfd_link_hash_warning) h = (struct elf_link_hash_entry *) h->root.u.i.link; } + eh = elf_xtensa_hash_entry (h); 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--; + case R_XTENSA_TLSDESC_FN: + if (info->shared) + { + is_got = TRUE; + is_tlsfunc = TRUE; + } break; - case R_XTENSA_PLT: - if (h == NULL) - goto local_literal; - if (h->plt.refcount > 0) - h->plt.refcount--; + case R_XTENSA_TLSDESC_ARG: + if (info->shared) + is_got = TRUE; + else + { + if (h && elf_xtensa_hash_entry (h) != htab->tlsbase) + is_got = TRUE; + } break; - local_literal: - if (local_got_refcounts[r_symndx] > 0) - local_got_refcounts[r_symndx] -= 1; + case R_XTENSA_TLS_TPOFF: + if (info->shared || h) + is_got = TRUE; break; - default: + case R_XTENSA_32: + is_got = TRUE; + break; + + case R_XTENSA_PLT: + is_plt = TRUE; break; + + default: + continue; + } + + if (h) + { + if (is_plt) + { + if (h->plt.refcount > 0) + h->plt.refcount--; + } + else if (is_got) + { + if (h->got.refcount > 0) + h->got.refcount--; + } + if (is_tlsfunc) + { + if (eh->tlsfunc_refcount > 0) + eh->tlsfunc_refcount--; + } + } + else + { + if (is_got || is_plt) + { + bfd_signed_vma *got_refcount + = &elf_local_got_refcounts (abfd) [r_symndx]; + if (*got_refcount > 0) + *got_refcount -= 1; + } + if (is_tlsfunc) + { + bfd_signed_vma *tlsfunc_refcount + = &elf_xtensa_local_tlsfunc_refcounts (abfd) [r_symndx]; + if (*tlsfunc_refcount > 0) + *tlsfunc_refcount -= 1; + } } } @@ -1078,16 +1404,25 @@ elf_xtensa_gc_sweep_hook (bfd *abfd, static bfd_boolean 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)) return FALSE; + htab->splt = bfd_get_section_by_name (dynobj, ".plt"); + htab->srelplt = bfd_get_section_by_name (dynobj, ".rela.plt"); + htab->sgot = bfd_get_section_by_name (dynobj, ".got"); + htab->sgotplt = bfd_get_section_by_name (dynobj, ".got.plt"); + htab->srelgot = bfd_get_section_by_name (dynobj, ".rela.got"); /* 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 +1430,21 @@ 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->sgotplt == NULL + || ! bfd_set_section_flags (dynobj, htab->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_with_flags (dynobj, ".got.loc", flags); + if (htab->sgotloc == NULL + || ! bfd_set_section_alignment (dynobj, 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_with_flags (dynobj, ".xt.lit.plt", + noalloc_flags); + if (htab->spltlittbl == NULL + || ! bfd_set_section_alignment (dynobj, htab->spltlittbl, 2)) return FALSE; return TRUE; @@ -1124,8 +1452,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 +1466,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,8 +1474,7 @@ 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_with_flags (dynobj, sname, flags | SEC_CODE); if (s == NULL || ! bfd_set_section_alignment (dynobj, s, 2)) return FALSE; @@ -1194,56 +1522,51 @@ 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); - - return TRUE; -} + if (h->root.type == bfd_link_hash_indirect) + return TRUE; + info = (struct bfd_link_info *) arg; + htab = elf_xtensa_hash_table (info); + if (htab == NULL) + return FALSE; -static bfd_boolean -elf_xtensa_allocate_plt_size (struct elf_link_hash_entry *h, void *arg) -{ - asection *srelplt = (asection *) arg; + /* If we saw any use of an IE model for this symbol, we can then optimize + away GOT entries for any TLSDESC_FN relocs. */ + if ((eh->tls_type & GOT_TLS_IE) != 0) + { + BFD_ASSERT (h->got.refcount >= eh->tlsfunc_refcount); + h->got.refcount -= eh->tlsfunc_refcount; + } - if (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)) + elf_xtensa_make_sym_local (info, h); 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->srelplt->size += (h->plt.refcount * sizeof (Elf32_External_Rela)); if (h->got.refcount > 0) - srelgot->size += (h->got.refcount * sizeof (Elf32_External_Rela)); + htab->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; + 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; @@ -1259,9 +1582,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->srelgot->size += (local_got_refcounts[j] + * sizeof (Elf32_External_Rela)); } } } @@ -1273,6 +1606,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,14 +1614,25 @@ 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->srelgot; + srelplt = htab->srelplt; if (elf_hash_table (info)->dynamic_sections_created) { + BFD_ASSERT (htab->srelgot != NULL + && htab->srelplt != NULL + && htab->sgot != NULL + && htab->spltlittbl != NULL + && htab->sgotloc != NULL); + /* Set the contents of the .interp section to the interpreter. */ if (info->executable) { @@ -1299,48 +1644,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->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); + 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 +1673,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,9 +1704,7 @@ 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) { @@ -1391,7 +1712,7 @@ elf_xtensa_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, 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; @@ -1413,7 +1734,7 @@ elf_xtensa_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, of the dynobj section names depend upon the input files. */ name = bfd_get_section_name (dynobj, s); - if (strncmp (name, ".rela", 5) == 0) + if (CONST_STRNEQ (name, ".rela")) { if (s->size != 0) { @@ -1427,8 +1748,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 +1787,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 +1812,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 (info->executable) { if (!add_dynamic_entry (DT_DEBUG, 0)) return FALSE; @@ -1501,8 +1820,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 +1834,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 +1844,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; - m_p = &elf_tdata (abfd)->segment_map; - while (*m_p) + htab = elf_xtensa_hash_table (info); + if (htab == NULL) + return FALSE; + + tls_sec = htab->elf.tls_sec; + + if (tls_sec && (htab->tlsbase->tls_type & GOT_TLS_ANY) != 0) { - 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 +1930,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,26 +1943,30 @@ 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)) { @@ -1629,12 +1989,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 +2002,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.... */ @@ -1727,11 +2099,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; } } @@ -1809,12 +2176,15 @@ vsprint_msg (const char *origmsg, const char *fmt, int arglen, ...) 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); + if (message != NULL) + { + if (!is_append) + memcpy (message, origmsg, orig_len); + vsprintf (message + orig_len, fmt, ap); + } VA_CLOSE (ap); return message; } @@ -1938,7 +2308,7 @@ 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) { @@ -1948,8 +2318,8 @@ elf_xtensa_create_plt_entry (bfd *dynobj, 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; @@ -1983,6 +2353,188 @@ elf_xtensa_create_plt_entry (bfd *dynobj, } +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. */ @@ -1996,31 +2548,30 @@ 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; + BFD_ASSERT (is_xtensa_elf (input_bfd)); + + 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); - - srelgot = NULL; - srelplt = NULL; - if (dynobj) - { - srelgot = bfd_get_section_by_name (dynobj, ".rela.got");; - srelplt = bfd_get_section_by_name (dynobj, ".rela.plt"); - } + local_got_tls_types = elf_xtensa_local_got_tls_type (input_bfd); if (elf_hash_table (info)->dynamic_sections_created) { @@ -2042,29 +2593,78 @@ 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 || r_type == (int) R_XTENSA_GNU_VTENTRY) continue; - if (r_type < 0 || r_type >= (int) R_XTENSA_max) + if (r_type < 0 || r_type >= (int) R_XTENSA_max) + { + bfd_set_error (bfd_error_bad_value); + return FALSE; + } + howto = &elf_howto_table[r_type]; + + r_symndx = ELF32_R_SYM (rel->r_info); + + h = NULL; + sym = NULL; + sec = NULL; + is_weak_undef = FALSE; + unresolved_reloc = FALSE; + warned = FALSE; + + if (howto->partial_inplace && !info->relocatable) + { + /* 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) { - bfd_set_error (bfd_error_bad_value); - return FALSE; + 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); } - howto = &elf_howto_table[r_type]; + else + { + RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel, + r_symndx, symtab_hdr, sym_hashes, + h, sec, relocation, + unresolved_reloc, warned); - r_symndx = ELF32_R_SYM (rel->r_info); + 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 (info->relocatable) { + 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. @@ -2079,12 +2679,14 @@ 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, @@ -2118,25 +2720,41 @@ 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) + { + if (!((*info->callbacks->reloc_dangerous) + (info, error_message, input_bfd, input_section, + rel->r_offset))) + return FALSE; + } /* Done with work for relocatable link; continue with next reloc. */ continue; @@ -2144,51 +2762,11 @@ 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. */ @@ -2202,36 +2780,58 @@ elf_xtensa_relocate_section (bfd *output_bfd, 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 (input_bfd, 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 + ? _("%B(%A+0x%lx): %s used with TLS symbol %s") + : _("%B(%A+0x%lx): %s used with non-TLS symbol %s")), + input_bfd, + input_section, + (long) 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 || info->shared)) { Elf_Internal_Rela outrel; bfd_byte *loc; asection *srel; if (dynamic_symbol && r_type == R_XTENSA_PLT) - srel = srelplt; + srel = htab->srelplt; else - srel = srelgot; + srel = htab->srelgot; BFD_ASSERT (srel != NULL); @@ -2280,7 +2880,7 @@ 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; @@ -2299,6 +2899,158 @@ 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 (! info->shared && ! 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 (! info->shared || (tls_type & GOT_TLS_IE) != 0) + r_type = R_XTENSA_NONE; + } + else if (r_type == R_XTENSA_TLSDESC_ARG) + { + if (info->shared) + { + 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"); + if (!((*info->callbacks->reloc_dangerous) + (info, error_message, input_bfd, input_section, + rel->r_offset))) + return FALSE; + } + else + { + Elf_Internal_Rela outrel; + bfd_byte *loc; + asection *srel = htab->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"); + if (!((*info->callbacks->reloc_dangerous) + (info, error_message, input_bfd, input_section, + rel->r_offset))) + return FALSE; + } + + 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 (! info->shared) + /* 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)) + { + if (!((*info->callbacks->reloc_dangerous) + (info, error_message, input_bfd, input_section, + rel->r_offset))) + return FALSE; + } + + 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); + if (!((*info->callbacks->reloc_dangerous) + (info, error_message, input_bfd, input_section, + rel->r_offset))) + return FALSE; + continue; + } + break; } /* Dynamic relocs are not propagated for SEC_DEBUGGING sections @@ -2306,14 +3058,22 @@ 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 %s relocation against symbol `%s'"), - input_bfd, - input_section, - (long) rel->r_offset, - howto->name, - 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) + (_("%B(%A+0x%lx): unresolvable %s relocation against symbol `%s'"), + input_bfd, + input_section, + (long) 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". */ @@ -2324,30 +3084,16 @@ 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, @@ -2374,17 +3120,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) + || h == elf_hash_table (info)->hgot) sym->st_shndx = SHN_ABS; return TRUE; @@ -2454,18 +3205,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++) { @@ -2510,21 +3261,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; 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"); 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->sgot; if (sgot) { BFD_ASSERT (sgot->size == 4); @@ -2536,7 +3292,7 @@ elf_xtensa_finish_dynamic_sections (bfd *output_bfd, sgot->contents); } - srelplt = bfd_get_section_by_name (dynobj, ".rela.plt"); + srelplt = htab->srelplt; if (srelplt && srelplt->size != 0) { asection *sgotplt, *srelgot, *spltlittbl; @@ -2545,11 +3301,9 @@ elf_xtensa_finish_dynamic_sections (bfd *output_bfd, 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); + srelgot = htab->srelgot; + spltlittbl = htab->spltlittbl; + BFD_ASSERT (srelgot != NULL && spltlittbl != NULL); /* Find the first XTENSA_RTLD relocation. Presumably the rest of them follow immediately after.... */ @@ -2570,7 +3324,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 @@ -2638,20 +3392,21 @@ elf_xtensa_finish_dynamic_sections (bfd *output_bfd, /* Combine adjacent literal table entries. */ BFD_ASSERT (! info->relocatable); 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); @@ -2665,23 +3420,19 @@ 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; + break; + case DT_PLTGOT: - name = ".got"; - goto get_vma; + dyn.d_un.d_ptr = htab->sgot->output_section->vma; + break; + 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->srelplt->output_section->vma; break; case DT_PLTRELSZ: - s = bfd_get_section_by_name (output_bfd, ".rela.plt"); - BFD_ASSERT (s); - dyn.d_un.d_val = s->size; + dyn.d_un.d_val = htab->srelplt->output_section->size; break; case DT_RELASZ: @@ -2691,9 +3442,8 @@ elf_xtensa_finish_dynamic_sections (bfd *output_bfd, 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; + if (htab->srelplt) + dyn.d_un.d_val -= htab->srelplt->output_section->size; break; } @@ -2715,7 +3465,7 @@ elf_xtensa_merge_private_bfd_data (bfd *ibfd, bfd *obfd) unsigned out_mach, in_mach; flagword out_flag, in_flag; - /* Check if we have the same endianess. */ + /* Check if we have the same endianness. */ if (!_bfd_generic_verify_endian_match (ibfd, obfd)) return FALSE; @@ -2865,18 +3615,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; @@ -2888,10 +3642,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; @@ -2915,11 +3674,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. */ @@ -2952,24 +3711,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 @@ -3009,6 +3765,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. */ @@ -3025,7 +3794,7 @@ elf_xtensa_grok_prstatus (bfd *abfd, Elf_Internal_Note *note) 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; @@ -3131,6 +3900,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) { @@ -3434,24 +4226,23 @@ 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; + 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); - BFD_ASSERT (loop_len != 0); - if (loop_len == 0) - return FALSE; - 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; + } return check_branch_target_aligned_address (address + loop_len, insn_len); } @@ -3563,60 +4354,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); @@ -3632,47 +4394,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) @@ -3680,7 +4436,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. */ @@ -3690,52 +4446,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); @@ -3743,7 +4488,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); @@ -3761,7 +4506,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 @@ -3779,14 +4560,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); @@ -3794,32 +4575,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) @@ -3827,7 +4605,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. */ @@ -3837,18 +4615,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; } @@ -4683,12 +5516,20 @@ text_action_add (text_action_list *l, 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 && action == ta_fill) + + if (action == ta_fill) { - t->removed_bytes += removed; - return; + /* When the action is another fill at the same address, + just increase the size. */ + if (t->offset == offset && t->action == ta_fill) + { + t->removed_bytes += removed; + return; + } + /* Fills need to happen before widens so that we don't + insert fill bytes into the instruction stream. */ + if (t->offset == offset && t->action == ta_widen_insn) + break; } } @@ -4739,34 +5580,58 @@ text_action_add_literal (text_action_list *l, } -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 **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; + 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 = r->next; } - 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) +static bfd_vma +offset_with_removed_text (text_action_list *action_list, bfd_vma offset) { - text_action *r; - int removed = 0; + text_action *r = action_list->head; + return offset - removed_by_actions (&r, offset, FALSE); +} - for (r = action_list->head; r && r->offset < offset; r = r->next) - removed += r->removed_bytes; - return (offset - removed); +static unsigned +action_list_count (text_action_list *action_list) +{ + text_action *r = action_list->head; + unsigned count = 0; + for (r = action_list->head; r != NULL; r = r->next) + { + count++; + } + return count; } @@ -4820,7 +5685,7 @@ print_action_list (FILE *fp, text_action_list *action_list) case ta_remove_longcall: t = "remove_longcall"; break; case ta_convert_longcall: - t = "remove_longcall"; break; + t = "convert_longcall"; break; case ta_narrow_insn: t = "narrow_insn"; break; case ta_widen_insn: @@ -4837,7 +5702,7 @@ print_action_list (FILE *fp, text_action_list *action_list) fprintf (fp, "%s: %s[0x%lx] \"%s\" %d\n", r->sec->owner->filename, - r->sec->name, r->offset, t, r->removed_bytes); + r->sec->name, (unsigned long) r->offset, t, r->removed_bytes); } } @@ -4995,13 +5860,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; + bfd_size_type 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); } @@ -5064,7 +5932,6 @@ struct reloc_bfd_fix_struct bfd_vma src_offset; unsigned src_type; /* Relocation type. */ - bfd *target_abfd; asection *target_sec; bfd_vma target_offset; bfd_boolean translated; @@ -5077,7 +5944,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) @@ -5088,7 +5954,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; @@ -5486,7 +6351,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; @@ -5559,7 +6424,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) @@ -5754,7 +6619,8 @@ static bfd_boolean compute_text_actions static bfd_boolean compute_ebb_proposed_actions (ebb_constraint *); static bfd_boolean compute_ebb_actions (ebb_constraint *); 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 *, 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 *); @@ -5766,7 +6632,8 @@ static bfd_boolean compute_removed_literals 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); + (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); @@ -5786,7 +6653,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 @@ -5913,6 +6780,8 @@ 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. */ @@ -5956,6 +6825,7 @@ 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); @@ -6006,13 +6876,21 @@ find_relaxable_sections (bfd *abfd, /* Count PC-relative operand relocations against the target section. 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; @@ -6290,6 +7168,24 @@ 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; +} + + /* 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 @@ -6306,6 +7202,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; @@ -6314,14 +7211,12 @@ 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; - 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; @@ -6396,7 +7291,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; @@ -6411,11 +7306,17 @@ compute_text_actions (bfd *abfd, ebb->start_reloc_idx = i; ebb->end_reloc_idx = i; + /* Precompute the opcode for each relocation. */ + if (reloc_opcodes == NULL) + reloc_opcodes = build_reloc_opcodes (abfd, sec, contents, + internal_relocs); + if (!extend_ebb_bounds (ebb) || !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, &ebb_table, + reloc_opcodes) || !check_section_ebb_reduces (&ebb_table)) { /* If anything goes wrong or we get unlucky and something does @@ -6447,11 +7348,30 @@ error_return: 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 @@ -6460,13 +7380,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; @@ -6488,15 +7419,9 @@ compute_ebb_proposed_actions (ebb_constraint *ebb_table) insn_len = insn_decode_len (ebb->contents, ebb->content_length, offset); - - /* 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; - } + goto decode_error; + if (check_branch_target_aligned_address (offset, insn_len)) align_type = EBB_REQUIRE_TGT_ALIGN; @@ -6527,12 +7452,7 @@ compute_ebb_proposed_actions (ebb_constraint *ebb_table) 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); @@ -6541,47 +7461,50 @@ compute_ebb_proposed_actions (ebb_constraint *ebb_table) 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; @@ -6595,6 +7518,12 @@ compute_ebb_proposed_actions (ebb_constraint *ebb_table) } return TRUE; + + decode_error: + (*_bfd_error_handler) + (_("%B(%A+0x%lx): could not decode instruction; possible configuration mismatch"), + ebb->sec->owner, ebb->sec, offset); + return FALSE; } @@ -6807,6 +7736,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) { @@ -6821,33 +7752,182 @@ compute_ebb_actions (ebb_constraint *ebb_table) } } - removed_bytes = 0; - for (i = 0; i < ebb_table->action_count; ++i) + removed_bytes = 0; + for (i = 0; i < ebb_table->action_count; ++i) + { + proposed_action *action = &ebb_table->actions[i]; + if (action->do_action) + removed_bytes += action->removed_bytes; + } + + if ((removed_bytes % (1 << ebb_table->ebb.sec->alignment_power)) != 0 + && ebb->ends_unreachable) + { + proposed_action *action; + int br; + int extra_space; + + BFD_ASSERT (ebb_table->action_count != 0); + action = &ebb_table->actions[ebb_table->action_count - 1]; + BFD_ASSERT (action->action == ta_fill); + BFD_ASSERT (ebb->ends_unreachable->flags & XTENSA_PROP_UNREACHABLE); + + extra_space = compute_fill_extra_space (ebb->ends_unreachable); + br = action->removed_bytes + removed_bytes + extra_space; + br = br & ((1 << ebb->sec->alignment_power ) - 1); + + action->removed_bytes = extra_space - br; + } + return TRUE; +} + + +/* 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 +{ + unsigned orig_address; + unsigned 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; + + if (map == NULL) + return offset_with_removed_text (action_list, offset); + + if (map->entry_count == 0) + return offset; + + r = bsearch (&offset, map->entry, map->entry_count, + sizeof (xlate_map_entry_t), &xlate_compare); + e = (xlate_map_entry_t *) r; + + BFD_ASSERT (e != NULL); + if (e == NULL) + return offset; + return e->new_address - e->orig_address + offset; +} + + +/* 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) +{ + xlate_map_t *map = (xlate_map_t *) bfd_malloc (sizeof (xlate_map_t)); + text_action_list *action_list = &relax_info->action_list; + unsigned num_actions = 0; + text_action *r; + int removed; + xlate_map_entry_t *current_entry; + + if (map == NULL) + return NULL; + + num_actions = action_list_count (action_list); + map->entry = (xlate_map_entry_t *) + bfd_malloc (sizeof (xlate_map_entry_t) * (num_actions + 1)); + if (map->entry == NULL) { - proposed_action *action = &ebb_table->actions[i]; - if (action->do_action) - removed_bytes += action->removed_bytes; + free (map); + return NULL; } + map->entry_count = 0; + + removed = 0; + current_entry = &map->entry[0]; - if ((removed_bytes % (1 << ebb_table->ebb.sec->alignment_power)) != 0 - && ebb->ends_unreachable) + current_entry->orig_address = 0; + current_entry->new_address = 0; + current_entry->size = 0; + + for (r = action_list->head; r != NULL; r = r->next) { - proposed_action *action; - int br; - int extra_space; + 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; + } + current_entry->size = + r->offset + orig_size - current_entry->orig_address; + if (current_entry->size != 0) + { + current_entry++; + map->entry_count++; + } + current_entry->orig_address = r->offset + orig_size; + removed += r->removed_bytes; + current_entry->new_address = r->offset + orig_size - removed; + current_entry->size = 0; + } - BFD_ASSERT (ebb_table->action_count != 0); - action = &ebb_table->actions[ebb_table->action_count - 1]; - BFD_ASSERT (action->action == ta_fill); - BFD_ASSERT (ebb->ends_unreachable->flags & XTENSA_PROP_UNREACHABLE); + current_entry->size = (bfd_get_section_limit (sec->owner, sec) + - current_entry->orig_address); + if (current_entry->size != 0) + map->entry_count++; - extra_space = compute_fill_extra_space (ebb->ends_unreachable); - br = action->removed_bytes + removed_bytes + extra_space; - br = br & ((1 << ebb->sec->alignment_power ) - 1); + return map; +} - action->removed_bytes = extra_space - br; - } - return TRUE; + +/* 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); } @@ -6860,14 +7940,24 @@ check_section_ebb_pcrels_fit (bfd *abfd, asection *sec, bfd_byte *contents, Elf_Internal_Rela *internal_relocs, - const ebb_constraint *constraint) + const ebb_constraint *constraint, + const xtensa_opcode *reloc_opcodes) { unsigned i, j; Elf_Internal_Rela *irel; + xlate_map_t *xmap = NULL; + bfd_boolean ok = TRUE; xtensa_relax_info *relax_info; relax_info = get_xtensa_relax_info (sec); + 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. */ + } + for (i = 0; i < sec->reloc_count; i++) { r_reloc r_rel; @@ -6885,7 +7975,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; @@ -6903,10 +7994,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; @@ -6940,20 +8033,35 @@ check_section_ebb_pcrels_fit (bfd *abfd, xtensa_opcode opcode; int opnum; - opcode = get_relocation_opcode (abfd, sec, contents, irel); + if (reloc_opcodes) + opcode = reloc_opcodes[i]; + else + opcode = get_relocation_opcode (abfd, sec, contents, irel); if (opcode == XTENSA_UNDEFINED) - return FALSE; + { + ok = FALSE; + break; + } opnum = get_relocation_opnd (opcode, ELF32_R_TYPE (irel->r_info)); if (opnum == XTENSA_UNDEFINED) - return FALSE; + { + 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; } @@ -7131,7 +8239,8 @@ compute_removed_literals (bfd *abfd, /* 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)) @@ -7215,12 +8324,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]; @@ -7462,6 +8581,11 @@ relocations_reach (source_reloc *reloc, != 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) { @@ -7505,7 +8629,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. */ @@ -7620,7 +8744,7 @@ move_shared_literal (asection *sec, relocs_fit = check_section_ebb_pcrels_fit (target_sec->owner, target_sec, target_sec_cache->contents, target_sec_cache->relocs, - &ebb_table); + &ebb_table, NULL); if (!relocs_fit) return FALSE; @@ -7733,6 +8857,9 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info) internal_relocs = retrieve_internal_relocs (abfd, sec, link_info->keep_memory); + if (!internal_relocs && !relax_info->action_list.head) + return TRUE; + contents = retrieve_contents (abfd, sec, link_info->keep_memory); if (contents == NULL && sec_size != 0) { @@ -7771,6 +8898,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)) @@ -7781,7 +8910,6 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info) irel->r_info = ELF32_R_INFO (0, R_XTENSA_NONE); irel->r_offset = offset_with_removed_text (&relax_info->action_list, irel->r_offset); - pin_internal_relocs (sec, internal_relocs); continue; } @@ -7826,17 +8954,62 @@ 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 @@ -7904,20 +9077,29 @@ 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); } } @@ -7929,12 +9111,11 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info) 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. */ @@ -8021,7 +9202,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; @@ -8054,7 +9235,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; @@ -8088,7 +9269,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); @@ -8129,6 +9309,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; } @@ -8250,26 +9432,22 @@ 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; + text_action *act; *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; @@ -8300,19 +9478,37 @@ 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; + act = relax_info->action_list.head; + if (base_offset <= target_offset) + { + int base_removed = removed_by_actions (&act, base_offset, FALSE); + int addend_removed = removed_by_actions (&act, target_offset, FALSE); + 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 (&act, target_offset, FALSE); + int addend_removed = removed_by_actions (&act, base_offset, FALSE); + 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; } @@ -8331,6 +9527,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; @@ -8338,6 +9535,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); @@ -8349,30 +9550,24 @@ 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)) { - 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->srelplt; is_plt = TRUE; } else - srel_name = ".rela.got"; + srel = htab->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); @@ -8391,15 +9586,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->srelgot; BFD_ASSERT (srelgot != NULL); srelgot->reloc_count -= 2; srelgot->size -= 2 * sizeof (Elf32_External_Rela); @@ -8452,12 +9647,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,7 +9661,7 @@ move_literal (bfd *abfd, /* 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, + fix = reloc_bfd_fix_init (sec, offset, r_type, r_reloc_get_section (r_rel), r_rel->target_offset + r_rel->virtual_offset, FALSE); @@ -8568,12 +9761,13 @@ 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, @@ -8585,10 +9779,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) { @@ -8617,12 +9812,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); @@ -8632,14 +9823,16 @@ 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); + text_action *act = target_relax_info->action_list.head; + new_offset = old_offset - + removed_by_actions (&act, old_offset, FALSE); /* Assert that we are not out of bounds. */ old_size = bfd_get_32 (abfd, size_p); + new_size = old_size; if (old_size == 0) { @@ -8651,39 +9844,34 @@ 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. */ + act = target_relax_info->action_list.head; + new_offset = old_offset - + removed_by_actions (&act, old_offset, TRUE); /* 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); - } - - new_size = new_end_offset - new_offset; + new_size -= + removed_by_actions (&act, old_offset + old_size, TRUE); if (new_size != old_size) { @@ -8691,9 +9879,9 @@ relax_property_section (bfd *abfd, 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,74 +9894,103 @@ relax_property_section (bfd *abfd, 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)) + || 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. + /* 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; + + BFD_ASSERT (sec->size % entry_size == 0); - for (offset = 0; offset < section_size; offset += entry_size) + 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]); @@ -8783,119 +10000,89 @@ relax_property_section (bfd *abfd, 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; } } } @@ -8942,19 +10129,14 @@ 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; + text_action *act = relax_info->action_list.head; + bfd_vma orig_addr = isym->st_value; - 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 -= removed_by_actions (&act, orig_addr, FALSE); - isym->st_value = new_address; - isym->st_size = new_size; + if (ELF32_ST_TYPE (isym->st_info) == STT_FUNC) + isym->st_size -= + removed_by_actions (&act, orig_addr + isym->st_size, FALSE); } } @@ -8972,20 +10154,15 @@ 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; + text_action *act = relax_info->action_list.head; + bfd_vma orig_addr = sym_hash->root.u.def.value; - 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_by_actions (&act, orig_addr, FALSE); - 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 (&act, orig_addr + sym_hash->size, FALSE); } } @@ -9081,26 +10258,43 @@ 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) { + struct elf_xtensa_link_hash_table *htab; + bfd *dynobj; char plt_name[10]; if (chunk == 0) - return bfd_get_section_by_name (dynobj, ".plt"); + { + htab = elf_xtensa_hash_table (info); + if (htab == NULL) + return NULL; + return htab->splt; + } + + dynobj = elf_hash_table (info)->dynobj; sprintf (plt_name, ".plt.%u", chunk); return bfd_get_section_by_name (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) { + struct elf_xtensa_link_hash_table *htab; + bfd *dynobj; char got_name[14]; if (chunk == 0) - return bfd_get_section_by_name (dynobj, ".got.plt"); + { + htab = elf_xtensa_hash_table (info); + if (htab == NULL) + return NULL; + return htab->sgotplt; + } + dynobj = elf_hash_table (info)->dynobj; sprintf (got_name, ".got.plt.%u", chunk); return bfd_get_section_by_name (dynobj, got_name); } @@ -9129,15 +10323,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 { @@ -9248,24 +10439,23 @@ 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 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; @@ -9275,12 +10465,19 @@ xtensa_is_property_section (asection *sec) 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; @@ -9322,13 +10519,43 @@ 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 int linkonce_len = sizeof (".gnu.linkonce.") - 1; + +static char * +xtensa_property_section_name (asection *sec, const char *base_name) { - 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 = (char *) bfd_malloc (strlen (base_name) + 1 + + (suffix ? strlen (suffix) : 0)); + strcpy (prop_sec_name, base_name); + if (suffix) + strcat (prop_sec_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) @@ -9348,30 +10575,74 @@ 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] == '.') + if (CONST_STRNEQ (suffix, "t.") && linkonce_kind[1] == '.') suffix += 2; strcat (prop_sec_name + linkonce_len, suffix); + } + else + prop_sec_name = strdup (base_name); + + return prop_sec_name; +} + + +static asection * +xtensa_get_property_section (asection *sec, const char *base_name) +{ + char *prop_sec_name; + asection *prop_sec; + + prop_sec_name = xtensa_property_section_name (sec, base_name); + 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; +} + - return prop_sec_name; +asection * +xtensa_make_property_section (asection *sec, const char *base_name) +{ + char *prop_sec_name; + asection *prop_sec; + + /* Check if the section already exists. */ + prop_sec_name = xtensa_property_section_name (sec, base_name); + 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_get_section_flags (sec->owner, 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; @@ -9398,7 +10669,7 @@ 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; @@ -9424,6 +10695,11 @@ xtensa_callback_required_dependence (bfd *abfd, (*callback) (sec, sec_size, sgotplt, 0, closure); } + /* 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 @@ -9475,12 +10751,14 @@ 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_NAME "elf32-xtensa-le" @@ -9507,12 +10785,17 @@ static const struct bfd_elf_special_section elf_xtensa_special_sections[] = #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 @@ -9527,11 +10810,15 @@ static const struct bfd_elf_special_section elf_xtensa_special_sections[] = #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_boolean (*) (bfd *, struct bfd_link_info *, asection *)) bfd_true) #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"