From d6c78d3df1be18c8ee46acd086e671147e501731 Mon Sep 17 00:00:00 2001 From: Nelson Chu Date: Thu, 23 Aug 2018 15:28:25 +0800 Subject: [PATCH 13/13] NDS32: Support TLS LE/IE/IEGP/DESC (GD + LD). This patch support full TLS models for nds32. 1. Instruction scheduling will discretize the TLS patterns, therefore, we have to mark the same patterns by R_NDS32_GROUP relocations in the assembler, and gather them together in the linker afterwards. 2. We have to translate the TLS models to the suitable one if the linked objects are with different TLS models (e.g., translate GD and LD to IEGP/IE/LE, IEGP to IE/LE, IE ot LE). 3. Traditionally, we use __tls_get_addr to get address to the TLS symbol under GD and LD scenarios, but __tls_get_addr is inefficient. Therefore, we support TLS descriptor for improving the performance on accessing TLS symbol in static TLS block. We call two resolved functions to get address to the TLS symbol rather than always call __tls_get_addr. One function is used for static TLS block (count TP + offset directly), the other is used for dynamic TLS block (same as traditional __tls_get_addr). bfd/ * reloc.c: Add TLS relocations. * libbfd.h: Regenerated. * bfd-in2.h: Regenerated. * elf32-nds32.h (struct section_id_list_t): New. (elf32_nds32_lookup_section_id, elf32_nds32_check_relax_group, elf32_nds32_unify_relax_group, nds32_elf_unify_tls_model): New prototypes. (elf32_nds32_compute_jump_table_size, elf32_nds32_local_tlsdesc_gotent): New macro. (nds32_insertion_sort, bfd_elf32_nds32_set_target_option, elf_nds32_link_hash_table): Updated. * elf32-nds32.c (enum elf_nds32_tls_type): New. (struct elf32_nds32_relax_group_t, struct relax_group_list_t): New. (elf32_nds32_add_dynreloc, patch_tls_desc_to_ie, get_tls_type, fls, ones32, list_insert, list_insert_sibling, dump_chain, elf32_nds32_check_relax_group, elf32_nds32_lookup_section_id, elf32_nds32_unify_relax_group, nds32_elf_unify_tls_model): New functions. (elf_nds32_obj_tdata): Add new fields. (elf32_nds32_relax_group_ptr, nds32_elf_local_tlsdesc_gotent): New macros. (nds32_elf_howto_table): Add TLS relocations. (nds32_reloc_map): Likewise. (nds32_elf_copy_indirect_symbol, nds32_elf_size_dynamic_sections, nds32_elf_finish_dynamic_symbol, elf32_nds32_allocate_local_sym_info, nds32_elf_relocate_section, bfd_elf32_nds32_set_target_option, nds32_elf_check_relocs, allocate_dynrelocs): Updated. (nds32_elf_relax_section): Call nds32_elf_unify_tls_model. (dtpoff_base): Rename it to `gottpof' and then update it. binutils/ * testsuite/binutils-all/objcopy.exp: Set the unsupported reloc number from 215 to 255 for NDS32. gas/ * configure.ac: Define NDS32_LINUX_TOOLCHAIN. * configure: Regenerated. * config.in: Regenerated. * config/tc-nds32.h (enum nds32_ramp): Updated. (enum nds32_relax_hint_type): Likewise. * config/tc-nds32.c: Include "errno.h" and "limits.h". (relax_ls_table): Add TLS relax patterns. (nds32_elf_append_relax_relocs): Attach BFD_RELOC_NDS32_GROUP on each instructions of TLS patterns. (nds32_elf_record_fixup_exp): Updated. (nds32_apply_fix): Likewise. (suffix_table): Add TLSDESC suffix. include/ * elf/nds32.h: Add new relocations for TLS. ld/ * emultempl/nds32elf.em (tls_desc_trampoline): New variable. * (nds32_elf_create_output_section_statements): Updated. * (nds32_elf_after_parse): Disable relaxations when PIC is enable. * (PARSE_AND_LIST_PROLOGUE, PARSE_AND_LIST_OPTIONS, PARSE_AND_LIST_ARGS_CASES): Add new option --m[no-]tlsdesc-trampoline. --- bfd/bfd-in2.h | 27 +- bfd/elf32-nds32.c | 1729 +++++++++++++++++-- bfd/elf32-nds32.h | 49 +- bfd/libbfd.h | 27 +- bfd/reloc.c | 44 +- binutils/testsuite/binutils-all/objcopy.exp | 6 +- gas/config.in | 3 + gas/config/tc-nds32.c | 280 ++- gas/config/tc-nds32.h | 12 +- gas/configure | 11 + gas/configure.ac | 9 + include/elf/nds32.h | 22 +- ld/emultempl/nds32elf.em | 17 +- 13 files changed, 2106 insertions(+), 130 deletions(-) diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h index b5ca04c149..59819224a6 100644 --- a/bfd/bfd-in2.h +++ b/bfd/bfd-in2.h @@ -4275,18 +4275,33 @@ This is a 5 bit absolute address. */ /* For TLS. */ BFD_RELOC_NDS32_TPOFF, + BFD_RELOC_NDS32_GOTTPOFF, BFD_RELOC_NDS32_TLS_LE_HI20, BFD_RELOC_NDS32_TLS_LE_LO12, - BFD_RELOC_NDS32_TLS_LE_ADD, - BFD_RELOC_NDS32_TLS_LE_LS, - BFD_RELOC_NDS32_GOTTPOFF, - BFD_RELOC_NDS32_TLS_IE_HI20, - BFD_RELOC_NDS32_TLS_IE_LO12S2, - BFD_RELOC_NDS32_TLS_TPOFF, BFD_RELOC_NDS32_TLS_LE_20, BFD_RELOC_NDS32_TLS_LE_15S0, BFD_RELOC_NDS32_TLS_LE_15S1, BFD_RELOC_NDS32_TLS_LE_15S2, + BFD_RELOC_NDS32_TLS_LE_ADD, + BFD_RELOC_NDS32_TLS_LE_LS, + BFD_RELOC_NDS32_TLS_IE_HI20, + BFD_RELOC_NDS32_TLS_IE_LO12, + BFD_RELOC_NDS32_TLS_IE_LO12S2, + BFD_RELOC_NDS32_TLS_IEGP_HI20, + BFD_RELOC_NDS32_TLS_IEGP_LO12, + BFD_RELOC_NDS32_TLS_IEGP_LO12S2, + BFD_RELOC_NDS32_TLS_IEGP_LW, + BFD_RELOC_NDS32_TLS_DESC, + BFD_RELOC_NDS32_TLS_DESC_HI20, + BFD_RELOC_NDS32_TLS_DESC_LO12, + BFD_RELOC_NDS32_TLS_DESC_20, + BFD_RELOC_NDS32_TLS_DESC_SDA17S2, + BFD_RELOC_NDS32_TLS_DESC_ADD, + BFD_RELOC_NDS32_TLS_DESC_FUNC, + BFD_RELOC_NDS32_TLS_DESC_CALL, + BFD_RELOC_NDS32_TLS_DESC_MEM, + BFD_RELOC_NDS32_REMOVE, + BFD_RELOC_NDS32_GROUP, /* For floating load store relaxation. */ BFD_RELOC_NDS32_LSI, diff --git a/bfd/elf32-nds32.c b/bfd/elf32-nds32.c index 6c41fae5c9..5347004cb4 100644 --- a/bfd/elf32-nds32.c +++ b/bfd/elf32-nds32.c @@ -180,6 +180,18 @@ struct elf_nds32_pcrel_relocs_copied bfd_size_type count; }; +enum elf_nds32_tls_type +{ + GOT_UNKNOWN = (0), + GOT_NORMAL = (1 << 0), + GOT_TLS_LE = (1 << 1), + GOT_TLS_IE = (1 << 2), + GOT_TLS_IEGP = (1 << 3), + GOT_TLS_LD = (1 << 4), + GOT_TLS_GD = (1 << 5), + GOT_TLS_DESC = (1 << 6), +}; + /* Nds32 ELF linker hash entry. */ struct elf_nds32_link_hash_entry @@ -190,10 +202,7 @@ struct elf_nds32_link_hash_entry struct elf_dyn_relocs *dyn_relocs; /* For checking relocation type. */ -#define GOT_UNKNOWN 0 -#define GOT_NORMAL 1 -#define GOT_TLS_IE 2 - unsigned int tls_type; + enum elf_nds32_tls_type tls_type; int offset_to_gp; }; @@ -206,6 +215,15 @@ static int check_start_export_sym = 0; /* The offset for executable tls relaxation. */ #define TP_OFFSET 0x0 +typedef struct +{ + int min_id; + int max_id; + int count; + int bias; + int init; +} elf32_nds32_relax_group_t; + struct elf_nds32_obj_tdata { struct elf_obj_tdata root; @@ -213,6 +231,12 @@ struct elf_nds32_obj_tdata /* tls_type for each local got entry. */ char *local_got_tls_type; + /* GOTPLT entries for TLS descriptors. */ + bfd_vma *local_tlsdesc_gotent; + + /* for R_NDS32_RELAX_GROUP handling. */ + elf32_nds32_relax_group_t relax_group; + unsigned int hdr_size; int* offset_to_gp; }; @@ -228,6 +252,9 @@ struct elf_nds32_obj_tdata #define elf32_nds32_hash_entry(ent) ((struct elf_nds32_link_hash_entry *)(ent)) +#define elf32_nds32_relax_group_ptr(bfd) \ + &(elf_nds32_tdata (bfd)->relax_group) + static bfd_boolean nds32_elf_mkobject (bfd *abfd) { @@ -1927,6 +1954,139 @@ static reloc_howto_type nds32_elf_howto_table[] = { 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ + + HOWTO2 (R_NDS32_TLS_IE_LO12, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 12, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_TLS_IE_LO12", /* name */ + FALSE, /* partial_inplace */ + 0x00000fff, /* src_mask */ + 0x00000fff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* Like R_NDS32_HI20, but referring to the TLS IE (PIE) + entry for the symbol. */ + HOWTO2 (R_NDS32_TLS_IEGP_HI20,/* type */ + 12, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 20, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_TLS_IEGP_HI20",/* name */ + FALSE, /* partial_inplace */ + 0x000fffff, /* src_mask */ + 0x000fffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + HOWTO2 (R_NDS32_TLS_IEGP_LO12,/* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 12, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_TLS_IEGP_LO12",/* name */ + FALSE, /* partial_inplace */ + 0x00000fff, /* src_mask */ + 0x00000fff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + HOWTO2 (R_NDS32_TLS_IEGP_LO12S2,/* type */ + 2, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 10, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_TLS_IEGP_LO12S2",/* name */ + FALSE, /* partial_inplace */ + 0x000003ff, /* src_mask */ + 0x000003ff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* TLS description relocation */ + HOWTO2 (R_NDS32_TLS_DESC, /* type */ + 12, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 20, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + nds32_elf_hi20_reloc, /* special_function */ + "R_NDS32_TLS_DESC_HI20",/* name */ + FALSE, /* partial_inplace */ + 0x000fffff, /* src_mask */ + 0x000fffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* TLS GD/LD description offset high part. */ + HOWTO2 (R_NDS32_TLS_DESC_HI20,/* type */ + 12, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 20, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + nds32_elf_hi20_reloc, /* special_function */ + "R_NDS32_TLS_DESC_HI20",/* name */ + FALSE, /* partial_inplace */ + 0x000fffff, /* src_mask */ + 0x000fffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* TLS GD/LD description offset low part. */ + HOWTO2 (R_NDS32_TLS_DESC_LO12,/* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 12, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + nds32_elf_lo12_reloc, /* special_function */ + "R_NDS32_TLS_DESC_LO12",/* name */ + FALSE, /* partial_inplace */ + 0x00000fff, /* src_mask */ + 0x00000fff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* TLS GD/LD description offset set (movi). */ + HOWTO2 (R_NDS32_TLS_DESC_20, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 20, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_TLS_DESC_20", /* name */ + FALSE, /* partial_inplace */ + 0x000fffff, /* src_mask */ + 0x000fffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* TLS GD/LD description offset set (lwi.gp). */ + HOWTO2 (R_NDS32_TLS_DESC_SDA17S2,/* type */ + 2, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 17, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_TLS_DESC_SDA17S2",/* name */ + FALSE, /* partial_inplace */ + 0x0001ffff, /* src_mask */ + 0x0001ffff, /* dst_mask */ + FALSE), /* pcrel_offset */ }; /* Relocations used for relaxation. */ @@ -2220,7 +2380,103 @@ static reloc_howto_type nds32_elf_relax_howto_table[] = { 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ - + /* TLS GD/LD description address base addition. */ + HOWTO3 (R_NDS32_TLS_DESC_ADD, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + nds32_elf_ignore_reloc,/* special_function */ + "R_NDS32_TLS_DESC_ADD",/* name */ + FALSE, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + /* TLS GD/LD description function load. */ + HOWTO3 (R_NDS32_TLS_DESC_FUNC,/* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + nds32_elf_ignore_reloc,/* special_function */ + "R_NDS32_TLS_DESC_FUNC",/* name */ + FALSE, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + /* TLS DESC resolve function call. */ + HOWTO3 (R_NDS32_TLS_DESC_CALL,/* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + nds32_elf_ignore_reloc,/* special_function */ + "R_NDS32_TLS_DESC_CALL",/* name */ + FALSE, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + /* TLS DESC variable access. */ + HOWTO3 (R_NDS32_TLS_DESC_MEM, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + nds32_elf_ignore_reloc,/* special_function */ + "R_NDS32_TLS_DESC_MEM",/* name */ + FALSE, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + /* TLS GD/LD description mark (@tlsdec). */ + HOWTO3 (R_NDS32_RELAX_REMOVE, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + nds32_elf_ignore_reloc,/* special_function */ + "R_NDS32_REMOVE", /* name */ + FALSE, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + /* TLS GD/LD description mark (@tlsdec). */ + HOWTO3 (R_NDS32_RELAX_GROUP, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + nds32_elf_ignore_reloc,/* special_function */ + "R_NDS32_GROUP", /* name */ + FALSE, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + HOWTO3 (R_NDS32_TLS_IEGP_LW, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + nds32_elf_ignore_reloc,/* special_function */ + "R_NDS32_TLS_IEGP_LW", /* name */ + FALSE, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE), /* pcrel_offset */ /* LA and FLSI relaxation. */ HOWTO3 (R_NDS32_LSI, /* type */ 0, /* rightshift */ @@ -2238,6 +2494,31 @@ static reloc_howto_type nds32_elf_relax_howto_table[] = { }; +static unsigned long dl_tlsdesc_lazy_trampoline[] = +{ + 0x46200000, /* sethi $r2,#0x0 */ + 0x58210000, /* ori $r2,$r2,#0x0 */ + 0x40217400, /* add $r2,$r2,$gp */ + 0x04210000, /* lwi $r2,[$r2+#0x0] */ + 0x46300000, /* sethi $r3,#0x0 */ + 0x58318000, /* ori $r3,$r3,#0x0 */ + 0x4031f400, /* add $r3,$r3,$gp */ + 0x4a000800, /* jr $r2 */ +}; + +static void +nds32_put_trampoline (void *contents, const unsigned long *template, + unsigned count) +{ + unsigned ix; + + for (ix = 0; ix != count; ix++) + { + unsigned long insn = template[ix]; + bfd_putb32 (insn, (char *) contents + ix * 4); + } +} + /* nds32_insertion_sort sorts an array with nmemb elements of size size. This prototype is the same as qsort (). */ @@ -2838,17 +3119,35 @@ static const struct nds32_reloc_map_entry nds32_reloc_map[] = {BFD_RELOC_NDS32_TRAN, R_NDS32_TRAN}, {BFD_RELOC_NDS32_17IFC_PCREL, R_NDS32_17IFC_PCREL_RELA}, {BFD_RELOC_NDS32_10IFCU_PCREL, R_NDS32_10IFCU_PCREL_RELA}, + /* Not sure */ + {BFD_RELOC_NDS32_TPOFF, R_NDS32_TLS_TPOFF}, + /* Missing: BFD_RELOC_NDS32_GOTTPOFF */ {BFD_RELOC_NDS32_TLS_LE_HI20, R_NDS32_TLS_LE_HI20}, {BFD_RELOC_NDS32_TLS_LE_LO12, R_NDS32_TLS_LE_LO12}, - {BFD_RELOC_NDS32_TLS_LE_ADD, R_NDS32_TLS_LE_ADD}, - {BFD_RELOC_NDS32_TLS_LE_LS, R_NDS32_TLS_LE_LS}, - {BFD_RELOC_NDS32_TLS_IE_HI20, R_NDS32_TLS_IE_HI20}, - {BFD_RELOC_NDS32_TLS_IE_LO12S2, R_NDS32_TLS_IE_LO12S2}, - {BFD_RELOC_NDS32_TLS_TPOFF, R_NDS32_TLS_TPOFF}, {BFD_RELOC_NDS32_TLS_LE_20, R_NDS32_TLS_LE_20}, {BFD_RELOC_NDS32_TLS_LE_15S0, R_NDS32_TLS_LE_15S0}, {BFD_RELOC_NDS32_TLS_LE_15S1, R_NDS32_TLS_LE_15S1}, {BFD_RELOC_NDS32_TLS_LE_15S2, R_NDS32_TLS_LE_15S2}, + {BFD_RELOC_NDS32_TLS_LE_ADD, R_NDS32_TLS_LE_ADD}, + {BFD_RELOC_NDS32_TLS_LE_LS, R_NDS32_TLS_LE_LS}, + {BFD_RELOC_NDS32_TLS_IE_HI20, R_NDS32_TLS_IE_HI20}, + {BFD_RELOC_NDS32_TLS_IE_LO12, R_NDS32_TLS_IE_LO12}, + {BFD_RELOC_NDS32_TLS_IE_LO12S2, R_NDS32_TLS_IE_LO12S2}, + {BFD_RELOC_NDS32_TLS_IEGP_HI20, R_NDS32_TLS_IEGP_HI20}, + {BFD_RELOC_NDS32_TLS_IEGP_LO12, R_NDS32_TLS_IEGP_LO12}, + {BFD_RELOC_NDS32_TLS_IEGP_LO12S2, R_NDS32_TLS_IEGP_LO12S2}, + {BFD_RELOC_NDS32_TLS_IEGP_LW, R_NDS32_TLS_IEGP_LW}, + {BFD_RELOC_NDS32_TLS_DESC, R_NDS32_TLS_DESC}, + {BFD_RELOC_NDS32_TLS_DESC_HI20, R_NDS32_TLS_DESC_HI20}, + {BFD_RELOC_NDS32_TLS_DESC_LO12, R_NDS32_TLS_DESC_LO12}, + {BFD_RELOC_NDS32_TLS_DESC_20, R_NDS32_TLS_DESC_20}, + {BFD_RELOC_NDS32_TLS_DESC_SDA17S2, R_NDS32_TLS_DESC_SDA17S2}, + {BFD_RELOC_NDS32_TLS_DESC_ADD, R_NDS32_TLS_DESC_ADD}, + {BFD_RELOC_NDS32_TLS_DESC_FUNC, R_NDS32_TLS_DESC_FUNC}, + {BFD_RELOC_NDS32_TLS_DESC_CALL, R_NDS32_TLS_DESC_CALL}, + {BFD_RELOC_NDS32_TLS_DESC_MEM, R_NDS32_TLS_DESC_MEM}, + {BFD_RELOC_NDS32_REMOVE, R_NDS32_RELAX_REMOVE}, + {BFD_RELOC_NDS32_GROUP, R_NDS32_RELAX_GROUP}, {BFD_RELOC_NDS32_LSI, R_NDS32_LSI}, }; @@ -3612,9 +3911,19 @@ nds32_elf_copy_indirect_symbol (struct bfd_link_info *info, eind->dyn_relocs = NULL; } + if (ind->root.type == bfd_link_hash_indirect) + { + 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); } + /* Find dynamic relocs for H that apply to read-only sections. */ static asection * @@ -3849,6 +4158,8 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) /* We also need to make an entry in the .rel.plt section. */ ehtab->srelplt->size += sizeof (Elf32_External_Rela); + if (htab->tls_desc_trampoline) + htab->next_tls_desc_index++; } else { @@ -3881,17 +4192,39 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) if (tls_type == GOT_UNKNOWN) abort (); - else if (tls_type == GOT_NORMAL - || tls_type == GOT_TLS_IE) - /* Need a GOT slot. */ + + /* Non-TLS symbols, and TLS_IE need one GOT slot. */ + if (tls_type & (GOT_NORMAL | GOT_TLS_IE | GOT_TLS_IEGP)) sgot->size += 4; + else + { + /* TLS_DESC, TLS_GD, and TLS_LD need 2 consecutive GOT slots. */ + if (tls_type & GOT_TLS_DESC) + sgot->size += 8; + } dyn = htab->root.dynamic_sections_created; + if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, bfd_link_pic (info), h)) - htab->root.srelgot->size += sizeof (Elf32_External_Rela); + { + if (tls_type == GOT_TLS_DESC && htab->tls_desc_trampoline) + { + /* TLS_DESC with trampoline needs a relocation slot + within .rela.plt. */ + htab->num_tls_desc++; + ehtab->srelplt->size += sizeof (Elf32_External_Rela); + htab->tls_trampoline = -1; + } + else + { + /* other relocations, including TLS_DESC without trampoline, need + a relocation slot within .rela.got. */ + ehtab->srelgot->size += sizeof (Elf32_External_Rela); + } + } } else - h->got.offset = (bfd_vma) -1; + h->got.offset = (bfd_vma)-1; if (eh->dyn_relocs == NULL) return TRUE; @@ -3960,6 +4293,25 @@ keep:; return TRUE; } +/* Add relocation REL to the end of relocation section SRELOC. */ + +static void +elf32_nds32_add_dynreloc (bfd *output_bfd, + struct bfd_link_info *info ATTRIBUTE_UNUSED, + asection *sreloc, Elf_Internal_Rela *rel) +{ + bfd_byte *loc; + if (sreloc == NULL) + abort (); + + loc = sreloc->contents; + loc += sreloc->reloc_count++ * sizeof (Elf32_External_Rela); + if (sreloc->reloc_count * sizeof (Elf32_External_Rela) > sreloc->size) + abort (); + + bfd_elf32_swap_reloca_out (output_bfd, rel, loc); +} + /* Set DF_TEXTREL if we find any dynamic relocs that apply to read-only sections. */ @@ -3997,13 +4349,14 @@ nds32_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, bfd *dynobj; asection *s; bfd_boolean relocs; + bfd_boolean plt; bfd *ibfd; htab = nds32_elf_hash_table (info); if (htab == NULL) return FALSE; - dynobj = htab->root.dynobj; + dynobj = elf_hash_table (info)->dynobj; BFD_ASSERT (dynobj != NULL); if (elf_hash_table (info)->dynamic_sections_created) @@ -4027,6 +4380,9 @@ nds32_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, bfd_size_type locsymcount; Elf_Internal_Shdr *symtab_hdr; asection *sgot; + char *local_tls_type; + unsigned long symndx; + bfd_vma *local_tlsdesc_gotent; if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour) continue; @@ -4065,29 +4421,93 @@ nds32_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, locsymcount = symtab_hdr->sh_info; end_local_got = local_got + locsymcount; sgot = elf_hash_table (info)->sgot; - for (; local_got < end_local_got; ++local_got) + local_tls_type = elf32_nds32_local_got_tls_type (ibfd); + local_tlsdesc_gotent = elf32_nds32_local_tlsdesc_gotent (ibfd); + for (symndx = 0; local_got < end_local_got; + ++local_got, ++local_tls_type, ++local_tlsdesc_gotent, ++symndx) { if (*local_got > 0) { + int num_of_got_entry_needed = 0; *local_got = sgot->size; - sgot->size += 4; - if (bfd_link_pic (info)) - htab->root.srelgot->size += sizeof (Elf32_External_Rela); - } - else - *local_got = (bfd_vma) - 1; - } - } + *local_tlsdesc_gotent = sgot->size; - /* Allocate global sym .plt and .got entries, and space for global - sym dynamic relocs. */ - elf_link_hash_traverse (&htab->root, allocate_dynrelocs, (void *) info); + /* TLS_NORMAL, and TLS_IE need one slot in .got. */ + if (*local_tls_type & (GOT_NORMAL | GOT_TLS_IE | GOT_TLS_IEGP)) + num_of_got_entry_needed = 1; + /* TLS_GD, TLS_LD, and TLS_DESC need an 8-byte structure in the GOT. */ + else if (*local_tls_type & GOT_TLS_DESC) + num_of_got_entry_needed = 2; - /* We now have determined the sizes of the various dynamic sections. - Allocate memory for them. */ - /* The check_relocs and adjust_dynamic_symbol entry points have - determined the sizes of the various dynamic sections. Allocate - memory for them. */ + sgot->size += (num_of_got_entry_needed << 2); + + /* non-relax-able TLS_DESCs need a slot in .rela.plt. + others need a slot in .rela.got. */ + if (*local_tls_type == GOT_TLS_DESC) + { + if (bfd_link_pic (info)) + { + if (htab->tls_desc_trampoline) + { + htab->num_tls_desc++; + htab->root.srelplt->size += sizeof (Elf32_External_Rela); + htab->tls_trampoline = -1; + } + else + htab->root.srelgot->size += sizeof (Elf32_External_Rela); + } + else + { + /* TLS_DESC -> TLS_LE */ + } + } + else + { + htab->root.srelgot->size += sizeof (Elf32_External_Rela); + } + } + else + { + *local_got = (bfd_vma) -1; + *local_tlsdesc_gotent = (bfd_vma) -1; + } + } + } + + /* Allocate global sym .plt and .got entries, and space for global + sym dynamic relocs. */ + elf_link_hash_traverse (&htab->root, allocate_dynrelocs, (void *) info); + + /* For every jump slot reserved in the sgotplt, reloc_count is + incremented. However, when we reserve space for TLS descriptors, + it's not incremented, so in order to compute the space reserved + for them, it suffices to multiply the reloc count by the jump + slot size. */ + if (htab->tls_desc_trampoline && htab->root.srelplt) + htab->sgotplt_jump_table_size = elf32_nds32_compute_jump_table_size (htab); + + if (htab->tls_trampoline) + { + htab->tls_trampoline = htab->root.splt->size; + + /* If we're not using lazy TLS relocations, don't generate the + PLT and GOT entries they require. */ + if (!(info->flags & DF_BIND_NOW)) + { + htab->dt_tlsdesc_got = htab->root.sgot->size; + htab->root.sgot->size += 4; + + htab->dt_tlsdesc_plt = htab->root.splt->size; + htab->root.splt->size += 4 * ARRAY_SIZE (dl_tlsdesc_lazy_trampoline); + } + } + + /* We now have determined the sizes of the various dynamic sections. + Allocate memory for them. */ + /* The check_relocs and adjust_dynamic_symbol entry points have + determined the sizes of the various dynamic sections. Allocate + memory for them. */ + plt = FALSE; relocs = FALSE; for (s = dynobj->sections; s != NULL; s = s->next) { @@ -4098,6 +4518,7 @@ nds32_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, { /* Strip this section if we don't need it; see the comment below. */ + plt = s->size != 0; } else if (s == elf_hash_table (info)->sgot) { @@ -4173,6 +4594,14 @@ nds32_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, return FALSE; } + if (htab->tls_desc_trampoline && plt) + { + if (htab->dt_tlsdesc_plt + && (!add_dynamic_entry (DT_TLSDESC_PLT, 0) + || !add_dynamic_entry (DT_TLSDESC_GOT, 0))) + return FALSE; + } + if (relocs) { if (!add_dynamic_entry (DT_RELA, 0) @@ -4477,15 +4906,180 @@ nds32_elf_output_symbol_hook (struct bfd_link_info *info, section, which means that the addend must be adjusted accordingly. */ +/* Return the base VMA address which should be subtracted from real addresses + when resolving @dtpoff relocation. + This is PT_TLS segment p_vaddr. */ + +/* Return the relocation value for @tpoff relocation + if STT_TLS virtual address is ADDRESS. */ + +/* Return the relocation value for @gottpoff relocation + if STT_TLS virtual address is ADDRESS. */ + static bfd_vma -dtpoff_base (struct bfd_link_info *info) +gottpoff (struct bfd_link_info *info, bfd_vma address) { + bfd_vma tp_base; + bfd_vma tp_offset; + /* If tls_sec is NULL, we should have signalled an error already. */ if (elf_hash_table (info)->tls_sec == NULL) return 0; - return elf_hash_table (info)->tls_sec->vma; + + tp_base = elf_hash_table (info)->tls_sec->vma; + tp_offset = address - tp_base; + + return tp_offset; +} + +static bfd_boolean +patch_tls_desc_to_ie (bfd_byte *contents, Elf_Internal_Rela *rel, bfd *ibfd) +{ + /* TLS_GD/TLS_LD model #1 + 46 00 00 00 sethi $r0,#0x0 + 58 00 00 00 ori $r0,$r0,#0x0 + 40 00 74 00 add $r0,$r0,$gp + 04 10 00 00 lwi $r1,[$r0+#0x0] + 4b e0 04 01 jral $lp,$r1 */ + + /* TLS_GD/TLS_LD model #2 + 46 00 00 00 sethi $r0,#0x0 + 58 00 00 00 ori $r0,$r0,#0x0 + 38 10 74 02 lw $r1,[$r0+($gp<<#0x0)] + 40 00 74 00 add $r0,$r0,$gp + 4b e0 04 01 jral $lp,$r1 */ + + /* TLS_IE model (non-PIC) + 46 00 00 00 sethi $r0,#0x0 + 04 00 00 00 lwi $r0,[$r0+#0x0] + 38 00 64 02 lw $r0,[$r0+($r25<<#0x0)] */ + + /* TLS_IE model (PIC) + 46 00 00 00 sethi $r0,#0x0 + 58 00 00 00 ori $r0,$r0,#0x0 + 38 00 74 02 lw $r0,[$r0+($gp<<#0x0)] + 38 00 64 02 lw $r0,[$r0+($r25<<#0x0)] */ + + /* TLS_GD_TO_IE model + 46 00 00 00 sethi $r0,#0x0 + 58 00 00 00 ori $r0,$r0,#0x0 + 40 00 74 00 add $r0,$rM,$gp + 04 00 00 01 lwi $r0,[$r0+#0x4] + 40 00 64 00 add $r0,$r0,$r25 */ + + bfd_boolean rz = FALSE; + + typedef struct + { + uint32_t opcode; + uint32_t mask; + } pat_t; + + uint32_t patch[3] = + { + 0x40007400, /* add $r0,$rM,$gp */ + 0x04000001, /* lwi $r0,[$r0+#0x4] */ + 0x40006400, /* add $r0,$r0,$r25 */ + }; + + pat_t mode0[3] = + { + { 0x40000000, 0xfe0003ff }, + { 0x04000000, 0xfe000000 }, + { 0x4be00001, 0xffff83ff }, + }; + + pat_t mode1[3] = + { + { 0x38007402, 0xfe007fff }, + { 0x40007400, 0xfe007fff }, + { 0x4be00001, 0xffff83ff }, + }; + + unsigned char *p = contents + rel->r_offset; + + uint32_t insn; + uint32_t regidx = 0; + insn = bfd_getb32 (p); + if (INSN_SETHI == (0xfe0fffffu & insn)) + { + regidx = 0x1f & (insn >> 20); + p += 4; + } + + insn = bfd_getb32 (p); + if (INSN_ORI == (0xfe007fffu & insn)) + { + regidx = 0x1f & (insn >> 20); + p += 4; + } + + if (patch[2] == bfd_getb32 (p + 8)) /* character instruction */ + { + /* already patched? */ + if ((patch[0] == (0xfff07fffu & bfd_getb32 (p + 0))) && + (patch[1] == bfd_getb32 (p + 4))) + rz = TRUE; + } + else if (mode0[0].opcode == (mode0[0].mask & bfd_getb32 (p + 0))) + { + if ((mode0[1].opcode == (mode0[1].mask & bfd_getb32 (p + 4))) && + (mode0[2].opcode == (mode0[2].mask & bfd_getb32 (p + 8)))) + { + bfd_putb32 (patch[0] | (regidx << 15), p + 0); + bfd_putb32 (patch[1], p + 4); + bfd_putb32 (patch[2], p + 8); + rz = TRUE; + } + } + else if (mode1[0].opcode == (mode1[0].mask & bfd_getb32 (p + 0))) + { + if ((mode1[1].opcode == (mode1[1].mask & bfd_getb32 (p + 4))) && + (mode1[2].opcode == (mode1[2].mask & bfd_getb32 (p + 8)))) + { + bfd_putb32 (patch[0] | (regidx << 15), p + 0); + bfd_putb32 (patch[1], p + 4); + bfd_putb32 (patch[2], p + 8); + rz = TRUE; + } + } + + if (!rz) + { + printf ("%s: %s @ 0x%08x\n", __func__, ibfd->filename, + (int) rel->r_offset); + BFD_ASSERT(0); /* unsupported pattern */ + } + + return rz; } +static enum elf_nds32_tls_type +get_tls_type (enum elf_nds32_reloc_type r_type, struct elf_link_hash_entry *h); + +static unsigned int +ones32 (register unsigned int x) +{ + /* 32-bit recursive reduction using SWAR... + but first step is mapping 2-bit values + into sum of 2 1-bit values in sneaky way. */ + x -= ((x >> 1) & 0x55555555); + x = (((x >> 2) & 0x33333333) + (x & 0x33333333)); + x = (((x >> 4) + x) & 0x0f0f0f0f); + x += (x >> 8); + x += (x >> 16); + return (x & 0x0000003f); +} + +static unsigned int +fls (register unsigned int x) +{ + return ffs (x & (-x)); +} + +#define nds32_elf_local_tlsdesc_gotent(bfd) \ + (elf_nds32_tdata (bfd)->local_tlsdesc_gotent) + static bfd_boolean nds32_elf_relocate_section (bfd * output_bfd ATTRIBUTE_UNUSED, struct bfd_link_info * info, @@ -4543,6 +5137,9 @@ nds32_elf_relocate_section (bfd * output_bfd ATTRIBUTE_UNUSED, return FALSE; } + /* Do TLS model conversion once at first. */ + nds32_elf_unify_tls_model (input_bfd, input_section, contents, info); + /* Use gp as fp to prevent truncated fit. Because in relaxation time the fp value is set as gp, and it has be reverted for instruction setting fp. */ @@ -4558,6 +5155,7 @@ nds32_elf_relocate_section (bfd * output_bfd ATTRIBUTE_UNUSED, Elf_Internal_Sym *sym = NULL; asection *sec; bfd_vma relocation; + bfd_vma relocation_sym = 0xdeadbeef; Elf_Internal_Rela *lorel; bfd_vma off; @@ -4634,6 +5232,8 @@ nds32_elf_relocate_section (bfd * output_bfd ATTRIBUTE_UNUSED, relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel); addend = rel->r_addend; + /* keep symbol location for static TLS_IE GOT entry */ + relocation_sym = relocation; if (bfd_link_relocatable (info)) { /* This is a relocatable link. We don't have to change @@ -4659,6 +5259,9 @@ nds32_elf_relocate_section (bfd * output_bfd ATTRIBUTE_UNUSED, relocation, unresolved_reloc, warned, ignored); + /* keep symbol location for static TLS_IE GOT entry */ + relocation_sym = relocation; + /* la $fp, _FP_BASE_ is per-function (region). Handle it specially. */ switch ((int) r_type) @@ -5272,27 +5875,39 @@ handle_sda: case R_NDS32_TLS_LE_15S0: case R_NDS32_TLS_LE_15S1: case R_NDS32_TLS_LE_15S2: + /* We do not have garbage collection for got entries. + Therefore, IE to LE may have one empty entry, and DESC to + LE may have two. */ if (elf_hash_table (info)->tls_sec != NULL) relocation -= (elf_hash_table (info)->tls_sec->vma + TP_OFFSET); break; + case R_NDS32_TLS_IE_HI20: case R_NDS32_TLS_IE_LO12S2: + case R_NDS32_TLS_DESC_HI20: + case R_NDS32_TLS_DESC_LO12: + case R_NDS32_TLS_IE_LO12: + case R_NDS32_TLS_IEGP_HI20: + case R_NDS32_TLS_IEGP_LO12: + case R_NDS32_TLS_IEGP_LO12S2: { /* Relocation is to the entry for this symbol in the global offset table. */ - unsigned int tls_type; + enum elf_nds32_tls_type tls_type, org_tls_type, eff_tls_type; asection *srelgot; Elf_Internal_Rela outrel; bfd_byte *loc; int indx = 0; + eff_tls_type = org_tls_type = get_tls_type (r_type, h); + BFD_ASSERT (sgot != NULL); if (h != NULL) { bfd_boolean dyn; off = h->got.offset; - BFD_ASSERT (off != (bfd_vma) - 1); + BFD_ASSERT (off != (bfd_vma) -1); dyn = htab->root.dynamic_sections_created; tls_type = ((struct elf_nds32_link_hash_entry *) h)->tls_type; if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, bfd_link_pic (info), h) @@ -5302,65 +5917,147 @@ handle_sda: } else { - /* Never happen currently. */ BFD_ASSERT (local_got_offsets != NULL && local_got_offsets[r_symndx] != (bfd_vma) - 1); - off = local_got_offsets[r_symndx]; - tls_type = elf32_nds32_local_got_tls_type (input_bfd)[r_symndx]; } + relocation = sgot->output_section->vma + sgot->output_offset + off; - if (r_type == R_NDS32_TLS_IE_LO12S2) - break; + if (1 < ones32 (tls_type)) + { + eff_tls_type = 1 << (fls (tls_type) - 1); + /* TLS model shall be handled in nds32_elf_unify_tls_model () */ + + /* TLS model X -> LE is not implement yet! + * workaround here! */ + if (eff_tls_type == GOT_TLS_LE) + { + eff_tls_type = 1 << (fls (tls_type ^ eff_tls_type) - 1); + } + } /* The offset must always be a multiple of 4. We use the least significant bit to record whether we have already processed this entry. */ - if ((off & 1) != 0) - off &= ~1; + bfd_boolean need_relocs = FALSE; + srelgot = ehtab->srelgot; + if ((bfd_link_pic (info) || indx != 0) + && (h == NULL || ELF_ST_VISIBILITY (h->other) == STV_DEFAULT + || h->root.type != bfd_link_hash_undefweak)) + { + need_relocs = TRUE; + BFD_ASSERT (srelgot != NULL); + } + + if (off & 1) + { + off &= ~1; + relocation &= ~1; + + if (eff_tls_type & GOT_TLS_DESC) + { + relocation -= elf_gp (output_bfd); + if ((R_NDS32_TLS_DESC_HI20 == r_type) && (!need_relocs)) + { + /* TLS model shall be converted */ + BFD_ASSERT(0); + } + } + else if (eff_tls_type & GOT_TLS_IEGP) + { + relocation -= elf_gp (output_bfd); + } + } else { - bfd_boolean need_relocs = FALSE; - srelgot = htab->root.srelgot; - if ((bfd_link_pic (info) || indx != 0) - && (h == NULL - || ELF_ST_VISIBILITY (h->other) == STV_DEFAULT - || h->root.type != bfd_link_hash_undefweak)) + if ((eff_tls_type & GOT_TLS_LE) && (tls_type ^ eff_tls_type)) { - need_relocs = TRUE; - BFD_ASSERT (srelgot != NULL); + /* TLS model workaround shall be applied */ + BFD_ASSERT(0); } - if (tls_type & GOT_TLS_IE) + else if (eff_tls_type & (GOT_TLS_IE | GOT_TLS_IEGP)) { + if (eff_tls_type & GOT_TLS_IEGP) + relocation -= elf_gp(output_bfd); + if (need_relocs) { - if (h->dynindx == 0) - outrel.r_addend = relocation - dtpoff_base (info); + if (indx == 0) + outrel.r_addend = gottpoff (info, relocation_sym); else outrel.r_addend = 0; outrel.r_offset = (sgot->output_section->vma - + sgot->output_offset - + off); - outrel.r_info = - ELF32_R_INFO (h->dynindx, R_NDS32_TLS_TPOFF); - - loc = srelgot->contents; - loc += - srelgot->reloc_count * sizeof (Elf32_External_Rela); - bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc); - ++srelgot->reloc_count; + + sgot->output_offset + off); + outrel.r_info = ELF32_R_INFO (indx, R_NDS32_TLS_TPOFF); + + elf32_nds32_add_dynreloc (output_bfd, info, srelgot, + &outrel); } else - bfd_put_32 (output_bfd, h->root.u.def.value - TP_OFFSET, - sgot->contents + off); + { + bfd_put_32 (output_bfd, gottpoff (info, relocation_sym), + sgot->contents + off); + } } + else if (eff_tls_type & GOT_TLS_DESC) + { + relocation -= elf_gp (output_bfd); + if (need_relocs) + { + if (indx == 0) + outrel.r_addend = gottpoff (info, relocation_sym); + else + outrel.r_addend = 0; + outrel.r_offset = (sgot->output_section->vma + + sgot->output_offset + off); + outrel.r_info = ELF32_R_INFO (indx, R_NDS32_TLS_DESC); + + if (htab->tls_desc_trampoline) + { + asection *srelplt; + srelplt = ehtab->srelplt; + loc = srelplt->contents; + loc += htab->next_tls_desc_index++ * sizeof (Elf32_External_Rela); + BFD_ASSERT (loc + sizeof (Elf32_External_Rela) + <= srelplt->contents + srelplt->size); + + bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc); + } + else + { + loc = srelgot->contents; + loc += srelgot->reloc_count * sizeof (Elf32_External_Rela); + bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc); + ++srelgot->reloc_count; + } + } + else + { + /* feed me! */ + bfd_put_32 (output_bfd, 0xdeadbeef, + sgot->contents + off); + bfd_put_32 (output_bfd, gottpoff (info, relocation_sym), + sgot->contents + off + 4); + patch_tls_desc_to_ie (contents, rel, input_bfd); + BFD_ASSERT(0); + } + } + else + { + /* TLS model workaround shall be applied */ + BFD_ASSERT(0); + } + + if (h != NULL) + h->got.offset |= 1; + else + local_got_offsets[r_symndx] |= 1; } } break; - - /* DON'T fall through. */ + /* DON'T fall through. */ default: /* OLD_NDS32_RELOC. */ @@ -5434,6 +6131,12 @@ handle_sda: case R_NDS32_TLS_LE_15S0: case R_NDS32_TLS_LE_15S1: case R_NDS32_TLS_LE_15S2: + case R_NDS32_TLS_DESC_HI20: + case R_NDS32_TLS_DESC_LO12: + case R_NDS32_TLS_IE_LO12: + case R_NDS32_TLS_IEGP_HI20: + case R_NDS32_TLS_IEGP_LO12: + case R_NDS32_TLS_IEGP_LO12S2: /* Instruction related relocs must handle endian properly. */ /* NOTE: PIC IS NOT HANDLE YET; DO IT LATER. */ r = nds32_elf_final_link_relocate (howto, input_bfd, @@ -5522,9 +6225,11 @@ nds32_elf_finish_dynamic_symbol (bfd *output_bfd, struct bfd_link_info *info, struct elf_link_hash_entry *h, Elf_Internal_Sym *sym) { struct elf_link_hash_table *ehtab; + struct elf_nds32_link_hash_entry *hent; bfd_byte *loc; ehtab = elf_hash_table (info); + hent = (struct elf_nds32_link_hash_entry *) h; if (h->plt.offset != (bfd_vma) - 1) { @@ -5640,7 +6345,8 @@ nds32_elf_finish_dynamic_symbol (bfd *output_bfd, struct bfd_link_info *info, } } - if (h->got.offset != (bfd_vma) - 1) + if (h->got.offset != (bfd_vma) - 1 + && hent->tls_type == GOT_NORMAL) { asection *sgot; asection *srelagot; @@ -5743,7 +6449,7 @@ nds32_elf_finish_dynamic_sections (bfd *output_bfd, struct bfd_link_info *info) if (htab == NULL) return FALSE; - dynobj = htab->root.dynobj; + dynobj = elf_hash_table (info)->dynobj; sgotplt = ehtab->sgotplt; /* A broken linker script might have discarded the dynamic sections. @@ -5810,6 +6516,20 @@ get_vma: } bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); break; + + case DT_TLSDESC_PLT: + s = htab->root.splt; + dyn.d_un.d_ptr = (s->output_section->vma + s->output_offset + + htab->dt_tlsdesc_plt); + bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); + break; + + case DT_TLSDESC_GOT: + s = htab->root.sgot; + dyn.d_un.d_ptr = (s->output_section->vma + s->output_offset + + htab->dt_tlsdesc_got); + bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); + break; } } @@ -5869,6 +6589,31 @@ get_vma: elf_section_data (splt->output_section)->this_hdr.sh_entsize = PLT_ENTRY_SIZE; } + + if (htab->dt_tlsdesc_plt) + { + /* Calculate addresses. */ + asection *sgot = sgot = ehtab->sgot; + bfd_vma pltgot = sgotplt->output_section->vma + + sgotplt->output_offset; + bfd_vma tlsdesc_got = sgot->output_section->vma + sgot->output_offset + + htab->dt_tlsdesc_got; + + /* Get GP offset. */ + pltgot -= elf_gp (output_bfd) - 4; /* PLTGOT[1] */ + tlsdesc_got -= elf_gp (output_bfd); + + /* Do relocation. */ + dl_tlsdesc_lazy_trampoline[0] += ((1 << 20) - 1) & (tlsdesc_got >> 12); + dl_tlsdesc_lazy_trampoline[1] += 0xfff & tlsdesc_got; + dl_tlsdesc_lazy_trampoline[4] += ((1 << 20) - 1) & (pltgot >> 12); + dl_tlsdesc_lazy_trampoline[5] += 0xfff & pltgot; + + /* Insert .plt. */ + nds32_put_trampoline (splt->contents + htab->dt_tlsdesc_plt, + dl_tlsdesc_lazy_trampoline, + ARRAY_SIZE (dl_tlsdesc_lazy_trampoline)); + } } /* Fill in the first three entries in the global offset table. */ @@ -6292,6 +7037,43 @@ nds32_elf_gc_mark_hook (asection *sec, struct bfd_link_info *info, return _bfd_elf_gc_mark_hook (sec, info, rel, h, sym); } +static enum elf_nds32_tls_type +get_tls_type (enum elf_nds32_reloc_type r_type, + struct elf_link_hash_entry *h ATTRIBUTE_UNUSED) +{ + enum elf_nds32_tls_type tls_type; + + switch (r_type) + { + case R_NDS32_TLS_LE_HI20: + case R_NDS32_TLS_LE_LO12: + tls_type = GOT_TLS_LE; + break; + case R_NDS32_TLS_IE_HI20: + case R_NDS32_TLS_IE_LO12S2: + case R_NDS32_TLS_IE_LO12: + tls_type = GOT_TLS_IE; + break; + case R_NDS32_TLS_IEGP_HI20: + case R_NDS32_TLS_IEGP_LO12: + case R_NDS32_TLS_IEGP_LO12S2: + tls_type = GOT_TLS_IEGP; + break; + case R_NDS32_TLS_DESC_HI20: + case R_NDS32_TLS_DESC_LO12: + case R_NDS32_TLS_DESC_ADD: + case R_NDS32_TLS_DESC_FUNC: + case R_NDS32_TLS_DESC_CALL: + tls_type = GOT_TLS_DESC; + break; + default: + tls_type = GOT_NORMAL; + break; + } + + return tls_type; +} + /* Ensure that we have allocated bookkeeping structures for ABFD's local symbols. */ @@ -6317,6 +7099,12 @@ elf32_nds32_allocate_local_sym_info (bfd *abfd) elf_local_got_refcounts (abfd) = (bfd_signed_vma *) data; data += num_syms * sizeof (bfd_signed_vma); + elf32_nds32_local_got_tls_type (abfd) = (char *) data; + data += num_syms * sizeof (char); + + elf32_nds32_local_tlsdesc_gotent (abfd) = (bfd_vma *) data; + data += num_syms * sizeof (bfd_vma); + elf32_nds32_local_gp_offset (abfd) = (int *) data; data += num_syms * sizeof (int); } @@ -6341,8 +7129,12 @@ nds32_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, bfd *dynobj; asection *sreloc = NULL; + /* No need for relocation if relocatable already. */ if (bfd_link_relocatable (info)) - return TRUE; + { + elf32_nds32_check_relax_group (abfd, sec); + return TRUE; + } /* Don't do anything special with non-loaded, non-alloced sections. In particular, any relocs in such sections should not affect GOT @@ -6370,7 +7162,7 @@ nds32_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, enum elf_nds32_reloc_type r_type; struct elf_link_hash_entry *h; unsigned long r_symndx; - int tls_type, old_tls_type; + enum elf_nds32_tls_type tls_type, old_tls_type; r_symndx = ELF32_R_SYM (rel->r_info); r_type = ELF32_R_TYPE (rel->r_info); @@ -6408,7 +7200,13 @@ nds32_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, case R_NDS32_GOTPC_LO12: case R_NDS32_GOT20: case R_NDS32_TLS_IE_HI20: + case R_NDS32_TLS_IE_LO12: case R_NDS32_TLS_IE_LO12S2: + case R_NDS32_TLS_IEGP_HI20: + case R_NDS32_TLS_IEGP_LO12: + case R_NDS32_TLS_IEGP_LO12S2: + case R_NDS32_TLS_DESC_HI20: + case R_NDS32_TLS_DESC_LO12: if (dynobj == NULL) htab->root.dynobj = dynobj = abfd; if (!create_got_section (dynobj, info)) @@ -6428,52 +7226,47 @@ nds32_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, case R_NDS32_GOT_LO15: case R_NDS32_GOT_LO19: case R_NDS32_GOT20: + case R_NDS32_TLS_LE_HI20: + case R_NDS32_TLS_LE_LO12: case R_NDS32_TLS_IE_HI20: + case R_NDS32_TLS_IE_LO12: case R_NDS32_TLS_IE_LO12S2: - switch (r_type) - { - case R_NDS32_TLS_IE_HI20: - case R_NDS32_TLS_IE_LO12S2: - tls_type = GOT_TLS_IE; - break; - default: - tls_type = GOT_NORMAL; - break; - } - if (h != NULL) + case R_NDS32_TLS_IEGP_HI20: + case R_NDS32_TLS_IEGP_LO12: + case R_NDS32_TLS_IEGP_LO12S2: + case R_NDS32_TLS_DESC_HI20: + case R_NDS32_TLS_DESC_LO12: + tls_type = get_tls_type (r_type, h); + if (h) { + if (tls_type != GOT_TLS_LE) + h->got.refcount += 1; old_tls_type = elf32_nds32_hash_entry (h)->tls_type; - h->got.refcount += 1; } else { - bfd_signed_vma *local_got_refcounts; - - /* 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; + /* This is a global offset table entry for a local symbol. */ + if (!elf32_nds32_allocate_local_sym_info (abfd)) + return FALSE; - 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; + BFD_ASSERT (r_symndx < symtab_hdr->sh_info); + if (tls_type != GOT_TLS_LE) + elf_local_got_refcounts (abfd)[r_symndx] += 1; old_tls_type = elf32_nds32_local_got_tls_type (abfd)[r_symndx]; } - /* We will already have issued an error message if there + /* We would already have issued an error message if there is a TLS/non-TLS mismatch, based on the symbol type. So just combine any TLS types needed. */ if (old_tls_type != GOT_UNKNOWN && old_tls_type != GOT_NORMAL && tls_type != GOT_NORMAL) tls_type |= old_tls_type; + /* DESC to IE/IEGP if link to executable */ + if ((tls_type & (GOT_TLS_DESC | GOT_TLS_IEGP)) + && (bfd_link_executable (info))) + tls_type |= (bfd_link_pie (info) ? GOT_TLS_IEGP : GOT_TLS_IE); + if (old_tls_type != tls_type) { if (h != NULL) @@ -11457,6 +12250,9 @@ nds32_elf_relax_section (bfd *abfd, asection *sec, if (sec->alignment_power > 2) return TRUE; + /* Do TLS model conversion once at first. */ + nds32_elf_unify_tls_model (abfd, sec, contents, link_info); + /* The optimization type to do. */ table = nds32_elf_hash_table (link_info); @@ -11842,6 +12638,7 @@ bfd_elf32_nds32_set_target_option (struct bfd_link_info *link_info, int eliminate_gc_relocs, FILE * sym_ld_script, int hyper_relax, + int tls_desc_trampoline, int load_store_relax) { struct elf_nds32_link_hash_table *table; @@ -11854,6 +12651,7 @@ bfd_elf32_nds32_set_target_option (struct bfd_link_info *link_info, table->eliminate_gc_relocs = eliminate_gc_relocs; table->sym_ld_script = sym_ld_script; table->hyper_relax = hyper_relax; + table->tls_desc_trampoline = tls_desc_trampoline; table ->load_store_relax = load_store_relax; } @@ -12520,6 +13318,733 @@ nds32_elf_maybe_function_sym (const asymbol *sym, asection *sec, } +/* Do TLS model conversion. */ + +typedef struct relax_group_list_t +{ + Elf_Internal_Rela *relo; + struct relax_group_list_t *next; + struct relax_group_list_t *next_sibling; + int id; +} relax_group_list_t; + +int +list_insert (relax_group_list_t *pHead, Elf_Internal_Rela *pElem); + +int +list_insert_sibling (relax_group_list_t *pNode, Elf_Internal_Rela *pElem); + +void +dump_chain (relax_group_list_t *pHead); + +int +list_insert (relax_group_list_t *pHead, Elf_Internal_Rela *pElem) +{ + relax_group_list_t *pNext = pHead; + + /* find place */ + while (pNext->next) + { + if (pNext->next->id > (int) pElem->r_addend) + break; + + pNext = pNext->next; + } + + /* insert node */ + relax_group_list_t *pNew = bfd_malloc (sizeof (relax_group_list_t)); + if (!pNew) + return FALSE; + + relax_group_list_t *tmp = pNext->next; + pNext->next = pNew; + + pNew->id = pElem->r_addend; + pNew->relo = pElem; + pNew->next = tmp; + pNew->next_sibling = NULL; + + return TRUE; +} + +int +list_insert_sibling (relax_group_list_t *pNode, Elf_Internal_Rela *pElem) +{ + relax_group_list_t *pNext = pNode; + + /* find place */ + while (pNext->next_sibling) + { + pNext = pNext->next_sibling; + } + + /* insert node */ + relax_group_list_t *pNew = bfd_malloc (sizeof (relax_group_list_t)); + if (!pNew) + return FALSE; + + relax_group_list_t *tmp = pNext->next_sibling; + pNext->next_sibling = pNew; + + pNew->id = -1; + pNew->relo = pElem; + pNew->next = NULL; + pNew->next_sibling = tmp; + + return TRUE; +} + +void +dump_chain (relax_group_list_t *pHead) +{ + relax_group_list_t *pNext = pHead->next; + while (pNext) + { + printf("group %d @ 0x%08x", pNext->id, (unsigned)pNext->relo->r_offset); + relax_group_list_t *pNextSib = pNext->next_sibling; + while (pNextSib) + { + printf(", %d", (unsigned) ELF32_R_TYPE (pNextSib->relo->r_info)); + pNextSib = pNextSib->next_sibling; + } + pNext = pNext->next; + printf("\n"); + } +} + +/* check R_NDS32_RELAX_GROUP of each section. + there might be multiple sections in one object file. */ +int +elf32_nds32_check_relax_group (bfd *abfd, asection *asec) +{ + elf32_nds32_relax_group_t *relax_group_ptr = + elf32_nds32_relax_group_ptr (abfd); + + int min_id = relax_group_ptr->min_id; + int max_id = relax_group_ptr->max_id; + + Elf_Internal_Rela *rel; + Elf_Internal_Rela *relend; + Elf_Internal_Rela *relocs; + enum elf_nds32_reloc_type rtype; + + do + { + /* Relocations MUST be kept in memory, because relaxation adjust them. */ + relocs = _bfd_elf_link_read_relocs (abfd, asec, NULL, NULL, + TRUE /* keep_memory */); + if (relocs == NULL) + break; + + /* check R_NDS32_RELAX_GROUP */ + relend = relocs + asec->reloc_count; + for (rel = relocs; rel < relend; rel++) + { + int id; + rtype = ELF32_R_TYPE (rel->r_info); + if (rtype != R_NDS32_RELAX_GROUP) + continue; + + id = rel->r_addend; + if (id < min_id) + min_id = id; + else if (id > max_id) + max_id = id; + } + } + while (FALSE); + + if ((relocs != NULL) && (elf_section_data (asec)->relocs != relocs)) + free (relocs); + + if ((min_id != relax_group_ptr->min_id) + || (max_id != relax_group_ptr->max_id)) + { + relax_group_ptr->count = max_id - min_id + 1; + BFD_ASSERT(min_id <= relax_group_ptr->min_id); + relax_group_ptr->min_id = min_id; + BFD_ASSERT(max_id >= relax_group_ptr->max_id); + relax_group_ptr->max_id = max_id; + } + + return relax_group_ptr->count; +} + +/* Reorder RELAX_GROUP ID when command line option '-r' is applied. */ +struct section_id_list_t *relax_group_section_id_list = NULL; + +struct section_id_list_t * +elf32_nds32_lookup_section_id (int id, struct section_id_list_t **lst_ptr) +{ + struct section_id_list_t *result = NULL; + struct section_id_list_t *lst = *lst_ptr; + + if (NULL == lst) + { + result = (struct section_id_list_t *) calloc + (1, sizeof (struct section_id_list_t)); + BFD_ASSERT (result); /* feed me */ + result->id = id; + *lst_ptr = result; + } + else + { + struct section_id_list_t *cur = lst; + struct section_id_list_t *prv = NULL; + struct section_id_list_t *sec = NULL; + while (cur) + { + if (cur->id < id) + { + prv = cur; + cur = cur->next; + continue; + } + + if (cur->id > id) + { + cur = NULL; /* to insert after prv */ + sec = cur; /* in case prv == NULL */ + } + + break; + } + + if (NULL == cur) + { + /* insert after prv */ + result = (struct section_id_list_t *) calloc + (1, sizeof (struct section_id_list_t)); + BFD_ASSERT (result); /* feed me */ + result->id = id; + if (NULL != prv) + { + result->next = prv->next; + prv->next = result; + } + else + { + *lst_ptr = result; + result->next = sec; + } + } + } + + return result; +} + +int +elf32_nds32_unify_relax_group (bfd *abfd, asection *asec) +{ + static int next_relax_group_bias = 0; + + elf32_nds32_relax_group_t *relax_group_ptr = + elf32_nds32_relax_group_ptr (abfd); + + bfd_boolean result = TRUE; + Elf_Internal_Rela *rel; + Elf_Internal_Rela *relend; + Elf_Internal_Rela *relocs = NULL; + enum elf_nds32_reloc_type rtype; + struct section_id_list_t *node = NULL; + int count = 0; + + do + { + if (0 == relax_group_ptr->count) + break; + + /* check if this section has handled */ + node = elf32_nds32_lookup_section_id (asec->id, &relax_group_section_id_list); + if (NULL == node) + break; /* hit, the section id has handled. */ + + /* Relocations MUST be kept in memory, because relaxation adjust them. */ + relocs = _bfd_elf_link_read_relocs (abfd, asec, NULL, NULL, + TRUE /* keep_memory */); + if (relocs == NULL) + { + BFD_ASSERT (0); /* feed me */ + break; + } + + /* allocate group id bias for this bfd! */ + if (0 == relax_group_ptr->init) + { + relax_group_ptr->bias = next_relax_group_bias; + next_relax_group_bias += relax_group_ptr->count; + relax_group_ptr->init = 1; + } + + /* reorder relax group groups */ + relend = relocs + asec->reloc_count; + for (rel = relocs; rel < relend; rel++) + { + rtype = ELF32_R_TYPE(rel->r_info); + if (rtype != R_NDS32_RELAX_GROUP) + continue; + + /* change it */ + rel->r_addend += relax_group_ptr->bias; + /* debugging count */ + count++; + } + } + while (FALSE); + + if (relocs != NULL && elf_section_data (asec)->relocs != relocs) + free (relocs); + + return result; +} + +int +nds32_elf_unify_tls_model (bfd *inbfd, asection *insec, bfd_byte *incontents, + struct bfd_link_info *lnkinfo) +{ + bfd_boolean result = TRUE; + Elf_Internal_Rela *irel; + Elf_Internal_Rela *irelend; + Elf_Internal_Rela *internal_relocs; + unsigned long r_symndx; + enum elf_nds32_reloc_type r_type; + + Elf_Internal_Sym *local_syms = NULL; + bfd_byte *contents = NULL; + + relax_group_list_t chain = { .id = -1, .next = NULL, .next_sibling = NULL }; + + Elf_Internal_Shdr *symtab_hdr = &elf_tdata (inbfd)->symtab_hdr; + struct elf_link_hash_entry **sym_hashes, **sym_hashes_end; + sym_hashes = elf_sym_hashes (inbfd); + sym_hashes_end = + sym_hashes + symtab_hdr->sh_size / sizeof (Elf32_External_Sym); + if (!elf_bad_symtab (inbfd)) + sym_hashes_end -= symtab_hdr->sh_info; + + /* reorder RELAX_GROUP when command line option '-r' is applied */ + if (bfd_link_relocatable (lnkinfo)) + { + elf32_nds32_unify_relax_group (inbfd, insec); + /* goto finish; */ + return result; + } + + /* Relocations MUST be kept in memory, because relaxation adjust them. */ + internal_relocs = _bfd_elf_link_read_relocs (inbfd, insec, NULL, NULL, + TRUE /* keep_memory */); + if (internal_relocs == NULL) + goto error_return; + + irelend = internal_relocs + insec->reloc_count; + irel = find_relocs_at_address (internal_relocs, internal_relocs, + irelend, R_NDS32_RELAX_ENTRY); + if (irel == irelend) + goto finish; + + /* chain/remove groups */ + for (irel = internal_relocs; irel < irelend; irel++) + { + r_symndx = ELF32_R_SYM (irel->r_info); + r_type = ELF32_R_TYPE (irel->r_info); + if (r_type != R_NDS32_RELAX_GROUP) + continue; + + /* remove it */ + irel->r_info = ELF32_R_INFO (r_symndx, R_NDS32_NONE); + /* chain it now */ + if (!list_insert (&chain, irel)) + goto error_return; + } + + /* collect group relocations */ + /* presume relocations are sorted */ + relax_group_list_t *pNext = chain.next; + while (pNext) + { + for (irel = internal_relocs; irel < irelend; irel++) + { + if (irel->r_offset == pNext->relo->r_offset) + { + /* ignore Non-TLS relocation types */ + r_type = ELF32_R_TYPE (irel->r_info); + if ((R_NDS32_TLS_LE_HI20 > r_type) + || (R_NDS32_RELAX_ENTRY == r_type)) + continue; + + if (!list_insert_sibling (pNext, irel)) + goto error_return; + } + else if (irel->r_offset > pNext->relo->r_offset) + { + pNext = pNext->next; + if (!pNext) + break; + + bfd_vma current_offset = pNext->relo->r_offset; + if (irel->r_offset > current_offset) + irel = internal_relocs; /* restart from head */ + else + --irel; /* check current irel again */ + continue; + } + else + { + /* This shouldn't be happened. */ + } + } + if (pNext) + pNext = pNext->next; + } + +#ifdef DUBUG_VERBOSE + dump_chain(&chain); +#endif + + /* Get symbol table and section content. */ + if (incontents) + contents = incontents; + else if (!nds32_get_section_contents (inbfd, insec, &contents, TRUE) + || !nds32_get_local_syms (inbfd, insec, &local_syms)) + goto error_return; + + char *local_got_tls_type = elf32_nds32_local_got_tls_type (inbfd); + + /* convert TLS model each group if necessary */ + pNext = chain.next; + int cur_grp_id = -1; + int sethi_rt = -1; + int add_rt = -1; + enum elf_nds32_tls_type tls_type, org_tls_type, eff_tls_type; + tls_type = org_tls_type = eff_tls_type = 0; + while (pNext) + { + relax_group_list_t *pNextSig = pNext->next_sibling; + while (pNextSig) + { + struct elf_link_hash_entry *h = NULL; + irel = pNextSig->relo; + r_symndx = ELF32_R_SYM(irel->r_info); + r_type = ELF32_R_TYPE(irel->r_info); + + if (pNext->id != cur_grp_id) + { + cur_grp_id = pNext->id; + org_tls_type = get_tls_type (r_type, NULL); + 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; + tls_type = ((struct elf_nds32_link_hash_entry *) h)->tls_type; + } + else + { + tls_type = local_got_tls_type + ? local_got_tls_type[r_symndx] + : GOT_NORMAL; + } + + eff_tls_type = 1 << (fls (tls_type) - 1); + sethi_rt = N32_RT5(bfd_getb32 (contents + irel->r_offset)); + } + + if (eff_tls_type != org_tls_type) + { + switch (org_tls_type) + { + /* DESC to IEGP/IE/LE. */ + case GOT_TLS_DESC: + switch (eff_tls_type) + { + case GOT_TLS_IE: + switch (r_type) + { + case R_NDS32_TLS_DESC_HI20: + irel->r_info = ELF32_R_INFO(r_symndx, + R_NDS32_TLS_IE_HI20); + break; + case R_NDS32_TLS_DESC_LO12: + irel->r_info = ELF32_R_INFO(r_symndx, + R_NDS32_TLS_IE_LO12); + break; + case R_NDS32_TLS_DESC_ADD: + { + uint32_t insn = bfd_getb32 (contents + irel->r_offset); + add_rt = N32_RT5 (insn); + insn = N32_TYPE2 (LWI, add_rt, sethi_rt, 0); + bfd_putb32 (insn, contents + irel->r_offset); + + irel->r_info = ELF32_R_INFO(r_symndx, R_NDS32_NONE); + } + break; + case R_NDS32_TLS_DESC_FUNC: + bfd_putb32 (INSN_NOP, contents + irel->r_offset); + irel->r_info = ELF32_R_INFO(r_symndx, + R_NDS32_RELAX_REMOVE); + break; + case R_NDS32_TLS_DESC_CALL: + { + uint32_t insn = N32_ALU1(ADD, REG_R0, add_rt, + REG_TP); + bfd_putb32 (insn, contents + irel->r_offset); + + irel->r_info = ELF32_R_INFO(r_symndx, R_NDS32_NONE); + } + break; + case R_NDS32_LOADSTORE: + case R_NDS32_PTR: + case R_NDS32_PTR_RESOLVED: + case R_NDS32_NONE: + case R_NDS32_LABEL: + break; + default: + BFD_ASSERT(0); + break; + } + break; + case GOT_TLS_IEGP: + switch (r_type) + { + case R_NDS32_TLS_DESC_HI20: + irel->r_info = ELF32_R_INFO(r_symndx, + R_NDS32_TLS_IEGP_HI20); + break; + case R_NDS32_TLS_DESC_LO12: + irel->r_info = ELF32_R_INFO(r_symndx, + R_NDS32_TLS_IEGP_LO12); + break; + case R_NDS32_TLS_DESC_ADD: + { + uint32_t insn = bfd_getb32 (contents + irel->r_offset); + add_rt = N32_RT5 (insn); + insn = N32_MEM(LW, add_rt, sethi_rt, REG_GP, 0); + bfd_putb32 (insn, contents + irel->r_offset); + + irel->r_info = ELF32_R_INFO(r_symndx, R_NDS32_NONE); + } + break; + case R_NDS32_TLS_DESC_FUNC: + bfd_putb32 (INSN_NOP, contents + irel->r_offset); + irel->r_info = ELF32_R_INFO(r_symndx, + R_NDS32_RELAX_REMOVE); + break; + case R_NDS32_TLS_DESC_CALL: + { + uint32_t insn = N32_ALU1(ADD, REG_R0, add_rt, + REG_TP); + bfd_putb32 (insn, contents + irel->r_offset); + + irel->r_info = ELF32_R_INFO(r_symndx, R_NDS32_NONE); + } + break; + case R_NDS32_LOADSTORE: + case R_NDS32_PTR: + case R_NDS32_PTR_RESOLVED: + case R_NDS32_NONE: + case R_NDS32_LABEL: + break; + default: + BFD_ASSERT(0); + break; + } + break; + case GOT_TLS_LE: + switch (r_type) + { + case R_NDS32_TLS_DESC_HI20: + irel->r_info = ELF32_R_INFO (r_symndx, R_NDS32_TLS_LE_HI20); + break; + case R_NDS32_TLS_DESC_LO12: + irel->r_info = ELF32_R_INFO (r_symndx, R_NDS32_TLS_LE_LO12); + break; + case R_NDS32_TLS_DESC_ADD: + { + uint32_t insn = bfd_getb32 (contents + irel->r_offset); + add_rt = N32_RT5 (insn); + insn = N32_ALU1 (ADD, REG_R0, sethi_rt, REG_TP); + bfd_putb32 (insn, contents + irel->r_offset); + + irel->r_info = ELF32_R_INFO (r_symndx, R_NDS32_TLS_LE_ADD); + } + break; + case R_NDS32_TLS_DESC_FUNC: + bfd_putb32 (INSN_NOP, contents + irel->r_offset); + irel->r_info = ELF32_R_INFO (r_symndx, R_NDS32_RELAX_REMOVE); + break; + case R_NDS32_TLS_DESC_CALL: + bfd_putb32 (INSN_NOP, contents + irel->r_offset); + irel->r_info = ELF32_R_INFO (r_symndx, R_NDS32_RELAX_REMOVE); + break; + case R_NDS32_LOADSTORE: + case R_NDS32_PTR: + case R_NDS32_PTR_RESOLVED: + case R_NDS32_NONE: + case R_NDS32_LABEL: + break; + default: + BFD_ASSERT(0); + break; + } + break; + default: + break; + } + break; + /* IEGP to IE/LE. */ + case GOT_TLS_IEGP: + switch (eff_tls_type) + { + case GOT_TLS_IE: + switch (r_type) + { + case R_NDS32_TLS_IEGP_HI20: + irel->r_info = ELF32_R_INFO(r_symndx, + R_NDS32_TLS_IE_HI20); + break; + case R_NDS32_TLS_IEGP_LO12: + irel->r_info = ELF32_R_INFO(r_symndx, + R_NDS32_TLS_IE_LO12); + break; + case R_NDS32_PTR_RESOLVED: + { + uint32_t insn = bfd_getb32 (contents + irel->r_offset); + add_rt = N32_RT5 (insn); + insn = N32_TYPE2 (LWI, add_rt, sethi_rt, 0); + bfd_putb32 (insn, contents + irel->r_offset); + } + break; + case R_NDS32_TLS_IEGP_LW: + break; + case R_NDS32_LOADSTORE: + case R_NDS32_PTR: + case R_NDS32_NONE: + case R_NDS32_LABEL: + break; + default: + BFD_ASSERT(0); + break; + } + break; + case GOT_TLS_LE: + switch (r_type) + { + case R_NDS32_TLS_IEGP_HI20: + irel->r_info = ELF32_R_INFO (r_symndx, R_NDS32_TLS_LE_HI20); + break; + case R_NDS32_TLS_IEGP_LO12: + irel->r_info = ELF32_R_INFO (r_symndx, R_NDS32_TLS_LE_LO12); + break; + case R_NDS32_TLS_IEGP_LW: + bfd_putb32 (INSN_NOP, contents + irel->r_offset); + irel->r_info = ELF32_R_INFO (r_symndx, R_NDS32_RELAX_REMOVE); + break; + case R_NDS32_LOADSTORE: + case R_NDS32_PTR: + case R_NDS32_NONE: + case R_NDS32_LABEL: + case R_NDS32_PTR_RESOLVED: + break; + default: + BFD_ASSERT(0); + break; + } + break; + default: + break; + } + break; + /* IE to LE. */ + case GOT_TLS_IE: + switch (eff_tls_type) + { + case GOT_TLS_LE: + switch (r_type) + { + case R_NDS32_TLS_IE_HI20: + irel->r_info = ELF32_R_INFO (r_symndx, R_NDS32_TLS_LE_HI20); + break; + case R_NDS32_TLS_IE_LO12S2: + { + uint32_t insn = bfd_getb32 (contents + irel->r_offset); + add_rt = N32_RT5 (insn); + insn = N32_TYPE2 (ORI, add_rt, sethi_rt, 0); + bfd_putb32 (insn, contents + irel->r_offset); + + irel->r_info = ELF32_R_INFO (r_symndx, R_NDS32_TLS_LE_LO12); + } + break; + case R_NDS32_LOADSTORE: + case R_NDS32_PTR: + case R_NDS32_NONE: + case R_NDS32_LABEL: + break; + default: + BFD_ASSERT(0); + break; + } + break; + default: + break; + } + break; + default: + break; + } + } + pNextSig = pNextSig->next_sibling; + } + +#if 1 + pNext = pNext->next; +#else + while (pNext) + { + if (pNext->id != cur_grp_id) + break; + pNext = pNext->next; + } +#endif + } + +finish: + if (incontents) + contents = NULL; + + if (internal_relocs != NULL + && elf_section_data (insec)->relocs != internal_relocs) + free (internal_relocs); + + if (contents != NULL + && elf_section_data (insec)->this_hdr.contents != contents) + free (contents); + + if (local_syms != NULL && symtab_hdr->contents != (bfd_byte *) local_syms) + free (local_syms); + + if (chain.next) + { + pNext = chain.next; + relax_group_list_t *pDel; + while (pNext) + { + pDel = pNext; + pNext = pNext->next; + free (pDel); + } + } + + return result; + +error_return: + result = FALSE; + goto finish; +} + +/* End TLS model conversion. */ + + #define ELF_ARCH bfd_arch_nds32 #define ELF_MACHINE_CODE EM_NDS32 #define ELF_MAXPAGESIZE 0x1000 diff --git a/bfd/elf32-nds32.h b/bfd/elf32-nds32.h index ecb5b0879c..216f371056 100644 --- a/bfd/elf32-nds32.h +++ b/bfd/elf32-nds32.h @@ -84,20 +84,38 @@ enum }; +struct section_id_list_t +{ + int id; + struct section_id_list_t *next; +}; + +extern struct section_id_list_t *elf32_nds32_lookup_section_id + (int, struct section_id_list_t **); +extern int elf32_nds32_check_relax_group (bfd *, asection *); +extern int elf32_nds32_unify_relax_group (bfd *, asection *); +extern int nds32_elf_unify_tls_model (bfd *, asection *, bfd_byte *, + struct bfd_link_info *); extern void nds32_insertion_sort - (void *, size_t, size_t, int (*) (const void *, const void *)); +(void *, size_t, size_t, int (*) (const void *, const void *)); extern int nds32_convert_32_to_16 (bfd *, uint32_t, uint16_t *, int *); extern int nds32_convert_16_to_32 (bfd *, uint16_t, uint32_t *); extern void bfd_elf32_nds32_set_target_option (struct bfd_link_info *, int, int, FILE *, - int, int); + int, int, int); #define nds32_elf_hash_table(info) \ (elf_hash_table_id ((struct elf_link_hash_table *) ((info)->hash)) \ == NDS32_ELF_DATA ? \ ((struct elf_nds32_link_hash_table *) ((info)->hash)) : NULL) +#define elf32_nds32_compute_jump_table_size(htab) \ + ((htab)->next_tls_desc_index * 4) + +#define elf32_nds32_local_tlsdesc_gotent(bfd) \ + (elf_nds32_tdata (bfd)->local_tlsdesc_gotent) + /* Hash table structure for target nds32. There are some members to save target options passed from nds32elf.em to bfd. */ @@ -117,8 +135,35 @@ struct elf_nds32_link_hash_table int eliminate_gc_relocs; /* --meliminate-gc-relocs */ FILE *sym_ld_script; /* --mgen-symbol-ld-script= */ bfd_boolean hyper_relax; /* Relax for symbol not in RW sections. */ + int tls_desc_trampoline; /* --m[no-]tlsdesc-trampoline. */ /* Disable if linking a dynamically linked executable. */ int load_store_relax; + + /* The offset into splt of the PLT entry for the TLS descriptor + resolver. Special values are 0, if not necessary (or not found + to be necessary yet), and -1 if needed but not determined + yet. */ + bfd_vma dt_tlsdesc_plt; + + /* The offset into sgot of the GOT entry used by the PLT entry + above. */ + bfd_vma dt_tlsdesc_got; + + /* Offset in .plt section of tls_nds32_trampoline. */ + bfd_vma tls_trampoline; + + /* The index of the next unused R_NDS32_TLS_DESC slot in .rel.plt. */ + bfd_vma next_tls_desc_index; + + /* How many R_NDS32_TLS_DESC relocations were generated so far. */ + bfd_vma num_tls_desc; + + /* The amount of space used by the reserved portion of the sgotplt + section, plus whatever space is used by the jump slots. */ + bfd_vma sgotplt_jump_table_size; + + /* True if the target uses REL relocations. */ + int use_rel; }; #ifdef __cplusplus diff --git a/bfd/libbfd.h b/bfd/libbfd.h index 1e0cf17153..d37716c363 100644 --- a/bfd/libbfd.h +++ b/bfd/libbfd.h @@ -2002,18 +2002,33 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@", "BFD_RELOC_NDS32_17IFC_PCREL", "BFD_RELOC_NDS32_10IFCU_PCREL", "BFD_RELOC_NDS32_TPOFF", + "BFD_RELOC_NDS32_GOTTPOFF", "BFD_RELOC_NDS32_TLS_LE_HI20", "BFD_RELOC_NDS32_TLS_LE_LO12", - "BFD_RELOC_NDS32_TLS_LE_ADD", - "BFD_RELOC_NDS32_TLS_LE_LS", - "BFD_RELOC_NDS32_GOTTPOFF", - "BFD_RELOC_NDS32_TLS_IE_HI20", - "BFD_RELOC_NDS32_TLS_IE_LO12S2", - "BFD_RELOC_NDS32_TLS_TPOFF", "BFD_RELOC_NDS32_TLS_LE_20", "BFD_RELOC_NDS32_TLS_LE_15S0", "BFD_RELOC_NDS32_TLS_LE_15S1", "BFD_RELOC_NDS32_TLS_LE_15S2", + "BFD_RELOC_NDS32_TLS_LE_ADD", + "BFD_RELOC_NDS32_TLS_LE_LS", + "BFD_RELOC_NDS32_TLS_IE_HI20", + "BFD_RELOC_NDS32_TLS_IE_LO12", + "BFD_RELOC_NDS32_TLS_IE_LO12S2", + "BFD_RELOC_NDS32_TLS_IEGP_HI20", + "BFD_RELOC_NDS32_TLS_IEGP_LO12", + "BFD_RELOC_NDS32_TLS_IEGP_LO12S2", + "BFD_RELOC_NDS32_TLS_IEGP_LW", + "BFD_RELOC_NDS32_TLS_DESC", + "BFD_RELOC_NDS32_TLS_DESC_HI20", + "BFD_RELOC_NDS32_TLS_DESC_LO12", + "BFD_RELOC_NDS32_TLS_DESC_20", + "BFD_RELOC_NDS32_TLS_DESC_SDA17S2", + "BFD_RELOC_NDS32_TLS_DESC_ADD", + "BFD_RELOC_NDS32_TLS_DESC_FUNC", + "BFD_RELOC_NDS32_TLS_DESC_CALL", + "BFD_RELOC_NDS32_TLS_DESC_MEM", + "BFD_RELOC_NDS32_REMOVE", + "BFD_RELOC_NDS32_GROUP", "BFD_RELOC_NDS32_LSI", "BFD_RELOC_V850_9_PCREL", "BFD_RELOC_V850_22_PCREL", diff --git a/bfd/reloc.c b/bfd/reloc.c index 86ae1257d6..07be1a4c7b 100644 --- a/bfd/reloc.c +++ b/bfd/reloc.c @@ -4253,30 +4253,60 @@ ENUMDOC For ex9 and ifc using. ENUM BFD_RELOC_NDS32_TPOFF +ENUMX + BFD_RELOC_NDS32_GOTTPOFF ENUMX BFD_RELOC_NDS32_TLS_LE_HI20 ENUMX BFD_RELOC_NDS32_TLS_LE_LO12 +ENUMX + BFD_RELOC_NDS32_TLS_LE_20 +ENUMX + BFD_RELOC_NDS32_TLS_LE_15S0 +ENUMX + BFD_RELOC_NDS32_TLS_LE_15S1 +ENUMX + BFD_RELOC_NDS32_TLS_LE_15S2 ENUMX BFD_RELOC_NDS32_TLS_LE_ADD ENUMX BFD_RELOC_NDS32_TLS_LE_LS -ENUMX - BFD_RELOC_NDS32_GOTTPOFF ENUMX BFD_RELOC_NDS32_TLS_IE_HI20 +ENUMX + BFD_RELOC_NDS32_TLS_IE_LO12 ENUMX BFD_RELOC_NDS32_TLS_IE_LO12S2 ENUMX - BFD_RELOC_NDS32_TLS_TPOFF + BFD_RELOC_NDS32_TLS_IEGP_HI20 ENUMX - BFD_RELOC_NDS32_TLS_LE_20 + BFD_RELOC_NDS32_TLS_IEGP_LO12 ENUMX - BFD_RELOC_NDS32_TLS_LE_15S0 + BFD_RELOC_NDS32_TLS_IEGP_LO12S2 ENUMX - BFD_RELOC_NDS32_TLS_LE_15S1 + BFD_RELOC_NDS32_TLS_IEGP_LW ENUMX - BFD_RELOC_NDS32_TLS_LE_15S2 + BFD_RELOC_NDS32_TLS_DESC +ENUMX + BFD_RELOC_NDS32_TLS_DESC_HI20 +ENUMX + BFD_RELOC_NDS32_TLS_DESC_LO12 +ENUMX + BFD_RELOC_NDS32_TLS_DESC_20 +ENUMX + BFD_RELOC_NDS32_TLS_DESC_SDA17S2 +ENUMX + BFD_RELOC_NDS32_TLS_DESC_ADD +ENUMX + BFD_RELOC_NDS32_TLS_DESC_FUNC +ENUMX + BFD_RELOC_NDS32_TLS_DESC_CALL +ENUMX + BFD_RELOC_NDS32_TLS_DESC_MEM +ENUMX + BFD_RELOC_NDS32_REMOVE +ENUMX + BFD_RELOC_NDS32_GROUP ENUMDOC For TLS. ENUM diff --git a/binutils/testsuite/binutils-all/objcopy.exp b/binutils/testsuite/binutils-all/objcopy.exp index df9935e6c9..a1610297ad 100644 --- a/binutils/testsuite/binutils-all/objcopy.exp +++ b/binutils/testsuite/binutils-all/objcopy.exp @@ -1098,7 +1098,11 @@ if [is_elf_format] { set reloc_format mips64 } # A relocation type not supported by any target - set reloc 215 + if { [istarget "nds32*-*"] } { + set reloc 255 + } else { + set reloc 215 + } run_dump_test "strip-13" [list \ [list source strip-13${reloc_format}.s] \ [list as "${elf64} --defsym RELOC=${reloc}"]] diff --git a/gas/config.in b/gas/config.in index 6c7cccaee0..9c15a070a3 100644 --- a/gas/config.in +++ b/gas/config.in @@ -222,6 +222,9 @@ /* Define default value for nds32_zol_ext */ #undef NDS32_DEFAULT_ZOL_EXT +/* Define default value for nds32_linux_toolchain */ +#undef NDS32_LINUX_TOOLCHAIN + /* Define if environ is not declared in system header files. */ #undef NEED_DECLARATION_ENVIRON diff --git a/gas/config/tc-nds32.c b/gas/config/tc-nds32.c index 015d5d4017..24dc390631 100644 --- a/gas/config/tc-nds32.c +++ b/gas/config/tc-nds32.c @@ -35,6 +35,8 @@ #include "opcode/nds32.h" #include +#include +#include /* GAS definitions. */ @@ -4804,7 +4806,10 @@ nds32_elf_record_fixup_exp (fragS *fragP, const char *str, reloc = BFD_RELOC_NDS32_TLS_LE_HI20; break; case BFD_RELOC_NDS32_GOTTPOFF: /* @GOTTPOFF */ - reloc = BFD_RELOC_NDS32_TLS_IE_HI20; + reloc = nds32_pic ? BFD_RELOC_NDS32_TLS_IEGP_HI20 : BFD_RELOC_NDS32_TLS_IE_HI20; + break; + case BFD_RELOC_NDS32_TLS_DESC: /* @TLSDESC */ + reloc = BFD_RELOC_NDS32_TLS_DESC_HI20; break; default: /* No suffix */ if (nds32_pic) @@ -4844,6 +4849,12 @@ nds32_elf_record_fixup_exp (fragS *fragP, const char *str, case BFD_RELOC_NDS32_TPOFF: /* @TPOFF */ reloc = BFD_RELOC_NDS32_TLS_LE_LO12; break; + case BFD_RELOC_NDS32_GOTTPOFF: /* @GOTTPOFF */ + reloc = nds32_pic ? BFD_RELOC_NDS32_TLS_IEGP_LO12 : BFD_RELOC_NDS32_TLS_IE_LO12; + break; + case BFD_RELOC_NDS32_TLS_DESC: /* @TLSDESC */ + reloc = BFD_RELOC_NDS32_TLS_DESC_LO12; + break; default: /* No suffix */ if (nds32_pic) /* When the file is pic, the address must be offset to gp. @@ -4862,7 +4873,7 @@ nds32_elf_record_fixup_exp (fragS *fragP, const char *str, switch (pexp->X_md) { case BFD_RELOC_NDS32_GOTTPOFF: /* @GOTTPOFF */ - reloc = BFD_RELOC_NDS32_TLS_IE_LO12S2; + reloc = nds32_pic ? BFD_RELOC_NDS32_TLS_IEGP_LO12S2 : BFD_RELOC_NDS32_TLS_IE_LO12S2; break; default: /* No suffix */ reloc = BFD_RELOC_NDS32_LO12S2; @@ -5120,6 +5131,88 @@ static struct nds32_relax_hint_table relax_ls_table[] = {0, 0, 0, 0} } }, + { + /* TLS LE LS|LA */ + .main_type = NDS32_RELAX_HINT_TLS_LE_LS, + .relax_code_size = 16, + .relax_code_seq = + { + OP6(SETHI), + OP6(ORI), + OP6(MEM), + OP6(ALU1), + }, + .relax_fixup = + { + {0, 4, NDS32_HINT | NDS32_ADDEND, BFD_RELOC_NDS32_LOADSTORE}, + {4, 4, NDS32_HINT | NDS32_PTR_MULTIPLE, BFD_RELOC_NDS32_PTR}, + {8, 4, NDS32_HINT | NDS32_ABS, BFD_RELOC_NDS32_PTR_RESOLVED}, + {8, 4, NDS32_HINT | NDS32_SYM, BFD_RELOC_NDS32_TLS_LE_LS}, + {12, 4, NDS32_HINT | NDS32_ABS, BFD_RELOC_NDS32_PTR_RESOLVED}, + {12, 4, NDS32_HINT | NDS32_SYM, BFD_RELOC_NDS32_TLS_LE_ADD}, + {0, 0, 0, 0} + } + }, + { + /* TLS IE LA */ + .main_type = NDS32_RELAX_HINT_TLS_IE_LA, + .relax_code_size = 8, + .relax_code_seq = + { + OP6(SETHI), + OP6(LBI), + }, + .relax_fixup = + { + {0, 4, NDS32_HINT | NDS32_ADDEND, BFD_RELOC_NDS32_LOADSTORE}, + {4, 4, NDS32_HINT | NDS32_INSN16, BFD_RELOC_NDS32_INSN16}, + {0, 0, 0, 0} + } + }, + { + /* TLS IEGP LA */ + .main_type = NDS32_RELAX_HINT_TLS_IEGP_LA, + .relax_code_size = 12, + .relax_code_seq = + { + OP6 (SETHI), + OP6 (ORI), + OP6 (MEM), + }, + .relax_fixup = + { + {0, 4, NDS32_HINT | NDS32_ADDEND, BFD_RELOC_NDS32_LOADSTORE}, + {4, 4, NDS32_HINT | NDS32_PTR_PATTERN, BFD_RELOC_NDS32_PTR}, + {8, 4, NDS32_HINT | NDS32_ABS, BFD_RELOC_NDS32_PTR_RESOLVED}, + {8, 4, NDS32_HINT | NDS32_SYM, BFD_RELOC_NDS32_TLS_IEGP_LW}, + {0, 0, 0, 0} + } + }, + { + /* TLS DESC LS: */ + .main_type = NDS32_RELAX_HINT_TLS_DESC_LS, + .relax_code_size = 24, + .relax_code_seq = + { + OP6 (SETHI), + OP6 (ORI), + OP6 (ALU1), + OP6 (LBI), /* load argument */ + OP6 (JREG), + OP6 (MEM), /* load/store variable or load argument */ + }, + .relax_fixup = + { + {0, 4, NDS32_HINT | NDS32_ADDEND, BFD_RELOC_NDS32_LOADSTORE}, + {4, 4, NDS32_HINT | NDS32_PTR_PATTERN, BFD_RELOC_NDS32_PTR}, + {8, 4, NDS32_HINT | NDS32_ABS, BFD_RELOC_NDS32_PTR_RESOLVED}, + {8, 4, NDS32_HINT | NDS32_SYM, BFD_RELOC_NDS32_TLS_DESC_ADD}, + {12, 4, NDS32_HINT | NDS32_SYM, BFD_RELOC_NDS32_TLS_DESC_FUNC}, + {16, 4, NDS32_HINT | NDS32_SYM, BFD_RELOC_NDS32_TLS_DESC_CALL}, + {20, 4, NDS32_HINT | NDS32_SYM_DESC_MEM, BFD_RELOC_NDS32_TLS_DESC_MEM}, + {0, 0, 0, 0} + } + }, { .main_type = 0, .relax_code_seq = {0}, @@ -5528,6 +5621,51 @@ static struct nds32_hint_map hint_map [] = N32_RELAX_SETHI | N32_RELAX_ORI, N32_RELAX_ALU1 | N32_RELAX_MEM, /* | N32_RELAX_LSI, */ }, + { + /* TLS LE LA|LS (@TPOFF) */ + BFD_RELOC_NDS32_TLS_LE_HI20, + NULL, + NDS32_RELAX_HINT_TLS_LE_LS, + BR_RANGE_U4G, + N32_RELAX_SETHI | N32_RELAX_ORI, + N32_RELAX_ALU1 | N32_RELAX_MEM, + }, + { + /* TLS IE LA */ + BFD_RELOC_NDS32_TLS_IE_HI20, + NULL, + NDS32_RELAX_HINT_TLS_IE_LA, + BR_RANGE_U4G, + N32_RELAX_SETHI | N32_RELAX_LSI, + 0, + }, +{ + /* TLS IE LS */ + BFD_RELOC_NDS32_TLS_IE_HI20, + NULL, + NDS32_RELAX_HINT_TLS_IE_LS, + BR_RANGE_U4G, + N32_RELAX_SETHI | N32_RELAX_LSI | N32_RELAX_MEM, + 0, + }, + { + /* TLS IEGP LA */ + BFD_RELOC_NDS32_TLS_IEGP_HI20, + NULL, + NDS32_RELAX_HINT_TLS_IEGP_LA, + BR_RANGE_U4G, + N32_RELAX_SETHI | N32_RELAX_ORI | N32_RELAX_MEM, + 0, + }, + { + /* TLS DESC LS */ + BFD_RELOC_NDS32_TLS_DESC_HI20, + NULL, + NDS32_RELAX_HINT_TLS_DESC_LS, + BR_RANGE_U4G, + N32_RELAX_SETHI | N32_RELAX_ORI | N32_RELAX_ALU1 | N32_RELAX_CALL, + N32_RELAX_LSI | N32_RELAX_MEM, + }, /* last one */ {0, NULL, 0, 0 ,0, 0} }; @@ -5854,6 +5992,23 @@ nds32_elf_append_relax_relocs (const char *key, void *value) relax_code_size = hint_info.relax_code_size; pattern_now = relocs_pattern; +#ifdef NDS32_LINUX_TOOLCHAIN + /* prepare group relocation ID (number). */ + long group_id = 0; + if (key) + { + /* convert .relax_hint key to number */ + errno = 0; + group_id = strtol (key, NULL, 10); + if ((errno == ERANGE && (group_id == LONG_MAX || group_id == LONG_MIN)) + || (errno != 0 && group_id == 0)) + { + as_bad (_("Internal error: .relax_hint KEY is not a number!")); + goto restore; + } + } +#endif + /* Insert relaxation. */ exp.X_op = O_symbol; @@ -5978,6 +6133,108 @@ nds32_elf_append_relax_relocs (const char *key, void *value) exp.X_add_symbol = hi_sym; exp.X_add_number = hi_branch_offset; } + else if (NDS32_SYM_DESC_MEM & fixup_now->ramp) + { + /* Do the same as NDS32_SYM. */ + exp.X_add_symbol = hi_sym; + exp.X_add_number = hi_branch_offset; + + /* Extra to NDS32_SYM. */ + /* Detect if DESC_FUNC relax type do apply. */ + if ((REG_GP == N32_RA5 (pattern_now->insn)) + || (REG_GP == N32_RB5 (pattern_now->insn))) + { + fixP = fix_new_exp (fragP, where - fragP->fr_literal, + fixup_size, &exp, pcrel, + BFD_RELOC_NDS32_TLS_DESC_FUNC); + fixP->fx_addnumber = fixP->fx_offset; + + fixup_size = 0; + } + /* Else do as usual. */ + } + else if (fixup_now->ramp & NDS32_PTR_PATTERN) + { + /* Find out PTR_RESOLVED code pattern. */ + nds32_relax_fixup_info_t *next_fixup = fixup_now + 1; + uint32_t resolved_pattern = 0; + while (next_fixup->offset) + { + if (next_fixup->r_type == BFD_RELOC_NDS32_PTR_RESOLVED) + { + uint32_t new_pattern = code_seq[next_fixup->offset >> 2]; + if (!resolved_pattern) + resolved_pattern = new_pattern; + else if (new_pattern != resolved_pattern) + { + as_warn (_("Multiple BFD_RELOC_NDS32_PTR_RESOLVED " + "patterns are not supported yet!")); + break; + } + } + ++next_fixup; + } + + /* Find matched code and insert fix-ups. */ + struct nds32_relocs_pattern *next_pattern = pattern_now->next; + /* This relocation has to point to another instruction. + Make sure each resolved relocation has to be pointed. */ + /* All instruction in relax_table should be 32-bit. */ + while (next_pattern) + { + uint32_t cur_pattern = GET_OPCODE (next_pattern->opcode->value); + if (cur_pattern == resolved_pattern) + { + ptr_offset = next_pattern->where + - next_pattern->frag->fr_literal; + exp.X_add_symbol = symbol_temp_new (now_seg, ptr_offset, + next_pattern->frag); + exp.X_add_number = 0; + fixP = fix_new_exp (fragP, where - fragP->fr_literal, + fixup_size, &exp, 0, + fixup_now->r_type); + fixP->fx_addnumber = fixP->fx_offset; + } + next_pattern = next_pattern->next; + } + + fixup_size = 0; + } + else if (fixup_now->ramp & NDS32_PTR_MULTIPLE) + { + /* Find each PTR_RESOLVED pattern after PTR. */ + nds32_relax_fixup_info_t *next_fixup = fixup_now + 1; + while (next_fixup->offset) + { + if (next_fixup->r_type == BFD_RELOC_NDS32_PTR_RESOLVED) + { + uint32_t pattern = code_seq[next_fixup->offset >> 2]; + /* Find matched code to insert fix-ups. */ + struct nds32_relocs_pattern *next_insn = pattern_now->next; + while (next_insn) + { + uint32_t insn_pattern = GET_OPCODE (next_insn->opcode->value); + if (insn_pattern == pattern) + { + ptr_offset = next_insn->where + - next_insn->frag->fr_literal; + exp.X_add_symbol = symbol_temp_new (now_seg, + ptr_offset, + next_insn->frag); + exp.X_add_number = 0; + fixP = fix_new_exp (fragP, + where - fragP->fr_literal, + fixup_size, &exp, 0, + fixup_now->r_type); + fixP->fx_addnumber = fixP->fx_offset; + } + next_insn = next_insn->next; + } + } + ++next_fixup; + } + fixup_size = 0; + } else { exp.X_add_symbol = sym; @@ -5994,6 +6251,18 @@ nds32_elf_append_relax_relocs (const char *key, void *value) fixup_size = fixup_now->size; } +#ifdef NDS32_LINUX_TOOLCHAIN + /* Insert group relocation for each relax hint. */ + if (key) + { + exp.X_add_symbol = hi_sym; /* for eyes only */ + exp.X_add_number = group_id; + fixP = fix_new_exp (fragP, where - fragP->fr_literal, fixup_size, + &exp, pcrel, BFD_RELOC_NDS32_GROUP); + fixP->fx_addnumber = fixP->fx_offset; + } +#endif + if (count < relax_code_size / 4) count++; pattern_now = pattern_now->next; @@ -7448,6 +7717,12 @@ nds32_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) case BFD_RELOC_NDS32_GOTTPOFF: case BFD_RELOC_NDS32_TLS_IE_HI20: case BFD_RELOC_NDS32_TLS_IE_LO12S2: + case BFD_RELOC_NDS32_TLS_DESC_HI20: + case BFD_RELOC_NDS32_TLS_DESC_LO12: + case BFD_RELOC_NDS32_TLS_IE_LO12: + case BFD_RELOC_NDS32_TLS_IEGP_HI20: + case BFD_RELOC_NDS32_TLS_IEGP_LO12: + case BFD_RELOC_NDS32_TLS_IEGP_LO12S2: S_SET_THREAD_LOCAL (fixP->fx_addsy); break; default: @@ -7637,6 +7912,7 @@ static struct suffix_name suffix_table[] = {"TPOFF", BFD_RELOC_NDS32_TPOFF}, {"PLT", BFD_RELOC_NDS32_25_PLTREL}, {"GOTTPOFF", BFD_RELOC_NDS32_GOTTPOFF}, + {"TLSDESC", BFD_RELOC_NDS32_TLS_DESC}, }; /* Implement md_parse_name. */ diff --git a/gas/config/tc-nds32.h b/gas/config/tc-nds32.h index e48d4c1f9b..150fbda869 100644 --- a/gas/config/tc-nds32.h +++ b/gas/config/tc-nds32.h @@ -246,7 +246,11 @@ enum nds32_ramp NDS32_FIX = (1 << 7), NDS32_ADDEND = (1 << 8), NDS32_SYM = (1 << 9), - NDS32_PCREL = (1 << 10) + NDS32_PCREL = (1 << 10), + NDS32_PTR_PATTERN = (1 << 11), + NDS32_PTR_MULTIPLE = (1 << 12), + NDS32_GROUP = (1 << 13), + NDS32_SYM_DESC_MEM = (1 << 14) }; typedef struct nds32_relax_fixup_info @@ -295,6 +299,12 @@ enum nds32_relax_hint_type NDS32_RELAX_HINT_LA_PLT, NDS32_RELAX_HINT_LA_GOT, NDS32_RELAX_HINT_LA_GOTOFF, + NDS32_RELAX_HINT_TLS_START = 0x100, + NDS32_RELAX_HINT_TLS_LE_LS, + NDS32_RELAX_HINT_TLS_IE_LS, + NDS32_RELAX_HINT_TLS_IE_LA, + NDS32_RELAX_HINT_TLS_IEGP_LA, + NDS32_RELAX_HINT_TLS_DESC_LS, }; struct nds32_relax_hint_table diff --git a/gas/configure b/gas/configure index 85803e272d..27b6e8e8c6 100755 --- a/gas/configure +++ b/gas/configure @@ -12841,6 +12841,17 @@ _ACEOF ;; nds32) + # setup NDS32_LINUX_TOOLCHAIN definition + if test "linux" = $em; then + +$as_echo "#define NDS32_LINUX_TOOLCHAIN 1" >>confdefs.h + + else + +$as_echo "#define NDS32_LINUX_TOOLCHAIN 0" >>confdefs.h + + fi + # Decide BASELINE, REDUCED_REGS, FPU_DP_EXT, FPU_SP_EXT features # based on arch_name. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for default configuration of --with-arch" >&5 diff --git a/gas/configure.ac b/gas/configure.ac index c99f89b9ce..93faa4113d 100644 --- a/gas/configure.ac +++ b/gas/configure.ac @@ -446,6 +446,15 @@ changequote([,])dnl ;; nds32) + # setup NDS32_LINUX_TOOLCHAIN definition + if test "linux" = $em; then + AC_DEFINE(NDS32_LINUX_TOOLCHAIN, 1, + [Define value for nds32_linux_toolchain]) + else + AC_DEFINE(NDS32_LINUX_TOOLCHAIN, 0, + [Define default value for nds32_linux_toolchain]) + fi + # Decide BASELINE, REDUCED_REGS, FPU_DP_EXT, FPU_SP_EXT features # based on arch_name. AC_MSG_CHECKING(for default configuration of --with-arch) diff --git a/include/elf/nds32.h b/include/elf/nds32.h index ff8efbca66..72c5dfb798 100644 --- a/include/elf/nds32.h +++ b/include/elf/nds32.h @@ -146,7 +146,18 @@ START_RELOC_NUMBERS (elf_nds32_reloc_type) RELOC_NUMBER (R_NDS32_LONGJUMP5, 111) RELOC_NUMBER (R_NDS32_LONGJUMP6, 112) RELOC_NUMBER (R_NDS32_LONGJUMP7, 113) - /* Reserved numbers: 114-191. */ + /* Reserved numbers: 114. */ + /* TLS support */ + RELOC_NUMBER (R_NDS32_TLS_IE_LO12, 115) + RELOC_NUMBER (R_NDS32_TLS_IEGP_HI20, 116) + RELOC_NUMBER (R_NDS32_TLS_IEGP_LO12, 117) + RELOC_NUMBER (R_NDS32_TLS_IEGP_LO12S2, 118) + RELOC_NUMBER (R_NDS32_TLS_DESC, 119) + RELOC_NUMBER (R_NDS32_TLS_DESC_HI20, 120) + RELOC_NUMBER (R_NDS32_TLS_DESC_LO12, 121) + RELOC_NUMBER (R_NDS32_TLS_DESC_20, 122) + RELOC_NUMBER (R_NDS32_TLS_DESC_SDA17S2, 123) + /* Reserved numbers: 124-191. */ /* These used only for relaxations */ RELOC_NUMBER (R_NDS32_RELAX_ENTRY, 192) @@ -168,10 +179,17 @@ START_RELOC_NUMBERS (elf_nds32_reloc_type) RELOC_NUMBER (R_NDS32_DIFF_ULEB128, 208) RELOC_NUMBER (R_NDS32_DATA, 209) RELOC_NUMBER (R_NDS32_TRAN, 210) + /* TLS support */ RELOC_NUMBER (R_NDS32_TLS_LE_ADD, 211) RELOC_NUMBER (R_NDS32_TLS_LE_LS, 212) RELOC_NUMBER (R_NDS32_EMPTY, 213) - /* Reserved numbers: 214-220. */ + RELOC_NUMBER (R_NDS32_TLS_DESC_ADD, 214) + RELOC_NUMBER (R_NDS32_TLS_DESC_FUNC, 215) + RELOC_NUMBER (R_NDS32_TLS_DESC_CALL, 216) + RELOC_NUMBER (R_NDS32_TLS_DESC_MEM, 217) + RELOC_NUMBER (R_NDS32_RELAX_REMOVE, 218) + RELOC_NUMBER (R_NDS32_RELAX_GROUP, 219) + RELOC_NUMBER (R_NDS32_TLS_IEGP_LW, 220) RELOC_NUMBER (R_NDS32_LSI, 221) /* Reserved numbers: 222-255. */ diff --git a/ld/emultempl/nds32elf.em b/ld/emultempl/nds32elf.em index a5a11d4ad3..6fb5a485ca 100644 --- a/ld/emultempl/nds32elf.em +++ b/ld/emultempl/nds32elf.em @@ -31,6 +31,7 @@ static int relax_fp_as_gp = 1; /* --mrelax-omit-fp */ static int eliminate_gc_relocs = 0; /* --meliminate-gc-relocs */ static FILE *sym_ld_script = NULL; /* --mgen-symbol-ld-script= */ static int hyper_relax = 1; /* --mhyper-relax */ +static int tls_desc_trampoline = 0; /* --m[no]tlsdesc-trampoline. */ /* Disable if linking a dynamically linked executable. */ static int load_store_relax = 1; @@ -53,13 +54,15 @@ nds32_elf_create_output_section_statements (void) eliminate_gc_relocs, sym_ld_script, hyper_relax, + tls_desc_trampoline, load_store_relax); } static void nds32_elf_after_parse (void) { - if (bfd_link_relocatable (&link_info)) + if (bfd_link_relocatable (&link_info) + || bfd_link_pic (&link_info)) DISABLE_RELAXATION; if (!RELAXATION_ENABLED) @@ -138,12 +141,16 @@ PARSE_AND_LIST_PROLOGUE=' #define OPTION_NO_REDUCE_FP_UPDATE (OPTION_BASELINE + 5) #define OPTION_EXPORT_SYMBOLS (OPTION_BASELINE + 6) #define OPTION_HYPER_RELAX (OPTION_BASELINE + 7) +#define OPTION_TLSDESC_TRAMPOLINE (OPTION_BASELINE + 8) +#define OPTION_NO_TLSDESC_TRAMPOLINE (OPTION_BASELINE + 9) ' PARSE_AND_LIST_LONGOPTS=' { "mfp-as-gp", no_argument, NULL, OPTION_FP_AS_GP}, { "mno-fp-as-gp", no_argument, NULL, OPTION_NO_FP_AS_GP}, { "mexport-symbols", required_argument, NULL, OPTION_EXPORT_SYMBOLS}, { "mhyper-relax", required_argument, NULL, OPTION_HYPER_RELAX}, + { "mtlsdesc-trampoline", no_argument, NULL, OPTION_TLSDESC_TRAMPOLINE}, + { "mno-tlsdesc-trampoline", no_argument, NULL, OPTION_NO_TLSDESC_TRAMPOLINE}, /* These are deprecated options. Remove them in the future. */ { "mrelax-reduce-fp-update", no_argument, NULL, OPTION_REDUCE_FP_UPDATE}, { "mrelax-no-reduce-fp-update", no_argument, NULL, OPTION_NO_REDUCE_FP_UPDATE}, @@ -160,6 +167,8 @@ PARSE_AND_LIST_OPTIONS=' --mexport-symbols=FILE Exporting symbols in linker script\n")); fprintf (file, _("\ --mhyper-relax=level Adjust relax level (low|medium|high). default: medium\n")); + fprintf (file, _("\ + --m[no-]tlsdesc-trampoline Disable/enable TLS DESC trampoline\n")); ' PARSE_AND_LIST_ARGS_CASES=' case OPTION_BASELINE: @@ -203,6 +212,12 @@ PARSE_AND_LIST_ARGS_CASES=' einfo (_("%P: valid arguments to --mhyper-relax=(low|medium|high)\n")); break; + case OPTION_TLSDESC_TRAMPOLINE: + tls_desc_trampoline = 1; + break; + case OPTION_NO_TLSDESC_TRAMPOLINE: + tls_desc_trampoline = 0; + break; ' LDEMUL_AFTER_OPEN=nds32_elf_after_open LDEMUL_AFTER_PARSE=nds32_elf_after_parse -- 2.19.0