#define ARM_BX_GLUE_SECTION_NAME ".v4_bx"
#define ARM_BX_GLUE_ENTRY_NAME "__bx_r%d"
+#define STUB_ENTRY_NAME "__%s_veneer"
+
/* The name of the dynamic interpreter. This is put in the .interp
section. */
#define ELF_DYNAMIC_INTERPRETER "/usr/lib/ld.so.1"
{
0x4e02b540, /* push {r6, lr} */
/* ldr r6, [pc, #8] */
- 0xe7fe46fe, /* mov lr, pc */
- /* b.n r6 */
+ 0x473046fe, /* mov lr, pc */
+ /* bx r6 */
0xbf00bd40, /* pop {r6, pc} */
/* nop */
0x00000000, /* dcd R_ARM_ABS32(X) */
{
0xe59fc000, /* ldr r12, [pc] */
0xe08ff00c, /* add pc, pc, ip */
- 0x00000000, /* dcd R_ARM_ABS32(X) */
+ 0x00000000, /* dcd R_ARM_REL32(X) */
};
/* Section name for stubs is the associated section name plus this
/* Where this stub is being called from, or, in the case of combined
stub sections, the first input section in the group. */
asection *id_sec;
+
+ /* The name for the local symbol at the start of this stub. The
+ stub name in the hash table has to be unique; this does not, so
+ it can be friendlier. */
+ char *output_name;
};
/* Used to build a map of a section. This is required for mixed-endian
symbols with Arm stubs. */
struct elf_link_hash_entry *export_glue;
- /* A pointer to the most recently used stub hash entry against this
+ /* A pointer to the most recently used stub hash entry against this
symbol. */
- struct elf32_arm_stub_hash_entry *stub_cache;
+ struct elf32_arm_stub_hash_entry *stub_cache;
};
/* Traverse an arm ELF linker hash table. */
return arch == TAG_CPU_ARCH_V6T2 || arch >= TAG_CPU_ARCH_V7;
}
+static bfd_boolean
+arm_stub_is_thumb (enum elf32_arm_stub_type stub_type)
+{
+ switch (stub_type)
+ {
+ case arm_thumb_thumb_stub_long_branch:
+ case arm_thumb_arm_v4t_stub_long_branch:
+ return TRUE;
+ case arm_stub_none:
+ BFD_FAIL ();
+ return FALSE;
+ break;
+ default:
+ return FALSE;
+ }
+}
+
/* Determine the type of stub needed, if any, for a call. */
static enum elf32_arm_stub_type
int thumb_only;
enum elf32_arm_stub_type stub_type = arm_stub_none;
+ /* We don't know the actual type of destination in case it is of
+ type STT_SECTION: give up */
+ if (st_type == STT_SECTION)
+ return stub_type;
+
globals = elf32_arm_hash_table (info);
thumb_only = using_thumb_only (globals);
return stub_entry;
}
-static void elf32_arm_stub_add_mapping_symbol (struct bfd_link_info * link_info,
- asection *stub_sec,
- char* name,
- bfd_vma val)
-{
- struct bfd_link_hash_entry * bh = NULL;
- struct elf_link_hash_entry * myh;
-
- _bfd_generic_link_add_one_symbol (link_info,
- stub_sec->owner, name,
- BSF_LOCAL, stub_sec, stub_sec->size + val,
- NULL, TRUE, FALSE, &bh);
-
- myh = (struct elf_link_hash_entry *) bh;
- myh->type = ELF_ST_INFO (STB_LOCAL, STT_NOTYPE);
- myh->forced_local = 1;
-}
-
/* 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,
- struct bfd_link_info * link_info,
- enum elf32_arm_stub_type stub_type)
+ struct elf32_arm_link_hash_table *htab)
{
asection *link_sec;
asection *stub_sec;
stub_entry->stub_offset = 0;
stub_entry->id_sec = link_sec;
- switch (stub_type)
- {
- case arm_stub_long_branch:
- elf32_arm_stub_add_mapping_symbol (link_info, stub_sec, "$a", 0);
- elf32_arm_stub_add_mapping_symbol (link_info, stub_sec, "$d", 4);
- break;
- case arm_thumb_v4t_stub_long_branch:
- elf32_arm_stub_add_mapping_symbol (link_info, stub_sec, "$a", 0);
- elf32_arm_stub_add_mapping_symbol (link_info, stub_sec, "$d", 8);
- break;
- case arm_thumb_thumb_stub_long_branch:
- elf32_arm_stub_add_mapping_symbol (link_info, stub_sec, "$t", 0);
- elf32_arm_stub_add_mapping_symbol (link_info, stub_sec, "$d", 12);
- break;
- case arm_thumb_arm_v4t_stub_long_branch:
- elf32_arm_stub_add_mapping_symbol (link_info, stub_sec, "$t", 0);
- elf32_arm_stub_add_mapping_symbol (link_info, stub_sec, "$a", 8);
- elf32_arm_stub_add_mapping_symbol (link_info, stub_sec, "$d", 16);
- break;
- case arm_stub_pic_long_branch:
- elf32_arm_stub_add_mapping_symbol (link_info, stub_sec, "$a", 0);
- elf32_arm_stub_add_mapping_symbol (link_info, stub_sec, "$d", 8);
- break;
- default:
- BFD_FAIL ();
- }
-
return stub_entry;
}
{
case arm_stub_long_branch:
_bfd_final_link_relocate (elf32_arm_howto_from_type (R_ARM_ABS32),
- stub_bfd, stub_sec, stub_sec->contents + 4,
- stub_entry->stub_offset, sym_value, 0);
+ stub_bfd, stub_sec, stub_sec->contents,
+ stub_entry->stub_offset + 4, sym_value, 0);
break;
case arm_thumb_v4t_stub_long_branch:
_bfd_final_link_relocate (elf32_arm_howto_from_type (R_ARM_ABS32),
- stub_bfd, stub_sec, stub_sec->contents + 8,
- stub_entry->stub_offset, sym_value, 0);
+ stub_bfd, stub_sec, stub_sec->contents,
+ stub_entry->stub_offset + 8, sym_value, 0);
break;
case arm_thumb_thumb_stub_long_branch:
_bfd_final_link_relocate (elf32_arm_howto_from_type (R_ARM_ABS32),
- stub_bfd, stub_sec, stub_sec->contents + 12,
- stub_entry->stub_offset, sym_value, 0);
+ stub_bfd, stub_sec, stub_sec->contents,
+ stub_entry->stub_offset + 12, sym_value, 0);
break;
case arm_thumb_arm_v4t_stub_long_branch:
_bfd_final_link_relocate (elf32_arm_howto_from_type (R_ARM_ABS32),
- stub_bfd, stub_sec, stub_sec->contents + 20,
- stub_entry->stub_offset, sym_value, 0);
+ stub_bfd, stub_sec, stub_sec->contents,
+ stub_entry->stub_offset + 16, sym_value, 0);
break;
case arm_stub_pic_long_branch:
/* We want the value relative to the address 8 bytes from the
start of the stub. */
- sym_value -= stub_addr + 8;
-
- _bfd_final_link_relocate (elf32_arm_howto_from_type (R_ARM_ABS32),
- stub_bfd, stub_sec, stub_sec->contents + 8,
- stub_entry->stub_offset, sym_value, 0);
+ _bfd_final_link_relocate (elf32_arm_howto_from_type (R_ARM_REL32),
+ stub_bfd, stub_sec, stub_sec->contents,
+ stub_entry->stub_offset + 8, sym_value, 0);
break;
default:
break;
bfd_vma sym_value;
bfd_vma destination;
struct elf32_arm_link_hash_entry *hash;
+ const char *sym_name;
char *stub_name;
const asection *id_sec;
unsigned char st_type;
sym_value = 0;
destination = 0;
hash = NULL;
+ sym_name = NULL;
if (r_indx < symtab_hdr->sh_info)
{
/* It's a local symbol. */
+ sym_sec->output_offset
+ sym_sec->output_section->vma);
st_type = ELF_ST_TYPE (sym->st_info);
+ sym_name
+ = bfd_elf_string_from_elf_section (input_bfd,
+ symtab_hdr->sh_link,
+ sym->st_name);
}
else
{
goto error_ret_free_internal;
}
st_type = ELF_ST_TYPE (hash->root.type);
+ sym_name = hash->root.root.root.string;
}
/* Determine what (if any) linker stub is needed. */
continue;
}
- stub_entry = elf32_arm_add_stub (stub_name, section, htab, info, stub_type);
+ stub_entry = elf32_arm_add_stub (stub_name, section, htab);
if (stub_entry == NULL)
{
free (stub_name);
stub_entry->stub_type = stub_type;
stub_entry->h = hash;
stub_entry->st_type = st_type;
+
+ if (sym_name == NULL)
+ sym_name = "unnamed";
+ stub_entry->output_name
+ = bfd_alloc (htab->stub_bfd,
+ sizeof (THUMB2ARM_GLUE_ENTRY_NAME)
+ + strlen (sym_name));
+ if (stub_entry->output_name == NULL)
+ {
+ free (stub_name);
+ goto error_ret_free_internal;
+ }
+
+ /* For historical reasons, use the existing names for
+ ARM-to-Thumb and Thumb-to-ARM stubs. */
+ if (r_type == (unsigned int) R_ARM_THM_CALL
+ && st_type != STT_ARM_TFUNC)
+ sprintf (stub_entry->output_name, THUMB2ARM_GLUE_ENTRY_NAME,
+ sym_name);
+ else if (r_type == (unsigned int) R_ARM_CALL
+ && st_type == STT_ARM_TFUNC)
+ sprintf (stub_entry->output_name, ARM2THUMB_GLUE_ENTRY_NAME,
+ sym_name);
+ else
+ sprintf (stub_entry->output_name, STUB_ENTRY_NAME,
+ sym_name);
+
stub_changed = TRUE;
}
/* Convert BL to BLX. */
lower_insn = (lower_insn & ~0x1000) | 0x0800;
}
- else if (elf32_thumb_to_arm_stub
+ else if (r_type != R_ARM_THM_CALL) {
+ if (elf32_thumb_to_arm_stub
(info, sym_name, input_bfd, output_bfd, input_section,
hit_data, sym_sec, rel->r_offset, signed_addend, value,
error_message))
else
return bfd_reloc_dangerous;
}
+ }
else if (sym_flags == STT_ARM_TFUNC && globals->use_blx
&& r_type == R_ARM_THM_CALL)
{
||
(thumb2
&& (branch_offset > THM2_MAX_FWD_BRANCH_OFFSET
- || (branch_offset < THM2_MAX_BWD_BRANCH_OFFSET))))
+ || (branch_offset < THM2_MAX_BWD_BRANCH_OFFSET)))
+ || ((sym_flags != STT_ARM_TFUNC) && !globals->use_blx))
{
/* The target is out of reach or we are changing modes, so
redirect the branch to the local stub for this
+ stub_entry->stub_sec->output_offset
+ stub_entry->stub_sec->output_section->vma);
- /* This call becomes a call to Arm for sure. Force BLX. */
- lower_insn = (lower_insn & ~0x1000) | 0x0800;
+ /* If this call becomes a call to Arm, force BLX. */
+ if (globals->use_blx)
+ {
+ if ((stub_entry
+ && !arm_stub_is_thumb (stub_entry->stub_type))
+ || (sym_flags != STT_ARM_TFUNC))
+ lower_insn = (lower_insn & ~0x1000) | 0x0800;
+ }
}
}
in_flags = elf_elfheader (ibfd)->e_flags;
out_flags = elf_elfheader (obfd)->e_flags;
+ /* In theory there is no reason why we couldn't handle this. However
+ in practice it isn't even close to working and there is no real
+ reason to want it. */
+ if (EF_ARM_EABI_VERSION (in_flags) >= EF_ARM_EABI_VER4
+ && !(ibfd->flags & DYNAMIC)
+ && (in_flags & EF_ARM_BE8))
+ {
+ _bfd_error_handler (_("ERROR: %B is already in final BE8 format"),
+ ibfd);
+ return FALSE;
+ }
+
if (!elf_flags_init (obfd))
{
/* If the input is the default architecture and had the default
};
-/* Output a single PLT mapping symbol. */
+/* Output a single mapping symbol. */
static bfd_boolean
-elf32_arm_ouput_plt_map_sym (output_arch_syminfo *osi,
- enum map_symbol_type type,
- bfd_vma offset)
+elf32_arm_output_map_sym (output_arch_syminfo *osi,
+ enum map_symbol_type type,
+ bfd_vma offset)
{
static const char *names[3] = {"$a", "$t", "$d"};
struct elf32_arm_link_hash_table *htab;
addr = h->plt.offset;
if (htab->symbian_p)
{
- if (!elf32_arm_ouput_plt_map_sym (osi, ARM_MAP_ARM, addr))
+ if (!elf32_arm_output_map_sym (osi, ARM_MAP_ARM, addr))
return FALSE;
- if (!elf32_arm_ouput_plt_map_sym (osi, ARM_MAP_DATA, addr + 4))
+ if (!elf32_arm_output_map_sym (osi, ARM_MAP_DATA, addr + 4))
return FALSE;
}
else if (htab->vxworks_p)
{
- if (!elf32_arm_ouput_plt_map_sym (osi, ARM_MAP_ARM, addr))
+ if (!elf32_arm_output_map_sym (osi, ARM_MAP_ARM, addr))
return FALSE;
- if (!elf32_arm_ouput_plt_map_sym (osi, ARM_MAP_DATA, addr + 8))
+ if (!elf32_arm_output_map_sym (osi, ARM_MAP_DATA, addr + 8))
return FALSE;
- if (!elf32_arm_ouput_plt_map_sym (osi, ARM_MAP_ARM, addr + 12))
+ if (!elf32_arm_output_map_sym (osi, ARM_MAP_ARM, addr + 12))
return FALSE;
- if (!elf32_arm_ouput_plt_map_sym (osi, ARM_MAP_DATA, addr + 20))
+ if (!elf32_arm_output_map_sym (osi, ARM_MAP_DATA, addr + 20))
return FALSE;
}
else
if (thumb_refs > 0)
{
- if (!elf32_arm_ouput_plt_map_sym (osi, ARM_MAP_THUMB, addr - 4))
+ if (!elf32_arm_output_map_sym (osi, ARM_MAP_THUMB, addr - 4))
return FALSE;
}
#ifdef FOUR_WORD_PLT
- if (!elf32_arm_ouput_plt_map_sym (osi, ARM_MAP_ARM, addr))
+ if (!elf32_arm_output_map_sym (osi, ARM_MAP_ARM, addr))
return FALSE;
- if (!elf32_arm_ouput_plt_map_sym (osi, ARM_MAP_DATA, addr + 12))
+ if (!elf32_arm_output_map_sym (osi, ARM_MAP_DATA, addr + 12))
return FALSE;
#else
/* A three-word PLT with no Thumb thunk contains only Arm code,
entries with thumb thunks. */
if (thumb_refs > 0 || addr == 20)
{
- if (!elf32_arm_ouput_plt_map_sym (osi, ARM_MAP_ARM, addr))
+ if (!elf32_arm_output_map_sym (osi, ARM_MAP_ARM, addr))
return FALSE;
}
#endif
return TRUE;
}
+/* Output a single local symbol for a generated stub. */
+
+static bfd_boolean
+elf32_arm_output_stub_sym (output_arch_syminfo *osi, const char *name,
+ bfd_vma offset, bfd_vma size)
+{
+ struct elf32_arm_link_hash_table *htab;
+ Elf_Internal_Sym sym;
+
+ htab = elf32_arm_hash_table (osi->info);
+ sym.st_value = osi->sec->output_section->vma
+ + osi->sec->output_offset
+ + offset;
+ sym.st_size = size;
+ sym.st_other = 0;
+ sym.st_info = ELF_ST_INFO (STB_LOCAL, STT_FUNC);
+ sym.st_shndx = osi->sec_shndx;
+ if (!osi->func (osi->finfo, name, &sym, osi->sec, NULL))
+ return FALSE;
+ return TRUE;
+}
+
+static bfd_boolean
+arm_map_one_stub (struct bfd_hash_entry *gen_entry,
+ PTR in_arg)
+{
+ struct elf32_arm_stub_hash_entry *stub_entry;
+ struct bfd_link_info *info;
+ struct elf32_arm_link_hash_table *htab;
+ asection *stub_sec;
+ bfd_vma addr;
+ char *stub_name;
+
+ /* Massage our args to the form they really have. */
+ stub_entry = (struct elf32_arm_stub_hash_entry *) gen_entry;
+
+ output_arch_syminfo *osi = (output_arch_syminfo *) in_arg;
+ info = osi->info;
+
+ htab = elf32_arm_hash_table (info);
+ stub_sec = stub_entry->stub_sec;
+
+ /* Ensure this stub is attached to the current section being
+ processed. */
+ if (stub_sec != osi->sec)
+ return TRUE;
+
+ addr = (bfd_vma) stub_entry->stub_offset;
+ stub_name = stub_entry->output_name;
+
+ switch (stub_entry->stub_type)
+ {
+ case arm_stub_long_branch:
+ if (!elf32_arm_output_stub_sym (osi, stub_name, addr, 8))
+ return FALSE;
+ if (!elf32_arm_output_map_sym (osi, ARM_MAP_ARM, addr))
+ return FALSE;
+ if (!elf32_arm_output_map_sym (osi, ARM_MAP_DATA, addr + 4))
+ return FALSE;
+ break;
+ case arm_thumb_v4t_stub_long_branch:
+ if (!elf32_arm_output_stub_sym (osi, stub_name, addr, 12))
+ return FALSE;
+ if (!elf32_arm_output_map_sym (osi, ARM_MAP_ARM, addr))
+ return FALSE;
+ if (!elf32_arm_output_map_sym (osi, ARM_MAP_DATA, addr + 8))
+ return FALSE;
+ break;
+ case arm_thumb_thumb_stub_long_branch:
+ if (!elf32_arm_output_stub_sym (osi, stub_name, addr + 1, 16))
+ return FALSE;
+ if (!elf32_arm_output_map_sym (osi, ARM_MAP_THUMB, addr))
+ return FALSE;
+ if (!elf32_arm_output_map_sym (osi, ARM_MAP_DATA, addr + 12))
+ return FALSE;
+ break;
+ case arm_thumb_arm_v4t_stub_long_branch:
+ if (!elf32_arm_output_stub_sym (osi, stub_name, addr + 1, 20))
+ return FALSE;
+ if (!elf32_arm_output_map_sym (osi, ARM_MAP_THUMB, addr))
+ return FALSE;
+ if (!elf32_arm_output_map_sym (osi, ARM_MAP_ARM, addr + 8))
+ return FALSE;
+ if (!elf32_arm_output_map_sym (osi, ARM_MAP_DATA, addr + 16))
+ return FALSE;
+ break;
+ case arm_stub_pic_long_branch:
+ if (!elf32_arm_output_stub_sym (osi, stub_name, addr, 12))
+ return FALSE;
+ if (!elf32_arm_output_map_sym (osi, ARM_MAP_ARM, addr))
+ return FALSE;
+ if (!elf32_arm_output_map_sym (osi, ARM_MAP_DATA, addr + 8))
+ return FALSE;
+ break;
+ default:
+ BFD_FAIL ();
+ }
+
+ return TRUE;
+}
/* Output mapping symbols for linker generated sections. */
for (offset = 0; offset < htab->arm_glue_size; offset += size)
{
- elf32_arm_ouput_plt_map_sym (&osi, ARM_MAP_ARM, offset);
- elf32_arm_ouput_plt_map_sym (&osi, ARM_MAP_DATA, offset + size - 4);
+ elf32_arm_output_map_sym (&osi, ARM_MAP_ARM, offset);
+ elf32_arm_output_map_sym (&osi, ARM_MAP_DATA, offset + size - 4);
}
}
for (offset = 0; offset < htab->thumb_glue_size; offset += size)
{
- elf32_arm_ouput_plt_map_sym (&osi, ARM_MAP_THUMB, offset);
- elf32_arm_ouput_plt_map_sym (&osi, ARM_MAP_ARM, offset + 4);
+ elf32_arm_output_map_sym (&osi, ARM_MAP_THUMB, offset);
+ elf32_arm_output_map_sym (&osi, ARM_MAP_ARM, offset + 4);
}
}
osi.sec_shndx = _bfd_elf_section_from_bfd_section
(output_bfd, osi.sec->output_section);
- elf32_arm_ouput_plt_map_sym (&osi, ARM_MAP_ARM, 0);
+ elf32_arm_output_map_sym (&osi, ARM_MAP_ARM, 0);
}
+ /* Long calls stubs. */
+ if (htab->stub_bfd && htab->stub_bfd->sections) {
+ asection* stub_sec;
+ for (stub_sec = htab->stub_bfd->sections;
+ stub_sec != NULL;
+ stub_sec = stub_sec->next) {
+
+ /* Ignore non-stub sections */
+ if (!strstr(stub_sec->name, STUB_SUFFIX))
+ continue;
+
+ osi.sec = stub_sec;
+
+ osi.sec_shndx = _bfd_elf_section_from_bfd_section
+ (output_bfd, osi.sec->output_section);
+
+ bfd_hash_traverse (&htab->stub_hash_table, arm_map_one_stub, &osi);
+ }
+ }
+
/* Finally, output mapping symbols for the PLT. */
if (!htab->splt || htab->splt->size == 0)
return TRUE;
/* VxWorks shared libraries have no PLT header. */
if (!info->shared)
{
- if (!elf32_arm_ouput_plt_map_sym (&osi, ARM_MAP_ARM, 0))
+ if (!elf32_arm_output_map_sym (&osi, ARM_MAP_ARM, 0))
return FALSE;
- if (!elf32_arm_ouput_plt_map_sym (&osi, ARM_MAP_DATA, 12))
+ if (!elf32_arm_output_map_sym (&osi, ARM_MAP_DATA, 12))
return FALSE;
}
}
else if (!htab->symbian_p)
{
- if (!elf32_arm_ouput_plt_map_sym (&osi, ARM_MAP_ARM, 0))
+ if (!elf32_arm_output_map_sym (&osi, ARM_MAP_ARM, 0))
return FALSE;
#ifndef FOUR_WORD_PLT
- if (!elf32_arm_ouput_plt_map_sym (&osi, ARM_MAP_DATA, 16))
+ if (!elf32_arm_output_map_sym (&osi, ARM_MAP_DATA, 16))
return FALSE;
#endif
}