#include "elf-vxworks.h"
#include "elf/arm.h"
-#ifndef NUM_ELEM
-#define NUM_ELEM(a) (sizeof (a) / (sizeof (a)[0]))
-#endif
-
/* Return the relocation section associated with NAME. HTAB is the
bfd's elf32_arm_link_hash_entry. */
#define RELOC_SECTION(HTAB, NAME) \
static reloc_howto_type *
elf32_arm_howto_from_type (unsigned int r_type)
{
- if (r_type < NUM_ELEM (elf32_arm_howto_table_1))
+ if (r_type < ARRAY_SIZE (elf32_arm_howto_table_1))
return &elf32_arm_howto_table_1[r_type];
if (r_type >= R_ARM_RREL32
- && r_type < R_ARM_RREL32 + NUM_ELEM (elf32_arm_howto_table_2))
+ && r_type < R_ARM_RREL32 + ARRAY_SIZE (elf32_arm_howto_table_2))
return &elf32_arm_howto_table_2[r_type - R_ARM_RREL32];
return NULL;
bfd_reloc_code_real_type code)
{
unsigned int i;
- for (i = 0; i < NUM_ELEM (elf32_arm_reloc_map); i ++)
+ for (i = 0; i < ARRAY_SIZE (elf32_arm_reloc_map); i ++)
if (elf32_arm_reloc_map[i].bfd_reloc_val == code)
return elf32_arm_howto_from_type (elf32_arm_reloc_map[i].elf_reloc_val);
{
unsigned int i;
- for (i = 0;
- i < (sizeof (elf32_arm_howto_table_1)
- / sizeof (elf32_arm_howto_table_1[0]));
- i++)
+ for (i = 0; i < ARRAY_SIZE (elf32_arm_howto_table_1); i++)
if (elf32_arm_howto_table_1[i].name != NULL
&& strcasecmp (elf32_arm_howto_table_1[i].name, r_name) == 0)
return &elf32_arm_howto_table_1[i];
- for (i = 0;
- i < (sizeof (elf32_arm_howto_table_2)
- / sizeof (elf32_arm_howto_table_2[0]));
- i++)
+ for (i = 0; i < ARRAY_SIZE (elf32_arm_howto_table_2); i++)
if (elf32_arm_howto_table_2[i].name != NULL
&& strcasecmp (elf32_arm_howto_table_2[i].name, r_name) == 0)
return &elf32_arm_howto_table_2[i];
return NULL;
}
-/* Support for core dump NOTE sections */
+/* Support for core dump NOTE sections. */
+
static bfd_boolean
elf32_arm_nabi_grok_prstatus (bfd *abfd, Elf_Internal_Note *note)
{
/* Note that for some reason, a spurious space is tacked
onto the end of the args in some (at least one anyway)
implementations, so strip it off if it exists. */
-
{
char *command = elf_tdata (abfd)->core_command;
int n = strlen (command);
#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"
/* The entries in a PLT when using a DLL-based target with multiple
address spaces. */
-static const bfd_vma elf32_arm_symbian_plt_entry [] =
+static const bfd_vma elf32_arm_symbian_plt_entry [] =
{
0xe51ff004, /* ldr pc, [pc, #-4] */
0x00000000, /* dcd R_ARM_GLOB_DAT(X) */
};
+#define ARM_MAX_FWD_BRANCH_OFFSET ((((1 << 23) - 1) << 2) + 8)
+#define ARM_MAX_BWD_BRANCH_OFFSET ((-((1 << 23) << 2)) + 8)
+#define THM_MAX_FWD_BRANCH_OFFSET ((1 << 22) -2 + 4)
+#define THM_MAX_BWD_BRANCH_OFFSET (-(1 << 22) + 4)
+#define THM2_MAX_FWD_BRANCH_OFFSET (((1 << 24) - 2) + 4)
+#define THM2_MAX_BWD_BRANCH_OFFSET (-(1 << 24) + 4)
+
+static const bfd_vma arm_long_branch_stub[] =
+ {
+ 0xe51ff004, /* ldr pc, [pc, #-4] */
+ 0x00000000, /* dcd R_ARM_ABS32(X) */
+ };
+
+static const bfd_vma arm_thumb_v4t_long_branch_stub[] =
+ {
+ 0xe59fc000, /* ldr ip, [pc, #0] */
+ 0xe12fff1c, /* bx ip */
+ 0x00000000, /* dcd R_ARM_ABS32(X) */
+ };
+
+static const bfd_vma arm_thumb_thumb_long_branch_stub[] =
+ {
+ 0x4e02b540, /* push {r6, lr} */
+ /* ldr r6, [pc, #8] */
+ 0x473046fe, /* mov lr, pc */
+ /* bx r6 */
+ 0xbf00bd40, /* pop {r6, pc} */
+ /* nop */
+ 0x00000000, /* dcd R_ARM_ABS32(X) */
+ };
+
+static const bfd_vma arm_thumb_arm_v4t_long_branch_stub[] =
+ {
+ 0x4e03b540, /* push {r6, lr} */
+ /* ldr r6, [pc, #12] */
+ 0x473046fe, /* mov lr, pc */
+ /* bx r6 */
+ 0xe8bd4040, /* pop {r6, pc} */
+ 0xe12fff1e, /* bx lr */
+ 0x00000000, /* dcd R_ARM_ABS32(X) */
+ };
+
+static const bfd_vma arm_pic_long_branch_stub[] =
+ {
+ 0xe59fc000, /* ldr r12, [pc] */
+ 0xe08ff00c, /* add pc, pc, ip */
+ 0x00000000, /* dcd R_ARM_REL32(X) */
+ };
+
+/* Section name for stubs is the associated section name plus this
+ string. */
+#define STUB_SUFFIX ".stub"
+
+enum elf32_arm_stub_type
+{
+ arm_stub_none,
+ arm_stub_long_branch,
+ arm_thumb_v4t_stub_long_branch,
+ arm_thumb_thumb_stub_long_branch,
+ arm_thumb_arm_v4t_stub_long_branch,
+ arm_stub_pic_long_branch,
+};
+
+struct elf32_arm_stub_hash_entry
+{
+ /* Base hash table entry structure. */
+ struct bfd_hash_entry root;
+
+ /* The stub section. */
+ asection *stub_sec;
+
+ /* Offset within stub_sec of the beginning of this stub. */
+ bfd_vma stub_offset;
+
+ /* Given the symbol's value and its section we can determine its final
+ value when building the stubs (so the stub knows where to jump). */
+ bfd_vma target_value;
+ asection *target_section;
+
+ enum elf32_arm_stub_type stub_type;
+
+ /* The symbol table entry, if any, that this was derived from. */
+ struct elf32_arm_link_hash_entry *h;
+
+ /* Destination symbol type (STT_ARM_TFUNC, ...) */
+ unsigned char st_type;
+
+ /* 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
code/data. */
/* The symbol marking the real symbol location for exported thumb
symbols with Arm stubs. */
struct elf_link_hash_entry *export_glue;
+
+ /* A pointer to the most recently used stub hash entry against this
+ symbol. */
+ struct elf32_arm_stub_hash_entry *stub_cache;
};
/* Traverse an arm ELF linker hash table. */
#define elf32_arm_hash_table(info) \
((struct elf32_arm_link_hash_table *) ((info)->hash))
+#define arm_stub_hash_lookup(table, string, create, copy) \
+ ((struct elf32_arm_stub_hash_entry *) \
+ bfd_hash_lookup ((table), (string), (create), (copy)))
+
/* ARM ELF linker hash table. */
struct elf32_arm_link_hash_table
- {
- /* The main hash table. */
- struct elf_link_hash_table root;
+{
+ /* The main hash table. */
+ struct elf_link_hash_table root;
- /* The size in bytes of the section containing the Thumb-to-ARM glue. */
- bfd_size_type thumb_glue_size;
+ /* The size in bytes of the section containing the Thumb-to-ARM glue. */
+ bfd_size_type thumb_glue_size;
- /* The size in bytes of the section containing the ARM-to-Thumb glue. */
- bfd_size_type arm_glue_size;
+ /* The size in bytes of the section containing the ARM-to-Thumb glue. */
+ bfd_size_type arm_glue_size;
- /* The size in bytes of section containing the ARMv4 BX veneers. */
- bfd_size_type bx_glue_size;
+ /* The size in bytes of section containing the ARMv4 BX veneers. */
+ bfd_size_type bx_glue_size;
- /* Offsets of ARMv4 BX veneers. Bit1 set if present, and Bit0 set when
- veneer has been populated. */
- bfd_vma bx_glue_offset[15];
+ /* Offsets of ARMv4 BX veneers. Bit1 set if present, and Bit0 set when
+ veneer has been populated. */
+ bfd_vma bx_glue_offset[15];
- /* The size in bytes of the section containing glue for VFP11 erratum
- veneers. */
- bfd_size_type vfp11_erratum_glue_size;
+ /* The size in bytes of the section containing glue for VFP11 erratum
+ veneers. */
+ bfd_size_type vfp11_erratum_glue_size;
- /* An arbitrary input BFD chosen to hold the glue sections. */
- bfd * bfd_of_glue_owner;
+ /* An arbitrary input BFD chosen to hold the glue sections. */
+ bfd * bfd_of_glue_owner;
- /* Nonzero to output a BE8 image. */
- int byteswap_code;
+ /* Nonzero to output a BE8 image. */
+ int byteswap_code;
- /* Zero if R_ARM_TARGET1 means R_ARM_ABS32.
- Nonzero if R_ARM_TARGET1 means R_ARM_REL32. */
- int target1_is_rel;
+ /* Zero if R_ARM_TARGET1 means R_ARM_ABS32.
+ Nonzero if R_ARM_TARGET1 means R_ARM_REL32. */
+ int target1_is_rel;
- /* The relocation to use for R_ARM_TARGET2 relocations. */
- int target2_reloc;
+ /* The relocation to use for R_ARM_TARGET2 relocations. */
+ int target2_reloc;
- /* 0 = Ignore R_ARM_V4BX.
- 1 = Convert BX to MOV PC.
- 2 = Generate v4 interworing stubs. */
- int fix_v4bx;
+ /* 0 = Ignore R_ARM_V4BX.
+ 1 = Convert BX to MOV PC.
+ 2 = Generate v4 interworing stubs. */
+ int fix_v4bx;
- /* Nonzero if the ARM/Thumb BLX instructions are available for use. */
- int use_blx;
+ /* Nonzero if the ARM/Thumb BLX instructions are available for use. */
+ int use_blx;
- /* What sort of code sequences we should look for which may trigger the
- VFP11 denorm erratum. */
- bfd_arm_vfp11_fix vfp11_fix;
+ /* What sort of code sequences we should look for which may trigger the
+ VFP11 denorm erratum. */
+ bfd_arm_vfp11_fix vfp11_fix;
- /* Global counter for the number of fixes we have emitted. */
- int num_vfp11_fixes;
+ /* Global counter for the number of fixes we have emitted. */
+ int num_vfp11_fixes;
- /* Nonzero to force PIC branch veneers. */
- int pic_veneer;
+ /* Nonzero to force PIC branch veneers. */
+ int pic_veneer;
- /* The number of bytes in the initial entry in the PLT. */
- bfd_size_type plt_header_size;
+ /* The number of bytes in the initial entry in the PLT. */
+ bfd_size_type plt_header_size;
- /* The number of bytes in the subsequent PLT etries. */
- bfd_size_type plt_entry_size;
+ /* The number of bytes in the subsequent PLT etries. */
+ bfd_size_type plt_entry_size;
- /* True if the target system is VxWorks. */
- int vxworks_p;
+ /* True if the target system is VxWorks. */
+ int vxworks_p;
- /* True if the target system is Symbian OS. */
- int symbian_p;
+ /* True if the target system is Symbian OS. */
+ int symbian_p;
- /* True if the target uses REL relocations. */
- int use_rel;
+ /* True if the target uses REL relocations. */
+ int use_rel;
- /* Short-cuts to get to dynamic linker sections. */
- asection *sgot;
- asection *sgotplt;
- asection *srelgot;
- asection *splt;
- asection *srelplt;
- asection *sdynbss;
- asection *srelbss;
+ /* Short-cuts to get to dynamic linker sections. */
+ asection *sgot;
+ asection *sgotplt;
+ asection *srelgot;
+ asection *splt;
+ asection *srelplt;
+ asection *sdynbss;
+ asection *srelbss;
- /* The (unloaded but important) VxWorks .rela.plt.unloaded section. */
- asection *srelplt2;
+ /* The (unloaded but important) VxWorks .rela.plt.unloaded section. */
+ asection *srelplt2;
- /* 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;
+ /* Data for R_ARM_TLS_LDM32 relocations. */
+ union
+ {
+ bfd_signed_vma refcount;
+ bfd_vma offset;
+ } tls_ldm_got;
- /* For convenience in allocate_dynrelocs. */
- bfd * obfd;
- };
+ /* Small local sym to section mapping cache. */
+ struct sym_sec_cache sym_sec;
+
+ /* For convenience in allocate_dynrelocs. */
+ bfd * obfd;
+
+ /* The stub hash table. */
+ struct bfd_hash_table stub_hash_table;
+
+ /* Linker stub bfd. */
+ bfd *stub_bfd;
+
+ /* Linker call-backs. */
+ asection * (*add_stub_section) (const char *, asection *);
+ void (*layout_sections_again) (void);
+
+ /* Array to keep track of which stub sections have been created, and
+ information on stub grouping. */
+ struct map_stub
+ {
+ /* This is the section to which stubs in the group will be
+ attached. */
+ asection *link_sec;
+ /* The stub section. */
+ asection *stub_sec;
+ } *stub_group;
+
+ /* Assorted information used by elf32_arm_size_stubs. */
+ unsigned int bfd_count;
+ int top_index;
+ asection **input_list;
+};
/* Create an entry in an ARM ELF linker hash table. */
/* Allocate the structure if it has not already been allocated by a
subclass. */
- if (ret == (struct elf32_arm_link_hash_entry *) NULL)
+ if (ret == NULL)
ret = bfd_hash_allocate (table, sizeof (struct elf32_arm_link_hash_entry));
if (ret == NULL)
return (struct bfd_hash_entry *) ret;
ret->plt_maybe_thumb_refcount = 0;
ret->plt_got_offset = -1;
ret->export_glue = NULL;
+
+ ret->stub_cache = NULL;
}
return (struct bfd_hash_entry *) ret;
}
+/* Initialize an entry in the stub hash table. */
+
+static struct bfd_hash_entry *
+stub_hash_newfunc (struct bfd_hash_entry *entry,
+ struct bfd_hash_table *table,
+ const char *string)
+{
+ /* Allocate the structure if it has not already been allocated by a
+ subclass. */
+ if (entry == NULL)
+ {
+ entry = bfd_hash_allocate (table,
+ sizeof (struct elf32_arm_stub_hash_entry));
+ if (entry == NULL)
+ return entry;
+ }
+
+ /* Call the allocation method of the superclass. */
+ entry = bfd_hash_newfunc (entry, table, string);
+ if (entry != NULL)
+ {
+ struct elf32_arm_stub_hash_entry *eh;
+
+ /* Initialize the local fields. */
+ eh = (struct elf32_arm_stub_hash_entry *) entry;
+ eh->stub_sec = NULL;
+ eh->stub_offset = 0;
+ eh->target_value = 0;
+ eh->target_section = NULL;
+ eh->stub_type = arm_stub_none;
+ eh->h = NULL;
+ eh->id_sec = NULL;
+ }
+
+ return entry;
+}
+
/* Return true if NAME is the name of the relocation section associated
with S. */
}
}
- if (!htab->splt
+ if (!htab->splt
|| !htab->srelplt
|| !htab->sdynbss
|| (!info->shared && !htab->srelbss))
ret->thumb_glue_size = 0;
ret->arm_glue_size = 0;
ret->bx_glue_size = 0;
- memset (ret->bx_glue_offset, 0, sizeof(ret->bx_glue_offset));
+ memset (ret->bx_glue_offset, 0, sizeof (ret->bx_glue_offset));
ret->vfp11_fix = BFD_ARM_VFP11_FIX_NONE;
ret->vfp11_erratum_glue_size = 0;
ret->num_vfp11_fixes = 0;
ret->obfd = abfd;
ret->tls_ldm_got.refcount = 0;
- return &ret->root.root;
+ if (!bfd_hash_table_init (&ret->stub_hash_table, stub_hash_newfunc,
+ sizeof (struct elf32_arm_stub_hash_entry)))
+ {
+ free (ret);
+ return NULL;
+ }
+
+ return &ret->root.root;
+}
+
+/* Free the derived linker hash table. */
+
+static void
+elf32_arm_hash_table_free (struct bfd_link_hash_table *hash)
+{
+ struct elf32_arm_link_hash_table *ret
+ = (struct elf32_arm_link_hash_table *) hash;
+
+ bfd_hash_table_free (&ret->stub_hash_table);
+ _bfd_generic_link_hash_table_free (hash);
+}
+
+/* Determine if we're dealing with a Thumb only architecture. */
+
+static bfd_boolean
+using_thumb_only (struct elf32_arm_link_hash_table *globals)
+{
+ int arch = bfd_elf_get_obj_attr_int (globals->obfd, OBJ_ATTR_PROC,
+ Tag_CPU_arch);
+ int profile;
+
+ if (arch != TAG_CPU_ARCH_V7)
+ return FALSE;
+
+ profile = bfd_elf_get_obj_attr_int (globals->obfd, OBJ_ATTR_PROC,
+ Tag_CPU_arch_profile);
+
+ return profile == 'M';
+}
+
+/* Determine if we're dealing with a Thumb-2 object. */
+
+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;
+}
+
+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
+arm_type_of_stub (struct bfd_link_info *info,
+ asection *input_sec,
+ const Elf_Internal_Rela *rel,
+ unsigned char st_type,
+ struct elf32_arm_link_hash_entry *hash,
+ bfd_vma destination)
+{
+ bfd_vma location;
+ bfd_signed_vma branch_offset;
+ unsigned int r_type;
+ struct elf32_arm_link_hash_table * globals;
+ int thumb2;
+ 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);
+
+ thumb2 = using_thumb2 (globals);
+
+ /* Determine where the call point is. */
+ location = (input_sec->output_offset
+ + input_sec->output_section->vma
+ + rel->r_offset);
+
+ branch_offset = (bfd_signed_vma)(destination - location);
+
+ r_type = ELF32_R_TYPE (rel->r_info);
+
+ /* If the call will go through a PLT entry then we do not need
+ glue. */
+ if (globals->splt != NULL && hash->root.plt.offset != (bfd_vma) -1)
+ return stub_type;
+
+ if (r_type == R_ARM_THM_CALL)
+ {
+ if ((!thumb2
+ && (branch_offset > THM_MAX_FWD_BRANCH_OFFSET
+ || (branch_offset < THM_MAX_BWD_BRANCH_OFFSET)))
+ || (thumb2
+ && (branch_offset > THM2_MAX_FWD_BRANCH_OFFSET
+ || (branch_offset < THM2_MAX_BWD_BRANCH_OFFSET)))
+ || ((st_type != STT_ARM_TFUNC) && !globals->use_blx))
+ {
+ if (st_type == STT_ARM_TFUNC)
+ {
+ /* Thumb to thumb. */
+ if (!thumb_only)
+ {
+ stub_type = (info->shared | globals->pic_veneer)
+ ? ((globals->use_blx)
+ ? arm_stub_pic_long_branch
+ : arm_stub_none)
+ : (globals->use_blx)
+ ? arm_stub_long_branch
+ : arm_stub_none;
+ }
+ else
+ {
+ stub_type = (info->shared | globals->pic_veneer)
+ ? arm_stub_none
+ : (globals->use_blx)
+ ? arm_thumb_thumb_stub_long_branch
+ : arm_stub_none;
+ }
+ }
+ else
+ {
+ /* Thumb to arm. */
+ stub_type = (info->shared | globals->pic_veneer)
+ ? ((globals->use_blx)
+ ? arm_stub_pic_long_branch
+ : arm_stub_none)
+ : (globals->use_blx)
+ ? arm_stub_long_branch
+ : arm_thumb_arm_v4t_stub_long_branch;
+ }
+ }
+ }
+ else if (r_type == R_ARM_CALL)
+ {
+ if (st_type == STT_ARM_TFUNC)
+ {
+ /* Arm to thumb. */
+ /* We have an extra 2-bytes reach because of 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)
+ || !globals->use_blx)
+ {
+ stub_type = (info->shared | globals->pic_veneer)
+ ? arm_stub_pic_long_branch
+ : (globals->use_blx)
+ ? arm_stub_long_branch
+ : arm_thumb_v4t_stub_long_branch;
+ }
+ }
+ else
+ {
+ /* Arm to arm. */
+ if (branch_offset > ARM_MAX_FWD_BRANCH_OFFSET
+ || (branch_offset < ARM_MAX_BWD_BRANCH_OFFSET))
+ {
+ stub_type = (info->shared | globals->pic_veneer)
+ ? arm_stub_pic_long_branch
+ : arm_stub_long_branch;
+ }
+ }
+ }
+
+ return stub_type;
+}
+
+/* Build a name for an entry in the stub hash table. */
+
+static char *
+elf32_arm_stub_name (const asection *input_section,
+ const asection *sym_sec,
+ const struct elf32_arm_link_hash_entry *hash,
+ const Elf_Internal_Rela *rel)
+{
+ char *stub_name;
+ bfd_size_type len;
+
+ if (hash)
+ {
+ len = 8 + 1 + strlen (hash->root.root.root.string) + 1 + 8 + 1;
+ stub_name = bfd_malloc (len);
+ if (stub_name != NULL)
+ sprintf (stub_name, "%08x_%s+%x",
+ input_section->id & 0xffffffff,
+ hash->root.root.root.string,
+ (int) rel->r_addend & 0xffffffff);
+ }
+ else
+ {
+ len = 8 + 1 + 8 + 1 + 8 + 1 + 8 + 1;
+ stub_name = bfd_malloc (len);
+ if (stub_name != NULL)
+ sprintf (stub_name, "%08x_%x:%x+%x",
+ input_section->id & 0xffffffff,
+ sym_sec->id & 0xffffffff,
+ (int) ELF32_R_SYM (rel->r_info) & 0xffffffff,
+ (int) rel->r_addend & 0xffffffff);
+ }
+
+ return stub_name;
+}
+
+/* Look up an entry in the stub hash. Stub entries are cached because
+ creating the stub name takes a bit of time. */
+
+static struct elf32_arm_stub_hash_entry *
+elf32_arm_get_stub_entry (const asection *input_section,
+ const asection *sym_sec,
+ struct elf_link_hash_entry *hash,
+ const Elf_Internal_Rela *rel,
+ struct elf32_arm_link_hash_table *htab)
+{
+ struct elf32_arm_stub_hash_entry *stub_entry;
+ struct elf32_arm_link_hash_entry *h = (struct elf32_arm_link_hash_entry *) hash;
+ const asection *id_sec;
+
+ if ((input_section->flags & SEC_CODE) == 0)
+ return NULL;
+
+ /* If this input section is part of a group of sections sharing one
+ stub section, then use the id of the first section in the group.
+ Stub names need to include a section id, as there may well be
+ more than one stub used to reach say, printf, and we need to
+ distinguish between them. */
+ id_sec = htab->stub_group[input_section->id].link_sec;
+
+ if (h != NULL && h->stub_cache != NULL
+ && h->stub_cache->h == h
+ && h->stub_cache->id_sec == id_sec)
+ {
+ stub_entry = h->stub_cache;
+ }
+ else
+ {
+ char *stub_name;
+
+ stub_name = elf32_arm_stub_name (id_sec, sym_sec, h, rel);
+ if (stub_name == NULL)
+ return NULL;
+
+ stub_entry = arm_stub_hash_lookup (&htab->stub_hash_table,
+ stub_name, FALSE, FALSE);
+ if (h != NULL)
+ h->stub_cache = stub_entry;
+
+ free (stub_name);
+ }
+
+ return stub_entry;
+}
+
+/* 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)
+{
+ asection *link_sec;
+ asection *stub_sec;
+ struct elf32_arm_stub_hash_entry *stub_entry;
+
+ link_sec = htab->stub_group[section->id].link_sec;
+ stub_sec = htab->stub_group[section->id].stub_sec;
+ if (stub_sec == NULL)
+ {
+ stub_sec = htab->stub_group[link_sec->id].stub_sec;
+ if (stub_sec == NULL)
+ {
+ size_t namelen;
+ bfd_size_type len;
+ char *s_name;
+
+ namelen = strlen (link_sec->name);
+ len = namelen + sizeof (STUB_SUFFIX);
+ s_name = 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));
+ stub_sec = (*htab->add_stub_section) (s_name, link_sec);
+ if (stub_sec == NULL)
+ return NULL;
+ htab->stub_group[link_sec->id].stub_sec = stub_sec;
+ }
+ htab->stub_group[section->id].stub_sec = stub_sec;
+ }
+
+ /* Enter this entry into the linker stub hash table. */
+ stub_entry = arm_stub_hash_lookup (&htab->stub_hash_table, stub_name,
+ TRUE, FALSE);
+ if (stub_entry == NULL)
+ {
+ (*_bfd_error_handler) (_("%s: cannot create stub entry %s"),
+ section->owner,
+ stub_name);
+ return NULL;
+ }
+
+ stub_entry->stub_sec = stub_sec;
+ stub_entry->stub_offset = 0;
+ stub_entry->id_sec = link_sec;
+
+ return stub_entry;
+}
+
+/* Store an Arm insn into an output section not processed by
+ elf32_arm_write_section. */
+
+static void
+put_arm_insn (struct elf32_arm_link_hash_table *htab,
+ bfd * output_bfd, bfd_vma val, void * ptr)
+{
+ if (htab->byteswap_code != bfd_little_endian (output_bfd))
+ bfd_putl32 (val, ptr);
+ else
+ bfd_putb32 (val, ptr);
+}
+
+/* Store a 16-bit Thumb insn into an output section not processed by
+ elf32_arm_write_section. */
+
+static void
+put_thumb_insn (struct elf32_arm_link_hash_table *htab,
+ bfd * output_bfd, bfd_vma val, void * ptr)
+{
+ if (htab->byteswap_code != bfd_little_endian (output_bfd))
+ bfd_putl16 (val, ptr);
+ else
+ bfd_putb16 (val, ptr);
+}
+
+static bfd_boolean
+arm_build_one_stub (struct bfd_hash_entry *gen_entry,
+ void * 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 *stub_bfd;
+ bfd_vma stub_addr;
+ bfd_byte *loc;
+ bfd_vma sym_value;
+ int template_size;
+ int size;
+ const bfd_vma *template;
+ int i;
+ struct elf32_arm_link_hash_table * globals;
+
+ /* Massage our args to the form they really have. */
+ stub_entry = (struct elf32_arm_stub_hash_entry *) gen_entry;
+ info = (struct bfd_link_info *) in_arg;
+
+ globals = elf32_arm_hash_table (info);
+
+ htab = elf32_arm_hash_table (info);
+ stub_sec = stub_entry->stub_sec;
+
+ /* Make a note of the offset within the stubs for this entry. */
+ stub_entry->stub_offset = stub_sec->size;
+ loc = stub_sec->contents + stub_entry->stub_offset;
+
+ stub_bfd = stub_sec->owner;
+
+ /* This is the address of the start of the stub. */
+ stub_addr = stub_sec->output_section->vma + stub_sec->output_offset
+ + stub_entry->stub_offset;
+
+ /* This is the address of the stub destination. */
+ sym_value = (stub_entry->target_value
+ + stub_entry->target_section->output_offset
+ + stub_entry->target_section->output_section->vma);
+
+ switch (stub_entry->stub_type)
+ {
+ case arm_stub_long_branch:
+ template = arm_long_branch_stub;
+ template_size = (sizeof (arm_long_branch_stub) / sizeof (bfd_vma)) * 4;
+ break;
+ case arm_thumb_v4t_stub_long_branch:
+ template = arm_thumb_v4t_long_branch_stub;
+ template_size = (sizeof (arm_thumb_v4t_long_branch_stub) / sizeof (bfd_vma)) * 4;
+ break;
+ case arm_thumb_thumb_stub_long_branch:
+ template = arm_thumb_thumb_long_branch_stub;
+ template_size = (sizeof (arm_thumb_thumb_long_branch_stub) / sizeof (bfd_vma)) * 4;
+ break;
+ case arm_thumb_arm_v4t_stub_long_branch:
+ template = arm_thumb_arm_v4t_long_branch_stub;
+ template_size = (sizeof (arm_thumb_arm_v4t_long_branch_stub) / sizeof (bfd_vma)) * 4;
+ break;
+ case arm_stub_pic_long_branch:
+ template = arm_pic_long_branch_stub;
+ template_size = (sizeof (arm_pic_long_branch_stub) / sizeof (bfd_vma)) * 4;
+ break;
+ default:
+ BFD_FAIL ();
+ return FALSE;
+ }
+
+ size = 0;
+ for (i = 0; i < (template_size / 4); i++)
+ {
+ /* A 0 pattern is a placeholder, every other pattern is an
+ instruction. */
+ if (template[i] != 0)
+ put_arm_insn (globals, stub_bfd, template[i], loc + size);
+ else
+ bfd_put_32 (stub_bfd, template[i], loc + size);
+
+ size += 4;
+ }
+ stub_sec->size += size;
+
+ /* Destination is Thumb. Force bit 0 to 1 to reflect this. */
+ if (stub_entry->st_type == STT_ARM_TFUNC)
+ sym_value |= 1;
+
+ switch (stub_entry->stub_type)
+ {
+ case arm_stub_long_branch:
+ _bfd_final_link_relocate (elf32_arm_howto_from_type (R_ARM_ABS32),
+ 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,
+ 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,
+ 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,
+ 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. */
+ _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;
+ }
+
+ return TRUE;
+}
+
+/* As above, but don't actually build the stub. Just bump offset so
+ we know stub section sizes. */
+
+static bfd_boolean
+arm_size_one_stub (struct bfd_hash_entry *gen_entry,
+ void * in_arg)
+{
+ struct elf32_arm_stub_hash_entry *stub_entry;
+ struct elf32_arm_link_hash_table *htab;
+ const bfd_vma *template;
+ int template_size;
+ int size;
+ int i;
+
+ /* Massage our args to the form they really have. */
+ stub_entry = (struct elf32_arm_stub_hash_entry *) gen_entry;
+ htab = (struct elf32_arm_link_hash_table *) in_arg;
+
+ switch (stub_entry->stub_type)
+ {
+ case arm_stub_long_branch:
+ template = arm_long_branch_stub;
+ template_size = (sizeof (arm_long_branch_stub) / sizeof (bfd_vma)) * 4;
+ break;
+ case arm_thumb_v4t_stub_long_branch:
+ template = arm_thumb_v4t_long_branch_stub;
+ template_size = (sizeof (arm_thumb_v4t_long_branch_stub) / sizeof (bfd_vma)) * 4;
+ break;
+ case arm_thumb_thumb_stub_long_branch:
+ template = arm_thumb_thumb_long_branch_stub;
+ template_size = (sizeof (arm_thumb_thumb_long_branch_stub) / sizeof (bfd_vma)) * 4;
+ break;
+ case arm_thumb_arm_v4t_stub_long_branch:
+ template = arm_thumb_arm_v4t_long_branch_stub;
+ template_size = (sizeof (arm_thumb_arm_v4t_long_branch_stub) / sizeof (bfd_vma)) * 4;
+ break;
+ case arm_stub_pic_long_branch:
+ template = arm_pic_long_branch_stub;
+ template_size = (sizeof (arm_pic_long_branch_stub) / sizeof (bfd_vma)) * 4;
+ break;
+ default:
+ BFD_FAIL ();
+ return FALSE;
+ break;
+ }
+
+ size = 0;
+ for (i = 0; i < (template_size / 4); i++)
+ size += 4;
+ size = (size + 7) & ~7;
+ stub_entry->stub_sec->size += size;
+ return TRUE;
+}
+
+/* External entry points for sizing and building linker stubs. */
+
+/* Set up various things so that we can make a list of input sections
+ for each output section included in the link. Returns -1 on error,
+ 0 when no stubs will be needed, and 1 on success. */
+
+int
+elf32_arm_setup_section_lists (bfd *output_bfd,
+ struct bfd_link_info *info)
+{
+ bfd *input_bfd;
+ unsigned int bfd_count;
+ int top_id, top_index;
+ asection *section;
+ asection **input_list, **list;
+ bfd_size_type amt;
+ struct elf32_arm_link_hash_table *htab = elf32_arm_hash_table (info);
+
+ if (! is_elf_hash_table (htab))
+ return 0;
+
+ /* Count the number of input BFDs and find the top input section id. */
+ for (input_bfd = info->input_bfds, bfd_count = 0, top_id = 0;
+ input_bfd != NULL;
+ input_bfd = input_bfd->link_next)
+ {
+ bfd_count += 1;
+ for (section = input_bfd->sections;
+ section != NULL;
+ section = section->next)
+ {
+ if (top_id < section->id)
+ top_id = section->id;
+ }
+ }
+ htab->bfd_count = bfd_count;
+
+ amt = sizeof (struct map_stub) * (top_id + 1);
+ htab->stub_group = bfd_zmalloc (amt);
+ if (htab->stub_group == NULL)
+ return -1;
+
+ /* We can't use output_bfd->section_count here to find the top output
+ section index as some sections may have been removed, and
+ _bfd_strip_section_from_output doesn't renumber the indices. */
+ for (section = output_bfd->sections, top_index = 0;
+ section != NULL;
+ section = section->next)
+ {
+ if (top_index < section->index)
+ top_index = section->index;
+ }
+
+ htab->top_index = top_index;
+ amt = sizeof (asection *) * (top_index + 1);
+ input_list = bfd_malloc (amt);
+ htab->input_list = input_list;
+ if (input_list == NULL)
+ return -1;
+
+ /* For sections we aren't interested in, mark their entries with a
+ value we can check later. */
+ list = input_list + top_index;
+ do
+ *list = bfd_abs_section_ptr;
+ while (list-- != input_list);
+
+ for (section = output_bfd->sections;
+ section != NULL;
+ section = section->next)
+ {
+ if ((section->flags & SEC_CODE) != 0)
+ input_list[section->index] = NULL;
+ }
+
+ return 1;
+}
+
+/* The linker repeatedly calls this function for each input section,
+ in the order that input sections are linked into output sections.
+ Build lists of input sections to determine groupings between which
+ we may insert linker stubs. */
+
+void
+elf32_arm_next_input_section (struct bfd_link_info *info,
+ asection *isec)
+{
+ struct elf32_arm_link_hash_table *htab = elf32_arm_hash_table (info);
+
+ if (isec->output_section->index <= htab->top_index)
+ {
+ asection **list = htab->input_list + isec->output_section->index;
+
+ if (*list != bfd_abs_section_ptr)
+ {
+ /* Steal the link_sec pointer for our list. */
+#define PREV_SEC(sec) (htab->stub_group[(sec)->id].link_sec)
+ /* This happens to make the list in reverse order,
+ which is what we want. */
+ PREV_SEC (isec) = *list;
+ *list = isec;
+ }
+ }
+}
+
+/* See whether we can group stub sections together. Grouping stub
+ sections may result in fewer stubs. More importantly, we need to
+ put all .init* and .fini* stubs at the beginning of the .init or
+ .fini output sections respectively, because glibc splits the
+ _init and _fini functions into multiple parts. Putting a stub in
+ the middle of a function is not a good idea. */
+
+static void
+group_sections (struct elf32_arm_link_hash_table *htab,
+ bfd_size_type stub_group_size,
+ bfd_boolean stubs_always_before_branch)
+{
+ asection **list = htab->input_list + htab->top_index;
+
+ do
+ {
+ asection *tail = *list;
+
+ if (tail == bfd_abs_section_ptr)
+ continue;
+
+ while (tail != NULL)
+ {
+ asection *curr;
+ asection *prev;
+ bfd_size_type total;
+
+ curr = tail;
+ total = tail->size;
+ while ((prev = PREV_SEC (curr)) != NULL
+ && ((total += curr->output_offset - prev->output_offset)
+ < stub_group_size))
+ curr = prev;
+
+ /* OK, the size from the start of CURR to the end is less
+ than stub_group_size and thus can be handled by one stub
+ section. (Or the tail section is itself larger than
+ stub_group_size, in which case we may be toast.)
+ We should really be keeping track of the total size of
+ stubs added here, as stubs contribute to the final output
+ section size. */
+ do
+ {
+ prev = PREV_SEC (tail);
+ /* Set up this stub group. */
+ htab->stub_group[tail->id].link_sec = curr;
+ }
+ while (tail != curr && (tail = prev) != NULL);
+
+ /* But wait, there's more! Input sections up to stub_group_size
+ bytes before the stub section can be handled by it too. */
+ if (!stubs_always_before_branch)
+ {
+ total = 0;
+ while (prev != NULL
+ && ((total += tail->output_offset - prev->output_offset)
+ < stub_group_size))
+ {
+ tail = prev;
+ prev = PREV_SEC (tail);
+ htab->stub_group[tail->id].link_sec = curr;
+ }
+ }
+ tail = prev;
+ }
+ }
+ while (list-- != htab->input_list);
+
+ free (htab->input_list);
+#undef PREV_SEC
+}
+
+/* Determine and set the size of the stub section for a final link.
+
+ The basic idea here is to examine all the relocations looking for
+ PC-relative calls to a target that is unreachable with a "bl"
+ instruction. */
+
+bfd_boolean
+elf32_arm_size_stubs (bfd *output_bfd,
+ bfd *stub_bfd,
+ struct bfd_link_info *info,
+ bfd_signed_vma group_size,
+ asection * (*add_stub_section) (const char *, asection *),
+ void (*layout_sections_again) (void))
+{
+ bfd_size_type stub_group_size;
+ bfd_boolean stubs_always_before_branch;
+ bfd_boolean stub_changed = 0;
+ struct elf32_arm_link_hash_table *htab = elf32_arm_hash_table (info);
+
+ /* Propagate mach to stub bfd, because it may not have been
+ finalized when we created stub_bfd. */
+ bfd_set_arch_mach (stub_bfd, bfd_get_arch (output_bfd),
+ bfd_get_mach (output_bfd));
+
+ /* Stash our params away. */
+ htab->stub_bfd = stub_bfd;
+ htab->add_stub_section = add_stub_section;
+ htab->layout_sections_again = layout_sections_again;
+ stubs_always_before_branch = group_size < 0;
+ if (group_size < 0)
+ stub_group_size = -group_size;
+ else
+ stub_group_size = group_size;
+
+ if (stub_group_size == 1)
+ {
+ /* Default values. */
+ /* Thumb branch range is +-4MB has to be used as the default
+ maximum size (a given section can contain both ARM and Thumb
+ code, so the worst case has to be taken into account).
+
+ This value is 24K less than that, which allows for 2025
+ 12-byte stubs. If we exceed that, then we will fail to link.
+ The user will have to relink with an explicit group size
+ option. */
+ stub_group_size = 4170000;
+ }
+
+ group_sections (htab, stub_group_size, stubs_always_before_branch);
+
+ while (1)
+ {
+ bfd *input_bfd;
+ unsigned int bfd_indx;
+ asection *stub_sec;
+
+ for (input_bfd = info->input_bfds, bfd_indx = 0;
+ input_bfd != NULL;
+ input_bfd = input_bfd->link_next, bfd_indx++)
+ {
+ Elf_Internal_Shdr *symtab_hdr;
+ asection *section;
+ Elf_Internal_Sym *local_syms = NULL;
+
+ /* We'll need the symbol table in a second. */
+ symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
+ if (symtab_hdr->sh_info == 0)
+ continue;
+
+ /* Walk over each section attached to the input bfd. */
+ for (section = input_bfd->sections;
+ section != NULL;
+ section = section->next)
+ {
+ Elf_Internal_Rela *internal_relocs, *irelaend, *irela;
+
+ /* If there aren't any relocs, then there's nothing more
+ to do. */
+ if ((section->flags & SEC_RELOC) == 0
+ || section->reloc_count == 0
+ || (section->flags & SEC_CODE) == 0)
+ continue;
+
+ /* If this section is a link-once section that will be
+ discarded, then don't create any stubs. */
+ if (section->output_section == NULL
+ || section->output_section->owner != output_bfd)
+ continue;
+
+ /* Get the relocs. */
+ internal_relocs
+ = _bfd_elf_link_read_relocs (input_bfd, section, NULL,
+ NULL, info->keep_memory);
+ if (internal_relocs == NULL)
+ goto error_ret_free_local;
+
+ /* Now examine each relocation. */
+ irela = internal_relocs;
+ irelaend = irela + section->reloc_count;
+ for (; irela < irelaend; irela++)
+ {
+ unsigned int r_type, r_indx;
+ enum elf32_arm_stub_type stub_type;
+ struct elf32_arm_stub_hash_entry *stub_entry;
+ asection *sym_sec;
+ 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;
+
+ r_type = ELF32_R_TYPE (irela->r_info);
+ r_indx = ELF32_R_SYM (irela->r_info);
+
+ if (r_type >= (unsigned int) R_ARM_max)
+ {
+ bfd_set_error (bfd_error_bad_value);
+ error_ret_free_internal:
+ if (elf_section_data (section)->relocs == NULL)
+ free (internal_relocs);
+ goto error_ret_free_local;
+ }
+
+ /* Only look for stubs on call instructions. */
+ if ((r_type != (unsigned int) R_ARM_CALL)
+ && (r_type != (unsigned int) R_ARM_THM_CALL))
+ continue;
+
+ /* Now determine the call target, its name, value,
+ section. */
+ sym_sec = NULL;
+ sym_value = 0;
+ destination = 0;
+ hash = NULL;
+ sym_name = NULL;
+ if (r_indx < symtab_hdr->sh_info)
+ {
+ /* It's a local symbol. */
+ Elf_Internal_Sym *sym;
+ Elf_Internal_Shdr *hdr;
+
+ if (local_syms == NULL)
+ {
+ local_syms
+ = (Elf_Internal_Sym *) symtab_hdr->contents;
+ if (local_syms == NULL)
+ local_syms
+ = bfd_elf_get_elf_syms (input_bfd, symtab_hdr,
+ symtab_hdr->sh_info, 0,
+ NULL, NULL, NULL);
+ if (local_syms == NULL)
+ goto error_ret_free_internal;
+ }
+
+ sym = local_syms + r_indx;
+ hdr = elf_elfsections (input_bfd)[sym->st_shndx];
+ sym_sec = hdr->bfd_section;
+ if (ELF_ST_TYPE (sym->st_info) != STT_SECTION)
+ sym_value = sym->st_value;
+ destination = (sym_value + irela->r_addend
+ + 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
+ {
+ /* 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 *)
+ hash->root.root.u.i.link);
+
+ if (hash->root.root.type == bfd_link_hash_defined
+ || hash->root.root.type == bfd_link_hash_defweak)
+ {
+ sym_sec = hash->root.root.u.def.section;
+ sym_value = hash->root.root.u.def.value;
+ if (sym_sec->output_section != NULL)
+ destination = (sym_value + irela->r_addend
+ + sym_sec->output_offset
+ + sym_sec->output_section->vma);
+ }
+ else if (hash->root.root.type == bfd_link_hash_undefweak
+ || hash->root.root.type == bfd_link_hash_undefined)
+ /* For a shared library, these will need a PLT stub,
+ which is treated separately.
+ For absolute code, they cannot be handled. */
+ continue;
+ else
+ {
+ bfd_set_error (bfd_error_bad_value);
+ 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. */
+ stub_type = arm_type_of_stub (info, section, irela, st_type,
+ hash, destination);
+ if (stub_type == arm_stub_none)
+ continue;
+
+ /* 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);
+ if (!stub_name)
+ goto error_ret_free_internal;
+
+ stub_entry = arm_stub_hash_lookup (&htab->stub_hash_table,
+ stub_name,
+ FALSE, FALSE);
+ if (stub_entry != NULL)
+ {
+ /* The proper stub has already been created. */
+ free (stub_name);
+ continue;
+ }
+
+ stub_entry = elf32_arm_add_stub (stub_name, section, htab);
+ if (stub_entry == NULL)
+ {
+ free (stub_name);
+ goto error_ret_free_internal;
+ }
+
+ stub_entry->target_value = sym_value;
+ stub_entry->target_section = sym_sec;
+ 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;
+ }
+
+ /* We're done with the internal relocs, free them. */
+ if (elf_section_data (section)->relocs == NULL)
+ free (internal_relocs);
+ }
+ }
+
+ if (!stub_changed)
+ break;
+
+ /* OK, we've added some stubs. Find out the new size of the
+ stub sections. */
+ for (stub_sec = htab->stub_bfd->sections;
+ stub_sec != NULL;
+ stub_sec = stub_sec->next)
+ stub_sec->size = 0;
+
+ bfd_hash_traverse (&htab->stub_hash_table, arm_size_one_stub, htab);
+
+ /* Ask the linker to do its stuff. */
+ (*htab->layout_sections_again) ();
+ stub_changed = FALSE;
+ }
+
+ return TRUE;
+
+ error_ret_free_local:
+ return FALSE;
+}
+
+/* Build all the stubs associated with the current output file. The
+ stubs are kept in a hash table attached to the main linker hash
+ table. We also set up the .plt entries for statically linked PIC
+ functions here. This function is called via arm_elf_finish in the
+ linker. */
+
+bfd_boolean
+elf32_arm_build_stubs (struct bfd_link_info *info)
+{
+ asection *stub_sec;
+ struct bfd_hash_table *table;
+ struct elf32_arm_link_hash_table *htab;
+
+ htab = elf32_arm_hash_table (info);
+
+ for (stub_sec = htab->stub_bfd->sections;
+ stub_sec != NULL;
+ stub_sec = stub_sec->next)
+ {
+ bfd_size_type size;
+
+ /* Ignore non-stub sections */
+ if (!strstr (stub_sec->name, STUB_SUFFIX))
+ continue;
+
+ /* Allocate memory to hold the linker stubs. */
+ size = stub_sec->size;
+ stub_sec->contents = bfd_zalloc (htab->stub_bfd, size);
+ if (stub_sec->contents == NULL && size != 0)
+ return FALSE;
+ stub_sec->size = 0;
+ }
+
+ /* Build the stubs as directed by the stub hash table. */
+ table = &htab->stub_hash_table;
+ bfd_hash_traverse (table, arm_build_one_stub, info);
+
+ return TRUE;
}
/* Locate the Thumb encoded calling stub for NAME. */
ldr r12, __func_addr
bx r12
__func_addr:
- .word func @ behave as if you saw a ARM_32 reloc.
+ .word func @ behave as if you saw a ARM_32 reloc.
(v5t static images)
.arm
__func_from_arm:
ldr pc, __func_addr
__func_addr:
- .word func @ behave as if you saw a ARM_32 reloc.
+ .word func @ behave as if you saw a ARM_32 reloc.
(relocatable images)
.arm
BFD_ASSERT (s->size == globals->thumb_glue_size);
s->contents = foo;
}
-
+
if (globals->vfp11_erratum_glue_size != 0)
{
BFD_ASSERT (globals->bfd_of_glue_owner != NULL);
-
+
s = bfd_get_section_by_name
(globals->bfd_of_glue_owner, VFP11_ERRATUM_VENEER_SECTION_NAME);
-
+
BFD_ASSERT (s != NULL);
-
+
foo = bfd_alloc (globals->bfd_of_glue_owner,
globals->vfp11_erratum_glue_size);
-
+
BFD_ASSERT (s->size == globals->vfp11_erratum_glue_size);
s->contents = foo;
}
}
/* Allocate space and symbols for calling a Thumb function from Arm mode.
- returns the symbol identifying teh stub. */
+ returns the symbol identifying the stub. */
+
static struct elf_link_hash_entry *
record_arm_to_thumb_glue (struct bfd_link_info * link_info,
struct elf_link_hash_entry * h)
/* Add symbol for veneer. */
tmp_name = bfd_malloc ((bfd_size_type) strlen (ARM_BX_GLUE_ENTRY_NAME) + 1);
-
+
BFD_ASSERT (tmp_name);
-
+
sprintf (tmp_name, ARM_BX_GLUE_ENTRY_NAME, reg);
-
+
myh = elf_link_hash_lookup
(&(globals)->root, tmp_name, FALSE, FALSE, FALSE);
-
+
BFD_ASSERT (myh == NULL);
-
+
bh = NULL;
val = globals->bx_glue_size;
_bfd_generic_link_add_one_symbol (link_info, globals->bfd_of_glue_owner,
{
struct _arm_elf_section_data *sec_data = elf32_arm_section_data (sec);
unsigned int newidx;
-
+
if (sec_data->map == NULL)
{
sec_data->map = bfd_malloc (sizeof (elf32_arm_section_map));
sec_data->mapcount = 0;
sec_data->mapsize = 1;
}
-
+
newidx = sec_data->mapcount++;
-
+
if (sec_data->mapcount > sec_data->mapsize)
{
sec_data->mapsize *= 2;
struct _arm_elf_section_data *sec_data;
int errcount;
elf32_vfp11_erratum_list *newerr;
-
+
hash_table = elf32_arm_hash_table (link_info);
-
+
BFD_ASSERT (hash_table != NULL);
BFD_ASSERT (hash_table->bfd_of_glue_owner != NULL);
-
+
s = bfd_get_section_by_name
(hash_table->bfd_of_glue_owner, VFP11_ERRATUM_VENEER_SECTION_NAME);
-
+
sec_data = elf32_arm_section_data (s);
-
+
BFD_ASSERT (s != NULL);
-
+
tmp_name = bfd_malloc ((bfd_size_type) strlen
(VFP11_ERRATUM_VENEER_ENTRY_NAME) + 10);
-
+
BFD_ASSERT (tmp_name);
-
+
sprintf (tmp_name, VFP11_ERRATUM_VENEER_ENTRY_NAME,
hash_table->num_vfp11_fixes);
-
+
myh = elf_link_hash_lookup
(&(hash_table)->root, tmp_name, FALSE, FALSE, FALSE);
-
+
BFD_ASSERT (myh == NULL);
-
+
bh = NULL;
val = hash_table->vfp11_erratum_glue_size;
_bfd_generic_link_add_one_symbol (link_info, hash_table->bfd_of_glue_owner,
/* Link veneer back to calling location. */
errcount = ++(sec_data->erratumcount);
newerr = bfd_zmalloc (sizeof (elf32_vfp11_erratum_list));
-
+
newerr->type = VFP11_ERRATUM_ARM_VENEER;
newerr->vma = -1;
newerr->u.v.branch = branch;
myh = elf_link_hash_lookup
(&(hash_table)->root, tmp_name, FALSE, FALSE, FALSE);
-
+
if (myh != NULL)
abort ();
val = offset + 4;
_bfd_generic_link_add_one_symbol (link_info, branch_bfd, tmp_name, BSF_LOCAL,
branch_sec, val, NULL, TRUE, FALSE, &bh);
-
+
myh = (struct elf_link_hash_entry *) bh;
myh->type = ELF_ST_INFO (STB_LOCAL, STT_FUNC);
myh->forced_local = 1;
free (tmp_name);
-
+
/* Generate a mapping symbol for the veneer section, and explicitly add an
entry for that symbol to the code/data map for the section. */
if (hash_table->vfp11_erratum_glue_size == 0)
myh = (struct elf_link_hash_entry *) bh;
myh->type = ELF_ST_INFO (STB_LOCAL, STT_NOTYPE);
myh->forced_local = 1;
-
+
/* The elf32_arm_init_maps function only cares about symbols from input
BFDs. We must make a note of this generated mapping symbol
ourselves so that code byteswapping works properly in
elf32_arm_write_section. */
elf32_arm_section_map_add (s, 'a', 0);
}
-
+
s->size += VFP11_ERRATUM_VENEER_SIZE;
hash_table->vfp11_erratum_glue_size += VFP11_ERRATUM_VENEER_SIZE;
hash_table->num_vfp11_fixes++;
-
+
/* The offset of the veneer. */
return val;
}
if (info->relocatable)
return TRUE;
+ /* linker stubs don't need glue */
+ if (!strcmp (abfd->filename, "linker stubs"))
+ return TRUE;
+
sec = bfd_get_section_by_name (abfd, ARM2THUMB_GLUE_SECTION_NAME);
if (sec == NULL)
return TRUE;
}
-static void check_use_blx(struct elf32_arm_link_hash_table *globals)
+static void
+check_use_blx (struct elf32_arm_link_hash_table *globals)
{
if (bfd_elf_get_obj_attr_int (globals->obfd, OBJ_ATTR_PROC,
Tag_CPU_arch) > 2)
/* Load the relocs. */
internal_relocs
- = _bfd_elf_link_read_relocs (abfd, sec, (void *) NULL,
- (Elf_Internal_Rela *) NULL, FALSE);
+ = _bfd_elf_link_read_relocs (abfd, sec, NULL, NULL, FALSE);
if (internal_relocs == NULL)
goto error_return;
/* These are the only relocation types we care about. */
if ( r_type != R_ARM_PC24
&& r_type != R_ARM_PLT32
- && r_type != R_ARM_CALL
&& r_type != R_ARM_JUMP24
- && r_type != R_ARM_THM_CALL
&& r_type != R_ARM_THM_JUMP24
&& (r_type != R_ARM_V4BX || globals->fix_v4bx < 2))
continue;
{
case R_ARM_PC24:
case R_ARM_PLT32:
- case R_ARM_CALL:
case R_ARM_JUMP24:
/* 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 (ELF_ST_TYPE(h->type) == STT_ARM_TFUNC
+ if (ELF_ST_TYPE (h->type) == STT_ARM_TFUNC
&& !(r_type == R_ARM_CALL && globals->use_blx))
record_arm_to_thumb_glue (link_info, h);
break;
- case R_ARM_THM_CALL:
case R_ARM_THM_JUMP24:
/* This one is a call from thumb code. We look
up the target of the call. If it is not a thumb
Elf_Internal_Sym *isym = &isymbuf[i];
asection *sec = bfd_section_from_elf_index (abfd, isym->st_shndx);
const char *name;
-
+
if (sec != NULL
&& ELF_ST_BIND (isym->st_info) == STB_LOCAL)
{
name = bfd_elf_string_from_elf_section (abfd,
hdr->sh_link, isym->st_name);
-
+
if (bfd_is_arm_special_symbol_name (name,
BFD_ARM_SPECIAL_SYM_TYPE_MAP))
elf32_arm_section_map_add (sec, name[1], isym->st_value);
{
struct elf32_arm_link_hash_table *globals = elf32_arm_hash_table (link_info);
obj_attribute *out_attr = elf_known_obj_attributes_proc (obfd);
-
+
/* We assume that ARMv7+ does not need the VFP11 denorm erratum fix. */
if (out_attr[Tag_CPU_arch].i >= TAG_CPU_ARCH_V7)
{
case BFD_ARM_VFP11_FIX_NONE:
globals->vfp11_fix = BFD_ARM_VFP11_FIX_NONE;
break;
-
+
default:
/* Give a warning, but do as the user requests anyway. */
(*_bfd_error_handler) (_("%B: warning: selected VFP11 erratum "
}
-enum bfd_arm_vfp11_pipe {
+enum bfd_arm_vfp11_pipe
+{
VFP11_FMAC,
VFP11_LS,
VFP11_DS,
0...31: single-precision registers s0...s31
32...63: double-precision registers d0...d31.
-
+
Although X should be zero for VFP11 (encoding d0...d15 only), we might
encounter VFP3 instructions, so we allow the full range for DP registers. */
-
+
static unsigned int
bfd_arm_vfp11_regno (unsigned int insn, bfd_boolean is_double, unsigned int rx,
unsigned int x)
bfd_arm_vfp11_antidependency (unsigned int wmask, int *regs, int numregs)
{
int i;
-
+
for (i = 0; i < numregs; i++)
{
unsigned int reg = regs[i];
if (reg < 32 && (wmask & (1 << reg)) != 0)
return TRUE;
-
+
reg -= 32;
if (reg >= 16)
continue;
-
+
if ((wmask & (3 << (reg * 2))) != 0)
return TRUE;
}
-
+
return FALSE;
}
else if ((insn & 0x0fe00ed0) == 0x0c400a10)
{
unsigned int fm = bfd_arm_vfp11_regno (insn, is_double, 0, 5);
-
+
if ((insn & 0x100000) == 0)
{
if (is_double)
{
int fd = bfd_arm_vfp11_regno (insn, is_double, 12, 22);
unsigned int puw = ((insn >> 21) & 0x1) | (((insn >> 23) & 3) << 1);
-
+
switch (puw)
{
case 0: /* Two-reg transfer. We should catch these above. */
abort ();
-
+
case 2: /* fldm[sdx]. */
case 3:
case 5:
bfd_arm_vfp11_write_mask (destmask, i);
}
break;
-
+
case 4: /* fld[sd]. */
case 6:
bfd_arm_vfp11_write_mask (destmask, fd);
break;
-
+
default:
return VFP11_BAD;
}
int regs[3], numregs = 0;
struct elf32_arm_link_hash_table *globals = elf32_arm_hash_table (link_info);
int use_vector = (globals->vfp11_fix == BFD_ARM_VFP11_FIX_VECTOR);
-
+
/* We use a simple FSM to match troublesome VFP11 instruction sequences.
The states transition as follows:
-
+
0 -> 1 (vector) or 0 -> 2 (scalar)
A VFP FMAC-pipeline instruction has been seen. Fill
regs[0]..regs[numregs-1] with its input operands. Remember this
1 -> 2
Any instruction, except for a VFP instruction which overwrites
regs[*].
-
+
1 -> 3 [ -> 0 ] or
2 -> 3 [ -> 0 ]
A VFP instruction has been seen which overwrites any of regs[*].
We must make a veneer! Reset state to 0 before examining next
instruction.
-
+
2 -> 0
If we fail to match anything in state 2, reset to state 0 and reset
the instruction pointer to the instruction after 'first_fmac'.
If the VFP11 vector mode is in use, there must be at least two unrelated
instructions between anti-dependent VFP11 instructions to properly avoid
- triggering the erratum, hence the use of the extra state 1.
- */
+ triggering the erratum, hence the use of the extra state 1. */
/* If we are only performing a partial link do not bother
to construct any glue. */
/* Skip if this bfd does not correspond to an ELF image. */
if (! is_arm_elf (abfd))
return TRUE;
-
+
/* We should have chosen a fix type by the time we get here. */
BFD_ASSERT (globals->vfp11_fix != BFD_ARM_VFP11_FIX_DEFAULT);
continue;
sec_data = elf32_arm_section_data (sec);
-
+
if (sec_data->mapcount == 0)
continue;
-
+
if (elf_section_data (sec)->this_hdr.contents != NULL)
contents = elf_section_data (sec)->this_hdr.contents;
else if (! bfd_malloc_and_get_section (abfd, sec, &contents))
unsigned int span_end = (span == sec_data->mapcount - 1)
? sec->size : sec_data->map[span + 1].vma;
char span_type = sec_data->map[span].type;
-
+
/* FIXME: Only ARM mode is supported at present. We may need to
support Thumb-2 mode also at some point. */
if (span_type != 'a')
case 'a':
newerr->type = VFP11_ERRATUM_BRANCH_TO_ARM_VENEER;
break;
-
+
default:
abort ();
}
i = next_i;
}
}
-
+
if (contents != NULL
&& elf_section_data (sec)->this_hdr.contents != contents)
free (contents);
if (contents != NULL
&& elf_section_data (sec)->this_hdr.contents != contents)
free (contents);
-
+
return FALSE;
}
asection *sec;
struct elf32_arm_link_hash_table *globals;
char *tmp_name;
-
+
if (link_info->relocatable)
return;
return;
globals = elf32_arm_hash_table (link_info);
-
+
tmp_name = bfd_malloc ((bfd_size_type) strlen
(VFP11_ERRATUM_VENEER_ENTRY_NAME) + 10);
{
struct _arm_elf_section_data *sec_data = elf32_arm_section_data (sec);
elf32_vfp11_erratum_list *errnode = sec_data->erratumlist;
-
+
for (; errnode != NULL; errnode = errnode->next)
{
struct elf_link_hash_entry *myh;
errnode->u.v.branch->vma = vma;
break;
-
+
default:
abort ();
}
}
}
-
+
free (tmp_name);
}
elf_arm_tdata (output_bfd)->no_enum_size_warning = no_enum_warn;
}
-/* The thumb form of a long branch is a bit finicky, because the offset
- encoding is split over two fields, each in it's own instruction. They
- can occur in any order. So given a thumb form of long branch, and an
- offset, insert the offset into the thumb branch and return finished
- instruction.
-
- It takes two thumb instructions to encode the target address. Each has
- 11 bits to invest. The upper 11 bits are stored in one (identified by
- H-0.. see below), the lower 11 bits are stored in the other (identified
- by H-1).
-
- Combine together and shifted left by 1 (it's a half word address) and
- there you have it.
-
- Op: 1111 = F,
- H-0, upper address-0 = 000
- Op: 1111 = F,
- H-1, lower address-0 = 800
-
- They can be ordered either way, but the arm tools I've seen always put
- the lower one first. It probably doesn't matter. krk@cygnus.com
-
- XXX: Actually the order does matter. The second instruction (H-1)
- moves the computed address into the PC, so it must be the second one
- in the sequence. The problem, however is that whilst little endian code
- stores the instructions in HI then LOW order, big endian code does the
- reverse. nickc@cygnus.com. */
-
-#define LOW_HI_ORDER 0xF800F000
-#define HI_LOW_ORDER 0xF000F800
-
-static insn32
-insert_thumb_branch (insn32 br_insn, int rel_off)
-{
- unsigned int low_bits;
- unsigned int high_bits;
-
- BFD_ASSERT ((rel_off & 1) != 1);
-
- rel_off >>= 1; /* Half word aligned address. */
- low_bits = rel_off & 0x000007FF; /* The bottom 11 bits. */
- high_bits = (rel_off >> 11) & 0x000007FF; /* The top 11 bits. */
-
- if ((br_insn & LOW_HI_ORDER) == LOW_HI_ORDER)
- br_insn = LOW_HI_ORDER | (low_bits << 16) | high_bits;
- else if ((br_insn & HI_LOW_ORDER) == HI_LOW_ORDER)
- br_insn = HI_LOW_ORDER | (high_bits << 16) | low_bits;
- else
- /* FIXME: abort is probably not the right call. krk@cygnus.com */
- abort (); /* Error - not a valid branch instruction form. */
-
- return br_insn;
-}
-
-
-/* Store an Arm insn into an output section not processed by
- elf32_arm_write_section. */
-
-static void
-put_arm_insn (struct elf32_arm_link_hash_table *htab,
- bfd * output_bfd, bfd_vma val, void * ptr)
-{
- if (htab->byteswap_code != bfd_little_endian (output_bfd))
- bfd_putl32 (val, ptr);
- else
- bfd_putb32 (val, ptr);
-}
-
-
-/* Store a 16-bit Thumb insn into an output section not processed by
- elf32_arm_write_section. */
+/* Replace the target offset of a Thumb bl or b.w instruction. */
static void
-put_thumb_insn (struct elf32_arm_link_hash_table *htab,
- bfd * output_bfd, bfd_vma val, void * ptr)
+insert_thumb_branch (bfd *abfd, long int offset, bfd_byte *insn)
{
- if (htab->byteswap_code != bfd_little_endian (output_bfd))
- bfd_putl16 (val, ptr);
- else
- bfd_putb16 (val, ptr);
+ bfd_vma upper;
+ bfd_vma lower;
+ int reloc_sign;
+
+ BFD_ASSERT ((offset & 1) == 0);
+
+ upper = bfd_get_16 (abfd, insn);
+ lower = bfd_get_16 (abfd, insn + 2);
+ reloc_sign = (offset < 0) ? 1 : 0;
+ upper = (upper & ~(bfd_vma) 0x7ff)
+ | ((offset >> 12) & 0x3ff)
+ | (reloc_sign << 10);
+ lower = (lower & ~(bfd_vma) 0x2fff)
+ | (((!((offset >> 23) & 1)) ^ reloc_sign) << 13)
+ | (((!((offset >> 22) & 1)) ^ reloc_sign) << 11)
+ | ((offset >> 1) & 0x7ff);
+ bfd_put_16 (abfd, upper, insn);
+ bfd_put_16 (abfd, lower, insn + 2);
}
-
/* Thumb code calling an ARM function. */
static int
{
asection * s = 0;
bfd_vma my_offset;
- unsigned long int tmp;
long int ret_offset;
struct elf_link_hash_entry * myh;
struct elf32_arm_link_hash_table * globals;
/* Biassing for PC-relative addressing. */
- 8;
- tmp = bfd_get_32 (input_bfd, hit_data
- - input_section->vma);
-
- bfd_put_32 (output_bfd,
- (bfd_vma) insert_thumb_branch (tmp, ret_offset),
- hit_data - input_section->vma);
+ insert_thumb_branch (input_bfd, ret_offset, hit_data - input_section->vma);
return TRUE;
}
bfd_vma val;
char *error_message;
- eh = elf32_arm_hash_entry(h);
+ eh = elf32_arm_hash_entry (h);
/* Allocate stubs for exported Thumb functions on v4t. */
if (eh->export_glue == NULL)
return TRUE;
/* Generate Arm stubs for exported Thumb symbols. */
static void
-elf32_arm_begin_write_processing (bfd *abfd ATTRIBUTE_UNUSED,
+elf32_arm_begin_write_processing (bfd *abfd ATTRIBUTE_UNUSED,
struct bfd_link_info *link_info)
{
struct elf32_arm_link_hash_table * globals;
/* Given an ARM instruction, determine whether it is an ADD or a SUB.
Returns 1 if it is an ADD, -1 if it is a SUB, and 0 otherwise. */
+
static int
-identify_add_or_sub(bfd_vma insn)
+identify_add_or_sub (bfd_vma insn)
{
int opcode = insn & 0x1e00000;
return 0;
}
-/* Determine if we're dealing with a Thumb-2 object. */
-
-static int 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;
-}
-
/* Perform a relocation as part of a final link. */
static bfd_reloc_status_type
run time. */
if ((info->shared || globals->root.is_relocatable_executable)
&& (input_section->flags & SEC_ALLOC)
+ && !(elf32_arm_hash_table (info)->vxworks_p
+ && strcmp (input_section->output_section->name,
+ ".tls_vars") == 0)
&& ((r_type != R_ARM_REL32 && r_type != R_ARM_REL32_NOI)
|| !SYMBOL_CALLS_LOCAL (info, h))
&& (h == NULL
case R_ARM_JUMP24:
case R_ARM_PC24: /* Arm B/BL instruction */
case R_ARM_PLT32:
+ {
+ bfd_vma from;
+ bfd_signed_vma branch_offset;
+ struct elf32_arm_stub_hash_entry *stub_entry = NULL;
+
+ from = (input_section->output_section->vma
+ + input_section->output_offset
+ + rel->r_offset);
+ branch_offset = (bfd_signed_vma)(value - from);
+
if (r_type == R_ARM_XPC25)
{
/* Check for Arm calling Arm function. */
input_bfd,
h ? h->root.root.string : "(local)");
}
- else if (r_type != R_ARM_CALL || !globals->use_blx)
+ else if (r_type != R_ARM_CALL)
{
/* Check for Arm calling Thumb function. */
if (sym_flags == STT_ARM_TFUNC)
}
}
+ /* Check if a stub has to be inserted because the
+ destination is too far or we are changing mode */
+ if (r_type == R_ARM_CALL)
+ {
+ if (branch_offset > ARM_MAX_FWD_BRANCH_OFFSET
+ || branch_offset < ARM_MAX_BWD_BRANCH_OFFSET
+ || sym_flags == STT_ARM_TFUNC)
+ {
+ /* The target is out of reach, so redirect the
+ branch to the local stub for this function. */
+
+ stub_entry = elf32_arm_get_stub_entry (input_section,
+ sym_sec, h,
+ rel, globals);
+ if (stub_entry != NULL)
+ value = (stub_entry->stub_offset
+ + stub_entry->stub_sec->output_offset
+ + stub_entry->stub_sec->output_section->vma);
+ }
+ }
+
/* The ARM ELF ABI says that this reloc is computed as: S - P + A
where:
S is the address of the symbol in the relocation.
if (r_type == R_ARM_CALL)
{
/* Select the correct instruction (BL or BLX). */
- if (sym_flags == STT_ARM_TFUNC)
+ /* Only if we are not handling a BL to a stub. In this
+ case, mode switching is performed by the stub. */
+ if (sym_flags == STT_ARM_TFUNC && !stub_entry)
value |= (1 << 28);
else
{
}
}
}
+ }
break;
case R_ARM_ABS32:
/* 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)
{
*unresolved_reloc_p = FALSE;
}
+ if (r_type == R_ARM_THM_CALL)
+ {
+ /* Check if a stub has to be inserted because the destination
+ is too far. */
+ bfd_vma from;
+ bfd_signed_vma branch_offset;
+ struct elf32_arm_stub_hash_entry *stub_entry = NULL;
+
+ from = (input_section->output_section->vma
+ + input_section->output_offset
+ + rel->r_offset);
+ branch_offset = (bfd_signed_vma)(value - from);
+
+ if ((!thumb2
+ && (branch_offset > THM_MAX_FWD_BRANCH_OFFSET
+ || (branch_offset < THM_MAX_BWD_BRANCH_OFFSET)))
+ ||
+ (thumb2
+ && (branch_offset > THM2_MAX_FWD_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
+ function. */
+ stub_entry = elf32_arm_get_stub_entry (input_section,
+ sym_sec, h,
+ rel, globals);
+ if (stub_entry != NULL)
+ value = (stub_entry->stub_offset
+ + stub_entry->stub_sec->output_offset
+ + stub_entry->stub_sec->output_section->vma);
+
+ /* 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;
+ }
+ }
+ }
+
relocation = value + signed_addend;
relocation -= (input_section->output_section->vma
upper_insn = (upper_insn & ~(bfd_vma) 0x7ff)
| ((relocation >> 12) & 0x3ff)
| (reloc_sign << 10);
- lower_insn = (lower_insn & ~(bfd_vma) 0x2fff)
+ lower_insn = (lower_insn & ~(bfd_vma) 0x2fff)
| (((!((relocation >> 23) & 1)) ^ reloc_sign) << 13)
| (((!((relocation >> 22) & 1)) ^ reloc_sign) << 11)
| ((relocation >> 1) & 0x7ff);
globals->tls_ldm_got.offset |= 1;
}
- value = globals->sgot->output_section->vma + globals->sgot->output_offset + off
+ 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,
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
+ 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,
(_("%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;
+ return FALSE;
}
else
value = tpoff (info, value);
-
+
return _bfd_final_link_relocate (howto, input_bfd, input_section,
contents, rel->r_offset, value,
rel->r_addend);
case R_ARM_THM_MOVT_BREL:
{
bfd_vma insn;
-
+
insn = bfd_get_16 (input_bfd, hit_data) << 16;
insn |= bfd_get_16 (input_bfd, hit_data + 2);
break;
default:
- abort();
+ abort ();
}
/* If REL, extract the addend from the insn. If RELA, it will
(_("%B(%A+0x%lx): Only ADD or SUB instructions are allowed for ALU group relocations"),
input_bfd, input_section,
(long) rel->r_offset, howto->name);
- return bfd_reloc_overflow;
+ return bfd_reloc_overflow;
}
signed_addend *= negative;
break;
default:
- abort();
+ abort ();
}
/* If REL, extract the addend from the insn. If RELA, it will
break;
default:
- abort();
+ abort ();
}
/* If REL, extract the addend from the insn. If RELA, it will
break;
default:
- abort();
+ abort ();
}
/* If REL, extract the addend from the insn. If RELA, it will
|| (R_TYPE) == R_ARM_TLS_IE32)
/* Relocate an ARM ELF section. */
+
static bfd_boolean
elf32_arm_relocate_section (bfd * output_bfd,
struct bfd_link_info * info,
/* Determine whether an object attribute tag takes an integer, a
string or both. */
+
static int
elf32_arm_obj_attrs_arg_type (int tag)
{
/* Merge EABI object attributes from IBFD into OBFD. Raise an error if there
are conflicting attributes. */
+
static bfd_boolean
elf32_arm_merge_eabi_attributes (bfd *ibfd, bfd *obfd)
{
/* Some tags have 0 = don't care, 1 = strong requirement,
2 = weak requirement. */
static const int order_312[3] = {3, 1, 2};
+ /* For use with Tag_VFP_arch. */
+ static const int order_01243[5] = {0, 1, 2, 4, 3};
int i;
if (!elf_known_obj_attributes_proc (obfd)[0].i)
case Tag_CPU_arch:
case Tag_ARM_ISA_use:
case Tag_THUMB_ISA_use:
- case Tag_VFP_arch:
case Tag_WMMX_arch:
case Tag_NEON_arch:
/* ??? Do NEON and WMMX conflict? */
if (in_attr[i].i)
out_attr[i].i = in_attr[i].i;
break;
+ case Tag_VFP_arch:
+ if (in_attr[i].i > 4 || out_attr[i].i > 4
+ || order_01243[in_attr[i].i] > order_01243[out_attr[i].i])
+ out_attr[i].i = in_attr[i].i;
+ break;
case Tag_PCS_config:
if (out_attr[i].i == 0)
out_attr[i].i = in_attr[i].i;
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
sym_hashes = elf_sym_hashes (abfd);
local_got_refcounts = elf_local_got_refcounts (abfd);
- check_use_blx(globals);
+ check_use_blx (globals);
relend = relocs + sec->reloc_count;
for (rel = relocs; rel < relend; rel++)
if (local_got_refcounts == NULL)
{
bfd_size_type size;
-
+
size = symtab_hdr->sh_info;
- size *= (sizeof (bfd_signed_vma) + sizeof(char));
+ size *= (sizeof (bfd_signed_vma) + sizeof (char));
local_got_refcounts = bfd_zalloc (abfd, size);
if (local_got_refcounts == NULL)
return FALSE;
required if the corresponding code section is marked. */
static bfd_boolean
-elf32_arm_gc_mark_extra_sections(struct bfd_link_info *info,
- elf_gc_mark_hook_fn gc_mark_hook)
+elf32_arm_gc_mark_extra_sections (struct bfd_link_info *info,
+ elf_gc_mark_hook_fn gc_mark_hook)
{
bfd *sub;
Elf_Internal_Shdr **elf_shdrp;
Elf_Internal_Shdr *hdr;
hdr = &elf_section_data (o)->this_hdr;
- if (hdr->sh_type == SHT_ARM_EXIDX && hdr->sh_link
+ if (hdr->sh_type == SHT_ARM_EXIDX
+ && hdr->sh_link
+ && hdr->sh_link < elf_numsections (sub)
&& !o->gc_mark
&& elf_shdrp[hdr->sh_link]->bfd_section->gc_mark)
{
*functionname_ptr = bfd_asymbol_name (func);
return TRUE;
-}
+}
/* Find the nearest line to a particular section and offset, for error
bh = NULL;
/* Create a new symbol to regist the real location of the function. */
s = h->root.u.def.section;
- sprintf(name, "__real_%s", h->root.root.string);
+ sprintf (name, "__real_%s", h->root.root.string);
_bfd_generic_link_add_one_symbol (info, s->owner,
name, BSF_GLOBAL, s,
h->root.u.def.value,
}
}
+ if (elf32_arm_hash_table (info)->vxworks_p)
+ {
+ struct elf32_arm_relocs_copied **pp;
+
+ for (pp = &eh->relocs_copied; (p = *pp) != NULL; )
+ {
+ if (strcmp (p->section->output_section->name, ".tls_vars") == 0)
+ *pp = p->next;
+ else
+ pp = &p->next;
+ }
+ }
+
/* Also discard relocs on undefined weak syms with non-default
visibility. */
if (eh->relocs_copied != NULL
bfd_size_type locsymcount;
Elf_Internal_Shdr *symtab_hdr;
asection *srel;
+ bfd_boolean is_vxworks = elf32_arm_hash_table (info)->vxworks_p;
if (! is_arm_elf (ibfd))
continue;
linker script /DISCARD/, so we'll be discarding
the relocs too. */
}
+ else if (is_vxworks
+ && strcmp (p->section->output_section->name,
+ ".tls_vars") == 0)
+ {
+ /* Relocations in vxworks .tls_vars sections are
+ handled specially by the loader. */
+ }
else if (p->count != 0)
{
srel = elf_section_data (p->section)->sreloc;
/* Initialise mapping tables for code/data. */
bfd_elf32_arm_init_maps (ibfd);
-
+
if (!bfd_elf32_arm_process_before_allocation (ibfd, info)
|| !bfd_elf32_arm_vfp11_erratum_scan (ibfd, info))
/* xgettext:c-format */
continue;
/* Allocate memory for the section contents. */
- s->contents = (bfd_byte *) bfd_zalloc (dynobj, s->size);
+ s->contents = bfd_zalloc (dynobj, s->size);
if (s->contents == NULL)
return FALSE;
}
dynamic sections here. */
static bfd_boolean
-elf32_arm_finish_dynamic_symbol (bfd * output_bfd, struct bfd_link_info * info,
- struct elf_link_hash_entry * h, Elf_Internal_Sym * sym)
+elf32_arm_finish_dynamic_symbol (bfd * output_bfd,
+ struct bfd_link_info * info,
+ struct elf_link_hash_entry * h,
+ Elf_Internal_Sym * sym)
{
bfd * dynobj;
struct elf32_arm_link_hash_table *htab;
/* Fill in the entry in the procedure linkage table. */
if (htab->symbian_p)
{
- put_arm_insn (htab, output_bfd,
+ put_arm_insn (htab, output_bfd,
elf32_arm_symbian_plt_entry[0],
splt->contents + h->plt.offset);
- bfd_put_32 (output_bfd,
+ bfd_put_32 (output_bfd,
elf32_arm_symbian_plt_entry[1],
splt->contents + h->plt.offset + 4);
-
+
/* Fill in the entry in the .rel.plt section. */
rel.r_offset = (splt->output_section->vma
+ splt->output_offset
corresponds to this symbol. This is the index of this symbol
in all the symbols for which we are making plt entries. The
first entry in the procedure linkage table is reserved. */
- plt_index = ((h->plt.offset - htab->plt_header_size)
+ plt_index = ((h->plt.offset - htab->plt_header_size)
/ htab->plt_entry_size);
}
else
bfd_vma got_displacement;
asection * sgot;
bfd_byte * ptr;
-
+
sgot = bfd_get_section_by_name (dynobj, ".got.plt");
BFD_ASSERT (sgot != NULL);
(splt->output_section->vma
+ splt->output_offset),
sgot->contents + got_offset);
-
+
/* Fill in the entry in the .rel(a).plt section. */
rel.r_addend = 0;
rel.r_offset = got_address;
if (info->shared
&& SYMBOL_REFERENCES_LOCAL (info, h))
{
- BFD_ASSERT((h->got.offset & 1) != 0);
+ BFD_ASSERT ((h->got.offset & 1) != 0);
rel.r_info = ELF32_R_INFO (0, R_ARM_RELATIVE);
if (!htab->use_rel)
{
}
else
{
- BFD_ASSERT((h->got.offset & 1) == 0);
+ BFD_ASSERT ((h->got.offset & 1) == 0);
bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents + offset);
rel.r_info = ELF32_R_INFO (h->dynindx, R_ARM_GLOB_DAT);
}
dyn.d_un.d_val = s->size;
bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon);
break;
-
+
case DT_RELSZ:
case DT_RELASZ:
if (!htab->symbian_p)
dyn.d_un.d_val = 0;
for (i = 1; i < elf_numsections (output_bfd); i++)
{
- Elf_Internal_Shdr *hdr
+ Elf_Internal_Shdr *hdr
= elf_elfsections (output_bfd)[i];
if (hdr->sh_type == type)
{
- if (dyn.d_tag == DT_RELSZ
+ if (dyn.d_tag == DT_RELSZ
|| dyn.d_tag == DT_RELASZ)
dyn.d_un.d_val += hdr->sh_size;
else if ((ufile_ptr) hdr->sh_offset
eh = elf_link_hash_lookup (elf_hash_table (info), name,
FALSE, FALSE, TRUE);
- if (eh != (struct elf_link_hash_entry *) NULL
+ if (eh != NULL
&& ELF_ST_TYPE (eh->type) == STT_ARM_TFUNC)
{
dyn.d_un.d_val |= 1;
};
-/* 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,
+ /* A three-word PLT with no Thumb thunk contains only Arm code,
so only need to output a mapping symbol for the first PLT entry and
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. */
static bfd_boolean
elf32_arm_output_arch_local_syms (bfd *output_bfd,
- struct bfd_link_info *info,
- void *finfo, bfd_boolean (*func) (void *, const char *,
- Elf_Internal_Sym *,
- asection *,
- struct elf_link_hash_entry *))
+ struct bfd_link_info *info,
+ void *finfo,
+ bfd_boolean (*func) (void *, const char *,
+ Elf_Internal_Sym *,
+ asection *,
+ struct elf_link_hash_entry *))
{
output_arch_syminfo osi;
struct elf32_arm_link_hash_table *htab;
bfd_size_type size;
htab = elf32_arm_hash_table (info);
- check_use_blx(htab);
+ check_use_blx (htab);
osi.finfo = finfo;
osi.info = info;
osi.func = func;
-
+
/* ARM->Thumb glue. */
if (htab->arm_glue_size > 0)
{
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
}
*/
newsym.st_value |= 1;
}
-
+
src = &newsym;
}
bfd_elf32_swap_symbol_out (abfd, src, cdst, shndx);
/* Add the PT_ARM_EXIDX program header. */
static bfd_boolean
-elf32_arm_modify_segment_map (bfd *abfd,
+elf32_arm_modify_segment_map (bfd *abfd,
struct bfd_link_info *info ATTRIBUTE_UNUSED)
{
struct elf_segment_map *m;
}
/* We have two function types: STT_FUNC and STT_ARM_TFUNC. */
+
static bfd_boolean
elf32_arm_is_function_type (unsigned int type)
{
}
/* We use this to override swap_symbol_in and swap_symbol_out. */
-const struct elf_size_info elf32_arm_size_info = {
+const struct elf_size_info elf32_arm_size_info =
+{
sizeof (Elf32_External_Ehdr),
sizeof (Elf32_External_Phdr),
sizeof (Elf32_External_Shdr),
#define bfd_elf32_bfd_set_private_flags elf32_arm_set_private_flags
#define bfd_elf32_bfd_print_private_bfd_data elf32_arm_print_private_bfd_data
#define bfd_elf32_bfd_link_hash_table_create elf32_arm_link_hash_table_create
+#define bfd_elf32_bfd_link_hash_table_free elf32_arm_hash_table_free
#define bfd_elf32_bfd_reloc_type_lookup elf32_arm_reloc_type_lookup
#define bfd_elf32_bfd_reloc_name_lookup elf32_arm_reloc_name_lookup
#define bfd_elf32_find_nearest_line elf32_arm_find_nearest_line
#define elf_backend_symbol_processing elf32_arm_symbol_processing
#define elf_backend_size_info elf32_arm_size_info
#define elf_backend_modify_segment_map elf32_arm_modify_segment_map
-#define elf_backend_additional_program_headers \
- elf32_arm_additional_program_headers
-#define elf_backend_output_arch_local_syms \
- elf32_arm_output_arch_local_syms
-#define elf_backend_begin_write_processing \
- elf32_arm_begin_write_processing
-#define elf_backend_is_function_type elf32_arm_is_function_type
-
-#define elf_backend_can_refcount 1
-#define elf_backend_can_gc_sections 1
-#define elf_backend_plt_readonly 1
-#define elf_backend_want_got_plt 1
-#define elf_backend_want_plt_sym 0
-#define elf_backend_may_use_rel_p 1
-#define elf_backend_may_use_rela_p 0
+#define elf_backend_additional_program_headers elf32_arm_additional_program_headers
+#define elf_backend_output_arch_local_syms elf32_arm_output_arch_local_syms
+#define elf_backend_begin_write_processing elf32_arm_begin_write_processing
+#define elf_backend_is_function_type elf32_arm_is_function_type
+
+#define elf_backend_can_refcount 1
+#define elf_backend_can_gc_sections 1
+#define elf_backend_plt_readonly 1
+#define elf_backend_want_got_plt 1
+#define elf_backend_want_plt_sym 0
+#define elf_backend_may_use_rel_p 1
+#define elf_backend_may_use_rela_p 0
#define elf_backend_default_use_rela_p 0
#define elf_backend_got_header_size 12
-#undef elf_backend_obj_attrs_vendor
-#define elf_backend_obj_attrs_vendor "aeabi"
-#undef elf_backend_obj_attrs_section
-#define elf_backend_obj_attrs_section ".ARM.attributes"
-#undef elf_backend_obj_attrs_arg_type
-#define elf_backend_obj_attrs_arg_type elf32_arm_obj_attrs_arg_type
-#undef elf_backend_obj_attrs_section_type
+#undef elf_backend_obj_attrs_vendor
+#define elf_backend_obj_attrs_vendor "aeabi"
+#undef elf_backend_obj_attrs_section
+#define elf_backend_obj_attrs_section ".ARM.attributes"
+#undef elf_backend_obj_attrs_arg_type
+#define elf_backend_obj_attrs_arg_type elf32_arm_obj_attrs_arg_type
+#undef elf_backend_obj_attrs_section_type
#define elf_backend_obj_attrs_section_type SHT_ARM_ATTRIBUTES
#include "elf32-target.h"
-/* VxWorks Targets */
+/* VxWorks Targets. */
-#undef TARGET_LITTLE_SYM
+#undef TARGET_LITTLE_SYM
#define TARGET_LITTLE_SYM bfd_elf32_littlearm_vxworks_vec
-#undef TARGET_LITTLE_NAME
+#undef TARGET_LITTLE_NAME
#define TARGET_LITTLE_NAME "elf32-littlearm-vxworks"
-#undef TARGET_BIG_SYM
+#undef TARGET_BIG_SYM
#define TARGET_BIG_SYM bfd_elf32_bigarm_vxworks_vec
-#undef TARGET_BIG_NAME
+#undef TARGET_BIG_NAME
#define TARGET_BIG_NAME "elf32-bigarm-vxworks"
/* Like elf32_arm_link_hash_table_create -- but overrides
appropriately for VxWorks. */
+
static struct bfd_link_hash_table *
elf32_arm_vxworks_link_hash_table_create (bfd *abfd)
{
htab->vxworks_p = 1;
}
return ret;
-}
+}
static void
elf32_arm_vxworks_final_write_processing (bfd *abfd, bfd_boolean linker)
elf_vxworks_final_write_processing (abfd, linker);
}
-#undef elf32_bed
+#undef elf32_bed
#define elf32_bed elf32_arm_vxworks_bed
-#undef bfd_elf32_bfd_link_hash_table_create
-#define bfd_elf32_bfd_link_hash_table_create \
- elf32_arm_vxworks_link_hash_table_create
-#undef elf_backend_add_symbol_hook
-#define elf_backend_add_symbol_hook \
- elf_vxworks_add_symbol_hook
-#undef elf_backend_final_write_processing
-#define elf_backend_final_write_processing \
- elf32_arm_vxworks_final_write_processing
-#undef elf_backend_emit_relocs
-#define elf_backend_emit_relocs \
- elf_vxworks_emit_relocs
+#undef bfd_elf32_bfd_link_hash_table_create
+#define bfd_elf32_bfd_link_hash_table_create elf32_arm_vxworks_link_hash_table_create
+#undef elf_backend_add_symbol_hook
+#define elf_backend_add_symbol_hook elf_vxworks_add_symbol_hook
+#undef elf_backend_final_write_processing
+#define elf_backend_final_write_processing elf32_arm_vxworks_final_write_processing
+#undef elf_backend_emit_relocs
+#define elf_backend_emit_relocs elf_vxworks_emit_relocs
-#undef elf_backend_may_use_rel_p
+#undef elf_backend_may_use_rel_p
#define elf_backend_may_use_rel_p 0
-#undef elf_backend_may_use_rela_p
+#undef elf_backend_may_use_rela_p
#define elf_backend_may_use_rela_p 1
-#undef elf_backend_default_use_rela_p
+#undef elf_backend_default_use_rela_p
#define elf_backend_default_use_rela_p 1
-#undef elf_backend_want_plt_sym
+#undef elf_backend_want_plt_sym
#define elf_backend_want_plt_sym 1
-#undef ELF_MAXPAGESIZE
+#undef ELF_MAXPAGESIZE
#define ELF_MAXPAGESIZE 0x1000
#include "elf32-target.h"
-/* Symbian OS Targets */
+/* Symbian OS Targets. */
-#undef TARGET_LITTLE_SYM
+#undef TARGET_LITTLE_SYM
#define TARGET_LITTLE_SYM bfd_elf32_littlearm_symbian_vec
-#undef TARGET_LITTLE_NAME
+#undef TARGET_LITTLE_NAME
#define TARGET_LITTLE_NAME "elf32-littlearm-symbian"
-#undef TARGET_BIG_SYM
+#undef TARGET_BIG_SYM
#define TARGET_BIG_SYM bfd_elf32_bigarm_symbian_vec
-#undef TARGET_BIG_NAME
+#undef TARGET_BIG_NAME
#define TARGET_BIG_NAME "elf32-bigarm-symbian"
/* Like elf32_arm_link_hash_table_create -- but overrides
appropriately for Symbian OS. */
+
static struct bfd_link_hash_table *
elf32_arm_symbian_link_hash_table_create (bfd *abfd)
{
= (struct elf32_arm_link_hash_table *)ret;
/* There is no PLT header for Symbian OS. */
htab->plt_header_size = 0;
- /* The PLT entries are each three instructions. */
- htab->plt_entry_size = 4 * NUM_ELEM (elf32_arm_symbian_plt_entry);
+ /* The PLT entries are each one instruction and one word. */
+ htab->plt_entry_size = 4 * ARRAY_SIZE (elf32_arm_symbian_plt_entry);
htab->symbian_p = 1;
/* Symbian uses armv5t or above, so use_blx is always true. */
htab->use_blx = 1;
htab->root.is_relocatable_executable = 1;
}
return ret;
-}
+}
static const struct bfd_elf_special_section
elf32_arm_symbian_special_sections[] =
};
static void
-elf32_arm_symbian_begin_write_processing (bfd *abfd,
+elf32_arm_symbian_begin_write_processing (bfd *abfd,
struct bfd_link_info *link_info)
{
/* BPABI objects are never loaded directly by an OS kernel; they are
recognize that the program headers should not be mapped into any
loadable segment. */
abfd->flags &= ~D_PAGED;
- elf32_arm_begin_write_processing(abfd, link_info);
+ elf32_arm_begin_write_processing (abfd, link_info);
}
static bfd_boolean
-elf32_arm_symbian_modify_segment_map (bfd *abfd,
+elf32_arm_symbian_modify_segment_map (bfd *abfd,
struct bfd_link_info *info)
{
struct elf_segment_map *m;
return elf32_arm_modify_segment_map (abfd, info);
}
+/* Return address for Ith PLT stub in section PLT, for relocation REL
+ or (bfd_vma) -1 if it should not be included. */
+
+static bfd_vma
+elf32_arm_symbian_plt_sym_val (bfd_vma i, const asection *plt,
+ const arelent *rel ATTRIBUTE_UNUSED)
+{
+ return plt->vma + 4 * ARRAY_SIZE (elf32_arm_symbian_plt_entry) * i;
+}
+
+
#undef elf32_bed
#define elf32_bed elf32_arm_symbian_bed
/* The dynamic sections are not allocated on SymbianOS; the postlinker
will process them and then discard them. */
-#undef ELF_DYNAMIC_SEC_FLAGS
+#undef ELF_DYNAMIC_SEC_FLAGS
#define ELF_DYNAMIC_SEC_FLAGS \
(SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED)
-#undef bfd_elf32_bfd_link_hash_table_create
-#define bfd_elf32_bfd_link_hash_table_create \
- elf32_arm_symbian_link_hash_table_create
#undef elf_backend_add_symbol_hook
-
-#undef elf_backend_special_sections
-#define elf_backend_special_sections elf32_arm_symbian_special_sections
-
-#undef elf_backend_begin_write_processing
-#define elf_backend_begin_write_processing \
- elf32_arm_symbian_begin_write_processing
-#undef elf_backend_final_write_processing
-#define elf_backend_final_write_processing \
- elf32_arm_final_write_processing
#undef elf_backend_emit_relocs
-#undef elf_backend_modify_segment_map
+#undef bfd_elf32_bfd_link_hash_table_create
+#define bfd_elf32_bfd_link_hash_table_create elf32_arm_symbian_link_hash_table_create
+#undef elf_backend_special_sections
+#define elf_backend_special_sections elf32_arm_symbian_special_sections
+#undef elf_backend_begin_write_processing
+#define elf_backend_begin_write_processing elf32_arm_symbian_begin_write_processing
+#undef elf_backend_final_write_processing
+#define elf_backend_final_write_processing elf32_arm_final_write_processing
+
+#undef elf_backend_modify_segment_map
#define elf_backend_modify_segment_map elf32_arm_symbian_modify_segment_map
/* There is no .got section for BPABI objects, and hence no header. */
-#undef elf_backend_got_header_size
+#undef elf_backend_got_header_size
#define elf_backend_got_header_size 0
/* Similarly, there is no .got.plt section. */
-#undef elf_backend_want_got_plt
+#undef elf_backend_want_got_plt
#define elf_backend_want_got_plt 0
-#undef elf_backend_may_use_rel_p
+#undef elf_backend_plt_sym_val
+#define elf_backend_plt_sym_val elf32_arm_symbian_plt_sym_val
+
+#undef elf_backend_may_use_rel_p
#define elf_backend_may_use_rel_p 1
-#undef elf_backend_may_use_rela_p
+#undef elf_backend_may_use_rela_p
#define elf_backend_may_use_rela_p 0
-#undef elf_backend_default_use_rela_p
+#undef elf_backend_default_use_rela_p
#define elf_backend_default_use_rela_p 0
-#undef elf_backend_want_plt_sym
+#undef elf_backend_want_plt_sym
#define elf_backend_want_plt_sym 0
-#undef ELF_MAXPAGESIZE
+#undef ELF_MAXPAGESIZE
#define ELF_MAXPAGESIZE 0x8000
#include "elf32-target.h"