aboutsummaryrefslogtreecommitdiff
path: root/bfd/elf32-arm.c
diff options
context:
space:
mode:
authorNathan Sidwell <nathan@codesourcery.com>2011-01-10 08:40:19 +0000
committerNathan Sidwell <nathan@codesourcery.com>2011-01-10 08:40:19 +0000
commit0855e32bf582698d8995b7e85e144ef105d71c82 (patch)
tree75e1748ec86d1c55cb57d5d4efb62d8d696ec6d0 /bfd/elf32-arm.c
parent9a153e0b6fc93873721787f23edceb272ac87994 (diff)
downloadgdb-0855e32bf582698d8995b7e85e144ef105d71c82.zip
gdb-0855e32bf582698d8995b7e85e144ef105d71c82.tar.gz
gdb-0855e32bf582698d8995b7e85e144ef105d71c82.tar.bz2
bfd/
* reloc.c (BFD_RELOC_ARM_TLS_GOTDESC, BFD_RELOC_ARM_TLS_CALL, BFD_RELOC_ARM_THM_TLS_CALL, BFD_RELOC_ARM_TLS_DESCSEQ, BFD_RELOC_ARM_THM_TLS_DESCSEQ, BFD_RELOC_ARM_TLS_DESC): New relocations. * libbfd.h: Rebuilt. * bfd-in2.h: Rebuilt. * elf32-arm.c (elf32_arm_howto_table_1): Add new relocations. (elf32_arm_reloc_map): Likewise. (tls_trampoline, dl_tlsdesc_lazy_trampoline): New PLT templates. (elf32_arm_stub_long_branch_any_tls_pic, elf32_arm_stub_long_branch_v4t_thumb_tls_pic): New stub templates. (DEF_STUBS): Add new stubs. (struct_elf_arm_obj_data): Add local_tlsdesc_gotent field. (elf32_arm_local_tlsdesc_gotent): New. (GOT_TLS_GDESC): New mask. (GOT_TLS_GD_ANY): Define. (struct elf32_arm_link_hash_entry): Add tlsdesc_got field. (elf32_arm_compute_jump_table_size): New. (struct elf32_arm_link_hash_table): Add next_tls_desc_index, num_tls_desc, dt_tlsdesc_plt, dt_tlsdesc_got, tls_trampoline, sgotplt_jump_table_size fields. (elf32_arm_link_hash_newfunc): Initialize tlsdesc_got field. (elf32_arm_link_hash_table_create): Initialize new fields. (arm_type_of_stub): Check TLS desc relocs too. (elf32_arm_stub_name): TLS desc relocs can be shared. (elf32_arm_tls_transition): Determine relaxation. (arm_stub_required_alignment): Add tls stubs. (elf32_arm_size_stubs): Likewise. (elf32_arm_tls_relax): Perform TLS relaxing. (elf32_arm_final_link_relocate): Process TLS DESC relocations. (IS_ARM_TLS_GNU_RELOC): New. (IS_ARM_TLS_RELOC): Use it. (elf32_arm_relocate_section): Perform TLS relaxing. (elf32_arm_check_relocs): Anticipate TLS relaxing, process tls desc relocations. (allocate_dynrelocs): Allocate tls desc relcoations. (elf32_arm_output_arch_local_syms): Emit tls trampoline mapping symbols. (elf32_arm_size_dynamic_sections): Allocate tls trampolines and got slots. (elf32_arm_always_size_sections): New. Create _TLS_MODULE_BASE symbol. (elf32_arm_finish_dynamic_symbol): Adjust. (arm_put_trampoline): New. (elf32_arm_finish_dynamic_sections): Emit new dynamic tags and tls trampolines. (elf_backend_always_size_sections): Define. include/elf/ * arm.h (R_ARM_TLS_DESC, R_ARM_TLS_GOTDESC, R_ARM_TLS_CALL, R_ARM_TLS_DESCSEQ, T_ARM_THM_TLS_CALL, R_ARM_THM_TLS_DESCSEQ): New relocations. gas/ * doc/c-arm.texi: Document TLSDESC and TLSCALL relocations, and .tlsdescseq directive. * config/tc-arm.c (arm_typed_reg_parse): Check for potential reloc following a symbol. (s_arm_tls_descseq): New directive. (md_pseudo_table): Add it. (encode_branch): Allow TLS_CALL relocs too. (do_t_blx, do_t_branch23): Use encode_branch. (reloc_names): Add tlsdesc and tlscall. (md_apply_fix): Process tls desc relocations. (tc_gen_reloc): Likewise. (arm_fix_adjustable): Likewise. gas/testsuite/ * gas/arm/tls.s: Add tlsdesc tests. * gas/arm/tls.d: Adjust. ld/testsuite/ * ld-arm/arm-elf.exp: Added tests for new TLS handling relocations. * ld-arm/tls-descrelax-be32.d: New. * ld-arm/tls-descrelax-be32.s: New. * ld-arm/tls-descrelax-be8.d: New. * ld-arm/tls-descrelax-be8.s: New. * ld-arm/tls-descrelax-v7.d: New. * ld-arm/tls-descrelax-v7.s: New. * ld-arm/tls-descrelax.d: New. * ld-arm/tls-descrelax.s: New. * ld-arm/tls-descseq.d: New. * ld-arm/tls-descseq.r: New. * ld-arm/tls-descseq.s: New. * ld-arm/tls-gdesc-got.d: New. * ld-arm/tls-gdesc-got.s: New. * ld-arm/tls-gdesc-nlazy.g: New. * ld-arm/tls-gdesc-nlazy.s: New. * ld-arm/tls-gdesc.d: New. * ld-arm/tls-gdesc.r: New. * ld-arm/tls-gdesc.s: New. * ld-arm/tls-gdierelax.d: New. * ld-arm/tls-gdierelax.s: New. * ld-arm/tls-gdierelax2.d: New. * ld-arm/tls-gdierelax2.s: New. * ld-arm/tls-gdlerelax.d: New. * ld-arm/tls-gdlerelax.s: New. * ld-arm/tls-lib-loc.d: New. * ld-arm/tls-lib-loc.r: New. * ld-arm/tls-lib-loc.s: New. * ld-arm/tls-longplt-lib.d: New. * ld-arm/tls-longplt-lib.s: New. * ld-arm/tls-longplt.d: New. * ld-arm/tls-longplt.s: New. * ld-arm/tls-mixed.r: New. * ld-arm/tls-mixed.s: New. * ld-arm/tls-thumb1.d: New. * ld-arm/tls-thumb1.s: New. * ld-arm/arm-elf.exp: New.
Diffstat (limited to 'bfd/elf32-arm.c')
-rw-r--r--bfd/elf32-arm.c1017
1 files changed, 935 insertions, 82 deletions
diff --git a/bfd/elf32-arm.c b/bfd/elf32-arm.c
index 7c8eb68..ea1e485 100644
--- a/bfd/elf32-arm.c
+++ b/bfd/elf32-arm.c
@@ -261,18 +261,18 @@ static reloc_howto_type elf32_arm_howto_table_1[] =
0xffffffff, /* dst_mask */
FALSE), /* pcrel_offset */
- HOWTO (R_ARM_SWI24, /* type */
+ HOWTO (R_ARM_TLS_DESC, /* type */
0, /* rightshift */
- 0, /* size (0 = byte, 1 = short, 2 = long) */
- 0, /* bitsize */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
FALSE, /* pc_relative */
0, /* bitpos */
- complain_overflow_signed,/* complain_on_overflow */
+ complain_overflow_bitfield,/* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
- "R_ARM_SWI24", /* name */
+ "R_ARM_TLS_DESC", /* name */
FALSE, /* partial_inplace */
- 0x00000000, /* src_mask */
- 0x00000000, /* dst_mask */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
FALSE), /* pcrel_offset */
HOWTO (R_ARM_THM_SWI8, /* type */
@@ -1352,10 +1352,61 @@ static reloc_howto_type elf32_arm_howto_table_1[] =
0x040f70ff, /* dst_mask */
FALSE), /* pcrel_offset */
- EMPTY_HOWTO (90), /* Unallocated. */
- EMPTY_HOWTO (91),
- EMPTY_HOWTO (92),
- EMPTY_HOWTO (93),
+ HOWTO (R_ARM_TLS_GOTDESC, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_bitfield,/* complain_on_overflow */
+ NULL, /* special_function */
+ "R_ARM_TLS_GOTDESC", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ HOWTO (R_ARM_TLS_CALL, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 24, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont,/* complain_on_overflow */
+ bfd_elf_generic_reloc, /* special_function */
+ "R_ARM_TLS_CALL", /* name */
+ FALSE, /* partial_inplace */
+ 0x00ffffff, /* src_mask */
+ 0x00ffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ HOWTO (R_ARM_TLS_DESCSEQ, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 0, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_bitfield,/* complain_on_overflow */
+ bfd_elf_generic_reloc, /* special_function */
+ "R_ARM_TLS_DESCSEQ", /* name */
+ FALSE, /* partial_inplace */
+ 0x00000000, /* src_mask */
+ 0x00000000, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ HOWTO (R_ARM_THM_TLS_CALL, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 24, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont,/* complain_on_overflow */
+ bfd_elf_generic_reloc, /* special_function */
+ "R_ARM_THM_TLS_CALL", /* name */
+ FALSE, /* partial_inplace */
+ 0x07ff07ff, /* src_mask */
+ 0x07ff07ff, /* dst_mask */
+ FALSE), /* pcrel_offset */
HOWTO (R_ARM_PLT32_ABS, /* type */
0, /* rightshift */
@@ -1599,6 +1650,38 @@ static reloc_howto_type elf32_arm_howto_table_1[] =
0x00000fff, /* src_mask */
0x00000fff, /* dst_mask */
FALSE), /* pcrel_offset */
+
+ EMPTY_HOWTO (112),
+ EMPTY_HOWTO (113),
+ EMPTY_HOWTO (114),
+ EMPTY_HOWTO (115),
+ EMPTY_HOWTO (116),
+ EMPTY_HOWTO (117),
+ EMPTY_HOWTO (118),
+ EMPTY_HOWTO (119),
+ EMPTY_HOWTO (120),
+ EMPTY_HOWTO (121),
+ EMPTY_HOWTO (122),
+ EMPTY_HOWTO (123),
+ EMPTY_HOWTO (124),
+ EMPTY_HOWTO (125),
+ EMPTY_HOWTO (126),
+ EMPTY_HOWTO (127),
+ EMPTY_HOWTO (128),
+
+ HOWTO (R_ARM_THM_TLS_DESCSEQ, /* type */
+ 0, /* rightshift */
+ 1, /* size (0 = byte, 1 = short, 2 = long) */
+ 0, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_bitfield,/* complain_on_overflow */
+ bfd_elf_generic_reloc, /* special_function */
+ "R_ARM_THM_TLS_DESCSEQ",/* name */
+ FALSE, /* partial_inplace */
+ 0x00000000, /* src_mask */
+ 0x00000000, /* dst_mask */
+ FALSE), /* pcrel_offset */
};
/* 112-127 private relocations
@@ -1730,6 +1813,12 @@ static const struct elf32_arm_reloc_map elf32_arm_reloc_map[] =
{BFD_RELOC_ARM_PREL31, R_ARM_PREL31},
{BFD_RELOC_ARM_TARGET2, R_ARM_TARGET2},
{BFD_RELOC_ARM_PLT32, R_ARM_PLT32},
+ {BFD_RELOC_ARM_TLS_GOTDESC, R_ARM_TLS_GOTDESC},
+ {BFD_RELOC_ARM_TLS_CALL, R_ARM_TLS_CALL},
+ {BFD_RELOC_ARM_THM_TLS_CALL, R_ARM_THM_TLS_CALL},
+ {BFD_RELOC_ARM_TLS_DESCSEQ, R_ARM_TLS_DESCSEQ},
+ {BFD_RELOC_ARM_THM_TLS_DESCSEQ, R_ARM_THM_TLS_DESCSEQ},
+ {BFD_RELOC_ARM_TLS_DESC, R_ARM_TLS_DESC},
{BFD_RELOC_ARM_TLS_GD32, R_ARM_TLS_GD32},
{BFD_RELOC_ARM_TLS_LDO32, R_ARM_TLS_LDO32},
{BFD_RELOC_ARM_TLS_LDM32, R_ARM_TLS_LDM32},
@@ -1912,6 +2001,26 @@ typedef unsigned short int insn16;
section. */
#define ELF_DYNAMIC_INTERPRETER "/usr/lib/ld.so.1"
+static const unsigned long tls_trampoline [] =
+ {
+ 0xe08e0000, /* add r0, lr, r0 */
+ 0xe5901004, /* ldr r1, [r0,#4] */
+ 0xe12fff11, /* bx r1 */
+ };
+
+static const unsigned long dl_tlsdesc_lazy_trampoline [] =
+ {
+ 0xe52d2004, /* push {r2} */
+ 0xe59f200c, /* ldr r2, [pc, #3f - . - 8] */
+ 0xe59f100c, /* ldr r1, [pc, #4f - . - 8] */
+ 0xe79f2002, /* 1: ldr r2, [pc, r2] */
+ 0xe081100f, /* 2: add r1, pc */
+ 0xe12fff12, /* bx r2 */
+ 0x00000014, /* 3: .word _GLOBAL_OFFSET_TABLE_ - 1b - 8
+ + dl_tlsdesc_lazy_resolver(GOT) */
+ 0x00000018, /* 4: .word _GLOBAL_OFFSET_TABLE_ - 2b - 8 */
+ };
+
#ifdef FOUR_WORD_PLT
/* The first entry in a procedure linkage table looks like
@@ -2167,6 +2276,26 @@ static const insn_sequence elf32_arm_stub_long_branch_v4t_thumb_thumb_pic[] =
DATA_WORD(0, R_ARM_REL32, 0), /* dcd R_ARM_REL32(X) */
};
+/* Thumb2/ARM -> TLS trampoline. Lowest common denominator, which is a
+ long PIC stub. We can use r1 as a scratch -- and cannot use ip. */
+static const insn_sequence elf32_arm_stub_long_branch_any_tls_pic[] =
+{
+ ARM_INSN(0xe59f1000), /* ldr r1, [pc] */
+ ARM_INSN(0xe08ff001), /* add pc, pc, r1 */
+ DATA_WORD(0, R_ARM_REL32, -4), /* dcd R_ARM_REL32(X-4) */
+};
+
+/* V4T Thumb -> TLS trampoline. lowest common denominator, which is a
+ long PIC stub. We can use r1 as a scratch -- and cannot use ip. */
+static const insn_sequence elf32_arm_stub_long_branch_v4t_thumb_tls_pic[] =
+{
+ THUMB16_INSN(0x4778), /* bx pc */
+ THUMB16_INSN(0x46c0), /* nop */
+ ARM_INSN(0xe59f1000), /* ldr r1, [pc, #0] */
+ ARM_INSN(0xe081f00f), /* add pc, r1, pc */
+ DATA_WORD(0, R_ARM_REL32, -4), /* dcd R_ARM_REL32(X) */
+};
+
/* Cortex-A8 erratum-workaround stubs. */
/* Stub used for conditional branches (which may be beyond +/-1MB away, so we
@@ -2218,6 +2347,8 @@ static const insn_sequence elf32_arm_stub_a8_veneer_blx[] =
DEF_STUB(long_branch_v4t_arm_thumb_pic) \
DEF_STUB(long_branch_v4t_thumb_arm_pic) \
DEF_STUB(long_branch_thumb_only_pic) \
+ DEF_STUB(long_branch_any_tls_pic) \
+ DEF_STUB(long_branch_v4t_thumb_tls_pic) \
DEF_STUB(a8_veneer_b_cond) \
DEF_STUB(a8_veneer_b) \
DEF_STUB(a8_veneer_bl) \
@@ -2426,6 +2557,9 @@ struct elf_arm_obj_tdata
/* tls_type for each local got entry. */
char *local_got_tls_type;
+ /* GOTPLT entries for TLS descriptors. */
+ bfd_vma *local_tlsdesc_gotent;
+
/* Zero to warn when linking objects with incompatible enum sizes. */
int no_enum_size_warning;
@@ -2439,6 +2573,9 @@ struct elf_arm_obj_tdata
#define elf32_arm_local_got_tls_type(bfd) \
(elf_arm_tdata (bfd)->local_got_tls_type)
+#define elf32_arm_local_tlsdesc_gotent(bfd) \
+ (elf_arm_tdata (bfd)->local_tlsdesc_gotent)
+
#define is_arm_elf(bfd) \
(bfd_get_flavour (bfd) == bfd_target_elf_flavour \
&& elf_tdata (bfd) != NULL \
@@ -2478,8 +2615,14 @@ struct elf32_arm_link_hash_entry
#define GOT_NORMAL 1
#define GOT_TLS_GD 2
#define GOT_TLS_IE 4
+#define GOT_TLS_GDESC 8
+#define GOT_TLS_GD_ANY_P(type) ((type & GOT_TLS_GD) || (type & GOT_TLS_GDESC))
unsigned char tls_type;
+ /* Offset of the GOTPLT entry reserved for the TLS descriptor,
+ starting at the end of the jump table. */
+ bfd_vma tlsdesc_got;
+
/* The symbol marking the real symbol location for exported thumb
symbols with Arm stubs. */
struct elf_link_hash_entry *export_glue;
@@ -2516,6 +2659,9 @@ struct map_stub
asection *stub_sec;
};
+#define elf32_arm_compute_jump_table_size(htab) \
+ ((htab)->next_tls_desc_index * 4)
+
/* ARM ELF linker hash table. */
struct elf32_arm_link_hash_table
{
@@ -2594,6 +2740,12 @@ struct elf32_arm_link_hash_table
/* True if the target uses REL relocations. */
int use_rel;
+ /* The index of the next unused R_ARM_TLS_DESC slot in .rel.plt. */
+ bfd_vma next_tls_desc_index;
+
+ /* How many R_ARM_TLS_DESC relocations were generated so far. */
+ bfd_vma num_tls_desc;
+
/* Short-cuts to get to dynamic linker sections. */
asection *sdynbss;
asection *srelbss;
@@ -2601,6 +2753,19 @@ struct elf32_arm_link_hash_table
/* The (unloaded but important) VxWorks .rela.plt.unloaded section. */
asection *srelplt2;
+ /* 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_arm_trampoline. */
+ bfd_vma tls_trampoline;
+
/* Data for R_ARM_TLS_LDM32 relocations. */
union
{
@@ -2614,6 +2779,10 @@ struct elf32_arm_link_hash_table
/* For convenience in allocate_dynrelocs. */
bfd * obfd;
+ /* 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;
+
/* The stub hash table. */
struct bfd_hash_table stub_hash_table;
@@ -2663,6 +2832,7 @@ elf32_arm_link_hash_newfunc (struct bfd_hash_entry * entry,
{
ret->dyn_relocs = NULL;
ret->tls_type = GOT_UNKNOWN;
+ ret->tlsdesc_got = (bfd_vma) -1;
ret->plt_thumb_refcount = 0;
ret->plt_maybe_thumb_refcount = 0;
ret->plt_got_offset = -1;
@@ -2877,6 +3047,11 @@ elf32_arm_link_hash_table_create (bfd *abfd)
ret->sdynbss = NULL;
ret->srelbss = NULL;
ret->srelplt2 = NULL;
+ ret->dt_tlsdesc_plt = 0;
+ ret->dt_tlsdesc_got = 0;
+ ret->tls_trampoline = 0;
+ ret->next_tls_desc_index = 0;
+ ret->num_tls_desc = 0;
ret->thumb_glue_size = 0;
ret->arm_glue_size = 0;
ret->bx_glue_size = 0;
@@ -3075,7 +3250,8 @@ arm_type_of_stub (struct bfd_link_info *info,
branch_offset = (bfd_signed_vma)(destination - location);
- if (r_type == R_ARM_THM_CALL || r_type == R_ARM_THM_JUMP24)
+ if (r_type == R_ARM_THM_CALL || r_type == R_ARM_THM_JUMP24
+ || r_type == R_ARM_THM_TLS_CALL)
{
/* Handle cases where:
- this call goes too far (different Thumb/Thumb2 max
@@ -3092,7 +3268,8 @@ arm_type_of_stub (struct bfd_link_info *info,
&& (branch_offset > THM2_MAX_FWD_BRANCH_OFFSET
|| (branch_offset < THM2_MAX_BWD_BRANCH_OFFSET)))
|| ((st_type != STT_ARM_TFUNC)
- && (((r_type == R_ARM_THM_CALL) && !globals->use_blx)
+ && (((r_type == R_ARM_THM_CALL
+ || r_type == R_ARM_THM_TLS_CALL) && !globals->use_blx)
|| (r_type == R_ARM_THM_JUMP24))
&& !use_plt))
{
@@ -3143,18 +3320,21 @@ arm_type_of_stub (struct bfd_link_info *info,
sym_sec->owner, input_bfd, name);
}
- stub_type = (info->shared | globals->pic_veneer)
+ stub_type =
+ (info->shared | globals->pic_veneer)
/* PIC stubs. */
- ? ((globals->use_blx
- && (r_type ==R_ARM_THM_CALL))
- /* V5T and above. */
- ? arm_stub_long_branch_any_arm_pic
- /* V4T PIC stub. */
- : arm_stub_long_branch_v4t_thumb_arm_pic)
+ ? (r_type == R_ARM_THM_TLS_CALL
+ /* TLS PIC stubs */
+ ? (globals->use_blx ? arm_stub_long_branch_any_tls_pic
+ : arm_stub_long_branch_v4t_thumb_tls_pic)
+ : ((globals->use_blx && r_type == R_ARM_THM_CALL)
+ /* V5T PIC and above. */
+ ? arm_stub_long_branch_any_arm_pic
+ /* V4T PIC stub. */
+ : arm_stub_long_branch_v4t_thumb_arm_pic))
/* non-PIC stubs. */
- : ((globals->use_blx
- && (r_type ==R_ARM_THM_CALL))
+ : ((globals->use_blx && r_type == R_ARM_THM_CALL)
/* V5T and above. */
? arm_stub_long_branch_any_any
/* V4T. */
@@ -3170,7 +3350,8 @@ arm_type_of_stub (struct bfd_link_info *info,
}
else if (r_type == R_ARM_CALL
|| r_type == R_ARM_JUMP24
- || r_type == R_ARM_PLT32)
+ || r_type == R_ARM_PLT32
+ || r_type == R_ARM_TLS_CALL)
{
if (st_type == STT_ARM_TFUNC)
{
@@ -3190,7 +3371,7 @@ arm_type_of_stub (struct bfd_link_info *info,
the mode change (bit 24 (H) of BLX encoding). */
if (branch_offset > (ARM_MAX_FWD_BRANCH_OFFSET + 2)
|| (branch_offset < ARM_MAX_BWD_BRANCH_OFFSET)
- || ((r_type == R_ARM_CALL) && !globals->use_blx)
+ || (r_type == R_ARM_CALL && !globals->use_blx)
|| (r_type == R_ARM_JUMP24)
|| (r_type == R_ARM_PLT32))
{
@@ -3216,9 +3397,13 @@ arm_type_of_stub (struct bfd_link_info *info,
if (branch_offset > ARM_MAX_FWD_BRANCH_OFFSET
|| (branch_offset < ARM_MAX_BWD_BRANCH_OFFSET))
{
- stub_type = (info->shared | globals->pic_veneer)
+ stub_type =
+ (info->shared | globals->pic_veneer)
/* PIC stubs. */
- ? arm_stub_long_branch_any_arm_pic
+ ? (r_type == R_ARM_TLS_CALL
+ /* TLS PIC Stub */
+ ? arm_stub_long_branch_any_tls_pic
+ : arm_stub_long_branch_any_arm_pic)
/* non-PIC stubs. */
: arm_stub_long_branch_any_any;
}
@@ -3263,7 +3448,9 @@ elf32_arm_stub_name (const asection *input_section,
sprintf (stub_name, "%08x_%x:%x+%x_%d",
input_section->id & 0xffffffff,
sym_sec->id & 0xffffffff,
- (int) ELF32_R_SYM (rel->r_info) & 0xffffffff,
+ ELF32_R_TYPE (rel->r_info) == R_ARM_TLS_CALL
+ || ELF32_R_TYPE (rel->r_info) == R_ARM_THM_TLS_CALL
+ ? 0 : (int) ELF32_R_SYM (rel->r_info) & 0xffffffff,
(int) rel->r_addend & 0xffffffff,
(int) stub_type);
}
@@ -3426,6 +3613,32 @@ put_thumb_insn (struct elf32_arm_link_hash_table * htab,
bfd_putb16 (val, ptr);
}
+/* If it's possible to change R_TYPE to a more efficient access
+ model, return the new reloc type. */
+
+static unsigned
+elf32_arm_tls_transition (struct bfd_link_info *info, int r_type,
+ struct elf_link_hash_entry *h)
+{
+ int is_local = (h == NULL);
+
+ if (info->shared || (h && h->root.type == bfd_link_hash_undefweak))
+ return r_type;
+
+ /* We do not support relaxations for Old TLS models. */
+ switch (r_type)
+ {
+ case R_ARM_TLS_GOTDESC:
+ case R_ARM_TLS_CALL:
+ case R_ARM_THM_TLS_CALL:
+ case R_ARM_TLS_DESCSEQ:
+ case R_ARM_THM_TLS_DESCSEQ:
+ return is_local ? R_ARM_TLS_LE32 : R_ARM_TLS_IE32;
+ }
+
+ return r_type;
+}
+
static bfd_reloc_status_type elf32_arm_final_link_relocate
(reloc_howto_type *, bfd *, bfd *, asection *, bfd_byte *,
Elf_Internal_Rela *, bfd_vma, struct bfd_link_info *, asection *,
@@ -3453,6 +3666,8 @@ arm_stub_required_alignment (enum elf32_arm_stub_type stub_type)
case arm_stub_long_branch_v4t_arm_thumb_pic:
case arm_stub_long_branch_v4t_thumb_arm_pic:
case arm_stub_long_branch_thumb_only_pic:
+ case arm_stub_long_branch_any_tls_pic:
+ case arm_stub_long_branch_v4t_thumb_tls_pic:
case arm_stub_a8_veneer_blx:
return 4;
@@ -4419,15 +4634,30 @@ elf32_arm_size_stubs (bfd *output_bfd,
free (internal_relocs);
goto error_ret_free_local;
}
-
- /* Only look for stubs on branch instructions. */
+
+ hash = NULL;
+ if (r_indx >= symtab_hdr->sh_info)
+ hash = elf32_arm_hash_entry
+ (elf_sym_hashes (input_bfd)
+ [r_indx - symtab_hdr->sh_info]);
+
+ /* Only look for stubs on branch instructions, or
+ non-relaxed TLSCALL */
if ((r_type != (unsigned int) R_ARM_CALL)
&& (r_type != (unsigned int) R_ARM_THM_CALL)
&& (r_type != (unsigned int) R_ARM_JUMP24)
&& (r_type != (unsigned int) R_ARM_THM_JUMP19)
&& (r_type != (unsigned int) R_ARM_THM_XPC22)
&& (r_type != (unsigned int) R_ARM_THM_JUMP24)
- && (r_type != (unsigned int) R_ARM_PLT32))
+ && (r_type != (unsigned int) R_ARM_PLT32)
+ && !((r_type == (unsigned int) R_ARM_TLS_CALL
+ || r_type == (unsigned int) R_ARM_THM_TLS_CALL)
+ && r_type == elf32_arm_tls_transition
+ (info, r_type, &hash->root)
+ && ((hash ? hash->tls_type
+ : (elf32_arm_local_got_tls_type
+ (input_bfd)[r_indx]))
+ & GOT_TLS_GDESC) != 0))
continue;
/* Now determine the call target, its name, value,
@@ -4435,9 +4665,21 @@ elf32_arm_size_stubs (bfd *output_bfd,
sym_sec = NULL;
sym_value = 0;
destination = 0;
- hash = NULL;
sym_name = NULL;
- if (r_indx < symtab_hdr->sh_info)
+
+ if (r_type == (unsigned int) R_ARM_TLS_CALL
+ || r_type == (unsigned int) R_ARM_THM_TLS_CALL)
+ {
+ /* A non-relaxed TLS call. The target is the
+ plt-resident trampoline and nothing to do
+ with the symbol. */
+ BFD_ASSERT (htab->tls_trampoline > 0);
+ sym_sec = htab->root.splt;
+ sym_value = htab->tls_trampoline;
+ hash = 0;
+ st_type = STT_FUNC;
+ }
+ else if (!hash)
{
/* It's a local symbol. */
Elf_Internal_Sym *sym;
@@ -4485,12 +4727,6 @@ elf32_arm_size_stubs (bfd *output_bfd,
else
{
/* It's an external symbol. */
- int e_indx;
-
- e_indx = r_indx - symtab_hdr->sh_info;
- hash = ((struct elf32_arm_link_hash_entry *)
- elf_sym_hashes (input_bfd)[e_indx]);
-
while (hash->root.root.type == bfd_link_hash_indirect
|| hash->root.root.type == bfd_link_hash_warning)
hash = ((struct elf32_arm_link_hash_entry *)
@@ -6743,6 +6979,148 @@ elf32_arm_abs12_reloc (bfd *abfd, void *data, bfd_vma value)
return bfd_reloc_ok;
}
+/* Handle TLS relaxations. Relaxing is possible for symbols that use
+ R_ARM_GOTDESC, R_ARM_{,THM_}TLS_CALL or
+ R_ARM_{,THM_}TLS_DESCSEQ relocations, during a static link.
+
+ Return bfd_reloc_ok if we're done, bfd_reloc_continue if the caller
+ is to then call final_link_relocate. Return other values in the
+ case of error. */
+
+
+static bfd_reloc_status_type
+elf32_arm_tls_relax (struct elf32_arm_link_hash_table *globals,
+ bfd *input_bfd, asection *input_sec, bfd_byte *contents,
+ Elf_Internal_Rela *rel, unsigned long is_local)
+{
+ unsigned long insn;
+
+ switch (ELF32_R_TYPE (rel->r_info))
+ {
+ default:
+ return bfd_reloc_notsupported;
+
+ case R_ARM_TLS_GOTDESC:
+ if (is_local)
+ insn = 0;
+ else
+ {
+ insn = bfd_get_32 (input_bfd, contents + rel->r_offset);
+ if (insn & 1)
+ insn -= 5; /* THUMB */
+ else
+ insn -= 8; /* ARM */
+ }
+ bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
+ return bfd_reloc_continue;
+
+ case R_ARM_THM_TLS_DESCSEQ:
+ /* Thumb insn. */
+ insn = bfd_get_16 (input_bfd, contents + rel->r_offset);
+ if ((insn & 0xff78) == 0x4478) /* add rx, pc */
+ {
+ if (is_local)
+ /* nop */
+ bfd_put_16 (input_bfd, 0x46c0, contents + rel->r_offset);
+ }
+ else if ((insn & 0xffc0) == 0x6840) /* ldr rx,[ry,#4] */
+ {
+ if (is_local)
+ /* nop */
+ bfd_put_16 (input_bfd, 0x46c0, contents + rel->r_offset);
+ else
+ /* ldr rx,[ry] */
+ bfd_put_16 (input_bfd, insn & 0xf83f, contents + rel->r_offset);
+ }
+ else if ((insn & 0xff87) == 0x4780) /* blx rx */
+ {
+ if (is_local)
+ /* nop */
+ bfd_put_16 (input_bfd, 0x46c0, contents + rel->r_offset);
+ else
+ /* mov r0, rx */
+ bfd_put_16 (input_bfd, 0x4600 | (insn & 0x78),
+ contents + rel->r_offset);
+ }
+ else
+ {
+ if ((insn & 0xf000) == 0xf000 || (insn & 0xf800) == 0xe800)
+ /* It's a 32 bit instruction, fetch the rest of it for
+ error generation. */
+ insn = (insn << 16)
+ | bfd_get_16 (input_bfd, contents + rel->r_offset + 2);
+ (*_bfd_error_handler)
+ (_("%B(%A+0x%lx):unexpected Thumb instruction '0x%x' in TLS trampoline"),
+ input_bfd, input_sec, (unsigned long)rel->r_offset, insn);
+ return bfd_reloc_notsupported;
+ }
+ break;
+
+ case R_ARM_TLS_DESCSEQ:
+ /* arm insn. */
+ insn = bfd_get_32 (input_bfd, contents + rel->r_offset);
+ if ((insn & 0xffff0ff0) == 0xe08f0000) /* add rx,pc,ry */
+ {
+ if (is_local)
+ /* mov rx, ry */
+ bfd_put_32 (input_bfd, 0xe1a00000 | (insn & 0xffff),
+ contents + rel->r_offset);
+ }
+ else if ((insn & 0xfff00fff) == 0xe5900004) /* ldr rx,[ry,#4]*/
+ {
+ if (is_local)
+ /* nop */
+ bfd_put_32 (input_bfd, 0xe1a00000, contents + rel->r_offset);
+ else
+ /* ldr rx,[ry] */
+ bfd_put_32 (input_bfd, insn & 0xfffff000,
+ contents + rel->r_offset);
+ }
+ else if ((insn & 0xfffffff0) == 0xe12fff30) /* blx rx */
+ {
+ if (is_local)
+ /* nop */
+ bfd_put_32 (input_bfd, 0xe1a00000, contents + rel->r_offset);
+ else
+ /* mov r0, rx */
+ bfd_put_32 (input_bfd, 0xe1a00000 | (insn & 0xf),
+ contents + rel->r_offset);
+ }
+ else
+ {
+ (*_bfd_error_handler)
+ (_("%B(%A+0x%lx):unexpected ARM instruction '0x%x' in TLS trampoline"),
+ input_bfd, input_sec, (unsigned long)rel->r_offset, insn);
+ return bfd_reloc_notsupported;
+ }
+ break;
+
+ case R_ARM_TLS_CALL:
+ /* GD->IE relaxation, turn the instruction into 'nop' or
+ 'ldr r0, [pc,r0]' */
+ insn = is_local ? 0xe1a00000 : 0xe79f0000;
+ bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
+ break;
+
+ case R_ARM_THM_TLS_CALL:
+ /* GD->IE relaxation */
+ if (!is_local)
+ /* add r0,pc; ldr r0, [r0] */
+ insn = 0x44786800;
+ else if (arch_has_thumb2_nop (globals))
+ /* nop.w */
+ insn = 0xf3af8000;
+ else
+ /* nop; nop */
+ insn = 0xbf00bf00;
+
+ bfd_put_16 (input_bfd, insn >> 16, contents + rel->r_offset);
+ bfd_put_16 (input_bfd, insn & 0xffff, contents + rel->r_offset + 2);
+ break;
+ }
+ return bfd_reloc_ok;
+}
+
/* For a given value of n, calculate the value of G_n as required to
deal with group relocations. We return it in the form of an
encoded constant-and-rotation, together with the final residual. If n is
@@ -6834,6 +7212,7 @@ elf32_arm_final_link_relocate (reloc_howto_type * howto,
unsigned long r_symndx;
bfd_byte * hit_data = contents + rel->r_offset;
bfd_vma * local_got_offsets;
+ bfd_vma * local_tlsdesc_gotents;
asection * sgot = NULL;
asection * splt = NULL;
asection * sreloc = NULL;
@@ -6851,6 +7230,11 @@ elf32_arm_final_link_relocate (reloc_howto_type * howto,
/* Some relocation types map to different relocations depending on the
target. We pick the right one here. */
r_type = arm_real_reloc_type (globals, r_type);
+
+ /* It is possible to have linker relaxations on some TLS access
+ models. Update our information here. */
+ r_type = elf32_arm_tls_transition (info, r_type, h);
+
if (r_type != howto->type)
howto = elf32_arm_howto_from_type (r_type);
@@ -6870,6 +7254,8 @@ elf32_arm_final_link_relocate (reloc_howto_type * howto,
splt = globals->root.splt;
srelgot = globals->root.srelgot;
local_got_offsets = elf_local_got_offsets (input_bfd);
+ local_tlsdesc_gotents = elf32_arm_local_tlsdesc_gotent (input_bfd);
+
r_symndx = ELF32_R_SYM (rel->r_info);
if (globals->use_rel)
@@ -8010,17 +8396,20 @@ elf32_arm_final_link_relocate (reloc_howto_type * howto,
rel->r_addend);
}
+ case R_ARM_TLS_CALL:
+ case R_ARM_THM_TLS_CALL:
case R_ARM_TLS_GD32:
case R_ARM_TLS_IE32:
+ case R_ARM_TLS_GOTDESC:
+ case R_ARM_TLS_DESCSEQ:
+ case R_ARM_THM_TLS_DESCSEQ:
{
- bfd_vma off;
- int indx;
+ bfd_vma off, offplt;
+ int indx = 0;
char tls_type;
- if (sgot == NULL)
- abort ();
+ BFD_ASSERT (sgot != NULL);
- indx = 0;
if (h != NULL)
{
bfd_boolean dyn;
@@ -8033,18 +8422,23 @@ elf32_arm_final_link_relocate (reloc_howto_type * howto,
indx = h->dynindx;
}
off = h->got.offset;
+ offplt = elf32_arm_hash_entry (h)->tlsdesc_got;
tls_type = ((struct elf32_arm_link_hash_entry *) h)->tls_type;
}
else
{
- if (local_got_offsets == NULL)
- abort ();
+ BFD_ASSERT (local_got_offsets != NULL);
off = local_got_offsets[r_symndx];
+ offplt = local_tlsdesc_gotents[r_symndx];
tls_type = elf32_arm_local_got_tls_type (input_bfd)[r_symndx];
}
- if (tls_type == GOT_UNKNOWN)
- abort ();
+ /* Linker relaxations happens from one of the
+ R_ARM_{GOTDESC,CALL,DESCSEQ} relocations to IE or LE. */
+ if (ELF32_R_TYPE(rel->r_info) != r_type)
+ tls_type = GOT_TLS_IE;
+
+ BFD_ASSERT (tls_type != GOT_UNKNOWN);
if ((off & 1) != 0)
off &= ~1;
@@ -8065,12 +8459,49 @@ elf32_arm_final_link_relocate (reloc_howto_type * howto,
|| h->root.type != bfd_link_hash_undefweak))
{
need_relocs = TRUE;
- if (srelgot == NULL)
- abort ();
- loc = srelgot->contents;
- loc += srelgot->reloc_count * RELOC_SIZE (globals);
+ BFD_ASSERT (srelgot != NULL);
}
+ if (tls_type & GOT_TLS_GDESC)
+ {
+ /* We should have relaxed, unless this is an undefined
+ weak symbol. */
+ BFD_ASSERT ((h && (h->root.type == bfd_link_hash_undefweak))
+ || info->shared);
+ BFD_ASSERT (globals->sgotplt_jump_table_size + offplt + 8
+ <= globals->root.sgotplt->size);
+
+ outrel.r_addend = 0;
+ outrel.r_offset = (globals->root.sgotplt->output_section->vma
+ + globals->root.sgotplt->output_offset
+ + offplt
+ + globals->sgotplt_jump_table_size);
+
+ outrel.r_info = ELF32_R_INFO (indx, R_ARM_TLS_DESC);
+ sreloc = globals->root.srelplt;
+ loc = sreloc->contents;
+ loc += globals->next_tls_desc_index++ * RELOC_SIZE (globals);
+ BFD_ASSERT (loc + RELOC_SIZE (globals)
+ <= sreloc->contents + sreloc->size);
+
+ SWAP_RELOC_OUT (globals) (output_bfd, &outrel, loc);
+
+ /* For globals, the first word in the relocation gets
+ the relocation index and the top bit set, or zero,
+ if we're binding now. For locals, it gets the
+ symbol's offset in the tls section. */
+ bfd_put_32 (output_bfd,
+ !h ? value - elf_hash_table (info)->tls_sec->vma
+ : info->flags & DF_BIND_NOW ? 0
+ : 0x80000000 | ELF32_R_SYM (outrel.r_info),
+ globals->root.sgotplt->contents + offplt +
+ globals->sgotplt_jump_table_size);
+
+ /* Second word in the relocation is always zero. */
+ bfd_put_32 (output_bfd, 0,
+ globals->root.sgotplt->contents + offplt +
+ globals->sgotplt_jump_table_size + 4);
+ }
if (tls_type & GOT_TLS_GD)
{
if (need_relocs)
@@ -8084,10 +8515,10 @@ elf32_arm_final_link_relocate (reloc_howto_type * howto,
if (globals->use_rel)
bfd_put_32 (output_bfd, outrel.r_addend,
sgot->contents + cur_off);
+ loc = srelgot->contents;
+ loc += srelgot->reloc_count++ * RELOC_SIZE (globals);
SWAP_RELOC_OUT (globals) (output_bfd, &outrel, loc);
- srelgot->reloc_count++;
- loc += RELOC_SIZE (globals);
if (indx == 0)
bfd_put_32 (output_bfd, value - dtpoff_base (info),
@@ -8103,10 +8534,10 @@ elf32_arm_final_link_relocate (reloc_howto_type * howto,
bfd_put_32 (output_bfd, outrel.r_addend,
sgot->contents + cur_off + 4);
+ loc = srelgot->contents;
+ loc += srelgot->reloc_count++ * RELOC_SIZE (globals);
SWAP_RELOC_OUT (globals) (output_bfd, &outrel, loc);
- srelgot->reloc_count++;
- loc += RELOC_SIZE (globals);
}
}
else
@@ -8142,9 +8573,10 @@ elf32_arm_final_link_relocate (reloc_howto_type * howto,
bfd_put_32 (output_bfd, outrel.r_addend,
sgot->contents + cur_off);
+ loc = srelgot->contents;
+ loc += srelgot->reloc_count++ * RELOC_SIZE (globals);
+
SWAP_RELOC_OUT (globals) (output_bfd, &outrel, loc);
- srelgot->reloc_count++;
- loc += RELOC_SIZE (globals);
}
else
bfd_put_32 (output_bfd, tpoff (info, value),
@@ -8160,8 +8592,142 @@ elf32_arm_final_link_relocate (reloc_howto_type * howto,
if ((tls_type & GOT_TLS_GD) && r_type != R_ARM_TLS_GD32)
off += 8;
- value = sgot->output_section->vma + sgot->output_offset + off
- - (input_section->output_section->vma + input_section->output_offset + rel->r_offset);
+ else if (tls_type & GOT_TLS_GDESC)
+ off = offplt;
+
+ if (ELF32_R_TYPE(rel->r_info) == R_ARM_TLS_CALL
+ || ELF32_R_TYPE(rel->r_info) == R_ARM_THM_TLS_CALL)
+ {
+ bfd_signed_vma offset;
+ enum elf32_arm_stub_type stub_type
+ = arm_type_of_stub (info, input_section, rel, &sym_flags,
+ (struct elf32_arm_link_hash_entry *)h,
+ globals->tls_trampoline, globals->root.splt,
+ input_bfd, sym_name);
+
+ if (stub_type != arm_stub_none)
+ {
+ struct elf32_arm_stub_hash_entry *stub_entry
+ = elf32_arm_get_stub_entry
+ (input_section, globals->root.splt, 0, rel,
+ globals, stub_type);
+ offset = (stub_entry->stub_offset
+ + stub_entry->stub_sec->output_offset
+ + stub_entry->stub_sec->output_section->vma);
+ }
+ else
+ offset = (globals->root.splt->output_section->vma
+ + globals->root.splt->output_offset
+ + globals->tls_trampoline);
+
+ if (ELF32_R_TYPE(rel->r_info) == R_ARM_TLS_CALL)
+ {
+ unsigned long inst;
+
+ offset -= (input_section->output_section->vma +
+ input_section->output_offset + rel->r_offset + 8);
+
+ inst = offset >> 2;
+ inst &= 0x00ffffff;
+ value = inst | (globals->use_blx ? 0xfa000000 : 0xeb000000);
+ }
+ else
+ {
+ /* Thumb blx encodes the offset in a complicated
+ fashion. */
+ unsigned upper_insn, lower_insn;
+ unsigned neg;
+
+ offset -= (input_section->output_section->vma +
+ input_section->output_offset
+ + rel->r_offset + 4);
+
+ /* Round up the offset to a word boundary */
+ offset = (offset + 2) & ~2;
+ neg = offset < 0;
+ upper_insn = (0xf000
+ | ((offset >> 12) & 0x3ff)
+ | (neg << 10));
+ lower_insn = (0xc000
+ | (((!((offset >> 23) & 1)) ^ neg) << 13)
+ | (((!((offset >> 22) & 1)) ^ neg) << 11)
+ | ((offset >> 1) & 0x7ff));
+ bfd_put_16 (input_bfd, upper_insn, hit_data);
+ bfd_put_16 (input_bfd, lower_insn, hit_data + 2);
+ return bfd_reloc_ok;
+ }
+ }
+ /* These relocations needs special care, as besides the fact
+ they point somewhere in .gotplt, the addend must be
+ adjusted accordingly depending on the type of instruction
+ we refer to */
+ else if ((r_type == R_ARM_TLS_GOTDESC) && (tls_type & GOT_TLS_GDESC))
+ {
+ unsigned long data, insn;
+ unsigned thumb;
+
+ data = bfd_get_32 (input_bfd, hit_data);
+ thumb = data & 1;
+ data &= ~1u;
+
+ if (thumb)
+ {
+ insn = bfd_get_16 (input_bfd, contents + rel->r_offset - data);
+ if ((insn & 0xf000) == 0xf000 || (insn & 0xf800) == 0xe800)
+ insn = (insn << 16)
+ | bfd_get_16 (input_bfd,
+ contents + rel->r_offset - data + 2);
+ if ((insn & 0xf800c000) == 0xf000c000)
+ /* bl/blx */
+ value = -6;
+ else if ((insn & 0xffffff00) == 0x4400)
+ /* add */
+ value = -5;
+ else
+ {
+ (*_bfd_error_handler)
+ (_("%B(%A+0x%lx):unexpected Thumb instruction '0x%x' referenced by TLS_GOTDESC"),
+ input_bfd, input_section,
+ (unsigned long)rel->r_offset, insn);
+ return bfd_reloc_notsupported;
+ }
+ }
+ else
+ {
+ insn = bfd_get_32 (input_bfd, contents + rel->r_offset - data);
+
+ switch (insn >> 24)
+ {
+ case 0xeb: /* bl */
+ case 0xfa: /* blx */
+ value = -4;
+ break;
+
+ case 0xe0: /* add */
+ value = -8;
+ break;
+
+ default:
+ (*_bfd_error_handler)
+ (_("%B(%A+0x%lx):unexpected ARM instruction '0x%x' referenced by TLS_GOTDESC"),
+ input_bfd, input_section,
+ (unsigned long)rel->r_offset, insn);
+ return bfd_reloc_notsupported;
+ }
+ }
+
+ value += ((globals->root.sgotplt->output_section->vma
+ + globals->root.sgotplt->output_offset + off)
+ - (input_section->output_section->vma
+ + input_section->output_offset
+ + rel->r_offset)
+ + globals->sgotplt_jump_table_size);
+ }
+ else
+ value = ((globals->root.sgot->output_section->vma
+ + globals->root.sgot->output_offset + off)
+ - (input_section->output_section->vma
+ + input_section->output_offset + rel->r_offset));
return _bfd_final_link_relocate (howto, input_bfd, input_section,
contents, rel->r_offset, value,
@@ -8789,7 +9355,16 @@ arm_add_to_rel (bfd * abfd,
|| (R_TYPE) == R_ARM_TLS_DTPMOD32 \
|| (R_TYPE) == R_ARM_TLS_TPOFF32 \
|| (R_TYPE) == R_ARM_TLS_LE32 \
- || (R_TYPE) == R_ARM_TLS_IE32)
+ || (R_TYPE) == R_ARM_TLS_IE32 \
+ || IS_ARM_TLS_GNU_RELOC (R_TYPE))
+
+/* Specific set of relocations for the gnu tls dialect. */
+#define IS_ARM_TLS_GNU_RELOC(R_TYPE) \
+ ((R_TYPE) == R_ARM_TLS_GOTDESC \
+ || (R_TYPE) == R_ARM_TLS_CALL \
+ || (R_TYPE) == R_ARM_THM_TLS_CALL \
+ || (R_TYPE) == R_ARM_TLS_DESCSEQ \
+ || (R_TYPE) == R_ARM_THM_TLS_DESCSEQ)
/* Relocate an ARM ELF section. */
@@ -9032,12 +9607,33 @@ elf32_arm_relocate_section (bfd * output_bfd,
name);
}
- r = elf32_arm_final_link_relocate (howto, input_bfd, output_bfd,
- input_section, contents, rel,
- relocation, info, sec, name,
- (h ? ELF_ST_TYPE (h->type) :
- ELF_ST_TYPE (sym->st_info)), h,
- &unresolved_reloc, &error_message);
+ /* We call elf32_arm_final_link_relocate unless we're completely
+ done, i.e., the relaxation produced the final output we want,
+ and we won't let anybody mess with it. Also, we have to do
+ addend adjustments in case of a R_ARM_TLS_GOTDESC relocation
+ both in relaxed and non-relaxed cases */
+ if ((elf32_arm_tls_transition (info, r_type, h) != (unsigned)r_type)
+ || (IS_ARM_TLS_GNU_RELOC (r_type)
+ && !((h ? elf32_arm_hash_entry (h)->tls_type :
+ elf32_arm_local_got_tls_type (input_bfd)[r_symndx])
+ & GOT_TLS_GDESC)))
+ {
+ r = elf32_arm_tls_relax (globals, input_bfd, input_section,
+ contents, rel, h == NULL);
+ /* This may have been marked unresolved because it came from
+ a shared library. But we've just dealt with that. */
+ unresolved_reloc = 0;
+ }
+ else
+ r = bfd_reloc_continue;
+
+ if (r == bfd_reloc_continue)
+ r = elf32_arm_final_link_relocate (howto, input_bfd, output_bfd,
+ input_section, contents, rel,
+ relocation, info, sec, name,
+ (h ? ELF_ST_TYPE (h->type) :
+ ELF_ST_TYPE (sym->st_info)), h,
+ &unresolved_reloc, &error_message);
/* Dynamic relocs are not propagated for SEC_DEBUGGING sections
because such sections are not SEC_ALLOC and thus ld.so will
@@ -10780,12 +11376,19 @@ elf32_arm_check_relocs (bfd *abfd, struct bfd_link_info *info,
eh = (struct elf32_arm_link_hash_entry *) h;
+ /* Could be done earlier, if h were already available. */
+ r_type = elf32_arm_tls_transition (info, r_type, h);
switch (r_type)
{
case R_ARM_GOT32:
case R_ARM_GOT_PREL:
case R_ARM_TLS_GD32:
case R_ARM_TLS_IE32:
+ case R_ARM_TLS_GOTDESC:
+ case R_ARM_TLS_DESCSEQ:
+ case R_ARM_THM_TLS_DESCSEQ:
+ case R_ARM_TLS_CALL:
+ case R_ARM_THM_TLS_CALL:
/* This symbol requires a global offset table entry. */
{
int tls_type, old_tls_type;
@@ -10793,7 +11396,14 @@ elf32_arm_check_relocs (bfd *abfd, struct bfd_link_info *info,
switch (r_type)
{
case R_ARM_TLS_GD32: tls_type = GOT_TLS_GD; break;
+
case R_ARM_TLS_IE32: tls_type = GOT_TLS_IE; break;
+
+ case R_ARM_TLS_GOTDESC:
+ case R_ARM_TLS_CALL: case R_ARM_THM_TLS_CALL:
+ case R_ARM_TLS_DESCSEQ: case R_ARM_THM_TLS_DESCSEQ:
+ tls_type = GOT_TLS_GDESC; break;
+
default: tls_type = GOT_NORMAL; break;
}
@@ -10813,27 +11423,44 @@ elf32_arm_check_relocs (bfd *abfd, struct bfd_link_info *info,
bfd_size_type size;
size = symtab_hdr->sh_info;
- size *= (sizeof (bfd_signed_vma) + sizeof (char));
+ size *= (sizeof (bfd_signed_vma)
+ + sizeof (bfd_vma) + sizeof (char));
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;
+ elf32_arm_local_tlsdesc_gotent (abfd)
+ = (bfd_vma *) (local_got_refcounts
+ + symtab_hdr->sh_info);
elf32_arm_local_got_tls_type (abfd)
- = (char *) (local_got_refcounts + symtab_hdr->sh_info);
+ = (char *) (elf32_arm_local_tlsdesc_gotent (abfd)
+ + symtab_hdr->sh_info);
}
local_got_refcounts[r_symndx] += 1;
old_tls_type = elf32_arm_local_got_tls_type (abfd) [r_symndx];
}
- /* We will already have issued an error message if there is a
- TLS / non-TLS mismatch, based on the symbol type. We don't
- support any linker relaxations. So just combine any TLS
- types needed. */
+ /* If a variable is accessed with both tls methods, two
+ slots may be created. */
+ if (GOT_TLS_GD_ANY_P (old_tls_type)
+ && GOT_TLS_GD_ANY_P (tls_type))
+ tls_type |= old_tls_type;
+
+ /* We will 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;
+ /* If the symbol is accessed in both IE and GDESC
+ method, we're able to relax. Turn off the GDESC flag,
+ without messing up with any other kind of tls types
+ that may be involved */
+ if ((tls_type & GOT_TLS_IE) && (tls_type & GOT_TLS_GDESC))
+ tls_type &= ~GOT_TLS_GDESC;
+
if (old_tls_type != tls_type)
{
if (h != NULL)
@@ -11454,12 +12081,14 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf)
{
/* We also need to make an entry in the .got.plt section, which
will be placed in the .got section by the linker script. */
- eh->plt_got_offset = htab->root.sgotplt->size;
+ eh->plt_got_offset = (htab->root.sgotplt->size
+ - 8 * htab->num_tls_desc);
htab->root.sgotplt->size += 4;
}
/* We also need to make an entry in the .rel(a).plt section. */
htab->root.srelplt->size += RELOC_SIZE (htab);
+ htab->next_tls_desc_index++;
/* VxWorks executables have a second set of relocations for
each PLT entry. They go in a separate relocation section,
@@ -11489,6 +12118,9 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf)
h->needs_plt = 0;
}
+ eh = (struct elf32_arm_link_hash_entry *) h;
+ eh->tlsdesc_got = (bfd_vma) -1;
+
if (h->got.refcount > 0)
{
asection *s;
@@ -11518,9 +12150,28 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf)
s->size += 4;
else
{
+ if (tls_type & GOT_TLS_GDESC)
+ {
+ /* R_ARM_TLS_DESC needs 2 GOT slots. */
+ eh->tlsdesc_got
+ = (htab->root.sgotplt->size
+ - elf32_arm_compute_jump_table_size (htab));
+ htab->root.sgotplt->size += 8;
+ h->got.offset = (bfd_vma) -2;
+ /* plt_got_offset needs to know there's a TLS_DESC
+ reloc in the middle of .got.plt. */
+ htab->num_tls_desc++;
+ }
+
if (tls_type & GOT_TLS_GD)
- /* R_ARM_TLS_GD32 needs 2 consecutive GOT slots. */
- s->size += 8;
+ {
+ /* R_ARM_TLS_GD32 needs 2 consecutive GOT slots. If
+ the symbol is both GD and GDESC, got.offset may
+ have been overwritten. */
+ h->got.offset = s->size;
+ s->size += 8;
+ }
+
if (tls_type & GOT_TLS_IE)
/* R_ARM_TLS_IE32 needs one GOT slot. */
s->size += 4;
@@ -11545,8 +12196,17 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf)
if (tls_type & GOT_TLS_GD)
htab->root.srelgot->size += RELOC_SIZE (htab);
- if ((tls_type & GOT_TLS_GD) && indx != 0)
- htab->root.srelgot->size += RELOC_SIZE (htab);
+ if (tls_type & GOT_TLS_GDESC)
+ {
+ htab->root.srelplt->size += RELOC_SIZE (htab);
+ /* GDESC needs a trampoline to jump to. */
+ htab->tls_trampoline = -1;
+ }
+
+ /* Only GD needs it. GDESC just emits one relocation per
+ 2 entries. */
+ if ((tls_type & GOT_TLS_GD) && indx != 0)
+ htab->root.srelgot->size += RELOC_SIZE (htab);
}
else if ((ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
|| h->root.type != bfd_link_hash_undefweak)
@@ -11788,6 +12448,7 @@ elf32_arm_size_dynamic_sections (bfd * output_bfd ATTRIBUTE_UNUSED,
bfd_signed_vma *local_got;
bfd_signed_vma *end_local_got;
char *local_tls_type;
+ bfd_vma *local_tlsdesc_gotent;
bfd_size_type locsymcount;
Elf_Internal_Shdr *symtab_hdr;
asection *srel;
@@ -11836,23 +12497,49 @@ elf32_arm_size_dynamic_sections (bfd * output_bfd ATTRIBUTE_UNUSED,
locsymcount = symtab_hdr->sh_info;
end_local_got = local_got + locsymcount;
local_tls_type = elf32_arm_local_got_tls_type (ibfd);
+ local_tlsdesc_gotent = elf32_arm_local_tlsdesc_gotent (ibfd);
s = htab->root.sgot;
srel = htab->root.srelgot;
- for (; local_got < end_local_got; ++local_got, ++local_tls_type)
+ for (; local_got < end_local_got;
+ ++local_got, ++local_tls_type, ++local_tlsdesc_gotent)
{
+ *local_tlsdesc_gotent = (bfd_vma) -1;
if (*local_got > 0)
{
*local_got = s->size;
if (*local_tls_type & GOT_TLS_GD)
/* TLS_GD relocs need an 8-byte structure in the GOT. */
s->size += 8;
+ if (*local_tls_type & GOT_TLS_GDESC)
+ {
+ *local_tlsdesc_gotent = htab->root.sgotplt->size
+ - elf32_arm_compute_jump_table_size (htab);
+ htab->root.sgotplt->size += 8;
+ *local_got = (bfd_vma) -2;
+ /* plt_got_offset needs to know there's a TLS_DESC
+ reloc in the middle of .got.plt. */
+ htab->num_tls_desc++;
+ }
if (*local_tls_type & GOT_TLS_IE)
s->size += 4;
- if (*local_tls_type == GOT_NORMAL)
- s->size += 4;
- if (info->shared || *local_tls_type == GOT_TLS_GD)
+ if (*local_tls_type & GOT_NORMAL)
+ {
+ /* If the symbol is both GD and GDESC, *local_got
+ may have been overwritten. */
+ *local_got = s->size;
+ s->size += 4;
+ }
+
+ if ((info->shared && !(*local_tls_type & GOT_TLS_GDESC))
+ || *local_tls_type & GOT_TLS_GD)
srel->size += RELOC_SIZE (htab);
+
+ if (info->shared && *local_tls_type & GOT_TLS_GDESC)
+ {
+ htab->root.srelplt->size += RELOC_SIZE (htab);
+ htab->tls_trampoline = -1;
+ }
}
else
*local_got = (bfd_vma) -1;
@@ -11894,6 +12581,34 @@ elf32_arm_size_dynamic_sections (bfd * output_bfd ATTRIBUTE_UNUSED,
/* Allocate space for the glue sections now that we've sized them. */
bfd_elf32_arm_allocate_interworking_sections (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->root.srelplt)
+ htab->sgotplt_jump_table_size = elf32_arm_compute_jump_table_size(htab);
+
+ if (htab->tls_trampoline)
+ {
+ if (htab->root.splt->size == 0)
+ htab->root.splt->size += htab->plt_header_size;
+
+ htab->tls_trampoline = htab->root.splt->size;
+ htab->root.splt->size += htab->plt_entry_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);
+ }
+ }
+
/* The check_relocs and adjust_dynamic_symbol entry points have
determined the sizes of the various dynamic sections. Allocate
memory for them. */
@@ -11984,6 +12699,11 @@ elf32_arm_size_dynamic_sections (bfd * output_bfd ATTRIBUTE_UNUSED,
htab->use_rel ? DT_REL : DT_RELA)
|| !add_dynamic_entry (DT_JMPREL, 0))
return FALSE;
+
+ if (htab->dt_tlsdesc_plt &&
+ (!add_dynamic_entry (DT_TLSDESC_PLT,0)
+ || !add_dynamic_entry (DT_TLSDESC_GOT,0)))
+ return FALSE;
}
if (relocs)
@@ -12024,6 +12744,49 @@ elf32_arm_size_dynamic_sections (bfd * output_bfd ATTRIBUTE_UNUSED,
return TRUE;
}
+/* Size sections even though they're not dynamic. We use it to setup
+ _TLS_MODULE_BASE_, if needed. */
+
+static bfd_boolean
+elf32_arm_always_size_sections (bfd *output_bfd,
+ struct bfd_link_info *info)
+{
+ asection *tls_sec;
+
+ if (info->relocatable)
+ return TRUE;
+
+ tls_sec = elf_hash_table (info)->tls_sec;
+
+ if (tls_sec)
+ {
+ struct elf_link_hash_entry *tlsbase;
+
+ tlsbase = elf_link_hash_lookup
+ (elf_hash_table (info), "_TLS_MODULE_BASE_", TRUE, TRUE, FALSE);
+
+ if (tlsbase)
+ {
+ struct bfd_link_hash_entry *bh = NULL;
+ const struct elf_backend_data *bed
+ = get_elf_backend_data (output_bfd);
+
+ 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->type = STT_TLS;
+ tlsbase = (struct elf_link_hash_entry *)bh;
+ tlsbase->def_regular = 1;
+ tlsbase->other = STV_HIDDEN;
+ (*bed->elf_backend_hide_symbol) (info, tlsbase, TRUE);
+ }
+ }
+ return TRUE;
+}
+
/* Finish up dynamic symbol handling. We set the contents of various
dynamic sections here. */
@@ -12240,7 +13003,7 @@ elf32_arm_finish_dynamic_symbol (bfd * output_bfd,
}
if (h->got.offset != (bfd_vma) -1
- && (elf32_arm_hash_entry (h)->tls_type & GOT_TLS_GD) == 0
+ && (! GOT_TLS_GD_ANY_P (elf32_arm_hash_entry (h)->tls_type))
&& (elf32_arm_hash_entry (h)->tls_type & GOT_TLS_IE) == 0)
{
asection * sgot;
@@ -12321,6 +13084,24 @@ elf32_arm_finish_dynamic_symbol (bfd * output_bfd,
return TRUE;
}
+static void
+arm_put_trampoline (struct elf32_arm_link_hash_table *htab, bfd *output_bfd,
+ void *contents,
+ const unsigned long *template, unsigned count)
+{
+ unsigned ix;
+
+ for (ix = 0; ix != count; ix++)
+ {
+ unsigned long insn = template[ix];
+
+ /* Emit mov pc,rx if bx is not permitted. */
+ if (htab->fix_v4bx == 1 && (insn & 0x0ffffff0) == 0x012fff10)
+ insn = (insn & 0xf000000f) | 0x01a0f000;
+ put_arm_insn (htab, output_bfd, insn, (char *)contents + ix*4);
+ }
+}
+
/* Finish up the dynamic sections. */
static bfd_boolean
@@ -12474,6 +13255,20 @@ elf32_arm_finish_dynamic_sections (bfd * output_bfd, struct bfd_link_info * info
}
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;
+
/* Set the bottom bit of DT_INIT/FINI if the
corresponding function is Thumb. */
case DT_INIT:
@@ -12563,6 +13358,41 @@ elf32_arm_finish_dynamic_sections (bfd * output_bfd, struct bfd_link_info * info
if (splt->output_section->owner == output_bfd)
elf_section_data (splt->output_section)->this_hdr.sh_entsize = 4;
+ if (htab->dt_tlsdesc_plt)
+ {
+ bfd_vma got_address
+ = sgot->output_section->vma + sgot->output_offset;
+ bfd_vma gotplt_address = (htab->root.sgot->output_section->vma
+ + htab->root.sgot->output_offset);
+ bfd_vma plt_address
+ = splt->output_section->vma + splt->output_offset;
+
+ arm_put_trampoline (htab, output_bfd,
+ splt->contents + htab->dt_tlsdesc_plt,
+ dl_tlsdesc_lazy_trampoline, 6);
+
+ bfd_put_32 (output_bfd,
+ gotplt_address + htab->dt_tlsdesc_got
+ - (plt_address + htab->dt_tlsdesc_plt)
+ - dl_tlsdesc_lazy_trampoline[6],
+ splt->contents + htab->dt_tlsdesc_plt + 24);
+ bfd_put_32 (output_bfd,
+ got_address - (plt_address + htab->dt_tlsdesc_plt)
+ - dl_tlsdesc_lazy_trampoline[7],
+ splt->contents + htab->dt_tlsdesc_plt + 24 + 4);
+ }
+
+ if (htab->tls_trampoline)
+ {
+ arm_put_trampoline (htab, output_bfd,
+ splt->contents + htab->tls_trampoline,
+ tls_trampoline, 3);
+#ifdef FOUR_WORD_PLT
+ bfd_put_32 (output_bfd, 0x00000000,
+ splt->contents + htab->tls_trampoline + 12);
+#endif
+ }
+
if (htab->vxworks_p && !info->shared && htab->root.splt->size > 0)
{
/* Correct the .rel(a).plt.unloaded relocations. They will have
@@ -13137,6 +13967,28 @@ elf32_arm_output_arch_local_syms (bfd *output_bfd,
#endif
}
+ if (htab->dt_tlsdesc_plt != 0)
+ {
+ /* Mapping symbols for the lazy tls trampoline. */
+ if (!elf32_arm_output_map_sym (&osi, ARM_MAP_ARM, htab->dt_tlsdesc_plt))
+ return FALSE;
+
+ if (!elf32_arm_output_map_sym (&osi, ARM_MAP_DATA,
+ htab->dt_tlsdesc_plt + 24))
+ return FALSE;
+ }
+ if (htab->tls_trampoline != 0)
+ {
+ /* Mapping symbols for the tls trampoline. */
+ if (!elf32_arm_output_map_sym (&osi, ARM_MAP_ARM, htab->tls_trampoline))
+ return FALSE;
+#ifdef FOUR_WORD_PLT
+ if (!elf32_arm_output_map_sym (&osi, ARM_MAP_DATA,
+ htab->tls_trampoline + 12))
+ return FALSE;
+#endif
+ }
+
elf_link_hash_traverse (&htab->root, elf32_arm_output_plt_map, (void *) &osi);
return TRUE;
}
@@ -13795,6 +14647,7 @@ const struct elf_size_info elf32_arm_size_info =
#define elf_backend_finish_dynamic_symbol elf32_arm_finish_dynamic_symbol
#define elf_backend_finish_dynamic_sections elf32_arm_finish_dynamic_sections
#define elf_backend_size_dynamic_sections elf32_arm_size_dynamic_sections
+#define elf_backend_always_size_sections elf32_arm_always_size_sections
#define elf_backend_init_index_section _bfd_elf_init_2_index_sections
#define elf_backend_post_process_headers elf32_arm_post_process_headers
#define elf_backend_reloc_type_class elf32_arm_reloc_type_class