is inserted in arm_build_one_stub(). */
#define THUMB16_BCOND_INSN(X) {(X), THUMB16_TYPE, R_ARM_NONE, 1}
#define THUMB32_INSN(X) {(X), THUMB32_TYPE, R_ARM_NONE, 0}
+#define THUMB32_MOVT(X) {(X), THUMB32_TYPE, R_ARM_THM_MOVT_ABS, 0}
+#define THUMB32_MOVW(X) {(X), THUMB32_TYPE, R_ARM_THM_MOVW_ABS_NC, 0}
#define THUMB32_B_INSN(X, Z) {(X), THUMB32_TYPE, R_ARM_THM_JUMP24, (Z)}
#define ARM_INSN(X) {(X), ARM_TYPE, R_ARM_NONE, 0}
#define ARM_REL_INSN(X, Z) {(X), ARM_TYPE, R_ARM_JUMP24, (Z)}
DATA_WORD (0, R_ARM_ABS32, 0), /* dcd R_ARM_ABS32(X) */
};
+/* Thumb -> Thumb long branch stub in thumb2 encoding. Used on armv7. */
+static const insn_sequence elf32_arm_stub_long_branch_thumb2_only[] =
+{
+ THUMB32_INSN (0xf85ff000), /* ldr.w pc, [pc, #-0] */
+ DATA_WORD (0, R_ARM_ABS32, 0), /* dcd R_ARM_ABS32(x) */
+};
+
+/* Thumb -> Thumb long branch stub. Used for PureCode sections on Thumb2
+ M-profile architectures. */
+static const insn_sequence elf32_arm_stub_long_branch_thumb2_only_pure[] =
+{
+ THUMB32_MOVW (0xf2400c00), /* mov.w ip, R_ARM_MOVW_ABS_NC */
+ THUMB32_MOVT (0xf2c00c00), /* movt ip, R_ARM_MOVT_ABS << 16 */
+ THUMB16_INSN (0x4760), /* bx ip */
+};
+
/* V4T Thumb -> Thumb long branch stub. Using the stack is not
allowed. */
static const insn_sequence elf32_arm_stub_long_branch_v4t_thumb_thumb[] =
DEF_STUB(a8_veneer_b_cond) \
DEF_STUB(a8_veneer_b) \
DEF_STUB(a8_veneer_bl) \
- DEF_STUB(a8_veneer_blx)
+ DEF_STUB(a8_veneer_blx) \
+ DEF_STUB(long_branch_thumb2_only) \
+ DEF_STUB(long_branch_thumb2_only_pure)
#define DEF_STUB(x) arm_stub_##x,
enum elf32_arm_stub_type
{
arm_stub_none,
DEF_STUBS
+ max_stub_type
};
#undef DEF_STUB
arch = bfd_elf_get_obj_attr_int (globals->obfd, OBJ_ATTR_PROC, Tag_CPU_arch);
+ /* Force return logic to be reviewed for each new architecture. */
+ BFD_ASSERT (arch <= TAG_CPU_ARCH_V8
+ || arch == TAG_CPU_ARCH_V8M_BASE
+ || arch == TAG_CPU_ARCH_V8M_MAIN);
+
if (arch == TAG_CPU_ARCH_V6_M
|| arch == TAG_CPU_ARCH_V6S_M
|| arch == TAG_CPU_ARCH_V7E_M
static bfd_boolean
using_thumb2 (struct elf32_arm_link_hash_table *globals)
{
- int arch = bfd_elf_get_obj_attr_int (globals->obfd, OBJ_ATTR_PROC,
- Tag_CPU_arch);
- return arch == TAG_CPU_ARCH_V6T2 || arch >= TAG_CPU_ARCH_V7;
+ int arch;
+ int thumb_isa = bfd_elf_get_obj_attr_int (globals->obfd, OBJ_ATTR_PROC,
+ Tag_THUMB_ISA_use);
+
+ if (thumb_isa)
+ return thumb_isa == 2;
+
+ arch = bfd_elf_get_obj_attr_int (globals->obfd, OBJ_ATTR_PROC, Tag_CPU_arch);
+
+ /* Force return logic to be reviewed for each new architecture. */
+ BFD_ASSERT (arch <= TAG_CPU_ARCH_V8
+ || arch == TAG_CPU_ARCH_V8M_BASE
+ || arch == TAG_CPU_ARCH_V8M_MAIN);
+
+ return (arch == TAG_CPU_ARCH_V6T2
+ || arch == TAG_CPU_ARCH_V7
+ || arch == TAG_CPU_ARCH_V7E_M
+ || arch == TAG_CPU_ARCH_V8
+ || arch == TAG_CPU_ARCH_V8M_MAIN);
+}
+
+/* Determine whether Thumb-2 BL instruction is available. */
+
+static bfd_boolean
+using_thumb2_bl (struct elf32_arm_link_hash_table *globals)
+{
+ int arch =
+ bfd_elf_get_obj_attr_int (globals->obfd, OBJ_ATTR_PROC, Tag_CPU_arch);
+
+ /* Force return logic to be reviewed for each new architecture. */
+ BFD_ASSERT (arch <= TAG_CPU_ARCH_V8
+ || arch == TAG_CPU_ARCH_V8M_BASE
+ || arch == TAG_CPU_ARCH_V8M_MAIN);
+
+ /* Architecture was introduced after ARMv6T2 (eg. ARMv6-M). */
+ return (arch == TAG_CPU_ARCH_V6T2
+ || arch >= TAG_CPU_ARCH_V7);
}
/* Create .plt, .rel(a).plt, .got, .got.plt, .rel(a).got, .dynbss, and
{
const int arch = bfd_elf_get_obj_attr_int (globals->obfd, OBJ_ATTR_PROC,
Tag_CPU_arch);
- return arch == TAG_CPU_ARCH_V6T2
- || arch == TAG_CPU_ARCH_V6K
- || arch == TAG_CPU_ARCH_V7
- || arch == TAG_CPU_ARCH_V7E_M;
-}
-static bfd_boolean
-arch_has_thumb2_nop (struct elf32_arm_link_hash_table *globals)
-{
- const int arch = bfd_elf_get_obj_attr_int (globals->obfd, OBJ_ATTR_PROC,
- Tag_CPU_arch);
- return (arch == TAG_CPU_ARCH_V6T2 || arch == TAG_CPU_ARCH_V7
- || arch == TAG_CPU_ARCH_V7E_M);
+ /* Force return logic to be reviewed for each new architecture. */
+ BFD_ASSERT (arch <= TAG_CPU_ARCH_V8
+ || arch == TAG_CPU_ARCH_V8M_BASE
+ || arch == TAG_CPU_ARCH_V8M_MAIN);
+
+ return (arch == TAG_CPU_ARCH_V6T2
+ || arch == TAG_CPU_ARCH_V6K
+ || arch == TAG_CPU_ARCH_V7
+ || arch == TAG_CPU_ARCH_V8);
}
static bfd_boolean
switch (stub_type)
{
case arm_stub_long_branch_thumb_only:
+ case arm_stub_long_branch_thumb2_only:
+ case arm_stub_long_branch_thumb2_only_pure:
case arm_stub_long_branch_v4t_thumb_arm:
case arm_stub_short_branch_v4t_thumb_arm:
case arm_stub_long_branch_v4t_thumb_arm_pic:
bfd_signed_vma branch_offset;
unsigned int r_type;
struct elf32_arm_link_hash_table * globals;
- int thumb2;
- int thumb_only;
+ bfd_boolean thumb2, thumb2_bl, thumb_only;
enum elf32_arm_stub_type stub_type = arm_stub_none;
int use_plt = 0;
enum arm_st_branch_type branch_type = *actual_branch_type;
union gotplt_union *root_plt;
struct arm_plt_info *arm_plt;
+ int arch;
+ int thumb2_movw;
if (branch_type == ST_BRANCH_LONG)
return stub_type;
return stub_type;
thumb_only = using_thumb_only (globals);
-
thumb2 = using_thumb2 (globals);
+ thumb2_bl = using_thumb2_bl (globals);
+
+ arch = bfd_elf_get_obj_attr_int (globals->obfd, OBJ_ATTR_PROC, Tag_CPU_arch);
+
+ /* True for architectures that implement the thumb2 movw instruction. */
+ thumb2_movw = thumb2 || (arch == TAG_CPU_ARCH_V8M_BASE);
/* Determine where the call point is. */
location = (input_sec->output_offset
but only if this call is not through a PLT entry. Indeed,
PLT stubs handle mode switching already.
*/
- if ((!thumb2
+ if ((!thumb2_bl
&& (branch_offset > THM_MAX_FWD_BRANCH_OFFSET
|| (branch_offset < THM_MAX_BWD_BRANCH_OFFSET)))
- || (thumb2
+ || (thumb2_bl
&& (branch_offset > THM2_MAX_FWD_BRANCH_OFFSET
|| (branch_offset < THM2_MAX_BWD_BRANCH_OFFSET)))
|| (thumb2
/* Thumb to thumb. */
if (!thumb_only)
{
+ if (input_sec->flags & SEC_ELF_PURECODE)
+ (*_bfd_error_handler) (_("%B(%s): warning: long branch "
+ " veneers used in section with "
+ "SHF_ARM_PURECODE section "
+ "attribute is only supported"
+ " for M-profile targets that "
+ "implement the movw "
+ "instruction."));
+
stub_type = (bfd_link_pic (info) | globals->pic_veneer)
/* PIC stubs. */
? ((globals->use_blx
}
else
{
- stub_type = (bfd_link_pic (info) | globals->pic_veneer)
- /* PIC stub. */
- ? arm_stub_long_branch_thumb_only_pic
- /* non-PIC stub. */
- : arm_stub_long_branch_thumb_only;
+ if (thumb2_movw && (input_sec->flags & SEC_ELF_PURECODE))
+ stub_type = arm_stub_long_branch_thumb2_only_pure;
+ else
+ {
+ if (input_sec->flags & SEC_ELF_PURECODE)
+ (*_bfd_error_handler) (_("%B(%s): warning: long branch "
+ " veneers used in section with "
+ "SHF_ARM_PURECODE section "
+ "attribute is only supported"
+ " for M-profile targets that "
+ "implement the movw "
+ "instruction."));
+
+ stub_type = (bfd_link_pic (info) | globals->pic_veneer)
+ /* PIC stub. */
+ ? arm_stub_long_branch_thumb_only_pic
+ /* non-PIC stub. */
+ : (thumb2 ? arm_stub_long_branch_thumb2_only
+ : arm_stub_long_branch_thumb_only);
+ }
}
}
else
{
+ if (input_sec->flags & SEC_ELF_PURECODE)
+ (*_bfd_error_handler) (_("%B(%s): warning: long branch "
+ " veneers used in section with "
+ "SHF_ARM_PURECODE section "
+ "attribute is only supported"
+ " for M-profile targets that "
+ "implement the movw "
+ "instruction."));
+
/* Thumb to arm. */
if (sym_sec != NULL
&& sym_sec->owner != NULL
|| r_type == R_ARM_PLT32
|| r_type == R_ARM_TLS_CALL)
{
+ if (input_sec->flags & SEC_ELF_PURECODE)
+ (*_bfd_error_handler) (_("%B(%s): warning: long branch "
+ " veneers used in section with "
+ "SHF_ARM_PURECODE section "
+ "attribute is only supported"
+ " for M-profile targets that "
+ "implement the movw "
+ "instruction."));
if (branch_type == ST_BRANCH_TO_THUMB)
{
/* Arm to thumb. */
return stub_entry;
}
-/* Find or create a stub section. Returns a pointer to the stub section, and
- the section to which the stub section will be attached (in *LINK_SEC_P).
+/* Whether veneers of type STUB_TYPE require to be in a dedicated output
+ section. */
+
+static bfd_boolean
+arm_dedicated_stub_output_section_required (enum elf32_arm_stub_type stub_type)
+{
+ if (stub_type >= max_stub_type)
+ abort (); /* Should be unreachable. */
+
+ return FALSE;
+}
+
+/* Required alignment (as a power of 2) for the dedicated section holding
+ veneers of type STUB_TYPE, or 0 if veneers of this type are interspersed
+ with input sections. */
+
+static int
+arm_dedicated_stub_output_section_required_alignment
+ (enum elf32_arm_stub_type stub_type)
+{
+ if (stub_type >= max_stub_type)
+ abort (); /* Should be unreachable. */
+
+ BFD_ASSERT (!arm_dedicated_stub_output_section_required (stub_type));
+ return 0;
+}
+
+/* Name of the dedicated output section to put veneers of type STUB_TYPE, or
+ NULL if veneers of this type are interspersed with input sections. */
+
+static const char *
+arm_dedicated_stub_output_section_name (enum elf32_arm_stub_type stub_type)
+{
+ if (stub_type >= max_stub_type)
+ abort (); /* Should be unreachable. */
+
+ BFD_ASSERT (!arm_dedicated_stub_output_section_required (stub_type));
+ return NULL;
+}
+
+/* If veneers of type STUB_TYPE should go in a dedicated output section,
+ returns the address of the hash table field in HTAB holding a pointer to the
+ corresponding input section. Otherwise, returns NULL. */
+
+static asection **
+arm_dedicated_stub_input_section_ptr
+ (struct elf32_arm_link_hash_table *htab ATTRIBUTE_UNUSED,
+ enum elf32_arm_stub_type stub_type)
+{
+ if (stub_type >= max_stub_type)
+ abort (); /* Should be unreachable. */
+
+ BFD_ASSERT (!arm_dedicated_stub_output_section_required (stub_type));
+ return NULL;
+}
+
+/* Find or create a stub section to contain a stub of type STUB_TYPE. SECTION
+ is the section that branch into veneer and can be NULL if stub should go in
+ a dedicated output section. Returns a pointer to the stub section, and the
+ section to which the stub section will be attached (in *LINK_SEC_P).
LINK_SEC_P may be NULL. */
static asection *
elf32_arm_create_or_find_stub_sec (asection **link_sec_p, asection *section,
- struct elf32_arm_link_hash_table *htab)
+ struct elf32_arm_link_hash_table *htab,
+ enum elf32_arm_stub_type stub_type)
{
- asection *link_sec;
- asection *stub_sec;
- asection *out_sec;
-
- link_sec = htab->stub_group[section->id].link_sec;
- BFD_ASSERT (link_sec != NULL);
- stub_sec = htab->stub_group[section->id].stub_sec;
+ asection *link_sec, *out_sec, **stub_sec_p;
+ const char *stub_sec_prefix;
+ bfd_boolean dedicated_output_section =
+ arm_dedicated_stub_output_section_required (stub_type);
+ int align;
- if (stub_sec == NULL)
+ if (dedicated_output_section)
{
- stub_sec = htab->stub_group[link_sec->id].stub_sec;
- if (stub_sec == NULL)
+ bfd *output_bfd = htab->obfd;
+ const char *out_sec_name =
+ arm_dedicated_stub_output_section_name (stub_type);
+ link_sec = NULL;
+ stub_sec_p = arm_dedicated_stub_input_section_ptr (htab, stub_type);
+ stub_sec_prefix = out_sec_name;
+ align = arm_dedicated_stub_output_section_required_alignment (stub_type);
+ out_sec = bfd_get_section_by_name (output_bfd, out_sec_name);
+ if (out_sec == NULL)
{
- size_t namelen;
- bfd_size_type len;
- char *s_name;
-
- namelen = strlen (link_sec->name);
- len = namelen + sizeof (STUB_SUFFIX);
- s_name = (char *) bfd_alloc (htab->stub_bfd, len);
- if (s_name == NULL)
- return NULL;
-
- memcpy (s_name, link_sec->name, namelen);
- memcpy (s_name + namelen, STUB_SUFFIX, sizeof (STUB_SUFFIX));
- out_sec = link_sec->output_section;
- stub_sec = (*htab->add_stub_section) (s_name, out_sec, link_sec,
- htab->nacl_p ? 4 : 3);
- if (stub_sec == NULL)
- return NULL;
- htab->stub_group[link_sec->id].stub_sec = stub_sec;
+ (*_bfd_error_handler) (_("No address assigned to the veneers output "
+ "section %s"), out_sec_name);
+ return NULL;
}
- htab->stub_group[section->id].stub_sec = stub_sec;
}
+ else
+ {
+ link_sec = htab->stub_group[section->id].link_sec;
+ BFD_ASSERT (link_sec != NULL);
+ stub_sec_p = &htab->stub_group[section->id].stub_sec;
+ if (*stub_sec_p == NULL)
+ stub_sec_p = &htab->stub_group[link_sec->id].stub_sec;
+ stub_sec_prefix = link_sec->name;
+ out_sec = link_sec->output_section;
+ align = htab->nacl_p ? 4 : 3;
+ }
+
+ if (*stub_sec_p == NULL)
+ {
+ size_t namelen;
+ bfd_size_type len;
+ char *s_name;
+
+ namelen = strlen (stub_sec_prefix);
+ len = namelen + sizeof (STUB_SUFFIX);
+ s_name = (char *) bfd_alloc (htab->stub_bfd, len);
+ if (s_name == NULL)
+ return NULL;
+
+ memcpy (s_name, stub_sec_prefix, namelen);
+ memcpy (s_name + namelen, STUB_SUFFIX, sizeof (STUB_SUFFIX));
+ *stub_sec_p = (*htab->add_stub_section) (s_name, out_sec, link_sec,
+ align);
+ if (*stub_sec_p == NULL)
+ return NULL;
+
+ out_sec->flags |= SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_CODE
+ | SEC_HAS_CONTENTS | SEC_RELOC | SEC_IN_MEMORY
+ | SEC_KEEP;
+ }
+
+ if (!dedicated_output_section)
+ htab->stub_group[section->id].stub_sec = *stub_sec_p;
if (link_sec_p)
*link_sec_p = link_sec;
- return stub_sec;
+ return *stub_sec_p;
}
/* Add a new stub entry to the stub hash. Not all fields of the new
stub entry are initialised. */
static struct elf32_arm_stub_hash_entry *
-elf32_arm_add_stub (const char *stub_name,
- asection *section,
- struct elf32_arm_link_hash_table *htab)
+elf32_arm_add_stub (const char *stub_name, asection *section,
+ struct elf32_arm_link_hash_table *htab,
+ enum elf32_arm_stub_type stub_type)
{
asection *link_sec;
asection *stub_sec;
struct elf32_arm_stub_hash_entry *stub_entry;
- stub_sec = elf32_arm_create_or_find_stub_sec (&link_sec, section, htab);
+ stub_sec = elf32_arm_create_or_find_stub_sec (&link_sec, section, htab,
+ stub_type);
if (stub_sec == NULL)
return NULL;
case arm_stub_long_branch_any_any:
case arm_stub_long_branch_v4t_arm_thumb:
case arm_stub_long_branch_thumb_only:
+ case arm_stub_long_branch_thumb2_only:
+ case arm_stub_long_branch_thumb2_only_pure:
case arm_stub_long_branch_v4t_thumb_thumb:
case arm_stub_long_branch_v4t_thumb_arm:
case arm_stub_short_branch_v4t_thumb_arm:
}
}
+/* Returns whether stubs of type STUB_TYPE take over the symbol they are
+ veneering (TRUE) or have their own symbol (FALSE). */
+
+static bfd_boolean
+arm_stub_sym_claimed (enum elf32_arm_stub_type stub_type)
+{
+ if (stub_type >= max_stub_type)
+ abort (); /* Should be unreachable. */
+
+ return FALSE;
+}
+
+/* Returns the padding needed for the dedicated section used stubs of type
+ STUB_TYPE. */
+
+static int
+arm_dedicated_stub_section_padding (enum elf32_arm_stub_type stub_type)
+{
+ if (stub_type >= max_stub_type)
+ abort (); /* Should be unreachable. */
+
+ return 0;
+}
+
static bfd_boolean
arm_build_one_stub (struct bfd_hash_entry *gen_entry,
void * in_arg)
char *stub_name;
struct elf32_arm_stub_hash_entry *stub_entry;
unsigned int r_type;
+ bfd_boolean sym_claimed = arm_stub_sym_claimed (stub_type);
BFD_ASSERT (stub_type != arm_stub_none);
*new_stub = FALSE;
- BFD_ASSERT (irela);
- BFD_ASSERT (section);
+ if (sym_claimed)
+ stub_name = sym_name;
+ else
+ {
+ BFD_ASSERT (irela);
+ BFD_ASSERT (section);
- /* Support for grouping stub sections. */
- id_sec = htab->stub_group[section->id].link_sec;
+ /* Support for grouping stub sections. */
+ id_sec = htab->stub_group[section->id].link_sec;
- /* Get the name of this stub. */
- stub_name = elf32_arm_stub_name (id_sec, sym_sec, hash, irela, stub_type);
- if (!stub_name)
- return FALSE;
+ /* Get the name of this stub. */
+ stub_name = elf32_arm_stub_name (id_sec, sym_sec, hash, irela,
+ stub_type);
+ if (!stub_name)
+ return FALSE;
+ }
stub_entry = arm_stub_hash_lookup (&htab->stub_hash_table, stub_name, FALSE,
FALSE);
/* The proper stub has already been created, just update its value. */
if (stub_entry != NULL)
{
- free (stub_name);
+ if (!sym_claimed)
+ free (stub_name);
stub_entry->target_value = sym_value;
return TRUE;
}
- stub_entry = elf32_arm_add_stub (stub_name, section, htab);
+ stub_entry = elf32_arm_add_stub (stub_name, section, htab, stub_type);
if (stub_entry == NULL)
{
- free (stub_name);
+ if (!sym_claimed)
+ free (stub_name);
return FALSE;
}
stub_entry->h = hash;
stub_entry->branch_type = branch_type;
- if (sym_name == NULL)
- sym_name = "unnamed";
- stub_entry->output_name = (char *)
- bfd_alloc (htab->stub_bfd, sizeof (THUMB2ARM_GLUE_ENTRY_NAME)
- + strlen (sym_name));
- if (stub_entry->output_name == NULL)
+ if (sym_claimed)
+ stub_entry->output_name = sym_name;
+ else
{
- free (stub_name);
- return FALSE;
- }
+ if (sym_name == NULL)
+ sym_name = "unnamed";
+ stub_entry->output_name = (char *)
+ bfd_alloc (htab->stub_bfd, sizeof (THUMB2ARM_GLUE_ENTRY_NAME)
+ + strlen (sym_name));
+ if (stub_entry->output_name == NULL)
+ {
+ free (stub_name);
+ return FALSE;
+ }
- /* For historical reasons, use the existing names for ARM-to-Thumb and
- Thumb-to-ARM stubs. */
- r_type = ELF32_R_TYPE (irela->r_info);
- if ((r_type == (unsigned int) R_ARM_THM_CALL
- || r_type == (unsigned int) R_ARM_THM_JUMP24
- || r_type == (unsigned int) R_ARM_THM_JUMP19)
- && branch_type == ST_BRANCH_TO_ARM)
- sprintf (stub_entry->output_name, THUMB2ARM_GLUE_ENTRY_NAME, sym_name);
- else if ((r_type == (unsigned int) R_ARM_CALL
- || r_type == (unsigned int) R_ARM_JUMP24)
- && branch_type == ST_BRANCH_TO_THUMB)
- sprintf (stub_entry->output_name, ARM2THUMB_GLUE_ENTRY_NAME, sym_name);
- else
- sprintf (stub_entry->output_name, STUB_ENTRY_NAME, sym_name);
+ /* For historical reasons, use the existing names for ARM-to-Thumb and
+ Thumb-to-ARM stubs. */
+ r_type = ELF32_R_TYPE (irela->r_info);
+ if ((r_type == (unsigned int) R_ARM_THM_CALL
+ || r_type == (unsigned int) R_ARM_THM_JUMP24
+ || r_type == (unsigned int) R_ARM_THM_JUMP19)
+ && branch_type == ST_BRANCH_TO_ARM)
+ sprintf (stub_entry->output_name, THUMB2ARM_GLUE_ENTRY_NAME, sym_name);
+ else if ((r_type == (unsigned int) R_ARM_CALL
+ || r_type == (unsigned int) R_ARM_JUMP24)
+ && branch_type == ST_BRANCH_TO_THUMB)
+ sprintf (stub_entry->output_name, ARM2THUMB_GLUE_ENTRY_NAME, sym_name);
+ else
+ sprintf (stub_entry->output_name, STUB_ENTRY_NAME, sym_name);
+ }
*new_stub = TRUE;
return TRUE;
bfd *input_bfd;
unsigned int bfd_indx;
asection *stub_sec;
+ enum elf32_arm_stub_type stub_type;
bfd_boolean stub_changed = FALSE;
unsigned prev_num_a8_fixes = num_a8_fixes;
for (; irela < irelaend; irela++)
{
unsigned int r_type, r_indx;
- enum elf32_arm_stub_type stub_type;
asection *sym_sec;
bfd_vma sym_value;
bfd_vma destination;
+ sym_sec->output_offset
+ sym_sec->output_section->vma);
st_type = ELF_ST_TYPE (sym->st_info);
- branch_type = ARM_SYM_BRANCH_TYPE (sym);
+ branch_type =
+ ARM_GET_SYM_BRANCH_TYPE (sym->st_target_internal);
sym_name
= bfd_elf_string_from_elf_section (input_bfd,
symtab_hdr->sh_link,
goto error_ret_free_internal;
}
st_type = hash->root.type;
- branch_type = hash->root.target_internal;
+ branch_type =
+ ARM_GET_SYM_BRANCH_TYPE (hash->root.target_internal);
sym_name = hash->root.root.root.string;
}
!= 0)
goto error_ret_free_local;
}
+
+ if (local_syms != NULL
+ && symtab_hdr->contents != (unsigned char *) local_syms)
+ {
+ if (!info->keep_memory)
+ free (local_syms);
+ else
+ symtab_hdr->contents = (unsigned char *) local_syms;
+ }
}
if (prev_num_a8_fixes != num_a8_fixes)
stub_sec->size = 0;
}
+ /* Compute stub section size, considering padding. */
bfd_hash_traverse (&htab->stub_hash_table, arm_size_one_stub, htab);
+ for (stub_type = arm_stub_none + 1; stub_type < max_stub_type;
+ stub_type++)
+ {
+ int size, padding;
+ asection **stub_sec_p;
+
+ padding = arm_dedicated_stub_section_padding (stub_type);
+ stub_sec_p = arm_dedicated_stub_input_section_ptr (htab, stub_type);
+ /* Skip if no stub input section or no stub section padding
+ required. */
+ if ((stub_sec_p != NULL && *stub_sec_p == NULL) || padding == 0)
+ continue;
+ /* Stub section padding required but no dedicated section. */
+ BFD_ASSERT (stub_sec_p);
+
+ size = (*stub_sec_p)->size;
+ size = (size + padding - 1) & ~(padding - 1);
+ (*stub_sec_p)->size = size;
+ }
/* Add Cortex-A8 erratum veneers to stub section sizes too. */
if (htab->fix_cortex_a8)
for (i = 0; i < num_a8_fixes; i++)
{
stub_sec = elf32_arm_create_or_find_stub_sec (NULL,
- a8_fixes[i].section, htab);
+ a8_fixes[i].section, htab, a8_fixes[i].stub_type);
if (stub_sec == NULL)
- goto error_ret_free_local;
+ return FALSE;
stub_sec->size
+= find_stub_size_and_template (a8_fixes[i].stub_type, NULL,
if (!strstr (stub_sec->name, STUB_SUFFIX))
continue;
- /* Allocate memory to hold the linker stubs. */
+ /* Allocate memory to hold the linker stubs. Zeroing the stub sections
+ must at least be done for stub section requiring padding. */
size = stub_sec->size;
stub_sec->contents = (unsigned char *) bfd_zalloc (htab->stub_bfd, size);
if (stub_sec->contents == NULL && size != 0)
&& arm_make_glue_section (abfd, STM32L4XX_ERRATUM_VENEER_SECTION_NAME);
}
+/* Mark output sections of veneers needing a dedicated one with SEC_KEEP. This
+ ensures they are not marked for deletion by
+ strip_excluded_output_sections () when veneers are going to be created
+ later. Not doing so would trigger assert on empty section size in
+ lang_size_sections_1 (). */
+
+void
+bfd_elf32_arm_keep_private_stub_output_sections (struct bfd_link_info *info)
+{
+ enum elf32_arm_stub_type stub_type;
+
+ /* If we are only performing a partial
+ link do not bother adding the glue. */
+ if (bfd_link_relocatable (info))
+ return;
+
+ for (stub_type = arm_stub_none + 1; stub_type < max_stub_type; stub_type++)
+ {
+ asection *out_sec;
+ const char *out_sec_name;
+
+ if (!arm_dedicated_stub_output_section_required (stub_type))
+ continue;
+
+ out_sec_name = arm_dedicated_stub_output_section_name (stub_type);
+ out_sec = bfd_get_section_by_name (info->output_bfd, out_sec_name);
+ if (out_sec != NULL)
+ out_sec->flags |= SEC_KEEP;
+ }
+}
+
/* Select a BFD to be used to hold the sections used by the glue code.
This function is called from the linker scripts in ld/emultempl/
{armelf/pe}.em. */
/* This one is a call from arm code. We need to look up
the target of the call. If it is a thumb target, we
insert glue. */
- if (h->target_internal == ST_BRANCH_TO_THUMB)
+ if (ARM_GET_SYM_BRANCH_TYPE (h->target_internal)
+ == ST_BRANCH_TO_THUMB)
record_arm_to_thumb_glue (link_info, h);
break;
if (!is_local)
/* add r0,pc; ldr r0, [r0] */
insn = 0x44786800;
- else if (arch_has_thumb2_nop (globals))
+ else if (using_thumb2 (globals))
/* nop.w */
insn = 0xf3af8000;
else
bfd_signed_vma signed_check;
int bitsize;
const int thumb2 = using_thumb2 (globals);
+ const int thumb2_bl = using_thumb2_bl (globals);
/* A branch to an undefined weak symbol is turned into a jump to
the next instruction unless a PLT entry will be created.
if (h && h->root.type == bfd_link_hash_undefweak
&& plt_offset == (bfd_vma) -1)
{
- if (arch_has_thumb2_nop (globals))
+ if (thumb2)
{
bfd_put_16 (input_bfd, 0xf3af, hit_data);
bfd_put_16 (input_bfd, 0x8000, hit_data + 2);
this relocation according to whether we're relocating for
Thumb-2 or not. */
bitsize = howto->bitsize;
- if (!thumb2)
+ if (!thumb2_bl)
bitsize -= 2;
reloc_signed_max = (1 << (bitsize - 1)) - 1;
reloc_signed_min = ~reloc_signed_max;
&& r_symndx != STN_UNDEF
&& bfd_is_und_section (sec)
&& ELF_ST_BIND (sym->st_info) != STB_WEAK)
- {
- if (!info->callbacks->undefined_symbol
- (info, bfd_elf_string_from_elf_section
- (input_bfd, symtab_hdr->sh_link, sym->st_name),
- input_bfd, input_section,
- rel->r_offset, TRUE))
- return FALSE;
- }
+ (*info->callbacks->undefined_symbol)
+ (info, bfd_elf_string_from_elf_section
+ (input_bfd, symtab_hdr->sh_link, sym->st_name),
+ input_bfd, input_section,
+ rel->r_offset, TRUE);
if (globals->use_rel)
{
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 ((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, sym_type,
- (h ? h->target_internal
- : ARM_SYM_BRANCH_TYPE (sym)), h,
- &unresolved_reloc, &error_message);
+ if (r == bfd_reloc_continue)
+ {
+ unsigned char branch_type =
+ h ? ARM_GET_SYM_BRANCH_TYPE (h->target_internal)
+ : ARM_GET_SYM_BRANCH_TYPE (sym->st_target_internal);
+
+ r = elf32_arm_final_link_relocate (howto, input_bfd, output_bfd,
+ input_section, contents, rel,
+ relocation, info, sec, name,
+ sym_type, branch_type, 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
/* If the overflowing reloc was to an undefined symbol,
we have already printed one error message and there
is no point complaining again. */
- if ((! h ||
- h->root.type != bfd_link_hash_undefined)
- && (!((*info->callbacks->reloc_overflow)
- (info, (h ? &h->root : NULL), name, howto->name,
- (bfd_vma) 0, input_bfd, input_section,
- rel->r_offset))))
- return FALSE;
+ if (!h || h->root.type != bfd_link_hash_undefined)
+ (*info->callbacks->reloc_overflow)
+ (info, (h ? &h->root : NULL), name, howto->name,
+ (bfd_vma) 0, input_bfd, input_section, rel->r_offset);
break;
case bfd_reloc_undefined:
- if (!((*info->callbacks->undefined_symbol)
- (info, name, input_bfd, input_section,
- rel->r_offset, TRUE)))
- return FALSE;
+ (*info->callbacks->undefined_symbol)
+ (info, name, input_bfd, input_section, rel->r_offset, TRUE);
break;
case bfd_reloc_outofrange:
common_error:
BFD_ASSERT (error_message != NULL);
- if (!((*info->callbacks->reloc_dangerous)
- (info, error_message, input_bfd, input_section,
- rel->r_offset)))
- return FALSE;
+ (*info->callbacks->reloc_dangerous)
+ (info, error_message, input_bfd, input_section, rel->r_offset);
break;
}
}
/* Make sure the function is not marked as Thumb, in case
it is the target of an ABS32 relocation, which will
point to the PLT entry. */
- h->target_internal = ST_BRANCH_TO_ARM;
+ ARM_SET_SYM_BRANCH_TYPE (h->target_internal, ST_BRANCH_TO_ARM);
}
/* VxWorks executables have a second set of relocations for
/* Allocate stubs for exported Thumb functions on v4t. */
if (!htab->use_blx && h->dynindx != -1
&& h->def_regular
- && h->target_internal == ST_BRANCH_TO_THUMB
+ && ARM_GET_SYM_BRANCH_TYPE (h->target_internal) == ST_BRANCH_TO_THUMB
&& ELF_ST_VISIBILITY (h->other) == STV_DEFAULT)
{
struct elf_link_hash_entry * th;
myh = (struct elf_link_hash_entry *) bh;
myh->type = ELF_ST_INFO (STB_LOCAL, STT_FUNC);
myh->forced_local = 1;
- myh->target_internal = ST_BRANCH_TO_THUMB;
+ ARM_SET_SYM_BRANCH_TYPE (myh->target_internal, ST_BRANCH_TO_THUMB);
eh->export_glue = myh;
th = record_arm_to_thumb_glue (info, h);
/* Point the symbol at the stub. */
h->type = ELF_ST_INFO (ELF_ST_BIND (h->type), STT_FUNC);
- h->target_internal = ST_BRANCH_TO_ARM;
+ ARM_SET_SYM_BRANCH_TYPE (h->target_internal, ST_BRANCH_TO_ARM);
h->root.u.def.section = th->root.u.def.section;
h->root.u.def.value = th->root.u.def.value & ~1;
}
/* At least one non-call relocation references this .iplt entry,
so the .iplt entry is the function's canonical address. */
sym->st_info = ELF_ST_INFO (ELF_ST_BIND (sym->st_info), STT_FUNC);
- sym->st_target_internal = ST_BRANCH_TO_ARM;
+ ARM_SET_SYM_BRANCH_TYPE (sym->st_target_internal, ST_BRANCH_TO_ARM);
sym->st_shndx = (_bfd_elf_section_from_bfd_section
(output_bfd, htab->root.iplt->output_section));
sym->st_value = (h->plt.offset
goto get_vma_if_bpabi;
case DT_PLTGOT:
- name = ".got";
+ name = htab->symbian_p ? ".got" : ".got.plt";
goto get_vma;
case DT_JMPREL:
name = RELOC_SECTION (htab, ".plt");
get_vma:
- s = bfd_get_section_by_name (output_bfd, name);
+ s = bfd_get_linker_section (dynobj, name);
if (s == NULL)
{
- /* PR ld/14397: Issue an error message if a required section is missing. */
(*_bfd_error_handler)
- (_("error: required section '%s' not found in the linker script"), name);
+ (_("could not find section %s"), name);
bfd_set_error (bfd_error_invalid_operation);
return FALSE;
}
if (!htab->symbian_p)
- dyn.d_un.d_ptr = s->vma;
+ dyn.d_un.d_ptr = s->output_section->vma + s->output_offset;
else
/* In the BPABI, tags in the PT_DYNAMIC section point
at the file offset, not the memory address, for the
convenience of the post linker. */
- dyn.d_un.d_ptr = s->filepos;
+ dyn.d_un.d_ptr = s->output_section->filepos + s->output_offset;
bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon);
break;
eh = elf_link_hash_lookup (elf_hash_table (info), name,
FALSE, FALSE, TRUE);
- if (eh != NULL && eh->target_internal == ST_BRANCH_TO_THUMB)
+ if (eh != NULL
+ && ARM_GET_SYM_BRANCH_TYPE (eh->target_internal)
+ == ST_BRANCH_TO_THUMB)
{
dyn.d_un.d_val |= 1;
bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon);
}
/* Scan segment to set p_flags attribute if it contains only sections with
- SHF_ARM_NOREAD flag. */
+ SHF_ARM_PURECODE flag. */
for (m = elf_seg_map (abfd); m != NULL; m = m->next)
{
unsigned int j;
continue;
for (j = 0; j < m->count; j++)
{
- if (!(elf_section_flags (m->sections[j]) & SHF_ARM_NOREAD))
+ if (!(elf_section_flags (m->sections[j]) & SHF_ARM_PURECODE))
break;
}
if (j == m->count)
hdr->sh_flags |= SHF_LINK_ORDER;
}
- if (sec->flags & SEC_ELF_NOREAD)
- hdr->sh_flags |= SHF_ARM_NOREAD;
+ if (sec->flags & SEC_ELF_PURECODE)
+ hdr->sh_flags |= SHF_ARM_PURECODE;
return TRUE;
}
&h->plt, &eh->plt);
}
+/* Bind a veneered symbol to its veneer identified by its hash entry
+ STUB_ENTRY. The veneered location thus loose its symbol. */
+
+static void
+arm_stub_claim_sym (struct elf32_arm_stub_hash_entry *stub_entry)
+{
+ struct elf32_arm_link_hash_entry *hash = stub_entry->h;
+
+ BFD_ASSERT (hash);
+ hash->root.root.u.def.section = stub_entry->stub_sec;
+ hash->root.root.u.def.value = stub_entry->stub_offset;
+ hash->root.size = stub_entry->stub_size;
+}
+
/* Output a single local symbol for a generated stub. */
static bfd_boolean
return TRUE;
addr = (bfd_vma) stub_entry->stub_offset;
- stub_name = stub_entry->output_name;
-
template_sequence = stub_entry->stub_template;
- switch (template_sequence[0].type)
+
+ if (arm_stub_sym_claimed (stub_entry->stub_type))
+ arm_stub_claim_sym (stub_entry);
+ else
{
- case ARM_TYPE:
- if (!elf32_arm_output_stub_sym (osi, stub_name, addr, stub_entry->stub_size))
- return FALSE;
- break;
- case THUMB16_TYPE:
- case THUMB32_TYPE:
- if (!elf32_arm_output_stub_sym (osi, stub_name, addr | 1,
- stub_entry->stub_size))
- return FALSE;
- break;
- default:
- BFD_FAIL ();
- return 0;
+ stub_name = stub_entry->output_name;
+ switch (template_sequence[0].type)
+ {
+ case ARM_TYPE:
+ if (!elf32_arm_output_stub_sym (osi, stub_name, addr,
+ stub_entry->stub_size))
+ return FALSE;
+ break;
+ case THUMB16_TYPE:
+ case THUMB32_TYPE:
+ if (!elf32_arm_output_stub_sym (osi, stub_name, addr | 1,
+ stub_entry->stub_size))
+ return FALSE;
+ break;
+ default:
+ BFD_FAIL ();
+ return 0;
+ }
}
prev_type = DATA_TYPE;
{
if (!bfd_elf32_swap_symbol_in (abfd, psrc, pshn, dst))
return FALSE;
+ dst->st_target_internal = 0;
/* New EABI objects mark thumb function symbols by setting the low bit of
the address. */
if (dst->st_value & 1)
{
dst->st_value &= ~(bfd_vma) 1;
- dst->st_target_internal = ST_BRANCH_TO_THUMB;
+ ARM_SET_SYM_BRANCH_TYPE (dst->st_target_internal,
+ ST_BRANCH_TO_THUMB);
}
else
- dst->st_target_internal = ST_BRANCH_TO_ARM;
+ ARM_SET_SYM_BRANCH_TYPE (dst->st_target_internal, ST_BRANCH_TO_ARM);
}
else if (ELF_ST_TYPE (dst->st_info) == STT_ARM_TFUNC)
{
dst->st_info = ELF_ST_INFO (ELF_ST_BIND (dst->st_info), STT_FUNC);
- dst->st_target_internal = ST_BRANCH_TO_THUMB;
+ ARM_SET_SYM_BRANCH_TYPE (dst->st_target_internal, ST_BRANCH_TO_THUMB);
}
else if (ELF_ST_TYPE (dst->st_info) == STT_SECTION)
- dst->st_target_internal = ST_BRANCH_LONG;
+ ARM_SET_SYM_BRANCH_TYPE (dst->st_target_internal, ST_BRANCH_LONG);
else
- dst->st_target_internal = ST_BRANCH_UNKNOWN;
+ ARM_SET_SYM_BRANCH_TYPE (dst->st_target_internal, ST_BRANCH_UNKNOWN);
return TRUE;
}
of the address set, as per the new EABI. We do this unconditionally
because objcopy does not set the elf header flags until after
it writes out the symbol table. */
- if (src->st_target_internal == ST_BRANCH_TO_THUMB)
+ if (ARM_GET_SYM_BRANCH_TYPE (src->st_target_internal) == ST_BRANCH_TO_THUMB)
{
newsym = *src;
if (ELF_ST_TYPE (src->st_info) != STT_GNU_IFUNC)
Elf_Internal_Sym *sym, const char **namep,
flagword *flagsp, asection **secp, bfd_vma *valp)
{
- if ((ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC
- || ELF_ST_BIND (sym->st_info) == STB_GNU_UNIQUE)
+ if (ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC
&& (abfd->flags & DYNAMIC) == 0
&& bfd_get_flavour (info->output_bfd) == bfd_target_elf_flavour)
- elf_tdata (info->output_bfd)->has_gnu_symbols = elf_gnu_symbol_any;
+ elf_tdata (info->output_bfd)->has_gnu_symbols |= elf_gnu_symbol_ifunc;
if (elf32_arm_hash_table (info) == NULL)
return FALSE;
static bfd_boolean
elf32_arm_section_flags (flagword *flags, const Elf_Internal_Shdr * hdr)
{
- if (hdr->sh_flags & SHF_ARM_NOREAD)
- *flags |= SEC_ELF_NOREAD;
+ if (hdr->sh_flags & SHF_ARM_PURECODE)
+ *flags |= SEC_ELF_PURECODE;
return TRUE;
}
static flagword
elf32_arm_lookup_section_flags (char *flag_name)
{
- if (!strcmp (flag_name, "SHF_ARM_NOREAD"))
- return SHF_ARM_NOREAD;
+ if (!strcmp (flag_name, "SHF_ARM_PURECODE"))
+ return SHF_ARM_PURECODE;
return SEC_NO_FLAGS;
}
return FALSE;
}
+/* Returns TRUE if NAME is an ARM mapping symbol.
+ Traditionally the symbols $a, $d and $t have been used.
+ The ARM ELF standard also defines $x (for A64 code). It also allows a
+ period initiated suffix to be added to the symbol: "$[adtx]\.[:sym_char]+".
+ Other tools might also produce $b (Thumb BL), $f, $p, $m and $v, but we do
+ not support them here. $t.x indicates the start of ThumbEE instructions. */
+
+static bfd_boolean
+is_arm_mapping_symbol (const char * name)
+{
+ return name != NULL /* Paranoia. */
+ && name[0] == '$' /* Note: if objcopy --prefix-symbols has been used then
+ the mapping symbols could have acquired a prefix.
+ We do not support this here, since such symbols no
+ longer conform to the ARM ELF ABI. */
+ && (name[1] == 'a' || name[1] == 'd' || name[1] == 't' || name[1] == 'x')
+ && (name[2] == 0 || name[2] == '.');
+ /* FIXME: Strictly speaking the symbol is only a valid mapping symbol if
+ any characters that follow the period are legal characters for the body
+ of a symbol's name. For now we just assume that this is the case. */
+}
+
+/* Make sure that mapping symbols in object files are not removed via the
+ "strip --strip-unneeded" tool. These symbols are needed in order to
+ correctly generate interworking veneers, and for byte swapping code
+ regions. Once an object file has been linked, it is safe to remove the
+ symbols as they will no longer be needed. */
+
+static void
+elf32_arm_backend_symbol_processing (bfd *abfd, asymbol *sym)
+{
+ if (((abfd->flags & (EXEC_P | DYNAMIC)) == 0)
+ && sym->section != bfd_abs_section_ptr
+ && is_arm_mapping_symbol (sym->name))
+ sym->flags |= BSF_KEEP;
+}
+
#undef elf_backend_copy_special_section_fields
#define elf_backend_copy_special_section_fields elf32_arm_copy_special_section_fields
#define elf_backend_begin_write_processing elf32_arm_begin_write_processing
#define elf_backend_add_symbol_hook elf32_arm_add_symbol_hook
#define elf_backend_count_additional_relocs elf32_arm_count_additional_relocs
+#define elf_backend_symbol_processing elf32_arm_backend_symbol_processing
#define elf_backend_can_refcount 1
#define elf_backend_can_gc_sections 1