/* The following bitmasks control CPU extensions (ARM7 onwards): */
#define ARM_LONGMUL 0x00000010 /* allow long multiplies */
-#define ARM_ARCH4 0x00000020
-#define ARM_THUMB ARM_ARCH4
+#define ARM_HALFWORD 0x00000020 /* allow half word loads */
+#define ARM_THUMB 0x00000040 /* allow BX instruction */
+
+#define ARM_ARCHv4 (ARM_7 | ARM_LONGMUL | ARM_HALFWORD)
/* Some useful combinations: */
#define ARM_ANY 0x00ffffff
#ifndef CPU_DEFAULT
#if defined __thumb__
-#define CPU_DEFAULT (ARM_7 | ARM_THUMB)
+#define CPU_DEFAULT (ARM_ARCHv4 | ARM_THUMB)
#else
#define CPU_DEFAULT ARM_ALL
#endif
#ifdef OBJ_COFF
/* Flags stored in private area of BFD COFF structure */
static boolean uses_apcs_26 = false;
+static boolean support_interwork = false;
+static boolean uses_apcs_float = false;
+static boolean pic_code = false;
#endif
/* This array holds the chars that always start a comment. If the
static void symbol_locate PARAMS ((symbolS *, CONST char *, segT,
valueT, fragS *));
static int add_to_lit_pool PARAMS ((void));
-static int validate_immediate PARAMS ((int));
+static unsigned validate_immediate PARAMS ((unsigned));
static int validate_offset_imm PARAMS ((int, int));
static void opcode_select PARAMS ((int));
static void end_of_line PARAMS ((char *));
static void s_arm PARAMS ((int));
static void s_thumb PARAMS ((int));
static void s_code PARAMS ((int));
+static void s_force_thumb PARAMS ((int));
+static void s_thumb_func PARAMS ((int));
static int my_get_expression PARAMS ((expressionS *, char **));
{"arm", s_arm, 0},
{"thumb", s_thumb, 0},
{"code", s_code, 0},
+ {"force_thumb", s_force_thumb, 0},
+ {"thumb_func", s_thumb_func, 0},
{"even", s_even, 0},
{"ltorg", s_ltorg, 0},
{"pool", s_ltorg, 0},
<insn>
*/
-symbolS *last_label_seen;
+symbolS * last_label_seen;
+static int label_is_thumb_function_name = false;
/* Literal stuff */
{
if (next_literal_pool_place > MAX_LITERAL_POOL_SIZE)
{
- inst.error = "Literal Pool Overflow\n";
+ inst.error = "Literal Pool Overflow";
return FAIL;
}
return symbolP;
}
-/* Check that an immediate is valid, and if so, convert it to the right format
- */
-
-/* OH, for a rotate instruction in C! */
+/* Check that an immediate is valid, and if so, convert it to the right format. */
-static int
+static unsigned int
validate_immediate (val)
- int val;
+ unsigned int val;
{
- unsigned int a = (unsigned int) val;
- int i;
-
- /* Do the easy (and most common ones) quickly */
- for (i = 0; i <= 24; i += 2)
- {
- if ((a & (0xff << i)) == a)
- return (int) (((32 - i) & 0x1e) << 7) | ((a >> i) & 0xff);
- }
+ unsigned int a;
+ unsigned int i;
- /* Now do the harder ones */
- for (; i < 32; i += 2)
- {
- if ((a & ((0xff << i) | (0xff >> (32 - i)))) == a)
- {
- a = ((a >> i) & 0xff) | ((a << (32 - i)) & 0xff);
- return (int) a | (((32 - i) >> 1) << 8);
- }
- }
- return FAIL;
+#define rotate_left(v, n) (v << n | v >> (32 - n))
+
+ for (i = 0; i < 32; i += 2)
+ if ((a = rotate_left (val, i)) <= 0xff)
+ return a | (i << 7); /* 12-bit pack: [shift-cnt,const] */
+ return FAIL;
}
static int
(valueT) frag_now_fix (), frag_now);
symbol_table_insert (current_poolP);
+ ARM_SET_THUMB (current_poolP, thumb_mode);
+ ARM_SET_INTERWORK (current_poolP, support_interwork);
+
while (lit_count < next_literal_pool_place)
/* First output the expression in the instruction to the pool */
emit_expr (&(literals[lit_count++].exp), 4); /* .word */
record_alignment (now_seg, temp);
}
+static void
+s_force_thumb (ignore)
+ int ignore;
+{
+ /* If we are not already in thumb mode go into it, EVEN if
+ the target processor does not support thumb instructions.
+ This is used by gcc/config/arm/lib1funcs.asm for example
+ to compile interworking support functions even if the
+ target processor should not support interworking. */
+
+ if (! thumb_mode)
+ {
+ thumb_mode = 1;
+
+ record_alignment (now_seg, 1);
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+static void
+s_thumb_func (ignore)
+ int ignore;
+{
+ /* The following label is the name/address of the start of a Thumb function.
+ We need to know this for the interworking support. */
+
+ label_is_thumb_function_name = true;
+
+ demand_empty_rest_of_line();
+}
+
static void
opcode_select (width)
int width;
}
else
{
- int value = validate_immediate (inst.reloc.exp.X_add_number);
+ unsigned value = validate_immediate (inst.reloc.exp.X_add_number);
if (value == FAIL)
{
inst.error = "Invalid constant";
{
/* This is actually a load/store of a halfword, or a
signed-extension load */
- if ((cpu_variant & ARM_ARCH4) == 0)
+ if ((cpu_variant & ARM_HALFWORD) == 0)
{
inst.error
- = "Processor does not support halfwords or signed bytes\n";
+ = "Processor does not support halfwords or signed bytes";
return;
}
if (add_to_lit_pool () == FAIL)
{
if (!inst.error)
- inst.error = "literal pool insertion failed\n";
+ inst.error = "literal pool insertion failed";
return;
}
if (Rs < 8 && Rd < 8)
{
if (move == THUMB_MOVE)
- /* A move of two lowregs is, by convention, encoded as
- ADD Rd, Rs, #0 */
+ /* A move of two lowregs is encoded as ADD Rd, Rs, #0
+ since a MOV instruction produces unpredictable results */
inst.instruction = T_OPCODE_ADD_I3;
else
inst.instruction = T_OPCODE_CMP_LR;
}
else if (*str == '=')
{
- /* TODO: We should allow the "ldr Rd,=expr" pseudo op in thumb mode */
- abort ();
+ /* Parse an "ldr Rd, =expr" instruction; this is another pseudo op */
+ str++;
+
+ while (*str == ' ')
+ str++;
+
+ if (my_get_expression (& inst.reloc.exp, & str))
+ return;
+
+ end_of_line (str);
+
+ if ( inst.reloc.exp.X_op != O_constant
+ && inst.reloc.exp.X_op != O_symbol)
+ {
+ inst.error = "Constant expression expected";
+ return;
+ }
+
+ if (inst.reloc.exp.X_op == O_constant
+ && ((inst.reloc.exp.X_add_number & ~0xFF) == 0))
+ {
+ /* This can be done with a mov instruction */
+
+ inst.instruction = T_OPCODE_MOV_I8 | (Rd << 8);
+ inst.instruction |= inst.reloc.exp.X_add_number;
+ return;
+ }
+
+ /* Insert into literal pool */
+ if (add_to_lit_pool () == FAIL)
+ {
+ if (!inst.error)
+ inst.error = "literal pool insertion failed";
+ return;
+ }
+
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET;
+ inst.reloc.pc_rel = 1;
+ inst.instruction = T_OPCODE_LDR_PC | (Rd << 8);
+ inst.reloc.exp.X_add_number += 4; /* Adjust ARM pipeline offset to Thumb */
+
+ return;
}
else
{
while (*str == ' ')
str++;
- if (reg_required_here (&str, 4) == FAIL
+ if (reg_required_here (&str, 4) == FAIL /* Store Rd in temporary location inside instruction. */
|| skip_past_comma (&str) == FAIL
|| my_get_expression (&inst.reloc.exp, &str))
{
md_begin ()
{
int i;
-
+
if ((arm_ops_hsh = hash_new ()) == NULL
|| (arm_tops_hsh = hash_new ()) == NULL
|| (arm_cond_hsh = hash_new ()) == NULL
set_constant_flonums ();
#ifdef OBJ_COFF
- /* Set the flags in the private structure */
- bfd_set_private_flags (stdoutput, uses_apcs_26 ? F_APCS26 : 0);
+ {
+ unsigned int flags = 0;
+
+ /* Set the flags in the private structure */
+ if (uses_apcs_26) flags |= F_APCS26;
+ if (support_interwork) flags |= F_INTERWORK;
+ if (uses_apcs_float) flags |= F_APCS_FLOAT;
+ if (pic_code) flags |= F_PIC;
+
+ bfd_set_private_flags (stdoutput, flags);
+ }
#endif
{
{
if (cpu_variant & ARM_THUMB)
mach = bfd_mach_arm_4T;
- else if (cpu_variant & ARM_ARCH4)
+ else if ((cpu_variant & ARM_ARCHv4) == ARM_ARCHv4)
mach = bfd_mach_arm_4;
else if (cpu_variant & ARM_LONGMUL)
mach = bfd_mach_arm_3M;
&& fixP->fx_subsy == NULL)
return 0; /* HACK */
+ if (fixP->fx_pcrel && (fixP->fx_r_type == BFD_RELOC_ARM_THUMB_ADD))
+ {
+ /* PC relative addressing on the Thumb is slightly odd
+ as the bottom two bits of the PC are forced to zero
+ for the calculation */
+ return (fixP->fx_where + fixP->fx_frag->fr_address) & ~3;
+ }
+
return fixP->fx_where + fixP->fx_frag->fr_address;
}
{
if (S_IS_DEFINED (fixP->fx_addsy)
&& S_GET_SEGMENT (fixP->fx_addsy) != seg)
- value += md_pcrel_from (fixP);
+ {
+ if (fixP->fx_r_type == BFD_RELOC_ARM_PCREL_BRANCH)
+ value = 0;
+ else
+ value += md_pcrel_from (fixP);
+ }
}
fixP->fx_addnumber = value; /* Remember value for emit_reloc */
break;
default:
- abort ();
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ "Unable to process relocation for thumb opcode: %x", newval);
+ break;
}
md_number_to_chars (buf, newval, THUMB_SIZE);
break;
"Literal referenced across section boundry (Implicit dump?)");
return NULL;
- case BFD_RELOC_ARM_IMMEDIATE:
- as_bad_where (fixp->fx_file, fixp->fx_line,
- "Internal_relocation (type %d) not fixed up (IMMEDIATE)",
- fixp->fx_r_type);
- return NULL;
-
- case BFD_RELOC_ARM_OFFSET_IMM:
- as_bad_where (fixp->fx_file, fixp->fx_line,
- "Internal_relocation (type %d) not fixed up (OFFSET_IMM)",
- fixp->fx_r_type);
- return NULL;
-
- case BFD_RELOC_ARM_OFFSET_IMM8:
- as_bad_where (fixp->fx_file, fixp->fx_line,
- "Internal_relocation (type %d) not fixed up (OFFSET_IMM8)",
- fixp->fx_r_type);
- return NULL;
-
- case BFD_RELOC_ARM_SHIFT_IMM:
- as_bad_where (fixp->fx_file, fixp->fx_line,
- "Internal_relocation (type %d) not fixed up (SHIFT_IMM)",
- fixp->fx_r_type);
- return NULL;
-
- case BFD_RELOC_ARM_SWI:
- as_bad_where (fixp->fx_file, fixp->fx_line,
- "Internal_relocation (type %d) not fixed up (SWI)",
- fixp->fx_r_type);
- return NULL;
-
- case BFD_RELOC_ARM_MULTI:
- as_bad_where (fixp->fx_file, fixp->fx_line,
- "Internal_relocation (type %d) not fixed up (MULTI)",
- fixp->fx_r_type);
- return NULL;
+ default:
+ {
+ char * type;
+ switch (fixp->fx_r_type)
+ {
+ case BFD_RELOC_ARM_IMMEDIATE: type = "IMMEDIATE"; break;
+ case BFD_RELOC_ARM_OFFSET_IMM: type = "OFFSET_IMM"; break;
+ case BFD_RELOC_ARM_OFFSET_IMM8: type = "OFFSET_IMM8"; break;
+ case BFD_RELOC_ARM_SHIFT_IMM: type = "SHIFT_IMM"; break;
+ case BFD_RELOC_ARM_SWI: type = "SWI"; break;
+ case BFD_RELOC_ARM_MULTI: type = "MULTI"; break;
+ case BFD_RELOC_ARM_CP_OFF_IMM: type = "CP_OFF_IMM"; break;
+ case BFD_RELOC_ARM_THUMB_ADD: type = "THUMB_ADD"; break;
+ case BFD_RELOC_ARM_THUMB_SHIFT: type = "THUMB_SHIFT"; break;
+ case BFD_RELOC_ARM_THUMB_IMM: type = "THUMB_IMM"; break;
+ case BFD_RELOC_ARM_THUMB_OFFSET: type = "THUMB_OFFSET"; break;
+ default: type = "<unknown>"; break;
+ }
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ "Can not represent %s relocation in this object file format (%d)",
+ type, fixp->fx_pcrel);
+ return NULL;
+ }
+ }
- case BFD_RELOC_ARM_CP_OFF_IMM:
- as_bad_where (fixp->fx_file, fixp->fx_line,
- "Internal_relocation (type %d) not fixed up (CP_OFF_IMM)",
- fixp->fx_r_type);
- return NULL;
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, code);
- case BFD_RELOC_ARM_THUMB_OFFSET:
+ if (reloc->howto == NULL)
+ {
as_bad_where (fixp->fx_file, fixp->fx_line,
- "Internal_relocation (type %d) not fixed up (THUMB_OFFSET)",
- fixp->fx_r_type);
+ "Can not represent %s relocation in this object file format",
+ bfd_get_reloc_code_name (code));
return NULL;
-
- default:
- abort ();
}
- reloc->howto = bfd_reloc_type_lookup (stdoutput, code);
- assert (reloc->howto != 0);
-
return reloc;
}
if (inst.error)
{
- as_bad ("%s -- statement `%s'\n", inst.error, str);
+ as_bad (inst.error);
return;
}
if (*q && !strncmp (q, ".req ", 4))
{
- int reg;
- if ((reg = arm_reg_parse (&str)) == FAIL)
- {
- char *r;
+ int reg;
+ char * copy_of_str = str;
+ char * r;
- q += 4;
- while (*q == ' ')
- q++;
+ q += 4;
+ while (*q == ' ')
+ q++;
- for (r = q; *r != '\0'; r++)
- if (*r == ' ')
- break;
+ for (r = q; *r != '\0'; r++)
+ if (*r == ' ')
+ break;
+
+ if (r != q)
+ {
+ int regnum;
+ char d = *r;
- if (r != q)
- {
- int regnum;
- char d = *r;
+ *r = '\0';
+ regnum = arm_reg_parse (& q);
+ *r = d;
- *r = '\0';
- regnum = arm_reg_parse (&q);
- *r = d;
+ reg = arm_reg_parse (& str);
+
+ if (reg == FAIL)
+ {
if (regnum != FAIL)
{
insert_reg_alias (str, regnum);
- *p = c;
- return;
+ }
+ else
+ {
+ as_warn ("register '%s' does not exist\n", q);
}
}
+ else if (regnum != FAIL)
+ {
+ if (reg != regnum)
+ as_warn ("ignoring redefinition of register alias '%s'", copy_of_str );
+
+ /* Do not warn abpout redefinitions to the same alias. */
+ }
+ else
+ as_warn ("ignoring redefinition of register alias '%s' to non-existant register '%s'",
+ copy_of_str, q);
}
else
- {
- *p = c;
- return;
- }
+ as_warn ("ignoring incomplete .req pseuso op");
+
+ *p = c;
+ return;
}
*p = c;
* ARM Procedure Calling Standard:
* -mapcs-32 32 bit APCS
* -mapcs-26 26 bit APCS
+ * -mapcs-float Pass floats in float regs
+ * -mapcs-reentrant Position independent code
+ * -mthumb-interwork Code supports Arm/Thumb interworking
*/
CONST char *md_shortopts = "m:";
cpu_variant = (cpu_variant & ~FPU_ALL) | FPU_NONE;
thumb_mode = 1;
}
+ else if (! strcmp (str, "thumb-interwork"))
+ {
+ cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_THUMB | ARM_ARCHv4;
+#ifdef OBJ_COFF
+ support_interwork = true;
+#endif
+ }
else
goto bad;
break;
return 1;
}
#ifdef OBJ_COFF
- if (! strcmp (str, "apcs-32"))
- {
- uses_apcs_26 = false;
- return 1;
- }
- else if (! strcmp (str, "apcs-26"))
+ if (! strncmp (str, "apcs-", 5))
{
- uses_apcs_26 = true;
- return 1;
- }
+ /* GCC passes on all command line options starting "-mapcs-..."
+ to us, so we must parse them here. */
+
+ str += 5;
+
+ if (! strcmp (str, "32"))
+ {
+ uses_apcs_26 = false;
+ return 1;
+ }
+ else if (! strcmp (str, "26"))
+ {
+ uses_apcs_26 = true;
+ return 1;
+ }
+ else if (! strcmp (str, "frame"))
+ {
+ /* Stack frames are being generated - does not affect
+ linkage of code. */
+ return 1;
+ }
+ else if (! strcmp (str, "stack-check"))
+ {
+ /* Stack checking is being performed - does not affect
+ linkage, but does require that the functions
+ __rt_stkovf_split_small and __rt_stkovf_split_big be
+ present in the final link. */
+
+ return 1;
+ }
+ else if (! strcmp (str, "float"))
+ {
+ /* Floating point arguments are being passed in the floating
+ point registers. This does affect linking, since this
+ version of the APCS is incompatible with the version that
+ passes floating points in the integer registers. */
+
+ uses_apcs_float = true;
+ return 1;
+ }
+ else if (! strcmp (str, "reentrant"))
+ {
+ /* Reentrant code has been generated. This does affect
+ linking, since there is no point in linking reentrant/
+ position independent code with absolute position code. */
+ pic_code = true;
+ return 1;
+ }
+
+ as_bad ("Unrecognised APCS switch -m%s", arg);
+ return 0;
+ }
#endif
/* Strip off optional "arm" */
if (! strncmp (str, "arm", 3))
goto bad;
break;
+ case 's':
+ if (streq (str, "strongarm") || streq (str, "strongarm110"))
+ cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_7 | ARM_ARCHv4 | ARM_LONGMUL;
+ else
+ goto bad;
+ break;
+
+ case '8':
+ if (streq (str, "8"))
+ cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_7 | ARM_ARCHv4 | ARM_LONGMUL;
+ else
+ goto bad;
+ break;
+
case '6':
if (! strcmp (str, "6"))
cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_6;
cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_7;
for (; *str; str++)
{
- switch (*str)
+ switch (* str)
{
case 't':
- cpu_variant |= ARM_THUMB;
+ cpu_variant |= (ARM_THUMB | ARM_ARCHv4);
break;
case 'm':
cpu_variant |= ARM_LONGMUL;
break;
+ case 'f': /* fe => fp enabled cpu. */
+ if (str[1] == 'e')
+ ++ str;
+ else
+ goto bad;
+
+ case 'c': /* Unknown */
case 'd': /* debug */
case 'i': /* embedded ice */
- /* Included for completeness in ARM processor
- naming. */
+ /* Included for completeness in ARM processor naming. */
break;
default:
break;
case '4':
- cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_7;
+ cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_ARCHv4;
switch (*++str)
{
FILE *fp;
{
fprintf (fp,
-"-m[arm][1|2|250|3|6|7[t][d][m][i]] select processor variant\n\
+"-m[arm][<processor name>] select processor variant\n\
-m[arm]v[2|2a|3|3m|4|4t] select architecture variant\n\
-mthumb\t\t\tonly allow Thumb instructions\n\
+-mthumb-interwork\tmark the assembled code as supporting interworking\n\
-mall\t\t\tallow any instruction\n\
-mfpa10, -mfpa11\tselect floating point architecture\n\
-mfpe-old\t\tdon't allow floating-point multiple instructions\n\
#ifdef OBJ_COFF
fprintf (fp,
"-mapcs-32, -mapcs-26\tspecify which ARM Procedure Calling Standard is in use\n");
+ fprintf (fp,
+"-mapcs-float\t\tfloating point args are passed in floating point regs\n");
+ fprintf (fp,
+"-mapcs-reentrant\tposition independent/reentrant code has been generated\n");
#endif
#ifdef ARM_BI_ENDIAN
fprintf (fp,
symbolS *sym;
{
last_label_seen = sym;
- ARM_SET_TYPE(sym,thumb_mode);
+ ARM_SET_THUMB (sym, thumb_mode);
+ ARM_SET_INTERWORK (sym, support_interwork);
+
+ if (label_is_thumb_function_name)
+ {
+ /* When the address of a Thumb function is taken the bottom
+ bit of that address should be set. This will allow
+ interworking between Arm and Thumb functions to work
+ correctly. */
+
+ THUMB_SET_FUNC (sym, 1);
+
+ label_is_thumb_function_name = false;
+ }
}
/* Adjust the symbol table. This marks Thumb symbols as distinct from
for (sym = symbol_rootP; sym != NULL; sym = symbol_next (sym))
{
- if (ARM_GET_TYPE(sym)) /* Thumb */
+ if (ARM_IS_THUMB (sym))
{
- switch (S_GET_STORAGE_CLASS (sym))
+ if (THUMB_IS_FUNC (sym))
+ {
+ /* Mark the symbol as a Thumb function. */
+ if ( S_GET_STORAGE_CLASS (sym) == C_STAT
+ || S_GET_STORAGE_CLASS (sym) == C_LABEL) /* This can happen! */
+ S_SET_STORAGE_CLASS (sym, C_THUMBSTATFUNC);
+ else if (S_GET_STORAGE_CLASS (sym) == C_EXT)
+ S_SET_STORAGE_CLASS (sym, C_THUMBEXTFUNC);
+ else
+ as_bad ("%s: unexpected function type: %d", S_GET_NAME (sym), S_GET_STORAGE_CLASS (sym));
+ }
+ else switch (S_GET_STORAGE_CLASS (sym))
{
case C_EXT:
S_SET_STORAGE_CLASS (sym, C_THUMBEXT);
break;
}
}
+
+ if (ARM_IS_INTERWORK (sym))
+ {
+ coffsymbol(sym->bsym)->native->u.syment.n_flags = 0xFF;
+ }
}
#endif
}