aboutsummaryrefslogtreecommitdiff
path: root/bfd/elf32-arm.c
diff options
context:
space:
mode:
authorDaniel Jacobowitz <drow@false.org>2005-03-29 16:54:22 +0000
committerDaniel Jacobowitz <drow@false.org>2005-03-29 16:54:22 +0000
commitba93b8aced8f39a02c47731e89278fe6f217d929 (patch)
tree10d828c846f4224226a06d2d2bd4e31283b9d289 /bfd/elf32-arm.c
parent71a976dd82f4a544374e4f40b9a1d6ec1f5525ad (diff)
downloadgdb-ba93b8aced8f39a02c47731e89278fe6f217d929.zip
gdb-ba93b8aced8f39a02c47731e89278fe6f217d929.tar.gz
gdb-ba93b8aced8f39a02c47731e89278fe6f217d929.tar.bz2
bfd/
* bfd-in2.h, libbfd.h: Regenerated. * reloc.c: Add ARM TLS relocations. * elf32-arm.c (elf32_arm_howto_table): Add dynamic TLS relocations. (elf32_arm_tls_gd32_howto, elf32_arm_tls_ldo32_howto) (elf32_arm_tls_ldm32_howto, elf32_arm_tls_le32_howto) (elf32_arm_tls_ie32_howto): New. (elf32_arm_howto_from_type): Support TLS relocations. (elf32_arm_reloc_map): Likewise. (elf32_arm_reloc_type_lookup): Likewise. (TCB_SIZE): Define. (struct elf32_arm_obj_tdata): New. (elf32_arm_tdata, elf32_arm_local_got_tls_type): Define. (elf32_arm_mkobject): New function. (struct elf32_arm_relocs_copied): Add pc_count. (elf32_arm_hash_entry, GOT_UNKNOWN, GOT_NORMAL, GOT_TLS_GD) (GOT_TLS_IE): Define. (struct elf32_arm_link_hash_table): Add tls_ldm_got. (elf32_arm_link_hash_newfunc): Initialize tls_type. (elf32_arm_copy_indirect_symbol): Copy pc_count and tls_type. (elf32_arm_link_hash_table_create): Initialize tls_ldm_got. (dtpoff_base, tpoff): New functions. (elf32_arm_final_link_relocate): Handle TLS relocations. (IS_ARM_TLS_RELOC): Define. (elf32_arm_relocate_section): Warn about TLS mismatches. (elf32_arm_gc_sweep_hook): Handle TLS relocations and pc_count. (elf32_arm_check_relocs): Detect invalid symbol indexes. Handle TLS relocations and pc_count. (elf32_arm_adjust_dynamic_symbol): Check non_got_ref. (allocate_dynrelocs): Handle TLS. Bind REL32 relocs to local calls. (elf32_arm_size_dynamic_sections): Handle TLS. (elf32_arm_finish_dynamic_symbol): Likewise. (bfd_elf32_mkobject): Define. gas/ * config/tc-arm.c (arm_parse_reloc): Add TLS relocations. (md_apply_fix3): Mark TLS symbols. (tc_gen_reloc): Handle TLS relocations. (arm_fix_adjustable): Ignore TLS relocations. (s_arm_elf_cons): Support expressions after decorated symbols. gas/testuite/ * gas/arm/tls.s, gas/arm/tls.d: New files. * gas/arm/arm.exp: Run TLS test. include/elf/ * arm.h: Add TLS relocations. ld/testsuite/ * ld-arm/tls-lib.s, ld-arm/tls-lib.d, ld-arm/tls-lib.r, ld-arm/tls-app.s, ld-arm/tls-app.d, ld-arm/tls-app.r: New files. * ld-arm/arm-lib.ld, ld-arm/arm-dyn.ld: Increase data segment alignment. * ld-arm/arm-elf.exp: Run TLS tests.
Diffstat (limited to 'bfd/elf32-arm.c')
-rw-r--r--bfd/elf32-arm.c753
1 files changed, 673 insertions, 80 deletions
diff --git a/bfd/elf32-arm.c b/bfd/elf32-arm.c
index 2e1f1d3..7464bbe 100644
--- a/bfd/elf32-arm.c
+++ b/bfd/elf32-arm.c
@@ -294,49 +294,49 @@ static reloc_howto_type elf32_arm_howto_table[] =
0x07ff07ff, /* dst_mask */
TRUE), /* pcrel_offset */
- /* These next three relocs are not defined, but we need to fill the space. */
+ /* Dynamic TLS relocations. */
- HOWTO (R_ARM_NONE, /* type */
- 0, /* rightshift */
- 0, /* size (0 = byte, 1 = short, 2 = long) */
- 0, /* bitsize */
- FALSE, /* pc_relative */
- 0, /* bitpos */
- complain_overflow_dont,/* complain_on_overflow */
- bfd_elf_generic_reloc, /* special_function */
- "R_ARM_unknown_17", /* name */
- FALSE, /* partial_inplace */
- 0, /* src_mask */
- 0, /* dst_mask */
- FALSE), /* pcrel_offset */
+ HOWTO (R_ARM_TLS_DTPMOD32, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_bitfield,/* complain_on_overflow */
+ bfd_elf_generic_reloc, /* special_function */
+ "R_ARM_TLS_DTPMOD32", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
- HOWTO (R_ARM_NONE, /* type */
- 0, /* rightshift */
- 0, /* size (0 = byte, 1 = short, 2 = long) */
- 0, /* bitsize */
- FALSE, /* pc_relative */
- 0, /* bitpos */
- complain_overflow_dont,/* complain_on_overflow */
- bfd_elf_generic_reloc, /* special_function */
- "R_ARM_unknown_18", /* name */
- FALSE, /* partial_inplace */
- 0, /* src_mask */
- 0, /* dst_mask */
- FALSE), /* pcrel_offset */
+ HOWTO (R_ARM_TLS_DTPOFF32, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_bitfield,/* complain_on_overflow */
+ bfd_elf_generic_reloc, /* special_function */
+ "R_ARM_TLS_DTPOFF32", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
- HOWTO (R_ARM_NONE, /* type */
- 0, /* rightshift */
- 0, /* size (0 = byte, 1 = short, 2 = long) */
- 0, /* bitsize */
- FALSE, /* pc_relative */
- 0, /* bitpos */
- complain_overflow_dont,/* complain_on_overflow */
- bfd_elf_generic_reloc, /* special_function */
- "R_ARM_unknown_19", /* name */
- FALSE, /* partial_inplace */
- 0, /* src_mask */
- 0, /* dst_mask */
- FALSE), /* pcrel_offset */
+ HOWTO (R_ARM_TLS_TPOFF32, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_bitfield,/* complain_on_overflow */
+ bfd_elf_generic_reloc, /* special_function */
+ "R_ARM_TLS_TPOFF32", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
/* Relocs used in ARM Linux */
@@ -663,6 +663,81 @@ static reloc_howto_type elf32_arm_howto_table[] =
TRUE), /* pcrel_offset */
};
+static reloc_howto_type elf32_arm_tls_gd32_howto =
+ HOWTO (R_ARM_TLS_GD32, /* 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_GD32", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE); /* pcrel_offset */
+
+static reloc_howto_type elf32_arm_tls_ldo32_howto =
+ HOWTO (R_ARM_TLS_LDO32, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_bitfield,/* complain_on_overflow */
+ bfd_elf_generic_reloc, /* special_function */
+ "R_ARM_TLS_LDO32", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE); /* pcrel_offset */
+
+static reloc_howto_type elf32_arm_tls_ldm32_howto =
+ HOWTO (R_ARM_TLS_LDM32, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_bitfield,/* complain_on_overflow */
+ bfd_elf_generic_reloc, /* special_function */
+ "R_ARM_TLS_LDM32", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE); /* pcrel_offset */
+
+static reloc_howto_type elf32_arm_tls_le32_howto =
+ HOWTO (R_ARM_TLS_LE32, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_bitfield,/* complain_on_overflow */
+ bfd_elf_generic_reloc, /* special_function */
+ "R_ARM_TLS_LE32", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE); /* pcrel_offset */
+
+static reloc_howto_type elf32_arm_tls_ie32_howto =
+ HOWTO (R_ARM_TLS_IE32, /* 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_IE32", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE); /* pcrel_offset */
+
/* GNU extension to record C++ vtable hierarchy */
static reloc_howto_type elf32_arm_vtinherit_howto =
HOWTO (R_ARM_GNU_VTINHERIT, /* type */
@@ -825,6 +900,26 @@ elf32_arm_howto_from_type (unsigned int r_type)
case R_ARM_THM_PC9:
return &elf32_arm_thm_pc9_howto;
+
+ case R_ARM_TLS_GD32:
+ return &elf32_arm_tls_gd32_howto;
+ break;
+
+ case R_ARM_TLS_LDO32:
+ return &elf32_arm_tls_ldo32_howto;
+ break;
+
+ case R_ARM_TLS_LDM32:
+ return &elf32_arm_tls_ldm32_howto;
+ break;
+
+ case R_ARM_TLS_IE32:
+ return &elf32_arm_tls_ie32_howto;
+ break;
+
+ case R_ARM_TLS_LE32:
+ return &elf32_arm_tls_le32_howto;
+ break;
case R_ARM_RREL32:
case R_ARM_RABS32:
@@ -879,7 +974,16 @@ static const struct elf32_arm_reloc_map elf32_arm_reloc_map[] =
{BFD_RELOC_ARM_ROSEGREL32, R_ARM_ROSEGREL32},
{BFD_RELOC_ARM_SBREL32, R_ARM_SBREL32},
{BFD_RELOC_ARM_PREL31, R_ARM_PREL31},
- {BFD_RELOC_ARM_TARGET2, R_ARM_TARGET2}
+ {BFD_RELOC_ARM_TARGET2, R_ARM_TARGET2},
+ {BFD_RELOC_ARM_PLT32, R_ARM_PLT32},
+ {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},
+ {BFD_RELOC_ARM_TLS_DTPMOD32, R_ARM_TLS_DTPMOD32},
+ {BFD_RELOC_ARM_TLS_DTPOFF32, R_ARM_TLS_DTPOFF32},
+ {BFD_RELOC_ARM_TLS_TPOFF32, R_ARM_TLS_TPOFF32},
+ {BFD_RELOC_ARM_TLS_IE32, R_ARM_TLS_IE32},
+ {BFD_RELOC_ARM_TLS_LE32, R_ARM_TLS_LE32},
};
static reloc_howto_type *
@@ -903,6 +1007,21 @@ elf32_arm_reloc_type_lookup (abfd, code)
case BFD_RELOC_THUMB_PCREL_BRANCH9:
return & elf32_arm_thm_pc9_howto;
+ case BFD_RELOC_ARM_TLS_GD32:
+ return & elf32_arm_tls_gd32_howto;
+
+ case BFD_RELOC_ARM_TLS_LDO32:
+ return & elf32_arm_tls_ldo32_howto;
+
+ case BFD_RELOC_ARM_TLS_LDM32:
+ return & elf32_arm_tls_ldm32_howto;
+
+ case BFD_RELOC_ARM_TLS_IE32:
+ return & elf32_arm_tls_ie32_howto;
+
+ case BFD_RELOC_ARM_TLS_LE32:
+ return & elf32_arm_tls_le32_howto;
+
default:
for (i = 0; i < NUM_ELEM (elf32_arm_reloc_map); i ++)
if (elf32_arm_reloc_map[i].bfd_reloc_val == code)
@@ -1093,14 +1212,41 @@ struct _arm_elf_section_data
#define elf32_arm_section_data(sec) \
((struct _arm_elf_section_data *) elf_section_data (sec))
+/* The size of the thread control block. */
+#define TCB_SIZE 8
+
+struct elf32_arm_obj_tdata
+{
+ struct elf_obj_tdata root;
+
+ /* tls_type for each local got entry. */
+ char *local_got_tls_type;
+};
+
+#define elf32_arm_tdata(abfd) \
+ ((struct elf32_arm_obj_tdata *) (abfd)->tdata.any)
+
+#define elf32_arm_local_got_tls_type(abfd) \
+ (elf32_arm_tdata (abfd)->local_got_tls_type)
+
+static bfd_boolean
+elf32_arm_mkobject (bfd *abfd)
+{
+ bfd_size_type amt = sizeof (struct elf32_arm_obj_tdata);
+ abfd->tdata.any = bfd_zalloc (abfd, amt);
+ if (abfd->tdata.any == NULL)
+ return FALSE;
+ return TRUE;
+}
+
/* The ARM linker needs to keep track of the number of relocs that it
decides to copy in check_relocs for each symbol. This is so that
it can discard PC relative relocs if it doesn't need them when
linking with -Bsymbolic. We store the information in a field
extending the regular ELF linker hash table. */
-/* This structure keeps track of the number of PC relative relocs we
- have copied for a given symbol. */
+/* This structure keeps track of the number of relocs we have copied
+ for a given symbol. */
struct elf32_arm_relocs_copied
{
/* Next section. */
@@ -1109,8 +1255,12 @@ struct elf32_arm_relocs_copied
asection * section;
/* Number of relocs copied in this section. */
bfd_size_type count;
+ /* Number of PC-relative relocs copied in this section. */
+ bfd_size_type pc_count;
};
+#define elf32_arm_hash_entry(ent) ((struct elf32_arm_link_hash_entry *)(ent))
+
/* Arm ELF linker hash entry. */
struct elf32_arm_link_hash_entry
{
@@ -1127,6 +1277,12 @@ struct elf32_arm_link_hash_entry
used, we need to record the index into .got.plt instead of
recomputing it from the PLT offset. */
bfd_signed_vma plt_got_offset;
+
+#define GOT_UNKNOWN 0
+#define GOT_NORMAL 1
+#define GOT_TLS_GD 2
+#define GOT_TLS_IE 4
+ unsigned char tls_type;
};
/* Traverse an arm ELF linker hash table. */
@@ -1189,6 +1345,12 @@ struct elf32_arm_link_hash_table
asection *sdynbss;
asection *srelbss;
+ /* Data for R_ARM_TLS_LDM32 relocations. */
+ union {
+ bfd_signed_vma refcount;
+ bfd_vma offset;
+ } tls_ldm_got;
+
/* Small local sym to section mapping cache. */
struct sym_sec_cache sym_sec;
@@ -1220,6 +1382,7 @@ elf32_arm_link_hash_newfunc (struct bfd_hash_entry * entry,
if (ret != NULL)
{
ret->relocs_copied = NULL;
+ ret->tls_type = GOT_UNKNOWN;
ret->plt_thumb_refcount = 0;
ret->plt_got_offset = -1;
}
@@ -1321,6 +1484,7 @@ elf32_arm_copy_indirect_symbol (const struct elf_backend_data *bed,
for (q = edir->relocs_copied; q != NULL; q = q->next)
if (q->section == p->section)
{
+ q->pc_count += p->pc_count;
q->count += p->count;
*pp = p->next;
break;
@@ -1346,6 +1510,13 @@ elf32_arm_copy_indirect_symbol (const struct elf_backend_data *bed,
else
BFD_ASSERT (eind->plt_thumb_refcount == 0);
+ if (ind->root.type == bfd_link_hash_indirect
+ && dir->got.refcount <= 0)
+ {
+ edir->tls_type = eind->tls_type;
+ eind->tls_type = GOT_UNKNOWN;
+ }
+
_bfd_elf_link_hash_copy_indirect (bed, dir, ind);
}
@@ -1392,6 +1563,7 @@ elf32_arm_link_hash_table_create (bfd *abfd)
ret->use_rel = 1;
ret->sym_sec.abfd = NULL;
ret->obfd = abfd;
+ ret->tls_ldm_got.refcount = 0;
return &ret->root.root;
}
@@ -2228,6 +2400,35 @@ arm_real_reloc_type (struct elf32_arm_link_hash_table * globals,
#endif /* OLD_ARM_ABI */
+/* Return the base VMA address which should be subtracted from real addresses
+ when resolving @dtpoff relocation.
+ This is PT_TLS segment p_vaddr. */
+
+static bfd_vma
+dtpoff_base (struct bfd_link_info *info)
+{
+ /* If tls_sec is NULL, we should have signalled an error already. */
+ if (elf_hash_table (info)->tls_sec == NULL)
+ return 0;
+ return elf_hash_table (info)->tls_sec->vma;
+}
+
+/* Return the relocation value for @tpoff relocation
+ if STT_TLS virtual address is ADDRESS. */
+
+static bfd_vma
+tpoff (struct bfd_link_info *info, bfd_vma address)
+{
+ struct elf_link_hash_table *htab = elf_hash_table (info);
+ bfd_vma base;
+
+ /* If tls_sec is NULL, we should have signalled an error already. */
+ if (htab->tls_sec == NULL)
+ return 0;
+ base = align_power ((bfd_vma) TCB_SIZE, htab->tls_sec->alignment_power);
+ return address - htab->tls_sec->vma + base;
+}
+
/* Perform a relocation as part of a final link. */
static bfd_reloc_status_type
@@ -2979,6 +3180,222 @@ elf32_arm_final_link_relocate (reloc_howto_type * howto,
contents, rel->r_offset, value,
(bfd_vma) 0);
+ case R_ARM_TLS_LDO32:
+ value = value - dtpoff_base (info);
+
+ return _bfd_final_link_relocate (howto, input_bfd, input_section,
+ contents, rel->r_offset, value, (bfd_vma) 0);
+
+ case R_ARM_TLS_LDM32:
+ {
+ bfd_vma off;
+
+ if (globals->sgot == NULL)
+ abort ();
+
+ off = globals->tls_ldm_got.offset;
+
+ if ((off & 1) != 0)
+ off &= ~1;
+ else
+ {
+ /* If we don't know the module number, create a relocation
+ for it. */
+ if (info->shared)
+ {
+ Elf_Internal_Rela outrel;
+ bfd_byte *loc;
+
+ if (globals->srelgot == NULL)
+ abort ();
+
+ outrel.r_offset = (globals->sgot->output_section->vma
+ + globals->sgot->output_offset + off);
+ outrel.r_info = ELF32_R_INFO (0, R_ARM_TLS_DTPMOD32);
+
+ bfd_put_32 (output_bfd, 0, globals->sgot->contents + off);
+
+ loc = globals->srelgot->contents;
+ loc += globals->srelgot->reloc_count++ * sizeof (Elf32_External_Rel);
+ bfd_elf32_swap_reloc_out (output_bfd, &outrel, loc);
+ }
+ else
+ bfd_put_32 (output_bfd, 1, globals->sgot->contents + off);
+
+ globals->tls_ldm_got.offset |= 1;
+ }
+
+ value = globals->sgot->output_section->vma + globals->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,
+ (bfd_vma) 0);
+ }
+
+ case R_ARM_TLS_GD32:
+ case R_ARM_TLS_IE32:
+ {
+ bfd_vma off;
+ int indx;
+ char tls_type;
+
+ if (globals->sgot == NULL)
+ abort ();
+
+ indx = 0;
+ if (h != NULL)
+ {
+ bfd_boolean dyn;
+ dyn = globals->root.dynamic_sections_created;
+ if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, h)
+ && (!info->shared
+ || !SYMBOL_REFERENCES_LOCAL (info, h)))
+ {
+ *unresolved_reloc_p = FALSE;
+ indx = h->dynindx;
+ }
+ off = h->got.offset;
+ tls_type = ((struct elf32_arm_link_hash_entry *) h)->tls_type;
+ }
+ else
+ {
+ if (local_got_offsets == NULL)
+ abort ();
+ off = local_got_offsets[r_symndx];
+ tls_type = elf32_arm_local_got_tls_type (input_bfd)[r_symndx];
+ }
+
+ if (tls_type == GOT_UNKNOWN)
+ abort ();
+
+ if ((off & 1) != 0)
+ off &= ~1;
+ else
+ {
+ bfd_boolean need_relocs = FALSE;
+ Elf_Internal_Rela outrel;
+ bfd_byte *loc = NULL;
+ int cur_off = off;
+
+ /* The GOT entries have not been initialized yet. Do it
+ now, and emit any relocations. If both an IE GOT and a
+ GD GOT are necessary, we emit the GD first. */
+
+ if ((info->shared || indx != 0)
+ && (h == NULL
+ || ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
+ || h->root.type != bfd_link_hash_undefweak))
+ {
+ need_relocs = TRUE;
+ if (globals->srelgot == NULL)
+ abort ();
+ loc = globals->srelgot->contents;
+ loc += globals->srelgot->reloc_count * sizeof (Elf32_External_Rel);
+ }
+
+ if (tls_type & GOT_TLS_GD)
+ {
+ if (need_relocs)
+ {
+ outrel.r_offset = (globals->sgot->output_section->vma
+ + globals->sgot->output_offset + cur_off);
+ outrel.r_info = ELF32_R_INFO (indx, R_ARM_TLS_DTPMOD32);
+ bfd_put_32 (output_bfd, 0, globals->sgot->contents + cur_off);
+
+ bfd_elf32_swap_reloc_out (output_bfd, &outrel, loc);
+ globals->srelgot->reloc_count++;
+ loc += sizeof (Elf32_External_Rel);
+
+ if (indx == 0)
+ bfd_put_32 (output_bfd, value - dtpoff_base (info),
+ globals->sgot->contents + cur_off + 4);
+ else
+ {
+ bfd_put_32 (output_bfd, 0,
+ globals->sgot->contents + cur_off + 4);
+
+ outrel.r_info = ELF32_R_INFO (indx,
+ R_ARM_TLS_DTPOFF32);
+ outrel.r_offset += 4;
+ bfd_elf32_swap_reloc_out (output_bfd, &outrel, loc);
+ globals->srelgot->reloc_count++;
+ loc += sizeof (Elf32_External_Rel);
+ }
+ }
+ else
+ {
+ /* If we are not emitting relocations for a
+ general dynamic reference, then we must be in a
+ static link or an executable link with the
+ symbol binding locally. Mark it as belonging
+ to module 1, the executable. */
+ bfd_put_32 (output_bfd, 1,
+ globals->sgot->contents + cur_off);
+ bfd_put_32 (output_bfd, value - dtpoff_base (info),
+ globals->sgot->contents + cur_off + 4);
+ }
+
+ cur_off += 8;
+ }
+
+ if (tls_type & GOT_TLS_IE)
+ {
+ if (need_relocs)
+ {
+ outrel.r_offset = (globals->sgot->output_section->vma
+ + globals->sgot->output_offset
+ + cur_off);
+ outrel.r_info = ELF32_R_INFO (indx, R_ARM_TLS_TPOFF32);
+
+ if (indx == 0)
+ bfd_put_32 (output_bfd, value - dtpoff_base (info),
+ globals->sgot->contents + cur_off);
+ else
+ bfd_put_32 (output_bfd, 0,
+ globals->sgot->contents + cur_off);
+
+ bfd_elf32_swap_reloc_out (output_bfd, &outrel, loc);
+ globals->srelgot->reloc_count++;
+ loc += sizeof (Elf32_External_Rel);
+ }
+ else
+ bfd_put_32 (output_bfd, tpoff (info, value),
+ globals->sgot->contents + cur_off);
+ cur_off += 4;
+ }
+
+ if (h != NULL)
+ h->got.offset |= 1;
+ else
+ local_got_offsets[r_symndx] |= 1;
+ }
+
+ if ((tls_type & GOT_TLS_GD) && r_type != R_ARM_TLS_GD32)
+ off += 8;
+ value = globals->sgot->output_section->vma + globals->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,
+ (bfd_vma) 0);
+ }
+
+ case R_ARM_TLS_LE32:
+ if (info->shared)
+ {
+ (*_bfd_error_handler)
+ (_("%B(%A+0x%lx): R_ARM_TLS_LE32 relocation not permitted in shared object"),
+ input_bfd, input_section,
+ (long) rel->r_offset, howto->name);
+ return FALSE;
+ }
+ else
+ value = tpoff (info, value);
+
+ return _bfd_final_link_relocate (howto, input_bfd, input_section,
+ contents, rel->r_offset, value, (bfd_vma) 0);
+
case R_ARM_SBREL32:
return bfd_reloc_notsupported;
@@ -3098,6 +3515,16 @@ arm_add_to_rel (bfd * abfd,
}
}
+#define IS_ARM_TLS_RELOC(R_TYPE) \
+ ((R_TYPE) == R_ARM_TLS_GD32 \
+ || (R_TYPE) == R_ARM_TLS_LDO32 \
+ || (R_TYPE) == R_ARM_TLS_LDM32 \
+ || (R_TYPE) == R_ARM_TLS_DTPOFF32 \
+ || (R_TYPE) == R_ARM_TLS_DTPMOD32 \
+ || (R_TYPE) == R_ARM_TLS_TPOFF32 \
+ || (R_TYPE) == R_ARM_TLS_LE32 \
+ || (R_TYPE) == R_ARM_TLS_IE32)
+
/* Relocate an ARM ELF section. */
static bfd_boolean
elf32_arm_relocate_section (bfd * output_bfd,
@@ -3136,6 +3563,7 @@ elf32_arm_relocate_section (bfd * output_bfd,
bfd_vma relocation;
bfd_reloc_status_type r;
arelent bfd_reloc;
+ char sym_type;
bfd_boolean unresolved_reloc = FALSE;
r_symndx = ELF32_R_SYM (rel->r_info);
@@ -3179,6 +3607,7 @@ elf32_arm_relocate_section (bfd * output_bfd,
if (r_symndx < symtab_hdr->sh_info)
{
sym = local_syms + r_symndx;
+ sym_type = ELF32_ST_TYPE (sym->st_info);
sec = local_sections[r_symndx];
if (globals->use_rel)
{
@@ -3232,6 +3661,8 @@ elf32_arm_relocate_section (bfd * output_bfd,
r_symndx, symtab_hdr, sym_hashes,
h, sec, relocation,
unresolved_reloc, warned);
+
+ sym_type = h->type;
}
if (h != NULL)
@@ -3244,6 +3675,24 @@ elf32_arm_relocate_section (bfd * output_bfd,
name = bfd_section_name (input_bfd, sec);
}
+ if (r_symndx != 0
+ && r_type != R_ARM_NONE
+ && (h == NULL
+ || h->root.type == bfd_link_hash_defined
+ || h->root.type == bfd_link_hash_defweak)
+ && IS_ARM_TLS_RELOC (r_type) != (sym_type == STT_TLS))
+ {
+ (*_bfd_error_handler)
+ ((sym_type == STT_TLS
+ ? _("%B(%A+0x%lx): %s used with TLS symbol %s")
+ : _("%B(%A+0x%lx): %s used with non-TLS symbol %s")),
+ input_bfd,
+ input_section,
+ (long) rel->r_offset,
+ howto->name,
+ name);
+ }
+
r = elf32_arm_final_link_relocate (howto, input_bfd, output_bfd,
input_section, contents, rel,
relocation, info, sec, name,
@@ -3823,10 +4272,10 @@ elf32_arm_gc_mark_hook (asection * sec,
/* Update the got entry reference counts for the section being removed. */
static bfd_boolean
-elf32_arm_gc_sweep_hook (bfd * abfd ATTRIBUTE_UNUSED,
- struct bfd_link_info * info ATTRIBUTE_UNUSED,
- asection * sec ATTRIBUTE_UNUSED,
- const Elf_Internal_Rela * relocs ATTRIBUTE_UNUSED)
+elf32_arm_gc_sweep_hook (bfd * abfd,
+ struct bfd_link_info * info,
+ asection * sec,
+ const Elf_Internal_Rela * relocs)
{
Elf_Internal_Shdr *symtab_hdr;
struct elf_link_hash_entry **sym_hashes;
@@ -3868,6 +4317,8 @@ elf32_arm_gc_sweep_hook (bfd * abfd ATTRIBUTE_UNUSED,
#ifndef OLD_ARM_ABI
case R_ARM_GOT_PREL:
#endif
+ case R_ARM_TLS_GD32:
+ case R_ARM_TLS_IE32:
if (h != NULL)
{
if (h->got.refcount > 0)
@@ -3880,6 +4331,10 @@ elf32_arm_gc_sweep_hook (bfd * abfd ATTRIBUTE_UNUSED,
}
break;
+ case R_ARM_TLS_LDM32:
+ elf32_arm_hash_table (info)->tls_ldm_got.refcount -= 1;
+ break;
+
case R_ARM_ABS32:
case R_ARM_REL32:
case R_ARM_PC24:
@@ -3915,6 +4370,8 @@ elf32_arm_gc_sweep_hook (bfd * abfd ATTRIBUTE_UNUSED,
if (p->section == sec)
{
p->count -= 1;
+ if (ELF32_R_TYPE (rel->r_info) == R_ARM_REL32)
+ p->pc_count -= 1;
if (p->count == 0)
*pp = p->next;
break;
@@ -3986,6 +4443,14 @@ elf32_arm_check_relocs (bfd *abfd, struct bfd_link_info *info,
#ifndef OLD_ARM_ABI
r_type = arm_real_reloc_type (htab, r_type);
#endif
+
+ if (r_symndx >= NUM_SHDR_ENTRIES (symtab_hdr))
+ {
+ (*_bfd_error_handler) (_("%B: bad symbol index: %d"), abfd,
+ r_symndx);
+ return FALSE;
+ }
+
if (r_symndx < symtab_hdr->sh_info)
h = NULL;
else
@@ -3999,33 +4464,69 @@ elf32_arm_check_relocs (bfd *abfd, struct bfd_link_info *info,
#ifndef OLD_ARM_ABI
case R_ARM_GOT_PREL:
#endif
+ case R_ARM_TLS_GD32:
+ case R_ARM_TLS_IE32:
/* This symbol requires a global offset table entry. */
- if (h != NULL)
- {
- h->got.refcount++;
- }
- else
- {
- bfd_signed_vma *local_got_refcounts;
+ {
+ int tls_type, old_tls_type;
- /* 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;
+ 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;
+ default: tls_type = GOT_NORMAL; break;
+ }
- size = symtab_hdr->sh_info;
- size *= (sizeof (bfd_signed_vma) + sizeof (char));
- local_got_refcounts = 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;
- }
- if (r_type == R_ARM_GOT32)
- break;
- /* Fall through. */
+ if (h != NULL)
+ {
+ h->got.refcount++;
+ old_tls_type = elf32_arm_hash_entry (h)->tls_type;
+ }
+ 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;
+
+ size = symtab_hdr->sh_info;
+ size *= (sizeof (bfd_signed_vma) + sizeof(char));
+ local_got_refcounts = bfd_zalloc (abfd, size);
+ if (local_got_refcounts == NULL)
+ return FALSE;
+ elf_local_got_refcounts (abfd) = local_got_refcounts;
+ elf32_arm_local_got_tls_type (abfd)
+ = (char *) (local_got_refcounts + 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 (old_tls_type != GOT_UNKNOWN && old_tls_type != GOT_NORMAL
+ && tls_type != GOT_NORMAL)
+ tls_type |= old_tls_type;
+
+ if (old_tls_type != tls_type)
+ {
+ if (h != NULL)
+ elf32_arm_hash_entry (h)->tls_type = tls_type;
+ else
+ elf32_arm_local_got_tls_type (abfd) [r_symndx] = tls_type;
+ }
+ }
+ /* Fall through */
+
+ case R_ARM_TLS_LDM32:
+ if (r_type == R_ARM_TLS_LDM32)
+ htab->tls_ldm_got.refcount++;
+ /* Fall through */
case R_ARM_GOTOFF:
case R_ARM_GOTPC:
@@ -4176,8 +4677,11 @@ elf32_arm_check_relocs (bfd *abfd, struct bfd_link_info *info,
*head = p;
p->section = sec;
p->count = 0;
+ p->pc_count = 0;
}
+ if (r_type == R_ARM_REL32)
+ p->pc_count += 1;
p->count += 1;
}
break;
@@ -4404,6 +4908,11 @@ elf32_arm_adjust_dynamic_symbol (struct bfd_link_info * info,
return TRUE;
}
+ /* If there are no non-GOT references, we do not need a copy
+ relocation. */
+ if (!h->non_got_ref)
+ return TRUE;
+
/* This is a reference to a symbol defined by a dynamic object which
is not a function. */
@@ -4571,6 +5080,8 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf)
{
asection *s;
bfd_boolean dyn;
+ int tls_type = elf32_arm_hash_entry (h)->tls_type;
+ int indx;
/* Make sure this symbol is output as a dynamic symbol.
Undefined weak syms won't yet be marked as dynamic. */
@@ -4585,12 +5096,49 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf)
{
s = htab->sgot;
h->got.offset = s->size;
- s->size += 4;
+
+ if (tls_type == GOT_UNKNOWN)
+ abort ();
+
+ if (tls_type == GOT_NORMAL)
+ /* Non-TLS symbols need one GOT slot. */
+ s->size += 4;
+ else
+ {
+ if (tls_type & GOT_TLS_GD)
+ /* R_ARM_TLS_GD32 needs 2 consecutive GOT slots. */
+ s->size += 8;
+ if (tls_type & GOT_TLS_IE)
+ /* R_ARM_TLS_IE32 needs one GOT slot. */
+ s->size += 4;
+ }
+
dyn = htab->root.dynamic_sections_created;
- if ((ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
- || h->root.type != bfd_link_hash_undefweak)
- && (info->shared
- || WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, 0, h)))
+
+ indx = 0;
+ if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, h)
+ && (!info->shared
+ || !SYMBOL_REFERENCES_LOCAL (info, h)))
+ indx = h->dynindx;
+
+ if (tls_type != GOT_NORMAL
+ && (info->shared || indx != 0)
+ && (ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
+ || h->root.type != bfd_link_hash_undefweak))
+ {
+ if (tls_type & GOT_TLS_IE)
+ htab->srelgot->size += sizeof (Elf32_External_Rel);
+
+ if (tls_type & GOT_TLS_GD)
+ htab->srelgot->size += sizeof (Elf32_External_Rel);
+
+ if ((tls_type & GOT_TLS_GD) && indx != 0)
+ htab->srelgot->size += sizeof (Elf32_External_Rel);
+ }
+ else if ((ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
+ || h->root.type != bfd_link_hash_undefweak)
+ && (info->shared
+ || WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, 0, h)))
htab->srelgot->size += sizeof (Elf32_External_Rel);
}
}
@@ -4608,7 +5156,28 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf)
if (info->shared || htab->root.is_relocatable_executable)
{
- /* Discard relocs on undefined weak syms with non-default
+ /* The only reloc that uses pc_count is R_ARM_REL32, which will
+ appear on something like ".long foo - .". We want calls to
+ protected symbols to resolve directly to the function rather
+ than going via the plt. If people want function pointer
+ comparisons to work as expected then they should avoid
+ writing assembly like ".long foo - .". */
+ if (SYMBOL_CALLS_LOCAL (info, h))
+ {
+ struct elf32_arm_relocs_copied **pp;
+
+ for (pp = &eh->relocs_copied; (p = *pp) != NULL; )
+ {
+ p->count -= p->pc_count;
+ p->pc_count = 0;
+ if (p->count == 0)
+ *pp = p->next;
+ else
+ pp = &p->next;
+ }
+ }
+
+ /* Also discard relocs on undefined weak syms with non-default
visibility. */
if (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
&& h->root.type == bfd_link_hash_undefweak)
@@ -4773,6 +5342,7 @@ elf32_arm_size_dynamic_sections (bfd * output_bfd ATTRIBUTE_UNUSED,
symtab_hdr = &elf_tdata (ibfd)->symtab_hdr;
locsymcount = symtab_hdr->sh_info;
end_local_got = local_got + locsymcount;
+ local_tls_type = elf32_arm_local_got_tls_type (ibfd);
s = htab->sgot;
srel = htab->srelgot;
for (; local_got < end_local_got; ++local_got, ++local_tls_type)
@@ -4780,8 +5350,15 @@ elf32_arm_size_dynamic_sections (bfd * output_bfd ATTRIBUTE_UNUSED,
if (*local_got > 0)
{
*local_got = s->size;
- s->size += 4;
- if (info->shared)
+ 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_IE)
+ s->size += 4;
+ if (*local_tls_type == GOT_NORMAL)
+ s->size += 4;
+
+ if (info->shared || *local_tls_type == GOT_TLS_GD)
srel->size += sizeof (Elf32_External_Rel);
}
else
@@ -4789,6 +5366,18 @@ elf32_arm_size_dynamic_sections (bfd * output_bfd ATTRIBUTE_UNUSED,
}
}
+ if (htab->tls_ldm_got.refcount > 0)
+ {
+ /* Allocate two GOT entries and one dynamic relocation (if necessary)
+ for R_ARM_TLS_LDM32 relocations. */
+ htab->tls_ldm_got.offset = htab->sgot->size;
+ htab->sgot->size += 8;
+ if (info->shared)
+ htab->srelgot->size += sizeof (Elf32_External_Rel);
+ }
+ else
+ htab->tls_ldm_got.offset = -1;
+
/* Allocate global sym .plt and .got entries, and space for global
sym dynamic relocs. */
elf_link_hash_traverse (& htab->root, allocate_dynrelocs, info);
@@ -5059,7 +5648,9 @@ elf32_arm_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
+ && (elf32_arm_hash_entry (h)->tls_type & GOT_TLS_GD) == 0
+ && (elf32_arm_hash_entry (h)->tls_type & GOT_TLS_IE) == 0)
{
asection * sgot;
asection * srel;
@@ -5754,6 +6345,8 @@ const struct elf_size_info elf32_arm_size_info = {
#endif
#define ELF_MINPAGESIZE 0x1000
+#define bfd_elf32_mkobject elf32_arm_mkobject
+
#define bfd_elf32_bfd_copy_private_bfd_data elf32_arm_copy_private_bfd_data
#define bfd_elf32_bfd_merge_private_bfd_data elf32_arm_merge_private_bfd_data
#define bfd_elf32_bfd_set_private_flags elf32_arm_set_private_flags