#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;
static bfd_boolean coff_arm_relocate_section
PARAMS ((bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *,
struct internal_reloc *, struct internal_syment *, asection **));
static bfd_reloc_status_type aoutarm_fix_pcrel_26_done
PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **));
static bfd_reloc_status_type aoutarm_fix_pcrel_26
PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **));
#ifndef ARM_WINCE
static bfd_reloc_status_type coff_thumb_pcrel_23
PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **));
static bfd_reloc_status_type coff_thumb_pcrel_9
PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **));
static insn32 insert_thumb_branch
PARAMS ((insn32, int));
#endif
static bfd_reloc_status_type coff_thumb_pcrel_12
PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **));
static bfd_reloc_status_type coff_arm_reloc
PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **));
static bfd_boolean coff_arm_adjust_symndx
PARAMS ((bfd *, struct bfd_link_info *, bfd *,
asection *, struct internal_reloc *, bfd_boolean *));
static reloc_howto_type * coff_arm_rtype_to_howto
PARAMS ((bfd *, asection *, struct internal_reloc *,
struct coff_link_hash_entry *, struct internal_syment *,
bfd_vma *));
static bfd_reloc_status_type coff_thumb_pcrel_common
PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **,
thumb_pcrel_branchtype));
static const struct reloc_howto_struct * coff_arm_reloc_type_lookup
PARAMS ((bfd *, bfd_reloc_code_real_type));
static struct bfd_link_hash_table * coff_arm_link_hash_table_create
PARAMS ((bfd *));
static struct coff_link_hash_entry * find_thumb_glue
PARAMS ((struct bfd_link_info *, const char *, bfd *));
static struct coff_link_hash_entry * find_arm_glue
PARAMS ((struct bfd_link_info *, const char *, bfd *));
#ifndef COFF_IMAGE_WITH_PE
static void record_arm_to_thumb_glue
PARAMS ((struct bfd_link_info *, struct coff_link_hash_entry *));
#ifndef ARM_WINCE
static void record_thumb_to_arm_glue
PARAMS ((struct bfd_link_info *, struct coff_link_hash_entry *));
#endif
#endif
static bfd_boolean coff_arm_merge_private_bfd_data
PARAMS ((bfd *, bfd *));
static bfd_boolean coff_arm_print_private_bfd_data
PARAMS ((bfd *, PTR));
static bfd_boolean _bfd_coff_arm_set_private_flags
PARAMS ((bfd *, flagword));
static bfd_boolean coff_arm_copy_private_bfd_data
PARAMS ((bfd *, bfd *));
static bfd_boolean coff_arm_is_local_label_name
PARAMS ((bfd *, const char *));
static bfd_boolean coff_arm_link_output_has_begun
PARAMS ((bfd *, struct coff_final_link_info *));
static bfd_boolean coff_arm_final_link_postscript
PARAMS ((bfd *, struct coff_final_link_info *));
static void arm_emit_base_file_entry
PARAMS ((struct bfd_link_info *, bfd *, asection *, bfd_vma));
#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 (abfd, reloc_entry, symbol, data, input_section, output_bfd,
error_message)
bfd *abfd;
arelent *reloc_entry;
asymbol *symbol ATTRIBUTE_UNUSED;
PTR data;
asection *input_section ATTRIBUTE_UNUSED;
bfd *output_bfd;
char **error_message ATTRIBUTE_UNUSED;
{
symvalue diff;
if (output_bfd == (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 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 PARAMS ((bfd *, reloc_howto_type *));
static bfd_boolean
in_reloc_p (abfd, howto)
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 (abfd, sec, rel, h, sym, addendp)
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 (abfd, reloc_entry, symbol, data, input_section,
output_bfd, error_message)
bfd *abfd ATTRIBUTE_UNUSED;
arelent *reloc_entry ATTRIBUTE_UNUSED;
asymbol *symbol ATTRIBUTE_UNUSED;
PTR 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 (abfd, reloc_entry, symbol, data, input_section,
output_bfd, error_message)
bfd *abfd;
arelent *reloc_entry;
asymbol *symbol;
PTR 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 (abfd, reloc_entry, symbol, data, input_section,
output_bfd, error_message, btype)
bfd *abfd;
arelent *reloc_entry;
asymbol *symbol;
PTR 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 (abfd, reloc_entry, symbol, data, input_section,
output_bfd, error_message)
bfd *abfd;
arelent *reloc_entry;
asymbol *symbol;
PTR 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 (abfd, reloc_entry, symbol, data, input_section,
output_bfd, error_message)
bfd *abfd;
arelent *reloc_entry;
asymbol *symbol;
PTR 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 (abfd, reloc_entry, symbol, data, input_section,
output_bfd, error_message)
bfd *abfd;
arelent *reloc_entry;
asymbol *symbol;
PTR 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 (abfd, code)
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 (const struct reloc_howto_struct *) 0;
}
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 (const struct reloc_howto_struct *) 0;
}
}
#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 (abfd)
bfd * abfd;
{
struct coff_arm_link_hash_table * ret;
bfd_size_type amt = sizeof (struct coff_arm_link_hash_table);
ret = (struct coff_arm_link_hash_table *) bfd_malloc (amt);
if (ret == (struct coff_arm_link_hash_table *) NULL)
return NULL;
if (! _bfd_coff_link_hash_table_init
(& ret->root, abfd, _bfd_coff_link_hash_newfunc))
{
free (ret);
return (struct bfd_link_hash_table *) 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 (info, output_bfd, input_section, reloc_offset)
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 (br_insn, rel_off)
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 (info, name, input_bfd)
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 = (char *) 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 (_("%s: unable to find THUMB glue '%s' for `%s'"),
bfd_archive_filename (input_bfd), tmp_name, name);
free (tmp_name);
return myh;
}
#endif
static struct coff_link_hash_entry *
find_arm_glue (info, name, input_bfd)
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 = (char *) 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 (_("%s: unable to find ARM glue '%s' for `%s'"),
bfd_archive_filename (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)
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;
static bfd_boolean
coff_arm_relocate_section (output_bfd, info, input_bfd, input_section,
contents, relocs, syms, sections)
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;
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
#if 0
if (howto->pc_relative && !info->relocatable)
addend -= 8;
#endif
#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 1
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
(_("%s(%s): warning: interworking not enabled."),
bfd_archive_filename (h_sec->owner), name);
_bfd_error_handler
(_(" first occurrence: %s: arm call to thumb"),
bfd_archive_filename (input_bfd));
}
--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
(_("%s(%s): warning: interworking not enabled."),
bfd_archive_filename (h_sec->owner), name);
_bfd_error_handler
(_(" first occurrence: %s: thumb call to arm"),
bfd_archive_filename (input_bfd));
_bfd_error_handler
(_(" consider relinking with --support-old-code enabled"));
}
-- 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
#endif
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 1
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 > input_section->_raw_size)
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
#endif
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 1
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);
}
}
#endif
switch (rstat)
{
default:
abort ();
case bfd_reloc_ok:
break;
case bfd_reloc_outofrange:
(*_bfd_error_handler)
(_("%s: bad reloc address 0x%lx in section `%s'"),
bfd_archive_filename (input_bfd),
(unsigned long) rel->r_vaddr,
bfd_get_section_name (input_bfd, input_section));
return FALSE;
case bfd_reloc_overflow:
{
const char *name;
char buf[SYMNMLEN + 1];
if (symndx == -1)
name = "*ABS*";
else if (h != NULL)
name = h->root.root.string;
else
{
name = _bfd_coff_internal_syment_name (input_bfd, sym, buf);
if (name == NULL)
return FALSE;
}
if (! ((*info->callbacks->reloc_overflow)
(info, 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 (info)
struct bfd_link_info * info;
{
asection * s;
bfd_byte * foo;
struct coff_arm_link_hash_table * globals;
#if 0
static char test_char = '1';
#endif
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_byte *) bfd_alloc (globals->bfd_of_glue_owner,
globals->arm_glue_size);
#if 0
memset (foo, test_char, (size_t) globals->arm_glue_size);
#endif
s->_raw_size = s->_cooked_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_byte *) bfd_alloc (globals->bfd_of_glue_owner,
globals->thumb_glue_size);
#if 0
memset (foo, test_char, (size_t) globals->thumb_glue_size);
#endif
s->_raw_size = s->_cooked_size = globals->thumb_glue_size;
s->contents = foo;
}
return TRUE;
}
static void
record_arm_to_thumb_glue (info, h)
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 = (char *) 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 (info, h)
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, THUMB2ARM_GLUE_SECTION_NAME);
BFD_ASSERT (s != NULL);
amt = strlen (name) + strlen (THUMB2ARM_GLUE_ENTRY_NAME) + 1;
tmp_name = (char *) 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 = (char *) 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 (abfd, info)
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 (abfd, info, support_old_code)
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 (_("%s: illegal symbol index in reloc: %d"),
bfd_archive_filename (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 (obfd, info, ibfd, sec, irel, adjustedp)
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 (ibfd, obfd)
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: %s is compiled for APCS-%d, whereas %s is compiled for APCS-%d"),
bfd_archive_filename (ibfd), APCS_26_FLAG (ibfd) ? 26 : 32,
bfd_get_filename (obfd), 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: %s passes floats in float registers, whereas %s passes them in integer registers");
else
msg = _("ERROR: %s passes floats in integer registers, whereas %s passes them in float registers");
_bfd_error_handler (msg, bfd_archive_filename (ibfd),
bfd_get_filename (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: %s is compiled as position independent code, whereas target %s is absolute position");
else
msg = _("ERROR: %s is compiled as absolute position code, whereas target %s is position independent");
_bfd_error_handler (msg, bfd_archive_filename (ibfd),
bfd_get_filename (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: %s supports interworking, whereas %s does not");
else
msg = _("Warning: %s does not support interworking, whereas %s does");
_bfd_error_handler (msg, bfd_archive_filename (ibfd),
bfd_get_filename (obfd));
}
}
else
{
SET_INTERWORK_FLAG (obfd, INTERWORK_FLAG (ibfd));
}
}
return TRUE;
}
static bfd_boolean
coff_arm_print_private_bfd_data (abfd, ptr)
bfd * abfd;
PTR 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 (abfd, 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 %s since it has already been specified as non-interworking"),
bfd_archive_filename (abfd));
else
_bfd_error_handler (_("Warning: Clearing the interworking flag of %s due to outside request"),
bfd_archive_filename (abfd));
flag = 0;
}
SET_INTERWORK_FLAG (abfd, flag);
return TRUE;
}
static bfd_boolean
coff_arm_copy_private_bfd_data (src, dest)
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 %s because non-interworking code in %s has been linked with it"),
bfd_get_filename (dest),
bfd_archive_filename (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 (abfd, name)
bfd * abfd ATTRIBUTE_UNUSED;
const char * name;
{
#ifdef USER_LABEL_PREFIX
if (USER_LABEL_PREFIX[0] != 0)
{
if (strncmp (name, USER_LABEL_PREFIX, strlen (USER_LABEL_PREFIX)) == 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 (sub, info)
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 (abfd, pfinfo)
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)