#include "bfd.h"
#include "sysdep.h"
#include "libbfd.h"
#include "coff/arm.h"
#include "coff/internal.h"
#ifdef COFF_WITH_PE
#include "coff/pe.h"
#endif
#include "libcoff.h"
#define APCS_26_FLAG(abfd) \
(coff_data (abfd)->flags & F_APCS_26)
#define APCS_FLOAT_FLAG(abfd) \
(coff_data (abfd)->flags & F_APCS_FLOAT)
#define PIC_FLAG(abfd) \
(coff_data (abfd)->flags & F_PIC)
#define APCS_SET(abfd) \
(coff_data (abfd)->flags & F_APCS_SET)
#define SET_APCS_FLAGS(abfd, flgs) \
do \
{ \
coff_data (abfd)->flags &= ~(F_APCS_26 | F_APCS_FLOAT | F_PIC); \
coff_data (abfd)->flags |= (flgs) | F_APCS_SET; \
} \
while (0)
#define INTERWORK_FLAG(abfd) \
(coff_data (abfd)->flags & F_INTERWORK)
#define INTERWORK_SET(abfd) \
(coff_data (abfd)->flags & F_INTERWORK_SET)
#define SET_INTERWORK_FLAG(abfd, flg) \
do \
{ \
coff_data (abfd)->flags &= ~F_INTERWORK; \
coff_data (abfd)->flags |= (flg) | F_INTERWORK_SET; \
} \
while (0)
#ifndef NUM_ELEM
#define NUM_ELEM(a) ((sizeof (a)) / sizeof ((a)[0]))
#endif
typedef enum {bunknown, b9, b12, b23} thumb_pcrel_branchtype;
typedef unsigned long int insn32;
typedef unsigned short int insn16;
#define THUMB2ARM_GLUE_SECTION_NAME ".glue_7t"
#define THUMB2ARM_GLUE_ENTRY_NAME "__%s_from_thumb"
#define ARM2THUMB_GLUE_SECTION_NAME ".glue_7"
#define ARM2THUMB_GLUE_ENTRY_NAME "__%s_from_arm"
static bfd_reloc_status_type
coff_arm_reloc (bfd *abfd,
arelent *reloc_entry,
asymbol *symbol ATTRIBUTE_UNUSED,
void * data,
asection *input_section ATTRIBUTE_UNUSED,
bfd *output_bfd,
char **error_message ATTRIBUTE_UNUSED)
{
symvalue diff;
if (output_bfd == NULL)
return bfd_reloc_continue;
diff = reloc_entry->addend;
#define DOIT(x) \
x = ((x & ~howto->dst_mask) \
| (((x & howto->src_mask) + diff) & howto->dst_mask))
if (diff != 0)
{
reloc_howto_type *howto = reloc_entry->howto;
unsigned char *addr = (unsigned char *) data + reloc_entry->address;
switch (howto->size)
{
case 0:
{
char x = bfd_get_8 (abfd, addr);
DOIT (x);
bfd_put_8 (abfd, x, addr);
}
break;
case 1:
{
short x = bfd_get_16 (abfd, addr);
DOIT (x);
bfd_put_16 (abfd, (bfd_vma) x, addr);
}
break;
case 2:
{
long x = bfd_get_32 (abfd, addr);
DOIT (x);
bfd_put_32 (abfd, (bfd_vma) x, addr);
}
break;
default:
abort ();
}
}
return bfd_reloc_continue;
}
#ifndef TARGET_UNDERSCORE
#define TARGET_UNDERSCORE '_'
#endif
#ifndef PCRELOFFSET
#define PCRELOFFSET TRUE
#endif
#define ARM_8 0
#define ARM_16 1
#define ARM_32 2
#define ARM_26 3
#define ARM_DISP8 4
#define ARM_DISP16 5
#define ARM_DISP32 6
#define ARM_26D 7
#define ARM_NEG16 9
#define ARM_NEG32 10
#define ARM_RVA32 11
#define ARM_THUMB9 12
#define ARM_THUMB12 13
#define ARM_THUMB23 14
#ifdef ARM_WINCE
#undef ARM_32
#undef ARM_RVA32
#undef ARM_26
#undef ARM_THUMB12
#undef ARM_26D
#define ARM_26D 0
#define ARM_32 1
#define ARM_RVA32 2
#define ARM_26 3
#define ARM_THUMB12 4
#define ARM_SECTION 14
#define ARM_SECREL 15
#endif
static bfd_reloc_status_type aoutarm_fix_pcrel_26_done
(bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
static bfd_reloc_status_type aoutarm_fix_pcrel_26
(bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
static bfd_reloc_status_type coff_thumb_pcrel_12
(bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
#ifndef ARM_WINCE
static bfd_reloc_status_type coff_thumb_pcrel_9
(bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
static bfd_reloc_status_type coff_thumb_pcrel_23
(bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
#endif
static reloc_howto_type aoutarm_std_reloc_howto[] =
{
#ifdef ARM_WINCE
HOWTO (ARM_26D,
2,
2,
24,
TRUE,
0,
complain_overflow_dont,
aoutarm_fix_pcrel_26_done,
"ARM_26D",
FALSE,
0x00ffffff,
0x0,
PCRELOFFSET),
HOWTO (ARM_32,
0,
2,
32,
FALSE,
0,
complain_overflow_bitfield,
coff_arm_reloc,
"ARM_32",
FALSE,
0xffffffff,
0xffffffff,
PCRELOFFSET),
HOWTO (ARM_RVA32,
0,
2,
32,
FALSE,
0,
complain_overflow_bitfield,
coff_arm_reloc,
"ARM_RVA32",
FALSE,
0xffffffff,
0xffffffff,
PCRELOFFSET),
HOWTO (ARM_26,
2,
2,
24,
TRUE,
0,
complain_overflow_signed,
aoutarm_fix_pcrel_26 ,
"ARM_26",
FALSE,
0x00ffffff,
0x00ffffff,
PCRELOFFSET),
HOWTO (ARM_THUMB12,
1,
1,
11,
TRUE,
0,
complain_overflow_signed,
coff_thumb_pcrel_12 ,
"ARM_THUMB12",
FALSE,
0x000007ff,
0x000007ff,
PCRELOFFSET),
EMPTY_HOWTO (-1),
EMPTY_HOWTO (-1),
EMPTY_HOWTO (-1),
EMPTY_HOWTO (-1),
EMPTY_HOWTO (-1),
EMPTY_HOWTO (-1),
EMPTY_HOWTO (-1),
EMPTY_HOWTO (-1),
EMPTY_HOWTO (-1),
HOWTO (ARM_SECTION,
0,
1,
16,
FALSE,
0,
complain_overflow_bitfield,
coff_arm_reloc,
"ARM_SECTION",
FALSE,
0x0000ffff,
0x0000ffff,
PCRELOFFSET),
HOWTO (ARM_SECREL,
0,
2,
32,
FALSE,
0,
complain_overflow_bitfield,
coff_arm_reloc,
"ARM_SECREL",
FALSE,
0xffffffff,
0xffffffff,
PCRELOFFSET),
#else
HOWTO (ARM_8,
0,
0,
8,
FALSE,
0,
complain_overflow_bitfield,
coff_arm_reloc,
"ARM_8",
TRUE,
0x000000ff,
0x000000ff,
PCRELOFFSET),
HOWTO (ARM_16,
0,
1,
16,
FALSE,
0,
complain_overflow_bitfield,
coff_arm_reloc,
"ARM_16",
TRUE,
0x0000ffff,
0x0000ffff,
PCRELOFFSET),
HOWTO (ARM_32,
0,
2,
32,
FALSE,
0,
complain_overflow_bitfield,
coff_arm_reloc,
"ARM_32",
TRUE,
0xffffffff,
0xffffffff,
PCRELOFFSET),
HOWTO (ARM_26,
2,
2,
24,
TRUE,
0,
complain_overflow_signed,
aoutarm_fix_pcrel_26 ,
"ARM_26",
FALSE,
0x00ffffff,
0x00ffffff,
PCRELOFFSET),
HOWTO (ARM_DISP8,
0,
0,
8,
TRUE,
0,
complain_overflow_signed,
coff_arm_reloc,
"ARM_DISP8",
TRUE,
0x000000ff,
0x000000ff,
TRUE),
HOWTO (ARM_DISP16,
0,
1,
16,
TRUE,
0,
complain_overflow_signed,
coff_arm_reloc,
"ARM_DISP16",
TRUE,
0x0000ffff,
0x0000ffff,
TRUE),
HOWTO (ARM_DISP32,
0,
2,
32,
TRUE,
0,
complain_overflow_signed,
coff_arm_reloc,
"ARM_DISP32",
TRUE,
0xffffffff,
0xffffffff,
TRUE),
HOWTO (ARM_26D,
2,
2,
24,
FALSE,
0,
complain_overflow_dont,
aoutarm_fix_pcrel_26_done,
"ARM_26D",
TRUE,
0x00ffffff,
0x0,
FALSE),
EMPTY_HOWTO (-1),
HOWTO (ARM_NEG16,
0,
-1,
16,
FALSE,
0,
complain_overflow_bitfield,
coff_arm_reloc,
"ARM_NEG16",
TRUE,
0x0000ffff,
0x0000ffff,
FALSE),
HOWTO (ARM_NEG32,
0,
-2,
32,
FALSE,
0,
complain_overflow_bitfield,
coff_arm_reloc,
"ARM_NEG32",
TRUE,
0xffffffff,
0xffffffff,
FALSE),
HOWTO (ARM_RVA32,
0,
2,
32,
FALSE,
0,
complain_overflow_bitfield,
coff_arm_reloc,
"ARM_RVA32",
TRUE,
0xffffffff,
0xffffffff,
PCRELOFFSET),
HOWTO (ARM_THUMB9,
1,
1,
8,
TRUE,
0,
complain_overflow_signed,
coff_thumb_pcrel_9 ,
"ARM_THUMB9",
FALSE,
0x000000ff,
0x000000ff,
PCRELOFFSET),
HOWTO (ARM_THUMB12,
1,
1,
11,
TRUE,
0,
complain_overflow_signed,
coff_thumb_pcrel_12 ,
"ARM_THUMB12",
FALSE,
0x000007ff,
0x000007ff,
PCRELOFFSET),
HOWTO (ARM_THUMB23,
1,
2,
22,
TRUE,
0,
complain_overflow_signed,
coff_thumb_pcrel_23 ,
"ARM_THUMB23",
FALSE,
0x07ff07ff,
0x07ff07ff,
PCRELOFFSET)
#endif
};
#define NUM_RELOCS NUM_ELEM (aoutarm_std_reloc_howto)
#ifdef COFF_WITH_PE
static bfd_boolean
in_reloc_p (bfd * abfd ATTRIBUTE_UNUSED,
reloc_howto_type * howto)
{
return !howto->pc_relative && howto->type != ARM_RVA32;
}
#endif
#define RTYPE2HOWTO(cache_ptr, dst) \
(cache_ptr)->howto = \
(dst)->r_type < NUM_RELOCS \
? aoutarm_std_reloc_howto + (dst)->r_type \
: NULL
#define coff_rtype_to_howto coff_arm_rtype_to_howto
static reloc_howto_type *
coff_arm_rtype_to_howto (bfd *abfd ATTRIBUTE_UNUSED,
asection *sec,
struct internal_reloc *rel,
struct coff_link_hash_entry *h ATTRIBUTE_UNUSED,
struct internal_syment *sym ATTRIBUTE_UNUSED,
bfd_vma *addendp)
{
reloc_howto_type * howto;
if (rel->r_type >= NUM_RELOCS)
return NULL;
howto = aoutarm_std_reloc_howto + rel->r_type;
if (rel->r_type == ARM_RVA32)
*addendp -= pe_data (sec->output_section->owner)->pe_opthdr.ImageBase;
return howto;
}
static bfd_reloc_status_type
aoutarm_fix_pcrel_26_done (bfd *abfd ATTRIBUTE_UNUSED,
arelent *reloc_entry ATTRIBUTE_UNUSED,
asymbol *symbol ATTRIBUTE_UNUSED,
void * data ATTRIBUTE_UNUSED,
asection *input_section ATTRIBUTE_UNUSED,
bfd *output_bfd ATTRIBUTE_UNUSED,
char **error_message ATTRIBUTE_UNUSED)
{
return bfd_reloc_ok;
}
static bfd_reloc_status_type
aoutarm_fix_pcrel_26 (bfd *abfd,
arelent *reloc_entry,
asymbol *symbol,
void * data,
asection *input_section,
bfd *output_bfd,
char **error_message ATTRIBUTE_UNUSED)
{
bfd_vma relocation;
bfd_size_type addr = reloc_entry->address;
long target = bfd_get_32 (abfd, (bfd_byte *) data + addr);
bfd_reloc_status_type flag = bfd_reloc_ok;
if (symbol->section == &bfd_und_section
&& (symbol->flags & BSF_WEAK) == 0)
return output_bfd ? bfd_reloc_continue : bfd_reloc_undefined;
if (symbol->section->name != input_section->name
&& output_bfd != (bfd *)NULL)
return bfd_reloc_continue;
relocation = (target & 0x00ffffff) << 2;
relocation = (relocation ^ 0x02000000) - 0x02000000;
relocation += symbol->value;
relocation += symbol->section->output_section->vma;
relocation += symbol->section->output_offset;
relocation += reloc_entry->addend;
relocation -= input_section->output_section->vma;
relocation -= input_section->output_offset;
relocation -= addr;
if (relocation & 3)
return bfd_reloc_overflow;
if (relocation & 0x02000000)
{
if ((relocation & ~ (bfd_vma) 0x03ffffff) != ~ (bfd_vma) 0x03ffffff)
flag = bfd_reloc_overflow;
}
else if (relocation & ~(bfd_vma) 0x03ffffff)
flag = bfd_reloc_overflow;
target &= ~0x00ffffff;
target |= (relocation >> 2) & 0x00ffffff;
bfd_put_32 (abfd, (bfd_vma) target, (bfd_byte *) data + addr);
reloc_entry->howto = &aoutarm_std_reloc_howto[ARM_26D];
return flag;
}
static bfd_reloc_status_type
coff_thumb_pcrel_common (bfd *abfd,
arelent *reloc_entry,
asymbol *symbol,
void * data,
asection *input_section,
bfd *output_bfd,
char **error_message ATTRIBUTE_UNUSED,
thumb_pcrel_branchtype btype)
{
bfd_vma relocation = 0;
bfd_size_type addr = reloc_entry->address;
long target = bfd_get_32 (abfd, (bfd_byte *) data + addr);
bfd_reloc_status_type flag = bfd_reloc_ok;
bfd_vma dstmsk;
bfd_vma offmsk;
bfd_vma signbit;
switch (btype)
{
case b9:
dstmsk = 0x000000ff;
offmsk = 0x000001fe;
signbit = 0x00000100;
break;
case b12:
dstmsk = 0x000007ff;
offmsk = 0x00000ffe;
signbit = 0x00000800;
break;
case b23:
dstmsk = 0x07ff07ff;
offmsk = 0x007fffff;
signbit = 0x00400000;
break;
default:
abort ();
}
if (symbol->section == &bfd_und_section
&& (symbol->flags & BSF_WEAK) == 0)
return output_bfd ? bfd_reloc_continue : bfd_reloc_undefined;
if (symbol->section->name != input_section->name
&& output_bfd != (bfd *)NULL)
return bfd_reloc_continue;
switch (btype)
{
case b9:
case b12:
relocation = ((target & dstmsk) << 1);
break;
case b23:
if (bfd_big_endian (abfd))
relocation = ((target & 0x7ff) << 1) | ((target & 0x07ff0000) >> 4);
else
relocation = ((target & 0x7ff) << 12) | ((target & 0x07ff0000) >> 15);
break;
default:
abort ();
}
relocation = (relocation ^ signbit) - signbit;
relocation += symbol->value;
relocation += symbol->section->output_section->vma;
relocation += symbol->section->output_offset;
relocation += reloc_entry->addend;
relocation -= input_section->output_section->vma;
relocation -= input_section->output_offset;
relocation -= addr;
if (relocation & 1)
return bfd_reloc_overflow;
if (relocation & signbit)
{
if ((relocation & ~offmsk) != ~offmsk)
flag = bfd_reloc_overflow;
}
else if (relocation & ~offmsk)
flag = bfd_reloc_overflow;
target &= ~dstmsk;
switch (btype)
{
case b9:
case b12:
target |= (relocation >> 1);
break;
case b23:
if (bfd_big_endian (abfd))
target |= (((relocation & 0xfff) >> 1)
| ((relocation << 4) & 0x07ff0000));
else
target |= (((relocation & 0xffe) << 15)
| ((relocation >> 12) & 0x7ff));
break;
default:
abort ();
}
bfd_put_32 (abfd, (bfd_vma) target, (bfd_byte *) data + addr);
reloc_entry->howto = & aoutarm_std_reloc_howto [ARM_26D];
return flag;
}
#ifndef ARM_WINCE
static bfd_reloc_status_type
coff_thumb_pcrel_23 (bfd *abfd,
arelent *reloc_entry,
asymbol *symbol,
void * data,
asection *input_section,
bfd *output_bfd,
char **error_message)
{
return coff_thumb_pcrel_common (abfd, reloc_entry, symbol, data,
input_section, output_bfd, error_message,
b23);
}
static bfd_reloc_status_type
coff_thumb_pcrel_9 (bfd *abfd,
arelent *reloc_entry,
asymbol *symbol,
void * data,
asection *input_section,
bfd *output_bfd,
char **error_message)
{
return coff_thumb_pcrel_common (abfd, reloc_entry, symbol, data,
input_section, output_bfd, error_message,
b9);
}
#endif
static bfd_reloc_status_type
coff_thumb_pcrel_12 (bfd *abfd,
arelent *reloc_entry,
asymbol *symbol,
void * data,
asection *input_section,
bfd *output_bfd,
char **error_message)
{
return coff_thumb_pcrel_common (abfd, reloc_entry, symbol, data,
input_section, output_bfd, error_message,
b12);
}
static const struct reloc_howto_struct *
coff_arm_reloc_type_lookup (bfd * abfd, bfd_reloc_code_real_type code)
{
#define ASTD(i,j) case i: return aoutarm_std_reloc_howto + j
if (code == BFD_RELOC_CTOR)
switch (bfd_get_arch_info (abfd)->bits_per_address)
{
case 32:
code = BFD_RELOC_32;
break;
default:
return NULL;
}
switch (code)
{
#ifdef ARM_WINCE
ASTD (BFD_RELOC_32, ARM_32);
ASTD (BFD_RELOC_RVA, ARM_RVA32);
ASTD (BFD_RELOC_ARM_PCREL_BRANCH, ARM_26);
ASTD (BFD_RELOC_THUMB_PCREL_BRANCH12, ARM_THUMB12);
#else
ASTD (BFD_RELOC_8, ARM_8);
ASTD (BFD_RELOC_16, ARM_16);
ASTD (BFD_RELOC_32, ARM_32);
ASTD (BFD_RELOC_ARM_PCREL_BRANCH, ARM_26);
ASTD (BFD_RELOC_ARM_PCREL_BLX, ARM_26);
ASTD (BFD_RELOC_8_PCREL, ARM_DISP8);
ASTD (BFD_RELOC_16_PCREL, ARM_DISP16);
ASTD (BFD_RELOC_32_PCREL, ARM_DISP32);
ASTD (BFD_RELOC_RVA, ARM_RVA32);
ASTD (BFD_RELOC_THUMB_PCREL_BRANCH9, ARM_THUMB9);
ASTD (BFD_RELOC_THUMB_PCREL_BRANCH12, ARM_THUMB12);
ASTD (BFD_RELOC_THUMB_PCREL_BRANCH23, ARM_THUMB23);
ASTD (BFD_RELOC_THUMB_PCREL_BLX, ARM_THUMB23);
#endif
default: return NULL;
}
}
#define COFF_DEFAULT_SECTION_ALIGNMENT_POWER 2
#define COFF_PAGE_SIZE 0x1000
#define SELECT_RELOC(x,howto) { x.r_type = howto->type; }
#define BADMAG(x) ARMBADMAG(x)
#define ARM 1
#ifndef ARM_WINCE
#define SWAP_IN_RELOC_OFFSET H_GET_32
#define SWAP_OUT_RELOC_OFFSET H_PUT_32
#endif
struct coff_arm_link_hash_table
{
struct coff_link_hash_table root;
bfd_size_type thumb_glue_size;
bfd_size_type arm_glue_size;
bfd * bfd_of_glue_owner;
int support_old_code;
};
#define coff_arm_hash_table(info) \
((struct coff_arm_link_hash_table *) ((info)->hash))
static struct bfd_link_hash_table *
coff_arm_link_hash_table_create (bfd * abfd)
{
struct coff_arm_link_hash_table * ret;
bfd_size_type amt = sizeof (struct coff_arm_link_hash_table);
ret = bfd_malloc (amt);
if (ret == NULL)
return NULL;
if (! _bfd_coff_link_hash_table_init
(& ret->root, abfd, _bfd_coff_link_hash_newfunc))
{
free (ret);
return NULL;
}
ret->thumb_glue_size = 0;
ret->arm_glue_size = 0;
ret->bfd_of_glue_owner = NULL;
return & ret->root.root;
}
static void
arm_emit_base_file_entry (struct bfd_link_info *info,
bfd *output_bfd,
asection *input_section,
bfd_vma reloc_offset)
{
bfd_vma addr = reloc_offset
- input_section->vma
+ input_section->output_offset
+ input_section->output_section->vma;
if (coff_data (output_bfd)->pe)
addr -= pe_data (output_bfd)->pe_opthdr.ImageBase;
fwrite (& addr, 1, sizeof (addr), (FILE *) info->base_file);
}
#ifndef ARM_WINCE
#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;
low_bits = rel_off & 0x000007FF;
high_bits = (rel_off >> 11) & 0x000007FF;
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
abort ();
return br_insn;
}
static struct coff_link_hash_entry *
find_thumb_glue (struct bfd_link_info *info,
const char *name,
bfd *input_bfd)
{
char *tmp_name;
struct coff_link_hash_entry *myh;
bfd_size_type amt = strlen (name) + strlen (THUMB2ARM_GLUE_ENTRY_NAME) + 1;
tmp_name = bfd_malloc (amt);
BFD_ASSERT (tmp_name);
sprintf (tmp_name, THUMB2ARM_GLUE_ENTRY_NAME, name);
myh = coff_link_hash_lookup
(coff_hash_table (info), tmp_name, FALSE, FALSE, TRUE);
if (myh == NULL)
_bfd_error_handler (_("%B: unable to find THUMB glue '%s' for `%s'"),
input_bfd, tmp_name, name);
free (tmp_name);
return myh;
}
#endif
static struct coff_link_hash_entry *
find_arm_glue (struct bfd_link_info *info,
const char *name,
bfd *input_bfd)
{
char *tmp_name;
struct coff_link_hash_entry * myh;
bfd_size_type amt = strlen (name) + strlen (ARM2THUMB_GLUE_ENTRY_NAME) + 1;
tmp_name = bfd_malloc (amt);
BFD_ASSERT (tmp_name);
sprintf (tmp_name, ARM2THUMB_GLUE_ENTRY_NAME, name);
myh = coff_link_hash_lookup
(coff_hash_table (info), tmp_name, FALSE, FALSE, TRUE);
if (myh == NULL)
_bfd_error_handler (_("%B: unable to find ARM glue '%s' for `%s'"),
input_bfd, tmp_name, name);
free (tmp_name);
return myh;
}
#define ARM2THUMB_GLUE_SIZE 12
static const insn32 a2t1_ldr_insn = 0xe59fc000;
static const insn32 a2t2_bx_r12_insn = 0xe12fff1c;
static const insn32 a2t3_func_addr_insn = 0x00000001;
#define THUMB2ARM_GLUE_SIZE (globals->support_old_code ? 20 : 8)
#ifndef ARM_WINCE
static const insn16 t2a1_bx_pc_insn = 0x4778;
static const insn16 t2a2_noop_insn = 0x46c0;
static const insn32 t2a3_b_insn = 0xea000000;
static const insn16 t2a1_push_insn = 0xb540;
static const insn16 t2a2_ldr_insn = 0x4e03;
static const insn16 t2a3_mov_insn = 0x46fe;
static const insn16 t2a4_bx_insn = 0x4730;
static const insn32 t2a5_pop_insn = 0xe8bd4040;
static const insn32 t2a6_bx_insn = 0xe12fff1e;
#endif
static bfd_boolean
coff_arm_relocate_section (bfd *output_bfd,
struct bfd_link_info *info,
bfd *input_bfd,
asection *input_section,
bfd_byte *contents,
struct internal_reloc *relocs,
struct internal_syment *syms,
asection **sections)
{
struct internal_reloc * rel;
struct internal_reloc * relend;
#ifndef ARM_WINCE
bfd_vma high_address = bfd_get_section_limit (input_bfd, input_section);
#endif
rel = relocs;
relend = rel + input_section->reloc_count;
for (; rel < relend; rel++)
{
int done = 0;
long symndx;
struct coff_link_hash_entry * h;
struct internal_syment * sym;
bfd_vma addend;
bfd_vma val;
reloc_howto_type * howto;
bfd_reloc_status_type rstat;
bfd_vma h_val;
symndx = rel->r_symndx;
if (symndx == -1)
{
h = NULL;
sym = NULL;
}
else
{
h = obj_coff_sym_hashes (input_bfd)[symndx];
sym = syms + symndx;
}
if (sym != NULL && sym->n_scnum != 0)
addend = - sym->n_value;
else
addend = 0;
howto = coff_rtype_to_howto (input_bfd, input_section, rel, h,
sym, &addend);
if (howto == NULL)
return FALSE;
if (rel->r_type == ARM_26
&& h != NULL
&& info->relocatable
&& (h->root.type == bfd_link_hash_defined
|| h->root.type == bfd_link_hash_defweak)
&& (h->root.u.def.section->output_section
== input_section->output_section))
{
static reloc_howto_type fake_arm26_reloc =
HOWTO (ARM_26,
2,
2,
24,
TRUE,
0,
complain_overflow_signed,
aoutarm_fix_pcrel_26 ,
"ARM_26",
TRUE,
0x00ffffff,
0x00ffffff,
FALSE);
addend -= rel->r_vaddr - input_section->vma;
#ifdef ARM_WINCE
addend -= 8;
#endif
howto = &fake_arm26_reloc;
}
#ifdef ARM_WINCE
#endif
if (howto->pc_relative && howto->pcrel_offset)
{
if (info->relocatable)
continue;
#ifdef ARM_COFF_BUGFIX
if (sym != NULL && sym->n_scnum != 0)
addend += sym->n_value;
#endif
}
val = 0;
if (h == NULL)
{
asection *sec;
if (symndx == -1)
{
sec = bfd_abs_section_ptr;
val = 0;
}
else
{
sec = sections[symndx];
val = (sec->output_section->vma
+ sec->output_offset
+ sym->n_value
- sec->vma);
}
}
else
{
if (! info->relocatable
&& ( h->root.type == bfd_link_hash_defined
|| h->root.type == bfd_link_hash_defweak))
{
asection * h_sec = h->root.u.def.section;
const char * name = h->root.root.string;
h_val = (h->root.u.def.value
+ h_sec->output_section->vma
+ h_sec->output_offset);
if (howto->type == ARM_26)
{
if ( h->class == C_THUMBSTATFUNC
|| h->class == C_THUMBEXTFUNC)
{
unsigned long int tmp;
bfd_vma my_offset;
asection * s;
long int ret_offset;
struct coff_link_hash_entry * myh;
struct coff_arm_link_hash_table * globals;
myh = find_arm_glue (info, name, input_bfd);
if (myh == NULL)
return FALSE;
globals = coff_arm_hash_table (info);
BFD_ASSERT (globals != NULL);
BFD_ASSERT (globals->bfd_of_glue_owner != NULL);
my_offset = myh->root.u.def.value;
s = bfd_get_section_by_name (globals->bfd_of_glue_owner,
ARM2THUMB_GLUE_SECTION_NAME);
BFD_ASSERT (s != NULL);
BFD_ASSERT (s->contents != NULL);
BFD_ASSERT (s->output_section != NULL);
if ((my_offset & 0x01) == 0x01)
{
if (h_sec->owner != NULL
&& INTERWORK_SET (h_sec->owner)
&& ! INTERWORK_FLAG (h_sec->owner))
_bfd_error_handler
(_("%B(%s): warning: interworking not enabled.\n"
" first occurrence: %B: arm call to thumb"),
h_sec->owner, input_bfd, name);
--my_offset;
myh->root.u.def.value = my_offset;
bfd_put_32 (output_bfd, (bfd_vma) a2t1_ldr_insn,
s->contents + my_offset);
bfd_put_32 (output_bfd, (bfd_vma) a2t2_bx_r12_insn,
s->contents + my_offset + 4);
bfd_put_32 (output_bfd, h_val | a2t3_func_addr_insn,
s->contents + my_offset + 8);
if (info->base_file)
arm_emit_base_file_entry (info, output_bfd, s,
my_offset + 8);
}
BFD_ASSERT (my_offset <= globals->arm_glue_size);
tmp = bfd_get_32 (input_bfd, contents + rel->r_vaddr
- input_section->vma);
tmp = tmp & 0xFF000000;
ret_offset =
s->output_offset
+ my_offset
+ s->output_section->vma
- (input_section->output_offset
+ input_section->output_section->vma
+ rel->r_vaddr)
- 8;
tmp = tmp | ((ret_offset >> 2) & 0x00FFFFFF);
bfd_put_32 (output_bfd, (bfd_vma) tmp,
contents + rel->r_vaddr - input_section->vma);
done = 1;
}
}
#ifndef ARM_WINCE
else if (howto->type == ARM_THUMB23)
{
if ( h->class == C_EXT
|| h->class == C_STAT
|| h->class == C_LABEL)
{
asection * s = 0;
bfd_vma my_offset;
unsigned long int tmp;
long int ret_offset;
struct coff_link_hash_entry * myh;
struct coff_arm_link_hash_table * globals;
myh = find_thumb_glue (info, name, input_bfd);
if (myh == NULL)
return FALSE;
globals = coff_arm_hash_table (info);
BFD_ASSERT (globals != NULL);
BFD_ASSERT (globals->bfd_of_glue_owner != NULL);
my_offset = myh->root.u.def.value;
s = bfd_get_section_by_name (globals->bfd_of_glue_owner,
THUMB2ARM_GLUE_SECTION_NAME);
BFD_ASSERT (s != NULL);
BFD_ASSERT (s->contents != NULL);
BFD_ASSERT (s->output_section != NULL);
if ((my_offset & 0x01) == 0x01)
{
if (h_sec->owner != NULL
&& INTERWORK_SET (h_sec->owner)
&& ! INTERWORK_FLAG (h_sec->owner)
&& ! globals->support_old_code)
_bfd_error_handler
(_("%B(%s): warning: interworking not enabled.\n"
" first occurrence: %B: thumb call to arm\n"
" consider relinking with --support-old-code enabled"),
h_sec->owner, input_bfd, name);
-- my_offset;
myh->root.u.def.value = my_offset;
if (globals->support_old_code)
{
bfd_put_16 (output_bfd, (bfd_vma) t2a1_push_insn,
s->contents + my_offset);
bfd_put_16 (output_bfd, (bfd_vma) t2a2_ldr_insn,
s->contents + my_offset + 2);
bfd_put_16 (output_bfd, (bfd_vma) t2a3_mov_insn,
s->contents + my_offset + 4);
bfd_put_16 (output_bfd, (bfd_vma) t2a4_bx_insn,
s->contents + my_offset + 6);
bfd_put_32 (output_bfd, (bfd_vma) t2a5_pop_insn,
s->contents + my_offset + 8);
bfd_put_32 (output_bfd, (bfd_vma) t2a6_bx_insn,
s->contents + my_offset + 12);
bfd_put_32 (output_bfd, h_val,
s->contents + my_offset + 16);
if (info->base_file)
arm_emit_base_file_entry (info, output_bfd, s,
my_offset + 16);
}
else
{
bfd_put_16 (output_bfd, (bfd_vma) t2a1_bx_pc_insn,
s->contents + my_offset);
bfd_put_16 (output_bfd, (bfd_vma) t2a2_noop_insn,
s->contents + my_offset + 2);
ret_offset =
((bfd_signed_vma) h_val)
- ((bfd_signed_vma)
(s->output_offset
+ my_offset
+ s->output_section->vma)
+ 4
+ 8);
bfd_put_32 (output_bfd,
(bfd_vma) t2a3_b_insn | ((ret_offset >> 2) & 0x00FFFFFF),
s->contents + my_offset + 4);
}
}
BFD_ASSERT (my_offset <= globals->thumb_glue_size);
ret_offset =
s->output_offset
+ my_offset
- (input_section->output_offset
+ rel->r_vaddr)
-4;
tmp = bfd_get_32 (input_bfd, contents + rel->r_vaddr
- input_section->vma);
bfd_put_32 (output_bfd,
(bfd_vma) insert_thumb_branch (tmp,
ret_offset),
contents + rel->r_vaddr - input_section->vma);
done = 1;
}
}
#endif
}
if (done)
rstat = bfd_reloc_ok;
else
if ( h->root.type == bfd_link_hash_defined
|| h->root.type == bfd_link_hash_defweak)
{
asection *sec;
sec = h->root.u.def.section;
val = (h->root.u.def.value
+ sec->output_section->vma
+ sec->output_offset);
}
else if (! info->relocatable)
{
if (! ((*info->callbacks->undefined_symbol)
(info, h->root.root.string, input_bfd, input_section,
rel->r_vaddr - input_section->vma, TRUE)))
return FALSE;
}
}
if (info->base_file)
{
if (sym && pe_data(output_bfd)->in_reloc_p(output_bfd, howto))
arm_emit_base_file_entry (info, output_bfd, input_section,
rel->r_vaddr);
}
if (done)
rstat = bfd_reloc_ok;
#ifndef ARM_WINCE
else if (! info->relocatable
&& howto->type == ARM_THUMB23)
{
bfd_vma address = rel->r_vaddr - input_section->vma;
if (address > high_address)
rstat = bfd_reloc_outofrange;
else
{
bfd_vma relocation = val + addend;
int size = bfd_get_reloc_size (howto);
bfd_boolean overflow = FALSE;
bfd_byte *location = contents + address;
bfd_vma x = bfd_get_32 (input_bfd, location);
bfd_vma src_mask = 0x007FFFFE;
bfd_signed_vma reloc_signed_max = (1 << (howto->bitsize - 1)) - 1;
bfd_signed_vma reloc_signed_min = ~reloc_signed_max;
bfd_vma check;
bfd_signed_vma signed_check;
bfd_vma add;
bfd_signed_vma signed_add;
BFD_ASSERT (size == 4);
relocation -= (input_section->output_section->vma
+ input_section->output_offset);
relocation -= address;
check = relocation >> howto->rightshift;
if ((bfd_signed_vma) relocation >= 0)
signed_check = check;
else
signed_check = (check
| ((bfd_vma) - 1
& ~((bfd_vma) - 1 >> howto->rightshift)));
if (bfd_big_endian (input_bfd))
add = (((x) & 0x07ff0000) >> 4) | (((x) & 0x7ff) << 1);
else
add = ((((x) & 0x7ff) << 12) | (((x) & 0x07ff0000) >> 15));
signed_add = add;
if ((add & (((~ src_mask) >> 1) & src_mask)) != 0)
signed_add -= (((~ src_mask) >> 1) & src_mask) << 1;
signed_check += signed_add;
relocation += signed_add;
BFD_ASSERT (howto->complain_on_overflow == complain_overflow_signed);
if ( signed_check > reloc_signed_max
|| signed_check < reloc_signed_min)
overflow = TRUE;
if (bfd_big_endian (input_bfd))
{
if ((x & 0x1800) == 0x0800 && (relocation & 0x02))
relocation += 2;
relocation = (((relocation & 0xffe) >> 1) | ((relocation << 4) & 0x07ff0000));
}
else
{
if ((x & 0x18000000) == 0x08000000 && (relocation & 0x02))
relocation += 2;
relocation = (((relocation & 0xffe) << 15) | ((relocation >> 12) & 0x7ff));
}
x = ((x & ~howto->dst_mask) | relocation);
bfd_put_32 (input_bfd, x, location);
rstat = overflow ? bfd_reloc_overflow : bfd_reloc_ok;
}
}
#endif
else
if (info->relocatable && ! howto->partial_inplace)
rstat = bfd_reloc_ok;
else
rstat = _bfd_final_link_relocate (howto, input_bfd, input_section,
contents,
rel->r_vaddr - input_section->vma,
val, addend);
if (! info->relocatable
&& (rel->r_type == ARM_32 || rel->r_type == ARM_RVA32))
{
int patchit = FALSE;
if (h != NULL
&& ( h->class == C_THUMBSTATFUNC
|| h->class == C_THUMBEXTFUNC))
{
patchit = TRUE;
}
else if (sym != NULL
&& sym->n_scnum > N_UNDEF)
{
if ( sym->n_sclass == C_THUMBSTATFUNC
|| sym->n_sclass == C_THUMBEXTFUNC)
patchit = TRUE;
}
if (patchit)
{
bfd_byte * location = contents + rel->r_vaddr - input_section->vma;
bfd_vma x = bfd_get_32 (input_bfd, location);
bfd_put_32 (input_bfd, x | 1, location);
}
}
switch (rstat)
{
default:
abort ();
case bfd_reloc_ok:
break;
case bfd_reloc_outofrange:
(*_bfd_error_handler)
(_("%B: bad reloc address 0x%lx in section `%A'"),
input_bfd, input_section, (unsigned long) rel->r_vaddr);
return FALSE;
case bfd_reloc_overflow:
{
const char *name;
char buf[SYMNMLEN + 1];
if (symndx == -1)
name = "*ABS*";
else if (h != NULL)
name = NULL;
else
{
name = _bfd_coff_internal_syment_name (input_bfd, sym, buf);
if (name == NULL)
return FALSE;
}
if (! ((*info->callbacks->reloc_overflow)
(info, (h ? &h->root : NULL), name, howto->name,
(bfd_vma) 0, input_bfd, input_section,
rel->r_vaddr - input_section->vma)))
return FALSE;
}
}
}
return TRUE;
}
#ifndef COFF_IMAGE_WITH_PE
bfd_boolean
bfd_arm_allocate_interworking_sections (struct bfd_link_info * info)
{
asection * s;
bfd_byte * foo;
struct coff_arm_link_hash_table * globals;
globals = coff_arm_hash_table (info);
BFD_ASSERT (globals != NULL);
if (globals->arm_glue_size != 0)
{
BFD_ASSERT (globals->bfd_of_glue_owner != NULL);
s = bfd_get_section_by_name
(globals->bfd_of_glue_owner, ARM2THUMB_GLUE_SECTION_NAME);
BFD_ASSERT (s != NULL);
foo = bfd_alloc (globals->bfd_of_glue_owner, globals->arm_glue_size);
s->size = globals->arm_glue_size;
s->contents = foo;
}
if (globals->thumb_glue_size != 0)
{
BFD_ASSERT (globals->bfd_of_glue_owner != NULL);
s = bfd_get_section_by_name
(globals->bfd_of_glue_owner, THUMB2ARM_GLUE_SECTION_NAME);
BFD_ASSERT (s != NULL);
foo = bfd_alloc (globals->bfd_of_glue_owner, globals->thumb_glue_size);
s->size = globals->thumb_glue_size;
s->contents = foo;
}
return TRUE;
}
static void
record_arm_to_thumb_glue (struct bfd_link_info * info,
struct coff_link_hash_entry * h)
{
const char * name = h->root.root.string;
register asection * s;
char * tmp_name;
struct coff_link_hash_entry * myh;
struct bfd_link_hash_entry * bh;
struct coff_arm_link_hash_table * globals;
bfd_vma val;
bfd_size_type amt;
globals = coff_arm_hash_table (info);
BFD_ASSERT (globals != NULL);
BFD_ASSERT (globals->bfd_of_glue_owner != NULL);
s = bfd_get_section_by_name
(globals->bfd_of_glue_owner, ARM2THUMB_GLUE_SECTION_NAME);
BFD_ASSERT (s != NULL);
amt = strlen (name) + strlen (ARM2THUMB_GLUE_ENTRY_NAME) + 1;
tmp_name = bfd_malloc (amt);
BFD_ASSERT (tmp_name);
sprintf (tmp_name, ARM2THUMB_GLUE_ENTRY_NAME, name);
myh = coff_link_hash_lookup
(coff_hash_table (info), tmp_name, FALSE, FALSE, TRUE);
if (myh != NULL)
{
free (tmp_name);
return;
}
bh = NULL;
val = globals->arm_glue_size + 1;
bfd_coff_link_add_one_symbol (info, globals->bfd_of_glue_owner, tmp_name,
BSF_GLOBAL, s, val, NULL, TRUE, FALSE, &bh);
free (tmp_name);
globals->arm_glue_size += ARM2THUMB_GLUE_SIZE;
return;
}
#ifndef ARM_WINCE
static void
record_thumb_to_arm_glue (struct bfd_link_info * info,
struct coff_link_hash_entry * h)
{
const char * name = h->root.root.string;
asection * s;
char * tmp_name;
struct coff_link_hash_entry * myh;
struct bfd_link_hash_entry * bh;
struct coff_arm_link_hash_table * globals;
bfd_vma val;
bfd_size_type amt;
globals = coff_arm_hash_table (info);
BFD_ASSERT (globals != NULL);
BFD_ASSERT (globals->bfd_of_glue_owner != NULL);
s = bfd_get_section_by_name
(globals->bfd_of_glue_owner, THUMB2ARM_GLUE_SECTION_NAME);
BFD_ASSERT (s != NULL);
amt = strlen (name) + strlen (THUMB2ARM_GLUE_ENTRY_NAME) + 1;
tmp_name = bfd_malloc (amt);
BFD_ASSERT (tmp_name);
sprintf (tmp_name, THUMB2ARM_GLUE_ENTRY_NAME, name);
myh = coff_link_hash_lookup
(coff_hash_table (info), tmp_name, FALSE, FALSE, TRUE);
if (myh != NULL)
{
free (tmp_name);
return;
}
bh = NULL;
val = globals->thumb_glue_size + 1;
bfd_coff_link_add_one_symbol (info, globals->bfd_of_glue_owner, tmp_name,
BSF_GLOBAL, s, val, NULL, TRUE, FALSE, &bh);
myh = (struct coff_link_hash_entry *) bh;
myh->class = C_THUMBEXTFUNC;
free (tmp_name);
#define CHANGE_TO_ARM "__%s_change_to_arm"
#define BACK_FROM_ARM "__%s_back_from_arm"
amt = strlen (name) + strlen (CHANGE_TO_ARM) + 1;
tmp_name = bfd_malloc (amt);
BFD_ASSERT (tmp_name);
sprintf (tmp_name, globals->support_old_code ? BACK_FROM_ARM : CHANGE_TO_ARM, name);
bh = NULL;
val = globals->thumb_glue_size + (globals->support_old_code ? 8 : 4);
bfd_coff_link_add_one_symbol (info, globals->bfd_of_glue_owner, tmp_name,
BSF_LOCAL, s, val, NULL, TRUE, FALSE, &bh);
free (tmp_name);
globals->thumb_glue_size += THUMB2ARM_GLUE_SIZE;
return;
}
#endif
bfd_boolean
bfd_arm_get_bfd_for_interworking (bfd * abfd,
struct bfd_link_info * info)
{
struct coff_arm_link_hash_table * globals;
flagword flags;
asection * sec;
if (info->relocatable)
return TRUE;
globals = coff_arm_hash_table (info);
BFD_ASSERT (globals != NULL);
if (globals->bfd_of_glue_owner != NULL)
return TRUE;
sec = bfd_get_section_by_name (abfd, ARM2THUMB_GLUE_SECTION_NAME);
if (sec == NULL)
{
flags = SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_CODE | SEC_READONLY;
sec = bfd_make_section (abfd, ARM2THUMB_GLUE_SECTION_NAME);
if (sec == NULL
|| ! bfd_set_section_flags (abfd, sec, flags)
|| ! bfd_set_section_alignment (abfd, sec, 2))
return FALSE;
}
sec = bfd_get_section_by_name (abfd, THUMB2ARM_GLUE_SECTION_NAME);
if (sec == NULL)
{
flags = SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_CODE | SEC_READONLY;
sec = bfd_make_section (abfd, THUMB2ARM_GLUE_SECTION_NAME);
if (sec == NULL
|| ! bfd_set_section_flags (abfd, sec, flags)
|| ! bfd_set_section_alignment (abfd, sec, 2))
return FALSE;
}
globals->bfd_of_glue_owner = abfd;
return TRUE;
}
bfd_boolean
bfd_arm_process_before_allocation (bfd * abfd,
struct bfd_link_info * info,
int support_old_code)
{
asection * sec;
struct coff_arm_link_hash_table * globals;
if (info->relocatable)
return TRUE;
_bfd_coff_get_external_symbols (abfd);
globals = coff_arm_hash_table (info);
BFD_ASSERT (globals != NULL);
BFD_ASSERT (globals->bfd_of_glue_owner != NULL);
globals->support_old_code = support_old_code;
sec = abfd->sections;
if (sec == NULL)
return TRUE;
for (; sec != NULL; sec = sec->next)
{
struct internal_reloc * i;
struct internal_reloc * rel;
if (sec->reloc_count == 0)
continue;
i = _bfd_coff_read_internal_relocs (abfd, sec, 1, 0, 0, 0);
BFD_ASSERT (i != 0);
for (rel = i; rel < i + sec->reloc_count; ++rel)
{
unsigned short r_type = rel->r_type;
long symndx;
struct coff_link_hash_entry * h;
symndx = rel->r_symndx;
if (symndx == -1)
continue;
if (symndx >= obj_conv_table_size (abfd))
{
_bfd_error_handler (_("%B: illegal symbol index in reloc: %d"),
abfd, symndx);
continue;
}
h = obj_coff_sym_hashes (abfd)[symndx];
if (h == NULL)
continue;
switch (r_type)
{
case ARM_26:
if (h->class == C_THUMBEXTFUNC)
record_arm_to_thumb_glue (info, h);
break;
#ifndef ARM_WINCE
case ARM_THUMB23:
switch (h->class)
{
case C_EXT:
case C_STAT:
case C_LABEL:
record_thumb_to_arm_glue (info, h);
break;
default:
;
}
break;
#endif
default:
break;
}
}
}
return TRUE;
}
#endif
#define coff_bfd_reloc_type_lookup coff_arm_reloc_type_lookup
#define coff_relocate_section coff_arm_relocate_section
#define coff_bfd_is_local_label_name coff_arm_is_local_label_name
#define coff_adjust_symndx coff_arm_adjust_symndx
#define coff_link_output_has_begun coff_arm_link_output_has_begun
#define coff_final_link_postscript coff_arm_final_link_postscript
#define coff_bfd_merge_private_bfd_data coff_arm_merge_private_bfd_data
#define coff_bfd_print_private_bfd_data coff_arm_print_private_bfd_data
#define coff_bfd_set_private_flags _bfd_coff_arm_set_private_flags
#define coff_bfd_copy_private_bfd_data coff_arm_copy_private_bfd_data
#define coff_bfd_link_hash_table_create coff_arm_link_hash_table_create
static bfd_boolean
coff_arm_adjust_symndx (bfd *obfd ATTRIBUTE_UNUSED,
struct bfd_link_info *info ATTRIBUTE_UNUSED,
bfd *ibfd,
asection *sec,
struct internal_reloc *irel,
bfd_boolean *adjustedp)
{
if (irel->r_type == ARM_26)
{
struct coff_link_hash_entry *h;
h = obj_coff_sym_hashes (ibfd)[irel->r_symndx];
if (h != NULL
&& (h->root.type == bfd_link_hash_defined
|| h->root.type == bfd_link_hash_defweak)
&& h->root.u.def.section->output_section == sec->output_section)
irel->r_type = ARM_26D;
}
*adjustedp = FALSE;
return TRUE;
}
static bfd_boolean
coff_arm_merge_private_bfd_data (bfd * ibfd, bfd * obfd)
{
BFD_ASSERT (ibfd != NULL && obfd != NULL);
if (ibfd == obfd)
return TRUE;
if ( ibfd->xvec->flavour != bfd_target_coff_flavour
|| obfd->xvec->flavour != bfd_target_coff_flavour)
return TRUE;
if (! bfd_arm_merge_machines (ibfd, obfd))
return FALSE;
if (APCS_SET (ibfd))
{
if (APCS_SET (obfd))
{
if (APCS_26_FLAG (obfd) != APCS_26_FLAG (ibfd))
{
_bfd_error_handler
(_("ERROR: %B is compiled for APCS-%d, whereas %B is compiled for APCS-%d"),
ibfd, obfd,
APCS_26_FLAG (ibfd) ? 26 : 32,
APCS_26_FLAG (obfd) ? 26 : 32
);
bfd_set_error (bfd_error_wrong_format);
return FALSE;
}
if (APCS_FLOAT_FLAG (obfd) != APCS_FLOAT_FLAG (ibfd))
{
const char *msg;
if (APCS_FLOAT_FLAG (ibfd))
msg = _("ERROR: %B passes floats in float registers, whereas %B passes them in integer registers");
else
msg = _("ERROR: %B passes floats in integer registers, whereas %B passes them in float registers");
_bfd_error_handler (msg, ibfd, obfd);
bfd_set_error (bfd_error_wrong_format);
return FALSE;
}
if (PIC_FLAG (obfd) != PIC_FLAG (ibfd))
{
const char * msg;
if (PIC_FLAG (ibfd))
msg = _("ERROR: %B is compiled as position independent code, whereas target %B is absolute position");
else
msg = _("ERROR: %B is compiled as absolute position code, whereas target %B is position independent");
_bfd_error_handler (msg, ibfd, obfd);
bfd_set_error (bfd_error_wrong_format);
return FALSE;
}
}
else
{
SET_APCS_FLAGS (obfd, APCS_26_FLAG (ibfd) | APCS_FLOAT_FLAG (ibfd) | PIC_FLAG (ibfd));
bfd_set_arch_mach (obfd, bfd_get_arch (ibfd), bfd_get_mach (ibfd));
}
}
if (INTERWORK_SET (ibfd))
{
if (INTERWORK_SET (obfd))
{
if (INTERWORK_FLAG (obfd) != INTERWORK_FLAG (ibfd))
{
const char * msg;
if (INTERWORK_FLAG (ibfd))
msg = _("Warning: %B supports interworking, whereas %B does not");
else
msg = _("Warning: %B does not support interworking, whereas %B does");
_bfd_error_handler (msg, ibfd, obfd);
}
}
else
{
SET_INTERWORK_FLAG (obfd, INTERWORK_FLAG (ibfd));
}
}
return TRUE;
}
static bfd_boolean
coff_arm_print_private_bfd_data (bfd * abfd, void * ptr)
{
FILE * file = (FILE *) ptr;
BFD_ASSERT (abfd != NULL && ptr != NULL);
fprintf (file, _("private flags = %x:"), coff_data (abfd)->flags);
if (APCS_SET (abfd))
{
fprintf (file, " [APCS-%d]", APCS_26_FLAG (abfd) ? 26 : 32);
if (APCS_FLOAT_FLAG (abfd))
fprintf (file, _(" [floats passed in float registers]"));
else
fprintf (file, _(" [floats passed in integer registers]"));
if (PIC_FLAG (abfd))
fprintf (file, _(" [position independent]"));
else
fprintf (file, _(" [absolute position]"));
}
if (! INTERWORK_SET (abfd))
fprintf (file, _(" [interworking flag not initialised]"));
else if (INTERWORK_FLAG (abfd))
fprintf (file, _(" [interworking supported]"));
else
fprintf (file, _(" [interworking not supported]"));
fputc ('\n', file);
return TRUE;
}
static bfd_boolean
_bfd_coff_arm_set_private_flags (bfd * abfd, flagword flags)
{
flagword flag;
BFD_ASSERT (abfd != NULL);
flag = (flags & F_APCS26) ? F_APCS_26 : 0;
if (APCS_SET (abfd)
&& ( (APCS_26_FLAG (abfd) != flag)
|| (APCS_FLOAT_FLAG (abfd) != (flags & F_APCS_FLOAT))
|| (PIC_FLAG (abfd) != (flags & F_PIC))
))
return FALSE;
flag |= (flags & (F_APCS_FLOAT | F_PIC));
SET_APCS_FLAGS (abfd, flag);
flag = (flags & F_INTERWORK);
if (INTERWORK_SET (abfd) && (INTERWORK_FLAG (abfd) != flag))
{
if (flag)
_bfd_error_handler (_("Warning: Not setting interworking flag of %B since it has already been specified as non-interworking"),
abfd);
else
_bfd_error_handler (_("Warning: Clearing the interworking flag of %B due to outside request"),
abfd);
flag = 0;
}
SET_INTERWORK_FLAG (abfd, flag);
return TRUE;
}
static bfd_boolean
coff_arm_copy_private_bfd_data (bfd * src, bfd * dest)
{
BFD_ASSERT (src != NULL && dest != NULL);
if (src == dest)
return TRUE;
if (src->xvec != dest->xvec)
return TRUE;
if (APCS_SET (src))
{
if (APCS_SET (dest))
{
if (APCS_26_FLAG (dest) != APCS_26_FLAG (src))
return FALSE;
if (APCS_FLOAT_FLAG (dest) != APCS_FLOAT_FLAG (src))
return FALSE;
if (PIC_FLAG (dest) != PIC_FLAG (src))
return FALSE;
}
else
SET_APCS_FLAGS (dest, APCS_26_FLAG (src) | APCS_FLOAT_FLAG (src)
| PIC_FLAG (src));
}
if (INTERWORK_SET (src))
{
if (INTERWORK_SET (dest))
{
if (INTERWORK_FLAG (dest) != INTERWORK_FLAG (src))
{
if (INTERWORK_FLAG (dest))
{
_bfd_error_handler (("\
Warning: Clearing the interworking flag of %B because non-interworking code in %B has been linked with it"),
dest, src);
}
SET_INTERWORK_FLAG (dest, 0);
}
}
else
{
SET_INTERWORK_FLAG (dest, INTERWORK_FLAG (src));
}
}
return TRUE;
}
#define LOCAL_LABEL_PREFIX ""
#ifndef USER_LABEL_PREFIX
#define USER_LABEL_PREFIX "_"
#endif
static bfd_boolean
coff_arm_is_local_label_name (bfd * abfd ATTRIBUTE_UNUSED,
const char * name)
{
#ifdef USER_LABEL_PREFIX
if (USER_LABEL_PREFIX[0] != 0)
{
size_t len = strlen (USER_LABEL_PREFIX);
if (strncmp (name, USER_LABEL_PREFIX, len) == 0)
return FALSE;
}
#endif
#ifdef LOCAL_LABEL_PREFIX
if (LOCAL_LABEL_PREFIX[0] != 0)
{
size_t len = strlen (LOCAL_LABEL_PREFIX);
if (strncmp (name, LOCAL_LABEL_PREFIX, len) != 0)
return FALSE;
name += len;
}
#endif
return name[0] == 'L';
}
static bfd_boolean
coff_arm_link_output_has_begun (bfd * sub, struct coff_final_link_info * info)
{
return (sub->output_has_begun
|| sub == coff_arm_hash_table (info->info)->bfd_of_glue_owner);
}
static bfd_boolean
coff_arm_final_link_postscript (bfd * abfd ATTRIBUTE_UNUSED,
struct coff_final_link_info * pfinfo)
{
struct coff_arm_link_hash_table * globals;
globals = coff_arm_hash_table (pfinfo->info);
BFD_ASSERT (globals != NULL);
if (globals->bfd_of_glue_owner != NULL)
{
if (! _bfd_coff_link_input_bfd (pfinfo, globals->bfd_of_glue_owner))
return FALSE;
globals->bfd_of_glue_owner->output_has_begun = TRUE;
}
return bfd_arm_update_notes (abfd, ARM_NOTE_SECTION);
}
#include "coffcode.h"
#ifndef TARGET_LITTLE_SYM
#define TARGET_LITTLE_SYM armcoff_little_vec
#endif
#ifndef TARGET_LITTLE_NAME
#define TARGET_LITTLE_NAME "coff-arm-little"
#endif
#ifndef TARGET_BIG_SYM
#define TARGET_BIG_SYM armcoff_big_vec
#endif
#ifndef TARGET_BIG_NAME
#define TARGET_BIG_NAME "coff-arm-big"
#endif
#ifndef TARGET_UNDERSCORE
#define TARGET_UNDERSCORE 0
#endif
#ifndef EXTRA_S_FLAGS
#ifdef COFF_WITH_PE
#define EXTRA_S_FLAGS (SEC_CODE | SEC_LINK_ONCE | SEC_LINK_DUPLICATES)
#else
#define EXTRA_S_FLAGS SEC_CODE
#endif
#endif
extern const bfd_target TARGET_BIG_SYM ;
CREATE_LITTLE_COFF_TARGET_VEC (TARGET_LITTLE_SYM, TARGET_LITTLE_NAME, D_PAGED, EXTRA_S_FLAGS, TARGET_UNDERSCORE, & TARGET_BIG_SYM, COFF_SWAP_TABLE)
CREATE_BIG_COFF_TARGET_VEC (TARGET_BIG_SYM, TARGET_BIG_NAME, D_PAGED, EXTRA_S_FLAGS, TARGET_UNDERSCORE, & TARGET_LITTLE_SYM, COFF_SWAP_TABLE)