MA 02110-1301, USA. */
#include "sysdep.h"
+#include <limits.h>
+
#include "bfd.h"
#include "libiberty.h"
#include "libbfd.h"
static struct elf_backend_data elf32_arm_vxworks_bed;
+static bfd_boolean elf32_arm_write_section (bfd *output_bfd,
+ struct bfd_link_info *link_info,
+ asection *sec,
+ bfd_byte *contents);
+
/* Note: code such as elf32_arm_reloc_type_lookup expect to use e.g.
R_ARM_PC24 as an index into this, and find the R_ARM_PC24 HOWTO
in that slot. */
interworkable. */
#define INTERWORK_FLAG(abfd) \
(EF_ARM_EABI_VERSION (elf_elfheader (abfd)->e_flags) >= EF_ARM_EABI_VER4 \
- || (elf_elfheader (abfd)->e_flags & EF_ARM_INTERWORK))
+ || (elf_elfheader (abfd)->e_flags & EF_ARM_INTERWORK) \
+ || ((abfd)->flags & BFD_LINKER_CREATED))
/* The linker script knows the section names for placement.
The entry_names are used to do simple name mangling on the stubs.
#define THM2_MAX_FWD_BRANCH_OFFSET (((1 << 24) - 2) + 4)
#define THM2_MAX_BWD_BRANCH_OFFSET (-(1 << 24) + 4)
+enum stub_insn_type
+ {
+ THUMB16_TYPE = 1,
+ THUMB32_TYPE,
+ ARM_TYPE,
+ DATA_TYPE
+ };
+
+#define THUMB16_INSN(X) {(X), THUMB16_TYPE, R_ARM_NONE, 0}
+#define THUMB32_INSN(X) {(X), THUMB32_TYPE, R_ARM_NONE, 0}
+#define ARM_INSN(X) {(X), ARM_TYPE, R_ARM_NONE, 0}
+#define ARM_REL_INSN(X, Z) {(X), ARM_TYPE, R_ARM_JUMP24, (Z)}
+#define DATA_WORD(X,Y,Z) {(X), DATA_TYPE, (Y), (Z)}
+
+typedef struct
+{
+ bfd_vma data;
+ enum stub_insn_type type;
+ unsigned int r_type;
+ int reloc_addend;
+} insn_sequence;
+
/* Arm/Thumb -> Arm/Thumb long branch stub. On V5T and above, use blx
to reach the stub if necessary. */
-static const bfd_vma elf32_arm_stub_long_branch_any_any[] =
+static const insn_sequence elf32_arm_stub_long_branch_any_any[] =
{
- 0xe51ff004, /* ldr pc, [pc, #-4] */
- 0x00000000, /* dcd R_ARM_ABS32(X) */
+ ARM_INSN(0xe51ff004), /* ldr pc, [pc, #-4] */
+ DATA_WORD(0, R_ARM_ABS32, 0), /* dcd R_ARM_ABS32(X) */
};
/* V4T Arm -> Thumb long branch stub. Used on V4T where blx is not
available. */
-static const bfd_vma elf32_arm_stub_long_branch_v4t_arm_thumb[] =
+static const insn_sequence elf32_arm_stub_long_branch_v4t_arm_thumb[] =
{
- 0xe59fc000, /* ldr ip, [pc, #0] */
- 0xe12fff1c, /* bx ip */
- 0x00000000, /* dcd R_ARM_ABS32(X) */
+ ARM_INSN(0xe59fc000), /* ldr ip, [pc, #0] */
+ ARM_INSN(0xe12fff1c), /* bx ip */
+ DATA_WORD(0, R_ARM_ABS32, 0), /* dcd R_ARM_ABS32(X) */
};
-/* Thumb -> Thumb long branch stub. Used on architectures which
- support only this mode, or on V4T where it is expensive to switch
- to ARM. */
-static const bfd_vma elf32_arm_stub_long_branch_thumb_only[] =
+/* Thumb -> Thumb long branch stub. Used on M-profile architectures. */
+static const insn_sequence elf32_arm_stub_long_branch_thumb_only[] =
{
- 0x4802b401, /* push {r0} */
- /* ldr r0, [pc, #8] */
- 0xbc014684, /* mov ip, r0 */
- /* pop {r0} */
- 0xbf004760, /* bx ip */
- /* nop */
- 0x00000000, /* dcd R_ARM_ABS32(X) */
+ THUMB16_INSN(0xb401), /* push {r0} */
+ THUMB16_INSN(0x4802), /* ldr r0, [pc, #8] */
+ THUMB16_INSN(0x4684), /* mov ip, r0 */
+ THUMB16_INSN(0xbc01), /* pop {r0} */
+ THUMB16_INSN(0x4760), /* bx ip */
+ THUMB16_INSN(0xbf00), /* nop */
+ DATA_WORD(0, R_ARM_ABS32, 0), /* dcd R_ARM_ABS32(X) */
+ };
+
+/* V4T Thumb -> Thumb long branch stub. Using the stack is not
+ allowed. */
+static const insn_sequence elf32_arm_stub_long_branch_v4t_thumb_thumb[] =
+ {
+ THUMB16_INSN(0x4778), /* bx pc */
+ THUMB16_INSN(0x46c0), /* nop */
+ ARM_INSN(0xe59fc000), /* ldr ip, [pc, #0] */
+ ARM_INSN(0xe12fff1c), /* bx ip */
+ DATA_WORD(0, R_ARM_ABS32, 0), /* dcd R_ARM_ABS32(X) */
};
/* V4T Thumb -> ARM long branch stub. Used on V4T where blx is not
available. */
-static const bfd_vma elf32_arm_stub_long_branch_v4t_thumb_arm[] =
+static const insn_sequence elf32_arm_stub_long_branch_v4t_thumb_arm[] =
{
- 0x46c04778, /* bx pc */
- /* nop */
- 0xe51ff004, /* ldr pc, [pc, #-4] */
- 0x00000000, /* dcd R_ARM_ABS32(X) */
+ THUMB16_INSN(0x4778), /* bx pc */
+ THUMB16_INSN(0x46c0), /* nop */
+ ARM_INSN(0xe51ff004), /* ldr pc, [pc, #-4] */
+ DATA_WORD(0, R_ARM_ABS32, 0), /* dcd R_ARM_ABS32(X) */
};
/* V4T Thumb -> ARM short branch stub. Shorter variant of the above
one, when the destination is close enough. */
-static const bfd_vma elf32_arm_stub_short_branch_v4t_thumb_arm[] =
+static const insn_sequence elf32_arm_stub_short_branch_v4t_thumb_arm[] =
{
- 0x46c04778, /* bx pc */
- /* nop */
- 0xea000000, /* b (X) */
+ THUMB16_INSN(0x4778), /* bx pc */
+ THUMB16_INSN(0x46c0), /* nop */
+ ARM_REL_INSN(0xea000000, -8), /* b (X-8) */
};
-/* ARM/Thumb -> ARM/Thumb long branch stub, PIC. On V5T and above, use
+/* ARM/Thumb -> ARM long branch stub, PIC. On V5T and above, use
blx to reach the stub if necessary. */
-static const bfd_vma elf32_arm_stub_long_branch_any_any_pic[] =
+static const insn_sequence elf32_arm_stub_long_branch_any_arm_pic[] =
+ {
+ ARM_INSN(0xe59fc000), /* ldr r12, [pc] */
+ ARM_INSN(0xe08ff00c), /* add pc, pc, ip */
+ DATA_WORD(0, R_ARM_REL32, -4), /* dcd R_ARM_REL32(X-4) */
+ };
+
+/* ARM/Thumb -> Thumb long branch stub, PIC. On V5T and above, use
+ blx to reach the stub if necessary. We can not add into pc;
+ it is not guaranteed to mode switch (different in ARMv6 and
+ ARMv7). */
+static const insn_sequence elf32_arm_stub_long_branch_any_thumb_pic[] =
+ {
+ ARM_INSN(0xe59fc004), /* ldr r12, [pc, #4] */
+ ARM_INSN(0xe08fc00c), /* add ip, pc, ip */
+ ARM_INSN(0xe12fff1c), /* bx ip */
+ DATA_WORD(0, R_ARM_REL32, 0), /* dcd R_ARM_REL32(X) */
+ };
+
+/* V4T ARM -> ARM long branch stub, PIC. */
+static const insn_sequence elf32_arm_stub_long_branch_v4t_arm_thumb_pic[] =
+ {
+ ARM_INSN(0xe59fc004), /* ldr ip, [pc, #4] */
+ ARM_INSN(0xe08fc00c), /* add ip, pc, ip */
+ ARM_INSN(0xe12fff1c), /* bx ip */
+ DATA_WORD(0, R_ARM_REL32, 0), /* dcd R_ARM_REL32(X) */
+ };
+
+/* V4T Thumb -> ARM long branch stub, PIC. */
+static const insn_sequence elf32_arm_stub_long_branch_v4t_thumb_arm_pic[] =
+ {
+ THUMB16_INSN(0x4778), /* bx pc */
+ THUMB16_INSN(0x46c0), /* nop */
+ ARM_INSN(0xe59fc000), /* ldr ip, [pc, #0] */
+ ARM_INSN(0xe08cf00f), /* add pc, ip, pc */
+ DATA_WORD(0, R_ARM_REL32, -4), /* dcd R_ARM_REL32(X) */
+ };
+
+/* Thumb -> Thumb long branch stub, PIC. Used on M-profile
+ architectures. */
+static const insn_sequence elf32_arm_stub_long_branch_thumb_only_pic[] =
+ {
+ THUMB16_INSN(0xb401), /* push {r0} */
+ THUMB16_INSN(0x4802), /* ldr r0, [pc, #8] */
+ THUMB16_INSN(0x46fc), /* mov ip, pc */
+ THUMB16_INSN(0x4484), /* add ip, r0 */
+ THUMB16_INSN(0xbc01), /* pop {r0} */
+ THUMB16_INSN(0x4760), /* bx ip */
+ DATA_WORD(0, R_ARM_REL32, 4), /* dcd R_ARM_REL32(X) */
+ };
+
+/* V4T Thumb -> Thumb long branch stub, PIC. Using the stack is not
+ allowed. */
+static const insn_sequence elf32_arm_stub_long_branch_v4t_thumb_thumb_pic[] =
{
- 0xe59fc000, /* ldr r12, [pc] */
- 0xe08ff00c, /* add pc, pc, ip */
- 0x00000000, /* dcd R_ARM_REL32(X-4) */
+ THUMB16_INSN(0x4778), /* bx pc */
+ THUMB16_INSN(0x46c0), /* nop */
+ ARM_INSN(0xe59fc004), /* ldr ip, [pc, #4] */
+ ARM_INSN(0xe08fc00c), /* add ip, pc, ip */
+ ARM_INSN(0xe12fff1c), /* bx ip */
+ DATA_WORD(0, R_ARM_REL32, 0), /* 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
-{
+/* One entry per long/short branch stub defined above. */
+#define DEF_STUBS \
+ DEF_STUB(long_branch_any_any) \
+ DEF_STUB(long_branch_v4t_arm_thumb) \
+ DEF_STUB(long_branch_thumb_only) \
+ DEF_STUB(long_branch_v4t_thumb_thumb) \
+ DEF_STUB(long_branch_v4t_thumb_arm) \
+ DEF_STUB(short_branch_v4t_thumb_arm) \
+ DEF_STUB(long_branch_any_arm_pic) \
+ DEF_STUB(long_branch_any_thumb_pic) \
+ DEF_STUB(long_branch_v4t_thumb_thumb_pic) \
+ DEF_STUB(long_branch_v4t_arm_thumb_pic) \
+ DEF_STUB(long_branch_v4t_thumb_arm_pic) \
+ DEF_STUB(long_branch_thumb_only_pic)
+
+#define DEF_STUB(x) arm_stub_##x,
+enum elf32_arm_stub_type {
arm_stub_none,
- arm_stub_long_branch_any_any,
- arm_stub_long_branch_v4t_arm_thumb,
- arm_stub_long_branch_thumb_only,
- arm_stub_long_branch_v4t_thumb_arm,
- arm_stub_short_branch_v4t_thumb_arm,
- arm_stub_long_branch_any_any_pic,
+ DEF_STUBS
+};
+#undef DEF_STUB
+
+typedef struct
+{
+ const insn_sequence* template;
+ int template_size;
+} stub_def;
+
+#define DEF_STUB(x) {elf32_arm_stub_##x, ARRAY_SIZE(elf32_arm_stub_##x)},
+static const stub_def stub_definitions[] = {
+ {NULL, 0},
+ DEF_STUBS
};
struct elf32_arm_stub_hash_entry
bfd_vma target_value;
asection *target_section;
+ /* The stub type. */
enum elf32_arm_stub_type stub_type;
+ /* Its encoding size in bytes. */
+ int stub_size;
+ /* Its template. */
+ const insn_sequence *stub_template;
+ /* The size of the template (number of entries). */
+ int stub_template_size;
/* The symbol table entry, if any, that this was derived from. */
struct elf32_arm_link_hash_entry *h;
}
elf32_vfp11_erratum_list;
+typedef enum
+{
+ DELETE_EXIDX_ENTRY,
+ INSERT_EXIDX_CANTUNWIND_AT_END
+}
+arm_unwind_edit_type;
+
+/* A (sorted) list of edits to apply to an unwind table. */
+typedef struct arm_unwind_table_edit
+{
+ arm_unwind_edit_type type;
+ /* Note: we sometimes want to insert an unwind entry corresponding to a
+ section different from the one we're currently writing out, so record the
+ (text) section this edit relates to here. */
+ asection *linked_section;
+ unsigned int index;
+ struct arm_unwind_table_edit *next;
+}
+arm_unwind_table_edit;
+
typedef struct _arm_elf_section_data
{
+ /* Information about mapping symbols. */
struct bfd_elf_section_data elf;
unsigned int mapcount;
unsigned int mapsize;
elf32_arm_section_map *map;
+ /* Information about CPU errata. */
unsigned int erratumcount;
elf32_vfp11_erratum_list *erratumlist;
+ /* Information about unwind tables. */
+ union
+ {
+ /* Unwind info attached to a text section. */
+ struct
+ {
+ asection *arm_exidx_sec;
+ } text;
+
+ /* Unwind info attached to an .ARM.exidx section. */
+ struct
+ {
+ arm_unwind_table_edit *unwind_edit_list;
+ arm_unwind_table_edit *unwind_edit_tail;
+ } exidx;
+ } u;
}
_arm_elf_section_data;
eh->target_value = 0;
eh->target_section = NULL;
eh->stub_type = arm_stub_none;
+ eh->stub_size = 0;
+ eh->stub_template = NULL;
+ eh->stub_template_size = 0;
eh->h = NULL;
eh->id_sec = NULL;
}
case arm_stub_long_branch_thumb_only:
case arm_stub_long_branch_v4t_thumb_arm:
case arm_stub_short_branch_v4t_thumb_arm:
+ case arm_stub_long_branch_v4t_thumb_arm_pic:
+ case arm_stub_long_branch_thumb_only_pic:
return TRUE;
case arm_stub_none:
BFD_FAIL ();
int thumb2;
int thumb_only;
enum elf32_arm_stub_type stub_type = arm_stub_none;
+ int use_plt = 0;
/* We don't know the actual type of destination in case it is of
type STT_SECTION: give up. */
r_type = ELF32_R_TYPE (rel->r_info);
- /* If the call will go through a PLT entry then we do not need
- glue. */
+ /* Keep a simpler condition, for the sake of clarity. */
if (globals->splt != NULL && hash != NULL && hash->root.plt.offset != (bfd_vma) -1)
- return stub_type;
-
- if (r_type == R_ARM_THM_CALL)
{
+ use_plt = 1;
+ /* Note when dealing with PLT entries: the main PLT stub is in
+ ARM mode, so if the branch is in Thumb mode, another
+ Thumb->ARM stub will be inserted later just before the ARM
+ PLT stub. We don't take this extra distance into account
+ here, because if a long branch stub is needed, we'll add a
+ Thumb->Arm one and branch directly to the ARM PLT entry
+ because it avoids spreading offset corrections in several
+ places. */
+ }
+
+ if (r_type == R_ARM_THM_CALL || r_type == R_ARM_THM_JUMP24)
+ {
+ /* Handle cases where:
+ - this call goes too far (different Thumb/Thumb2 max
+ distance)
+ - it's a Thumb->Arm call and blx is not available, or it's a
+ Thumb->Arm branch (not bl). A stub is needed in this case,
+ but only if this call is not through a PLT entry. Indeed,
+ PLT stubs handle mode switching already.
+ */
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))
+ || ((st_type != STT_ARM_TFUNC)
+ && (((r_type == R_ARM_THM_CALL) && !globals->use_blx)
+ || (r_type == R_ARM_THM_JUMP24))
+ && !use_plt))
{
if (st_type == STT_ARM_TFUNC)
{
{
stub_type = (info->shared | globals->pic_veneer)
/* PIC stubs. */
- ? ((globals->use_blx)
- /* V5T and above. */
- ? arm_stub_long_branch_any_any_pic
- /* not yet supported on V4T. */
- : arm_stub_none)
+ ? ((globals->use_blx
+ && (r_type ==R_ARM_THM_CALL))
+ /* V5T and above. Stub starts with ARM code, so
+ we must be able to switch mode before
+ reaching it, which is only possible for 'bl'
+ (ie R_ARM_THM_CALL relocation). */
+ ? arm_stub_long_branch_any_thumb_pic
+ /* On V4T, use Thumb code only. */
+ : arm_stub_long_branch_v4t_thumb_thumb_pic)
/* non-PIC stubs. */
- : ((globals->use_blx)
+ : ((globals->use_blx
+ && (r_type ==R_ARM_THM_CALL))
/* V5T and above. */
? arm_stub_long_branch_any_any
/* V4T. */
- : arm_stub_long_branch_thumb_only);
+ : arm_stub_long_branch_v4t_thumb_thumb);
}
else
{
stub_type = (info->shared | globals->pic_veneer)
- /* PIC stub not yet supported on V4T. */
- ? arm_stub_none
+ /* PIC stub. */
+ ? arm_stub_long_branch_thumb_only_pic
/* non-PIC stub. */
: arm_stub_long_branch_thumb_only;
}
stub_type = (info->shared | globals->pic_veneer)
/* PIC stubs. */
- ? ((globals->use_blx)
+ ? ((globals->use_blx
+ && (r_type ==R_ARM_THM_CALL))
/* V5T and above. */
- ? arm_stub_long_branch_any_any_pic
- /* not yet supported on V4T. */
- : arm_stub_none)
+ ? arm_stub_long_branch_any_arm_pic
+ /* V4T PIC stub. */
+ : arm_stub_long_branch_v4t_thumb_arm_pic)
/* non-PIC stubs. */
- : ((globals->use_blx)
+ : ((globals->use_blx
+ && (r_type ==R_ARM_THM_CALL))
/* V5T and above. */
? arm_stub_long_branch_any_any
/* V4T. */
}
}
}
- else if (r_type == R_ARM_CALL)
+ else if (r_type == R_ARM_CALL || r_type == R_ARM_JUMP24 || r_type == R_ARM_PLT32)
{
if (st_type == STT_ARM_TFUNC)
{
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)
+ || ((r_type == R_ARM_CALL) && !globals->use_blx)
+ || (r_type == R_ARM_JUMP24)
+ || (r_type == R_ARM_PLT32))
{
stub_type = (info->shared | globals->pic_veneer)
/* PIC stubs. */
- ? arm_stub_long_branch_any_any_pic
+ ? ((globals->use_blx)
+ /* V5T and above. */
+ ? arm_stub_long_branch_any_thumb_pic
+ /* V4T stub. */
+ : arm_stub_long_branch_v4t_arm_thumb_pic)
+
/* non-PIC stubs. */
: ((globals->use_blx)
/* V5T and above. */
{
stub_type = (info->shared | globals->pic_veneer)
/* PIC stubs. */
- ? arm_stub_long_branch_any_any_pic
+ ? arm_stub_long_branch_any_arm_pic
/* non-PIC stubs. */
: arm_stub_long_branch_any_any;
}
bfd_vma sym_value;
int template_size;
int size;
- const bfd_vma *template;
+ const insn_sequence *template;
int i;
struct elf32_arm_link_hash_table * globals;
+ int stub_reloc_idx = -1;
+ int stub_reloc_offset = 0;
/* Massage our args to the form they really have. */
stub_entry = (struct elf32_arm_stub_hash_entry *) gen_entry;
+ stub_entry->target_section->output_offset
+ stub_entry->target_section->output_section->vma);
- switch (stub_entry->stub_type)
- {
- case arm_stub_long_branch_any_any:
- template = elf32_arm_stub_long_branch_any_any;
- template_size = (sizeof (elf32_arm_stub_long_branch_any_any) / sizeof (bfd_vma)) * 4;
- break;
- case arm_stub_long_branch_v4t_arm_thumb:
- template = elf32_arm_stub_long_branch_v4t_arm_thumb;
- template_size = (sizeof (elf32_arm_stub_long_branch_v4t_arm_thumb) / sizeof (bfd_vma)) * 4;
- break;
- case arm_stub_long_branch_thumb_only:
- template = elf32_arm_stub_long_branch_thumb_only;
- template_size = (sizeof (elf32_arm_stub_long_branch_thumb_only) / sizeof (bfd_vma)) * 4;
- break;
- case arm_stub_long_branch_v4t_thumb_arm:
- template = elf32_arm_stub_long_branch_v4t_thumb_arm;
- template_size = (sizeof (elf32_arm_stub_long_branch_v4t_thumb_arm) / sizeof (bfd_vma)) * 4;
- break;
- case arm_stub_short_branch_v4t_thumb_arm:
- template = elf32_arm_stub_short_branch_v4t_thumb_arm;
- template_size = (sizeof(elf32_arm_stub_short_branch_v4t_thumb_arm) / sizeof (bfd_vma)) * 4;
- break;
- case arm_stub_long_branch_any_any_pic:
- template = elf32_arm_stub_long_branch_any_any_pic;
- template_size = (sizeof (elf32_arm_stub_long_branch_any_any_pic) / sizeof (bfd_vma)) * 4;
- break;
- default:
- BFD_FAIL ();
- return FALSE;
- }
+ template = stub_entry->stub_template;
+ template_size = stub_entry->stub_template_size;
size = 0;
- for (i = 0; i < (template_size / 4); i++)
+ for (i = 0; i < template_size; 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);
+ switch (template[i].type)
+ {
+ case THUMB16_TYPE:
+ put_thumb_insn (globals, stub_bfd, template[i].data, loc + size);
+ size += 2;
+ break;
+
+ case ARM_TYPE:
+ put_arm_insn (globals, stub_bfd, template[i].data, loc + size);
+ /* Handle cases where the target is encoded within the
+ instruction. */
+ if (template[i].r_type == R_ARM_JUMP24)
+ {
+ stub_reloc_idx = i;
+ stub_reloc_offset = size;
+ }
+ size += 4;
+ break;
+
+ case DATA_TYPE:
+ bfd_put_32 (stub_bfd, template[i].data, loc + size);
+ stub_reloc_idx = i;
+ stub_reloc_offset = size;
+ size += 4;
+ break;
- size += 4;
+ default:
+ BFD_FAIL ();
+ return FALSE;
+ }
}
+
stub_sec->size += size;
+ /* Stub size has already been computed in arm_size_one_stub. Check
+ consistency. */
+ BFD_ASSERT (size == stub_entry->stub_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_any_any:
- _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_stub_long_branch_v4t_arm_thumb:
- _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_stub_long_branch_thumb_only:
- _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_stub_long_branch_v4t_thumb_arm:
- _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_stub_short_branch_v4t_thumb_arm:
- {
- long int rel_offset;
- static const insn32 t2a3_b_insn = 0xea000000;
+ /* Assume there is one and only one entry to relocate in each stub. */
+ BFD_ASSERT (stub_reloc_idx != -1);
- rel_offset = sym_value - (stub_addr + 8 + 4);
-
- put_arm_insn (globals, stub_bfd,
- (bfd_vma) t2a3_b_insn | ((rel_offset >> 2) & 0x00FFFFFF),
- loc + 4);
- }
- break;
-
- case arm_stub_long_branch_any_any_pic:
- /* 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, -4);
- break;
- default:
- BFD_FAIL();
- break;
- }
+ _bfd_final_link_relocate (elf32_arm_howto_from_type (template[stub_reloc_idx].r_type),
+ stub_bfd, stub_sec, stub_sec->contents,
+ stub_entry->stub_offset + stub_reloc_offset,
+ sym_value, template[stub_reloc_idx].reloc_addend);
return TRUE;
}
{
struct elf32_arm_stub_hash_entry *stub_entry;
struct elf32_arm_link_hash_table *htab;
- const bfd_vma *template;
+ const insn_sequence *template;
int template_size;
int size;
int i;
stub_entry = (struct elf32_arm_stub_hash_entry *) gen_entry;
htab = (struct elf32_arm_link_hash_table *) in_arg;
- switch (stub_entry->stub_type)
+ BFD_ASSERT((stub_entry->stub_type > arm_stub_none)
+ && stub_entry->stub_type < ARRAY_SIZE(stub_definitions));
+
+ template = stub_definitions[stub_entry->stub_type].template;
+ template_size = stub_definitions[stub_entry->stub_type].template_size;
+
+ size = 0;
+ for (i = 0; i < template_size; i++)
{
- case arm_stub_long_branch_any_any:
- template = elf32_arm_stub_long_branch_any_any;
- template_size = (sizeof (elf32_arm_stub_long_branch_any_any) / sizeof (bfd_vma)) * 4;
- break;
- case arm_stub_long_branch_v4t_arm_thumb:
- template = elf32_arm_stub_long_branch_v4t_arm_thumb;
- template_size = (sizeof (elf32_arm_stub_long_branch_v4t_arm_thumb) / sizeof (bfd_vma)) * 4;
- break;
- case arm_stub_long_branch_thumb_only:
- template = elf32_arm_stub_long_branch_thumb_only;
- template_size = (sizeof (elf32_arm_stub_long_branch_thumb_only) / sizeof (bfd_vma)) * 4;
- break;
- case arm_stub_long_branch_v4t_thumb_arm:
- template = elf32_arm_stub_long_branch_v4t_thumb_arm;
- template_size = (sizeof (elf32_arm_stub_long_branch_v4t_thumb_arm) / sizeof (bfd_vma)) * 4;
- break;
- case arm_stub_short_branch_v4t_thumb_arm:
- template = elf32_arm_stub_short_branch_v4t_thumb_arm;
- template_size = (sizeof(elf32_arm_stub_short_branch_v4t_thumb_arm) / sizeof (bfd_vma)) * 4;
- break;
- case arm_stub_long_branch_any_any_pic:
- template = elf32_arm_stub_long_branch_any_any_pic;
- template_size = (sizeof (elf32_arm_stub_long_branch_any_any_pic) / sizeof (bfd_vma)) * 4;
- break;
- default:
- BFD_FAIL ();
- return FALSE;
- break;
+ switch (template[i].type)
+ {
+ case THUMB16_TYPE:
+ size += 2;
+ break;
+
+ case ARM_TYPE:
+ size += 4;
+ break;
+
+ case DATA_TYPE:
+ size += 4;
+ break;
+
+ default:
+ BFD_FAIL ();
+ return FALSE;
+ }
}
- size = 0;
- for (i = 0; i < (template_size / 4); i++)
- size += 4;
+ stub_entry->stub_size = size;
+ stub_entry->stub_template = template;
+ stub_entry->stub_template_size = template_size;
+
size = (size + 7) & ~7;
stub_entry->stub_sec->size += size;
+
return TRUE;
}
/* 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. */
+ which we reverse later. */
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
+ put all .init* and .fini* stubs at the end 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)
+ bfd_boolean stubs_always_after_branch)
{
- asection **list = htab->input_list + htab->top_index;
+ asection **list = htab->input_list;
do
{
asection *tail = *list;
+ asection *head;
if (tail == bfd_abs_section_ptr)
continue;
+ /* Reverse the list: we must avoid placing stubs at the
+ beginning of the section because the beginning of the text
+ section may be required for an interrupt vector in bare metal
+ code. */
+#define NEXT_SEC PREV_SEC
+ head = NULL;
while (tail != NULL)
+ {
+ /* Pop from tail. */
+ asection *item = tail;
+ tail = PREV_SEC (item);
+
+ /* Push on head. */
+ NEXT_SEC (item) = head;
+ head = item;
+ }
+
+ while (head != NULL)
{
asection *curr;
- asection *prev;
- bfd_size_type total;
+ asection *next;
+ bfd_vma stub_group_start = head->output_offset;
+ bfd_vma end_of_next;
- curr = tail;
- total = tail->size;
- while ((prev = PREV_SEC (curr)) != NULL
- && ((total += curr->output_offset - prev->output_offset)
- < stub_group_size))
- curr = prev;
+ curr = head;
+ while (NEXT_SEC (curr) != NULL)
+ {
+ next = NEXT_SEC (curr);
+ end_of_next = next->output_offset + next->size;
+ if (end_of_next - stub_group_start >= stub_group_size)
+ /* End of NEXT is too far from start, so stop. */
+ break;
+ /* Add NEXT to the group. */
+ curr = next;
+ }
- /* OK, the size from the start of CURR to the end is less
+ /* OK, the size from the start to the start of CURR is less
than stub_group_size and thus can be handled by one stub
- section. (Or the tail section is itself larger than
+ section. (Or the head 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);
+ next = NEXT_SEC (head);
/* Set up this stub group. */
- htab->stub_group[tail->id].link_sec = curr;
+ htab->stub_group[head->id].link_sec = curr;
}
- while (tail != curr && (tail = prev) != NULL);
+ while (head != curr && (head = next) != 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)
+ bytes after the stub section can be handled by it too. */
+ if (!stubs_always_after_branch)
{
- total = 0;
- while (prev != NULL
- && ((total += tail->output_offset - prev->output_offset)
- < stub_group_size))
+ stub_group_start = curr->output_offset + curr->size;
+
+ while (next != NULL)
{
- tail = prev;
- prev = PREV_SEC (tail);
- htab->stub_group[tail->id].link_sec = curr;
+ end_of_next = next->output_offset + next->size;
+ if (end_of_next - stub_group_start >= stub_group_size)
+ /* End of NEXT is too far from stubs, so stop. */
+ break;
+ /* Add NEXT to the stub group. */
+ head = next;
+ next = NEXT_SEC (head);
+ htab->stub_group[head->id].link_sec = curr;
}
}
- tail = prev;
+ head = next;
}
}
- while (list-- != htab->input_list);
+ while (list++ != htab->input_list + htab->top_index);
free (htab->input_list);
#undef PREV_SEC
+#undef NEXT_SEC
}
/* Determine and set the size of the stub section for a final link.
void (*layout_sections_again) (void))
{
bfd_size_type stub_group_size;
- bfd_boolean stubs_always_before_branch;
+ bfd_boolean stubs_always_after_branch;
bfd_boolean stub_changed = 0;
struct elf32_arm_link_hash_table *htab = elf32_arm_hash_table (info);
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;
+ stubs_always_after_branch = group_size < 0;
if (group_size < 0)
stub_group_size = -group_size;
else
stub_group_size = 4170000;
}
- group_sections (htab, stub_group_size, stubs_always_before_branch);
+ group_sections (htab, stub_group_size, stubs_always_after_branch);
while (1)
{
goto error_ret_free_local;
}
- /* Only look for stubs on call instructions. */
+ /* Only look for stubs on branch instructions. */
if ((r_type != (unsigned int) R_ARM_CALL)
- && (r_type != (unsigned int) R_ARM_THM_CALL))
+ && (r_type != (unsigned int) R_ARM_THM_CALL)
+ && (r_type != (unsigned int) R_ARM_JUMP24)
+ && (r_type != (unsigned int) R_ARM_THM_JUMP24)
+ && (r_type != (unsigned int) R_ARM_PLT32))
continue;
/* Now determine the call target, its name, value,
+ 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 if ((hash->root.root.type == bfd_link_hash_undefined)
+ || (hash->root.root.type == bfd_link_hash_undefweak))
+ {
+ /* For a shared library, use the PLT stub as
+ target address to decide whether a long
+ branch stub is needed.
+ For absolute code, they cannot be handled. */
+ struct elf32_arm_link_hash_table *globals =
+ elf32_arm_hash_table (info);
+
+ if (globals->splt != NULL && hash != NULL
+ && hash->root.plt.offset != (bfd_vma) -1)
+ {
+ sym_sec = globals->splt;
+ sym_value = hash->root.plt.offset;
+ if (sym_sec->output_section != NULL)
+ destination = (sym_value
+ + sym_sec->output_offset
+ + sym_sec->output_section->vma);
+ }
+ else
+ continue;
+ }
else
{
bfd_set_error (bfd_error_bad_value);
/* 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)
+ if ( ((r_type == (unsigned int) R_ARM_THM_CALL)
+ || (r_type == (unsigned int) R_ARM_THM_JUMP24))
+ && 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
+ else if ( ((r_type == (unsigned int) R_ARM_CALL)
+ || (r_type == (unsigned int) R_ARM_JUMP24))
&& st_type == STT_ARM_TFUNC)
sprintf (stub_entry->output_name, ARM2THUMB_GLUE_ENTRY_NAME,
sym_name);
for (stub_sec = htab->stub_bfd->sections;
stub_sec != NULL;
stub_sec = stub_sec->next)
- stub_sec->size = 0;
+ {
+ /* Ignore non-stub sections. */
+ if (!strstr (stub_sec->name, STUB_SUFFIX))
+ continue;
+
+ stub_sec->size = 0;
+ }
bfd_hash_traverse (&htab->stub_hash_table, arm_size_one_stub, htab);
bfd_byte * contents;
if (size == 0)
- return;
+ {
+ /* Do not include empty glue sections in the output. */
+ if (abfd != NULL)
+ {
+ s = bfd_get_section_by_name (abfd, name);
+ if (s != NULL)
+ s->flags |= SEC_EXCLUDE;
+ }
+ return;
+ }
BFD_ASSERT (abfd != NULL);
return myh;
}
-static void
-record_thumb_to_arm_glue (struct bfd_link_info *link_info,
- struct elf_link_hash_entry *h)
-{
- const char *name = h->root.root.string;
- asection *s;
- char *tmp_name;
- struct elf_link_hash_entry *myh;
- struct bfd_link_hash_entry *bh;
- struct elf32_arm_link_hash_table *hash_table;
- bfd_vma val;
-
- 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, THUMB2ARM_GLUE_SECTION_NAME);
-
- BFD_ASSERT (s != NULL);
-
- tmp_name = bfd_malloc ((bfd_size_type) strlen (name)
- + strlen (THUMB2ARM_GLUE_ENTRY_NAME) + 1);
-
- BFD_ASSERT (tmp_name);
-
- sprintf (tmp_name, THUMB2ARM_GLUE_ENTRY_NAME, name);
-
- myh = elf_link_hash_lookup
- (&(hash_table)->root, tmp_name, FALSE, FALSE, TRUE);
-
- if (myh != NULL)
- {
- /* We've already seen this guy. */
- free (tmp_name);
- return;
- }
-
- /* The only trick here is using hash_table->thumb_glue_size as the value.
- Even though the section isn't allocated yet, this is where we will be
- putting it. The +1 on the value marks that the stub has not been
- output yet - not that it is a Thumb function. */
- bh = NULL;
- val = hash_table->thumb_glue_size + 1;
- _bfd_generic_link_add_one_symbol (link_info, hash_table->bfd_of_glue_owner,
- tmp_name, BSF_GLOBAL, s, val,
- NULL, TRUE, FALSE, &bh);
-
- /* If we mark it 'Thumb', the disassembler will do a better job. */
- myh = (struct elf_link_hash_entry *) bh;
- myh->type = ELF_ST_INFO (STB_LOCAL, STT_ARM_TFUNC);
- myh->forced_local = 1;
-
- free (tmp_name);
-
-#define CHANGE_TO_ARM "__%s_change_to_arm"
-#define BACK_FROM_ARM "__%s_back_from_arm"
-
- /* Allocate another symbol to mark where we switch to Arm mode. */
- tmp_name = bfd_malloc ((bfd_size_type) strlen (name)
- + strlen (CHANGE_TO_ARM) + 1);
-
- BFD_ASSERT (tmp_name);
-
- sprintf (tmp_name, CHANGE_TO_ARM, name);
-
- bh = NULL;
- val = hash_table->thumb_glue_size + 4,
- _bfd_generic_link_add_one_symbol (link_info, hash_table->bfd_of_glue_owner,
- tmp_name, BSF_LOCAL, s, val,
- NULL, TRUE, FALSE, &bh);
-
- free (tmp_name);
-
- s->size += THUMB2ARM_GLUE_SIZE;
- hash_table->thumb_glue_size += THUMB2ARM_GLUE_SIZE;
-}
-
-
/* Allocate space for ARMv4 BX veneers. */
static void
return val;
}
-/* Note: we do not include the flag SEC_LINKER_CREATED, as that
- would prevent elf_link_input_bfd() from processing the contents
- of the section. */
#define ARM_GLUE_SECTION_FLAGS \
- (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_CODE | SEC_READONLY)
+ (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_CODE \
+ | SEC_READONLY | SEC_LINKER_CREATED)
/* Create a fake section for use by the ARM backend of the linker. */
if (info->relocatable)
return TRUE;
- /* Linker stubs don't need glue. */
- if (!strcmp (abfd->filename, "linker stubs"))
- return TRUE;
-
return arm_make_glue_section (abfd, ARM2THUMB_GLUE_SECTION_NAME)
&& arm_make_glue_section (abfd, THUMB2ARM_GLUE_SECTION_NAME)
&& arm_make_glue_section (abfd, VFP11_ERRATUM_VENEER_SECTION_NAME)
/* These are the only relocation types we care about. */
if ( r_type != R_ARM_PC24
- && r_type != R_ARM_PLT32
- && r_type != R_ARM_JUMP24
- && r_type != R_ARM_THM_JUMP24
&& (r_type != R_ARM_V4BX || globals->fix_v4bx < 2))
continue;
switch (r_type)
{
case R_ARM_PC24:
- case R_ARM_PLT32:
- 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
- && !(r_type == R_ARM_CALL && globals->use_blx))
+ if (ELF_ST_TYPE (h->type) == STT_ARM_TFUNC)
record_arm_to_thumb_glue (link_info, h);
break;
- 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
- target, we insert glue. */
- if (ELF_ST_TYPE (h->type) != STT_ARM_TFUNC
- && !(globals->use_blx && r_type == R_ARM_THM_CALL)
- && h->root.type != bfd_link_hash_undefweak)
- record_thumb_to_arm_glue (link_info, h);
- break;
-
default:
abort ();
}
/* Handle relocations which should use the PLT entry. ABS32/REL32
will use the symbol's value, which may point to a PLT entry, but we
don't need to handle that here. If we created a PLT entry, all
- branches in this object should go to it. */
+ branches in this object should go to it, except if the PLT is too
+ far away, in which case a long branch stub should be inserted. */
if ((r_type != R_ARM_ABS32 && r_type != R_ARM_REL32
- && r_type != R_ARM_ABS32_NOI && r_type != R_ARM_REL32_NOI)
+ && r_type != R_ARM_ABS32_NOI && r_type != R_ARM_REL32_NOI
+ && r_type != R_ARM_CALL
+ && r_type != R_ARM_JUMP24
+ && r_type != R_ARM_PLT32)
&& h != NULL
&& splt != NULL
&& h->plt.offset != (bfd_vma) -1)
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)
+ else if (r_type == R_ARM_PC24)
{
/* 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 ( r_type == R_ARM_CALL
+ || r_type == R_ARM_JUMP24
+ || r_type == R_ARM_PLT32)
{
+ /* If the call goes through a PLT entry, make sure to
+ check distance to the right destination address. */
+ if (h != NULL && splt != NULL && h->plt.offset != (bfd_vma) -1)
+ {
+ value = (splt->output_section->vma
+ + splt->output_offset
+ + h->plt.offset);
+ *unresolved_reloc_p = FALSE;
+ }
+
+ from = (input_section->output_section->vma
+ + input_section->output_offset
+ + rel->r_offset);
+ branch_offset = (bfd_signed_vma)(value - from);
+
if (branch_offset > ARM_MAX_FWD_BRANCH_OFFSET
|| branch_offset < ARM_MAX_BWD_BRANCH_OFFSET
- || sym_flags == STT_ARM_TFUNC)
+ || ((sym_flags == STT_ARM_TFUNC)
+ && (((r_type == R_ARM_CALL) && !globals->use_blx)
+ || (r_type == R_ARM_JUMP24)
+ || (r_type == R_ARM_PLT32) ))
+ )
{
/* The target is out of reach, so redirect the
branch to the local stub for this function. */
signed_addend >>= howto->rightshift;
/* A branch to an undefined weak symbol is turned into a jump to
- the next instruction. */
- if (h && h->root.type == bfd_link_hash_undefweak)
+ the next instruction unless a PLT entry will be created. */
+ if (h && h->root.type == bfd_link_hash_undefweak
+ && !(splt != NULL && h->plt.offset != (bfd_vma) -1))
{
value = (bfd_get_32 (input_bfd, hit_data) & 0xf0000000)
| 0x0affffff;
value = (signed_addend & howto->dst_mask)
| (bfd_get_32 (input_bfd, hit_data) & (~ howto->dst_mask));
- /* Set the H bit in the BLX instruction. */
- if (sym_flags == STT_ARM_TFUNC)
- {
- if (addend)
- value |= (1 << 24);
- else
- value &= ~(bfd_vma)(1 << 24);
- }
if (r_type == R_ARM_CALL)
{
+ /* Set the H bit in the BLX instruction. */
+ if (sym_flags == STT_ARM_TFUNC)
+ {
+ if (addend)
+ value |= (1 << 24);
+ else
+ value &= ~(bfd_vma)(1 << 24);
+ }
+
/* Select the correct instruction (BL or BLX). */
/* Only if we are not handling a BL to a stub. In this
case, mode switching is performed by the stub. */
/* Convert BL to BLX. */
lower_insn = (lower_insn & ~0x1000) | 0x0800;
}
- else if (r_type != R_ARM_THM_CALL)
+ else if (( r_type != R_ARM_THM_CALL)
+ && (r_type != R_ARM_THM_JUMP24))
{
if (elf32_thumb_to_arm_stub
(info, sym_name, input_bfd, output_bfd, input_section,
*unresolved_reloc_p = FALSE;
}
- if (r_type == R_ARM_THM_CALL)
+ if (r_type == R_ARM_THM_CALL || r_type == R_ARM_THM_JUMP24)
{
/* Check if a stub has to be inserted because the destination
is too far. */
(thumb2
&& (branch_offset > THM2_MAX_FWD_BRANCH_OFFSET
|| (branch_offset < THM2_MAX_BWD_BRANCH_OFFSET)))
- || ((sym_flags != STT_ARM_TFUNC) && !globals->use_blx))
+ || ((sym_flags != STT_ARM_TFUNC)
+ && (((r_type == R_ARM_THM_CALL) && !globals->use_blx)
+ || r_type == R_ARM_THM_JUMP24)))
{
/* 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_section->vma);
/* If this call becomes a call to Arm, force BLX. */
- if (globals->use_blx)
+ if (globals->use_blx && (r_type == R_ARM_THM_CALL))
{
if ((stub_entry
&& !arm_stub_is_thumb (stub_entry->stub_type))
return TRUE;
}
+/* Add a new unwind edit to the list described by HEAD, TAIL. If INDEX is zero,
+ adds the edit to the start of the list. (The list must be built in order of
+ ascending INDEX: the function's callers are primarily responsible for
+ maintaining that condition). */
+
+static void
+add_unwind_table_edit (arm_unwind_table_edit **head,
+ arm_unwind_table_edit **tail,
+ arm_unwind_edit_type type,
+ asection *linked_section,
+ unsigned int index)
+{
+ arm_unwind_table_edit *new_edit = xmalloc (sizeof (arm_unwind_table_edit));
+
+ new_edit->type = type;
+ new_edit->linked_section = linked_section;
+ new_edit->index = index;
+
+ if (index > 0)
+ {
+ new_edit->next = NULL;
+
+ if (*tail)
+ (*tail)->next = new_edit;
+
+ (*tail) = new_edit;
+
+ if (!*head)
+ (*head) = new_edit;
+ }
+ else
+ {
+ new_edit->next = *head;
+
+ if (!*tail)
+ *tail = new_edit;
+
+ *head = new_edit;
+ }
+}
+
+static _arm_elf_section_data *get_arm_elf_section_data (asection *);
+
+/* Increase the size of EXIDX_SEC by ADJUST bytes. ADJUST mau be negative. */
+static void
+adjust_exidx_size(asection *exidx_sec, int adjust)
+{
+ asection *out_sec;
+
+ if (!exidx_sec->rawsize)
+ exidx_sec->rawsize = exidx_sec->size;
+
+ bfd_set_section_size (exidx_sec->owner, exidx_sec, exidx_sec->size + adjust);
+ out_sec = exidx_sec->output_section;
+ /* Adjust size of output section. */
+ bfd_set_section_size (out_sec->owner, out_sec, out_sec->size +adjust);
+}
+
+/* Insert an EXIDX_CANTUNWIND marker at the end of a section. */
+static void
+insert_cantunwind_after(asection *text_sec, asection *exidx_sec)
+{
+ struct _arm_elf_section_data *exidx_arm_data;
+
+ exidx_arm_data = get_arm_elf_section_data (exidx_sec);
+ add_unwind_table_edit (
+ &exidx_arm_data->u.exidx.unwind_edit_list,
+ &exidx_arm_data->u.exidx.unwind_edit_tail,
+ INSERT_EXIDX_CANTUNWIND_AT_END, text_sec, UINT_MAX);
+
+ adjust_exidx_size(exidx_sec, 8);
+}
+
+/* Scan .ARM.exidx tables, and create a list describing edits which should be
+ made to those tables, such that:
+
+ 1. Regions without unwind data are marked with EXIDX_CANTUNWIND entries.
+ 2. Duplicate entries are merged together (EXIDX_CANTUNWIND, or unwind
+ codes which have been inlined into the index).
+
+ The edits are applied when the tables are written
+ (in elf32_arm_write_section).
+*/
+
+bfd_boolean
+elf32_arm_fix_exidx_coverage (asection **text_section_order,
+ unsigned int num_text_sections,
+ struct bfd_link_info *info)
+{
+ bfd *inp;
+ unsigned int last_second_word = 0, i;
+ asection *last_exidx_sec = NULL;
+ asection *last_text_sec = NULL;
+ int last_unwind_type = -1;
+
+ /* Walk over all EXIDX sections, and create backlinks from the corrsponding
+ text sections. */
+ for (inp = info->input_bfds; inp != NULL; inp = inp->link_next)
+ {
+ asection *sec;
+
+ for (sec = inp->sections; sec != NULL; sec = sec->next)
+ {
+ struct bfd_elf_section_data *elf_sec = elf_section_data (sec);
+ Elf_Internal_Shdr *hdr = &elf_sec->this_hdr;
+
+ if (hdr->sh_type != SHT_ARM_EXIDX)
+ continue;
+
+ if (elf_sec->linked_to)
+ {
+ Elf_Internal_Shdr *linked_hdr
+ = &elf_section_data (elf_sec->linked_to)->this_hdr;
+ struct _arm_elf_section_data *linked_sec_arm_data
+ = get_arm_elf_section_data (linked_hdr->bfd_section);
+
+ if (linked_sec_arm_data == NULL)
+ continue;
+
+ /* Link this .ARM.exidx section back from the text section it
+ describes. */
+ linked_sec_arm_data->u.text.arm_exidx_sec = sec;
+ }
+ }
+ }
+
+ /* Walk all text sections in order of increasing VMA. Eilminate duplicate
+ index table entries (EXIDX_CANTUNWIND and inlined unwind opcodes),
+ and add EXIDX_CANTUNWIND entries for sections with no unwind table data.
+ */
+
+ for (i = 0; i < num_text_sections; i++)
+ {
+ asection *sec = text_section_order[i];
+ asection *exidx_sec;
+ struct _arm_elf_section_data *arm_data = get_arm_elf_section_data (sec);
+ struct _arm_elf_section_data *exidx_arm_data;
+ bfd_byte *contents = NULL;
+ int deleted_exidx_bytes = 0;
+ bfd_vma j;
+ arm_unwind_table_edit *unwind_edit_head = NULL;
+ arm_unwind_table_edit *unwind_edit_tail = NULL;
+ Elf_Internal_Shdr *hdr;
+ bfd *ibfd;
+
+ if (arm_data == NULL)
+ continue;
+
+ exidx_sec = arm_data->u.text.arm_exidx_sec;
+ if (exidx_sec == NULL)
+ {
+ /* Section has no unwind data. */
+ if (last_unwind_type == 0 || !last_exidx_sec)
+ continue;
+
+ /* Ignore zero sized sections. */
+ if (sec->size == 0)
+ continue;
+
+ insert_cantunwind_after(last_text_sec, last_exidx_sec);
+ last_unwind_type = 0;
+ continue;
+ }
+
+ hdr = &elf_section_data (exidx_sec)->this_hdr;
+ if (hdr->sh_type != SHT_ARM_EXIDX)
+ continue;
+
+ exidx_arm_data = get_arm_elf_section_data (exidx_sec);
+ if (exidx_arm_data == NULL)
+ continue;
+
+ ibfd = exidx_sec->owner;
+
+ if (hdr->contents != NULL)
+ contents = hdr->contents;
+ else if (! bfd_malloc_and_get_section (ibfd, exidx_sec, &contents))
+ /* An error? */
+ continue;
+
+ for (j = 0; j < hdr->sh_size; j += 8)
+ {
+ unsigned int second_word = bfd_get_32 (ibfd, contents + j + 4);
+ int unwind_type;
+ int elide = 0;
+
+ /* An EXIDX_CANTUNWIND entry. */
+ if (second_word == 1)
+ {
+ if (last_unwind_type == 0)
+ elide = 1;
+ unwind_type = 0;
+ }
+ /* Inlined unwinding data. Merge if equal to previous. */
+ else if ((second_word & 0x80000000) != 0)
+ {
+ if (last_second_word == second_word && last_unwind_type == 1)
+ elide = 1;
+ unwind_type = 1;
+ last_second_word = second_word;
+ }
+ /* Normal table entry. In theory we could merge these too,
+ but duplicate entries are likely to be much less common. */
+ else
+ unwind_type = 2;
+
+ if (elide)
+ {
+ add_unwind_table_edit (&unwind_edit_head, &unwind_edit_tail,
+ DELETE_EXIDX_ENTRY, NULL, j / 8);
+
+ deleted_exidx_bytes += 8;
+ }
+
+ last_unwind_type = unwind_type;
+ }
+
+ /* Free contents if we allocated it ourselves. */
+ if (contents != hdr->contents)
+ free (contents);
+
+ /* Record edits to be applied later (in elf32_arm_write_section). */
+ exidx_arm_data->u.exidx.unwind_edit_list = unwind_edit_head;
+ exidx_arm_data->u.exidx.unwind_edit_tail = unwind_edit_tail;
+
+ if (deleted_exidx_bytes > 0)
+ adjust_exidx_size(exidx_sec, -deleted_exidx_bytes);
+
+ last_exidx_sec = exidx_sec;
+ last_text_sec = sec;
+ }
+
+ /* Add terminating CANTUNWIND entry. */
+ if (last_exidx_sec && last_unwind_type != 0)
+ insert_cantunwind_after(last_text_sec, last_exidx_sec);
+
+ return TRUE;
+}
+
+static bfd_boolean
+elf32_arm_output_glue_section (struct bfd_link_info *info, bfd *obfd,
+ bfd *ibfd, const char *name)
+{
+ asection *sec, *osec;
+
+ sec = bfd_get_section_by_name (ibfd, name);
+ if (sec == NULL || (sec->flags & SEC_EXCLUDE) != 0)
+ return TRUE;
+
+ osec = sec->output_section;
+ if (elf32_arm_write_section (obfd, info, sec, sec->contents))
+ return TRUE;
+
+ if (! bfd_set_section_contents (obfd, osec, sec->contents,
+ sec->output_offset, sec->size))
+ return FALSE;
+
+ return TRUE;
+}
+
+static bfd_boolean
+elf32_arm_final_link (bfd *abfd, struct bfd_link_info *info)
+{
+ struct elf32_arm_link_hash_table *globals = elf32_arm_hash_table (info);
+
+ /* Invoke the regular ELF backend linker to do all the work. */
+ if (!bfd_elf_final_link (abfd, info))
+ return FALSE;
+
+ /* Write out any glue sections now that we have created all the
+ stubs. */
+ if (globals->bfd_of_glue_owner != NULL)
+ {
+ if (! elf32_arm_output_glue_section (info, abfd,
+ globals->bfd_of_glue_owner,
+ ARM2THUMB_GLUE_SECTION_NAME))
+ return FALSE;
+
+ if (! elf32_arm_output_glue_section (info, abfd,
+ globals->bfd_of_glue_owner,
+ THUMB2ARM_GLUE_SECTION_NAME))
+ return FALSE;
+
+ if (! elf32_arm_output_glue_section (info, abfd,
+ globals->bfd_of_glue_owner,
+ VFP11_ERRATUM_VENEER_SECTION_NAME))
+ return FALSE;
+
+ if (! elf32_arm_output_glue_section (info, abfd,
+ globals->bfd_of_glue_owner,
+ ARM_BX_GLUE_SECTION_NAME))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
/* Set the right machine number. */
static bfd_boolean
if (oldtag >= MAX_TAG_CPU_ARCH || newtag >= MAX_TAG_CPU_ARCH)
{
- _bfd_error_handler (_("ERROR: %B: Unknown CPU architecture"), ibfd);
+ _bfd_error_handler (_("error: %B: Unknown CPU architecture"), ibfd);
return -1;
}
if (result == -1)
{
- _bfd_error_handler (_("ERROR: %B: Conflicting CPU architectures %d/%d"),
+ _bfd_error_handler (_("error: %B: Conflicting CPU architectures %d/%d"),
ibfd, oldtag, newtag);
return -1;
}
int i;
bfd_boolean result = TRUE;
+ /* Skip the linker stubs file. This preserves previous behavior
+ of accepting unknown attributes in the first input file - but
+ is that a bug? */
+ if (ibfd->flags & BFD_LINKER_CREATED)
+ return TRUE;
+
if (!elf_known_obj_attributes_proc (obfd)[0].i)
{
/* This is the first object. Copy the attributes. */
else if (in_attr[Tag_ABI_FP_number_model].i != 0)
{
_bfd_error_handler
- (_("ERROR: %B uses VFP register arguments, %B does not"),
+ (_("error: %B uses VFP register arguments, %B does not"),
ibfd, obfd);
result = FALSE;
}
binaries in the toolchain have had the attributes set
properly.
_bfd_error_handler
- (_("ERROR: %B: 8-byte data alignment conflicts with %B"),
+ (_("error: %B: 8-byte data alignment conflicts with %B"),
obfd, ibfd);
result = FALSE; */
}
else
{
_bfd_error_handler
- (_("ERROR: %B: Conflicting architecture profiles %c/%c"),
+ (_("error: %B: Conflicting architecture profiles %c/%c"),
ibfd,
in_attr[i].i ? in_attr[i].i : '0',
out_attr[i].i ? out_attr[i].i : '0');
&& in_attr[i].i != AEABI_R9_unused)
{
_bfd_error_handler
- (_("ERROR: %B: Conflicting use of R9"), ibfd);
+ (_("error: %B: Conflicting use of R9"), ibfd);
result = FALSE;
}
if (out_attr[i].i == AEABI_R9_unused)
&& out_attr[Tag_ABI_PCS_R9_use].i != AEABI_R9_unused)
{
_bfd_error_handler
- (_("ERROR: %B: SB relative addressing conflicts with use of R9"),
+ (_("error: %B: SB relative addressing conflicts with use of R9"),
ibfd);
result = FALSE;
}
if (in_attr[i].i != out_attr[i].i)
{
_bfd_error_handler
- (_("ERROR: %B uses iWMMXt register arguments, %B does not"),
+ (_("error: %B uses iWMMXt register arguments, %B does not"),
ibfd, obfd);
result = FALSE;
}
if (in_attr[i].i != out_attr[i].i)
{
_bfd_error_handler
- (_("ERROR: fp16 format mismatch between %B and %B"),
+ (_("error: fp16 format mismatch between %B and %B"),
ibfd, obfd);
result = FALSE;
}
&& !(ibfd->flags & DYNAMIC)
&& (in_flags & EF_ARM_BE8))
{
- _bfd_error_handler (_("ERROR: %B is already in final BE8 format"),
+ _bfd_error_handler (_("error: %B is already in final BE8 format"),
ibfd);
return FALSE;
}
EF_ARM_EABI_VERSION (out_flags)))
{
_bfd_error_handler
- (_("ERROR: Source object %B has EABI version %d, but target %B has EABI version %d"),
+ (_("error: Source object %B has EABI version %d, but target %B has EABI version %d"),
ibfd, obfd,
(in_flags & EF_ARM_EABIMASK) >> 24,
(out_flags & EF_ARM_EABIMASK) >> 24);
if ((in_flags & EF_ARM_APCS_26) != (out_flags & EF_ARM_APCS_26))
{
_bfd_error_handler
- (_("ERROR: %B is compiled for APCS-%d, whereas target %B uses APCS-%d"),
+ (_("error: %B is compiled for APCS-%d, whereas target %B uses APCS-%d"),
ibfd, obfd,
in_flags & EF_ARM_APCS_26 ? 26 : 32,
out_flags & EF_ARM_APCS_26 ? 26 : 32);
{
if (in_flags & EF_ARM_APCS_FLOAT)
_bfd_error_handler
- (_("ERROR: %B passes floats in float registers, whereas %B passes them in integer registers"),
+ (_("error: %B passes floats in float registers, whereas %B passes them in integer registers"),
ibfd, obfd);
else
_bfd_error_handler
- (_("ERROR: %B passes floats in integer registers, whereas %B passes them in float registers"),
+ (_("error: %B passes floats in integer registers, whereas %B passes them in float registers"),
ibfd, obfd);
flags_compatible = FALSE;
{
if (in_flags & EF_ARM_VFP_FLOAT)
_bfd_error_handler
- (_("ERROR: %B uses VFP instructions, whereas %B does not"),
+ (_("error: %B uses VFP instructions, whereas %B does not"),
ibfd, obfd);
else
_bfd_error_handler
- (_("ERROR: %B uses FPA instructions, whereas %B does not"),
+ (_("error: %B uses FPA instructions, whereas %B does not"),
ibfd, obfd);
flags_compatible = FALSE;
{
if (in_flags & EF_ARM_MAVERICK_FLOAT)
_bfd_error_handler
- (_("ERROR: %B uses Maverick instructions, whereas %B does not"),
+ (_("error: %B uses Maverick instructions, whereas %B does not"),
ibfd, obfd);
else
_bfd_error_handler
- (_("ERROR: %B does not use Maverick instructions, whereas %B does"),
+ (_("error: %B does not use Maverick instructions, whereas %B does"),
ibfd, obfd);
flags_compatible = FALSE;
{
if (in_flags & EF_ARM_SOFT_FLOAT)
_bfd_error_handler
- (_("ERROR: %B uses software FP, whereas %B uses hardware FP"),
+ (_("error: %B uses software FP, whereas %B uses hardware FP"),
ibfd, obfd);
else
_bfd_error_handler
- (_("ERROR: %B uses hardware FP, whereas %B uses software FP"),
+ (_("error: %B uses hardware FP, whereas %B uses software FP"),
ibfd, obfd);
flags_compatible = FALSE;
bfd_vma *local_got_offsets;
struct elf32_arm_link_hash_table *htab;
bfd_boolean needs_plt;
+ unsigned long nsyms;
if (info->relocatable)
return TRUE;
symtab_hdr = & elf_symtab_hdr (abfd);
sym_hashes = elf_sym_hashes (abfd);
-
+ nsyms = NUM_SHDR_ENTRIES (symtab_hdr);
+
rel_end = relocs + sec->reloc_count;
for (rel = relocs; rel < rel_end; rel++)
{
r_type = ELF32_R_TYPE (rel->r_info);
r_type = arm_real_reloc_type (htab, r_type);
- if (r_symndx >= NUM_SHDR_ENTRIES (symtab_hdr))
+ if (r_symndx >= nsyms
+ /* PR 9934: It is possible to have relocations that do not
+ refer to symbols, thus it is also possible to have an
+ object file containing relocations but no symbol table. */
+ && (r_symndx > 0 || nsyms > 0))
{
(*_bfd_error_handler) (_("%B: bad symbol index: %d"), abfd,
- r_symndx);
+ r_symndx);
return FALSE;
}
- if (r_symndx < symtab_hdr->sh_info)
+ if (nsyms == 0 || r_symndx < symtab_hdr->sh_info)
h = NULL;
else
{
needs_plt = 1;
goto normal_reloc;
+ case R_ARM_MOVW_ABS_NC:
+ case R_ARM_MOVT_ABS:
+ case R_ARM_THM_MOVW_ABS_NC:
+ case R_ARM_THM_MOVT_ABS:
+ if (info->shared)
+ {
+ (*_bfd_error_handler)
+ (_("%B: relocation %s against `%s' can not be used when making a shared object; recompile with -fPIC"),
+ abfd, elf32_arm_howto_table_1[r_type].name,
+ (h) ? h->root.root.string : "a local symbol");
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
+ }
+
+ /* Fall through. */
case R_ARM_ABS32:
case R_ARM_ABS32_NOI:
case R_ARM_REL32:
case R_ARM_REL32_NOI:
- case R_ARM_MOVW_ABS_NC:
- case R_ARM_MOVT_ABS:
case R_ARM_MOVW_PREL_NC:
case R_ARM_MOVT_PREL:
- case R_ARM_THM_MOVW_ABS_NC:
- case R_ARM_THM_MOVT_ABS:
case R_ARM_THM_MOVW_PREL_NC:
case R_ARM_THM_MOVT_PREL:
needs_plt = 0;
return FALSE;
/* BPABI objects never have dynamic relocations mapped. */
- if (! htab->symbian_p)
+ if (htab->symbian_p)
{
flagword flags;
flags = bfd_get_section_flags (dynobj, sreloc);
- flags |= (SEC_LOAD | SEC_ALLOC);
+ flags &= ~(SEC_LOAD | SEC_ALLOC);
bfd_set_section_flags (dynobj, sreloc, flags);
}
}
ibfd->filename);
}
+ /* Allocate space for the glue sections now that we've sized them. */
+ bfd_elf32_arm_allocate_interworking_sections (info);
+
/* The check_relocs and adjust_dynamic_symbol entry points have
determined the sizes of the various dynamic sections. Allocate
memory for them. */
bfd_vma addr;
char *stub_name;
output_arch_syminfo *osi;
+ const insn_sequence *template;
+ enum stub_insn_type prev_type;
+ int size;
+ int i;
+ enum map_symbol_type sym_type;
/* Massage our args to the form they really have. */
stub_entry = (struct elf32_arm_stub_hash_entry *) gen_entry;
addr = (bfd_vma) stub_entry->stub_offset;
stub_name = stub_entry->output_name;
- switch (stub_entry->stub_type)
+ template = stub_entry->stub_template;
+ switch (template[0].type)
{
- case arm_stub_long_branch_any_any:
- 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_stub_long_branch_v4t_arm_thumb:
- 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))
+ case ARM_TYPE:
+ if (!elf32_arm_output_stub_sym (osi, stub_name, addr, stub_entry->stub_size))
return FALSE;
break;
- case arm_stub_long_branch_thumb_only:
- 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_stub_long_branch_v4t_thumb_arm:
- 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 + 4))
- return FALSE;
- if (!elf32_arm_output_map_sym (osi, ARM_MAP_DATA, addr + 8))
- return FALSE;
- break;
- case arm_stub_short_branch_v4t_thumb_arm:
- if (!elf32_arm_output_stub_sym (osi, stub_name, addr | 1, 8))
- 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 + 4))
- return FALSE;
- break;
- case arm_stub_long_branch_any_any_pic:
- 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))
+ case THUMB16_TYPE:
+ if (!elf32_arm_output_stub_sym (osi, stub_name, addr | 1,
+ stub_entry->stub_size))
return FALSE;
break;
default:
BFD_FAIL ();
+ return FALSE;
+ }
+
+ prev_type = DATA_TYPE;
+ size = 0;
+ for (i = 0; i < stub_entry->stub_template_size; i++)
+ {
+ switch (template[i].type)
+ {
+ case ARM_TYPE:
+ sym_type = ARM_MAP_ARM;
+ break;
+
+ case THUMB16_TYPE:
+ sym_type = ARM_MAP_THUMB;
+ break;
+
+ case DATA_TYPE:
+ sym_type = ARM_MAP_DATA;
+ break;
+
+ default:
+ BFD_FAIL ();
+ return FALSE;
+ }
+
+ if (template[i].type != prev_type)
+ {
+ prev_type = template[i].type;
+ if (!elf32_arm_output_map_sym (osi, sym_type, addr + size))
+ return FALSE;
+ }
+
+ switch (template[i].type)
+ {
+ case ARM_TYPE:
+ size += 4;
+ break;
+
+ case THUMB16_TYPE:
+ size += 2;
+ break;
+
+ case DATA_TYPE:
+ size += 4;
+ break;
+
+ default:
+ BFD_FAIL ();
+ return FALSE;
+ }
}
return TRUE;
return 0;
}
+/* Add OFFSET to lower 31 bits of ADDR, leaving other bits unmodified. */
+
+static unsigned long
+offset_prel31 (unsigned long addr, bfd_vma offset)
+{
+ return (addr & ~0x7ffffffful) | ((addr + offset) & 0x7ffffffful);
+}
+
+/* Copy an .ARM.exidx table entry, adding OFFSET to (applied) PREL31
+ relocations. */
+
+static void
+copy_exidx_entry (bfd *output_bfd, bfd_byte *to, bfd_byte *from, bfd_vma offset)
+{
+ unsigned long first_word = bfd_get_32 (output_bfd, from);
+ unsigned long second_word = bfd_get_32 (output_bfd, from + 4);
+
+ /* High bit of first word is supposed to be zero. */
+ if ((first_word & 0x80000000ul) == 0)
+ first_word = offset_prel31 (first_word, offset);
+
+ /* If the high bit of the first word is clear, and the bit pattern is not 0x1
+ (EXIDX_CANTUNWIND), this is an offset to an .ARM.extab entry. */
+ if ((second_word != 0x1) && ((second_word & 0x80000000ul) == 0))
+ second_word = offset_prel31 (second_word, offset);
+
+ bfd_put_32 (output_bfd, first_word, to);
+ bfd_put_32 (output_bfd, second_word, to + 4);
+}
/* Do code byteswapping. Return FALSE afterwards so that the section is
written out as normal. */
}
}
+ if (arm_data->elf.this_hdr.sh_type == SHT_ARM_EXIDX)
+ {
+ arm_unwind_table_edit *edit_node
+ = arm_data->u.exidx.unwind_edit_list;
+ /* Now, sec->size is the size of the section we will write. The original
+ size (before we merged duplicate entries and inserted EXIDX_CANTUNWIND
+ markers) was sec->rawsize. (This isn't the case if we perform no
+ edits, then rawsize will be zero and we should use size). */
+ bfd_byte *edited_contents = bfd_malloc (sec->size);
+ unsigned int input_size = sec->rawsize ? sec->rawsize : sec->size;
+ unsigned int in_index, out_index;
+ bfd_vma add_to_offsets = 0;
+
+ for (in_index = 0, out_index = 0; in_index * 8 < input_size || edit_node;)
+ {
+ if (edit_node)
+ {
+ unsigned int edit_index = edit_node->index;
+
+ if (in_index < edit_index && in_index * 8 < input_size)
+ {
+ copy_exidx_entry (output_bfd, edited_contents + out_index * 8,
+ contents + in_index * 8, add_to_offsets);
+ out_index++;
+ in_index++;
+ }
+ else if (in_index == edit_index
+ || (in_index * 8 >= input_size
+ && edit_index == UINT_MAX))
+ {
+ switch (edit_node->type)
+ {
+ case DELETE_EXIDX_ENTRY:
+ in_index++;
+ add_to_offsets += 8;
+ break;
+
+ case INSERT_EXIDX_CANTUNWIND_AT_END:
+ {
+ asection *text_sec = edit_node->linked_section;
+ bfd_vma text_offset = text_sec->output_section->vma
+ + text_sec->output_offset
+ + text_sec->size;
+ bfd_vma exidx_offset = offset + out_index * 8;
+ unsigned long prel31_offset;
+
+ /* Note: this is meant to be equivalent to an
+ R_ARM_PREL31 relocation. These synthetic
+ EXIDX_CANTUNWIND markers are not relocated by the
+ usual BFD method. */
+ prel31_offset = (text_offset - exidx_offset)
+ & 0x7ffffffful;
+
+ /* First address we can't unwind. */
+ bfd_put_32 (output_bfd, prel31_offset,
+ &edited_contents[out_index * 8]);
+
+ /* Code for EXIDX_CANTUNWIND. */
+ bfd_put_32 (output_bfd, 0x1,
+ &edited_contents[out_index * 8 + 4]);
+
+ out_index++;
+ add_to_offsets -= 8;
+ }
+ break;
+ }
+
+ edit_node = edit_node->next;
+ }
+ }
+ else
+ {
+ /* No more edits, copy remaining entries verbatim. */
+ copy_exidx_entry (output_bfd, edited_contents + out_index * 8,
+ contents + in_index * 8, add_to_offsets);
+ out_index++;
+ in_index++;
+ }
+ }
+
+ if (!(sec->flags & SEC_EXCLUDE) && !(sec->flags & SEC_NEVER_LOAD))
+ bfd_set_section_contents (output_bfd, sec->output_section,
+ edited_contents,
+ (file_ptr) sec->output_offset, sec->size);
+
+ return TRUE;
+ }
+
if (mapcount == 0)
return FALSE;
#define bfd_elf32_bfd_is_target_special_symbol elf32_arm_is_target_special_symbol
#define bfd_elf32_close_and_cleanup elf32_arm_close_and_cleanup
#define bfd_elf32_bfd_free_cached_info elf32_arm_bfd_free_cached_info
+#define bfd_elf32_bfd_final_link elf32_arm_final_link
#define elf_backend_get_symbol_type elf32_arm_get_symbol_type
#define elf_backend_gc_mark_hook elf32_arm_gc_mark_hook