tc-arm.c revision 68765
160484Sobrien/* tc-arm.c -- Assemble for the ARM 260484Sobrien Copyright (C) 1994, 95, 96, 97, 98, 1999, 2000 Free Software Foundation, Inc. 360484Sobrien Contributed by Richard Earnshaw (rwe@pegasus.esprit.ec.org) 460484Sobrien Modified by David Taylor (dtaylor@armltd.co.uk) 560484Sobrien 660484Sobrien This file is part of GAS, the GNU Assembler. 760484Sobrien 860484Sobrien GAS is free software; you can redistribute it and/or modify 960484Sobrien it under the terms of the GNU General Public License as published by 1060484Sobrien the Free Software Foundation; either version 2, or (at your option) 1160484Sobrien any later version. 1260484Sobrien 1360484Sobrien GAS is distributed in the hope that it will be useful, 1460484Sobrien but WITHOUT ANY WARRANTY; without even the implied warranty of 1560484Sobrien MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1660484Sobrien GNU General Public License for more details. 1760484Sobrien 1860484Sobrien You should have received a copy of the GNU General Public License 1960484Sobrien along with GAS; see the file COPYING. If not, write to the Free 2060484Sobrien Software Foundation, 59 Temple Place - Suite 330, Boston, MA 2160484Sobrien 02111-1307, USA. */ 2260484Sobrien 2360484Sobrien#include <ctype.h> 2460484Sobrien#include <string.h> 2560484Sobrien#define NO_RELOC 0 2660484Sobrien#include "as.h" 2760484Sobrien 2860484Sobrien/* need TARGET_CPU */ 2960484Sobrien#include "config.h" 3060484Sobrien#include "subsegs.h" 3160484Sobrien#include "obstack.h" 3260484Sobrien#include "symbols.h" 3360484Sobrien#include "listing.h" 3460484Sobrien 3560484Sobrien#ifdef OBJ_ELF 3660484Sobrien#include "elf/arm.h" 3760484Sobrien#endif 3860484Sobrien 3960484Sobrien/* Types of processor to assemble for. */ 4060484Sobrien#define ARM_1 0x00000001 4160484Sobrien#define ARM_2 0x00000002 4260484Sobrien#define ARM_3 0x00000004 4360484Sobrien#define ARM_250 ARM_3 4460484Sobrien#define ARM_6 0x00000008 4560484Sobrien#define ARM_7 ARM_6 /* same core instruction set */ 4660484Sobrien#define ARM_8 ARM_6 /* same core instruction set */ 4760484Sobrien#define ARM_9 ARM_6 /* same core instruction set */ 4860484Sobrien#define ARM_CPU_MASK 0x0000000f 4960484Sobrien 5060484Sobrien/* The following bitmasks control CPU extensions (ARM7 onwards): */ 5160484Sobrien#define ARM_LONGMUL 0x00000010 /* allow long multiplies */ 5260484Sobrien#define ARM_HALFWORD 0x00000020 /* allow half word loads */ 5360484Sobrien#define ARM_THUMB 0x00000040 /* allow BX instruction */ 5460484Sobrien#define ARM_EXT_V5 0x00000080 /* allow CLZ etc */ 5560484Sobrien#define ARM_EXT_V5E 0x00000200 /* "El Segundo" */ 5660484Sobrien 5760484Sobrien/* Architectures are the sum of the base and extensions. */ 5860484Sobrien#define ARM_ARCH_V4 (ARM_7 | ARM_LONGMUL | ARM_HALFWORD) 5960484Sobrien#define ARM_ARCH_V4T (ARM_ARCH_V4 | ARM_THUMB) 6060484Sobrien#define ARM_ARCH_V5 (ARM_ARCH_V4 | ARM_EXT_V5) 6160484Sobrien#define ARM_ARCH_V5T (ARM_ARCH_V5 | ARM_THUMB) 6260484Sobrien 6360484Sobrien/* Some useful combinations: */ 6460484Sobrien#define ARM_ANY 0x00ffffff 6560484Sobrien#define ARM_2UP (ARM_ANY - ARM_1) 6660484Sobrien#define ARM_ALL ARM_2UP /* Not arm1 only */ 6760484Sobrien#define ARM_3UP 0x00fffffc 6860484Sobrien#define ARM_6UP 0x00fffff8 /* Includes ARM7 */ 6960484Sobrien 7060484Sobrien#define FPU_CORE 0x80000000 7160484Sobrien#define FPU_FPA10 0x40000000 7260484Sobrien#define FPU_FPA11 0x40000000 7360484Sobrien#define FPU_NONE 0 7460484Sobrien 7560484Sobrien/* Some useful combinations */ 7660484Sobrien#define FPU_ALL 0xff000000 /* Note this is ~ARM_ANY */ 7760484Sobrien#define FPU_MEMMULTI 0x7f000000 /* Not fpu_core */ 7860484Sobrien 7960484Sobrien 8060484Sobrien#ifndef CPU_DEFAULT 8160484Sobrien#if defined __thumb__ 8260484Sobrien#define CPU_DEFAULT (ARM_ARCH_V4 | ARM_THUMB) 8360484Sobrien#else 8460484Sobrien#define CPU_DEFAULT ARM_ALL 8560484Sobrien#endif 8660484Sobrien#endif 8760484Sobrien 8860484Sobrien#ifndef FPU_DEFAULT 8960484Sobrien#define FPU_DEFAULT FPU_ALL 9060484Sobrien#endif 9160484Sobrien 9260484Sobrien#define streq(a, b) (strcmp (a, b) == 0) 9360484Sobrien#define skip_whitespace(str) while (* (str) == ' ') ++ (str) 9460484Sobrien 9560484Sobrienstatic unsigned long cpu_variant = CPU_DEFAULT | FPU_DEFAULT; 9660484Sobrienstatic int target_oabi = 0; 9760484Sobrien 9860484Sobrien#if defined OBJ_COFF || defined OBJ_ELF 9960484Sobrien/* Flags stored in private area of BFD structure */ 10060484Sobrienstatic boolean uses_apcs_26 = false; 10160484Sobrienstatic boolean support_interwork = false; 10260484Sobrienstatic boolean uses_apcs_float = false; 10360484Sobrienstatic boolean pic_code = false; 10460484Sobrien#endif 10560484Sobrien 10660484Sobrien/* This array holds the chars that always start a comment. If the 10760484Sobrien pre-processor is disabled, these aren't very useful. */ 10860484SobrienCONST char comment_chars[] = "@"; 10960484Sobrien 11060484Sobrien/* This array holds the chars that only start a comment at the beginning of 11160484Sobrien a line. If the line seems to have the form '# 123 filename' 11260484Sobrien .line and .file directives will appear in the pre-processed output. */ 11360484Sobrien/* Note that input_file.c hand checks for '#' at the beginning of the 11460484Sobrien first line of the input file. This is because the compiler outputs 11560484Sobrien #NO_APP at the beginning of its output. */ 11660484Sobrien/* Also note that comments like this one will always work. */ 11760484SobrienCONST char line_comment_chars[] = "#"; 11860484Sobrien 11960484Sobrien#ifdef TE_LINUX 12060484SobrienCONST char line_separator_chars[] = ";"; 12160484Sobrien#else 12260484SobrienCONST char line_separator_chars[] = ""; 12360484Sobrien#endif 12460484Sobrien 12560484Sobrien/* Chars that can be used to separate mant 12660484Sobrien from exp in floating point numbers. */ 12760484SobrienCONST char EXP_CHARS[] = "eE"; 12860484Sobrien 12960484Sobrien/* Chars that mean this number is a floating point constant */ 13060484Sobrien/* As in 0f12.456 */ 13160484Sobrien/* or 0d1.2345e12 */ 13260484Sobrien 13360484SobrienCONST char FLT_CHARS[] = "rRsSfFdDxXeEpP"; 13460484Sobrien 13560484Sobrien/* Prefix characters that indicate the start of an immediate 13660484Sobrien value. */ 13760484Sobrien#define is_immediate_prefix(C) ((C) == '#' || (C) == '$') 13860484Sobrien 13960484Sobrien#ifdef OBJ_ELF 14060484SobriensymbolS * GOT_symbol; /* Pre-defined "_GLOBAL_OFFSET_TABLE_" */ 14160484Sobrien#endif 14260484Sobrien 14360484SobrienCONST int md_reloc_size = 8; /* Size of relocation record */ 14460484Sobrien 14560484Sobrienstatic int thumb_mode = 0; /* 0: assemble for ARM, 1: assemble for Thumb, 14660484Sobrien 2: assemble for Thumb even though target cpu 14760484Sobrien does not support thumb instructions. */ 14860484Sobrientypedef struct arm_fix 14960484Sobrien{ 15060484Sobrien int thumb_mode; 15160484Sobrien} arm_fix_data; 15260484Sobrien 15360484Sobrienstruct arm_it 15460484Sobrien{ 15560484Sobrien CONST char * error; 15660484Sobrien unsigned long instruction; 15760484Sobrien int suffix; 15860484Sobrien int size; 15960484Sobrien struct 16060484Sobrien { 16160484Sobrien bfd_reloc_code_real_type type; 16260484Sobrien expressionS exp; 16360484Sobrien int pc_rel; 16460484Sobrien } reloc; 16560484Sobrien}; 16660484Sobrien 16760484Sobrienstruct arm_it inst; 16860484Sobrien 16960484Sobrienstruct asm_shift 17060484Sobrien{ 17160484Sobrien CONST char * template; 17260484Sobrien unsigned long value; 17360484Sobrien}; 17460484Sobrien 17560484Sobrienstatic CONST struct asm_shift shift[] = 17660484Sobrien{ 17760484Sobrien {"asl", 0}, 17860484Sobrien {"lsl", 0}, 17960484Sobrien {"lsr", 0x00000020}, 18060484Sobrien {"asr", 0x00000040}, 18160484Sobrien {"ror", 0x00000060}, 18260484Sobrien {"rrx", 0x00000060}, 18360484Sobrien {"ASL", 0}, 18460484Sobrien {"LSL", 0}, 18560484Sobrien {"LSR", 0x00000020}, 18660484Sobrien {"ASR", 0x00000040}, 18760484Sobrien {"ROR", 0x00000060}, 18860484Sobrien {"RRX", 0x00000060} 18960484Sobrien}; 19060484Sobrien 19160484Sobrien#define NO_SHIFT_RESTRICT 1 19260484Sobrien#define SHIFT_RESTRICT 0 19360484Sobrien 19460484Sobrien#define NUM_FLOAT_VALS 8 19560484Sobrien 19660484SobrienCONST char * fp_const[] = 19760484Sobrien{ 19860484Sobrien "0.0", "1.0", "2.0", "3.0", "4.0", "5.0", "0.5", "10.0", 0 19960484Sobrien}; 20060484Sobrien 20160484Sobrien/* Number of littlenums required to hold an extended precision number. */ 20260484Sobrien#define MAX_LITTLENUMS 6 20360484Sobrien 20460484SobrienLITTLENUM_TYPE fp_values[NUM_FLOAT_VALS][MAX_LITTLENUMS]; 20560484Sobrien 20660484Sobrien#define FAIL (-1) 20760484Sobrien#define SUCCESS (0) 20860484Sobrien 20960484Sobrien#define SUFF_S 1 21060484Sobrien#define SUFF_D 2 21160484Sobrien#define SUFF_E 3 21260484Sobrien#define SUFF_P 4 21360484Sobrien 21460484Sobrien#define CP_T_X 0x00008000 21560484Sobrien#define CP_T_Y 0x00400000 21660484Sobrien#define CP_T_Pre 0x01000000 21760484Sobrien#define CP_T_UD 0x00800000 21860484Sobrien#define CP_T_WB 0x00200000 21960484Sobrien 22060484Sobrien#define CONDS_BIT (0x00100000) 22160484Sobrien#define LOAD_BIT (0x00100000) 22260484Sobrien#define TRANS_BIT (0x00200000) 22360484Sobrien 22460484Sobrienstruct asm_cond 22560484Sobrien{ 22660484Sobrien CONST char * template; 22760484Sobrien unsigned long value; 22860484Sobrien}; 22960484Sobrien 23060484Sobrien/* This is to save a hash look-up in the common case. */ 23160484Sobrien#define COND_ALWAYS 0xe0000000 23260484Sobrien 23360484Sobrienstatic CONST struct asm_cond conds[] = 23460484Sobrien{ 23560484Sobrien {"eq", 0x00000000}, 23660484Sobrien {"ne", 0x10000000}, 23760484Sobrien {"cs", 0x20000000}, {"hs", 0x20000000}, 23860484Sobrien {"cc", 0x30000000}, {"ul", 0x30000000}, {"lo", 0x30000000}, 23960484Sobrien {"mi", 0x40000000}, 24060484Sobrien {"pl", 0x50000000}, 24160484Sobrien {"vs", 0x60000000}, 24260484Sobrien {"vc", 0x70000000}, 24360484Sobrien {"hi", 0x80000000}, 24460484Sobrien {"ls", 0x90000000}, 24560484Sobrien {"ge", 0xa0000000}, 24660484Sobrien {"lt", 0xb0000000}, 24760484Sobrien {"gt", 0xc0000000}, 24860484Sobrien {"le", 0xd0000000}, 24960484Sobrien {"al", 0xe0000000}, 25060484Sobrien {"nv", 0xf0000000} 25160484Sobrien}; 25260484Sobrien 25360484Sobrien/* Warning: If the top bit of the set_bits is set, then the standard 25460484Sobrien instruction bitmask is ignored, and the new bitmask is taken from 25560484Sobrien the set_bits: */ 25660484Sobrienstruct asm_flg 25760484Sobrien{ 25860484Sobrien CONST char * template; /* Basic flag string */ 25960484Sobrien unsigned long set_bits; /* Bits to set */ 26060484Sobrien}; 26160484Sobrien 26260484Sobrienstatic CONST struct asm_flg s_flag[] = 26360484Sobrien{ 26460484Sobrien {"s", CONDS_BIT}, 26560484Sobrien {NULL, 0} 26660484Sobrien}; 26760484Sobrien 26860484Sobrienstatic CONST struct asm_flg ldr_flags[] = 26960484Sobrien{ 27060484Sobrien {"b", 0x00400000}, 27160484Sobrien {"t", TRANS_BIT}, 27260484Sobrien {"bt", 0x00400000 | TRANS_BIT}, 27360484Sobrien {"h", 0x801000b0}, 27460484Sobrien {"sh", 0x801000f0}, 27560484Sobrien {"sb", 0x801000d0}, 27660484Sobrien {NULL, 0} 27760484Sobrien}; 27860484Sobrien 27960484Sobrienstatic CONST struct asm_flg str_flags[] = 28060484Sobrien{ 28160484Sobrien {"b", 0x00400000}, 28260484Sobrien {"t", TRANS_BIT}, 28360484Sobrien {"bt", 0x00400000 | TRANS_BIT}, 28460484Sobrien {"h", 0x800000b0}, 28560484Sobrien {NULL, 0} 28660484Sobrien}; 28760484Sobrien 28860484Sobrienstatic CONST struct asm_flg byte_flag[] = 28960484Sobrien{ 29060484Sobrien {"b", 0x00400000}, 29160484Sobrien {NULL, 0} 29260484Sobrien}; 29360484Sobrien 29460484Sobrienstatic CONST struct asm_flg cmp_flags[] = 29560484Sobrien{ 29660484Sobrien {"s", CONDS_BIT}, 29760484Sobrien {"p", 0x0010f000}, 29860484Sobrien {NULL, 0} 29960484Sobrien}; 30060484Sobrien 30160484Sobrienstatic CONST struct asm_flg ldm_flags[] = 30260484Sobrien{ 30360484Sobrien {"ed", 0x01800000}, 30460484Sobrien {"fd", 0x00800000}, 30560484Sobrien {"ea", 0x01000000}, 30660484Sobrien {"fa", 0x08000000}, 30760484Sobrien {"ib", 0x01800000}, 30860484Sobrien {"ia", 0x00800000}, 30960484Sobrien {"db", 0x01000000}, 31060484Sobrien {"da", 0x08000000}, 31160484Sobrien {NULL, 0} 31260484Sobrien}; 31360484Sobrien 31460484Sobrienstatic CONST struct asm_flg stm_flags[] = 31560484Sobrien{ 31660484Sobrien {"ed", 0x08000000}, 31760484Sobrien {"fd", 0x01000000}, 31860484Sobrien {"ea", 0x00800000}, 31960484Sobrien {"fa", 0x01800000}, 32060484Sobrien {"ib", 0x01800000}, 32160484Sobrien {"ia", 0x00800000}, 32260484Sobrien {"db", 0x01000000}, 32360484Sobrien {"da", 0x08000000}, 32460484Sobrien {NULL, 0} 32560484Sobrien}; 32660484Sobrien 32760484Sobrienstatic CONST struct asm_flg lfm_flags[] = 32860484Sobrien{ 32960484Sobrien {"fd", 0x00800000}, 33060484Sobrien {"ea", 0x01000000}, 33160484Sobrien {NULL, 0} 33260484Sobrien}; 33360484Sobrien 33460484Sobrienstatic CONST struct asm_flg sfm_flags[] = 33560484Sobrien{ 33660484Sobrien {"fd", 0x01000000}, 33760484Sobrien {"ea", 0x00800000}, 33860484Sobrien {NULL, 0} 33960484Sobrien}; 34060484Sobrien 34160484Sobrienstatic CONST struct asm_flg round_flags[] = 34260484Sobrien{ 34360484Sobrien {"p", 0x00000020}, 34460484Sobrien {"m", 0x00000040}, 34560484Sobrien {"z", 0x00000060}, 34660484Sobrien {NULL, 0} 34760484Sobrien}; 34860484Sobrien 34960484Sobrien/* The implementation of the FIX instruction is broken on some assemblers, 35060484Sobrien in that it accepts a precision specifier as well as a rounding specifier, 35160484Sobrien despite the fact that this is meaningless. To be more compatible, we 35260484Sobrien accept it as well, though of course it does not set any bits. */ 35360484Sobrienstatic CONST struct asm_flg fix_flags[] = 35460484Sobrien{ 35560484Sobrien {"p", 0x00000020}, 35660484Sobrien {"m", 0x00000040}, 35760484Sobrien {"z", 0x00000060}, 35860484Sobrien {"sp", 0x00000020}, 35960484Sobrien {"sm", 0x00000040}, 36060484Sobrien {"sz", 0x00000060}, 36160484Sobrien {"dp", 0x00000020}, 36260484Sobrien {"dm", 0x00000040}, 36360484Sobrien {"dz", 0x00000060}, 36460484Sobrien {"ep", 0x00000020}, 36560484Sobrien {"em", 0x00000040}, 36660484Sobrien {"ez", 0x00000060}, 36760484Sobrien {NULL, 0} 36860484Sobrien}; 36960484Sobrien 37060484Sobrienstatic CONST struct asm_flg except_flag[] = 37160484Sobrien{ 37260484Sobrien {"e", 0x00400000}, 37360484Sobrien {NULL, 0} 37460484Sobrien}; 37560484Sobrien 37660484Sobrienstatic CONST struct asm_flg cplong_flag[] = 37760484Sobrien{ 37860484Sobrien {"l", 0x00400000}, 37960484Sobrien {NULL, 0} 38060484Sobrien}; 38160484Sobrien 38260484Sobrienstruct asm_psr 38360484Sobrien{ 38460484Sobrien CONST char * template; 38561843Sobrien boolean cpsr; 38661843Sobrien unsigned long field; 38760484Sobrien}; 38860484Sobrien 38961843Sobrien#define SPSR_BIT (1 << 22) /* The bit that distnguishes CPSR and SPSR. */ 39061843Sobrien#define PSR_SHIFT 16 /* How many bits to shift the PSR_xxx bits up by. */ 39160484Sobrien 39261843Sobrien#define PSR_c (1 << 0) 39361843Sobrien#define PSR_x (1 << 1) 39461843Sobrien#define PSR_s (1 << 2) 39561843Sobrien#define PSR_f (1 << 3) 39660484Sobrien 39760484Sobrienstatic CONST struct asm_psr psrs[] = 39860484Sobrien{ 39961843Sobrien {"CPSR", true, PSR_c | PSR_f}, 40061843Sobrien {"CPSR_all", true, PSR_c | PSR_f}, 40161843Sobrien {"SPSR", false, PSR_c | PSR_f}, 40261843Sobrien {"SPSR_all", false, PSR_c | PSR_f}, 40361843Sobrien {"CPSR_flg", true, PSR_f}, 40461843Sobrien {"CPSR_f", true, PSR_f}, 40561843Sobrien {"SPSR_flg", false, PSR_f}, 40661843Sobrien {"SPSR_f", false, PSR_f}, 40761843Sobrien {"CPSR_c", true, PSR_c}, 40861843Sobrien {"CPSR_ctl", true, PSR_c}, 40961843Sobrien {"SPSR_c", false, PSR_c}, 41061843Sobrien {"SPSR_ctl", false, PSR_c}, 41161843Sobrien {"CPSR_x", true, PSR_x}, 41261843Sobrien {"CPSR_s", true, PSR_s}, 41361843Sobrien {"SPSR_x", false, PSR_x}, 41461843Sobrien {"SPSR_s", false, PSR_s}, 41561843Sobrien /* For backwards compatability with older toolchain we also 41661843Sobrien support lower case versions of some of these flags. */ 41761843Sobrien {"cpsr", true, PSR_c | PSR_f}, 41861843Sobrien {"cpsr_all", true, PSR_c | PSR_f}, 41961843Sobrien {"spsr", false, PSR_c | PSR_f}, 42061843Sobrien {"spsr_all", false, PSR_c | PSR_f}, 42161843Sobrien {"cpsr_flg", true, PSR_f}, 42261843Sobrien {"cpsr_f", true, PSR_f}, 42361843Sobrien {"spsr_flg", false, PSR_f}, 42461843Sobrien {"spsr_f", false, PSR_f}, 42561843Sobrien {"cpsr_c", true, PSR_c}, 42661843Sobrien {"cpsr_ctl", true, PSR_c}, 42761843Sobrien {"spsr_c", false, PSR_c}, 42861843Sobrien {"spsr_ctl", false, PSR_c} 42960484Sobrien}; 43060484Sobrien 43160484Sobrien/* Functions called by parser. */ 43260484Sobrien/* ARM instructions */ 43360484Sobrienstatic void do_arit PARAMS ((char *, unsigned long)); 43460484Sobrienstatic void do_cmp PARAMS ((char *, unsigned long)); 43560484Sobrienstatic void do_mov PARAMS ((char *, unsigned long)); 43660484Sobrienstatic void do_ldst PARAMS ((char *, unsigned long)); 43760484Sobrienstatic void do_ldmstm PARAMS ((char *, unsigned long)); 43860484Sobrienstatic void do_branch PARAMS ((char *, unsigned long)); 43960484Sobrienstatic void do_swi PARAMS ((char *, unsigned long)); 44060484Sobrien/* Pseudo Op codes */ 44160484Sobrienstatic void do_adr PARAMS ((char *, unsigned long)); 44260484Sobrienstatic void do_adrl PARAMS ((char *, unsigned long)); 44360484Sobrienstatic void do_nop PARAMS ((char *, unsigned long)); 44460484Sobrien/* ARM 2 */ 44560484Sobrienstatic void do_mul PARAMS ((char *, unsigned long)); 44660484Sobrienstatic void do_mla PARAMS ((char *, unsigned long)); 44760484Sobrien/* ARM 3 */ 44860484Sobrienstatic void do_swap PARAMS ((char *, unsigned long)); 44960484Sobrien/* ARM 6 */ 45060484Sobrienstatic void do_msr PARAMS ((char *, unsigned long)); 45160484Sobrienstatic void do_mrs PARAMS ((char *, unsigned long)); 45260484Sobrien/* ARM 7M */ 45360484Sobrienstatic void do_mull PARAMS ((char *, unsigned long)); 45460484Sobrien/* ARM THUMB */ 45560484Sobrienstatic void do_bx PARAMS ((char *, unsigned long)); 45660484Sobrien 45760484Sobrien 45860484Sobrien/* Coprocessor Instructions */ 45960484Sobrienstatic void do_cdp PARAMS ((char *, unsigned long)); 46060484Sobrienstatic void do_lstc PARAMS ((char *, unsigned long)); 46160484Sobrienstatic void do_co_reg PARAMS ((char *, unsigned long)); 46260484Sobrienstatic void do_fp_ctrl PARAMS ((char *, unsigned long)); 46360484Sobrienstatic void do_fp_ldst PARAMS ((char *, unsigned long)); 46460484Sobrienstatic void do_fp_ldmstm PARAMS ((char *, unsigned long)); 46560484Sobrienstatic void do_fp_dyadic PARAMS ((char *, unsigned long)); 46660484Sobrienstatic void do_fp_monadic PARAMS ((char *, unsigned long)); 46760484Sobrienstatic void do_fp_cmp PARAMS ((char *, unsigned long)); 46860484Sobrienstatic void do_fp_from_reg PARAMS ((char *, unsigned long)); 46960484Sobrienstatic void do_fp_to_reg PARAMS ((char *, unsigned long)); 47060484Sobrien 47160484Sobrienstatic void fix_new_arm PARAMS ((fragS *, int, short, expressionS *, int, int)); 47260484Sobrienstatic int arm_reg_parse PARAMS ((char **)); 47361843Sobrienstatic CONST struct asm_psr * arm_psr_parse PARAMS ((char **)); 47460484Sobrienstatic void symbol_locate PARAMS ((symbolS *, CONST char *, segT, valueT, fragS *)); 47560484Sobrienstatic int add_to_lit_pool PARAMS ((void)); 47660484Sobrienstatic unsigned validate_immediate PARAMS ((unsigned)); 47760484Sobrienstatic unsigned validate_immediate_twopart PARAMS ((unsigned int, unsigned int *)); 47860484Sobrienstatic int validate_offset_imm PARAMS ((unsigned int, int)); 47960484Sobrienstatic void opcode_select PARAMS ((int)); 48060484Sobrienstatic void end_of_line PARAMS ((char *)); 48160484Sobrienstatic int reg_required_here PARAMS ((char **, int)); 48261843Sobrienstatic int psr_required_here PARAMS ((char **)); 48360484Sobrienstatic int co_proc_number PARAMS ((char **)); 48460484Sobrienstatic int cp_opc_expr PARAMS ((char **, int, int)); 48560484Sobrienstatic int cp_reg_required_here PARAMS ((char **, int)); 48660484Sobrienstatic int fp_reg_required_here PARAMS ((char **, int)); 48760484Sobrienstatic int cp_address_offset PARAMS ((char **)); 48860484Sobrienstatic int cp_address_required_here PARAMS ((char **)); 48960484Sobrienstatic int my_get_float_expression PARAMS ((char **)); 49060484Sobrienstatic int skip_past_comma PARAMS ((char **)); 49160484Sobrienstatic int walk_no_bignums PARAMS ((symbolS *)); 49260484Sobrienstatic int negate_data_op PARAMS ((unsigned long *, unsigned long)); 49360484Sobrienstatic int data_op2 PARAMS ((char **)); 49460484Sobrienstatic int fp_op2 PARAMS ((char **)); 49560484Sobrienstatic long reg_list PARAMS ((char **)); 49660484Sobrienstatic void thumb_load_store PARAMS ((char *, int, int)); 49760484Sobrienstatic int decode_shift PARAMS ((char **, int)); 49860484Sobrienstatic int ldst_extend PARAMS ((char **, int)); 49960484Sobrienstatic void thumb_add_sub PARAMS ((char *, int)); 50060484Sobrienstatic void insert_reg PARAMS ((int)); 50160484Sobrienstatic void thumb_shift PARAMS ((char *, int)); 50260484Sobrienstatic void thumb_mov_compare PARAMS ((char *, int)); 50360484Sobrienstatic void set_constant_flonums PARAMS ((void)); 50460484Sobrienstatic valueT md_chars_to_number PARAMS ((char *, int)); 50560484Sobrienstatic void insert_reg_alias PARAMS ((char *, int)); 50660484Sobrienstatic void output_inst PARAMS ((void)); 50760484Sobrien#ifdef OBJ_ELF 50860484Sobrienstatic bfd_reloc_code_real_type arm_parse_reloc PARAMS ((void)); 50960484Sobrien#endif 51060484Sobrien 51160484Sobrien/* ARM instructions take 4bytes in the object file, Thumb instructions 51260484Sobrien take 2: */ 51360484Sobrien#define INSN_SIZE 4 51460484Sobrien 51560484Sobrien/* LONGEST_INST is the longest basic instruction name without conditions or 51660484Sobrien flags. ARM7M has 4 of length 5. */ 51760484Sobrien 51860484Sobrien#define LONGEST_INST 5 51960484Sobrien 52060484Sobrien 52160484Sobrienstruct asm_opcode 52260484Sobrien{ 52360484Sobrien CONST char * template; /* Basic string to match */ 52460484Sobrien unsigned long value; /* Basic instruction code */ 52560484Sobrien 52660484Sobrien /* Compulsory suffix that must follow conds. If "", then the 52760484Sobrien instruction is not conditional and must have no suffix. */ 52860484Sobrien CONST char * comp_suffix; 52960484Sobrien 53060484Sobrien CONST struct asm_flg * flags; /* Bits to toggle if flag 'n' set */ 53160484Sobrien unsigned long variants; /* Which CPU variants this exists for */ 53260484Sobrien /* Function to call to parse args */ 53360484Sobrien void (* parms) PARAMS ((char *, unsigned long)); 53460484Sobrien}; 53560484Sobrien 53660484Sobrienstatic CONST struct asm_opcode insns[] = 53760484Sobrien{ 53860484Sobrien/* ARM Instructions */ 53960484Sobrien {"and", 0x00000000, NULL, s_flag, ARM_ANY, do_arit}, 54060484Sobrien {"eor", 0x00200000, NULL, s_flag, ARM_ANY, do_arit}, 54160484Sobrien {"sub", 0x00400000, NULL, s_flag, ARM_ANY, do_arit}, 54260484Sobrien {"rsb", 0x00600000, NULL, s_flag, ARM_ANY, do_arit}, 54360484Sobrien {"add", 0x00800000, NULL, s_flag, ARM_ANY, do_arit}, 54460484Sobrien {"adc", 0x00a00000, NULL, s_flag, ARM_ANY, do_arit}, 54560484Sobrien {"sbc", 0x00c00000, NULL, s_flag, ARM_ANY, do_arit}, 54660484Sobrien {"rsc", 0x00e00000, NULL, s_flag, ARM_ANY, do_arit}, 54760484Sobrien {"orr", 0x01800000, NULL, s_flag, ARM_ANY, do_arit}, 54860484Sobrien {"bic", 0x01c00000, NULL, s_flag, ARM_ANY, do_arit}, 54960484Sobrien {"tst", 0x01000000, NULL, cmp_flags, ARM_ANY, do_cmp}, 55060484Sobrien {"teq", 0x01200000, NULL, cmp_flags, ARM_ANY, do_cmp}, 55160484Sobrien {"cmp", 0x01400000, NULL, cmp_flags, ARM_ANY, do_cmp}, 55260484Sobrien {"cmn", 0x01600000, NULL, cmp_flags, ARM_ANY, do_cmp}, 55360484Sobrien {"mov", 0x01a00000, NULL, s_flag, ARM_ANY, do_mov}, 55460484Sobrien {"mvn", 0x01e00000, NULL, s_flag, ARM_ANY, do_mov}, 55560484Sobrien {"str", 0x04000000, NULL, str_flags, ARM_ANY, do_ldst}, 55660484Sobrien {"ldr", 0x04100000, NULL, ldr_flags, ARM_ANY, do_ldst}, 55760484Sobrien {"stm", 0x08000000, NULL, stm_flags, ARM_ANY, do_ldmstm}, 55860484Sobrien {"ldm", 0x08100000, NULL, ldm_flags, ARM_ANY, do_ldmstm}, 55960484Sobrien {"swi", 0x0f000000, NULL, NULL, ARM_ANY, do_swi}, 56060484Sobrien#ifdef TE_WINCE 56160484Sobrien {"bl", 0x0b000000, NULL, NULL, ARM_ANY, do_branch}, 56260484Sobrien {"b", 0x0a000000, NULL, NULL, ARM_ANY, do_branch}, 56360484Sobrien#else 56460484Sobrien {"bl", 0x0bfffffe, NULL, NULL, ARM_ANY, do_branch}, 56560484Sobrien {"b", 0x0afffffe, NULL, NULL, ARM_ANY, do_branch}, 56660484Sobrien#endif 56760484Sobrien 56860484Sobrien/* Pseudo ops */ 56960484Sobrien {"adr", 0x028f0000, NULL, NULL, ARM_ANY, do_adr}, 57060484Sobrien {"adrl", 0x028f0000, NULL, NULL, ARM_ANY, do_adrl}, 57160484Sobrien {"nop", 0x01a00000, NULL, NULL, ARM_ANY, do_nop}, 57260484Sobrien 57360484Sobrien/* ARM 2 multiplies */ 57460484Sobrien {"mul", 0x00000090, NULL, s_flag, ARM_2UP, do_mul}, 57560484Sobrien {"mla", 0x00200090, NULL, s_flag, ARM_2UP, do_mla}, 57660484Sobrien 57760484Sobrien/* ARM 3 - swp instructions */ 57860484Sobrien {"swp", 0x01000090, NULL, byte_flag, ARM_3UP, do_swap}, 57960484Sobrien 58060484Sobrien/* ARM 6 Coprocessor instructions */ 58160484Sobrien {"mrs", 0x010f0000, NULL, NULL, ARM_6UP, do_mrs}, 58260484Sobrien {"msr", 0x0120f000, NULL, NULL, ARM_6UP, do_msr}, 58360484Sobrien/* ScottB: our code uses 0x0128f000 for msr. 58461843Sobrien NickC: but this is wrong because the bits 16 through 19 are 58561843Sobrien handled by the PSR_xxx defines above. */ 58660484Sobrien 58760484Sobrien/* ARM 7M long multiplies - need signed/unsigned flags! */ 58860484Sobrien {"smull", 0x00c00090, NULL, s_flag, ARM_LONGMUL, do_mull}, 58960484Sobrien {"umull", 0x00800090, NULL, s_flag, ARM_LONGMUL, do_mull}, 59060484Sobrien {"smlal", 0x00e00090, NULL, s_flag, ARM_LONGMUL, do_mull}, 59160484Sobrien {"umlal", 0x00a00090, NULL, s_flag, ARM_LONGMUL, do_mull}, 59260484Sobrien 59360484Sobrien/* ARM THUMB interworking */ 59460484Sobrien {"bx", 0x012fff10, NULL, NULL, ARM_THUMB, do_bx}, 59560484Sobrien 59660484Sobrien/* Floating point instructions */ 59760484Sobrien {"wfs", 0x0e200110, NULL, NULL, FPU_ALL, do_fp_ctrl}, 59860484Sobrien {"rfs", 0x0e300110, NULL, NULL, FPU_ALL, do_fp_ctrl}, 59960484Sobrien {"wfc", 0x0e400110, NULL, NULL, FPU_ALL, do_fp_ctrl}, 60060484Sobrien {"rfc", 0x0e500110, NULL, NULL, FPU_ALL, do_fp_ctrl}, 60160484Sobrien {"ldf", 0x0c100100, "sdep", NULL, FPU_ALL, do_fp_ldst}, 60260484Sobrien {"stf", 0x0c000100, "sdep", NULL, FPU_ALL, do_fp_ldst}, 60360484Sobrien {"lfm", 0x0c100200, NULL, lfm_flags, FPU_MEMMULTI, do_fp_ldmstm}, 60460484Sobrien {"sfm", 0x0c000200, NULL, sfm_flags, FPU_MEMMULTI, do_fp_ldmstm}, 60560484Sobrien {"mvf", 0x0e008100, "sde", round_flags, FPU_ALL, do_fp_monadic}, 60660484Sobrien {"mnf", 0x0e108100, "sde", round_flags, FPU_ALL, do_fp_monadic}, 60760484Sobrien {"abs", 0x0e208100, "sde", round_flags, FPU_ALL, do_fp_monadic}, 60860484Sobrien {"rnd", 0x0e308100, "sde", round_flags, FPU_ALL, do_fp_monadic}, 60960484Sobrien {"sqt", 0x0e408100, "sde", round_flags, FPU_ALL, do_fp_monadic}, 61060484Sobrien {"log", 0x0e508100, "sde", round_flags, FPU_ALL, do_fp_monadic}, 61160484Sobrien {"lgn", 0x0e608100, "sde", round_flags, FPU_ALL, do_fp_monadic}, 61260484Sobrien {"exp", 0x0e708100, "sde", round_flags, FPU_ALL, do_fp_monadic}, 61360484Sobrien {"sin", 0x0e808100, "sde", round_flags, FPU_ALL, do_fp_monadic}, 61460484Sobrien {"cos", 0x0e908100, "sde", round_flags, FPU_ALL, do_fp_monadic}, 61560484Sobrien {"tan", 0x0ea08100, "sde", round_flags, FPU_ALL, do_fp_monadic}, 61660484Sobrien {"asn", 0x0eb08100, "sde", round_flags, FPU_ALL, do_fp_monadic}, 61760484Sobrien {"acs", 0x0ec08100, "sde", round_flags, FPU_ALL, do_fp_monadic}, 61860484Sobrien {"atn", 0x0ed08100, "sde", round_flags, FPU_ALL, do_fp_monadic}, 61960484Sobrien {"urd", 0x0ee08100, "sde", round_flags, FPU_ALL, do_fp_monadic}, 62060484Sobrien {"nrm", 0x0ef08100, "sde", round_flags, FPU_ALL, do_fp_monadic}, 62160484Sobrien {"adf", 0x0e000100, "sde", round_flags, FPU_ALL, do_fp_dyadic}, 62260484Sobrien {"suf", 0x0e200100, "sde", round_flags, FPU_ALL, do_fp_dyadic}, 62360484Sobrien {"rsf", 0x0e300100, "sde", round_flags, FPU_ALL, do_fp_dyadic}, 62460484Sobrien {"muf", 0x0e100100, "sde", round_flags, FPU_ALL, do_fp_dyadic}, 62560484Sobrien {"dvf", 0x0e400100, "sde", round_flags, FPU_ALL, do_fp_dyadic}, 62660484Sobrien {"rdf", 0x0e500100, "sde", round_flags, FPU_ALL, do_fp_dyadic}, 62760484Sobrien {"pow", 0x0e600100, "sde", round_flags, FPU_ALL, do_fp_dyadic}, 62860484Sobrien {"rpw", 0x0e700100, "sde", round_flags, FPU_ALL, do_fp_dyadic}, 62960484Sobrien {"rmf", 0x0e800100, "sde", round_flags, FPU_ALL, do_fp_dyadic}, 63060484Sobrien {"fml", 0x0e900100, "sde", round_flags, FPU_ALL, do_fp_dyadic}, 63160484Sobrien {"fdv", 0x0ea00100, "sde", round_flags, FPU_ALL, do_fp_dyadic}, 63260484Sobrien {"frd", 0x0eb00100, "sde", round_flags, FPU_ALL, do_fp_dyadic}, 63360484Sobrien {"pol", 0x0ec00100, "sde", round_flags, FPU_ALL, do_fp_dyadic}, 63460484Sobrien {"cmf", 0x0e90f110, NULL, except_flag, FPU_ALL, do_fp_cmp}, 63560484Sobrien {"cnf", 0x0eb0f110, NULL, except_flag, FPU_ALL, do_fp_cmp}, 63660484Sobrien/* The FPA10 data sheet suggests that the 'E' of cmfe/cnfe should not 63760484Sobrien be an optional suffix, but part of the instruction. To be compatible, 63860484Sobrien we accept either. */ 63960484Sobrien {"cmfe", 0x0ed0f110, NULL, NULL, FPU_ALL, do_fp_cmp}, 64060484Sobrien {"cnfe", 0x0ef0f110, NULL, NULL, FPU_ALL, do_fp_cmp}, 64160484Sobrien {"flt", 0x0e000110, "sde", round_flags, FPU_ALL, do_fp_from_reg}, 64260484Sobrien {"fix", 0x0e100110, NULL, fix_flags, FPU_ALL, do_fp_to_reg}, 64360484Sobrien 64460484Sobrien/* Generic copressor instructions. */ 64560484Sobrien {"cdp", 0x0e000000, NULL, NULL, ARM_2UP, do_cdp}, 64660484Sobrien {"ldc", 0x0c100000, NULL, cplong_flag, ARM_2UP, do_lstc}, 64760484Sobrien {"stc", 0x0c000000, NULL, cplong_flag, ARM_2UP, do_lstc}, 64860484Sobrien {"mcr", 0x0e000010, NULL, NULL, ARM_2UP, do_co_reg}, 64960484Sobrien {"mrc", 0x0e100010, NULL, NULL, ARM_2UP, do_co_reg}, 65060484Sobrien}; 65160484Sobrien 65260484Sobrien/* Defines for various bits that we will want to toggle. */ 65360484Sobrien#define INST_IMMEDIATE 0x02000000 65460484Sobrien#define OFFSET_REG 0x02000000 65560484Sobrien#define HWOFFSET_IMM 0x00400000 65660484Sobrien#define SHIFT_BY_REG 0x00000010 65760484Sobrien#define PRE_INDEX 0x01000000 65860484Sobrien#define INDEX_UP 0x00800000 65960484Sobrien#define WRITE_BACK 0x00200000 66060484Sobrien#define LDM_TYPE_2_OR_3 0x00400000 66160484Sobrien 66260484Sobrien#define LITERAL_MASK 0xf000f000 66360484Sobrien#define COND_MASK 0xf0000000 66460484Sobrien#define OPCODE_MASK 0xfe1fffff 66560484Sobrien#define DATA_OP_SHIFT 21 66660484Sobrien 66760484Sobrien/* Codes to distinguish the arithmetic instructions. */ 66860484Sobrien#define OPCODE_AND 0 66960484Sobrien#define OPCODE_EOR 1 67060484Sobrien#define OPCODE_SUB 2 67160484Sobrien#define OPCODE_RSB 3 67260484Sobrien#define OPCODE_ADD 4 67360484Sobrien#define OPCODE_ADC 5 67460484Sobrien#define OPCODE_SBC 6 67560484Sobrien#define OPCODE_RSC 7 67660484Sobrien#define OPCODE_TST 8 67760484Sobrien#define OPCODE_TEQ 9 67860484Sobrien#define OPCODE_CMP 10 67960484Sobrien#define OPCODE_CMN 11 68060484Sobrien#define OPCODE_ORR 12 68160484Sobrien#define OPCODE_MOV 13 68260484Sobrien#define OPCODE_BIC 14 68360484Sobrien#define OPCODE_MVN 15 68460484Sobrien 68560484Sobrienstatic void do_t_nop PARAMS ((char *)); 68660484Sobrienstatic void do_t_arit PARAMS ((char *)); 68760484Sobrienstatic void do_t_add PARAMS ((char *)); 68860484Sobrienstatic void do_t_asr PARAMS ((char *)); 68960484Sobrienstatic void do_t_branch9 PARAMS ((char *)); 69060484Sobrienstatic void do_t_branch12 PARAMS ((char *)); 69160484Sobrienstatic void do_t_branch23 PARAMS ((char *)); 69260484Sobrienstatic void do_t_bx PARAMS ((char *)); 69360484Sobrienstatic void do_t_compare PARAMS ((char *)); 69460484Sobrienstatic void do_t_ldmstm PARAMS ((char *)); 69560484Sobrienstatic void do_t_ldr PARAMS ((char *)); 69660484Sobrienstatic void do_t_ldrb PARAMS ((char *)); 69760484Sobrienstatic void do_t_ldrh PARAMS ((char *)); 69860484Sobrienstatic void do_t_lds PARAMS ((char *)); 69960484Sobrienstatic void do_t_lsl PARAMS ((char *)); 70060484Sobrienstatic void do_t_lsr PARAMS ((char *)); 70160484Sobrienstatic void do_t_mov PARAMS ((char *)); 70260484Sobrienstatic void do_t_push_pop PARAMS ((char *)); 70360484Sobrienstatic void do_t_str PARAMS ((char *)); 70460484Sobrienstatic void do_t_strb PARAMS ((char *)); 70560484Sobrienstatic void do_t_strh PARAMS ((char *)); 70660484Sobrienstatic void do_t_sub PARAMS ((char *)); 70760484Sobrienstatic void do_t_swi PARAMS ((char *)); 70860484Sobrienstatic void do_t_adr PARAMS ((char *)); 70960484Sobrien 71060484Sobrien#define T_OPCODE_MUL 0x4340 71160484Sobrien#define T_OPCODE_TST 0x4200 71260484Sobrien#define T_OPCODE_CMN 0x42c0 71360484Sobrien#define T_OPCODE_NEG 0x4240 71460484Sobrien#define T_OPCODE_MVN 0x43c0 71560484Sobrien 71660484Sobrien#define T_OPCODE_ADD_R3 0x1800 71760484Sobrien#define T_OPCODE_SUB_R3 0x1a00 71860484Sobrien#define T_OPCODE_ADD_HI 0x4400 71960484Sobrien#define T_OPCODE_ADD_ST 0xb000 72060484Sobrien#define T_OPCODE_SUB_ST 0xb080 72160484Sobrien#define T_OPCODE_ADD_SP 0xa800 72260484Sobrien#define T_OPCODE_ADD_PC 0xa000 72360484Sobrien#define T_OPCODE_ADD_I8 0x3000 72460484Sobrien#define T_OPCODE_SUB_I8 0x3800 72560484Sobrien#define T_OPCODE_ADD_I3 0x1c00 72660484Sobrien#define T_OPCODE_SUB_I3 0x1e00 72760484Sobrien 72860484Sobrien#define T_OPCODE_ASR_R 0x4100 72960484Sobrien#define T_OPCODE_LSL_R 0x4080 73060484Sobrien#define T_OPCODE_LSR_R 0x40c0 73160484Sobrien#define T_OPCODE_ASR_I 0x1000 73260484Sobrien#define T_OPCODE_LSL_I 0x0000 73360484Sobrien#define T_OPCODE_LSR_I 0x0800 73460484Sobrien 73560484Sobrien#define T_OPCODE_MOV_I8 0x2000 73660484Sobrien#define T_OPCODE_CMP_I8 0x2800 73760484Sobrien#define T_OPCODE_CMP_LR 0x4280 73860484Sobrien#define T_OPCODE_MOV_HR 0x4600 73960484Sobrien#define T_OPCODE_CMP_HR 0x4500 74060484Sobrien 74160484Sobrien#define T_OPCODE_LDR_PC 0x4800 74260484Sobrien#define T_OPCODE_LDR_SP 0x9800 74360484Sobrien#define T_OPCODE_STR_SP 0x9000 74460484Sobrien#define T_OPCODE_LDR_IW 0x6800 74560484Sobrien#define T_OPCODE_STR_IW 0x6000 74660484Sobrien#define T_OPCODE_LDR_IH 0x8800 74760484Sobrien#define T_OPCODE_STR_IH 0x8000 74860484Sobrien#define T_OPCODE_LDR_IB 0x7800 74960484Sobrien#define T_OPCODE_STR_IB 0x7000 75060484Sobrien#define T_OPCODE_LDR_RW 0x5800 75160484Sobrien#define T_OPCODE_STR_RW 0x5000 75260484Sobrien#define T_OPCODE_LDR_RH 0x5a00 75360484Sobrien#define T_OPCODE_STR_RH 0x5200 75460484Sobrien#define T_OPCODE_LDR_RB 0x5c00 75560484Sobrien#define T_OPCODE_STR_RB 0x5400 75660484Sobrien 75760484Sobrien#define T_OPCODE_PUSH 0xb400 75860484Sobrien#define T_OPCODE_POP 0xbc00 75960484Sobrien 76060484Sobrien#define T_OPCODE_BRANCH 0xe7fe 76160484Sobrien 76260484Sobrienstatic int thumb_reg PARAMS ((char ** str, int hi_lo)); 76360484Sobrien 76460484Sobrien#define THUMB_SIZE 2 /* Size of thumb instruction. */ 76560484Sobrien#define THUMB_REG_LO 0x1 76660484Sobrien#define THUMB_REG_HI 0x2 76760484Sobrien#define THUMB_REG_ANY 0x3 76860484Sobrien 76960484Sobrien#define THUMB_H1 0x0080 77060484Sobrien#define THUMB_H2 0x0040 77160484Sobrien 77260484Sobrien#define THUMB_ASR 0 77360484Sobrien#define THUMB_LSL 1 77460484Sobrien#define THUMB_LSR 2 77560484Sobrien 77660484Sobrien#define THUMB_MOVE 0 77760484Sobrien#define THUMB_COMPARE 1 77860484Sobrien 77960484Sobrien#define THUMB_LOAD 0 78060484Sobrien#define THUMB_STORE 1 78160484Sobrien 78260484Sobrien#define THUMB_PP_PC_LR 0x0100 78360484Sobrien 78460484Sobrien/* These three are used for immediate shifts, do not alter. */ 78560484Sobrien#define THUMB_WORD 2 78660484Sobrien#define THUMB_HALFWORD 1 78760484Sobrien#define THUMB_BYTE 0 78860484Sobrien 78960484Sobrienstruct thumb_opcode 79060484Sobrien{ 79160484Sobrien CONST char * template; /* Basic string to match */ 79260484Sobrien unsigned long value; /* Basic instruction code */ 79360484Sobrien int size; 79460484Sobrien unsigned long variants; /* Which CPU variants this exists for */ 79560484Sobrien void (* parms) PARAMS ((char *)); /* Function to call to parse args */ 79660484Sobrien}; 79760484Sobrien 79860484Sobrienstatic CONST struct thumb_opcode tinsns[] = 79960484Sobrien{ 80060484Sobrien {"adc", 0x4140, 2, ARM_THUMB, do_t_arit}, 80160484Sobrien {"add", 0x0000, 2, ARM_THUMB, do_t_add}, 80260484Sobrien {"and", 0x4000, 2, ARM_THUMB, do_t_arit}, 80360484Sobrien {"asr", 0x0000, 2, ARM_THUMB, do_t_asr}, 80460484Sobrien {"b", T_OPCODE_BRANCH, 2, ARM_THUMB, do_t_branch12}, 80560484Sobrien {"beq", 0xd0fe, 2, ARM_THUMB, do_t_branch9}, 80660484Sobrien {"bne", 0xd1fe, 2, ARM_THUMB, do_t_branch9}, 80760484Sobrien {"bcs", 0xd2fe, 2, ARM_THUMB, do_t_branch9}, 80860484Sobrien {"bhs", 0xd2fe, 2, ARM_THUMB, do_t_branch9}, 80960484Sobrien {"bcc", 0xd3fe, 2, ARM_THUMB, do_t_branch9}, 81060484Sobrien {"bul", 0xd3fe, 2, ARM_THUMB, do_t_branch9}, 81160484Sobrien {"blo", 0xd3fe, 2, ARM_THUMB, do_t_branch9}, 81260484Sobrien {"bmi", 0xd4fe, 2, ARM_THUMB, do_t_branch9}, 81360484Sobrien {"bpl", 0xd5fe, 2, ARM_THUMB, do_t_branch9}, 81460484Sobrien {"bvs", 0xd6fe, 2, ARM_THUMB, do_t_branch9}, 81560484Sobrien {"bvc", 0xd7fe, 2, ARM_THUMB, do_t_branch9}, 81660484Sobrien {"bhi", 0xd8fe, 2, ARM_THUMB, do_t_branch9}, 81760484Sobrien {"bls", 0xd9fe, 2, ARM_THUMB, do_t_branch9}, 81860484Sobrien {"bge", 0xdafe, 2, ARM_THUMB, do_t_branch9}, 81960484Sobrien {"blt", 0xdbfe, 2, ARM_THUMB, do_t_branch9}, 82060484Sobrien {"bgt", 0xdcfe, 2, ARM_THUMB, do_t_branch9}, 82160484Sobrien {"ble", 0xddfe, 2, ARM_THUMB, do_t_branch9}, 82260484Sobrien {"bal", 0xdefe, 2, ARM_THUMB, do_t_branch9}, 82360484Sobrien {"bic", 0x4380, 2, ARM_THUMB, do_t_arit}, 82460484Sobrien {"bl", 0xf7fffffe, 4, ARM_THUMB, do_t_branch23}, 82560484Sobrien {"bx", 0x4700, 2, ARM_THUMB, do_t_bx}, 82660484Sobrien {"cmn", T_OPCODE_CMN, 2, ARM_THUMB, do_t_arit}, 82760484Sobrien {"cmp", 0x0000, 2, ARM_THUMB, do_t_compare}, 82860484Sobrien {"eor", 0x4040, 2, ARM_THUMB, do_t_arit}, 82960484Sobrien {"ldmia", 0xc800, 2, ARM_THUMB, do_t_ldmstm}, 83060484Sobrien {"ldr", 0x0000, 2, ARM_THUMB, do_t_ldr}, 83160484Sobrien {"ldrb", 0x0000, 2, ARM_THUMB, do_t_ldrb}, 83260484Sobrien {"ldrh", 0x0000, 2, ARM_THUMB, do_t_ldrh}, 83360484Sobrien {"ldrsb", 0x5600, 2, ARM_THUMB, do_t_lds}, 83460484Sobrien {"ldrsh", 0x5e00, 2, ARM_THUMB, do_t_lds}, 83560484Sobrien {"ldsb", 0x5600, 2, ARM_THUMB, do_t_lds}, 83660484Sobrien {"ldsh", 0x5e00, 2, ARM_THUMB, do_t_lds}, 83760484Sobrien {"lsl", 0x0000, 2, ARM_THUMB, do_t_lsl}, 83860484Sobrien {"lsr", 0x0000, 2, ARM_THUMB, do_t_lsr}, 83960484Sobrien {"mov", 0x0000, 2, ARM_THUMB, do_t_mov}, 84060484Sobrien {"mul", T_OPCODE_MUL, 2, ARM_THUMB, do_t_arit}, 84160484Sobrien {"mvn", T_OPCODE_MVN, 2, ARM_THUMB, do_t_arit}, 84260484Sobrien {"neg", T_OPCODE_NEG, 2, ARM_THUMB, do_t_arit}, 84360484Sobrien {"orr", 0x4300, 2, ARM_THUMB, do_t_arit}, 84460484Sobrien {"pop", 0xbc00, 2, ARM_THUMB, do_t_push_pop}, 84560484Sobrien {"push", 0xb400, 2, ARM_THUMB, do_t_push_pop}, 84660484Sobrien {"ror", 0x41c0, 2, ARM_THUMB, do_t_arit}, 84760484Sobrien {"sbc", 0x4180, 2, ARM_THUMB, do_t_arit}, 84860484Sobrien {"stmia", 0xc000, 2, ARM_THUMB, do_t_ldmstm}, 84960484Sobrien {"str", 0x0000, 2, ARM_THUMB, do_t_str}, 85060484Sobrien {"strb", 0x0000, 2, ARM_THUMB, do_t_strb}, 85160484Sobrien {"strh", 0x0000, 2, ARM_THUMB, do_t_strh}, 85260484Sobrien {"swi", 0xdf00, 2, ARM_THUMB, do_t_swi}, 85360484Sobrien {"sub", 0x0000, 2, ARM_THUMB, do_t_sub}, 85460484Sobrien {"tst", T_OPCODE_TST, 2, ARM_THUMB, do_t_arit}, 85560484Sobrien /* Pseudo ops: */ 85660484Sobrien {"adr", 0x0000, 2, ARM_THUMB, do_t_adr}, 85760484Sobrien {"nop", 0x46C0, 2, ARM_THUMB, do_t_nop}, /* mov r8,r8 */ 85860484Sobrien}; 85960484Sobrien 86060484Sobrienstruct reg_entry 86160484Sobrien{ 86260484Sobrien CONST char * name; 86360484Sobrien int number; 86460484Sobrien}; 86560484Sobrien 86660484Sobrien#define int_register(reg) ((reg) >= 0 && (reg) <= 15) 86760484Sobrien#define cp_register(reg) ((reg) >= 32 && (reg) <= 47) 86860484Sobrien#define fp_register(reg) ((reg) >= 16 && (reg) <= 23) 86960484Sobrien 87060484Sobrien#define REG_PC 15 87160484Sobrien#define REG_LR 14 87260484Sobrien#define REG_SP 13 87360484Sobrien 87460484Sobrien/* These are the standard names. Users can add aliases with .req */ 87560484Sobrienstatic CONST struct reg_entry reg_table[] = 87660484Sobrien{ 87760484Sobrien /* Processor Register Numbers. */ 87860484Sobrien {"r0", 0}, {"r1", 1}, {"r2", 2}, {"r3", 3}, 87960484Sobrien {"r4", 4}, {"r5", 5}, {"r6", 6}, {"r7", 7}, 88060484Sobrien {"r8", 8}, {"r9", 9}, {"r10", 10}, {"r11", 11}, 88160484Sobrien {"r12", 12}, {"r13", REG_SP},{"r14", REG_LR},{"r15", REG_PC}, 88260484Sobrien /* APCS conventions. */ 88360484Sobrien {"a1", 0}, {"a2", 1}, {"a3", 2}, {"a4", 3}, 88460484Sobrien {"v1", 4}, {"v2", 5}, {"v3", 6}, {"v4", 7}, {"v5", 8}, 88560484Sobrien {"v6", 9}, {"sb", 9}, {"v7", 10}, {"sl", 10}, 88660484Sobrien {"fp", 11}, {"ip", 12}, {"sp", REG_SP},{"lr", REG_LR},{"pc", REG_PC}, 88760484Sobrien /* ATPCS additions to APCS conventions. */ 88860484Sobrien {"wr", 7}, {"v8", 11}, 88960484Sobrien /* FP Registers. */ 89060484Sobrien {"f0", 16}, {"f1", 17}, {"f2", 18}, {"f3", 19}, 89160484Sobrien {"f4", 20}, {"f5", 21}, {"f6", 22}, {"f7", 23}, 89260484Sobrien {"c0", 32}, {"c1", 33}, {"c2", 34}, {"c3", 35}, 89360484Sobrien {"c4", 36}, {"c5", 37}, {"c6", 38}, {"c7", 39}, 89460484Sobrien {"c8", 40}, {"c9", 41}, {"c10", 42}, {"c11", 43}, 89560484Sobrien {"c12", 44}, {"c13", 45}, {"c14", 46}, {"c15", 47}, 89660484Sobrien {"cr0", 32}, {"cr1", 33}, {"cr2", 34}, {"cr3", 35}, 89760484Sobrien {"cr4", 36}, {"cr5", 37}, {"cr6", 38}, {"cr7", 39}, 89860484Sobrien {"cr8", 40}, {"cr9", 41}, {"cr10", 42}, {"cr11", 43}, 89960484Sobrien {"cr12", 44}, {"cr13", 45}, {"cr14", 46}, {"cr15", 47}, 90060484Sobrien /* ATPCS additions to float register names. */ 90160484Sobrien {"s0",16}, {"s1",17}, {"s2",18}, {"s3",19}, 90260484Sobrien {"s4",20}, {"s5",21}, {"s6",22}, {"s7",23}, 90360484Sobrien {"d0",16}, {"d1",17}, {"d2",18}, {"d3",19}, 90460484Sobrien {"d4",20}, {"d5",21}, {"d6",22}, {"d7",23}, 90560484Sobrien /* FIXME: At some point we need to add VFP register names. */ 90660484Sobrien /* Array terminator. */ 90760484Sobrien {NULL, 0} 90860484Sobrien}; 90960484Sobrien 91060484Sobrien#define BAD_ARGS _("Bad arguments to instruction") 91160484Sobrien#define BAD_PC _("r15 not allowed here") 91260484Sobrien#define BAD_FLAGS _("Instruction should not have flags") 91360484Sobrien#define BAD_COND _("Instruction is not conditional") 91460484Sobrien 91560484Sobrienstatic struct hash_control * arm_ops_hsh = NULL; 91660484Sobrienstatic struct hash_control * arm_tops_hsh = NULL; 91760484Sobrienstatic struct hash_control * arm_cond_hsh = NULL; 91860484Sobrienstatic struct hash_control * arm_shift_hsh = NULL; 91960484Sobrienstatic struct hash_control * arm_reg_hsh = NULL; 92060484Sobrienstatic struct hash_control * arm_psr_hsh = NULL; 92160484Sobrien 92260484Sobrien/* This table describes all the machine specific pseudo-ops the assembler 92360484Sobrien has to support. The fields are: 92460484Sobrien pseudo-op name without dot 92560484Sobrien function to call to execute this pseudo-op 92660484Sobrien Integer arg to pass to the function. */ 92760484Sobrien 92860484Sobrienstatic void s_req PARAMS ((int)); 92960484Sobrienstatic void s_align PARAMS ((int)); 93060484Sobrienstatic void s_bss PARAMS ((int)); 93160484Sobrienstatic void s_even PARAMS ((int)); 93260484Sobrienstatic void s_ltorg PARAMS ((int)); 93360484Sobrienstatic void s_arm PARAMS ((int)); 93460484Sobrienstatic void s_thumb PARAMS ((int)); 93560484Sobrienstatic void s_code PARAMS ((int)); 93660484Sobrienstatic void s_force_thumb PARAMS ((int)); 93760484Sobrienstatic void s_thumb_func PARAMS ((int)); 93860484Sobrienstatic void s_thumb_set PARAMS ((int)); 93960484Sobrienstatic void arm_s_text PARAMS ((int)); 94060484Sobrienstatic void arm_s_data PARAMS ((int)); 94160484Sobrien#ifdef OBJ_ELF 94260484Sobrienstatic void arm_s_section PARAMS ((int)); 94360484Sobrienstatic void s_arm_elf_cons PARAMS ((int)); 94460484Sobrien#endif 94560484Sobrien 94660484Sobrienstatic int my_get_expression PARAMS ((expressionS *, char **)); 94760484Sobrien 94860484SobrienCONST pseudo_typeS md_pseudo_table[] = 94960484Sobrien{ 95060484Sobrien { "req", s_req, 0 }, /* Never called becasue '.req' does not start line */ 95160484Sobrien { "bss", s_bss, 0 }, 95260484Sobrien { "align", s_align, 0 }, 95360484Sobrien { "arm", s_arm, 0 }, 95460484Sobrien { "thumb", s_thumb, 0 }, 95560484Sobrien { "code", s_code, 0 }, 95660484Sobrien { "force_thumb", s_force_thumb, 0 }, 95760484Sobrien { "thumb_func", s_thumb_func, 0 }, 95860484Sobrien { "thumb_set", s_thumb_set, 0 }, 95960484Sobrien { "even", s_even, 0 }, 96060484Sobrien { "ltorg", s_ltorg, 0 }, 96160484Sobrien { "pool", s_ltorg, 0 }, 96260484Sobrien /* Allow for the effect of section changes. */ 96360484Sobrien { "text", arm_s_text, 0 }, 96460484Sobrien { "data", arm_s_data, 0 }, 96560484Sobrien#ifdef OBJ_ELF 96660484Sobrien { "section", arm_s_section, 0 }, 96760484Sobrien { "section.s", arm_s_section, 0 }, 96860484Sobrien { "sect", arm_s_section, 0 }, 96960484Sobrien { "sect.s", arm_s_section, 0 }, 97060484Sobrien { "word", s_arm_elf_cons, 4 }, 97160484Sobrien { "long", s_arm_elf_cons, 4 }, 97260484Sobrien#else 97360484Sobrien { "word", cons, 4}, 97460484Sobrien#endif 97560484Sobrien { "extend", float_cons, 'x' }, 97660484Sobrien { "ldouble", float_cons, 'x' }, 97760484Sobrien { "packed", float_cons, 'p' }, 97860484Sobrien { 0, 0, 0 } 97960484Sobrien}; 98060484Sobrien 98160484Sobrien/* Stuff needed to resolve the label ambiguity 98260484Sobrien As: 98360484Sobrien ... 98460484Sobrien label: <insn> 98560484Sobrien may differ from: 98660484Sobrien ... 98760484Sobrien label: 98860484Sobrien <insn> 98960484Sobrien*/ 99060484Sobrien 99160484SobriensymbolS * last_label_seen; 99260484Sobrienstatic int label_is_thumb_function_name = false; 99360484Sobrien 99460484Sobrien/* Literal stuff */ 99560484Sobrien 99660484Sobrien#define MAX_LITERAL_POOL_SIZE 1024 99760484Sobrien 99860484Sobrientypedef struct literalS 99960484Sobrien{ 100060484Sobrien struct expressionS exp; 100160484Sobrien struct arm_it * inst; 100260484Sobrien} literalT; 100360484Sobrien 100460484SobrienliteralT literals[MAX_LITERAL_POOL_SIZE]; 100560484Sobrienint next_literal_pool_place = 0; /* Next free entry in the pool */ 100660484Sobrienint lit_pool_num = 1; /* Next literal pool number */ 100760484SobriensymbolS * current_poolP = NULL; 100860484Sobrien 100960484Sobrienstatic int 101060484Sobrienadd_to_lit_pool () 101160484Sobrien{ 101260484Sobrien int lit_count = 0; 101360484Sobrien 101460484Sobrien if (current_poolP == NULL) 101560484Sobrien current_poolP = symbol_create (FAKE_LABEL_NAME, undefined_section, 101660484Sobrien (valueT) 0, &zero_address_frag); 101760484Sobrien 101860484Sobrien /* Check if this literal value is already in the pool: */ 101960484Sobrien while (lit_count < next_literal_pool_place) 102060484Sobrien { 102160484Sobrien if (literals[lit_count].exp.X_op == inst.reloc.exp.X_op 102260484Sobrien && inst.reloc.exp.X_op == O_constant 102360484Sobrien && literals[lit_count].exp.X_add_number 102460484Sobrien == inst.reloc.exp.X_add_number 102560484Sobrien && literals[lit_count].exp.X_unsigned == inst.reloc.exp.X_unsigned) 102660484Sobrien break; 102760484Sobrien lit_count++; 102860484Sobrien } 102960484Sobrien 103060484Sobrien if (lit_count == next_literal_pool_place) /* new entry */ 103160484Sobrien { 103260484Sobrien if (next_literal_pool_place > MAX_LITERAL_POOL_SIZE) 103360484Sobrien { 103460484Sobrien inst.error = _("Literal Pool Overflow"); 103560484Sobrien return FAIL; 103660484Sobrien } 103760484Sobrien 103860484Sobrien literals[next_literal_pool_place].exp = inst.reloc.exp; 103960484Sobrien lit_count = next_literal_pool_place++; 104060484Sobrien } 104160484Sobrien 104260484Sobrien inst.reloc.exp.X_op = O_symbol; 104360484Sobrien inst.reloc.exp.X_add_number = (lit_count) * 4 - 8; 104460484Sobrien inst.reloc.exp.X_add_symbol = current_poolP; 104560484Sobrien 104660484Sobrien return SUCCESS; 104760484Sobrien} 104860484Sobrien 104960484Sobrien/* Can't use symbol_new here, so have to create a symbol and then at 105060484Sobrien a later date assign it a value. Thats what these functions do. */ 105160484Sobrienstatic void 105260484Sobriensymbol_locate (symbolP, name, segment, valu, frag) 105360484Sobrien symbolS * symbolP; 105460484Sobrien CONST char * name; /* It is copied, the caller can modify */ 105560484Sobrien segT segment; /* Segment identifier (SEG_<something>) */ 105660484Sobrien valueT valu; /* Symbol value */ 105760484Sobrien fragS * frag; /* Associated fragment */ 105860484Sobrien{ 105960484Sobrien unsigned int name_length; 106060484Sobrien char * preserved_copy_of_name; 106160484Sobrien 106260484Sobrien name_length = strlen (name) + 1; /* +1 for \0 */ 106360484Sobrien obstack_grow (¬es, name, name_length); 106460484Sobrien preserved_copy_of_name = obstack_finish (¬es); 106560484Sobrien#ifdef STRIP_UNDERSCORE 106660484Sobrien if (preserved_copy_of_name[0] == '_') 106760484Sobrien preserved_copy_of_name++; 106860484Sobrien#endif 106960484Sobrien 107060484Sobrien#ifdef tc_canonicalize_symbol_name 107160484Sobrien preserved_copy_of_name = 107260484Sobrien tc_canonicalize_symbol_name (preserved_copy_of_name); 107360484Sobrien#endif 107460484Sobrien 107560484Sobrien S_SET_NAME (symbolP, preserved_copy_of_name); 107660484Sobrien 107760484Sobrien S_SET_SEGMENT (symbolP, segment); 107860484Sobrien S_SET_VALUE (symbolP, valu); 107960484Sobrien symbol_clear_list_pointers(symbolP); 108060484Sobrien 108160484Sobrien symbol_set_frag (symbolP, frag); 108260484Sobrien 108360484Sobrien /* Link to end of symbol chain. */ 108460484Sobrien { 108560484Sobrien extern int symbol_table_frozen; 108660484Sobrien if (symbol_table_frozen) 108760484Sobrien abort (); 108860484Sobrien } 108960484Sobrien 109060484Sobrien symbol_append (symbolP, symbol_lastP, & symbol_rootP, & symbol_lastP); 109160484Sobrien 109260484Sobrien obj_symbol_new_hook (symbolP); 109360484Sobrien 109460484Sobrien#ifdef tc_symbol_new_hook 109560484Sobrien tc_symbol_new_hook (symbolP); 109660484Sobrien#endif 109760484Sobrien 109860484Sobrien#ifdef DEBUG_SYMS 109960484Sobrien verify_symbol_chain (symbol_rootP, symbol_lastP); 110060484Sobrien#endif /* DEBUG_SYMS */ 110160484Sobrien} 110260484Sobrien 110360484Sobrien/* Check that an immediate is valid, and if so, 110460484Sobrien convert it to the right format. */ 110560484Sobrienstatic unsigned int 110660484Sobrienvalidate_immediate (val) 110760484Sobrien unsigned int val; 110860484Sobrien{ 110960484Sobrien unsigned int a; 111060484Sobrien unsigned int i; 111160484Sobrien 111260484Sobrien#define rotate_left(v, n) (v << n | v >> (32 - n)) 111360484Sobrien 111460484Sobrien for (i = 0; i < 32; i += 2) 111560484Sobrien if ((a = rotate_left (val, i)) <= 0xff) 111660484Sobrien return a | (i << 7); /* 12-bit pack: [shift-cnt,const] */ 111760484Sobrien 111860484Sobrien return FAIL; 111960484Sobrien} 112060484Sobrien 112160484Sobrien/* Check to see if an immediate can be computed as two seperate immediate 112260484Sobrien values, added together. We already know that this value cannot be 112360484Sobrien computed by just one ARM instruction. */ 112460484Sobrienstatic unsigned int 112560484Sobrienvalidate_immediate_twopart (val, highpart) 112660484Sobrien unsigned int val; 112760484Sobrien unsigned int * highpart; 112860484Sobrien{ 112960484Sobrien unsigned int a; 113060484Sobrien unsigned int i; 113160484Sobrien 113260484Sobrien for (i = 0; i < 32; i += 2) 113360484Sobrien if (((a = rotate_left (val, i)) & 0xff) != 0) 113460484Sobrien { 113560484Sobrien if (a & 0xff00) 113660484Sobrien { 113760484Sobrien if (a & ~ 0xffff) 113860484Sobrien continue; 113960484Sobrien * highpart = (a >> 8) | ((i + 24) << 7); 114060484Sobrien } 114160484Sobrien else if (a & 0xff0000) 114260484Sobrien { 114360484Sobrien if (a & 0xff000000) 114460484Sobrien continue; 114560484Sobrien 114660484Sobrien * highpart = (a >> 16) | ((i + 16) << 7); 114760484Sobrien } 114860484Sobrien else 114960484Sobrien { 115060484Sobrien assert (a & 0xff000000); 115160484Sobrien 115260484Sobrien * highpart = (a >> 24) | ((i + 8) << 7); 115360484Sobrien } 115460484Sobrien 115560484Sobrien return (a & 0xff) | (i << 7); 115660484Sobrien } 115760484Sobrien 115860484Sobrien return FAIL; 115960484Sobrien} 116060484Sobrien 116160484Sobrienstatic int 116260484Sobrienvalidate_offset_imm (val, hwse) 116360484Sobrien unsigned int val; 116460484Sobrien int hwse; 116560484Sobrien{ 116660484Sobrien if ((hwse && val > 255) || val > 4095) 116760484Sobrien return FAIL; 116860484Sobrien return val; 116960484Sobrien} 117060484Sobrien 117160484Sobrien 117260484Sobrienstatic void 117360484Sobriens_req (a) 117460484Sobrien int a ATTRIBUTE_UNUSED; 117560484Sobrien{ 117660484Sobrien as_bad (_("Invalid syntax for .req directive.")); 117760484Sobrien} 117860484Sobrien 117960484Sobrienstatic void 118060484Sobriens_bss (ignore) 118160484Sobrien int ignore ATTRIBUTE_UNUSED; 118260484Sobrien{ 118360484Sobrien /* We don't support putting frags in the BSS segment, we fake it by 118460484Sobrien marking in_bss, then looking at s_skip for clues?.. */ 118560484Sobrien subseg_set (bss_section, 0); 118660484Sobrien demand_empty_rest_of_line (); 118760484Sobrien} 118860484Sobrien 118960484Sobrienstatic void 119060484Sobriens_even (ignore) 119160484Sobrien int ignore ATTRIBUTE_UNUSED; 119260484Sobrien{ 119360484Sobrien if (!need_pass_2) /* Never make frag if expect extra pass. */ 119460484Sobrien frag_align (1, 0, 0); 119560484Sobrien 119660484Sobrien record_alignment (now_seg, 1); 119760484Sobrien 119860484Sobrien demand_empty_rest_of_line (); 119960484Sobrien} 120060484Sobrien 120160484Sobrienstatic void 120260484Sobriens_ltorg (ignored) 120360484Sobrien int ignored ATTRIBUTE_UNUSED; 120460484Sobrien{ 120560484Sobrien int lit_count = 0; 120660484Sobrien char sym_name[20]; 120760484Sobrien 120860484Sobrien if (current_poolP == NULL) 120960484Sobrien return; 121060484Sobrien 121160484Sobrien /* Align pool as you have word accesses */ 121260484Sobrien /* Only make a frag if we have to ... */ 121360484Sobrien if (!need_pass_2) 121460484Sobrien frag_align (2, 0, 0); 121560484Sobrien 121660484Sobrien record_alignment (now_seg, 2); 121760484Sobrien 121860484Sobrien sprintf (sym_name, "$$lit_\002%x", lit_pool_num++); 121960484Sobrien 122060484Sobrien symbol_locate (current_poolP, sym_name, now_seg, 122160484Sobrien (valueT) frag_now_fix (), frag_now); 122260484Sobrien symbol_table_insert (current_poolP); 122360484Sobrien 122460484Sobrien ARM_SET_THUMB (current_poolP, thumb_mode); 122560484Sobrien 122660484Sobrien#if defined OBJ_COFF || defined OBJ_ELF 122760484Sobrien ARM_SET_INTERWORK (current_poolP, support_interwork); 122860484Sobrien#endif 122960484Sobrien 123060484Sobrien while (lit_count < next_literal_pool_place) 123160484Sobrien /* First output the expression in the instruction to the pool. */ 123260484Sobrien emit_expr (&(literals[lit_count++].exp), 4); /* .word */ 123360484Sobrien 123460484Sobrien next_literal_pool_place = 0; 123560484Sobrien current_poolP = NULL; 123660484Sobrien} 123760484Sobrien 123860484Sobrienstatic void 123960484Sobriens_align (unused) /* Same as s_align_ptwo but align 0 => align 2 */ 124060484Sobrien int unused ATTRIBUTE_UNUSED; 124160484Sobrien{ 124260484Sobrien register int temp; 124360484Sobrien register long temp_fill; 124460484Sobrien long max_alignment = 15; 124560484Sobrien 124660484Sobrien temp = get_absolute_expression (); 124760484Sobrien if (temp > max_alignment) 124860484Sobrien as_bad (_("Alignment too large: %d. assumed."), temp = max_alignment); 124960484Sobrien else if (temp < 0) 125060484Sobrien { 125160484Sobrien as_bad (_("Alignment negative. 0 assumed.")); 125260484Sobrien temp = 0; 125360484Sobrien } 125460484Sobrien 125560484Sobrien if (*input_line_pointer == ',') 125660484Sobrien { 125760484Sobrien input_line_pointer++; 125860484Sobrien temp_fill = get_absolute_expression (); 125960484Sobrien } 126060484Sobrien else 126160484Sobrien temp_fill = 0; 126260484Sobrien 126360484Sobrien if (!temp) 126460484Sobrien temp = 2; 126560484Sobrien 126660484Sobrien /* Only make a frag if we HAVE to. . . */ 126760484Sobrien if (temp && !need_pass_2) 126860484Sobrien frag_align (temp, (int) temp_fill, 0); 126960484Sobrien demand_empty_rest_of_line (); 127060484Sobrien 127160484Sobrien record_alignment (now_seg, temp); 127260484Sobrien} 127360484Sobrien 127460484Sobrienstatic void 127560484Sobriens_force_thumb (ignore) 127660484Sobrien int ignore ATTRIBUTE_UNUSED; 127760484Sobrien{ 127860484Sobrien /* If we are not already in thumb mode go into it, EVEN if 127960484Sobrien the target processor does not support thumb instructions. 128060484Sobrien This is used by gcc/config/arm/lib1funcs.asm for example 128160484Sobrien to compile interworking support functions even if the 128260484Sobrien target processor should not support interworking. */ 128360484Sobrien 128460484Sobrien if (! thumb_mode) 128560484Sobrien { 128660484Sobrien thumb_mode = 2; 128760484Sobrien 128860484Sobrien record_alignment (now_seg, 1); 128960484Sobrien } 129060484Sobrien 129160484Sobrien demand_empty_rest_of_line (); 129260484Sobrien} 129360484Sobrien 129460484Sobrienstatic void 129560484Sobriens_thumb_func (ignore) 129660484Sobrien int ignore ATTRIBUTE_UNUSED; 129760484Sobrien{ 129860484Sobrien /* The following label is the name/address of the start of a Thumb function. 129960484Sobrien We need to know this for the interworking support. */ 130060484Sobrien 130160484Sobrien label_is_thumb_function_name = true; 130260484Sobrien 130360484Sobrien demand_empty_rest_of_line (); 130460484Sobrien} 130560484Sobrien 130660484Sobrien/* Perform a .set directive, but also mark the alias as 130760484Sobrien being a thumb function. */ 130860484Sobrien 130960484Sobrienstatic void 131060484Sobriens_thumb_set (equiv) 131160484Sobrien int equiv; 131260484Sobrien{ 131360484Sobrien /* XXX the following is a duplicate of the code for s_set() in read.c 131460484Sobrien We cannot just call that code as we need to get at the symbol that 131560484Sobrien is created. */ 131660484Sobrien register char * name; 131760484Sobrien register char delim; 131860484Sobrien register char * end_name; 131960484Sobrien register symbolS * symbolP; 132060484Sobrien 132160484Sobrien /* 132260484Sobrien * Especial apologies for the random logic: 132360484Sobrien * this just grew, and could be parsed much more simply! 132460484Sobrien * Dean in haste. 132560484Sobrien */ 132660484Sobrien name = input_line_pointer; 132760484Sobrien delim = get_symbol_end (); 132860484Sobrien end_name = input_line_pointer; 132960484Sobrien *end_name = delim; 133060484Sobrien 133160484Sobrien SKIP_WHITESPACE (); 133260484Sobrien 133360484Sobrien if (*input_line_pointer != ',') 133460484Sobrien { 133560484Sobrien *end_name = 0; 133660484Sobrien as_bad (_("Expected comma after name \"%s\""), name); 133760484Sobrien *end_name = delim; 133860484Sobrien ignore_rest_of_line (); 133960484Sobrien return; 134060484Sobrien } 134160484Sobrien 134260484Sobrien input_line_pointer++; 134360484Sobrien *end_name = 0; 134460484Sobrien 134560484Sobrien if (name[0] == '.' && name[1] == '\0') 134660484Sobrien { 134760484Sobrien /* XXX - this should not happen to .thumb_set */ 134860484Sobrien abort (); 134960484Sobrien } 135060484Sobrien 135160484Sobrien if ((symbolP = symbol_find (name)) == NULL 135260484Sobrien && (symbolP = md_undefined_symbol (name)) == NULL) 135360484Sobrien { 135460484Sobrien#ifndef NO_LISTING 135560484Sobrien /* When doing symbol listings, play games with dummy fragments living 135660484Sobrien outside the normal fragment chain to record the file and line info 135760484Sobrien for this symbol. */ 135860484Sobrien if (listing & LISTING_SYMBOLS) 135960484Sobrien { 136060484Sobrien extern struct list_info_struct * listing_tail; 136160484Sobrien fragS * dummy_frag = (fragS *) xmalloc (sizeof(fragS)); 136260484Sobrien memset (dummy_frag, 0, sizeof(fragS)); 136360484Sobrien dummy_frag->fr_type = rs_fill; 136460484Sobrien dummy_frag->line = listing_tail; 136560484Sobrien symbolP = symbol_new (name, undefined_section, 0, dummy_frag); 136660484Sobrien dummy_frag->fr_symbol = symbolP; 136760484Sobrien } 136860484Sobrien else 136960484Sobrien#endif 137060484Sobrien symbolP = symbol_new (name, undefined_section, 0, &zero_address_frag); 137160484Sobrien 137260484Sobrien#ifdef OBJ_COFF 137360484Sobrien /* "set" symbols are local unless otherwise specified. */ 137460484Sobrien SF_SET_LOCAL (symbolP); 137560484Sobrien#endif /* OBJ_COFF */ 137660484Sobrien } /* make a new symbol */ 137760484Sobrien 137860484Sobrien symbol_table_insert (symbolP); 137960484Sobrien 138060484Sobrien * end_name = delim; 138160484Sobrien 138260484Sobrien if (equiv 138360484Sobrien && S_IS_DEFINED (symbolP) 138460484Sobrien && S_GET_SEGMENT (symbolP) != reg_section) 138560484Sobrien as_bad (_("symbol `%s' already defined"), S_GET_NAME (symbolP)); 138660484Sobrien 138760484Sobrien pseudo_set (symbolP); 138860484Sobrien 138960484Sobrien demand_empty_rest_of_line (); 139060484Sobrien 139160484Sobrien /* XXX Now we come to the Thumb specific bit of code. */ 139260484Sobrien 139360484Sobrien THUMB_SET_FUNC (symbolP, 1); 139460484Sobrien ARM_SET_THUMB (symbolP, 1); 139560484Sobrien#if defined OBJ_ELF || defined OBJ_COFF 139660484Sobrien ARM_SET_INTERWORK (symbolP, support_interwork); 139760484Sobrien#endif 139860484Sobrien} 139960484Sobrien 140060484Sobrien/* If we change section we must dump the literal pool first. */ 140160484Sobrienstatic void 140260484Sobrienarm_s_text (ignore) 140360484Sobrien int ignore; 140460484Sobrien{ 140560484Sobrien if (now_seg != text_section) 140660484Sobrien s_ltorg (0); 140760484Sobrien 140860484Sobrien#ifdef OBJ_ELF 140960484Sobrien obj_elf_text (ignore); 141060484Sobrien#else 141160484Sobrien s_text (ignore); 141260484Sobrien#endif 141360484Sobrien} 141460484Sobrien 141560484Sobrienstatic void 141660484Sobrienarm_s_data (ignore) 141760484Sobrien int ignore; 141860484Sobrien{ 141960484Sobrien if (flag_readonly_data_in_text) 142060484Sobrien { 142160484Sobrien if (now_seg != text_section) 142260484Sobrien s_ltorg (0); 142360484Sobrien } 142460484Sobrien else if (now_seg != data_section) 142560484Sobrien s_ltorg (0); 142660484Sobrien 142760484Sobrien#ifdef OBJ_ELF 142860484Sobrien obj_elf_data (ignore); 142960484Sobrien#else 143060484Sobrien s_data (ignore); 143160484Sobrien#endif 143260484Sobrien} 143360484Sobrien 143460484Sobrien#ifdef OBJ_ELF 143560484Sobrienstatic void 143660484Sobrienarm_s_section (ignore) 143760484Sobrien int ignore; 143860484Sobrien{ 143960484Sobrien s_ltorg (0); 144060484Sobrien 144160484Sobrien obj_elf_section (ignore); 144260484Sobrien} 144360484Sobrien#endif 144460484Sobrien 144560484Sobrienstatic void 144660484Sobrienopcode_select (width) 144760484Sobrien int width; 144860484Sobrien{ 144960484Sobrien switch (width) 145060484Sobrien { 145160484Sobrien case 16: 145260484Sobrien if (! thumb_mode) 145360484Sobrien { 145460484Sobrien if (! (cpu_variant & ARM_THUMB)) 145560484Sobrien as_bad (_("selected processor does not support THUMB opcodes")); 145660484Sobrien thumb_mode = 1; 145760484Sobrien /* No need to force the alignment, since we will have been 145860484Sobrien coming from ARM mode, which is word-aligned. */ 145960484Sobrien record_alignment (now_seg, 1); 146060484Sobrien } 146160484Sobrien break; 146260484Sobrien 146360484Sobrien case 32: 146460484Sobrien if (thumb_mode) 146560484Sobrien { 146660484Sobrien if ((cpu_variant & ARM_ANY) == ARM_THUMB) 146760484Sobrien as_bad (_("selected processor does not support ARM opcodes")); 146860484Sobrien thumb_mode = 0; 146960484Sobrien if (!need_pass_2) 147060484Sobrien frag_align (2, 0, 0); 147160484Sobrien record_alignment (now_seg, 1); 147260484Sobrien } 147360484Sobrien break; 147460484Sobrien 147560484Sobrien default: 147660484Sobrien as_bad (_("invalid instruction size selected (%d)"), width); 147760484Sobrien } 147860484Sobrien} 147960484Sobrien 148060484Sobrienstatic void 148160484Sobriens_arm (ignore) 148260484Sobrien int ignore ATTRIBUTE_UNUSED; 148360484Sobrien{ 148460484Sobrien opcode_select (32); 148560484Sobrien demand_empty_rest_of_line (); 148660484Sobrien} 148760484Sobrien 148860484Sobrienstatic void 148960484Sobriens_thumb (ignore) 149060484Sobrien int ignore ATTRIBUTE_UNUSED; 149160484Sobrien{ 149260484Sobrien opcode_select (16); 149360484Sobrien demand_empty_rest_of_line (); 149460484Sobrien} 149560484Sobrien 149660484Sobrienstatic void 149760484Sobriens_code (unused) 149860484Sobrien int unused ATTRIBUTE_UNUSED; 149960484Sobrien{ 150060484Sobrien register int temp; 150160484Sobrien 150260484Sobrien temp = get_absolute_expression (); 150360484Sobrien switch (temp) 150460484Sobrien { 150560484Sobrien case 16: 150660484Sobrien case 32: 150760484Sobrien opcode_select (temp); 150860484Sobrien break; 150960484Sobrien 151060484Sobrien default: 151160484Sobrien as_bad (_("invalid operand to .code directive (%d) (expecting 16 or 32)"), temp); 151260484Sobrien } 151360484Sobrien} 151460484Sobrien 151560484Sobrienstatic void 151660484Sobrienend_of_line (str) 151760484Sobrien char * str; 151860484Sobrien{ 151960484Sobrien skip_whitespace (str); 152060484Sobrien 152160484Sobrien if (* str != '\0') 152260484Sobrien inst.error = _("Garbage following instruction"); 152360484Sobrien} 152460484Sobrien 152560484Sobrienstatic int 152660484Sobrienskip_past_comma (str) 152760484Sobrien char ** str; 152860484Sobrien{ 152960484Sobrien char *p = *str, c; 153060484Sobrien int comma = 0; 153160484Sobrien 153260484Sobrien while ((c = *p) == ' ' || c == ',') 153360484Sobrien { 153460484Sobrien p++; 153560484Sobrien if (c == ',' && comma++) 153660484Sobrien return FAIL; 153760484Sobrien } 153860484Sobrien 153960484Sobrien if (c == '\0') 154060484Sobrien return FAIL; 154160484Sobrien 154260484Sobrien *str = p; 154360484Sobrien return comma ? SUCCESS : FAIL; 154460484Sobrien} 154560484Sobrien 154660484Sobrien/* A standard register must be given at this point. 154760484Sobrien Shift is the place to put it in inst.instruction. 154860484Sobrien Restores input start point on err. 154960484Sobrien Returns the reg#, or FAIL. */ 155060484Sobrienstatic int 155160484Sobrienreg_required_here (str, shift) 155260484Sobrien char ** str; 155360484Sobrien int shift; 155460484Sobrien{ 155560484Sobrien static char buff [128]; /* XXX */ 155660484Sobrien int reg; 155760484Sobrien char * start = *str; 155860484Sobrien 155960484Sobrien if ((reg = arm_reg_parse (str)) != FAIL && int_register (reg)) 156060484Sobrien { 156160484Sobrien if (shift >= 0) 156260484Sobrien inst.instruction |= reg << shift; 156360484Sobrien return reg; 156460484Sobrien } 156560484Sobrien 156660484Sobrien /* Restore the start point, we may have got a reg of the wrong class. */ 156760484Sobrien *str = start; 156860484Sobrien 156960484Sobrien /* In the few cases where we might be able to accept something else 157060484Sobrien this error can be overridden. */ 157160484Sobrien sprintf (buff, _("Register expected, not '%.100s'"), start); 157260484Sobrien inst.error = buff; 157360484Sobrien 157460484Sobrien return FAIL; 157560484Sobrien} 157660484Sobrien 157761843Sobrienstatic CONST struct asm_psr * 157861843Sobrienarm_psr_parse (ccp) 157961843Sobrien register char ** ccp; 158061843Sobrien{ 158161843Sobrien char * start = * ccp; 158261843Sobrien char c; 158361843Sobrien char * p; 158461843Sobrien CONST struct asm_psr * psr; 158561843Sobrien 158661843Sobrien p = start; 158761843Sobrien 158861843Sobrien /* Skip to the end of the next word in the input stream. */ 158961843Sobrien do 159061843Sobrien { 159161843Sobrien c = *p++; 159261843Sobrien } 159361843Sobrien while (isalpha (c) || c == '_'); 159461843Sobrien 159561843Sobrien /* Terminate the word. */ 159661843Sobrien *--p = 0; 159761843Sobrien 159861843Sobrien /* Now locate the word in the psr hash table. */ 159961843Sobrien psr = (CONST struct asm_psr *) hash_find (arm_psr_hsh, start); 160061843Sobrien 160161843Sobrien /* Restore the input stream. */ 160261843Sobrien *p = c; 160361843Sobrien 160461843Sobrien /* If we found a valid match, advance the 160561843Sobrien stream pointer past the end of the word. */ 160661843Sobrien *ccp = p; 160761843Sobrien 160861843Sobrien return psr; 160961843Sobrien} 161061843Sobrien 161161843Sobrien/* Parse the input looking for a PSR flag. */ 161260484Sobrienstatic int 161361843Sobrienpsr_required_here (str) 161460484Sobrien char ** str; 161560484Sobrien{ 161660484Sobrien char * start = *str; 161761843Sobrien CONST struct asm_psr * psr; 161861843Sobrien 161960484Sobrien psr = arm_psr_parse (str); 162061843Sobrien 162161843Sobrien if (psr) 162260484Sobrien { 162361843Sobrien /* If this is the SPSR that is being modified, set the R bit. */ 162461843Sobrien if (! psr->cpsr) 162561843Sobrien inst.instruction |= SPSR_BIT; 162661843Sobrien 162761843Sobrien /* Set the psr flags in the MSR instruction. */ 162861843Sobrien inst.instruction |= psr->field << PSR_SHIFT; 162960484Sobrien 163060484Sobrien return SUCCESS; 163160484Sobrien } 163260484Sobrien 163361843Sobrien /* In the few cases where we might be able to accept 163461843Sobrien something else this error can be overridden. */ 163561843Sobrien inst.error = _("flag for {c}psr instruction expected"); 163660484Sobrien 163760484Sobrien /* Restore the start point. */ 163860484Sobrien *str = start; 163960484Sobrien return FAIL; 164060484Sobrien} 164160484Sobrien 164260484Sobrienstatic int 164360484Sobrienco_proc_number (str) 164460484Sobrien char ** str; 164560484Sobrien{ 164660484Sobrien int processor, pchar; 164760484Sobrien 164860484Sobrien skip_whitespace (* str); 164960484Sobrien 165060484Sobrien /* The data sheet seems to imply that just a number on its own is valid 165160484Sobrien here, but the RISC iX assembler seems to accept a prefix 'p'. We will 165260484Sobrien accept either. */ 165360484Sobrien if (**str == 'p' || **str == 'P') 165460484Sobrien (*str)++; 165560484Sobrien 165660484Sobrien pchar = *(*str)++; 165760484Sobrien if (pchar >= '0' && pchar <= '9') 165860484Sobrien { 165960484Sobrien processor = pchar - '0'; 166060484Sobrien if (**str >= '0' && **str <= '9') 166160484Sobrien { 166260484Sobrien processor = processor * 10 + *(*str)++ - '0'; 166360484Sobrien if (processor > 15) 166460484Sobrien { 166560484Sobrien inst.error = _("Illegal co-processor number"); 166660484Sobrien return FAIL; 166760484Sobrien } 166860484Sobrien } 166960484Sobrien } 167060484Sobrien else 167160484Sobrien { 167260484Sobrien inst.error = _("Bad or missing co-processor number"); 167360484Sobrien return FAIL; 167460484Sobrien } 167560484Sobrien 167660484Sobrien inst.instruction |= processor << 8; 167760484Sobrien return SUCCESS; 167860484Sobrien} 167960484Sobrien 168060484Sobrienstatic int 168160484Sobriencp_opc_expr (str, where, length) 168260484Sobrien char ** str; 168360484Sobrien int where; 168460484Sobrien int length; 168560484Sobrien{ 168660484Sobrien expressionS expr; 168760484Sobrien 168860484Sobrien skip_whitespace (* str); 168960484Sobrien 169060484Sobrien memset (&expr, '\0', sizeof (expr)); 169160484Sobrien 169260484Sobrien if (my_get_expression (&expr, str)) 169360484Sobrien return FAIL; 169460484Sobrien if (expr.X_op != O_constant) 169560484Sobrien { 169660484Sobrien inst.error = _("bad or missing expression"); 169760484Sobrien return FAIL; 169860484Sobrien } 169960484Sobrien 170060484Sobrien if ((expr.X_add_number & ((1 << length) - 1)) != expr.X_add_number) 170160484Sobrien { 170260484Sobrien inst.error = _("immediate co-processor expression too large"); 170360484Sobrien return FAIL; 170460484Sobrien } 170560484Sobrien 170660484Sobrien inst.instruction |= expr.X_add_number << where; 170760484Sobrien return SUCCESS; 170860484Sobrien} 170960484Sobrien 171060484Sobrienstatic int 171160484Sobriencp_reg_required_here (str, where) 171260484Sobrien char ** str; 171360484Sobrien int where; 171460484Sobrien{ 171560484Sobrien int reg; 171660484Sobrien char * start = *str; 171760484Sobrien 171860484Sobrien if ((reg = arm_reg_parse (str)) != FAIL && cp_register (reg)) 171960484Sobrien { 172060484Sobrien reg &= 15; 172160484Sobrien inst.instruction |= reg << where; 172260484Sobrien return reg; 172360484Sobrien } 172460484Sobrien 172560484Sobrien /* In the few cases where we might be able to accept something else 172660484Sobrien this error can be overridden. */ 172760484Sobrien inst.error = _("Co-processor register expected"); 172860484Sobrien 172960484Sobrien /* Restore the start point. */ 173060484Sobrien *str = start; 173160484Sobrien return FAIL; 173260484Sobrien} 173360484Sobrien 173460484Sobrienstatic int 173560484Sobrienfp_reg_required_here (str, where) 173660484Sobrien char ** str; 173760484Sobrien int where; 173860484Sobrien{ 173960484Sobrien int reg; 174060484Sobrien char * start = *str; 174160484Sobrien 174260484Sobrien if ((reg = arm_reg_parse (str)) != FAIL && fp_register (reg)) 174360484Sobrien { 174460484Sobrien reg &= 7; 174560484Sobrien inst.instruction |= reg << where; 174660484Sobrien return reg; 174760484Sobrien } 174860484Sobrien 174960484Sobrien /* In the few cases where we might be able to accept something else 175060484Sobrien this error can be overridden. */ 175160484Sobrien inst.error = _("Floating point register expected"); 175260484Sobrien 175360484Sobrien /* Restore the start point. */ 175460484Sobrien *str = start; 175560484Sobrien return FAIL; 175660484Sobrien} 175760484Sobrien 175860484Sobrienstatic int 175960484Sobriencp_address_offset (str) 176060484Sobrien char ** str; 176160484Sobrien{ 176260484Sobrien int offset; 176360484Sobrien 176460484Sobrien skip_whitespace (* str); 176560484Sobrien 176660484Sobrien if (! is_immediate_prefix (**str)) 176760484Sobrien { 176860484Sobrien inst.error = _("immediate expression expected"); 176960484Sobrien return FAIL; 177060484Sobrien } 177160484Sobrien 177260484Sobrien (*str)++; 177360484Sobrien 177460484Sobrien if (my_get_expression (& inst.reloc.exp, str)) 177560484Sobrien return FAIL; 177660484Sobrien 177760484Sobrien if (inst.reloc.exp.X_op == O_constant) 177860484Sobrien { 177960484Sobrien offset = inst.reloc.exp.X_add_number; 178060484Sobrien 178160484Sobrien if (offset & 3) 178260484Sobrien { 178360484Sobrien inst.error = _("co-processor address must be word aligned"); 178460484Sobrien return FAIL; 178560484Sobrien } 178660484Sobrien 178760484Sobrien if (offset > 1023 || offset < -1023) 178860484Sobrien { 178960484Sobrien inst.error = _("offset too large"); 179060484Sobrien return FAIL; 179160484Sobrien } 179260484Sobrien 179360484Sobrien if (offset >= 0) 179460484Sobrien inst.instruction |= INDEX_UP; 179560484Sobrien else 179660484Sobrien offset = -offset; 179760484Sobrien 179860484Sobrien inst.instruction |= offset >> 2; 179960484Sobrien } 180060484Sobrien else 180160484Sobrien inst.reloc.type = BFD_RELOC_ARM_CP_OFF_IMM; 180260484Sobrien 180360484Sobrien return SUCCESS; 180460484Sobrien} 180560484Sobrien 180660484Sobrienstatic int 180760484Sobriencp_address_required_here (str) 180860484Sobrien char ** str; 180960484Sobrien{ 181060484Sobrien char * p = * str; 181160484Sobrien int pre_inc = 0; 181260484Sobrien int write_back = 0; 181360484Sobrien 181460484Sobrien if (*p == '[') 181560484Sobrien { 181660484Sobrien int reg; 181760484Sobrien 181860484Sobrien p++; 181960484Sobrien skip_whitespace (p); 182060484Sobrien 182160484Sobrien if ((reg = reg_required_here (& p, 16)) == FAIL) 182260484Sobrien return FAIL; 182360484Sobrien 182460484Sobrien skip_whitespace (p); 182560484Sobrien 182660484Sobrien if (*p == ']') 182760484Sobrien { 182860484Sobrien p++; 182960484Sobrien 183060484Sobrien if (skip_past_comma (& p) == SUCCESS) 183160484Sobrien { 183260484Sobrien /* [Rn], #expr */ 183360484Sobrien write_back = WRITE_BACK; 183460484Sobrien 183560484Sobrien if (reg == REG_PC) 183660484Sobrien { 183760484Sobrien inst.error = _("pc may not be used in post-increment"); 183860484Sobrien return FAIL; 183960484Sobrien } 184060484Sobrien 184160484Sobrien if (cp_address_offset (& p) == FAIL) 184260484Sobrien return FAIL; 184360484Sobrien } 184460484Sobrien else 184560484Sobrien pre_inc = PRE_INDEX | INDEX_UP; 184660484Sobrien } 184760484Sobrien else 184860484Sobrien { 184960484Sobrien /* '['Rn, #expr']'[!] */ 185060484Sobrien 185160484Sobrien if (skip_past_comma (& p) == FAIL) 185260484Sobrien { 185360484Sobrien inst.error = _("pre-indexed expression expected"); 185460484Sobrien return FAIL; 185560484Sobrien } 185660484Sobrien 185760484Sobrien pre_inc = PRE_INDEX; 185860484Sobrien 185960484Sobrien if (cp_address_offset (& p) == FAIL) 186060484Sobrien return FAIL; 186160484Sobrien 186260484Sobrien skip_whitespace (p); 186360484Sobrien 186460484Sobrien if (*p++ != ']') 186560484Sobrien { 186660484Sobrien inst.error = _("missing ]"); 186760484Sobrien return FAIL; 186860484Sobrien } 186960484Sobrien 187060484Sobrien skip_whitespace (p); 187160484Sobrien 187260484Sobrien if (*p == '!') 187360484Sobrien { 187460484Sobrien if (reg == REG_PC) 187560484Sobrien { 187660484Sobrien inst.error = _("pc may not be used with write-back"); 187760484Sobrien return FAIL; 187860484Sobrien } 187960484Sobrien 188060484Sobrien p++; 188160484Sobrien write_back = WRITE_BACK; 188260484Sobrien } 188360484Sobrien } 188460484Sobrien } 188560484Sobrien else 188660484Sobrien { 188760484Sobrien if (my_get_expression (&inst.reloc.exp, &p)) 188860484Sobrien return FAIL; 188960484Sobrien 189060484Sobrien inst.reloc.type = BFD_RELOC_ARM_CP_OFF_IMM; 189160484Sobrien inst.reloc.exp.X_add_number -= 8; /* PC rel adjust */ 189260484Sobrien inst.reloc.pc_rel = 1; 189360484Sobrien inst.instruction |= (REG_PC << 16); 189460484Sobrien pre_inc = PRE_INDEX; 189560484Sobrien } 189660484Sobrien 189760484Sobrien inst.instruction |= write_back | pre_inc; 189860484Sobrien *str = p; 189960484Sobrien return SUCCESS; 190060484Sobrien} 190160484Sobrien 190260484Sobrienstatic void 190360484Sobriendo_nop (str, flags) 190460484Sobrien char * str; 190560484Sobrien unsigned long flags; 190660484Sobrien{ 190760484Sobrien /* Do nothing really. */ 190860484Sobrien inst.instruction |= flags; /* This is pointless. */ 190960484Sobrien end_of_line (str); 191060484Sobrien return; 191160484Sobrien} 191260484Sobrien 191360484Sobrienstatic void 191460484Sobriendo_mrs (str, flags) 191560484Sobrien char *str; 191660484Sobrien unsigned long flags; 191760484Sobrien{ 191861843Sobrien int skip = 0; 191961843Sobrien 192060484Sobrien /* Only one syntax. */ 192160484Sobrien skip_whitespace (str); 192260484Sobrien 192360484Sobrien if (reg_required_here (&str, 12) == FAIL) 192460484Sobrien { 192560484Sobrien inst.error = BAD_ARGS; 192660484Sobrien return; 192760484Sobrien } 192860484Sobrien 192961843Sobrien if (skip_past_comma (&str) == FAIL) 193060484Sobrien { 193161843Sobrien inst.error = _("comma expected after register name"); 193260484Sobrien return; 193360484Sobrien } 193460484Sobrien 193561843Sobrien skip_whitespace (str); 193661843Sobrien 193761843Sobrien if ( strcmp (str, "CPSR") == 0 193861843Sobrien || strcmp (str, "SPSR") == 0 193961843Sobrien /* Lower case versions for backwards compatability. */ 194061843Sobrien || strcmp (str, "cpsr") == 0 194161843Sobrien || strcmp (str, "spsr") == 0) 194261843Sobrien skip = 4; 194361843Sobrien /* This is for backwards compatability with older toolchains. */ 194461843Sobrien else if (strcmp (str, "cpsr_all") == 0 194561843Sobrien || strcmp (str, "spsr_all") == 0) 194668765Sobrien skip = 8; 194761843Sobrien else 194861843Sobrien { 194961843Sobrien inst.error = _("{C|S}PSR expected"); 195061843Sobrien return; 195161843Sobrien } 195261843Sobrien 195361843Sobrien if (* str == 's' || * str == 'S') 195461843Sobrien inst.instruction |= SPSR_BIT; 195561843Sobrien str += skip; 195661843Sobrien 195760484Sobrien inst.instruction |= flags; 195860484Sobrien end_of_line (str); 195960484Sobrien} 196060484Sobrien 196161843Sobrien/* Two possible forms: 196261843Sobrien "{C|S}PSR_<field>, Rm", 196361843Sobrien "{C|S}PSR_f, #expression". */ 196460484Sobrienstatic void 196560484Sobriendo_msr (str, flags) 196660484Sobrien char * str; 196760484Sobrien unsigned long flags; 196860484Sobrien{ 196961843Sobrien skip_whitespace (str); 197060484Sobrien 197161843Sobrien if (psr_required_here (& str) == FAIL) 197261843Sobrien return; 197361843Sobrien 197461843Sobrien if (skip_past_comma (& str) == FAIL) 197561843Sobrien { 197661843Sobrien inst.error = _("comma missing after psr flags"); 197761843Sobrien return; 197861843Sobrien } 197961843Sobrien 198060484Sobrien skip_whitespace (str); 198160484Sobrien 198261843Sobrien if (reg_required_here (& str, 0) != FAIL) 198360484Sobrien { 198461843Sobrien inst.error = NULL; 198561843Sobrien inst.instruction |= flags; 198661843Sobrien end_of_line (str); 198761843Sobrien return; 198861843Sobrien } 198960484Sobrien 199061843Sobrien if (! is_immediate_prefix (* str)) 199161843Sobrien { 199261843Sobrien inst.error = _("only a register or immediate value can follow a psr flag"); 199361843Sobrien return; 199460484Sobrien } 199561843Sobrien 199661843Sobrien str ++; 199761843Sobrien inst.error = NULL; 199861843Sobrien 199961843Sobrien if (my_get_expression (& inst.reloc.exp, & str)) 200061843Sobrien { 200161843Sobrien inst.error = _("only a register or immediate value can follow a psr flag"); 200261843Sobrien return; 200361843Sobrien } 200461843Sobrien 200561843Sobrien if (inst.instruction & ((PSR_c | PSR_x | PSR_s) << PSR_SHIFT)) 200661843Sobrien { 200768765Sobrien inst.error = _("only flag field of psr can be set with immediate value"); 200861843Sobrien return; 200961843Sobrien } 201061843Sobrien 201161843Sobrien flags |= INST_IMMEDIATE; 201261843Sobrien 201361843Sobrien if (inst.reloc.exp.X_add_symbol) 201461843Sobrien { 201561843Sobrien inst.reloc.type = BFD_RELOC_ARM_IMMEDIATE; 201661843Sobrien inst.reloc.pc_rel = 0; 201761843Sobrien } 201860484Sobrien else 201960484Sobrien { 202061843Sobrien unsigned value = validate_immediate (inst.reloc.exp.X_add_number); 202160484Sobrien 202261843Sobrien if (value == (unsigned) FAIL) 202360484Sobrien { 202461843Sobrien inst.error = _("Invalid constant"); 202560484Sobrien return; 202660484Sobrien } 202760484Sobrien 202861843Sobrien inst.instruction |= value; 202960484Sobrien } 203060484Sobrien 203160484Sobrien inst.error = NULL; 203260484Sobrien inst.instruction |= flags; 203360484Sobrien end_of_line (str); 203460484Sobrien} 203560484Sobrien 203660484Sobrien/* Long Multiply Parser 203760484Sobrien UMULL RdLo, RdHi, Rm, Rs 203860484Sobrien SMULL RdLo, RdHi, Rm, Rs 203960484Sobrien UMLAL RdLo, RdHi, Rm, Rs 204060484Sobrien SMLAL RdLo, RdHi, Rm, Rs 204160484Sobrien*/ 204260484Sobrienstatic void 204360484Sobriendo_mull (str, flags) 204460484Sobrien char * str; 204560484Sobrien unsigned long flags; 204660484Sobrien{ 204760484Sobrien int rdlo, rdhi, rm, rs; 204860484Sobrien 204960484Sobrien /* Only one format "rdlo, rdhi, rm, rs" */ 205060484Sobrien skip_whitespace (str); 205160484Sobrien 205260484Sobrien if ((rdlo = reg_required_here (&str, 12)) == FAIL) 205360484Sobrien { 205460484Sobrien inst.error = BAD_ARGS; 205560484Sobrien return; 205660484Sobrien } 205760484Sobrien 205860484Sobrien if (skip_past_comma (&str) == FAIL 205960484Sobrien || (rdhi = reg_required_here (&str, 16)) == FAIL) 206060484Sobrien { 206160484Sobrien inst.error = BAD_ARGS; 206260484Sobrien return; 206360484Sobrien } 206460484Sobrien 206560484Sobrien if (skip_past_comma (&str) == FAIL 206660484Sobrien || (rm = reg_required_here (&str, 0)) == FAIL) 206760484Sobrien { 206860484Sobrien inst.error = BAD_ARGS; 206960484Sobrien return; 207060484Sobrien } 207160484Sobrien 207260484Sobrien /* rdhi, rdlo and rm must all be different */ 207360484Sobrien if (rdlo == rdhi || rdlo == rm || rdhi == rm) 207460484Sobrien as_tsktsk (_("rdhi, rdlo and rm must all be different")); 207560484Sobrien 207660484Sobrien if (skip_past_comma (&str) == FAIL 207760484Sobrien || (rs = reg_required_here (&str, 8)) == FAIL) 207860484Sobrien { 207960484Sobrien inst.error = BAD_ARGS; 208060484Sobrien return; 208160484Sobrien } 208260484Sobrien 208360484Sobrien if (rdhi == REG_PC || rdhi == REG_PC || rdhi == REG_PC || rdhi == REG_PC) 208460484Sobrien { 208560484Sobrien inst.error = BAD_PC; 208660484Sobrien return; 208760484Sobrien } 208860484Sobrien 208960484Sobrien inst.instruction |= flags; 209060484Sobrien end_of_line (str); 209160484Sobrien return; 209260484Sobrien} 209360484Sobrien 209460484Sobrienstatic void 209560484Sobriendo_mul (str, flags) 209660484Sobrien char * str; 209760484Sobrien unsigned long flags; 209860484Sobrien{ 209960484Sobrien int rd, rm; 210060484Sobrien 210160484Sobrien /* Only one format "rd, rm, rs" */ 210260484Sobrien skip_whitespace (str); 210360484Sobrien 210460484Sobrien if ((rd = reg_required_here (&str, 16)) == FAIL) 210560484Sobrien { 210660484Sobrien inst.error = BAD_ARGS; 210760484Sobrien return; 210860484Sobrien } 210960484Sobrien 211060484Sobrien if (rd == REG_PC) 211160484Sobrien { 211260484Sobrien inst.error = BAD_PC; 211360484Sobrien return; 211460484Sobrien } 211560484Sobrien 211660484Sobrien if (skip_past_comma (&str) == FAIL 211760484Sobrien || (rm = reg_required_here (&str, 0)) == FAIL) 211860484Sobrien { 211960484Sobrien inst.error = BAD_ARGS; 212060484Sobrien return; 212160484Sobrien } 212260484Sobrien 212360484Sobrien if (rm == REG_PC) 212460484Sobrien { 212560484Sobrien inst.error = BAD_PC; 212660484Sobrien return; 212760484Sobrien } 212860484Sobrien 212960484Sobrien if (rm == rd) 213060484Sobrien as_tsktsk (_("rd and rm should be different in mul")); 213160484Sobrien 213260484Sobrien if (skip_past_comma (&str) == FAIL 213360484Sobrien || (rm = reg_required_here (&str, 8)) == FAIL) 213460484Sobrien { 213560484Sobrien inst.error = BAD_ARGS; 213660484Sobrien return; 213760484Sobrien } 213860484Sobrien 213960484Sobrien if (rm == REG_PC) 214060484Sobrien { 214160484Sobrien inst.error = BAD_PC; 214260484Sobrien return; 214360484Sobrien } 214460484Sobrien 214560484Sobrien inst.instruction |= flags; 214660484Sobrien end_of_line (str); 214760484Sobrien return; 214860484Sobrien} 214960484Sobrien 215060484Sobrienstatic void 215160484Sobriendo_mla (str, flags) 215260484Sobrien char * str; 215360484Sobrien unsigned long flags; 215460484Sobrien{ 215560484Sobrien int rd, rm; 215660484Sobrien 215760484Sobrien /* Only one format "rd, rm, rs, rn" */ 215860484Sobrien skip_whitespace (str); 215960484Sobrien 216060484Sobrien if ((rd = reg_required_here (&str, 16)) == FAIL) 216160484Sobrien { 216260484Sobrien inst.error = BAD_ARGS; 216360484Sobrien return; 216460484Sobrien } 216560484Sobrien 216660484Sobrien if (rd == REG_PC) 216760484Sobrien { 216860484Sobrien inst.error = BAD_PC; 216960484Sobrien return; 217060484Sobrien } 217160484Sobrien 217260484Sobrien if (skip_past_comma (&str) == FAIL 217360484Sobrien || (rm = reg_required_here (&str, 0)) == FAIL) 217460484Sobrien { 217560484Sobrien inst.error = BAD_ARGS; 217660484Sobrien return; 217760484Sobrien } 217860484Sobrien 217960484Sobrien if (rm == REG_PC) 218060484Sobrien { 218160484Sobrien inst.error = BAD_PC; 218260484Sobrien return; 218360484Sobrien } 218460484Sobrien 218560484Sobrien if (rm == rd) 218660484Sobrien as_tsktsk (_("rd and rm should be different in mla")); 218760484Sobrien 218860484Sobrien if (skip_past_comma (&str) == FAIL 218960484Sobrien || (rd = reg_required_here (&str, 8)) == FAIL 219060484Sobrien || skip_past_comma (&str) == FAIL 219160484Sobrien || (rm = reg_required_here (&str, 12)) == FAIL) 219260484Sobrien { 219360484Sobrien inst.error = BAD_ARGS; 219460484Sobrien return; 219560484Sobrien } 219660484Sobrien 219760484Sobrien if (rd == REG_PC || rm == REG_PC) 219860484Sobrien { 219960484Sobrien inst.error = BAD_PC; 220060484Sobrien return; 220160484Sobrien } 220260484Sobrien 220360484Sobrien inst.instruction |= flags; 220460484Sobrien end_of_line (str); 220560484Sobrien return; 220660484Sobrien} 220760484Sobrien 220860484Sobrien/* Returns the index into fp_values of a floating point number, or -1 if 220960484Sobrien not in the table. */ 221060484Sobrienstatic int 221160484Sobrienmy_get_float_expression (str) 221260484Sobrien char ** str; 221360484Sobrien{ 221460484Sobrien LITTLENUM_TYPE words[MAX_LITTLENUMS]; 221560484Sobrien char * save_in; 221660484Sobrien expressionS exp; 221760484Sobrien int i; 221860484Sobrien int j; 221960484Sobrien 222060484Sobrien memset (words, 0, MAX_LITTLENUMS * sizeof (LITTLENUM_TYPE)); 222160484Sobrien /* Look for a raw floating point number */ 222260484Sobrien if ((save_in = atof_ieee (*str, 'x', words)) != NULL 222360484Sobrien && (is_end_of_line [(int)(*save_in)] || *save_in == '\0')) 222460484Sobrien { 222560484Sobrien for (i = 0; i < NUM_FLOAT_VALS; i++) 222660484Sobrien { 222760484Sobrien for (j = 0; j < MAX_LITTLENUMS; j++) 222860484Sobrien { 222960484Sobrien if (words[j] != fp_values[i][j]) 223060484Sobrien break; 223160484Sobrien } 223260484Sobrien 223360484Sobrien if (j == MAX_LITTLENUMS) 223460484Sobrien { 223560484Sobrien *str = save_in; 223660484Sobrien return i; 223760484Sobrien } 223860484Sobrien } 223960484Sobrien } 224060484Sobrien 224160484Sobrien /* Try and parse a more complex expression, this will probably fail 224260484Sobrien unless the code uses a floating point prefix (eg "0f") */ 224360484Sobrien save_in = input_line_pointer; 224460484Sobrien input_line_pointer = *str; 224560484Sobrien if (expression (&exp) == absolute_section 224660484Sobrien && exp.X_op == O_big 224760484Sobrien && exp.X_add_number < 0) 224860484Sobrien { 224960484Sobrien /* FIXME: 5 = X_PRECISION, should be #define'd where we can use it. 225060484Sobrien Ditto for 15. */ 225160484Sobrien if (gen_to_words (words, 5, (long)15) == 0) 225260484Sobrien { 225360484Sobrien for (i = 0; i < NUM_FLOAT_VALS; i++) 225460484Sobrien { 225560484Sobrien for (j = 0; j < MAX_LITTLENUMS; j++) 225660484Sobrien { 225760484Sobrien if (words[j] != fp_values[i][j]) 225860484Sobrien break; 225960484Sobrien } 226060484Sobrien 226160484Sobrien if (j == MAX_LITTLENUMS) 226260484Sobrien { 226360484Sobrien *str = input_line_pointer; 226460484Sobrien input_line_pointer = save_in; 226560484Sobrien return i; 226660484Sobrien } 226760484Sobrien } 226860484Sobrien } 226960484Sobrien } 227060484Sobrien 227160484Sobrien *str = input_line_pointer; 227260484Sobrien input_line_pointer = save_in; 227360484Sobrien return -1; 227460484Sobrien} 227560484Sobrien 227660484Sobrien/* Return true if anything in the expression is a bignum */ 227760484Sobrienstatic int 227860484Sobrienwalk_no_bignums (sp) 227960484Sobrien symbolS * sp; 228060484Sobrien{ 228160484Sobrien if (symbol_get_value_expression (sp)->X_op == O_big) 228260484Sobrien return 1; 228360484Sobrien 228460484Sobrien if (symbol_get_value_expression (sp)->X_add_symbol) 228560484Sobrien { 228660484Sobrien return (walk_no_bignums (symbol_get_value_expression (sp)->X_add_symbol) 228760484Sobrien || (symbol_get_value_expression (sp)->X_op_symbol 228860484Sobrien && walk_no_bignums (symbol_get_value_expression (sp)->X_op_symbol))); 228960484Sobrien } 229060484Sobrien 229160484Sobrien return 0; 229260484Sobrien} 229360484Sobrien 229460484Sobrienstatic int 229560484Sobrienmy_get_expression (ep, str) 229660484Sobrien expressionS * ep; 229760484Sobrien char ** str; 229860484Sobrien{ 229960484Sobrien char * save_in; 230060484Sobrien segT seg; 230160484Sobrien 230260484Sobrien save_in = input_line_pointer; 230360484Sobrien input_line_pointer = *str; 230460484Sobrien seg = expression (ep); 230560484Sobrien 230660484Sobrien#ifdef OBJ_AOUT 230760484Sobrien if (seg != absolute_section 230860484Sobrien && seg != text_section 230960484Sobrien && seg != data_section 231060484Sobrien && seg != bss_section 231160484Sobrien && seg != undefined_section) 231260484Sobrien { 231360484Sobrien inst.error = _("bad_segment"); 231460484Sobrien *str = input_line_pointer; 231560484Sobrien input_line_pointer = save_in; 231660484Sobrien return 1; 231760484Sobrien } 231860484Sobrien#endif 231960484Sobrien 232060484Sobrien /* Get rid of any bignums now, so that we don't generate an error for which 232160484Sobrien we can't establish a line number later on. Big numbers are never valid 232260484Sobrien in instructions, which is where this routine is always called. */ 232360484Sobrien if (ep->X_op == O_big 232460484Sobrien || (ep->X_add_symbol 232560484Sobrien && (walk_no_bignums (ep->X_add_symbol) 232660484Sobrien || (ep->X_op_symbol 232760484Sobrien && walk_no_bignums (ep->X_op_symbol))))) 232860484Sobrien { 232960484Sobrien inst.error = _("Invalid constant"); 233060484Sobrien *str = input_line_pointer; 233160484Sobrien input_line_pointer = save_in; 233260484Sobrien return 1; 233360484Sobrien } 233460484Sobrien 233560484Sobrien *str = input_line_pointer; 233660484Sobrien input_line_pointer = save_in; 233760484Sobrien return 0; 233860484Sobrien} 233960484Sobrien 234060484Sobrien/* unrestrict should be one if <shift> <register> is permitted for this 234160484Sobrien instruction */ 234260484Sobrien 234360484Sobrienstatic int 234460484Sobriendecode_shift (str, unrestrict) 234560484Sobrien char ** str; 234660484Sobrien int unrestrict; 234760484Sobrien{ 234860484Sobrien struct asm_shift * shft; 234960484Sobrien char * p; 235060484Sobrien char c; 235160484Sobrien 235260484Sobrien skip_whitespace (* str); 235360484Sobrien 235460484Sobrien for (p = *str; isalpha (*p); p++) 235560484Sobrien ; 235660484Sobrien 235760484Sobrien if (p == *str) 235860484Sobrien { 235960484Sobrien inst.error = _("Shift expression expected"); 236060484Sobrien return FAIL; 236160484Sobrien } 236260484Sobrien 236360484Sobrien c = *p; 236460484Sobrien *p = '\0'; 236560484Sobrien shft = (struct asm_shift *) hash_find (arm_shift_hsh, *str); 236660484Sobrien *p = c; 236760484Sobrien if (shft) 236860484Sobrien { 236960484Sobrien if (!strncmp (*str, "rrx", 3) 237060484Sobrien || !strncmp (*str, "RRX", 3)) 237160484Sobrien { 237260484Sobrien *str = p; 237360484Sobrien inst.instruction |= shft->value; 237460484Sobrien return SUCCESS; 237560484Sobrien } 237660484Sobrien 237760484Sobrien skip_whitespace (p); 237860484Sobrien 237960484Sobrien if (unrestrict && reg_required_here (&p, 8) != FAIL) 238060484Sobrien { 238160484Sobrien inst.instruction |= shft->value | SHIFT_BY_REG; 238260484Sobrien *str = p; 238360484Sobrien return SUCCESS; 238460484Sobrien } 238560484Sobrien else if (is_immediate_prefix (* p)) 238660484Sobrien { 238760484Sobrien inst.error = NULL; 238860484Sobrien p++; 238960484Sobrien if (my_get_expression (&inst.reloc.exp, &p)) 239060484Sobrien return FAIL; 239160484Sobrien 239260484Sobrien /* Validate some simple #expressions */ 239360484Sobrien if (inst.reloc.exp.X_op == O_constant) 239460484Sobrien { 239560484Sobrien unsigned num = inst.reloc.exp.X_add_number; 239660484Sobrien 239760484Sobrien /* Reject operations greater than 32, or lsl #32 */ 239860484Sobrien if (num > 32 || (num == 32 && shft->value == 0)) 239960484Sobrien { 240060484Sobrien inst.error = _("Invalid immediate shift"); 240160484Sobrien return FAIL; 240260484Sobrien } 240360484Sobrien 240460484Sobrien /* Shifts of zero should be converted to lsl (which is zero)*/ 240560484Sobrien if (num == 0) 240660484Sobrien { 240760484Sobrien *str = p; 240860484Sobrien return SUCCESS; 240960484Sobrien } 241060484Sobrien 241160484Sobrien /* Shifts of 32 are encoded as 0, for those shifts that 241260484Sobrien support it. */ 241360484Sobrien if (num == 32) 241460484Sobrien num = 0; 241560484Sobrien 241660484Sobrien inst.instruction |= (num << 7) | shft->value; 241760484Sobrien *str = p; 241860484Sobrien return SUCCESS; 241960484Sobrien } 242060484Sobrien 242160484Sobrien inst.reloc.type = BFD_RELOC_ARM_SHIFT_IMM; 242260484Sobrien inst.reloc.pc_rel = 0; 242360484Sobrien inst.instruction |= shft->value; 242460484Sobrien *str = p; 242560484Sobrien return SUCCESS; 242660484Sobrien } 242760484Sobrien else 242860484Sobrien { 242960484Sobrien inst.error = unrestrict ? _("shift requires register or #expression") 243060484Sobrien : _("shift requires #expression"); 243160484Sobrien *str = p; 243260484Sobrien return FAIL; 243360484Sobrien } 243460484Sobrien } 243560484Sobrien 243660484Sobrien inst.error = _("Shift expression expected"); 243760484Sobrien return FAIL; 243860484Sobrien} 243960484Sobrien 244060484Sobrien/* Do those data_ops which can take a negative immediate constant */ 244160484Sobrien/* by altering the instuction. A bit of a hack really */ 244260484Sobrien/* MOV <-> MVN 244360484Sobrien AND <-> BIC 244460484Sobrien ADC <-> SBC 244560484Sobrien by inverting the second operand, and 244660484Sobrien ADD <-> SUB 244760484Sobrien CMP <-> CMN 244860484Sobrien by negating the second operand. 244960484Sobrien*/ 245060484Sobrienstatic int 245160484Sobriennegate_data_op (instruction, value) 245260484Sobrien unsigned long * instruction; 245360484Sobrien unsigned long value; 245460484Sobrien{ 245560484Sobrien int op, new_inst; 245660484Sobrien unsigned long negated, inverted; 245760484Sobrien 245860484Sobrien negated = validate_immediate (-value); 245960484Sobrien inverted = validate_immediate (~value); 246060484Sobrien 246160484Sobrien op = (*instruction >> DATA_OP_SHIFT) & 0xf; 246260484Sobrien switch (op) 246360484Sobrien { 246460484Sobrien /* First negates */ 246560484Sobrien case OPCODE_SUB: /* ADD <-> SUB */ 246660484Sobrien new_inst = OPCODE_ADD; 246760484Sobrien value = negated; 246860484Sobrien break; 246960484Sobrien 247060484Sobrien case OPCODE_ADD: 247160484Sobrien new_inst = OPCODE_SUB; 247260484Sobrien value = negated; 247360484Sobrien break; 247460484Sobrien 247560484Sobrien case OPCODE_CMP: /* CMP <-> CMN */ 247660484Sobrien new_inst = OPCODE_CMN; 247760484Sobrien value = negated; 247860484Sobrien break; 247960484Sobrien 248060484Sobrien case OPCODE_CMN: 248160484Sobrien new_inst = OPCODE_CMP; 248260484Sobrien value = negated; 248360484Sobrien break; 248460484Sobrien 248560484Sobrien /* Now Inverted ops */ 248660484Sobrien case OPCODE_MOV: /* MOV <-> MVN */ 248760484Sobrien new_inst = OPCODE_MVN; 248860484Sobrien value = inverted; 248960484Sobrien break; 249060484Sobrien 249160484Sobrien case OPCODE_MVN: 249260484Sobrien new_inst = OPCODE_MOV; 249360484Sobrien value = inverted; 249460484Sobrien break; 249560484Sobrien 249660484Sobrien case OPCODE_AND: /* AND <-> BIC */ 249760484Sobrien new_inst = OPCODE_BIC; 249860484Sobrien value = inverted; 249960484Sobrien break; 250060484Sobrien 250160484Sobrien case OPCODE_BIC: 250260484Sobrien new_inst = OPCODE_AND; 250360484Sobrien value = inverted; 250460484Sobrien break; 250560484Sobrien 250660484Sobrien case OPCODE_ADC: /* ADC <-> SBC */ 250760484Sobrien new_inst = OPCODE_SBC; 250860484Sobrien value = inverted; 250960484Sobrien break; 251060484Sobrien 251160484Sobrien case OPCODE_SBC: 251260484Sobrien new_inst = OPCODE_ADC; 251360484Sobrien value = inverted; 251460484Sobrien break; 251560484Sobrien 251660484Sobrien /* We cannot do anything */ 251760484Sobrien default: 251860484Sobrien return FAIL; 251960484Sobrien } 252060484Sobrien 252160484Sobrien if (value == (unsigned) FAIL) 252260484Sobrien return FAIL; 252360484Sobrien 252460484Sobrien *instruction &= OPCODE_MASK; 252560484Sobrien *instruction |= new_inst << DATA_OP_SHIFT; 252660484Sobrien return value; 252760484Sobrien} 252860484Sobrien 252960484Sobrienstatic int 253060484Sobriendata_op2 (str) 253160484Sobrien char ** str; 253260484Sobrien{ 253360484Sobrien int value; 253460484Sobrien expressionS expr; 253560484Sobrien 253660484Sobrien skip_whitespace (* str); 253760484Sobrien 253860484Sobrien if (reg_required_here (str, 0) != FAIL) 253960484Sobrien { 254060484Sobrien if (skip_past_comma (str) == SUCCESS) 254160484Sobrien /* Shift operation on register. */ 254260484Sobrien return decode_shift (str, NO_SHIFT_RESTRICT); 254360484Sobrien 254460484Sobrien return SUCCESS; 254560484Sobrien } 254660484Sobrien else 254760484Sobrien { 254860484Sobrien /* Immediate expression */ 254960484Sobrien if (is_immediate_prefix (**str)) 255060484Sobrien { 255160484Sobrien (*str)++; 255260484Sobrien inst.error = NULL; 255360484Sobrien 255460484Sobrien if (my_get_expression (&inst.reloc.exp, str)) 255560484Sobrien return FAIL; 255660484Sobrien 255760484Sobrien if (inst.reloc.exp.X_add_symbol) 255860484Sobrien { 255960484Sobrien inst.reloc.type = BFD_RELOC_ARM_IMMEDIATE; 256060484Sobrien inst.reloc.pc_rel = 0; 256160484Sobrien } 256260484Sobrien else 256360484Sobrien { 256460484Sobrien if (skip_past_comma (str) == SUCCESS) 256560484Sobrien { 256660484Sobrien /* #x, y -- ie explicit rotation by Y */ 256760484Sobrien if (my_get_expression (&expr, str)) 256860484Sobrien return FAIL; 256960484Sobrien 257060484Sobrien if (expr.X_op != O_constant) 257160484Sobrien { 257260484Sobrien inst.error = _("Constant expression expected"); 257360484Sobrien return FAIL; 257460484Sobrien } 257560484Sobrien 257660484Sobrien /* Rotate must be a multiple of 2 */ 257760484Sobrien if (((unsigned) expr.X_add_number) > 30 257860484Sobrien || (expr.X_add_number & 1) != 0 257960484Sobrien || ((unsigned) inst.reloc.exp.X_add_number) > 255) 258060484Sobrien { 258160484Sobrien inst.error = _("Invalid constant"); 258260484Sobrien return FAIL; 258360484Sobrien } 258460484Sobrien inst.instruction |= INST_IMMEDIATE; 258560484Sobrien inst.instruction |= inst.reloc.exp.X_add_number; 258660484Sobrien inst.instruction |= expr.X_add_number << 7; 258760484Sobrien return SUCCESS; 258860484Sobrien } 258960484Sobrien 259060484Sobrien /* Implicit rotation, select a suitable one */ 259160484Sobrien value = validate_immediate (inst.reloc.exp.X_add_number); 259260484Sobrien 259360484Sobrien if (value == FAIL) 259460484Sobrien { 259560484Sobrien /* Can't be done, perhaps the code reads something like 259660484Sobrien "add Rd, Rn, #-n", where "sub Rd, Rn, #n" would be ok */ 259760484Sobrien if ((value = negate_data_op (&inst.instruction, 259860484Sobrien inst.reloc.exp.X_add_number)) 259960484Sobrien == FAIL) 260060484Sobrien { 260160484Sobrien inst.error = _("Invalid constant"); 260260484Sobrien return FAIL; 260360484Sobrien } 260460484Sobrien } 260560484Sobrien 260660484Sobrien inst.instruction |= value; 260760484Sobrien } 260860484Sobrien 260960484Sobrien inst.instruction |= INST_IMMEDIATE; 261060484Sobrien return SUCCESS; 261160484Sobrien } 261260484Sobrien 261360484Sobrien (*str)++; 261460484Sobrien inst.error = _("Register or shift expression expected"); 261560484Sobrien return FAIL; 261660484Sobrien } 261760484Sobrien} 261860484Sobrien 261960484Sobrienstatic int 262060484Sobrienfp_op2 (str) 262160484Sobrien char ** str; 262260484Sobrien{ 262360484Sobrien skip_whitespace (* str); 262460484Sobrien 262560484Sobrien if (fp_reg_required_here (str, 0) != FAIL) 262660484Sobrien return SUCCESS; 262760484Sobrien else 262860484Sobrien { 262960484Sobrien /* Immediate expression */ 263060484Sobrien if (*((*str)++) == '#') 263160484Sobrien { 263260484Sobrien int i; 263360484Sobrien 263460484Sobrien inst.error = NULL; 263560484Sobrien 263660484Sobrien skip_whitespace (* str); 263760484Sobrien 263860484Sobrien /* First try and match exact strings, this is to guarantee that 263960484Sobrien some formats will work even for cross assembly */ 264060484Sobrien 264160484Sobrien for (i = 0; fp_const[i]; i++) 264260484Sobrien { 264360484Sobrien if (strncmp (*str, fp_const[i], strlen (fp_const[i])) == 0) 264460484Sobrien { 264560484Sobrien char *start = *str; 264660484Sobrien 264760484Sobrien *str += strlen (fp_const[i]); 264860484Sobrien if (is_end_of_line[(int)**str] || **str == '\0') 264960484Sobrien { 265060484Sobrien inst.instruction |= i + 8; 265160484Sobrien return SUCCESS; 265260484Sobrien } 265360484Sobrien *str = start; 265460484Sobrien } 265560484Sobrien } 265660484Sobrien 265760484Sobrien /* Just because we didn't get a match doesn't mean that the 265860484Sobrien constant isn't valid, just that it is in a format that we 265960484Sobrien don't automatically recognize. Try parsing it with 266060484Sobrien the standard expression routines. */ 266160484Sobrien if ((i = my_get_float_expression (str)) >= 0) 266260484Sobrien { 266360484Sobrien inst.instruction |= i + 8; 266460484Sobrien return SUCCESS; 266560484Sobrien } 266660484Sobrien 266760484Sobrien inst.error = _("Invalid floating point immediate expression"); 266860484Sobrien return FAIL; 266960484Sobrien } 267060484Sobrien inst.error = _("Floating point register or immediate expression expected"); 267160484Sobrien return FAIL; 267260484Sobrien } 267360484Sobrien} 267460484Sobrien 267560484Sobrienstatic void 267660484Sobriendo_arit (str, flags) 267760484Sobrien char * str; 267860484Sobrien unsigned long flags; 267960484Sobrien{ 268060484Sobrien skip_whitespace (str); 268160484Sobrien 268260484Sobrien if (reg_required_here (&str, 12) == FAIL 268360484Sobrien || skip_past_comma (&str) == FAIL 268460484Sobrien || reg_required_here (&str, 16) == FAIL 268560484Sobrien || skip_past_comma (&str) == FAIL 268660484Sobrien || data_op2 (&str) == FAIL) 268760484Sobrien { 268860484Sobrien if (!inst.error) 268960484Sobrien inst.error = BAD_ARGS; 269060484Sobrien return; 269160484Sobrien } 269260484Sobrien 269360484Sobrien inst.instruction |= flags; 269460484Sobrien end_of_line (str); 269560484Sobrien return; 269660484Sobrien} 269760484Sobrien 269860484Sobrienstatic void 269960484Sobriendo_adr (str, flags) 270060484Sobrien char * str; 270160484Sobrien unsigned long flags; 270260484Sobrien{ 270360484Sobrien /* This is a pseudo-op of the form "adr rd, label" to be converted 270460484Sobrien into a relative address of the form "add rd, pc, #label-.-8". */ 270560484Sobrien skip_whitespace (str); 270660484Sobrien 270760484Sobrien if (reg_required_here (&str, 12) == FAIL 270860484Sobrien || skip_past_comma (&str) == FAIL 270960484Sobrien || my_get_expression (&inst.reloc.exp, &str)) 271060484Sobrien { 271160484Sobrien if (!inst.error) 271260484Sobrien inst.error = BAD_ARGS; 271360484Sobrien return; 271460484Sobrien } 271560484Sobrien 271660484Sobrien /* Frag hacking will turn this into a sub instruction if the offset turns 271760484Sobrien out to be negative. */ 271860484Sobrien inst.reloc.type = BFD_RELOC_ARM_IMMEDIATE; 271960484Sobrien inst.reloc.exp.X_add_number -= 8; /* PC relative adjust. */ 272060484Sobrien inst.reloc.pc_rel = 1; 272160484Sobrien inst.instruction |= flags; 272260484Sobrien end_of_line (str); 272360484Sobrien return; 272460484Sobrien} 272560484Sobrien 272660484Sobrienstatic void 272760484Sobriendo_adrl (str, flags) 272860484Sobrien char * str; 272960484Sobrien unsigned long flags; 273060484Sobrien{ 273160484Sobrien /* This is a pseudo-op of the form "adrl rd, label" to be converted 273260484Sobrien into a relative address of the form: 273360484Sobrien add rd, pc, #low(label-.-8)" 273460484Sobrien add rd, rd, #high(label-.-8)" */ 273560484Sobrien 273660484Sobrien skip_whitespace (str); 273760484Sobrien 273860484Sobrien if (reg_required_here (& str, 12) == FAIL 273960484Sobrien || skip_past_comma (& str) == FAIL 274060484Sobrien || my_get_expression (& inst.reloc.exp, & str)) 274160484Sobrien { 274260484Sobrien if (!inst.error) 274360484Sobrien inst.error = BAD_ARGS; 274460484Sobrien return; 274560484Sobrien } 274660484Sobrien 274760484Sobrien end_of_line (str); 274860484Sobrien 274960484Sobrien /* Frag hacking will turn this into a sub instruction if the offset turns 275060484Sobrien out to be negative. */ 275160484Sobrien inst.reloc.type = BFD_RELOC_ARM_ADRL_IMMEDIATE; 275260484Sobrien inst.reloc.exp.X_add_number -= 8; /* PC relative adjust */ 275360484Sobrien inst.reloc.pc_rel = 1; 275460484Sobrien inst.instruction |= flags; 275560484Sobrien inst.size = INSN_SIZE * 2; 275660484Sobrien 275760484Sobrien return; 275860484Sobrien} 275960484Sobrien 276060484Sobrienstatic void 276160484Sobriendo_cmp (str, flags) 276260484Sobrien char * str; 276360484Sobrien unsigned long flags; 276460484Sobrien{ 276560484Sobrien skip_whitespace (str); 276660484Sobrien 276760484Sobrien if (reg_required_here (&str, 16) == FAIL) 276860484Sobrien { 276960484Sobrien if (!inst.error) 277060484Sobrien inst.error = BAD_ARGS; 277160484Sobrien return; 277260484Sobrien } 277360484Sobrien 277460484Sobrien if (skip_past_comma (&str) == FAIL 277560484Sobrien || data_op2 (&str) == FAIL) 277660484Sobrien { 277760484Sobrien if (!inst.error) 277860484Sobrien inst.error = BAD_ARGS; 277960484Sobrien return; 278060484Sobrien } 278160484Sobrien 278260484Sobrien inst.instruction |= flags; 278360484Sobrien if ((flags & 0x0000f000) == 0) 278460484Sobrien inst.instruction |= CONDS_BIT; 278560484Sobrien 278660484Sobrien end_of_line (str); 278760484Sobrien return; 278860484Sobrien} 278960484Sobrien 279060484Sobrienstatic void 279160484Sobriendo_mov (str, flags) 279260484Sobrien char * str; 279360484Sobrien unsigned long flags; 279460484Sobrien{ 279560484Sobrien skip_whitespace (str); 279660484Sobrien 279760484Sobrien if (reg_required_here (&str, 12) == FAIL) 279860484Sobrien { 279960484Sobrien if (!inst.error) 280060484Sobrien inst.error = BAD_ARGS; 280160484Sobrien return; 280260484Sobrien } 280360484Sobrien 280460484Sobrien if (skip_past_comma (&str) == FAIL 280560484Sobrien || data_op2 (&str) == FAIL) 280660484Sobrien { 280760484Sobrien if (!inst.error) 280860484Sobrien inst.error = BAD_ARGS; 280960484Sobrien return; 281060484Sobrien } 281160484Sobrien 281260484Sobrien inst.instruction |= flags; 281360484Sobrien end_of_line (str); 281460484Sobrien return; 281560484Sobrien} 281660484Sobrien 281760484Sobrienstatic int 281860484Sobrienldst_extend (str, hwse) 281960484Sobrien char ** str; 282060484Sobrien int hwse; 282160484Sobrien{ 282260484Sobrien int add = INDEX_UP; 282360484Sobrien 282460484Sobrien switch (**str) 282560484Sobrien { 282660484Sobrien case '#': 282760484Sobrien case '$': 282860484Sobrien (*str)++; 282960484Sobrien if (my_get_expression (& inst.reloc.exp, str)) 283060484Sobrien return FAIL; 283160484Sobrien 283260484Sobrien if (inst.reloc.exp.X_op == O_constant) 283360484Sobrien { 283460484Sobrien int value = inst.reloc.exp.X_add_number; 283560484Sobrien 283660484Sobrien if ((hwse && (value < -255 || value > 255)) 283760484Sobrien || (value < -4095 || value > 4095)) 283860484Sobrien { 283960484Sobrien inst.error = _("address offset too large"); 284060484Sobrien return FAIL; 284160484Sobrien } 284260484Sobrien 284360484Sobrien if (value < 0) 284460484Sobrien { 284560484Sobrien value = -value; 284660484Sobrien add = 0; 284760484Sobrien } 284860484Sobrien 284960484Sobrien /* Halfword and signextension instructions have the 285060484Sobrien immediate value split across bits 11..8 and bits 3..0 */ 285160484Sobrien if (hwse) 285260484Sobrien inst.instruction |= add | HWOFFSET_IMM | ((value >> 4) << 8) | (value & 0xF); 285360484Sobrien else 285460484Sobrien inst.instruction |= add | value; 285560484Sobrien } 285660484Sobrien else 285760484Sobrien { 285860484Sobrien if (hwse) 285960484Sobrien { 286060484Sobrien inst.instruction |= HWOFFSET_IMM; 286160484Sobrien inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM8; 286260484Sobrien } 286360484Sobrien else 286460484Sobrien inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM; 286560484Sobrien inst.reloc.pc_rel = 0; 286660484Sobrien } 286760484Sobrien return SUCCESS; 286860484Sobrien 286960484Sobrien case '-': 287060484Sobrien add = 0; /* and fall through */ 287160484Sobrien case '+': 287260484Sobrien (*str)++; /* and fall through */ 287360484Sobrien default: 287460484Sobrien if (reg_required_here (str, 0) == FAIL) 287560484Sobrien return FAIL; 287660484Sobrien 287760484Sobrien if (hwse) 287860484Sobrien inst.instruction |= add; 287960484Sobrien else 288060484Sobrien { 288160484Sobrien inst.instruction |= add | OFFSET_REG; 288260484Sobrien if (skip_past_comma (str) == SUCCESS) 288360484Sobrien return decode_shift (str, SHIFT_RESTRICT); 288460484Sobrien } 288560484Sobrien 288660484Sobrien return SUCCESS; 288760484Sobrien } 288860484Sobrien} 288960484Sobrien 289060484Sobrienstatic void 289160484Sobriendo_ldst (str, flags) 289260484Sobrien char * str; 289360484Sobrien unsigned long flags; 289460484Sobrien{ 289560484Sobrien int halfword = 0; 289660484Sobrien int pre_inc = 0; 289760484Sobrien int conflict_reg; 289860484Sobrien int value; 289960484Sobrien 290060484Sobrien /* This is not ideal, but it is the simplest way of dealing with the 290160484Sobrien ARM7T halfword instructions (since they use a different 290260484Sobrien encoding, but the same mnemonic): */ 290360484Sobrien halfword = (flags & 0x80000000) != 0; 290460484Sobrien if (halfword) 290560484Sobrien { 290660484Sobrien /* This is actually a load/store of a halfword, or a 290760484Sobrien signed-extension load */ 290860484Sobrien if ((cpu_variant & ARM_HALFWORD) == 0) 290960484Sobrien { 291060484Sobrien inst.error 291160484Sobrien = _("Processor does not support halfwords or signed bytes"); 291260484Sobrien return; 291360484Sobrien } 291460484Sobrien 291560484Sobrien inst.instruction = (inst.instruction & COND_MASK) 291660484Sobrien | (flags & ~COND_MASK); 291760484Sobrien 291860484Sobrien flags = 0; 291960484Sobrien } 292060484Sobrien 292160484Sobrien skip_whitespace (str); 292260484Sobrien 292360484Sobrien if ((conflict_reg = reg_required_here (& str, 12)) == FAIL) 292460484Sobrien { 292560484Sobrien if (!inst.error) 292660484Sobrien inst.error = BAD_ARGS; 292760484Sobrien return; 292860484Sobrien } 292960484Sobrien 293060484Sobrien if (skip_past_comma (& str) == FAIL) 293160484Sobrien { 293260484Sobrien inst.error = _("Address expected"); 293360484Sobrien return; 293460484Sobrien } 293560484Sobrien 293660484Sobrien if (*str == '[') 293760484Sobrien { 293860484Sobrien int reg; 293960484Sobrien 294060484Sobrien str++; 294160484Sobrien 294260484Sobrien skip_whitespace (str); 294360484Sobrien 294460484Sobrien if ((reg = reg_required_here (&str, 16)) == FAIL) 294560484Sobrien return; 294660484Sobrien 294760484Sobrien /* Conflicts can occur on stores as well as loads. */ 294860484Sobrien conflict_reg = (conflict_reg == reg); 294960484Sobrien 295060484Sobrien skip_whitespace (str); 295160484Sobrien 295260484Sobrien if (*str == ']') 295360484Sobrien { 295460484Sobrien str ++; 295560484Sobrien 295660484Sobrien if (skip_past_comma (&str) == SUCCESS) 295760484Sobrien { 295860484Sobrien /* [Rn],... (post inc) */ 295960484Sobrien if (ldst_extend (&str, halfword) == FAIL) 296060484Sobrien return; 296160484Sobrien if (conflict_reg) 296260484Sobrien as_warn (_("%s register same as write-back base"), 296360484Sobrien (inst.instruction & LOAD_BIT) ? _("destination") : _("source") ); 296460484Sobrien } 296560484Sobrien else 296660484Sobrien { 296760484Sobrien /* [Rn] */ 296860484Sobrien if (halfword) 296960484Sobrien inst.instruction |= HWOFFSET_IMM; 297060484Sobrien 297160484Sobrien skip_whitespace (str); 297260484Sobrien 297360484Sobrien if (*str == '!') 297460484Sobrien { 297560484Sobrien if (conflict_reg) 297660484Sobrien as_warn (_("%s register same as write-back base"), 297760484Sobrien (inst.instruction & LOAD_BIT) ? _("destination") : _("source") ); 297860484Sobrien str++; 297960484Sobrien inst.instruction |= WRITE_BACK; 298060484Sobrien } 298160484Sobrien 298260484Sobrien flags |= INDEX_UP; 298360484Sobrien if (! (flags & TRANS_BIT)) 298460484Sobrien pre_inc = 1; 298560484Sobrien } 298660484Sobrien } 298760484Sobrien else 298860484Sobrien { 298960484Sobrien /* [Rn,...] */ 299060484Sobrien if (skip_past_comma (&str) == FAIL) 299160484Sobrien { 299260484Sobrien inst.error = _("pre-indexed expression expected"); 299360484Sobrien return; 299460484Sobrien } 299560484Sobrien 299660484Sobrien pre_inc = 1; 299760484Sobrien if (ldst_extend (&str, halfword) == FAIL) 299860484Sobrien return; 299960484Sobrien 300060484Sobrien skip_whitespace (str); 300160484Sobrien 300260484Sobrien if (*str++ != ']') 300360484Sobrien { 300460484Sobrien inst.error = _("missing ]"); 300560484Sobrien return; 300660484Sobrien } 300760484Sobrien 300860484Sobrien skip_whitespace (str); 300960484Sobrien 301060484Sobrien if (*str == '!') 301160484Sobrien { 301260484Sobrien if (conflict_reg) 301360484Sobrien as_warn (_("%s register same as write-back base"), 301460484Sobrien (inst.instruction & LOAD_BIT) ? _("destination") : _("source") ); 301560484Sobrien str++; 301660484Sobrien inst.instruction |= WRITE_BACK; 301760484Sobrien } 301860484Sobrien } 301960484Sobrien } 302060484Sobrien else if (*str == '=') 302160484Sobrien { 302260484Sobrien /* Parse an "ldr Rd, =expr" instruction; this is another pseudo op */ 302360484Sobrien str++; 302460484Sobrien 302560484Sobrien skip_whitespace (str); 302660484Sobrien 302760484Sobrien if (my_get_expression (&inst.reloc.exp, &str)) 302860484Sobrien return; 302960484Sobrien 303060484Sobrien if (inst.reloc.exp.X_op != O_constant 303160484Sobrien && inst.reloc.exp.X_op != O_symbol) 303260484Sobrien { 303360484Sobrien inst.error = _("Constant expression expected"); 303460484Sobrien return; 303560484Sobrien } 303660484Sobrien 303760484Sobrien if (inst.reloc.exp.X_op == O_constant 303860484Sobrien && (value = validate_immediate(inst.reloc.exp.X_add_number)) != FAIL) 303960484Sobrien { 304060484Sobrien /* This can be done with a mov instruction */ 304160484Sobrien inst.instruction &= LITERAL_MASK; 304260484Sobrien inst.instruction |= INST_IMMEDIATE | (OPCODE_MOV << DATA_OP_SHIFT); 304360484Sobrien inst.instruction |= (flags & COND_MASK) | (value & 0xfff); 304460484Sobrien end_of_line(str); 304560484Sobrien return; 304660484Sobrien } 304760484Sobrien else 304860484Sobrien { 304960484Sobrien /* Insert into literal pool */ 305060484Sobrien if (add_to_lit_pool () == FAIL) 305160484Sobrien { 305260484Sobrien if (!inst.error) 305360484Sobrien inst.error = _("literal pool insertion failed"); 305460484Sobrien return; 305560484Sobrien } 305660484Sobrien 305760484Sobrien /* Change the instruction exp to point to the pool */ 305860484Sobrien if (halfword) 305960484Sobrien { 306060484Sobrien inst.instruction |= HWOFFSET_IMM; 306160484Sobrien inst.reloc.type = BFD_RELOC_ARM_HWLITERAL; 306260484Sobrien } 306360484Sobrien else 306460484Sobrien inst.reloc.type = BFD_RELOC_ARM_LITERAL; 306560484Sobrien inst.reloc.pc_rel = 1; 306660484Sobrien inst.instruction |= (REG_PC << 16); 306760484Sobrien pre_inc = 1; 306860484Sobrien } 306960484Sobrien } 307060484Sobrien else 307160484Sobrien { 307260484Sobrien if (my_get_expression (&inst.reloc.exp, &str)) 307360484Sobrien return; 307460484Sobrien 307560484Sobrien if (halfword) 307660484Sobrien { 307760484Sobrien inst.instruction |= HWOFFSET_IMM; 307860484Sobrien inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM8; 307960484Sobrien } 308060484Sobrien else 308160484Sobrien inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM; 308260484Sobrien#ifndef TE_WINCE 308360484Sobrien inst.reloc.exp.X_add_number -= 8; /* PC rel adjust */ 308460484Sobrien#endif 308560484Sobrien inst.reloc.pc_rel = 1; 308660484Sobrien inst.instruction |= (REG_PC << 16); 308760484Sobrien pre_inc = 1; 308860484Sobrien } 308960484Sobrien 309060484Sobrien if (pre_inc && (flags & TRANS_BIT)) 309160484Sobrien inst.error = _("Pre-increment instruction with translate"); 309260484Sobrien 309360484Sobrien inst.instruction |= flags | (pre_inc ? PRE_INDEX : 0); 309460484Sobrien end_of_line (str); 309560484Sobrien return; 309660484Sobrien} 309760484Sobrien 309860484Sobrienstatic long 309960484Sobrienreg_list (strp) 310060484Sobrien char ** strp; 310160484Sobrien{ 310260484Sobrien char * str = *strp; 310360484Sobrien long range = 0; 310460484Sobrien int another_range; 310560484Sobrien 310660484Sobrien /* We come back here if we get ranges concatenated by '+' or '|' */ 310760484Sobrien do 310860484Sobrien { 310960484Sobrien another_range = 0; 311060484Sobrien 311160484Sobrien if (*str == '{') 311260484Sobrien { 311360484Sobrien int in_range = 0; 311460484Sobrien int cur_reg = -1; 311560484Sobrien 311660484Sobrien str++; 311760484Sobrien do 311860484Sobrien { 311960484Sobrien int reg; 312060484Sobrien 312160484Sobrien skip_whitespace (str); 312260484Sobrien 312360484Sobrien if ((reg = reg_required_here (& str, -1)) == FAIL) 312460484Sobrien return FAIL; 312560484Sobrien 312660484Sobrien if (in_range) 312760484Sobrien { 312860484Sobrien int i; 312960484Sobrien 313060484Sobrien if (reg <= cur_reg) 313160484Sobrien { 313260484Sobrien inst.error = _("Bad range in register list"); 313360484Sobrien return FAIL; 313460484Sobrien } 313560484Sobrien 313660484Sobrien for (i = cur_reg + 1; i < reg; i++) 313760484Sobrien { 313860484Sobrien if (range & (1 << i)) 313960484Sobrien as_tsktsk 314060484Sobrien (_("Warning: Duplicated register (r%d) in register list"), 314160484Sobrien i); 314260484Sobrien else 314360484Sobrien range |= 1 << i; 314460484Sobrien } 314560484Sobrien in_range = 0; 314660484Sobrien } 314760484Sobrien 314860484Sobrien if (range & (1 << reg)) 314960484Sobrien as_tsktsk (_("Warning: Duplicated register (r%d) in register list"), 315060484Sobrien reg); 315160484Sobrien else if (reg <= cur_reg) 315260484Sobrien as_tsktsk (_("Warning: Register range not in ascending order")); 315360484Sobrien 315460484Sobrien range |= 1 << reg; 315560484Sobrien cur_reg = reg; 315660484Sobrien } while (skip_past_comma (&str) != FAIL 315760484Sobrien || (in_range = 1, *str++ == '-')); 315860484Sobrien str--; 315960484Sobrien skip_whitespace (str); 316060484Sobrien 316160484Sobrien if (*str++ != '}') 316260484Sobrien { 316360484Sobrien inst.error = _("Missing `}'"); 316460484Sobrien return FAIL; 316560484Sobrien } 316660484Sobrien } 316760484Sobrien else 316860484Sobrien { 316960484Sobrien expressionS expr; 317060484Sobrien 317160484Sobrien if (my_get_expression (&expr, &str)) 317260484Sobrien return FAIL; 317360484Sobrien 317460484Sobrien if (expr.X_op == O_constant) 317560484Sobrien { 317660484Sobrien if (expr.X_add_number 317760484Sobrien != (expr.X_add_number & 0x0000ffff)) 317860484Sobrien { 317960484Sobrien inst.error = _("invalid register mask"); 318060484Sobrien return FAIL; 318160484Sobrien } 318260484Sobrien 318360484Sobrien if ((range & expr.X_add_number) != 0) 318460484Sobrien { 318560484Sobrien int regno = range & expr.X_add_number; 318660484Sobrien 318760484Sobrien regno &= -regno; 318860484Sobrien regno = (1 << regno) - 1; 318960484Sobrien as_tsktsk 319060484Sobrien (_("Warning: Duplicated register (r%d) in register list"), 319160484Sobrien regno); 319260484Sobrien } 319360484Sobrien 319460484Sobrien range |= expr.X_add_number; 319560484Sobrien } 319660484Sobrien else 319760484Sobrien { 319860484Sobrien if (inst.reloc.type != 0) 319960484Sobrien { 320060484Sobrien inst.error = _("expression too complex"); 320160484Sobrien return FAIL; 320260484Sobrien } 320360484Sobrien 320460484Sobrien memcpy (&inst.reloc.exp, &expr, sizeof (expressionS)); 320560484Sobrien inst.reloc.type = BFD_RELOC_ARM_MULTI; 320660484Sobrien inst.reloc.pc_rel = 0; 320760484Sobrien } 320860484Sobrien } 320960484Sobrien 321060484Sobrien skip_whitespace (str); 321160484Sobrien 321260484Sobrien if (*str == '|' || *str == '+') 321360484Sobrien { 321460484Sobrien str++; 321560484Sobrien another_range = 1; 321660484Sobrien } 321760484Sobrien } while (another_range); 321860484Sobrien 321960484Sobrien *strp = str; 322060484Sobrien return range; 322160484Sobrien} 322260484Sobrien 322360484Sobrienstatic void 322460484Sobriendo_ldmstm (str, flags) 322560484Sobrien char * str; 322660484Sobrien unsigned long flags; 322760484Sobrien{ 322860484Sobrien int base_reg; 322960484Sobrien long range; 323060484Sobrien 323160484Sobrien skip_whitespace (str); 323260484Sobrien 323360484Sobrien if ((base_reg = reg_required_here (&str, 16)) == FAIL) 323460484Sobrien return; 323560484Sobrien 323660484Sobrien if (base_reg == REG_PC) 323760484Sobrien { 323860484Sobrien inst.error = _("r15 not allowed as base register"); 323960484Sobrien return; 324060484Sobrien } 324160484Sobrien 324260484Sobrien skip_whitespace (str); 324360484Sobrien 324460484Sobrien if (*str == '!') 324560484Sobrien { 324660484Sobrien flags |= WRITE_BACK; 324760484Sobrien str++; 324860484Sobrien } 324960484Sobrien 325060484Sobrien if (skip_past_comma (&str) == FAIL 325160484Sobrien || (range = reg_list (&str)) == FAIL) 325260484Sobrien { 325360484Sobrien if (! inst.error) 325460484Sobrien inst.error = BAD_ARGS; 325560484Sobrien return; 325660484Sobrien } 325760484Sobrien 325860484Sobrien if (*str == '^') 325960484Sobrien { 326060484Sobrien str++; 326160484Sobrien flags |= LDM_TYPE_2_OR_3; 326260484Sobrien } 326360484Sobrien 326460484Sobrien inst.instruction |= flags | range; 326560484Sobrien end_of_line (str); 326660484Sobrien return; 326760484Sobrien} 326860484Sobrien 326960484Sobrienstatic void 327060484Sobriendo_swi (str, flags) 327160484Sobrien char * str; 327260484Sobrien unsigned long flags; 327360484Sobrien{ 327460484Sobrien skip_whitespace (str); 327560484Sobrien 327660484Sobrien /* Allow optional leading '#'. */ 327760484Sobrien if (is_immediate_prefix (*str)) 327860484Sobrien str++; 327960484Sobrien 328060484Sobrien if (my_get_expression (& inst.reloc.exp, & str)) 328160484Sobrien return; 328260484Sobrien 328360484Sobrien inst.reloc.type = BFD_RELOC_ARM_SWI; 328460484Sobrien inst.reloc.pc_rel = 0; 328560484Sobrien inst.instruction |= flags; 328660484Sobrien 328760484Sobrien end_of_line (str); 328860484Sobrien 328960484Sobrien return; 329060484Sobrien} 329160484Sobrien 329260484Sobrienstatic void 329360484Sobriendo_swap (str, flags) 329460484Sobrien char * str; 329560484Sobrien unsigned long flags; 329660484Sobrien{ 329760484Sobrien int reg; 329860484Sobrien 329960484Sobrien skip_whitespace (str); 330060484Sobrien 330160484Sobrien if ((reg = reg_required_here (&str, 12)) == FAIL) 330260484Sobrien return; 330360484Sobrien 330460484Sobrien if (reg == REG_PC) 330560484Sobrien { 330660484Sobrien inst.error = _("r15 not allowed in swap"); 330760484Sobrien return; 330860484Sobrien } 330960484Sobrien 331060484Sobrien if (skip_past_comma (&str) == FAIL 331160484Sobrien || (reg = reg_required_here (&str, 0)) == FAIL) 331260484Sobrien { 331360484Sobrien if (!inst.error) 331460484Sobrien inst.error = BAD_ARGS; 331560484Sobrien return; 331660484Sobrien } 331760484Sobrien 331860484Sobrien if (reg == REG_PC) 331960484Sobrien { 332060484Sobrien inst.error = _("r15 not allowed in swap"); 332160484Sobrien return; 332260484Sobrien } 332360484Sobrien 332460484Sobrien if (skip_past_comma (&str) == FAIL 332560484Sobrien || *str++ != '[') 332660484Sobrien { 332760484Sobrien inst.error = BAD_ARGS; 332860484Sobrien return; 332960484Sobrien } 333060484Sobrien 333160484Sobrien skip_whitespace (str); 333260484Sobrien 333360484Sobrien if ((reg = reg_required_here (&str, 16)) == FAIL) 333460484Sobrien return; 333560484Sobrien 333660484Sobrien if (reg == REG_PC) 333760484Sobrien { 333860484Sobrien inst.error = BAD_PC; 333960484Sobrien return; 334060484Sobrien } 334160484Sobrien 334260484Sobrien skip_whitespace (str); 334360484Sobrien 334460484Sobrien if (*str++ != ']') 334560484Sobrien { 334660484Sobrien inst.error = _("missing ]"); 334760484Sobrien return; 334860484Sobrien } 334960484Sobrien 335060484Sobrien inst.instruction |= flags; 335160484Sobrien end_of_line (str); 335260484Sobrien return; 335360484Sobrien} 335460484Sobrien 335560484Sobrienstatic void 335660484Sobriendo_branch (str, flags) 335760484Sobrien char * str; 335860484Sobrien unsigned long flags ATTRIBUTE_UNUSED; 335960484Sobrien{ 336060484Sobrien if (my_get_expression (&inst.reloc.exp, &str)) 336160484Sobrien return; 336260484Sobrien 336360484Sobrien#ifdef OBJ_ELF 336460484Sobrien { 336560484Sobrien char * save_in; 336660484Sobrien 336760484Sobrien /* ScottB: February 5, 1998 */ 336860484Sobrien /* Check to see of PLT32 reloc required for the instruction. */ 336960484Sobrien 337060484Sobrien /* arm_parse_reloc() works on input_line_pointer. 337160484Sobrien We actually want to parse the operands to the branch instruction 337260484Sobrien passed in 'str'. Save the input pointer and restore it later. */ 337360484Sobrien save_in = input_line_pointer; 337460484Sobrien input_line_pointer = str; 337560484Sobrien if (inst.reloc.exp.X_op == O_symbol 337660484Sobrien && *str == '(' 337760484Sobrien && arm_parse_reloc () == BFD_RELOC_ARM_PLT32) 337860484Sobrien { 337960484Sobrien inst.reloc.type = BFD_RELOC_ARM_PLT32; 338060484Sobrien inst.reloc.pc_rel = 0; 338160484Sobrien /* Modify str to point to after parsed operands, otherwise 338260484Sobrien end_of_line() will complain about the (PLT) left in str. */ 338360484Sobrien str = input_line_pointer; 338460484Sobrien } 338560484Sobrien else 338660484Sobrien { 338760484Sobrien inst.reloc.type = BFD_RELOC_ARM_PCREL_BRANCH; 338860484Sobrien inst.reloc.pc_rel = 1; 338960484Sobrien } 339060484Sobrien input_line_pointer = save_in; 339160484Sobrien } 339260484Sobrien#else 339360484Sobrien inst.reloc.type = BFD_RELOC_ARM_PCREL_BRANCH; 339460484Sobrien inst.reloc.pc_rel = 1; 339560484Sobrien#endif /* OBJ_ELF */ 339660484Sobrien 339760484Sobrien end_of_line (str); 339860484Sobrien return; 339960484Sobrien} 340060484Sobrien 340160484Sobrienstatic void 340260484Sobriendo_bx (str, flags) 340360484Sobrien char * str; 340460484Sobrien unsigned long flags ATTRIBUTE_UNUSED; 340560484Sobrien{ 340660484Sobrien int reg; 340760484Sobrien 340860484Sobrien skip_whitespace (str); 340960484Sobrien 341060484Sobrien if ((reg = reg_required_here (&str, 0)) == FAIL) 341160484Sobrien { 341260484Sobrien inst.error = BAD_ARGS; 341360484Sobrien return; 341460484Sobrien } 341560484Sobrien 341660484Sobrien if (reg == REG_PC) 341760484Sobrien inst.error = BAD_PC; 341860484Sobrien 341960484Sobrien end_of_line (str); 342060484Sobrien} 342160484Sobrien 342260484Sobrienstatic void 342360484Sobriendo_cdp (str, flags) 342460484Sobrien char * str; 342560484Sobrien unsigned long flags ATTRIBUTE_UNUSED; 342660484Sobrien{ 342760484Sobrien /* Co-processor data operation. 342860484Sobrien Format: CDP{cond} CP#,<expr>,CRd,CRn,CRm{,<expr>} */ 342960484Sobrien skip_whitespace (str); 343060484Sobrien 343160484Sobrien if (co_proc_number (&str) == FAIL) 343260484Sobrien { 343360484Sobrien if (!inst.error) 343460484Sobrien inst.error = BAD_ARGS; 343560484Sobrien return; 343660484Sobrien } 343760484Sobrien 343860484Sobrien if (skip_past_comma (&str) == FAIL 343960484Sobrien || cp_opc_expr (&str, 20,4) == FAIL) 344060484Sobrien { 344160484Sobrien if (!inst.error) 344260484Sobrien inst.error = BAD_ARGS; 344360484Sobrien return; 344460484Sobrien } 344560484Sobrien 344660484Sobrien if (skip_past_comma (&str) == FAIL 344760484Sobrien || cp_reg_required_here (&str, 12) == FAIL) 344860484Sobrien { 344960484Sobrien if (!inst.error) 345060484Sobrien inst.error = BAD_ARGS; 345160484Sobrien return; 345260484Sobrien } 345360484Sobrien 345460484Sobrien if (skip_past_comma (&str) == FAIL 345560484Sobrien || cp_reg_required_here (&str, 16) == FAIL) 345660484Sobrien { 345760484Sobrien if (!inst.error) 345860484Sobrien inst.error = BAD_ARGS; 345960484Sobrien return; 346060484Sobrien } 346160484Sobrien 346260484Sobrien if (skip_past_comma (&str) == FAIL 346360484Sobrien || cp_reg_required_here (&str, 0) == FAIL) 346460484Sobrien { 346560484Sobrien if (!inst.error) 346660484Sobrien inst.error = BAD_ARGS; 346760484Sobrien return; 346860484Sobrien } 346960484Sobrien 347060484Sobrien if (skip_past_comma (&str) == SUCCESS) 347160484Sobrien { 347260484Sobrien if (cp_opc_expr (&str, 5, 3) == FAIL) 347360484Sobrien { 347460484Sobrien if (!inst.error) 347560484Sobrien inst.error = BAD_ARGS; 347660484Sobrien return; 347760484Sobrien } 347860484Sobrien } 347960484Sobrien 348060484Sobrien end_of_line (str); 348160484Sobrien return; 348260484Sobrien} 348360484Sobrien 348460484Sobrienstatic void 348560484Sobriendo_lstc (str, flags) 348660484Sobrien char * str; 348760484Sobrien unsigned long flags; 348860484Sobrien{ 348960484Sobrien /* Co-processor register load/store. 349060484Sobrien Format: <LDC|STC{cond}[L] CP#,CRd,<address> */ 349160484Sobrien 349260484Sobrien skip_whitespace (str); 349360484Sobrien 349460484Sobrien if (co_proc_number (&str) == FAIL) 349560484Sobrien { 349660484Sobrien if (!inst.error) 349760484Sobrien inst.error = BAD_ARGS; 349860484Sobrien return; 349960484Sobrien } 350060484Sobrien 350160484Sobrien if (skip_past_comma (&str) == FAIL 350260484Sobrien || cp_reg_required_here (&str, 12) == FAIL) 350360484Sobrien { 350460484Sobrien if (!inst.error) 350560484Sobrien inst.error = BAD_ARGS; 350660484Sobrien return; 350760484Sobrien } 350860484Sobrien 350960484Sobrien if (skip_past_comma (&str) == FAIL 351060484Sobrien || cp_address_required_here (&str) == FAIL) 351160484Sobrien { 351260484Sobrien if (! inst.error) 351360484Sobrien inst.error = BAD_ARGS; 351460484Sobrien return; 351560484Sobrien } 351660484Sobrien 351760484Sobrien inst.instruction |= flags; 351860484Sobrien end_of_line (str); 351960484Sobrien return; 352060484Sobrien} 352160484Sobrien 352260484Sobrienstatic void 352360484Sobriendo_co_reg (str, flags) 352460484Sobrien char * str; 352560484Sobrien unsigned long flags; 352660484Sobrien{ 352760484Sobrien /* Co-processor register transfer. 352860484Sobrien Format: <MCR|MRC>{cond} CP#,<expr1>,Rd,CRn,CRm{,<expr2>} */ 352960484Sobrien 353060484Sobrien skip_whitespace (str); 353160484Sobrien 353260484Sobrien if (co_proc_number (&str) == FAIL) 353360484Sobrien { 353460484Sobrien if (!inst.error) 353560484Sobrien inst.error = BAD_ARGS; 353660484Sobrien return; 353760484Sobrien } 353860484Sobrien 353960484Sobrien if (skip_past_comma (&str) == FAIL 354060484Sobrien || cp_opc_expr (&str, 21, 3) == FAIL) 354160484Sobrien { 354260484Sobrien if (!inst.error) 354360484Sobrien inst.error = BAD_ARGS; 354460484Sobrien return; 354560484Sobrien } 354660484Sobrien 354760484Sobrien if (skip_past_comma (&str) == FAIL 354860484Sobrien || reg_required_here (&str, 12) == FAIL) 354960484Sobrien { 355060484Sobrien if (!inst.error) 355160484Sobrien inst.error = BAD_ARGS; 355260484Sobrien return; 355360484Sobrien } 355460484Sobrien 355560484Sobrien if (skip_past_comma (&str) == FAIL 355660484Sobrien || cp_reg_required_here (&str, 16) == FAIL) 355760484Sobrien { 355860484Sobrien if (!inst.error) 355960484Sobrien inst.error = BAD_ARGS; 356060484Sobrien return; 356160484Sobrien } 356260484Sobrien 356360484Sobrien if (skip_past_comma (&str) == FAIL 356460484Sobrien || cp_reg_required_here (&str, 0) == FAIL) 356560484Sobrien { 356660484Sobrien if (!inst.error) 356760484Sobrien inst.error = BAD_ARGS; 356860484Sobrien return; 356960484Sobrien } 357060484Sobrien 357160484Sobrien if (skip_past_comma (&str) == SUCCESS) 357260484Sobrien { 357360484Sobrien if (cp_opc_expr (&str, 5, 3) == FAIL) 357460484Sobrien { 357560484Sobrien if (!inst.error) 357660484Sobrien inst.error = BAD_ARGS; 357760484Sobrien return; 357860484Sobrien } 357960484Sobrien } 358060484Sobrien if (flags) 358160484Sobrien { 358260484Sobrien inst.error = BAD_COND; 358360484Sobrien } 358460484Sobrien 358560484Sobrien end_of_line (str); 358660484Sobrien return; 358760484Sobrien} 358860484Sobrien 358960484Sobrienstatic void 359060484Sobriendo_fp_ctrl (str, flags) 359160484Sobrien char * str; 359260484Sobrien unsigned long flags ATTRIBUTE_UNUSED; 359360484Sobrien{ 359460484Sobrien /* FP control registers. 359560484Sobrien Format: <WFS|RFS|WFC|RFC>{cond} Rn */ 359660484Sobrien 359760484Sobrien skip_whitespace (str); 359860484Sobrien 359960484Sobrien if (reg_required_here (&str, 12) == FAIL) 360060484Sobrien { 360160484Sobrien if (!inst.error) 360260484Sobrien inst.error = BAD_ARGS; 360360484Sobrien return; 360460484Sobrien } 360560484Sobrien 360660484Sobrien end_of_line (str); 360760484Sobrien return; 360860484Sobrien} 360960484Sobrien 361060484Sobrienstatic void 361160484Sobriendo_fp_ldst (str, flags) 361260484Sobrien char * str; 361360484Sobrien unsigned long flags ATTRIBUTE_UNUSED; 361460484Sobrien{ 361560484Sobrien skip_whitespace (str); 361660484Sobrien 361760484Sobrien switch (inst.suffix) 361860484Sobrien { 361960484Sobrien case SUFF_S: 362060484Sobrien break; 362160484Sobrien case SUFF_D: 362260484Sobrien inst.instruction |= CP_T_X; 362360484Sobrien break; 362460484Sobrien case SUFF_E: 362560484Sobrien inst.instruction |= CP_T_Y; 362660484Sobrien break; 362760484Sobrien case SUFF_P: 362860484Sobrien inst.instruction |= CP_T_X | CP_T_Y; 362960484Sobrien break; 363060484Sobrien default: 363160484Sobrien abort (); 363260484Sobrien } 363360484Sobrien 363460484Sobrien if (fp_reg_required_here (&str, 12) == FAIL) 363560484Sobrien { 363660484Sobrien if (!inst.error) 363760484Sobrien inst.error = BAD_ARGS; 363860484Sobrien return; 363960484Sobrien } 364060484Sobrien 364160484Sobrien if (skip_past_comma (&str) == FAIL 364260484Sobrien || cp_address_required_here (&str) == FAIL) 364360484Sobrien { 364460484Sobrien if (!inst.error) 364560484Sobrien inst.error = BAD_ARGS; 364660484Sobrien return; 364760484Sobrien } 364860484Sobrien 364960484Sobrien end_of_line (str); 365060484Sobrien} 365160484Sobrien 365260484Sobrienstatic void 365360484Sobriendo_fp_ldmstm (str, flags) 365460484Sobrien char * str; 365560484Sobrien unsigned long flags; 365660484Sobrien{ 365760484Sobrien int num_regs; 365860484Sobrien 365960484Sobrien skip_whitespace (str); 366060484Sobrien 366160484Sobrien if (fp_reg_required_here (&str, 12) == FAIL) 366260484Sobrien { 366360484Sobrien if (! inst.error) 366460484Sobrien inst.error = BAD_ARGS; 366560484Sobrien return; 366660484Sobrien } 366760484Sobrien 366860484Sobrien /* Get Number of registers to transfer */ 366960484Sobrien if (skip_past_comma (&str) == FAIL 367060484Sobrien || my_get_expression (&inst.reloc.exp, &str)) 367160484Sobrien { 367260484Sobrien if (! inst.error) 367360484Sobrien inst.error = _("constant expression expected"); 367460484Sobrien return; 367560484Sobrien } 367660484Sobrien 367760484Sobrien if (inst.reloc.exp.X_op != O_constant) 367860484Sobrien { 367960484Sobrien inst.error = _("Constant value required for number of registers"); 368060484Sobrien return; 368160484Sobrien } 368260484Sobrien 368360484Sobrien num_regs = inst.reloc.exp.X_add_number; 368460484Sobrien 368560484Sobrien if (num_regs < 1 || num_regs > 4) 368660484Sobrien { 368760484Sobrien inst.error = _("number of registers must be in the range [1:4]"); 368860484Sobrien return; 368960484Sobrien } 369060484Sobrien 369160484Sobrien switch (num_regs) 369260484Sobrien { 369360484Sobrien case 1: 369460484Sobrien inst.instruction |= CP_T_X; 369560484Sobrien break; 369660484Sobrien case 2: 369760484Sobrien inst.instruction |= CP_T_Y; 369860484Sobrien break; 369960484Sobrien case 3: 370060484Sobrien inst.instruction |= CP_T_Y | CP_T_X; 370160484Sobrien break; 370260484Sobrien case 4: 370360484Sobrien break; 370460484Sobrien default: 370560484Sobrien abort (); 370660484Sobrien } 370760484Sobrien 370860484Sobrien if (flags) 370960484Sobrien { 371060484Sobrien int reg; 371160484Sobrien int write_back; 371260484Sobrien int offset; 371360484Sobrien 371460484Sobrien /* The instruction specified "ea" or "fd", so we can only accept 371560484Sobrien [Rn]{!}. The instruction does not really support stacking or 371660484Sobrien unstacking, so we have to emulate these by setting appropriate 371760484Sobrien bits and offsets. */ 371860484Sobrien if (skip_past_comma (&str) == FAIL 371960484Sobrien || *str != '[') 372060484Sobrien { 372160484Sobrien if (! inst.error) 372260484Sobrien inst.error = BAD_ARGS; 372360484Sobrien return; 372460484Sobrien } 372560484Sobrien 372660484Sobrien str++; 372760484Sobrien skip_whitespace (str); 372860484Sobrien 372960484Sobrien if ((reg = reg_required_here (&str, 16)) == FAIL) 373060484Sobrien return; 373160484Sobrien 373260484Sobrien skip_whitespace (str); 373360484Sobrien 373460484Sobrien if (*str != ']') 373560484Sobrien { 373660484Sobrien inst.error = BAD_ARGS; 373760484Sobrien return; 373860484Sobrien } 373960484Sobrien 374060484Sobrien str++; 374160484Sobrien if (*str == '!') 374260484Sobrien { 374360484Sobrien write_back = 1; 374460484Sobrien str++; 374560484Sobrien if (reg == REG_PC) 374660484Sobrien { 374760484Sobrien inst.error = _("R15 not allowed as base register with write-back"); 374860484Sobrien return; 374960484Sobrien } 375060484Sobrien } 375160484Sobrien else 375260484Sobrien write_back = 0; 375360484Sobrien 375460484Sobrien if (flags & CP_T_Pre) 375560484Sobrien { 375660484Sobrien /* Pre-decrement */ 375760484Sobrien offset = 3 * num_regs; 375860484Sobrien if (write_back) 375960484Sobrien flags |= CP_T_WB; 376060484Sobrien } 376160484Sobrien else 376260484Sobrien { 376360484Sobrien /* Post-increment */ 376460484Sobrien if (write_back) 376560484Sobrien { 376660484Sobrien flags |= CP_T_WB; 376760484Sobrien offset = 3 * num_regs; 376860484Sobrien } 376960484Sobrien else 377060484Sobrien { 377160484Sobrien /* No write-back, so convert this into a standard pre-increment 377260484Sobrien instruction -- aesthetically more pleasing. */ 377360484Sobrien flags = CP_T_Pre | CP_T_UD; 377460484Sobrien offset = 0; 377560484Sobrien } 377660484Sobrien } 377760484Sobrien 377860484Sobrien inst.instruction |= flags | offset; 377960484Sobrien } 378060484Sobrien else if (skip_past_comma (&str) == FAIL 378160484Sobrien || cp_address_required_here (&str) == FAIL) 378260484Sobrien { 378360484Sobrien if (! inst.error) 378460484Sobrien inst.error = BAD_ARGS; 378560484Sobrien return; 378660484Sobrien } 378760484Sobrien 378860484Sobrien end_of_line (str); 378960484Sobrien} 379060484Sobrien 379160484Sobrienstatic void 379260484Sobriendo_fp_dyadic (str, flags) 379360484Sobrien char * str; 379460484Sobrien unsigned long flags; 379560484Sobrien{ 379660484Sobrien skip_whitespace (str); 379760484Sobrien 379860484Sobrien switch (inst.suffix) 379960484Sobrien { 380060484Sobrien case SUFF_S: 380160484Sobrien break; 380260484Sobrien case SUFF_D: 380360484Sobrien inst.instruction |= 0x00000080; 380460484Sobrien break; 380560484Sobrien case SUFF_E: 380660484Sobrien inst.instruction |= 0x00080000; 380760484Sobrien break; 380860484Sobrien default: 380960484Sobrien abort (); 381060484Sobrien } 381160484Sobrien 381260484Sobrien if (fp_reg_required_here (&str, 12) == FAIL) 381360484Sobrien { 381460484Sobrien if (! inst.error) 381560484Sobrien inst.error = BAD_ARGS; 381660484Sobrien return; 381760484Sobrien } 381860484Sobrien 381960484Sobrien if (skip_past_comma (&str) == FAIL 382060484Sobrien || fp_reg_required_here (&str, 16) == FAIL) 382160484Sobrien { 382260484Sobrien if (! inst.error) 382360484Sobrien inst.error = BAD_ARGS; 382460484Sobrien return; 382560484Sobrien } 382660484Sobrien 382760484Sobrien if (skip_past_comma (&str) == FAIL 382860484Sobrien || fp_op2 (&str) == FAIL) 382960484Sobrien { 383060484Sobrien if (! inst.error) 383160484Sobrien inst.error = BAD_ARGS; 383260484Sobrien return; 383360484Sobrien } 383460484Sobrien 383560484Sobrien inst.instruction |= flags; 383660484Sobrien end_of_line (str); 383760484Sobrien return; 383860484Sobrien} 383960484Sobrien 384060484Sobrienstatic void 384160484Sobriendo_fp_monadic (str, flags) 384260484Sobrien char * str; 384360484Sobrien unsigned long flags; 384460484Sobrien{ 384560484Sobrien skip_whitespace (str); 384660484Sobrien 384760484Sobrien switch (inst.suffix) 384860484Sobrien { 384960484Sobrien case SUFF_S: 385060484Sobrien break; 385160484Sobrien case SUFF_D: 385260484Sobrien inst.instruction |= 0x00000080; 385360484Sobrien break; 385460484Sobrien case SUFF_E: 385560484Sobrien inst.instruction |= 0x00080000; 385660484Sobrien break; 385760484Sobrien default: 385860484Sobrien abort (); 385960484Sobrien } 386060484Sobrien 386160484Sobrien if (fp_reg_required_here (&str, 12) == FAIL) 386260484Sobrien { 386360484Sobrien if (! inst.error) 386460484Sobrien inst.error = BAD_ARGS; 386560484Sobrien return; 386660484Sobrien } 386760484Sobrien 386860484Sobrien if (skip_past_comma (&str) == FAIL 386960484Sobrien || fp_op2 (&str) == FAIL) 387060484Sobrien { 387160484Sobrien if (! inst.error) 387260484Sobrien inst.error = BAD_ARGS; 387360484Sobrien return; 387460484Sobrien } 387560484Sobrien 387660484Sobrien inst.instruction |= flags; 387760484Sobrien end_of_line (str); 387860484Sobrien return; 387960484Sobrien} 388060484Sobrien 388160484Sobrienstatic void 388260484Sobriendo_fp_cmp (str, flags) 388360484Sobrien char * str; 388460484Sobrien unsigned long flags; 388560484Sobrien{ 388660484Sobrien skip_whitespace (str); 388760484Sobrien 388860484Sobrien if (fp_reg_required_here (&str, 16) == FAIL) 388960484Sobrien { 389060484Sobrien if (! inst.error) 389160484Sobrien inst.error = BAD_ARGS; 389260484Sobrien return; 389360484Sobrien } 389460484Sobrien 389560484Sobrien if (skip_past_comma (&str) == FAIL 389660484Sobrien || fp_op2 (&str) == FAIL) 389760484Sobrien { 389860484Sobrien if (! inst.error) 389960484Sobrien inst.error = BAD_ARGS; 390060484Sobrien return; 390160484Sobrien } 390260484Sobrien 390360484Sobrien inst.instruction |= flags; 390460484Sobrien end_of_line (str); 390560484Sobrien return; 390660484Sobrien} 390760484Sobrien 390860484Sobrienstatic void 390960484Sobriendo_fp_from_reg (str, flags) 391060484Sobrien char * str; 391160484Sobrien unsigned long flags; 391260484Sobrien{ 391360484Sobrien skip_whitespace (str); 391460484Sobrien 391560484Sobrien switch (inst.suffix) 391660484Sobrien { 391760484Sobrien case SUFF_S: 391860484Sobrien break; 391960484Sobrien case SUFF_D: 392060484Sobrien inst.instruction |= 0x00000080; 392160484Sobrien break; 392260484Sobrien case SUFF_E: 392360484Sobrien inst.instruction |= 0x00080000; 392460484Sobrien break; 392560484Sobrien default: 392660484Sobrien abort (); 392760484Sobrien } 392860484Sobrien 392960484Sobrien if (fp_reg_required_here (&str, 16) == FAIL) 393060484Sobrien { 393160484Sobrien if (! inst.error) 393260484Sobrien inst.error = BAD_ARGS; 393360484Sobrien return; 393460484Sobrien } 393560484Sobrien 393660484Sobrien if (skip_past_comma (&str) == FAIL 393760484Sobrien || reg_required_here (&str, 12) == FAIL) 393860484Sobrien { 393960484Sobrien if (! inst.error) 394060484Sobrien inst.error = BAD_ARGS; 394160484Sobrien return; 394260484Sobrien } 394360484Sobrien 394460484Sobrien inst.instruction |= flags; 394560484Sobrien end_of_line (str); 394660484Sobrien return; 394760484Sobrien} 394860484Sobrien 394960484Sobrienstatic void 395060484Sobriendo_fp_to_reg (str, flags) 395160484Sobrien char * str; 395260484Sobrien unsigned long flags; 395360484Sobrien{ 395460484Sobrien skip_whitespace (str); 395560484Sobrien 395660484Sobrien if (reg_required_here (&str, 12) == FAIL) 395760484Sobrien return; 395860484Sobrien 395960484Sobrien if (skip_past_comma (&str) == FAIL 396060484Sobrien || fp_reg_required_here (&str, 0) == FAIL) 396160484Sobrien { 396260484Sobrien if (! inst.error) 396360484Sobrien inst.error = BAD_ARGS; 396460484Sobrien return; 396560484Sobrien } 396660484Sobrien 396760484Sobrien inst.instruction |= flags; 396860484Sobrien end_of_line (str); 396960484Sobrien return; 397060484Sobrien} 397160484Sobrien 397260484Sobrien/* Thumb specific routines */ 397360484Sobrien 397460484Sobrien/* Parse and validate that a register is of the right form, this saves 397560484Sobrien repeated checking of this information in many similar cases. 397660484Sobrien Unlike the 32-bit case we do not insert the register into the opcode 397760484Sobrien here, since the position is often unknown until the full instruction 397860484Sobrien has been parsed. */ 397960484Sobrienstatic int 398060484Sobrienthumb_reg (strp, hi_lo) 398160484Sobrien char ** strp; 398260484Sobrien int hi_lo; 398360484Sobrien{ 398460484Sobrien int reg; 398560484Sobrien 398660484Sobrien if ((reg = reg_required_here (strp, -1)) == FAIL) 398760484Sobrien return FAIL; 398860484Sobrien 398960484Sobrien switch (hi_lo) 399060484Sobrien { 399160484Sobrien case THUMB_REG_LO: 399260484Sobrien if (reg > 7) 399360484Sobrien { 399460484Sobrien inst.error = _("lo register required"); 399560484Sobrien return FAIL; 399660484Sobrien } 399760484Sobrien break; 399860484Sobrien 399960484Sobrien case THUMB_REG_HI: 400060484Sobrien if (reg < 8) 400160484Sobrien { 400260484Sobrien inst.error = _("hi register required"); 400360484Sobrien return FAIL; 400460484Sobrien } 400560484Sobrien break; 400660484Sobrien 400760484Sobrien default: 400860484Sobrien break; 400960484Sobrien } 401060484Sobrien 401160484Sobrien return reg; 401260484Sobrien} 401360484Sobrien 401460484Sobrien/* Parse an add or subtract instruction, SUBTRACT is non-zero if the opcode 401560484Sobrien was SUB. */ 401660484Sobrienstatic void 401760484Sobrienthumb_add_sub (str, subtract) 401860484Sobrien char * str; 401960484Sobrien int subtract; 402060484Sobrien{ 402160484Sobrien int Rd, Rs, Rn = FAIL; 402260484Sobrien 402360484Sobrien skip_whitespace (str); 402460484Sobrien 402560484Sobrien if ((Rd = thumb_reg (&str, THUMB_REG_ANY)) == FAIL 402660484Sobrien || skip_past_comma (&str) == FAIL) 402760484Sobrien { 402860484Sobrien if (! inst.error) 402960484Sobrien inst.error = BAD_ARGS; 403060484Sobrien return; 403160484Sobrien } 403260484Sobrien 403360484Sobrien if (is_immediate_prefix (*str)) 403460484Sobrien { 403560484Sobrien Rs = Rd; 403660484Sobrien str++; 403760484Sobrien if (my_get_expression (&inst.reloc.exp, &str)) 403860484Sobrien return; 403960484Sobrien } 404060484Sobrien else 404160484Sobrien { 404260484Sobrien if ((Rs = thumb_reg (&str, THUMB_REG_ANY)) == FAIL) 404360484Sobrien return; 404460484Sobrien 404560484Sobrien if (skip_past_comma (&str) == FAIL) 404660484Sobrien { 404760484Sobrien /* Two operand format, shuffle the registers and pretend there 404860484Sobrien are 3 */ 404960484Sobrien Rn = Rs; 405060484Sobrien Rs = Rd; 405160484Sobrien } 405260484Sobrien else if (is_immediate_prefix (*str)) 405360484Sobrien { 405460484Sobrien str++; 405560484Sobrien if (my_get_expression (&inst.reloc.exp, &str)) 405660484Sobrien return; 405760484Sobrien } 405860484Sobrien else if ((Rn = thumb_reg (&str, THUMB_REG_ANY)) == FAIL) 405960484Sobrien return; 406060484Sobrien } 406160484Sobrien 406260484Sobrien /* We now have Rd and Rs set to registers, and Rn set to a register or FAIL; 406360484Sobrien for the latter case, EXPR contains the immediate that was found. */ 406460484Sobrien if (Rn != FAIL) 406560484Sobrien { 406660484Sobrien /* All register format. */ 406760484Sobrien if (Rd > 7 || Rs > 7 || Rn > 7) 406860484Sobrien { 406960484Sobrien if (Rs != Rd) 407060484Sobrien { 407160484Sobrien inst.error = _("dest and source1 must be the same register"); 407260484Sobrien return; 407360484Sobrien } 407460484Sobrien 407560484Sobrien /* Can't do this for SUB */ 407660484Sobrien if (subtract) 407760484Sobrien { 407860484Sobrien inst.error = _("subtract valid only on lo regs"); 407960484Sobrien return; 408060484Sobrien } 408160484Sobrien 408260484Sobrien inst.instruction = (T_OPCODE_ADD_HI 408360484Sobrien | (Rd > 7 ? THUMB_H1 : 0) 408460484Sobrien | (Rn > 7 ? THUMB_H2 : 0)); 408560484Sobrien inst.instruction |= (Rd & 7) | ((Rn & 7) << 3); 408660484Sobrien } 408760484Sobrien else 408860484Sobrien { 408960484Sobrien inst.instruction = subtract ? T_OPCODE_SUB_R3 : T_OPCODE_ADD_R3; 409060484Sobrien inst.instruction |= Rd | (Rs << 3) | (Rn << 6); 409160484Sobrien } 409260484Sobrien } 409360484Sobrien else 409460484Sobrien { 409560484Sobrien /* Immediate expression, now things start to get nasty. */ 409660484Sobrien 409760484Sobrien /* First deal with HI regs, only very restricted cases allowed: 409860484Sobrien Adjusting SP, and using PC or SP to get an address. */ 409960484Sobrien if ((Rd > 7 && (Rd != REG_SP || Rs != REG_SP)) 410060484Sobrien || (Rs > 7 && Rs != REG_SP && Rs != REG_PC)) 410160484Sobrien { 410260484Sobrien inst.error = _("invalid Hi register with immediate"); 410360484Sobrien return; 410460484Sobrien } 410560484Sobrien 410660484Sobrien if (inst.reloc.exp.X_op != O_constant) 410760484Sobrien { 410860484Sobrien /* Value isn't known yet, all we can do is store all the fragments 410960484Sobrien we know about in the instruction and let the reloc hacking 411060484Sobrien work it all out. */ 411160484Sobrien inst.instruction = (subtract ? 0x8000 : 0) | (Rd << 4) | Rs; 411260484Sobrien inst.reloc.type = BFD_RELOC_ARM_THUMB_ADD; 411360484Sobrien } 411460484Sobrien else 411560484Sobrien { 411660484Sobrien int offset = inst.reloc.exp.X_add_number; 411760484Sobrien 411860484Sobrien if (subtract) 411960484Sobrien offset = -offset; 412060484Sobrien 412160484Sobrien if (offset < 0) 412260484Sobrien { 412360484Sobrien offset = -offset; 412460484Sobrien subtract = 1; 412560484Sobrien 412660484Sobrien /* Quick check, in case offset is MIN_INT */ 412760484Sobrien if (offset < 0) 412860484Sobrien { 412960484Sobrien inst.error = _("immediate value out of range"); 413060484Sobrien return; 413160484Sobrien } 413260484Sobrien } 413360484Sobrien else 413460484Sobrien subtract = 0; 413560484Sobrien 413660484Sobrien if (Rd == REG_SP) 413760484Sobrien { 413860484Sobrien if (offset & ~0x1fc) 413960484Sobrien { 414060484Sobrien inst.error = _("invalid immediate value for stack adjust"); 414160484Sobrien return; 414260484Sobrien } 414360484Sobrien inst.instruction = subtract ? T_OPCODE_SUB_ST : T_OPCODE_ADD_ST; 414460484Sobrien inst.instruction |= offset >> 2; 414560484Sobrien } 414660484Sobrien else if (Rs == REG_PC || Rs == REG_SP) 414760484Sobrien { 414860484Sobrien if (subtract 414960484Sobrien || (offset & ~0x3fc)) 415060484Sobrien { 415160484Sobrien inst.error = _("invalid immediate for address calculation"); 415260484Sobrien return; 415360484Sobrien } 415460484Sobrien inst.instruction = (Rs == REG_PC ? T_OPCODE_ADD_PC 415560484Sobrien : T_OPCODE_ADD_SP); 415660484Sobrien inst.instruction |= (Rd << 8) | (offset >> 2); 415760484Sobrien } 415860484Sobrien else if (Rs == Rd) 415960484Sobrien { 416060484Sobrien if (offset & ~0xff) 416160484Sobrien { 416260484Sobrien inst.error = _("immediate value out of range"); 416360484Sobrien return; 416460484Sobrien } 416560484Sobrien inst.instruction = subtract ? T_OPCODE_SUB_I8 : T_OPCODE_ADD_I8; 416660484Sobrien inst.instruction |= (Rd << 8) | offset; 416760484Sobrien } 416860484Sobrien else 416960484Sobrien { 417060484Sobrien if (offset & ~0x7) 417160484Sobrien { 417260484Sobrien inst.error = _("immediate value out of range"); 417360484Sobrien return; 417460484Sobrien } 417560484Sobrien inst.instruction = subtract ? T_OPCODE_SUB_I3 : T_OPCODE_ADD_I3; 417660484Sobrien inst.instruction |= Rd | (Rs << 3) | (offset << 6); 417760484Sobrien } 417860484Sobrien } 417960484Sobrien } 418060484Sobrien 418160484Sobrien end_of_line (str); 418260484Sobrien} 418360484Sobrien 418460484Sobrienstatic void 418560484Sobrienthumb_shift (str, shift) 418660484Sobrien char * str; 418760484Sobrien int shift; 418860484Sobrien{ 418960484Sobrien int Rd, Rs, Rn = FAIL; 419060484Sobrien 419160484Sobrien skip_whitespace (str); 419260484Sobrien 419360484Sobrien if ((Rd = thumb_reg (&str, THUMB_REG_LO)) == FAIL 419460484Sobrien || skip_past_comma (&str) == FAIL) 419560484Sobrien { 419660484Sobrien if (! inst.error) 419760484Sobrien inst.error = BAD_ARGS; 419860484Sobrien return; 419960484Sobrien } 420060484Sobrien 420160484Sobrien if (is_immediate_prefix (*str)) 420260484Sobrien { 420360484Sobrien /* Two operand immediate format, set Rs to Rd. */ 420460484Sobrien Rs = Rd; 420560484Sobrien str ++; 420660484Sobrien if (my_get_expression (&inst.reloc.exp, &str)) 420760484Sobrien return; 420860484Sobrien } 420960484Sobrien else 421060484Sobrien { 421160484Sobrien if ((Rs = thumb_reg (&str, THUMB_REG_LO)) == FAIL) 421260484Sobrien return; 421360484Sobrien 421460484Sobrien if (skip_past_comma (&str) == FAIL) 421560484Sobrien { 421660484Sobrien /* Two operand format, shuffle the registers and pretend there 421760484Sobrien are 3 */ 421860484Sobrien Rn = Rs; 421960484Sobrien Rs = Rd; 422060484Sobrien } 422160484Sobrien else if (is_immediate_prefix (*str)) 422260484Sobrien { 422360484Sobrien str++; 422460484Sobrien if (my_get_expression (&inst.reloc.exp, &str)) 422560484Sobrien return; 422660484Sobrien } 422760484Sobrien else if ((Rn = thumb_reg (&str, THUMB_REG_LO)) == FAIL) 422860484Sobrien return; 422960484Sobrien } 423060484Sobrien 423160484Sobrien /* We now have Rd and Rs set to registers, and Rn set to a register or FAIL; 423260484Sobrien for the latter case, EXPR contains the immediate that was found. */ 423360484Sobrien 423460484Sobrien if (Rn != FAIL) 423560484Sobrien { 423660484Sobrien if (Rs != Rd) 423760484Sobrien { 423860484Sobrien inst.error = _("source1 and dest must be same register"); 423960484Sobrien return; 424060484Sobrien } 424160484Sobrien 424260484Sobrien switch (shift) 424360484Sobrien { 424460484Sobrien case THUMB_ASR: inst.instruction = T_OPCODE_ASR_R; break; 424560484Sobrien case THUMB_LSL: inst.instruction = T_OPCODE_LSL_R; break; 424660484Sobrien case THUMB_LSR: inst.instruction = T_OPCODE_LSR_R; break; 424760484Sobrien } 424860484Sobrien 424960484Sobrien inst.instruction |= Rd | (Rn << 3); 425060484Sobrien } 425160484Sobrien else 425260484Sobrien { 425360484Sobrien switch (shift) 425460484Sobrien { 425560484Sobrien case THUMB_ASR: inst.instruction = T_OPCODE_ASR_I; break; 425660484Sobrien case THUMB_LSL: inst.instruction = T_OPCODE_LSL_I; break; 425760484Sobrien case THUMB_LSR: inst.instruction = T_OPCODE_LSR_I; break; 425860484Sobrien } 425960484Sobrien 426060484Sobrien if (inst.reloc.exp.X_op != O_constant) 426160484Sobrien { 426260484Sobrien /* Value isn't known yet, create a dummy reloc and let reloc 426360484Sobrien hacking fix it up */ 426460484Sobrien 426560484Sobrien inst.reloc.type = BFD_RELOC_ARM_THUMB_SHIFT; 426660484Sobrien } 426760484Sobrien else 426860484Sobrien { 426960484Sobrien unsigned shift_value = inst.reloc.exp.X_add_number; 427060484Sobrien 427160484Sobrien if (shift_value > 32 || (shift_value == 32 && shift == THUMB_LSL)) 427260484Sobrien { 427360484Sobrien inst.error = _("Invalid immediate for shift"); 427460484Sobrien return; 427560484Sobrien } 427660484Sobrien 427760484Sobrien /* Shifts of zero are handled by converting to LSL */ 427860484Sobrien if (shift_value == 0) 427960484Sobrien inst.instruction = T_OPCODE_LSL_I; 428060484Sobrien 428160484Sobrien /* Shifts of 32 are encoded as a shift of zero */ 428260484Sobrien if (shift_value == 32) 428360484Sobrien shift_value = 0; 428460484Sobrien 428560484Sobrien inst.instruction |= shift_value << 6; 428660484Sobrien } 428760484Sobrien 428860484Sobrien inst.instruction |= Rd | (Rs << 3); 428960484Sobrien } 429060484Sobrien 429160484Sobrien end_of_line (str); 429260484Sobrien} 429360484Sobrien 429460484Sobrienstatic void 429560484Sobrienthumb_mov_compare (str, move) 429660484Sobrien char * str; 429760484Sobrien int move; 429860484Sobrien{ 429960484Sobrien int Rd, Rs = FAIL; 430060484Sobrien 430160484Sobrien skip_whitespace (str); 430260484Sobrien 430360484Sobrien if ((Rd = thumb_reg (&str, THUMB_REG_ANY)) == FAIL 430460484Sobrien || skip_past_comma (&str) == FAIL) 430560484Sobrien { 430660484Sobrien if (! inst.error) 430760484Sobrien inst.error = BAD_ARGS; 430860484Sobrien return; 430960484Sobrien } 431060484Sobrien 431160484Sobrien if (is_immediate_prefix (*str)) 431260484Sobrien { 431360484Sobrien str++; 431460484Sobrien if (my_get_expression (&inst.reloc.exp, &str)) 431560484Sobrien return; 431660484Sobrien } 431760484Sobrien else if ((Rs = thumb_reg (&str, THUMB_REG_ANY)) == FAIL) 431860484Sobrien return; 431960484Sobrien 432060484Sobrien if (Rs != FAIL) 432160484Sobrien { 432260484Sobrien if (Rs < 8 && Rd < 8) 432360484Sobrien { 432460484Sobrien if (move == THUMB_MOVE) 432560484Sobrien /* A move of two lowregs is encoded as ADD Rd, Rs, #0 432660484Sobrien since a MOV instruction produces unpredictable results */ 432760484Sobrien inst.instruction = T_OPCODE_ADD_I3; 432860484Sobrien else 432960484Sobrien inst.instruction = T_OPCODE_CMP_LR; 433060484Sobrien inst.instruction |= Rd | (Rs << 3); 433160484Sobrien } 433260484Sobrien else 433360484Sobrien { 433460484Sobrien if (move == THUMB_MOVE) 433560484Sobrien inst.instruction = T_OPCODE_MOV_HR; 433660484Sobrien else 433760484Sobrien inst.instruction = T_OPCODE_CMP_HR; 433860484Sobrien 433960484Sobrien if (Rd > 7) 434060484Sobrien inst.instruction |= THUMB_H1; 434160484Sobrien 434260484Sobrien if (Rs > 7) 434360484Sobrien inst.instruction |= THUMB_H2; 434460484Sobrien 434560484Sobrien inst.instruction |= (Rd & 7) | ((Rs & 7) << 3); 434660484Sobrien } 434760484Sobrien } 434860484Sobrien else 434960484Sobrien { 435060484Sobrien if (Rd > 7) 435160484Sobrien { 435260484Sobrien inst.error = _("only lo regs allowed with immediate"); 435360484Sobrien return; 435460484Sobrien } 435560484Sobrien 435660484Sobrien if (move == THUMB_MOVE) 435760484Sobrien inst.instruction = T_OPCODE_MOV_I8; 435860484Sobrien else 435960484Sobrien inst.instruction = T_OPCODE_CMP_I8; 436060484Sobrien 436160484Sobrien inst.instruction |= Rd << 8; 436260484Sobrien 436360484Sobrien if (inst.reloc.exp.X_op != O_constant) 436460484Sobrien inst.reloc.type = BFD_RELOC_ARM_THUMB_IMM; 436560484Sobrien else 436660484Sobrien { 436760484Sobrien unsigned value = inst.reloc.exp.X_add_number; 436860484Sobrien 436960484Sobrien if (value > 255) 437060484Sobrien { 437160484Sobrien inst.error = _("invalid immediate"); 437260484Sobrien return; 437360484Sobrien } 437460484Sobrien 437560484Sobrien inst.instruction |= value; 437660484Sobrien } 437760484Sobrien } 437860484Sobrien 437960484Sobrien end_of_line (str); 438060484Sobrien} 438160484Sobrien 438260484Sobrienstatic void 438360484Sobrienthumb_load_store (str, load_store, size) 438460484Sobrien char * str; 438560484Sobrien int load_store; 438660484Sobrien int size; 438760484Sobrien{ 438860484Sobrien int Rd, Rb, Ro = FAIL; 438960484Sobrien 439060484Sobrien skip_whitespace (str); 439160484Sobrien 439260484Sobrien if ((Rd = thumb_reg (&str, THUMB_REG_LO)) == FAIL 439360484Sobrien || skip_past_comma (&str) == FAIL) 439460484Sobrien { 439560484Sobrien if (! inst.error) 439660484Sobrien inst.error = BAD_ARGS; 439760484Sobrien return; 439860484Sobrien } 439960484Sobrien 440060484Sobrien if (*str == '[') 440160484Sobrien { 440260484Sobrien str++; 440360484Sobrien if ((Rb = thumb_reg (&str, THUMB_REG_ANY)) == FAIL) 440460484Sobrien return; 440560484Sobrien 440660484Sobrien if (skip_past_comma (&str) != FAIL) 440760484Sobrien { 440860484Sobrien if (is_immediate_prefix (*str)) 440960484Sobrien { 441060484Sobrien str++; 441160484Sobrien if (my_get_expression (&inst.reloc.exp, &str)) 441260484Sobrien return; 441360484Sobrien } 441460484Sobrien else if ((Ro = thumb_reg (&str, THUMB_REG_LO)) == FAIL) 441560484Sobrien return; 441660484Sobrien } 441760484Sobrien else 441860484Sobrien { 441960484Sobrien inst.reloc.exp.X_op = O_constant; 442060484Sobrien inst.reloc.exp.X_add_number = 0; 442160484Sobrien } 442260484Sobrien 442360484Sobrien if (*str != ']') 442460484Sobrien { 442560484Sobrien inst.error = _("expected ']'"); 442660484Sobrien return; 442760484Sobrien } 442860484Sobrien str++; 442960484Sobrien } 443060484Sobrien else if (*str == '=') 443160484Sobrien { 443260484Sobrien /* Parse an "ldr Rd, =expr" instruction; this is another pseudo op */ 443360484Sobrien str++; 443460484Sobrien 443560484Sobrien skip_whitespace (str); 443660484Sobrien 443760484Sobrien if (my_get_expression (& inst.reloc.exp, & str)) 443860484Sobrien return; 443960484Sobrien 444060484Sobrien end_of_line (str); 444160484Sobrien 444260484Sobrien if ( inst.reloc.exp.X_op != O_constant 444360484Sobrien && inst.reloc.exp.X_op != O_symbol) 444460484Sobrien { 444560484Sobrien inst.error = "Constant expression expected"; 444660484Sobrien return; 444760484Sobrien } 444860484Sobrien 444960484Sobrien if (inst.reloc.exp.X_op == O_constant 445060484Sobrien && ((inst.reloc.exp.X_add_number & ~0xFF) == 0)) 445160484Sobrien { 445260484Sobrien /* This can be done with a mov instruction */ 445360484Sobrien 445460484Sobrien inst.instruction = T_OPCODE_MOV_I8 | (Rd << 8); 445560484Sobrien inst.instruction |= inst.reloc.exp.X_add_number; 445660484Sobrien return; 445760484Sobrien } 445860484Sobrien 445960484Sobrien /* Insert into literal pool */ 446060484Sobrien if (add_to_lit_pool () == FAIL) 446160484Sobrien { 446260484Sobrien if (!inst.error) 446360484Sobrien inst.error = "literal pool insertion failed"; 446460484Sobrien return; 446560484Sobrien } 446660484Sobrien 446760484Sobrien inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET; 446860484Sobrien inst.reloc.pc_rel = 1; 446960484Sobrien inst.instruction = T_OPCODE_LDR_PC | (Rd << 8); 447060484Sobrien inst.reloc.exp.X_add_number += 4; /* Adjust ARM pipeline offset to Thumb */ 447160484Sobrien 447260484Sobrien return; 447360484Sobrien } 447460484Sobrien else 447560484Sobrien { 447660484Sobrien if (my_get_expression (&inst.reloc.exp, &str)) 447760484Sobrien return; 447860484Sobrien 447960484Sobrien inst.instruction = T_OPCODE_LDR_PC | (Rd << 8); 448060484Sobrien inst.reloc.pc_rel = 1; 448160484Sobrien inst.reloc.exp.X_add_number -= 4; /* Pipeline offset */ 448260484Sobrien inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET; 448360484Sobrien end_of_line (str); 448460484Sobrien return; 448560484Sobrien } 448660484Sobrien 448760484Sobrien if (Rb == REG_PC || Rb == REG_SP) 448860484Sobrien { 448960484Sobrien if (size != THUMB_WORD) 449060484Sobrien { 449160484Sobrien inst.error = _("byte or halfword not valid for base register"); 449260484Sobrien return; 449360484Sobrien } 449460484Sobrien else if (Rb == REG_PC && load_store != THUMB_LOAD) 449560484Sobrien { 449660484Sobrien inst.error = _("R15 based store not allowed"); 449760484Sobrien return; 449860484Sobrien } 449960484Sobrien else if (Ro != FAIL) 450060484Sobrien { 450160484Sobrien inst.error = _("Invalid base register for register offset"); 450260484Sobrien return; 450360484Sobrien } 450460484Sobrien 450560484Sobrien if (Rb == REG_PC) 450660484Sobrien inst.instruction = T_OPCODE_LDR_PC; 450760484Sobrien else if (load_store == THUMB_LOAD) 450860484Sobrien inst.instruction = T_OPCODE_LDR_SP; 450960484Sobrien else 451060484Sobrien inst.instruction = T_OPCODE_STR_SP; 451160484Sobrien 451260484Sobrien inst.instruction |= Rd << 8; 451360484Sobrien if (inst.reloc.exp.X_op == O_constant) 451460484Sobrien { 451560484Sobrien unsigned offset = inst.reloc.exp.X_add_number; 451660484Sobrien 451760484Sobrien if (offset & ~0x3fc) 451860484Sobrien { 451960484Sobrien inst.error = _("invalid offset"); 452060484Sobrien return; 452160484Sobrien } 452260484Sobrien 452360484Sobrien inst.instruction |= offset >> 2; 452460484Sobrien } 452560484Sobrien else 452660484Sobrien inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET; 452760484Sobrien } 452860484Sobrien else if (Rb > 7) 452960484Sobrien { 453060484Sobrien inst.error = _("invalid base register in load/store"); 453160484Sobrien return; 453260484Sobrien } 453360484Sobrien else if (Ro == FAIL) 453460484Sobrien { 453560484Sobrien /* Immediate offset */ 453660484Sobrien if (size == THUMB_WORD) 453760484Sobrien inst.instruction = (load_store == THUMB_LOAD 453860484Sobrien ? T_OPCODE_LDR_IW : T_OPCODE_STR_IW); 453960484Sobrien else if (size == THUMB_HALFWORD) 454060484Sobrien inst.instruction = (load_store == THUMB_LOAD 454160484Sobrien ? T_OPCODE_LDR_IH : T_OPCODE_STR_IH); 454260484Sobrien else 454360484Sobrien inst.instruction = (load_store == THUMB_LOAD 454460484Sobrien ? T_OPCODE_LDR_IB : T_OPCODE_STR_IB); 454560484Sobrien 454660484Sobrien inst.instruction |= Rd | (Rb << 3); 454760484Sobrien 454860484Sobrien if (inst.reloc.exp.X_op == O_constant) 454960484Sobrien { 455060484Sobrien unsigned offset = inst.reloc.exp.X_add_number; 455160484Sobrien 455260484Sobrien if (offset & ~(0x1f << size)) 455360484Sobrien { 455460484Sobrien inst.error = _("Invalid offset"); 455560484Sobrien return; 455660484Sobrien } 455760484Sobrien inst.instruction |= (offset >> size) << 6; 455860484Sobrien } 455960484Sobrien else 456060484Sobrien inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET; 456160484Sobrien } 456260484Sobrien else 456360484Sobrien { 456460484Sobrien /* Register offset */ 456560484Sobrien if (size == THUMB_WORD) 456660484Sobrien inst.instruction = (load_store == THUMB_LOAD 456760484Sobrien ? T_OPCODE_LDR_RW : T_OPCODE_STR_RW); 456860484Sobrien else if (size == THUMB_HALFWORD) 456960484Sobrien inst.instruction = (load_store == THUMB_LOAD 457060484Sobrien ? T_OPCODE_LDR_RH : T_OPCODE_STR_RH); 457160484Sobrien else 457260484Sobrien inst.instruction = (load_store == THUMB_LOAD 457360484Sobrien ? T_OPCODE_LDR_RB : T_OPCODE_STR_RB); 457460484Sobrien 457560484Sobrien inst.instruction |= Rd | (Rb << 3) | (Ro << 6); 457660484Sobrien } 457760484Sobrien 457860484Sobrien end_of_line (str); 457960484Sobrien} 458060484Sobrien 458160484Sobrienstatic void 458260484Sobriendo_t_nop (str) 458360484Sobrien char * str; 458460484Sobrien{ 458560484Sobrien /* Do nothing */ 458660484Sobrien end_of_line (str); 458760484Sobrien return; 458860484Sobrien} 458960484Sobrien 459060484Sobrien/* Handle the Format 4 instructions that do not have equivalents in other 459160484Sobrien formats. That is, ADC, AND, EOR, SBC, ROR, TST, NEG, CMN, ORR, MUL, 459260484Sobrien BIC and MVN. */ 459360484Sobrienstatic void 459460484Sobriendo_t_arit (str) 459560484Sobrien char * str; 459660484Sobrien{ 459760484Sobrien int Rd, Rs, Rn; 459860484Sobrien 459960484Sobrien skip_whitespace (str); 460060484Sobrien 460160484Sobrien if ((Rd = thumb_reg (&str, THUMB_REG_LO)) == FAIL 460260484Sobrien || skip_past_comma (&str) == FAIL 460360484Sobrien || (Rs = thumb_reg (&str, THUMB_REG_LO)) == FAIL) 460460484Sobrien { 460560484Sobrien inst.error = BAD_ARGS; 460660484Sobrien return; 460760484Sobrien } 460860484Sobrien 460960484Sobrien if (skip_past_comma (&str) != FAIL) 461060484Sobrien { 461160484Sobrien /* Three operand format not allowed for TST, CMN, NEG and MVN. 461260484Sobrien (It isn't allowed for CMP either, but that isn't handled by this 461360484Sobrien function.) */ 461460484Sobrien if (inst.instruction == T_OPCODE_TST 461560484Sobrien || inst.instruction == T_OPCODE_CMN 461660484Sobrien || inst.instruction == T_OPCODE_NEG 461760484Sobrien || inst.instruction == T_OPCODE_MVN) 461860484Sobrien { 461960484Sobrien inst.error = BAD_ARGS; 462060484Sobrien return; 462160484Sobrien } 462260484Sobrien 462360484Sobrien if ((Rn = thumb_reg (&str, THUMB_REG_LO)) == FAIL) 462460484Sobrien return; 462560484Sobrien 462660484Sobrien if (Rs != Rd) 462760484Sobrien { 462860484Sobrien inst.error = _("dest and source1 one must be the same register"); 462960484Sobrien return; 463060484Sobrien } 463160484Sobrien Rs = Rn; 463260484Sobrien } 463360484Sobrien 463460484Sobrien if (inst.instruction == T_OPCODE_MUL 463560484Sobrien && Rs == Rd) 463660484Sobrien as_tsktsk (_("Rs and Rd must be different in MUL")); 463760484Sobrien 463860484Sobrien inst.instruction |= Rd | (Rs << 3); 463960484Sobrien end_of_line (str); 464060484Sobrien} 464160484Sobrien 464260484Sobrienstatic void 464360484Sobriendo_t_add (str) 464460484Sobrien char * str; 464560484Sobrien{ 464660484Sobrien thumb_add_sub (str, 0); 464760484Sobrien} 464860484Sobrien 464960484Sobrienstatic void 465060484Sobriendo_t_asr (str) 465160484Sobrien char * str; 465260484Sobrien{ 465360484Sobrien thumb_shift (str, THUMB_ASR); 465460484Sobrien} 465560484Sobrien 465660484Sobrienstatic void 465760484Sobriendo_t_branch9 (str) 465860484Sobrien char * str; 465960484Sobrien{ 466060484Sobrien if (my_get_expression (&inst.reloc.exp, &str)) 466160484Sobrien return; 466260484Sobrien inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH9; 466360484Sobrien inst.reloc.pc_rel = 1; 466460484Sobrien end_of_line (str); 466560484Sobrien} 466660484Sobrien 466760484Sobrienstatic void 466860484Sobriendo_t_branch12 (str) 466960484Sobrien char * str; 467060484Sobrien{ 467160484Sobrien if (my_get_expression (&inst.reloc.exp, &str)) 467260484Sobrien return; 467360484Sobrien inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH12; 467460484Sobrien inst.reloc.pc_rel = 1; 467560484Sobrien end_of_line (str); 467660484Sobrien} 467760484Sobrien 467860484Sobrien/* Find the real, Thumb encoded start of a Thumb function. */ 467960484Sobrien 468060484Sobrienstatic symbolS * 468160484Sobrienfind_real_start (symbolP) 468260484Sobrien symbolS * symbolP; 468360484Sobrien{ 468460484Sobrien char * real_start; 468560484Sobrien const char * name = S_GET_NAME (symbolP); 468660484Sobrien symbolS * new_target; 468760484Sobrien 468860484Sobrien /* This definiton must agree with the one in gcc/config/arm/thumb.c */ 468960484Sobrien#define STUB_NAME ".real_start_of" 469060484Sobrien 469160484Sobrien if (name == NULL) 469260484Sobrien abort(); 469360484Sobrien 469460484Sobrien /* Names that start with '.' are local labels, not function entry points. 469560484Sobrien The compiler may generate BL instructions to these labels because it 469660484Sobrien needs to perform a branch to a far away location. */ 469760484Sobrien if (name[0] == '.') 469860484Sobrien return symbolP; 469960484Sobrien 470060484Sobrien real_start = malloc (strlen (name) + strlen (STUB_NAME) + 1); 470160484Sobrien sprintf (real_start, "%s%s", STUB_NAME, name); 470260484Sobrien 470360484Sobrien new_target = symbol_find (real_start); 470460484Sobrien 470560484Sobrien if (new_target == NULL) 470660484Sobrien { 470760484Sobrien as_warn ("Failed to find real start of function: %s\n", name); 470860484Sobrien new_target = symbolP; 470960484Sobrien } 471060484Sobrien 471160484Sobrien free (real_start); 471260484Sobrien 471360484Sobrien return new_target; 471460484Sobrien} 471560484Sobrien 471660484Sobrien 471760484Sobrienstatic void 471860484Sobriendo_t_branch23 (str) 471960484Sobrien char * str; 472060484Sobrien{ 472160484Sobrien if (my_get_expression (& inst.reloc.exp, & str)) 472260484Sobrien return; 472360484Sobrien 472460484Sobrien inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH23; 472560484Sobrien inst.reloc.pc_rel = 1; 472660484Sobrien end_of_line (str); 472760484Sobrien 472860484Sobrien /* If the destination of the branch is a defined symbol which does not have 472960484Sobrien the THUMB_FUNC attribute, then we must be calling a function which has 473060484Sobrien the (interfacearm) attribute. We look for the Thumb entry point to that 473160484Sobrien function and change the branch to refer to that function instead. */ 473260484Sobrien if ( inst.reloc.exp.X_op == O_symbol 473360484Sobrien && inst.reloc.exp.X_add_symbol != NULL 473460484Sobrien && S_IS_DEFINED (inst.reloc.exp.X_add_symbol) 473560484Sobrien && ! THUMB_IS_FUNC (inst.reloc.exp.X_add_symbol)) 473660484Sobrien inst.reloc.exp.X_add_symbol = find_real_start (inst.reloc.exp.X_add_symbol); 473760484Sobrien} 473860484Sobrien 473960484Sobrienstatic void 474060484Sobriendo_t_bx (str) 474160484Sobrien char * str; 474260484Sobrien{ 474360484Sobrien int reg; 474460484Sobrien 474560484Sobrien skip_whitespace (str); 474660484Sobrien 474760484Sobrien if ((reg = thumb_reg (&str, THUMB_REG_ANY)) == FAIL) 474860484Sobrien return; 474960484Sobrien 475060484Sobrien /* This sets THUMB_H2 from the top bit of reg. */ 475160484Sobrien inst.instruction |= reg << 3; 475260484Sobrien 475360484Sobrien /* ??? FIXME: Should add a hacky reloc here if reg is REG_PC. The reloc 475460484Sobrien should cause the alignment to be checked once it is known. This is 475560484Sobrien because BX PC only works if the instruction is word aligned. */ 475660484Sobrien 475760484Sobrien end_of_line (str); 475860484Sobrien} 475960484Sobrien 476060484Sobrienstatic void 476160484Sobriendo_t_compare (str) 476260484Sobrien char * str; 476360484Sobrien{ 476460484Sobrien thumb_mov_compare (str, THUMB_COMPARE); 476560484Sobrien} 476660484Sobrien 476760484Sobrienstatic void 476860484Sobriendo_t_ldmstm (str) 476960484Sobrien char * str; 477060484Sobrien{ 477160484Sobrien int Rb; 477260484Sobrien long range; 477360484Sobrien 477460484Sobrien skip_whitespace (str); 477560484Sobrien 477660484Sobrien if ((Rb = thumb_reg (&str, THUMB_REG_LO)) == FAIL) 477760484Sobrien return; 477860484Sobrien 477960484Sobrien if (*str != '!') 478060484Sobrien as_warn (_("Inserted missing '!': load/store multiple always writes back base register")); 478160484Sobrien else 478260484Sobrien str++; 478360484Sobrien 478460484Sobrien if (skip_past_comma (&str) == FAIL 478560484Sobrien || (range = reg_list (&str)) == FAIL) 478660484Sobrien { 478760484Sobrien if (! inst.error) 478860484Sobrien inst.error = BAD_ARGS; 478960484Sobrien return; 479060484Sobrien } 479160484Sobrien 479260484Sobrien if (inst.reloc.type != BFD_RELOC_NONE) 479360484Sobrien { 479460484Sobrien /* This really doesn't seem worth it. */ 479560484Sobrien inst.reloc.type = BFD_RELOC_NONE; 479660484Sobrien inst.error = _("Expression too complex"); 479760484Sobrien return; 479860484Sobrien } 479960484Sobrien 480060484Sobrien if (range & ~0xff) 480160484Sobrien { 480260484Sobrien inst.error = _("only lo-regs valid in load/store multiple"); 480360484Sobrien return; 480460484Sobrien } 480560484Sobrien 480660484Sobrien inst.instruction |= (Rb << 8) | range; 480760484Sobrien end_of_line (str); 480860484Sobrien} 480960484Sobrien 481060484Sobrienstatic void 481160484Sobriendo_t_ldr (str) 481260484Sobrien char * str; 481360484Sobrien{ 481460484Sobrien thumb_load_store (str, THUMB_LOAD, THUMB_WORD); 481560484Sobrien} 481660484Sobrien 481760484Sobrienstatic void 481860484Sobriendo_t_ldrb (str) 481960484Sobrien char * str; 482060484Sobrien{ 482160484Sobrien thumb_load_store (str, THUMB_LOAD, THUMB_BYTE); 482260484Sobrien} 482360484Sobrien 482460484Sobrienstatic void 482560484Sobriendo_t_ldrh (str) 482660484Sobrien char * str; 482760484Sobrien{ 482860484Sobrien thumb_load_store (str, THUMB_LOAD, THUMB_HALFWORD); 482960484Sobrien} 483060484Sobrien 483160484Sobrienstatic void 483260484Sobriendo_t_lds (str) 483360484Sobrien char * str; 483460484Sobrien{ 483560484Sobrien int Rd, Rb, Ro; 483660484Sobrien 483760484Sobrien skip_whitespace (str); 483860484Sobrien 483960484Sobrien if ((Rd = thumb_reg (&str, THUMB_REG_LO)) == FAIL 484060484Sobrien || skip_past_comma (&str) == FAIL 484160484Sobrien || *str++ != '[' 484260484Sobrien || (Rb = thumb_reg (&str, THUMB_REG_LO)) == FAIL 484360484Sobrien || skip_past_comma (&str) == FAIL 484460484Sobrien || (Ro = thumb_reg (&str, THUMB_REG_LO)) == FAIL 484560484Sobrien || *str++ != ']') 484660484Sobrien { 484760484Sobrien if (! inst.error) 484860484Sobrien inst.error = _("Syntax: ldrs[b] Rd, [Rb, Ro]"); 484960484Sobrien return; 485060484Sobrien } 485160484Sobrien 485260484Sobrien inst.instruction |= Rd | (Rb << 3) | (Ro << 6); 485360484Sobrien end_of_line (str); 485460484Sobrien} 485560484Sobrien 485660484Sobrienstatic void 485760484Sobriendo_t_lsl (str) 485860484Sobrien char * str; 485960484Sobrien{ 486060484Sobrien thumb_shift (str, THUMB_LSL); 486160484Sobrien} 486260484Sobrien 486360484Sobrienstatic void 486460484Sobriendo_t_lsr (str) 486560484Sobrien char * str; 486660484Sobrien{ 486760484Sobrien thumb_shift (str, THUMB_LSR); 486860484Sobrien} 486960484Sobrien 487060484Sobrienstatic void 487160484Sobriendo_t_mov (str) 487260484Sobrien char * str; 487360484Sobrien{ 487460484Sobrien thumb_mov_compare (str, THUMB_MOVE); 487560484Sobrien} 487660484Sobrien 487760484Sobrienstatic void 487860484Sobriendo_t_push_pop (str) 487960484Sobrien char * str; 488060484Sobrien{ 488160484Sobrien long range; 488260484Sobrien 488360484Sobrien skip_whitespace (str); 488460484Sobrien 488560484Sobrien if ((range = reg_list (&str)) == FAIL) 488660484Sobrien { 488760484Sobrien if (! inst.error) 488860484Sobrien inst.error = BAD_ARGS; 488960484Sobrien return; 489060484Sobrien } 489160484Sobrien 489260484Sobrien if (inst.reloc.type != BFD_RELOC_NONE) 489360484Sobrien { 489460484Sobrien /* This really doesn't seem worth it. */ 489560484Sobrien inst.reloc.type = BFD_RELOC_NONE; 489660484Sobrien inst.error = _("Expression too complex"); 489760484Sobrien return; 489860484Sobrien } 489960484Sobrien 490060484Sobrien if (range & ~0xff) 490160484Sobrien { 490260484Sobrien if ((inst.instruction == T_OPCODE_PUSH 490360484Sobrien && (range & ~0xff) == 1 << REG_LR) 490460484Sobrien || (inst.instruction == T_OPCODE_POP 490560484Sobrien && (range & ~0xff) == 1 << REG_PC)) 490660484Sobrien { 490760484Sobrien inst.instruction |= THUMB_PP_PC_LR; 490860484Sobrien range &= 0xff; 490960484Sobrien } 491060484Sobrien else 491160484Sobrien { 491260484Sobrien inst.error = _("invalid register list to push/pop instruction"); 491360484Sobrien return; 491460484Sobrien } 491560484Sobrien } 491660484Sobrien 491760484Sobrien inst.instruction |= range; 491860484Sobrien end_of_line (str); 491960484Sobrien} 492060484Sobrien 492160484Sobrienstatic void 492260484Sobriendo_t_str (str) 492360484Sobrien char * str; 492460484Sobrien{ 492560484Sobrien thumb_load_store (str, THUMB_STORE, THUMB_WORD); 492660484Sobrien} 492760484Sobrien 492860484Sobrienstatic void 492960484Sobriendo_t_strb (str) 493060484Sobrien char * str; 493160484Sobrien{ 493260484Sobrien thumb_load_store (str, THUMB_STORE, THUMB_BYTE); 493360484Sobrien} 493460484Sobrien 493560484Sobrienstatic void 493660484Sobriendo_t_strh (str) 493760484Sobrien char * str; 493860484Sobrien{ 493960484Sobrien thumb_load_store (str, THUMB_STORE, THUMB_HALFWORD); 494060484Sobrien} 494160484Sobrien 494260484Sobrienstatic void 494360484Sobriendo_t_sub (str) 494460484Sobrien char * str; 494560484Sobrien{ 494660484Sobrien thumb_add_sub (str, 1); 494760484Sobrien} 494860484Sobrien 494960484Sobrienstatic void 495060484Sobriendo_t_swi (str) 495160484Sobrien char * str; 495260484Sobrien{ 495360484Sobrien skip_whitespace (str); 495460484Sobrien 495560484Sobrien if (my_get_expression (&inst.reloc.exp, &str)) 495660484Sobrien return; 495760484Sobrien 495860484Sobrien inst.reloc.type = BFD_RELOC_ARM_SWI; 495960484Sobrien end_of_line (str); 496060484Sobrien return; 496160484Sobrien} 496260484Sobrien 496360484Sobrienstatic void 496460484Sobriendo_t_adr (str) 496560484Sobrien char * str; 496660484Sobrien{ 496760484Sobrien int reg; 496860484Sobrien 496960484Sobrien /* This is a pseudo-op of the form "adr rd, label" to be converted 497060484Sobrien into a relative address of the form "add rd, pc, #label-.-4". */ 497160484Sobrien skip_whitespace (str); 497260484Sobrien 497360484Sobrien /* Store Rd in temporary location inside instruction. */ 497460484Sobrien if ((reg = reg_required_here (&str, 4)) == FAIL 497560484Sobrien || (reg > 7) /* For Thumb reg must be r0..r7. */ 497660484Sobrien || skip_past_comma (&str) == FAIL 497760484Sobrien || my_get_expression (&inst.reloc.exp, &str)) 497860484Sobrien { 497960484Sobrien if (!inst.error) 498060484Sobrien inst.error = BAD_ARGS; 498160484Sobrien return; 498260484Sobrien } 498360484Sobrien 498460484Sobrien inst.reloc.type = BFD_RELOC_ARM_THUMB_ADD; 498560484Sobrien inst.reloc.exp.X_add_number -= 4; /* PC relative adjust. */ 498660484Sobrien inst.reloc.pc_rel = 1; 498760484Sobrien inst.instruction |= REG_PC; /* Rd is already placed into the instruction. */ 498860484Sobrien 498960484Sobrien end_of_line (str); 499060484Sobrien} 499160484Sobrien 499260484Sobrienstatic void 499360484Sobrieninsert_reg (entry) 499460484Sobrien int entry; 499560484Sobrien{ 499660484Sobrien int len = strlen (reg_table[entry].name) + 2; 499760484Sobrien char * buf = (char *) xmalloc (len); 499860484Sobrien char * buf2 = (char *) xmalloc (len); 499960484Sobrien int i = 0; 500060484Sobrien 500160484Sobrien#ifdef REGISTER_PREFIX 500260484Sobrien buf[i++] = REGISTER_PREFIX; 500360484Sobrien#endif 500460484Sobrien 500560484Sobrien strcpy (buf + i, reg_table[entry].name); 500660484Sobrien 500760484Sobrien for (i = 0; buf[i]; i++) 500860484Sobrien buf2[i] = islower (buf[i]) ? toupper (buf[i]) : buf[i]; 500960484Sobrien 501060484Sobrien buf2[i] = '\0'; 501160484Sobrien 501260484Sobrien hash_insert (arm_reg_hsh, buf, (PTR) ®_table[entry]); 501360484Sobrien hash_insert (arm_reg_hsh, buf2, (PTR) ®_table[entry]); 501460484Sobrien} 501560484Sobrien 501660484Sobrienstatic void 501760484Sobrieninsert_reg_alias (str, regnum) 501860484Sobrien char *str; 501960484Sobrien int regnum; 502060484Sobrien{ 502160484Sobrien struct reg_entry *new = 502260484Sobrien (struct reg_entry *)xmalloc (sizeof (struct reg_entry)); 502360484Sobrien char *name = xmalloc (strlen (str) + 1); 502460484Sobrien strcpy (name, str); 502560484Sobrien 502660484Sobrien new->name = name; 502760484Sobrien new->number = regnum; 502860484Sobrien 502960484Sobrien hash_insert (arm_reg_hsh, name, (PTR) new); 503060484Sobrien} 503160484Sobrien 503260484Sobrienstatic void 503360484Sobrienset_constant_flonums () 503460484Sobrien{ 503560484Sobrien int i; 503660484Sobrien 503760484Sobrien for (i = 0; i < NUM_FLOAT_VALS; i++) 503860484Sobrien if (atof_ieee ((char *)fp_const[i], 'x', fp_values[i]) == NULL) 503960484Sobrien abort (); 504060484Sobrien} 504160484Sobrien 504260484Sobrienvoid 504360484Sobrienmd_begin () 504460484Sobrien{ 504560484Sobrien unsigned mach; 504660484Sobrien unsigned int i; 504760484Sobrien 504860484Sobrien if ( (arm_ops_hsh = hash_new ()) == NULL 504960484Sobrien || (arm_tops_hsh = hash_new ()) == NULL 505060484Sobrien || (arm_cond_hsh = hash_new ()) == NULL 505160484Sobrien || (arm_shift_hsh = hash_new ()) == NULL 505260484Sobrien || (arm_reg_hsh = hash_new ()) == NULL 505360484Sobrien || (arm_psr_hsh = hash_new ()) == NULL) 505460484Sobrien as_fatal (_("Virtual memory exhausted")); 505560484Sobrien 505660484Sobrien for (i = 0; i < sizeof (insns) / sizeof (struct asm_opcode); i++) 505760484Sobrien hash_insert (arm_ops_hsh, insns[i].template, (PTR) (insns + i)); 505860484Sobrien for (i = 0; i < sizeof (tinsns) / sizeof (struct thumb_opcode); i++) 505960484Sobrien hash_insert (arm_tops_hsh, tinsns[i].template, (PTR) (tinsns + i)); 506060484Sobrien for (i = 0; i < sizeof (conds) / sizeof (struct asm_cond); i++) 506160484Sobrien hash_insert (arm_cond_hsh, conds[i].template, (PTR) (conds + i)); 506260484Sobrien for (i = 0; i < sizeof (shift) / sizeof (struct asm_shift); i++) 506360484Sobrien hash_insert (arm_shift_hsh, shift[i].template, (PTR) (shift + i)); 506460484Sobrien for (i = 0; i < sizeof (psrs) / sizeof (struct asm_psr); i++) 506560484Sobrien hash_insert (arm_psr_hsh, psrs[i].template, (PTR) (psrs + i)); 506660484Sobrien 506760484Sobrien for (i = 0; reg_table[i].name; i++) 506860484Sobrien insert_reg (i); 506960484Sobrien 507060484Sobrien set_constant_flonums (); 507160484Sobrien 507260484Sobrien#if defined OBJ_COFF || defined OBJ_ELF 507360484Sobrien { 507460484Sobrien unsigned int flags = 0; 507560484Sobrien 507660484Sobrien /* Set the flags in the private structure. */ 507760484Sobrien if (uses_apcs_26) flags |= F_APCS26; 507860484Sobrien if (support_interwork) flags |= F_INTERWORK; 507960484Sobrien if (uses_apcs_float) flags |= F_APCS_FLOAT; 508060484Sobrien if (pic_code) flags |= F_PIC; 508160484Sobrien if ((cpu_variant & FPU_ALL) == FPU_NONE) flags |= F_SOFT_FLOAT; 508260484Sobrien 508360484Sobrien bfd_set_private_flags (stdoutput, flags); 508460484Sobrien } 508560484Sobrien#endif 508660484Sobrien 508760484Sobrien /* Record the CPU type as well. */ 508860484Sobrien switch (cpu_variant & ARM_CPU_MASK) 508960484Sobrien { 509060484Sobrien case ARM_2: 509160484Sobrien mach = bfd_mach_arm_2; 509260484Sobrien break; 509360484Sobrien 509460484Sobrien case ARM_3: /* Also ARM_250. */ 509560484Sobrien mach = bfd_mach_arm_2a; 509660484Sobrien break; 509760484Sobrien 509860484Sobrien default: 509960484Sobrien case ARM_6 | ARM_3 | ARM_2: /* Actually no CPU type defined. */ 510060484Sobrien mach = bfd_mach_arm_4; 510160484Sobrien break; 510260484Sobrien 510360484Sobrien case ARM_7: /* Also ARM_6. */ 510460484Sobrien mach = bfd_mach_arm_3; 510560484Sobrien break; 510660484Sobrien } 510760484Sobrien 510860484Sobrien /* Catch special cases. */ 510960484Sobrien if (cpu_variant != (FPU_DEFAULT | CPU_DEFAULT)) 511060484Sobrien { 511160484Sobrien if (cpu_variant & (ARM_EXT_V5 & ARM_THUMB)) 511260484Sobrien mach = bfd_mach_arm_5T; 511360484Sobrien else if (cpu_variant & ARM_EXT_V5) 511460484Sobrien mach = bfd_mach_arm_5; 511560484Sobrien else if (cpu_variant & ARM_THUMB) 511660484Sobrien mach = bfd_mach_arm_4T; 511760484Sobrien else if ((cpu_variant & ARM_ARCH_V4) == ARM_ARCH_V4) 511860484Sobrien mach = bfd_mach_arm_4; 511960484Sobrien else if (cpu_variant & ARM_LONGMUL) 512060484Sobrien mach = bfd_mach_arm_3M; 512160484Sobrien } 512260484Sobrien 512360484Sobrien bfd_set_arch_mach (stdoutput, TARGET_ARCH, mach); 512460484Sobrien} 512560484Sobrien 512660484Sobrien/* Turn an integer of n bytes (in val) into a stream of bytes appropriate 512760484Sobrien for use in the a.out file, and stores them in the array pointed to by buf. 512860484Sobrien This knows about the endian-ness of the target machine and does 512960484Sobrien THE RIGHT THING, whatever it is. Possible values for n are 1 (byte) 513060484Sobrien 2 (short) and 4 (long) Floating numbers are put out as a series of 513160484Sobrien LITTLENUMS (shorts, here at least). */ 513260484Sobrienvoid 513360484Sobrienmd_number_to_chars (buf, val, n) 513460484Sobrien char * buf; 513560484Sobrien valueT val; 513660484Sobrien int n; 513760484Sobrien{ 513860484Sobrien if (target_big_endian) 513960484Sobrien number_to_chars_bigendian (buf, val, n); 514060484Sobrien else 514160484Sobrien number_to_chars_littleendian (buf, val, n); 514260484Sobrien} 514360484Sobrien 514460484Sobrienstatic valueT 514560484Sobrienmd_chars_to_number (buf, n) 514660484Sobrien char * buf; 514760484Sobrien int n; 514860484Sobrien{ 514960484Sobrien valueT result = 0; 515060484Sobrien unsigned char * where = (unsigned char *) buf; 515160484Sobrien 515260484Sobrien if (target_big_endian) 515360484Sobrien { 515460484Sobrien while (n--) 515560484Sobrien { 515660484Sobrien result <<= 8; 515760484Sobrien result |= (*where++ & 255); 515860484Sobrien } 515960484Sobrien } 516060484Sobrien else 516160484Sobrien { 516260484Sobrien while (n--) 516360484Sobrien { 516460484Sobrien result <<= 8; 516560484Sobrien result |= (where[n] & 255); 516660484Sobrien } 516760484Sobrien } 516860484Sobrien 516960484Sobrien return result; 517060484Sobrien} 517160484Sobrien 517260484Sobrien/* Turn a string in input_line_pointer into a floating point constant 517360484Sobrien of type TYPE, and store the appropriate bytes in *litP. The number 517460484Sobrien of LITTLENUMS emitted is stored in *sizeP . An error message is 517560484Sobrien returned, or NULL on OK. 517660484Sobrien 517760484Sobrien Note that fp constants aren't represent in the normal way on the ARM. 517860484Sobrien In big endian mode, things are as expected. However, in little endian 517960484Sobrien mode fp constants are big-endian word-wise, and little-endian byte-wise 518060484Sobrien within the words. For example, (double) 1.1 in big endian mode is 518160484Sobrien the byte sequence 3f f1 99 99 99 99 99 9a, and in little endian mode is 518260484Sobrien the byte sequence 99 99 f1 3f 9a 99 99 99. 518360484Sobrien 518460484Sobrien ??? The format of 12 byte floats is uncertain according to gcc's arm.h. */ 518560484Sobrien 518660484Sobrienchar * 518760484Sobrienmd_atof (type, litP, sizeP) 518860484Sobrien char type; 518960484Sobrien char * litP; 519060484Sobrien int * sizeP; 519160484Sobrien{ 519260484Sobrien int prec; 519360484Sobrien LITTLENUM_TYPE words[MAX_LITTLENUMS]; 519460484Sobrien char *t; 519560484Sobrien int i; 519660484Sobrien 519760484Sobrien switch (type) 519860484Sobrien { 519960484Sobrien case 'f': 520060484Sobrien case 'F': 520160484Sobrien case 's': 520260484Sobrien case 'S': 520360484Sobrien prec = 2; 520460484Sobrien break; 520560484Sobrien 520660484Sobrien case 'd': 520760484Sobrien case 'D': 520860484Sobrien case 'r': 520960484Sobrien case 'R': 521060484Sobrien prec = 4; 521160484Sobrien break; 521260484Sobrien 521360484Sobrien case 'x': 521460484Sobrien case 'X': 521560484Sobrien prec = 6; 521660484Sobrien break; 521760484Sobrien 521860484Sobrien case 'p': 521960484Sobrien case 'P': 522060484Sobrien prec = 6; 522160484Sobrien break; 522260484Sobrien 522360484Sobrien default: 522460484Sobrien *sizeP = 0; 522560484Sobrien return _("Bad call to MD_ATOF()"); 522660484Sobrien } 522760484Sobrien 522860484Sobrien t = atof_ieee (input_line_pointer, type, words); 522960484Sobrien if (t) 523060484Sobrien input_line_pointer = t; 523160484Sobrien *sizeP = prec * 2; 523260484Sobrien 523360484Sobrien if (target_big_endian) 523460484Sobrien { 523560484Sobrien for (i = 0; i < prec; i++) 523660484Sobrien { 523760484Sobrien md_number_to_chars (litP, (valueT) words[i], 2); 523860484Sobrien litP += 2; 523960484Sobrien } 524060484Sobrien } 524160484Sobrien else 524260484Sobrien { 524360484Sobrien /* For a 4 byte float the order of elements in `words' is 1 0. For an 524460484Sobrien 8 byte float the order is 1 0 3 2. */ 524560484Sobrien for (i = 0; i < prec; i += 2) 524660484Sobrien { 524760484Sobrien md_number_to_chars (litP, (valueT) words[i + 1], 2); 524860484Sobrien md_number_to_chars (litP + 2, (valueT) words[i], 2); 524960484Sobrien litP += 4; 525060484Sobrien } 525160484Sobrien } 525260484Sobrien 525360484Sobrien return 0; 525460484Sobrien} 525560484Sobrien 525660484Sobrien/* The knowledge of the PC's pipeline offset is built into the insns themselves. */ 525760484Sobrienlong 525860484Sobrienmd_pcrel_from (fixP) 525960484Sobrien fixS * fixP; 526060484Sobrien{ 526160484Sobrien if ( fixP->fx_addsy 526260484Sobrien && S_GET_SEGMENT (fixP->fx_addsy) == undefined_section 526360484Sobrien && fixP->fx_subsy == NULL) 526460484Sobrien return 0; 526560484Sobrien 526660484Sobrien if (fixP->fx_pcrel && (fixP->fx_r_type == BFD_RELOC_ARM_THUMB_ADD)) 526760484Sobrien { 526860484Sobrien /* PC relative addressing on the Thumb is slightly odd 526960484Sobrien as the bottom two bits of the PC are forced to zero 527060484Sobrien for the calculation. */ 527160484Sobrien return (fixP->fx_where + fixP->fx_frag->fr_address) & ~3; 527260484Sobrien } 527360484Sobrien 527460484Sobrien#ifdef TE_WINCE 527560484Sobrien /* The pattern was adjusted to accomodate CE's off-by-one fixups, 527660484Sobrien so we un-adjust here to compensate for the accomodation. */ 527760484Sobrien return fixP->fx_where + fixP->fx_frag->fr_address + 8; 527860484Sobrien#else 527960484Sobrien return fixP->fx_where + fixP->fx_frag->fr_address; 528060484Sobrien#endif 528160484Sobrien} 528260484Sobrien 528360484Sobrien/* Round up a section size to the appropriate boundary. */ 528460484SobrienvalueT 528560484Sobrienmd_section_align (segment, size) 528660484Sobrien segT segment ATTRIBUTE_UNUSED; 528760484Sobrien valueT size; 528860484Sobrien{ 528960484Sobrien#ifdef OBJ_ELF 529060484Sobrien return size; 529160484Sobrien#else 529260484Sobrien /* Round all sects to multiple of 4 */ 529360484Sobrien return (size + 3) & ~3; 529460484Sobrien#endif 529560484Sobrien} 529660484Sobrien 529760484Sobrien/* Under ELF we need to default _GLOBAL_OFFSET_TABLE. Otherwise 529860484Sobrien we have no need to default values of symbols. */ 529960484Sobrien 530060484Sobrien/* ARGSUSED */ 530160484SobriensymbolS * 530260484Sobrienmd_undefined_symbol (name) 530361843Sobrien char * name ATTRIBUTE_UNUSED; 530460484Sobrien{ 530560484Sobrien#ifdef OBJ_ELF 530660484Sobrien if (name[0] == '_' && name[1] == 'G' 530760484Sobrien && streq (name, GLOBAL_OFFSET_TABLE_NAME)) 530860484Sobrien { 530960484Sobrien if (!GOT_symbol) 531060484Sobrien { 531160484Sobrien if (symbol_find (name)) 531260484Sobrien as_bad ("GOT already in the symbol table"); 531360484Sobrien 531460484Sobrien GOT_symbol = symbol_new (name, undefined_section, 531560484Sobrien (valueT)0, & zero_address_frag); 531660484Sobrien } 531760484Sobrien 531860484Sobrien return GOT_symbol; 531960484Sobrien } 532060484Sobrien#endif 532160484Sobrien 532260484Sobrien return 0; 532360484Sobrien} 532460484Sobrien 532560484Sobrien/* arm_reg_parse () := if it looks like a register, return its token and 532660484Sobrien advance the pointer. */ 532760484Sobrien 532860484Sobrienstatic int 532960484Sobrienarm_reg_parse (ccp) 533060484Sobrien register char ** ccp; 533160484Sobrien{ 533260484Sobrien char * start = * ccp; 533360484Sobrien char c; 533460484Sobrien char * p; 533560484Sobrien struct reg_entry * reg; 533660484Sobrien 533760484Sobrien#ifdef REGISTER_PREFIX 533860484Sobrien if (*start != REGISTER_PREFIX) 533960484Sobrien return FAIL; 534060484Sobrien p = start + 1; 534160484Sobrien#else 534260484Sobrien p = start; 534360484Sobrien#ifdef OPTIONAL_REGISTER_PREFIX 534460484Sobrien if (*p == OPTIONAL_REGISTER_PREFIX) 534560484Sobrien p++, start++; 534660484Sobrien#endif 534760484Sobrien#endif 534860484Sobrien if (!isalpha (*p) || !is_name_beginner (*p)) 534960484Sobrien return FAIL; 535060484Sobrien 535160484Sobrien c = *p++; 535260484Sobrien while (isalpha (c) || isdigit (c) || c == '_') 535360484Sobrien c = *p++; 535460484Sobrien 535560484Sobrien *--p = 0; 535660484Sobrien reg = (struct reg_entry *) hash_find (arm_reg_hsh, start); 535760484Sobrien *p = c; 535860484Sobrien 535960484Sobrien if (reg) 536060484Sobrien { 536160484Sobrien *ccp = p; 536260484Sobrien return reg->number; 536360484Sobrien } 536460484Sobrien 536560484Sobrien return FAIL; 536660484Sobrien} 536760484Sobrien 536860484Sobrienint 536960484Sobrienmd_apply_fix3 (fixP, val, seg) 537060484Sobrien fixS * fixP; 537160484Sobrien valueT * val; 537260484Sobrien segT seg; 537360484Sobrien{ 537460484Sobrien offsetT value = * val; 537560484Sobrien offsetT newval; 537660484Sobrien unsigned int newimm; 537760484Sobrien unsigned long temp; 537860484Sobrien int sign; 537960484Sobrien char * buf = fixP->fx_where + fixP->fx_frag->fr_literal; 538060484Sobrien arm_fix_data * arm_data = (arm_fix_data *) fixP->tc_fix_data; 538160484Sobrien 538260484Sobrien assert (fixP->fx_r_type < BFD_RELOC_UNUSED); 538360484Sobrien 538460484Sobrien /* Note whether this will delete the relocation. */ 538560484Sobrien#if 0 /* patch from REarnshaw to JDavis (disabled for the moment, since it doesn't work fully) */ 538660484Sobrien if ((fixP->fx_addsy == 0 || symbol_constant_p (fixP->fx_addsy)) 538760484Sobrien && !fixP->fx_pcrel) 538860484Sobrien#else 538960484Sobrien if (fixP->fx_addsy == 0 && !fixP->fx_pcrel) 539060484Sobrien#endif 539160484Sobrien fixP->fx_done = 1; 539260484Sobrien 539360484Sobrien /* If this symbol is in a different section then we need to leave it for 539460484Sobrien the linker to deal with. Unfortunately, md_pcrel_from can't tell, 539560484Sobrien so we have to undo it's effects here. */ 539660484Sobrien if (fixP->fx_pcrel) 539760484Sobrien { 539860484Sobrien if (fixP->fx_addsy != NULL 539960484Sobrien && S_IS_DEFINED (fixP->fx_addsy) 540060484Sobrien && S_GET_SEGMENT (fixP->fx_addsy) != seg) 540160484Sobrien { 540260484Sobrien if (target_oabi 540360484Sobrien && (fixP->fx_r_type == BFD_RELOC_ARM_PCREL_BRANCH 540460484Sobrien )) 540560484Sobrien value = 0; 540660484Sobrien else 540760484Sobrien value += md_pcrel_from (fixP); 540860484Sobrien } 540960484Sobrien } 541060484Sobrien 541160484Sobrien fixP->fx_addnumber = value; /* Remember value for emit_reloc. */ 541260484Sobrien 541360484Sobrien switch (fixP->fx_r_type) 541460484Sobrien { 541560484Sobrien case BFD_RELOC_ARM_IMMEDIATE: 541660484Sobrien newimm = validate_immediate (value); 541760484Sobrien temp = md_chars_to_number (buf, INSN_SIZE); 541860484Sobrien 541960484Sobrien /* If the instruction will fail, see if we can fix things up by 542060484Sobrien changing the opcode. */ 542160484Sobrien if (newimm == (unsigned int) FAIL 542260484Sobrien && (newimm = negate_data_op (&temp, value)) == (unsigned int) FAIL) 542360484Sobrien { 542460484Sobrien as_bad_where (fixP->fx_file, fixP->fx_line, 542560484Sobrien _("invalid constant (%lx) after fixup"), 542660484Sobrien (unsigned long) value); 542760484Sobrien break; 542860484Sobrien } 542960484Sobrien 543060484Sobrien newimm |= (temp & 0xfffff000); 543160484Sobrien md_number_to_chars (buf, (valueT) newimm, INSN_SIZE); 543260484Sobrien break; 543360484Sobrien 543460484Sobrien case BFD_RELOC_ARM_ADRL_IMMEDIATE: 543560484Sobrien { 543660484Sobrien unsigned int highpart = 0; 543760484Sobrien unsigned int newinsn = 0xe1a00000; /* nop */ 543860484Sobrien newimm = validate_immediate (value); 543960484Sobrien temp = md_chars_to_number (buf, INSN_SIZE); 544060484Sobrien 544160484Sobrien /* If the instruction will fail, see if we can fix things up by 544260484Sobrien changing the opcode. */ 544360484Sobrien if (newimm == (unsigned int) FAIL 544460484Sobrien && (newimm = negate_data_op (& temp, value)) == (unsigned int) FAIL) 544560484Sobrien { 544660484Sobrien /* No ? OK - try using two ADD instructions to generate the value. */ 544760484Sobrien newimm = validate_immediate_twopart (value, & highpart); 544860484Sobrien 544960484Sobrien /* Yes - then make sure that the second instruction is also an add. */ 545060484Sobrien if (newimm != (unsigned int) FAIL) 545160484Sobrien newinsn = temp; 545260484Sobrien /* Still No ? Try using a negated value. */ 545368765Sobrien else if ((newimm = validate_immediate_twopart (- value, & highpart)) != (unsigned int) FAIL) 545460484Sobrien temp = newinsn = (temp & OPCODE_MASK) | OPCODE_SUB << DATA_OP_SHIFT; 545560484Sobrien /* Otherwise - give up. */ 545660484Sobrien else 545760484Sobrien { 545860484Sobrien as_bad_where (fixP->fx_file, fixP->fx_line, 545960484Sobrien _("Unable to compute ADRL instructions for PC offset of 0x%x"), value); 546060484Sobrien break; 546160484Sobrien } 546260484Sobrien 546360484Sobrien /* Replace the first operand in the 2nd instruction (which is the PC) 546460484Sobrien with the destination register. We have already added in the PC in the 546560484Sobrien first instruction and we do not want to do it again. */ 546660484Sobrien newinsn &= ~ 0xf0000; 546760484Sobrien newinsn |= ((newinsn & 0x0f000) << 4); 546860484Sobrien } 546960484Sobrien 547060484Sobrien newimm |= (temp & 0xfffff000); 547160484Sobrien md_number_to_chars (buf, (valueT) newimm, INSN_SIZE); 547260484Sobrien 547360484Sobrien highpart |= (newinsn & 0xfffff000); 547460484Sobrien md_number_to_chars (buf + INSN_SIZE, (valueT) highpart, INSN_SIZE); 547560484Sobrien } 547660484Sobrien break; 547760484Sobrien 547860484Sobrien case BFD_RELOC_ARM_OFFSET_IMM: 547960484Sobrien sign = value >= 0; 548060484Sobrien 548160484Sobrien if (value < 0) 548260484Sobrien value = - value; 548360484Sobrien 548460484Sobrien if (validate_offset_imm (value, 0) == FAIL) 548560484Sobrien { 548660484Sobrien as_bad_where (fixP->fx_file, fixP->fx_line, 548760484Sobrien _("bad immediate value for offset (%ld)"), (long) value); 548860484Sobrien break; 548960484Sobrien } 549060484Sobrien 549160484Sobrien newval = md_chars_to_number (buf, INSN_SIZE); 549260484Sobrien newval &= 0xff7ff000; 549360484Sobrien newval |= value | (sign ? INDEX_UP : 0); 549460484Sobrien md_number_to_chars (buf, newval, INSN_SIZE); 549560484Sobrien break; 549660484Sobrien 549760484Sobrien case BFD_RELOC_ARM_OFFSET_IMM8: 549860484Sobrien case BFD_RELOC_ARM_HWLITERAL: 549960484Sobrien sign = value >= 0; 550060484Sobrien 550160484Sobrien if (value < 0) 550260484Sobrien value = - value; 550360484Sobrien 550460484Sobrien if (validate_offset_imm (value, 1) == FAIL) 550560484Sobrien { 550660484Sobrien if (fixP->fx_r_type == BFD_RELOC_ARM_HWLITERAL) 550760484Sobrien as_bad_where (fixP->fx_file, fixP->fx_line, 550860484Sobrien _("invalid literal constant: pool needs to be closer")); 550960484Sobrien else 551060484Sobrien as_bad (_("bad immediate value for half-word offset (%ld)"), 551160484Sobrien (long) value); 551260484Sobrien break; 551360484Sobrien } 551460484Sobrien 551560484Sobrien newval = md_chars_to_number (buf, INSN_SIZE); 551660484Sobrien newval &= 0xff7ff0f0; 551760484Sobrien newval |= ((value >> 4) << 8) | (value & 0xf) | (sign ? INDEX_UP : 0); 551860484Sobrien md_number_to_chars (buf, newval, INSN_SIZE); 551960484Sobrien break; 552060484Sobrien 552160484Sobrien case BFD_RELOC_ARM_LITERAL: 552260484Sobrien sign = value >= 0; 552360484Sobrien 552460484Sobrien if (value < 0) 552560484Sobrien value = - value; 552660484Sobrien 552760484Sobrien if (validate_offset_imm (value, 0) == FAIL) 552860484Sobrien { 552960484Sobrien as_bad_where (fixP->fx_file, fixP->fx_line, 553060484Sobrien _("invalid literal constant: pool needs to be closer")); 553160484Sobrien break; 553260484Sobrien } 553360484Sobrien 553460484Sobrien newval = md_chars_to_number (buf, INSN_SIZE); 553560484Sobrien newval &= 0xff7ff000; 553660484Sobrien newval |= value | (sign ? INDEX_UP : 0); 553760484Sobrien md_number_to_chars (buf, newval, INSN_SIZE); 553860484Sobrien break; 553960484Sobrien 554060484Sobrien case BFD_RELOC_ARM_SHIFT_IMM: 554160484Sobrien newval = md_chars_to_number (buf, INSN_SIZE); 554260484Sobrien if (((unsigned long) value) > 32 554360484Sobrien || (value == 32 554460484Sobrien && (((newval & 0x60) == 0) || (newval & 0x60) == 0x60))) 554560484Sobrien { 554660484Sobrien as_bad_where (fixP->fx_file, fixP->fx_line, 554760484Sobrien _("shift expression is too large")); 554860484Sobrien break; 554960484Sobrien } 555060484Sobrien 555160484Sobrien if (value == 0) 555260484Sobrien newval &= ~0x60; /* Shifts of zero must be done as lsl */ 555360484Sobrien else if (value == 32) 555460484Sobrien value = 0; 555560484Sobrien newval &= 0xfffff07f; 555660484Sobrien newval |= (value & 0x1f) << 7; 555760484Sobrien md_number_to_chars (buf, newval , INSN_SIZE); 555860484Sobrien break; 555960484Sobrien 556060484Sobrien case BFD_RELOC_ARM_SWI: 556160484Sobrien if (arm_data->thumb_mode) 556260484Sobrien { 556360484Sobrien if (((unsigned long) value) > 0xff) 556460484Sobrien as_bad_where (fixP->fx_file, fixP->fx_line, 556560484Sobrien _("Invalid swi expression")); 556660484Sobrien newval = md_chars_to_number (buf, THUMB_SIZE) & 0xff00; 556760484Sobrien newval |= value; 556860484Sobrien md_number_to_chars (buf, newval, THUMB_SIZE); 556960484Sobrien } 557060484Sobrien else 557160484Sobrien { 557260484Sobrien if (((unsigned long) value) > 0x00ffffff) 557360484Sobrien as_bad_where (fixP->fx_file, fixP->fx_line, 557460484Sobrien _("Invalid swi expression")); 557560484Sobrien newval = md_chars_to_number (buf, INSN_SIZE) & 0xff000000; 557660484Sobrien newval |= value; 557760484Sobrien md_number_to_chars (buf, newval , INSN_SIZE); 557860484Sobrien } 557960484Sobrien break; 558060484Sobrien 558160484Sobrien case BFD_RELOC_ARM_MULTI: 558260484Sobrien if (((unsigned long) value) > 0xffff) 558360484Sobrien as_bad_where (fixP->fx_file, fixP->fx_line, 558460484Sobrien _("Invalid expression in load/store multiple")); 558560484Sobrien newval = value | md_chars_to_number (buf, INSN_SIZE); 558660484Sobrien md_number_to_chars (buf, newval, INSN_SIZE); 558760484Sobrien break; 558860484Sobrien 558960484Sobrien case BFD_RELOC_ARM_PCREL_BRANCH: 559060484Sobrien newval = md_chars_to_number (buf, INSN_SIZE); 559160484Sobrien 559260484Sobrien /* Sign-extend a 24-bit number. */ 559360484Sobrien#define SEXT24(x) ((((x) & 0xffffff) ^ (~ 0x7fffff)) + 0x800000) 559460484Sobrien 559560484Sobrien#ifdef OBJ_ELF 559660484Sobrien if (! target_oabi) 559760484Sobrien value = fixP->fx_offset; 559860484Sobrien#endif 559960484Sobrien 560060484Sobrien /* We are going to store value (shifted right by two) in the 560160484Sobrien instruction, in a 24 bit, signed field. Thus we need to check 560260484Sobrien that none of the top 8 bits of the shifted value (top 7 bits of 560360484Sobrien the unshifted, unsigned value) are set, or that they are all set. */ 560460484Sobrien if ((value & 0xfe000000UL) != 0 560560484Sobrien && ((value & 0xfe000000UL) != 0xfe000000UL)) 560660484Sobrien { 560760484Sobrien#ifdef OBJ_ELF 560860484Sobrien /* Normally we would be stuck at this point, since we cannot store 560960484Sobrien the absolute address that is the destination of the branch in the 561060484Sobrien 24 bits of the branch instruction. If however, we happen to know 561160484Sobrien that the destination of the branch is in the same section as the 561260484Sobrien branch instruciton itself, then we can compute the relocation for 561360484Sobrien ourselves and not have to bother the linker with it. 561460484Sobrien 561560484Sobrien FIXME: The tests for OBJ_ELF and ! target_oabi are only here 561660484Sobrien because I have not worked out how to do this for OBJ_COFF or 561760484Sobrien target_oabi. */ 561860484Sobrien if (! target_oabi 561960484Sobrien && fixP->fx_addsy != NULL 562060484Sobrien && S_IS_DEFINED (fixP->fx_addsy) 562160484Sobrien && S_GET_SEGMENT (fixP->fx_addsy) == seg) 562260484Sobrien { 562360484Sobrien /* Get pc relative value to go into the branch. */ 562460484Sobrien value = * val; 562560484Sobrien 562660484Sobrien /* Permit a backward branch provided that enough bits are set. 562760484Sobrien Allow a forwards branch, provided that enough bits are clear. */ 562860484Sobrien if ((value & 0xfe000000UL) == 0xfe000000UL 562960484Sobrien || (value & 0xfe000000UL) == 0) 563060484Sobrien fixP->fx_done = 1; 563160484Sobrien } 563260484Sobrien 563360484Sobrien if (! fixP->fx_done) 563460484Sobrien#endif 563560484Sobrien as_bad_where (fixP->fx_file, fixP->fx_line, 563660484Sobrien _("gas can't handle same-section branch dest >= 0x04000000")); 563760484Sobrien } 563860484Sobrien 563960484Sobrien value >>= 2; 564060484Sobrien value += SEXT24 (newval); 564160484Sobrien 564260484Sobrien if ((value & 0xff000000UL) != 0 564360484Sobrien && ((value & 0xff000000UL) != 0xff000000UL)) 564460484Sobrien as_bad_where (fixP->fx_file, fixP->fx_line, 564560484Sobrien _("out of range branch")); 564660484Sobrien 564760484Sobrien newval = (value & 0x00ffffff) | (newval & 0xff000000); 564860484Sobrien md_number_to_chars (buf, newval, INSN_SIZE); 564960484Sobrien break; 565060484Sobrien 565160484Sobrien 565260484Sobrien case BFD_RELOC_THUMB_PCREL_BRANCH9: /* conditional branch */ 565360484Sobrien newval = md_chars_to_number (buf, THUMB_SIZE); 565460484Sobrien { 565560484Sobrien addressT diff = (newval & 0xff) << 1; 565660484Sobrien if (diff & 0x100) 565760484Sobrien diff |= ~0xff; 565860484Sobrien 565960484Sobrien value += diff; 566060484Sobrien if ((value & ~0xff) && ((value & ~0xff) != ~0xff)) 566160484Sobrien as_bad_where (fixP->fx_file, fixP->fx_line, 566260484Sobrien _("Branch out of range")); 566360484Sobrien newval = (newval & 0xff00) | ((value & 0x1ff) >> 1); 566460484Sobrien } 566560484Sobrien md_number_to_chars (buf, newval, THUMB_SIZE); 566660484Sobrien break; 566760484Sobrien 566860484Sobrien case BFD_RELOC_THUMB_PCREL_BRANCH12: /* unconditional branch */ 566960484Sobrien newval = md_chars_to_number (buf, THUMB_SIZE); 567060484Sobrien { 567160484Sobrien addressT diff = (newval & 0x7ff) << 1; 567260484Sobrien if (diff & 0x800) 567360484Sobrien diff |= ~0x7ff; 567460484Sobrien 567560484Sobrien value += diff; 567660484Sobrien if ((value & ~0x7ff) && ((value & ~0x7ff) != ~0x7ff)) 567760484Sobrien as_bad_where (fixP->fx_file, fixP->fx_line, 567860484Sobrien _("Branch out of range")); 567960484Sobrien newval = (newval & 0xf800) | ((value & 0xfff) >> 1); 568060484Sobrien } 568160484Sobrien md_number_to_chars (buf, newval, THUMB_SIZE); 568260484Sobrien break; 568360484Sobrien 568460484Sobrien case BFD_RELOC_THUMB_PCREL_BRANCH23: 568560484Sobrien { 568660484Sobrien offsetT newval2; 568760484Sobrien addressT diff; 568860484Sobrien 568960484Sobrien newval = md_chars_to_number (buf, THUMB_SIZE); 569060484Sobrien newval2 = md_chars_to_number (buf + THUMB_SIZE, THUMB_SIZE); 569160484Sobrien diff = ((newval & 0x7ff) << 12) | ((newval2 & 0x7ff) << 1); 569260484Sobrien if (diff & 0x400000) 569360484Sobrien diff |= ~0x3fffff; 569460484Sobrien#ifdef OBJ_ELF 569560484Sobrien value = fixP->fx_offset; 569660484Sobrien#endif 569760484Sobrien value += diff; 569860484Sobrien if ((value & ~0x3fffff) && ((value & ~0x3fffff) != ~0x3fffff)) 569960484Sobrien as_bad_where (fixP->fx_file, fixP->fx_line, 570060484Sobrien _("Branch with link out of range")); 570160484Sobrien 570260484Sobrien newval = (newval & 0xf800) | ((value & 0x7fffff) >> 12); 570360484Sobrien newval2 = (newval2 & 0xf800) | ((value & 0xfff) >> 1); 570460484Sobrien md_number_to_chars (buf, newval, THUMB_SIZE); 570560484Sobrien md_number_to_chars (buf + THUMB_SIZE, newval2, THUMB_SIZE); 570660484Sobrien } 570760484Sobrien break; 570860484Sobrien 570960484Sobrien case BFD_RELOC_8: 571060484Sobrien if (fixP->fx_done || fixP->fx_pcrel) 571160484Sobrien md_number_to_chars (buf, value, 1); 571260484Sobrien#ifdef OBJ_ELF 571360484Sobrien else if (!target_oabi) 571460484Sobrien { 571560484Sobrien value = fixP->fx_offset; 571660484Sobrien md_number_to_chars (buf, value, 1); 571760484Sobrien } 571860484Sobrien#endif 571960484Sobrien break; 572060484Sobrien 572160484Sobrien case BFD_RELOC_16: 572260484Sobrien if (fixP->fx_done || fixP->fx_pcrel) 572360484Sobrien md_number_to_chars (buf, value, 2); 572460484Sobrien#ifdef OBJ_ELF 572560484Sobrien else if (!target_oabi) 572660484Sobrien { 572760484Sobrien value = fixP->fx_offset; 572860484Sobrien md_number_to_chars (buf, value, 2); 572960484Sobrien } 573060484Sobrien#endif 573160484Sobrien break; 573260484Sobrien 573360484Sobrien#ifdef OBJ_ELF 573460484Sobrien case BFD_RELOC_ARM_GOT32: 573560484Sobrien case BFD_RELOC_ARM_GOTOFF: 573660484Sobrien md_number_to_chars (buf, 0, 4); 573760484Sobrien break; 573860484Sobrien#endif 573960484Sobrien 574060484Sobrien case BFD_RELOC_RVA: 574160484Sobrien case BFD_RELOC_32: 574260484Sobrien if (fixP->fx_done || fixP->fx_pcrel) 574360484Sobrien md_number_to_chars (buf, value, 4); 574460484Sobrien#ifdef OBJ_ELF 574560484Sobrien else if (!target_oabi) 574660484Sobrien { 574760484Sobrien value = fixP->fx_offset; 574860484Sobrien md_number_to_chars (buf, value, 4); 574960484Sobrien } 575060484Sobrien#endif 575160484Sobrien break; 575260484Sobrien 575360484Sobrien#ifdef OBJ_ELF 575460484Sobrien case BFD_RELOC_ARM_PLT32: 575560484Sobrien /* It appears the instruction is fully prepared at this point. */ 575660484Sobrien break; 575760484Sobrien#endif 575860484Sobrien 575960484Sobrien case BFD_RELOC_ARM_GOTPC: 576060484Sobrien md_number_to_chars (buf, value, 4); 576160484Sobrien break; 576260484Sobrien 576360484Sobrien case BFD_RELOC_ARM_CP_OFF_IMM: 576460484Sobrien sign = value >= 0; 576560484Sobrien if (value < -1023 || value > 1023 || (value & 3)) 576660484Sobrien as_bad_where (fixP->fx_file, fixP->fx_line, 576760484Sobrien _("Illegal value for co-processor offset")); 576860484Sobrien if (value < 0) 576960484Sobrien value = -value; 577060484Sobrien newval = md_chars_to_number (buf, INSN_SIZE) & 0xff7fff00; 577160484Sobrien newval |= (value >> 2) | (sign ? INDEX_UP : 0); 577260484Sobrien md_number_to_chars (buf, newval , INSN_SIZE); 577360484Sobrien break; 577460484Sobrien 577560484Sobrien case BFD_RELOC_ARM_THUMB_OFFSET: 577660484Sobrien newval = md_chars_to_number (buf, THUMB_SIZE); 577760484Sobrien /* Exactly what ranges, and where the offset is inserted depends on 577860484Sobrien the type of instruction, we can establish this from the top 4 bits */ 577960484Sobrien switch (newval >> 12) 578060484Sobrien { 578160484Sobrien case 4: /* PC load */ 578260484Sobrien /* Thumb PC loads are somewhat odd, bit 1 of the PC is 578360484Sobrien forced to zero for these loads, so we will need to round 578460484Sobrien up the offset if the instruction address is not word 578560484Sobrien aligned (since the final address produced must be, and 578660484Sobrien we can only describe word-aligned immediate offsets). */ 578760484Sobrien 578860484Sobrien if ((fixP->fx_frag->fr_address + fixP->fx_where + value) & 3) 578960484Sobrien as_bad_where (fixP->fx_file, fixP->fx_line, 579060484Sobrien _("Invalid offset, target not word aligned (0x%08X)"), 579160484Sobrien (unsigned int)(fixP->fx_frag->fr_address + fixP->fx_where + value)); 579260484Sobrien 579360484Sobrien if ((value + 2) & ~0x3fe) 579460484Sobrien as_bad_where (fixP->fx_file, fixP->fx_line, 579560484Sobrien _("Invalid offset, value too big (0x%08X)"), value); 579660484Sobrien 579760484Sobrien /* Round up, since pc will be rounded down. */ 579860484Sobrien newval |= (value + 2) >> 2; 579960484Sobrien break; 580060484Sobrien 580160484Sobrien case 9: /* SP load/store */ 580260484Sobrien if (value & ~0x3fc) 580360484Sobrien as_bad_where (fixP->fx_file, fixP->fx_line, 580460484Sobrien _("Invalid offset, value too big (0x%08X)"), value); 580560484Sobrien newval |= value >> 2; 580660484Sobrien break; 580760484Sobrien 580860484Sobrien case 6: /* Word load/store */ 580960484Sobrien if (value & ~0x7c) 581060484Sobrien as_bad_where (fixP->fx_file, fixP->fx_line, 581160484Sobrien _("Invalid offset, value too big (0x%08X)"), value); 581260484Sobrien newval |= value << 4; /* 6 - 2 */ 581360484Sobrien break; 581460484Sobrien 581560484Sobrien case 7: /* Byte load/store */ 581660484Sobrien if (value & ~0x1f) 581760484Sobrien as_bad_where (fixP->fx_file, fixP->fx_line, 581860484Sobrien _("Invalid offset, value too big (0x%08X)"), value); 581960484Sobrien newval |= value << 6; 582060484Sobrien break; 582160484Sobrien 582260484Sobrien case 8: /* Halfword load/store */ 582360484Sobrien if (value & ~0x3e) 582460484Sobrien as_bad_where (fixP->fx_file, fixP->fx_line, 582560484Sobrien _("Invalid offset, value too big (0x%08X)"), value); 582660484Sobrien newval |= value << 5; /* 6 - 1 */ 582760484Sobrien break; 582860484Sobrien 582960484Sobrien default: 583060484Sobrien as_bad_where (fixP->fx_file, fixP->fx_line, 583160484Sobrien "Unable to process relocation for thumb opcode: %lx", 583260484Sobrien (unsigned long) newval); 583360484Sobrien break; 583460484Sobrien } 583560484Sobrien md_number_to_chars (buf, newval, THUMB_SIZE); 583660484Sobrien break; 583760484Sobrien 583860484Sobrien case BFD_RELOC_ARM_THUMB_ADD: 583960484Sobrien /* This is a complicated relocation, since we use it for all of 584060484Sobrien the following immediate relocations: 584160484Sobrien 3bit ADD/SUB 584260484Sobrien 8bit ADD/SUB 584360484Sobrien 9bit ADD/SUB SP word-aligned 584460484Sobrien 10bit ADD PC/SP word-aligned 584560484Sobrien 584660484Sobrien The type of instruction being processed is encoded in the 584760484Sobrien instruction field: 584860484Sobrien 0x8000 SUB 584960484Sobrien 0x00F0 Rd 585060484Sobrien 0x000F Rs 585160484Sobrien */ 585260484Sobrien newval = md_chars_to_number (buf, THUMB_SIZE); 585360484Sobrien { 585460484Sobrien int rd = (newval >> 4) & 0xf; 585560484Sobrien int rs = newval & 0xf; 585660484Sobrien int subtract = newval & 0x8000; 585760484Sobrien 585860484Sobrien if (rd == REG_SP) 585960484Sobrien { 586060484Sobrien if (value & ~0x1fc) 586160484Sobrien as_bad_where (fixP->fx_file, fixP->fx_line, 586260484Sobrien _("Invalid immediate for stack address calculation")); 586360484Sobrien newval = subtract ? T_OPCODE_SUB_ST : T_OPCODE_ADD_ST; 586460484Sobrien newval |= value >> 2; 586560484Sobrien } 586660484Sobrien else if (rs == REG_PC || rs == REG_SP) 586760484Sobrien { 586860484Sobrien if (subtract || 586960484Sobrien value & ~0x3fc) 587060484Sobrien as_bad_where (fixP->fx_file, fixP->fx_line, 587160484Sobrien _("Invalid immediate for address calculation (value = 0x%08lX)"), 587260484Sobrien (unsigned long) value); 587360484Sobrien newval = (rs == REG_PC ? T_OPCODE_ADD_PC : T_OPCODE_ADD_SP); 587460484Sobrien newval |= rd << 8; 587560484Sobrien newval |= value >> 2; 587660484Sobrien } 587760484Sobrien else if (rs == rd) 587860484Sobrien { 587960484Sobrien if (value & ~0xff) 588060484Sobrien as_bad_where (fixP->fx_file, fixP->fx_line, 588160484Sobrien _("Invalid 8bit immediate")); 588260484Sobrien newval = subtract ? T_OPCODE_SUB_I8 : T_OPCODE_ADD_I8; 588360484Sobrien newval |= (rd << 8) | value; 588460484Sobrien } 588560484Sobrien else 588660484Sobrien { 588760484Sobrien if (value & ~0x7) 588860484Sobrien as_bad_where (fixP->fx_file, fixP->fx_line, 588960484Sobrien _("Invalid 3bit immediate")); 589060484Sobrien newval = subtract ? T_OPCODE_SUB_I3 : T_OPCODE_ADD_I3; 589160484Sobrien newval |= rd | (rs << 3) | (value << 6); 589260484Sobrien } 589360484Sobrien } 589460484Sobrien md_number_to_chars (buf, newval , THUMB_SIZE); 589560484Sobrien break; 589660484Sobrien 589760484Sobrien case BFD_RELOC_ARM_THUMB_IMM: 589860484Sobrien newval = md_chars_to_number (buf, THUMB_SIZE); 589960484Sobrien switch (newval >> 11) 590060484Sobrien { 590160484Sobrien case 0x04: /* 8bit immediate MOV */ 590260484Sobrien case 0x05: /* 8bit immediate CMP */ 590360484Sobrien if (value < 0 || value > 255) 590460484Sobrien as_bad_where (fixP->fx_file, fixP->fx_line, 590560484Sobrien _("Invalid immediate: %ld is too large"), 590660484Sobrien (long) value); 590760484Sobrien newval |= value; 590860484Sobrien break; 590960484Sobrien 591060484Sobrien default: 591160484Sobrien abort (); 591260484Sobrien } 591360484Sobrien md_number_to_chars (buf, newval , THUMB_SIZE); 591460484Sobrien break; 591560484Sobrien 591660484Sobrien case BFD_RELOC_ARM_THUMB_SHIFT: 591760484Sobrien /* 5bit shift value (0..31) */ 591860484Sobrien if (value < 0 || value > 31) 591960484Sobrien as_bad_where (fixP->fx_file, fixP->fx_line, 592060484Sobrien _("Illegal Thumb shift value: %ld"), (long) value); 592160484Sobrien newval = md_chars_to_number (buf, THUMB_SIZE) & 0xf03f; 592260484Sobrien newval |= value << 6; 592360484Sobrien md_number_to_chars (buf, newval , THUMB_SIZE); 592460484Sobrien break; 592560484Sobrien 592660484Sobrien case BFD_RELOC_VTABLE_INHERIT: 592760484Sobrien case BFD_RELOC_VTABLE_ENTRY: 592860484Sobrien fixP->fx_done = 0; 592960484Sobrien return 1; 593060484Sobrien 593160484Sobrien case BFD_RELOC_NONE: 593260484Sobrien default: 593360484Sobrien as_bad_where (fixP->fx_file, fixP->fx_line, 593460484Sobrien _("Bad relocation fixup type (%d)"), fixP->fx_r_type); 593560484Sobrien } 593660484Sobrien 593760484Sobrien return 1; 593860484Sobrien} 593960484Sobrien 594060484Sobrien/* Translate internal representation of relocation info to BFD target 594160484Sobrien format. */ 594260484Sobrienarelent * 594360484Sobrientc_gen_reloc (section, fixp) 594460484Sobrien asection * section ATTRIBUTE_UNUSED; 594560484Sobrien fixS * fixp; 594660484Sobrien{ 594760484Sobrien arelent * reloc; 594860484Sobrien bfd_reloc_code_real_type code; 594960484Sobrien 595060484Sobrien reloc = (arelent *) xmalloc (sizeof (arelent)); 595160484Sobrien 595260484Sobrien reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *)); 595360484Sobrien *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy); 595460484Sobrien reloc->address = fixp->fx_frag->fr_address + fixp->fx_where; 595560484Sobrien 595660484Sobrien /* @@ Why fx_addnumber sometimes and fx_offset other times? */ 595760484Sobrien#ifndef OBJ_ELF 595860484Sobrien if (fixp->fx_pcrel == 0) 595960484Sobrien reloc->addend = fixp->fx_offset; 596060484Sobrien else 596160484Sobrien reloc->addend = fixp->fx_offset = reloc->address; 596260484Sobrien#else /* OBJ_ELF */ 596360484Sobrien reloc->addend = fixp->fx_offset; 596460484Sobrien#endif 596560484Sobrien 596660484Sobrien switch (fixp->fx_r_type) 596760484Sobrien { 596860484Sobrien case BFD_RELOC_8: 596960484Sobrien if (fixp->fx_pcrel) 597060484Sobrien { 597160484Sobrien code = BFD_RELOC_8_PCREL; 597260484Sobrien break; 597360484Sobrien } 597460484Sobrien 597560484Sobrien case BFD_RELOC_16: 597660484Sobrien if (fixp->fx_pcrel) 597760484Sobrien { 597860484Sobrien code = BFD_RELOC_16_PCREL; 597960484Sobrien break; 598060484Sobrien } 598160484Sobrien 598260484Sobrien case BFD_RELOC_32: 598360484Sobrien if (fixp->fx_pcrel) 598460484Sobrien { 598560484Sobrien code = BFD_RELOC_32_PCREL; 598660484Sobrien break; 598760484Sobrien } 598860484Sobrien 598960484Sobrien case BFD_RELOC_ARM_PCREL_BRANCH: 599060484Sobrien case BFD_RELOC_RVA: 599160484Sobrien case BFD_RELOC_THUMB_PCREL_BRANCH9: 599260484Sobrien case BFD_RELOC_THUMB_PCREL_BRANCH12: 599360484Sobrien case BFD_RELOC_THUMB_PCREL_BRANCH23: 599460484Sobrien case BFD_RELOC_VTABLE_ENTRY: 599560484Sobrien case BFD_RELOC_VTABLE_INHERIT: 599660484Sobrien code = fixp->fx_r_type; 599760484Sobrien break; 599860484Sobrien 599960484Sobrien case BFD_RELOC_ARM_LITERAL: 600060484Sobrien case BFD_RELOC_ARM_HWLITERAL: 600160484Sobrien /* If this is called then the a literal has been referenced across 600260484Sobrien a section boundary - possibly due to an implicit dump */ 600360484Sobrien as_bad_where (fixp->fx_file, fixp->fx_line, 600460484Sobrien _("Literal referenced across section boundary (Implicit dump?)")); 600560484Sobrien return NULL; 600660484Sobrien 600760484Sobrien#ifdef OBJ_ELF 600860484Sobrien case BFD_RELOC_ARM_GOT32: 600960484Sobrien case BFD_RELOC_ARM_GOTOFF: 601060484Sobrien case BFD_RELOC_ARM_PLT32: 601160484Sobrien code = fixp->fx_r_type; 601260484Sobrien break; 601360484Sobrien#endif 601460484Sobrien 601560484Sobrien case BFD_RELOC_ARM_IMMEDIATE: 601660484Sobrien as_bad_where (fixp->fx_file, fixp->fx_line, 601760484Sobrien _("Internal_relocation (type %d) not fixed up (IMMEDIATE)"), 601860484Sobrien fixp->fx_r_type); 601960484Sobrien return NULL; 602060484Sobrien 602160484Sobrien case BFD_RELOC_ARM_ADRL_IMMEDIATE: 602260484Sobrien as_bad_where (fixp->fx_file, fixp->fx_line, 602360484Sobrien _("ADRL used for a symbol not defined in the same file"), 602460484Sobrien fixp->fx_r_type); 602560484Sobrien return NULL; 602660484Sobrien 602760484Sobrien case BFD_RELOC_ARM_OFFSET_IMM: 602860484Sobrien as_bad_where (fixp->fx_file, fixp->fx_line, 602960484Sobrien _("Internal_relocation (type %d) not fixed up (OFFSET_IMM)"), 603060484Sobrien fixp->fx_r_type); 603160484Sobrien return NULL; 603260484Sobrien 603360484Sobrien default: 603460484Sobrien { 603560484Sobrien char * type; 603660484Sobrien switch (fixp->fx_r_type) 603760484Sobrien { 603860484Sobrien case BFD_RELOC_ARM_IMMEDIATE: type = "IMMEDIATE"; break; 603960484Sobrien case BFD_RELOC_ARM_OFFSET_IMM: type = "OFFSET_IMM"; break; 604060484Sobrien case BFD_RELOC_ARM_OFFSET_IMM8: type = "OFFSET_IMM8"; break; 604160484Sobrien case BFD_RELOC_ARM_SHIFT_IMM: type = "SHIFT_IMM"; break; 604260484Sobrien case BFD_RELOC_ARM_SWI: type = "SWI"; break; 604360484Sobrien case BFD_RELOC_ARM_MULTI: type = "MULTI"; break; 604460484Sobrien case BFD_RELOC_ARM_CP_OFF_IMM: type = "CP_OFF_IMM"; break; 604560484Sobrien case BFD_RELOC_ARM_THUMB_ADD: type = "THUMB_ADD"; break; 604660484Sobrien case BFD_RELOC_ARM_THUMB_SHIFT: type = "THUMB_SHIFT"; break; 604760484Sobrien case BFD_RELOC_ARM_THUMB_IMM: type = "THUMB_IMM"; break; 604860484Sobrien case BFD_RELOC_ARM_THUMB_OFFSET: type = "THUMB_OFFSET"; break; 604960484Sobrien default: type = _("<unknown>"); break; 605060484Sobrien } 605160484Sobrien as_bad_where (fixp->fx_file, fixp->fx_line, 605260484Sobrien _("Can not represent %s relocation in this object file format (%d)"), 605360484Sobrien type, fixp->fx_pcrel); 605460484Sobrien return NULL; 605560484Sobrien } 605660484Sobrien } 605760484Sobrien 605860484Sobrien#ifdef OBJ_ELF 605960484Sobrien if (code == BFD_RELOC_32_PCREL 606060484Sobrien && GOT_symbol 606160484Sobrien && fixp->fx_addsy == GOT_symbol) 606260484Sobrien { 606360484Sobrien code = BFD_RELOC_ARM_GOTPC; 606460484Sobrien reloc->addend = fixp->fx_offset = reloc->address; 606560484Sobrien } 606660484Sobrien#endif 606760484Sobrien 606860484Sobrien reloc->howto = bfd_reloc_type_lookup (stdoutput, code); 606960484Sobrien 607060484Sobrien if (reloc->howto == NULL) 607160484Sobrien { 607260484Sobrien as_bad_where (fixp->fx_file, fixp->fx_line, 607360484Sobrien _("Can not represent %s relocation in this object file format"), 607460484Sobrien bfd_get_reloc_code_name (code)); 607560484Sobrien return NULL; 607660484Sobrien } 607760484Sobrien 607860484Sobrien /* HACK: Since arm ELF uses Rel instead of Rela, encode the 607960484Sobrien vtable entry to be used in the relocation's section offset. */ 608060484Sobrien if (fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY) 608160484Sobrien reloc->address = fixp->fx_offset; 608260484Sobrien 608360484Sobrien return reloc; 608460484Sobrien} 608560484Sobrien 608660484Sobrienint 608760484Sobrienmd_estimate_size_before_relax (fragP, segtype) 608860484Sobrien fragS * fragP ATTRIBUTE_UNUSED; 608960484Sobrien segT segtype ATTRIBUTE_UNUSED; 609060484Sobrien{ 609160484Sobrien as_fatal (_("md_estimate_size_before_relax\n")); 609260484Sobrien return 1; 609360484Sobrien} 609460484Sobrien 609560484Sobrienstatic void 609660484Sobrienoutput_inst PARAMS ((void)) 609760484Sobrien{ 609860484Sobrien char * to = NULL; 609960484Sobrien 610060484Sobrien if (inst.error) 610160484Sobrien { 610260484Sobrien as_bad (inst.error); 610360484Sobrien return; 610460484Sobrien } 610560484Sobrien 610660484Sobrien to = frag_more (inst.size); 610760484Sobrien 610860484Sobrien if (thumb_mode && (inst.size > THUMB_SIZE)) 610960484Sobrien { 611060484Sobrien assert (inst.size == (2 * THUMB_SIZE)); 611160484Sobrien md_number_to_chars (to, inst.instruction >> 16, THUMB_SIZE); 611260484Sobrien md_number_to_chars (to + THUMB_SIZE, inst.instruction, THUMB_SIZE); 611360484Sobrien } 611460484Sobrien else if (inst.size > INSN_SIZE) 611560484Sobrien { 611660484Sobrien assert (inst.size == (2 * INSN_SIZE)); 611760484Sobrien md_number_to_chars (to, inst.instruction, INSN_SIZE); 611860484Sobrien md_number_to_chars (to + INSN_SIZE, inst.instruction, INSN_SIZE); 611960484Sobrien } 612060484Sobrien else 612160484Sobrien md_number_to_chars (to, inst.instruction, inst.size); 612260484Sobrien 612360484Sobrien if (inst.reloc.type != BFD_RELOC_NONE) 612460484Sobrien fix_new_arm (frag_now, to - frag_now->fr_literal, 612560484Sobrien inst.size, & inst.reloc.exp, inst.reloc.pc_rel, 612660484Sobrien inst.reloc.type); 612760484Sobrien 612860484Sobrien return; 612960484Sobrien} 613060484Sobrien 613160484Sobrienvoid 613260484Sobrienmd_assemble (str) 613360484Sobrien char * str; 613460484Sobrien{ 613560484Sobrien char c; 613660484Sobrien char * p; 613760484Sobrien char * q; 613860484Sobrien char * start; 613960484Sobrien 614060484Sobrien /* Align the instruction. 614160484Sobrien This may not be the right thing to do but ... */ 614260484Sobrien /* arm_align (2, 0); */ 614360484Sobrien listing_prev_line (); /* Defined in listing.h */ 614460484Sobrien 614560484Sobrien /* Align the previous label if needed. */ 614660484Sobrien if (last_label_seen != NULL) 614760484Sobrien { 614860484Sobrien symbol_set_frag (last_label_seen, frag_now); 614960484Sobrien S_SET_VALUE (last_label_seen, (valueT) frag_now_fix ()); 615060484Sobrien S_SET_SEGMENT (last_label_seen, now_seg); 615160484Sobrien } 615260484Sobrien 615360484Sobrien memset (&inst, '\0', sizeof (inst)); 615460484Sobrien inst.reloc.type = BFD_RELOC_NONE; 615560484Sobrien 615660484Sobrien skip_whitespace (str); 615760484Sobrien 615860484Sobrien /* Scan up to the end of the op-code, which must end in white space or 615960484Sobrien end of string. */ 616060484Sobrien for (start = p = str; *p != '\0'; p++) 616160484Sobrien if (*p == ' ') 616260484Sobrien break; 616360484Sobrien 616460484Sobrien if (p == str) 616560484Sobrien { 616660484Sobrien as_bad (_("No operator -- statement `%s'\n"), str); 616760484Sobrien return; 616860484Sobrien } 616960484Sobrien 617060484Sobrien if (thumb_mode) 617160484Sobrien { 617260484Sobrien CONST struct thumb_opcode * opcode; 617360484Sobrien 617460484Sobrien c = *p; 617560484Sobrien *p = '\0'; 617660484Sobrien opcode = (CONST struct thumb_opcode *) hash_find (arm_tops_hsh, str); 617760484Sobrien *p = c; 617860484Sobrien 617960484Sobrien if (opcode) 618060484Sobrien { 618160484Sobrien /* Check that this instruction is supported for this CPU. */ 618260484Sobrien if (thumb_mode == 1 && (opcode->variants & cpu_variant) == 0) 618360484Sobrien { 618460484Sobrien as_bad (_("selected processor does not support this opcode")); 618560484Sobrien return; 618660484Sobrien } 618760484Sobrien 618860484Sobrien inst.instruction = opcode->value; 618960484Sobrien inst.size = opcode->size; 619060484Sobrien (*opcode->parms)(p); 619160484Sobrien output_inst (); 619260484Sobrien return; 619360484Sobrien } 619460484Sobrien } 619560484Sobrien else 619660484Sobrien { 619760484Sobrien CONST struct asm_opcode * opcode; 619860484Sobrien unsigned long cond_code; 619960484Sobrien 620060484Sobrien inst.size = INSN_SIZE; 620160484Sobrien /* p now points to the end of the opcode, probably white space, but we 620260484Sobrien have to break the opcode up in case it contains condionals and flags; 620360484Sobrien keep trying with progressively smaller basic instructions until one 620460484Sobrien matches, or we run out of opcode. */ 620560484Sobrien q = (p - str > LONGEST_INST) ? str + LONGEST_INST : p; 620660484Sobrien for (; q != str; q--) 620760484Sobrien { 620860484Sobrien c = *q; 620960484Sobrien *q = '\0'; 621060484Sobrien opcode = (CONST struct asm_opcode *) hash_find (arm_ops_hsh, str); 621160484Sobrien *q = c; 621260484Sobrien 621360484Sobrien if (opcode && opcode->template) 621460484Sobrien { 621560484Sobrien unsigned long flag_bits = 0; 621660484Sobrien char * r; 621760484Sobrien 621860484Sobrien /* Check that this instruction is supported for this CPU. */ 621960484Sobrien if ((opcode->variants & cpu_variant) == 0) 622060484Sobrien goto try_shorter; 622160484Sobrien 622260484Sobrien inst.instruction = opcode->value; 622360484Sobrien if (q == p) /* Just a simple opcode. */ 622460484Sobrien { 622560484Sobrien if (opcode->comp_suffix) 622660484Sobrien { 622760484Sobrien if (*opcode->comp_suffix != '\0') 622860484Sobrien as_bad (_("Opcode `%s' must have suffix from list: <%s>"), 622960484Sobrien str, opcode->comp_suffix); 623060484Sobrien else 623160484Sobrien /* Not a conditional instruction. */ 623260484Sobrien (*opcode->parms)(q, 0); 623360484Sobrien } 623460484Sobrien else 623560484Sobrien { 623660484Sobrien /* A conditional instruction with default condition. */ 623760484Sobrien inst.instruction |= COND_ALWAYS; 623860484Sobrien (*opcode->parms)(q, 0); 623960484Sobrien } 624060484Sobrien output_inst (); 624160484Sobrien return; 624260484Sobrien } 624360484Sobrien 624460484Sobrien /* Not just a simple opcode. Check if extra is a conditional. */ 624560484Sobrien r = q; 624660484Sobrien if (p - r >= 2) 624760484Sobrien { 624860484Sobrien CONST struct asm_cond *cond; 624960484Sobrien char d = *(r + 2); 625060484Sobrien 625160484Sobrien *(r + 2) = '\0'; 625260484Sobrien cond = (CONST struct asm_cond *) hash_find (arm_cond_hsh, r); 625360484Sobrien *(r + 2) = d; 625460484Sobrien if (cond) 625560484Sobrien { 625660484Sobrien if (cond->value == 0xf0000000) 625760484Sobrien as_tsktsk ( 625860484Sobrien_("Warning: Use of the 'nv' conditional is deprecated\n")); 625960484Sobrien 626060484Sobrien cond_code = cond->value; 626160484Sobrien r += 2; 626260484Sobrien } 626360484Sobrien else 626460484Sobrien cond_code = COND_ALWAYS; 626560484Sobrien } 626660484Sobrien else 626760484Sobrien cond_code = COND_ALWAYS; 626860484Sobrien 626960484Sobrien /* Apply the conditional, or complain it's not allowed. */ 627060484Sobrien if (opcode->comp_suffix && *opcode->comp_suffix == '\0') 627160484Sobrien { 627260484Sobrien /* Instruction isn't conditional */ 627360484Sobrien if (cond_code != COND_ALWAYS) 627460484Sobrien { 627560484Sobrien as_bad (_("Opcode `%s' is unconditional\n"), str); 627660484Sobrien return; 627760484Sobrien } 627860484Sobrien } 627960484Sobrien else 628060484Sobrien /* Instruction is conditional: set the condition into it. */ 628160484Sobrien inst.instruction |= cond_code; 628260484Sobrien 628360484Sobrien 628460484Sobrien /* If there is a compulsory suffix, it should come here, before 628560484Sobrien any optional flags. */ 628660484Sobrien if (opcode->comp_suffix && *opcode->comp_suffix != '\0') 628760484Sobrien { 628860484Sobrien CONST char *s = opcode->comp_suffix; 628960484Sobrien 629060484Sobrien while (*s) 629160484Sobrien { 629260484Sobrien inst.suffix++; 629360484Sobrien if (*r == *s) 629460484Sobrien break; 629560484Sobrien s++; 629660484Sobrien } 629760484Sobrien 629860484Sobrien if (*s == '\0') 629960484Sobrien { 630060484Sobrien as_bad (_("Opcode `%s' must have suffix from <%s>\n"), str, 630160484Sobrien opcode->comp_suffix); 630260484Sobrien return; 630360484Sobrien } 630460484Sobrien 630560484Sobrien r++; 630660484Sobrien } 630760484Sobrien 630860484Sobrien /* The remainder, if any should now be flags for the instruction; 630960484Sobrien Scan these checking each one found with the opcode. */ 631060484Sobrien if (r != p) 631160484Sobrien { 631260484Sobrien char d; 631360484Sobrien CONST struct asm_flg *flag = opcode->flags; 631460484Sobrien 631560484Sobrien if (flag) 631660484Sobrien { 631760484Sobrien int flagno; 631860484Sobrien 631960484Sobrien d = *p; 632060484Sobrien *p = '\0'; 632160484Sobrien 632260484Sobrien for (flagno = 0; flag[flagno].template; flagno++) 632360484Sobrien { 632460484Sobrien if (streq (r, flag[flagno].template)) 632560484Sobrien { 632660484Sobrien flag_bits |= flag[flagno].set_bits; 632760484Sobrien break; 632860484Sobrien } 632960484Sobrien } 633060484Sobrien 633160484Sobrien *p = d; 633260484Sobrien if (! flag[flagno].template) 633360484Sobrien goto try_shorter; 633460484Sobrien } 633560484Sobrien else 633660484Sobrien goto try_shorter; 633760484Sobrien } 633860484Sobrien 633960484Sobrien (*opcode->parms) (p, flag_bits); 634060484Sobrien output_inst (); 634160484Sobrien return; 634260484Sobrien } 634360484Sobrien 634460484Sobrien try_shorter: 634560484Sobrien ; 634660484Sobrien } 634760484Sobrien } 634860484Sobrien 634960484Sobrien /* It wasn't an instruction, but it might be a register alias of the form 635060484Sobrien alias .req reg */ 635160484Sobrien q = p; 635260484Sobrien skip_whitespace (q); 635360484Sobrien 635460484Sobrien c = *p; 635560484Sobrien *p = '\0'; 635660484Sobrien 635760484Sobrien if (*q && !strncmp (q, ".req ", 4)) 635860484Sobrien { 635960484Sobrien int reg; 636060484Sobrien char * copy_of_str = str; 636160484Sobrien char * r; 636260484Sobrien 636360484Sobrien q += 4; 636460484Sobrien skip_whitespace (q); 636560484Sobrien 636660484Sobrien for (r = q; *r != '\0'; r++) 636760484Sobrien if (*r == ' ') 636860484Sobrien break; 636960484Sobrien 637060484Sobrien if (r != q) 637160484Sobrien { 637260484Sobrien int regnum; 637360484Sobrien char d = *r; 637460484Sobrien 637560484Sobrien *r = '\0'; 637660484Sobrien regnum = arm_reg_parse (& q); 637760484Sobrien *r = d; 637860484Sobrien 637960484Sobrien reg = arm_reg_parse (& str); 638060484Sobrien 638160484Sobrien if (reg == FAIL) 638260484Sobrien { 638360484Sobrien if (regnum != FAIL) 638460484Sobrien insert_reg_alias (str, regnum); 638560484Sobrien else 638660484Sobrien as_warn (_("register '%s' does not exist\n"), q); 638760484Sobrien } 638860484Sobrien else if (regnum != FAIL) 638960484Sobrien { 639060484Sobrien if (reg != regnum) 639160484Sobrien as_warn (_("ignoring redefinition of register alias '%s'"), 639260484Sobrien copy_of_str ); 639360484Sobrien 639460484Sobrien /* Do not warn about redefinitions to the same alias. */ 639560484Sobrien } 639660484Sobrien else 639760484Sobrien as_warn (_("ignoring redefinition of register alias '%s' to non-existant register '%s'"), 639860484Sobrien copy_of_str, q); 639960484Sobrien } 640060484Sobrien else 640160484Sobrien as_warn (_("ignoring incomplete .req pseuso op")); 640260484Sobrien 640360484Sobrien *p = c; 640460484Sobrien return; 640560484Sobrien } 640660484Sobrien 640760484Sobrien *p = c; 640860484Sobrien as_bad (_("bad instruction `%s'"), start); 640960484Sobrien} 641060484Sobrien 641160484Sobrien/* 641260484Sobrien * md_parse_option 641360484Sobrien * Invocation line includes a switch not recognized by the base assembler. 641460484Sobrien * See if it's a processor-specific option. These are: 641560484Sobrien * Cpu variants, the arm part is optional: 641660484Sobrien * -m[arm]1 Currently not supported. 641760484Sobrien * -m[arm]2, -m[arm]250 Arm 2 and Arm 250 processor 641860484Sobrien * -m[arm]3 Arm 3 processor 641960484Sobrien * -m[arm]6[xx], Arm 6 processors 642060484Sobrien * -m[arm]7[xx][t][[d]m] Arm 7 processors 642160484Sobrien * -m[arm]8[10] Arm 8 processors 642260484Sobrien * -m[arm]9[20][tdmi] Arm 9 processors 642360484Sobrien * -mstrongarm[110[0]] StrongARM processors 642461843Sobrien * -m[arm]v[2345[t]] Arm architectures 642560484Sobrien * -mall All (except the ARM1) 642660484Sobrien * FP variants: 642760484Sobrien * -mfpa10, -mfpa11 FPA10 and 11 co-processor instructions 642860484Sobrien * -mfpe-old (No float load/store multiples) 642960484Sobrien * -mno-fpu Disable all floating point instructions 643060484Sobrien * Run-time endian selection: 643160484Sobrien * -EB big endian cpu 643260484Sobrien * -EL little endian cpu 643360484Sobrien * ARM Procedure Calling Standard: 643460484Sobrien * -mapcs-32 32 bit APCS 643560484Sobrien * -mapcs-26 26 bit APCS 643660484Sobrien * -mapcs-float Pass floats in float regs 643760484Sobrien * -mapcs-reentrant Position independent code 643860484Sobrien * -mthumb-interwork Code supports Arm/Thumb interworking 643960484Sobrien * -moabi Old ELF ABI 644060484Sobrien */ 644160484Sobrien 644260484SobrienCONST char * md_shortopts = "m:k"; 644360484Sobrienstruct option md_longopts[] = 644460484Sobrien{ 644560484Sobrien#ifdef ARM_BI_ENDIAN 644660484Sobrien#define OPTION_EB (OPTION_MD_BASE + 0) 644760484Sobrien {"EB", no_argument, NULL, OPTION_EB}, 644860484Sobrien#define OPTION_EL (OPTION_MD_BASE + 1) 644960484Sobrien {"EL", no_argument, NULL, OPTION_EL}, 645060484Sobrien#ifdef OBJ_ELF 645160484Sobrien#define OPTION_OABI (OPTION_MD_BASE +2) 645260484Sobrien {"oabi", no_argument, NULL, OPTION_OABI}, 645360484Sobrien#endif 645460484Sobrien#endif 645560484Sobrien {NULL, no_argument, NULL, 0} 645660484Sobrien}; 645760484Sobriensize_t md_longopts_size = sizeof (md_longopts); 645860484Sobrien 645960484Sobrienint 646060484Sobrienmd_parse_option (c, arg) 646160484Sobrien int c; 646260484Sobrien char * arg; 646360484Sobrien{ 646460484Sobrien char * str = arg; 646560484Sobrien 646660484Sobrien switch (c) 646760484Sobrien { 646860484Sobrien#ifdef ARM_BI_ENDIAN 646960484Sobrien case OPTION_EB: 647060484Sobrien target_big_endian = 1; 647160484Sobrien break; 647260484Sobrien case OPTION_EL: 647360484Sobrien target_big_endian = 0; 647460484Sobrien break; 647560484Sobrien#endif 647660484Sobrien 647760484Sobrien case 'm': 647860484Sobrien switch (*str) 647960484Sobrien { 648060484Sobrien case 'f': 648160484Sobrien if (streq (str, "fpa10")) 648260484Sobrien cpu_variant = (cpu_variant & ~FPU_ALL) | FPU_FPA10; 648360484Sobrien else if (streq (str, "fpa11")) 648460484Sobrien cpu_variant = (cpu_variant & ~FPU_ALL) | FPU_FPA11; 648560484Sobrien else if (streq (str, "fpe-old")) 648660484Sobrien cpu_variant = (cpu_variant & ~FPU_ALL) | FPU_CORE; 648760484Sobrien else 648860484Sobrien goto bad; 648960484Sobrien break; 649060484Sobrien 649160484Sobrien case 'n': 649260484Sobrien if (streq (str, "no-fpu")) 649360484Sobrien cpu_variant &= ~FPU_ALL; 649460484Sobrien break; 649560484Sobrien 649660484Sobrien#ifdef OBJ_ELF 649760484Sobrien case 'o': 649860484Sobrien if (streq (str, "oabi")) 649960484Sobrien target_oabi = true; 650060484Sobrien break; 650160484Sobrien#endif 650260484Sobrien 650360484Sobrien case 't': 650460484Sobrien /* Limit assembler to generating only Thumb instructions: */ 650560484Sobrien if (streq (str, "thumb")) 650660484Sobrien { 650760484Sobrien cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_THUMB; 650860484Sobrien cpu_variant = (cpu_variant & ~FPU_ALL) | FPU_NONE; 650960484Sobrien thumb_mode = 1; 651060484Sobrien } 651160484Sobrien else if (streq (str, "thumb-interwork")) 651260484Sobrien { 651360484Sobrien if ((cpu_variant & ARM_THUMB) == 0) 651460484Sobrien cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_ARCH_V4T; 651560484Sobrien#if defined OBJ_COFF || defined OBJ_ELF 651660484Sobrien support_interwork = true; 651760484Sobrien#endif 651860484Sobrien } 651960484Sobrien else 652060484Sobrien goto bad; 652160484Sobrien break; 652260484Sobrien 652360484Sobrien default: 652460484Sobrien if (streq (str, "all")) 652560484Sobrien { 652660484Sobrien cpu_variant = ARM_ALL | FPU_ALL; 652760484Sobrien return 1; 652860484Sobrien } 652960484Sobrien#if defined OBJ_COFF || defined OBJ_ELF 653060484Sobrien if (! strncmp (str, "apcs-", 5)) 653160484Sobrien { 653260484Sobrien /* GCC passes on all command line options starting "-mapcs-..." 653360484Sobrien to us, so we must parse them here. */ 653460484Sobrien 653560484Sobrien str += 5; 653660484Sobrien 653760484Sobrien if (streq (str, "32")) 653860484Sobrien { 653960484Sobrien uses_apcs_26 = false; 654060484Sobrien return 1; 654160484Sobrien } 654260484Sobrien else if (streq (str, "26")) 654360484Sobrien { 654460484Sobrien uses_apcs_26 = true; 654560484Sobrien return 1; 654660484Sobrien } 654760484Sobrien else if (streq (str, "frame")) 654860484Sobrien { 654960484Sobrien /* Stack frames are being generated - does not affect 655060484Sobrien linkage of code. */ 655160484Sobrien return 1; 655260484Sobrien } 655360484Sobrien else if (streq (str, "stack-check")) 655460484Sobrien { 655560484Sobrien /* Stack checking is being performed - does not affect 655660484Sobrien linkage, but does require that the functions 655760484Sobrien __rt_stkovf_split_small and __rt_stkovf_split_big be 655860484Sobrien present in the final link. */ 655960484Sobrien 656060484Sobrien return 1; 656160484Sobrien } 656260484Sobrien else if (streq (str, "float")) 656360484Sobrien { 656460484Sobrien /* Floating point arguments are being passed in the floating 656560484Sobrien point registers. This does affect linking, since this 656660484Sobrien version of the APCS is incompatible with the version that 656760484Sobrien passes floating points in the integer registers. */ 656860484Sobrien 656960484Sobrien uses_apcs_float = true; 657060484Sobrien return 1; 657160484Sobrien } 657260484Sobrien else if (streq (str, "reentrant")) 657360484Sobrien { 657460484Sobrien /* Reentrant code has been generated. This does affect 657560484Sobrien linking, since there is no point in linking reentrant/ 657660484Sobrien position independent code with absolute position code. */ 657760484Sobrien pic_code = true; 657860484Sobrien return 1; 657960484Sobrien } 658060484Sobrien 658160484Sobrien as_bad (_("Unrecognised APCS switch -m%s"), arg); 658260484Sobrien return 0; 658360484Sobrien } 658460484Sobrien#endif 658560484Sobrien /* Strip off optional "arm" */ 658660484Sobrien if (! strncmp (str, "arm", 3)) 658760484Sobrien str += 3; 658860484Sobrien 658960484Sobrien switch (*str) 659060484Sobrien { 659160484Sobrien case '1': 659260484Sobrien if (streq (str, "1")) 659360484Sobrien cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_1; 659460484Sobrien else 659560484Sobrien goto bad; 659660484Sobrien break; 659760484Sobrien 659860484Sobrien case '2': 659960484Sobrien if (streq (str, "2")) 660060484Sobrien cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_2; 660160484Sobrien else if (streq (str, "250")) 660260484Sobrien cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_250; 660360484Sobrien else 660460484Sobrien goto bad; 660560484Sobrien break; 660660484Sobrien 660760484Sobrien case '3': 660860484Sobrien if (streq (str, "3")) 660960484Sobrien cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_3; 661060484Sobrien else 661160484Sobrien goto bad; 661260484Sobrien break; 661360484Sobrien 661460484Sobrien case '6': 661560484Sobrien switch (strtol (str, NULL, 10)) 661660484Sobrien { 661760484Sobrien case 6: 661860484Sobrien case 60: 661960484Sobrien case 600: 662060484Sobrien case 610: 662160484Sobrien case 620: 662260484Sobrien cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_6; 662360484Sobrien break; 662460484Sobrien default: 662560484Sobrien goto bad; 662660484Sobrien } 662760484Sobrien break; 662860484Sobrien 662960484Sobrien case '7': 663060484Sobrien switch (strtol (str, & str, 10)) /* Eat the processor name */ 663160484Sobrien { 663260484Sobrien case 7: 663360484Sobrien case 70: 663460484Sobrien case 700: 663560484Sobrien case 710: 663660484Sobrien case 720: 663760484Sobrien case 7100: 663860484Sobrien case 7500: 663960484Sobrien break; 664060484Sobrien default: 664160484Sobrien goto bad; 664260484Sobrien } 664360484Sobrien cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_7; 664460484Sobrien for (; *str; str++) 664560484Sobrien { 664660484Sobrien switch (* str) 664760484Sobrien { 664860484Sobrien case 't': 664960484Sobrien cpu_variant |= (ARM_THUMB | ARM_ARCH_V4); 665060484Sobrien break; 665160484Sobrien 665260484Sobrien case 'm': 665360484Sobrien cpu_variant |= ARM_LONGMUL; 665460484Sobrien break; 665560484Sobrien 665660484Sobrien case 'f': /* fe => fp enabled cpu. */ 665760484Sobrien if (str[1] == 'e') 665860484Sobrien ++ str; 665960484Sobrien else 666060484Sobrien goto bad; 666160484Sobrien 666260484Sobrien case 'c': /* Left over from 710c processor name. */ 666360484Sobrien case 'd': /* Debug */ 666460484Sobrien case 'i': /* Embedded ICE */ 666560484Sobrien /* Included for completeness in ARM processor naming. */ 666660484Sobrien break; 666760484Sobrien 666860484Sobrien default: 666960484Sobrien goto bad; 667060484Sobrien } 667160484Sobrien } 667260484Sobrien break; 667360484Sobrien 667460484Sobrien case '8': 667560484Sobrien if (streq (str, "8") || streq (str, "810")) 667660484Sobrien cpu_variant = (cpu_variant & ~ARM_ANY) 667760484Sobrien | ARM_8 | ARM_ARCH_V4 | ARM_LONGMUL; 667860484Sobrien else 667960484Sobrien goto bad; 668060484Sobrien break; 668160484Sobrien 668260484Sobrien case '9': 668360484Sobrien if (streq (str, "9")) 668460484Sobrien cpu_variant = (cpu_variant & ~ARM_ANY) 668560484Sobrien | ARM_9 | ARM_ARCH_V4 | ARM_LONGMUL | ARM_THUMB; 668660484Sobrien else if (streq (str, "920")) 668760484Sobrien cpu_variant = (cpu_variant & ~ARM_ANY) 668860484Sobrien | ARM_9 | ARM_ARCH_V4 | ARM_LONGMUL; 668960484Sobrien else if (streq (str, "920t")) 669060484Sobrien cpu_variant = (cpu_variant & ~ARM_ANY) 669160484Sobrien | ARM_9 | ARM_ARCH_V4 | ARM_LONGMUL | ARM_THUMB; 669260484Sobrien else if (streq (str, "9tdmi")) 669360484Sobrien cpu_variant = (cpu_variant & ~ARM_ANY) 669460484Sobrien | ARM_9 | ARM_ARCH_V4 | ARM_LONGMUL | ARM_THUMB; 669560484Sobrien else 669660484Sobrien goto bad; 669760484Sobrien break; 669860484Sobrien 669960484Sobrien 670060484Sobrien case 's': 670160484Sobrien if (streq (str, "strongarm") 670260484Sobrien || streq (str, "strongarm110") 670360484Sobrien || streq (str, "strongarm1100")) 670460484Sobrien cpu_variant = (cpu_variant & ~ARM_ANY) 670560484Sobrien | ARM_8 | ARM_ARCH_V4 | ARM_LONGMUL; 670660484Sobrien else 670760484Sobrien goto bad; 670860484Sobrien break; 670960484Sobrien 671060484Sobrien case 'v': 671160484Sobrien /* Select variant based on architecture rather than processor. */ 671260484Sobrien switch (*++str) 671360484Sobrien { 671460484Sobrien case '2': 671560484Sobrien switch (*++str) 671660484Sobrien { 671760484Sobrien case 'a': 671860484Sobrien cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_3; 671960484Sobrien break; 672060484Sobrien case 0: 672160484Sobrien cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_2; 672260484Sobrien break; 672360484Sobrien default: 672460484Sobrien as_bad (_("Invalid architecture variant -m%s"), arg); 672560484Sobrien break; 672660484Sobrien } 672760484Sobrien break; 672860484Sobrien 672960484Sobrien case '3': 673060484Sobrien cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_7; 673160484Sobrien 673260484Sobrien switch (*++str) 673360484Sobrien { 673460484Sobrien case 'm': cpu_variant |= ARM_LONGMUL; break; 673560484Sobrien case 0: break; 673660484Sobrien default: 673760484Sobrien as_bad (_("Invalid architecture variant -m%s"), arg); 673860484Sobrien break; 673960484Sobrien } 674060484Sobrien break; 674160484Sobrien 674260484Sobrien case '4': 674360484Sobrien cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_ARCH_V4; 674460484Sobrien 674560484Sobrien switch (*++str) 674660484Sobrien { 674760484Sobrien case 't': cpu_variant |= ARM_THUMB; break; 674860484Sobrien case 0: break; 674960484Sobrien default: 675060484Sobrien as_bad (_("Invalid architecture variant -m%s"), arg); 675160484Sobrien break; 675260484Sobrien } 675360484Sobrien break; 675460484Sobrien 675560484Sobrien case '5': 675660484Sobrien cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_ARCH_V5; 675760484Sobrien switch (*++str) 675860484Sobrien { 675960484Sobrien case 't': cpu_variant |= ARM_THUMB; break; 676060484Sobrien case 'e': cpu_variant |= ARM_EXT_V5E; break; 676160484Sobrien case 0: break; 676260484Sobrien default: 676360484Sobrien as_bad (_("Invalid architecture variant -m%s"), arg); 676460484Sobrien break; 676560484Sobrien } 676660484Sobrien break; 676760484Sobrien 676860484Sobrien default: 676960484Sobrien as_bad (_("Invalid architecture variant -m%s"), arg); 677060484Sobrien break; 677160484Sobrien } 677260484Sobrien break; 677360484Sobrien 677460484Sobrien default: 677560484Sobrien bad: 677660484Sobrien as_bad (_("Invalid processor variant -m%s"), arg); 677760484Sobrien return 0; 677860484Sobrien } 677960484Sobrien } 678060484Sobrien break; 678160484Sobrien 678260484Sobrien#if defined OBJ_ELF || defined OBJ_COFF 678360484Sobrien case 'k': 678460484Sobrien pic_code = 1; 678560484Sobrien break; 678660484Sobrien#endif 678760484Sobrien 678860484Sobrien default: 678960484Sobrien return 0; 679060484Sobrien } 679160484Sobrien 679260484Sobrien return 1; 679360484Sobrien} 679460484Sobrien 679560484Sobrienvoid 679660484Sobrienmd_show_usage (fp) 679760484Sobrien FILE * fp; 679860484Sobrien{ 679960484Sobrien fprintf (fp, _("\ 680060484Sobrien ARM Specific Assembler Options:\n\ 680160484Sobrien -m[arm][<processor name>] select processor variant\n\ 680260484Sobrien -m[arm]v[2|2a|3|3m|4|4t|5[t][e]] select architecture variant\n\ 680360484Sobrien -mthumb only allow Thumb instructions\n\ 680460484Sobrien -mthumb-interwork mark the assembled code as supporting interworking\n\ 680560484Sobrien -mall allow any instruction\n\ 680660484Sobrien -mfpa10, -mfpa11 select floating point architecture\n\ 680760484Sobrien -mfpe-old don't allow floating-point multiple instructions\n\ 680860484Sobrien -mno-fpu don't allow any floating-point instructions.\n\ 680960484Sobrien -k generate PIC code.\n")); 681060484Sobrien#if defined OBJ_COFF || defined OBJ_ELF 681160484Sobrien fprintf (fp, _("\ 681260484Sobrien -mapcs-32, -mapcs-26 specify which ARM Procedure Calling Standard to use\n\ 681360484Sobrien -mapcs-float floating point args are passed in FP regs\n\ 681460484Sobrien -mapcs-reentrant the code is position independent/reentrant\n")); 681560484Sobrien #endif 681660484Sobrien#ifdef OBJ_ELF 681760484Sobrien fprintf (fp, _("\ 681860484Sobrien -moabi support the old ELF ABI\n")); 681960484Sobrien#endif 682060484Sobrien#ifdef ARM_BI_ENDIAN 682160484Sobrien fprintf (fp, _("\ 682260484Sobrien -EB assemble code for a big endian cpu\n\ 682360484Sobrien -EL assemble code for a little endian cpu\n")); 682460484Sobrien#endif 682560484Sobrien} 682660484Sobrien 682760484Sobrien/* We need to be able to fix up arbitrary expressions in some statements. 682860484Sobrien This is so that we can handle symbols that are an arbitrary distance from 682960484Sobrien the pc. The most common cases are of the form ((+/-sym -/+ . - 8) & mask), 683060484Sobrien which returns part of an address in a form which will be valid for 683160484Sobrien a data instruction. We do this by pushing the expression into a symbol 683260484Sobrien in the expr_section, and creating a fix for that. */ 683360484Sobrien 683460484Sobrienstatic void 683560484Sobrienfix_new_arm (frag, where, size, exp, pc_rel, reloc) 683660484Sobrien fragS * frag; 683760484Sobrien int where; 683860484Sobrien short int size; 683960484Sobrien expressionS * exp; 684060484Sobrien int pc_rel; 684160484Sobrien int reloc; 684260484Sobrien{ 684360484Sobrien fixS * new_fix; 684460484Sobrien arm_fix_data * arm_data; 684560484Sobrien 684660484Sobrien switch (exp->X_op) 684760484Sobrien { 684860484Sobrien case O_constant: 684960484Sobrien case O_symbol: 685060484Sobrien case O_add: 685160484Sobrien case O_subtract: 685260484Sobrien new_fix = fix_new_exp (frag, where, size, exp, pc_rel, reloc); 685360484Sobrien break; 685460484Sobrien 685560484Sobrien default: 685660484Sobrien new_fix = fix_new (frag, where, size, make_expr_symbol (exp), 0, 685760484Sobrien pc_rel, reloc); 685860484Sobrien break; 685960484Sobrien } 686060484Sobrien 686160484Sobrien /* Mark whether the fix is to a THUMB instruction, or an ARM instruction */ 686260484Sobrien arm_data = (arm_fix_data *) obstack_alloc (& notes, sizeof (arm_fix_data)); 686360484Sobrien new_fix->tc_fix_data = (PTR) arm_data; 686460484Sobrien arm_data->thumb_mode = thumb_mode; 686560484Sobrien 686660484Sobrien return; 686760484Sobrien} 686860484Sobrien 686960484Sobrien 687060484Sobrien/* This fix_new is called by cons via TC_CONS_FIX_NEW. */ 687160484Sobrienvoid 687260484Sobriencons_fix_new_arm (frag, where, size, exp) 687360484Sobrien fragS * frag; 687460484Sobrien int where; 687560484Sobrien int size; 687660484Sobrien expressionS * exp; 687760484Sobrien{ 687860484Sobrien bfd_reloc_code_real_type type; 687960484Sobrien int pcrel = 0; 688060484Sobrien 688161843Sobrien /* Pick a reloc. 688261843Sobrien FIXME: @@ Should look at CPU word size. */ 688360484Sobrien switch (size) 688460484Sobrien { 688561843Sobrien case 1: 688661843Sobrien type = BFD_RELOC_8; 688761843Sobrien break; 688860484Sobrien case 2: 688960484Sobrien type = BFD_RELOC_16; 689060484Sobrien break; 689160484Sobrien case 4: 689260484Sobrien default: 689360484Sobrien type = BFD_RELOC_32; 689460484Sobrien break; 689560484Sobrien case 8: 689660484Sobrien type = BFD_RELOC_64; 689760484Sobrien break; 689860484Sobrien } 689960484Sobrien 690060484Sobrien fix_new_exp (frag, where, (int) size, exp, pcrel, type); 690160484Sobrien} 690260484Sobrien 690360484Sobrien/* A good place to do this, although this was probably not intended 690460484Sobrien for this kind of use. We need to dump the literal pool before 690560484Sobrien references are made to a null symbol pointer. */ 690660484Sobrienvoid 690760484Sobrienarm_cleanup () 690860484Sobrien{ 690960484Sobrien if (current_poolP == NULL) 691060484Sobrien return; 691160484Sobrien 691260484Sobrien subseg_set (text_section, 0); /* Put it at the end of text section. */ 691360484Sobrien s_ltorg (0); 691460484Sobrien listing_prev_line (); 691560484Sobrien} 691660484Sobrien 691760484Sobrienvoid 691860484Sobrienarm_start_line_hook () 691960484Sobrien{ 692060484Sobrien last_label_seen = NULL; 692160484Sobrien} 692260484Sobrien 692360484Sobrienvoid 692460484Sobrienarm_frob_label (sym) 692560484Sobrien symbolS * sym; 692660484Sobrien{ 692760484Sobrien last_label_seen = sym; 692860484Sobrien 692960484Sobrien ARM_SET_THUMB (sym, thumb_mode); 693060484Sobrien 693160484Sobrien#if defined OBJ_COFF || defined OBJ_ELF 693260484Sobrien ARM_SET_INTERWORK (sym, support_interwork); 693360484Sobrien#endif 693460484Sobrien 693560484Sobrien if (label_is_thumb_function_name) 693660484Sobrien { 693760484Sobrien /* When the address of a Thumb function is taken the bottom 693860484Sobrien bit of that address should be set. This will allow 693960484Sobrien interworking between Arm and Thumb functions to work 694060484Sobrien correctly. */ 694160484Sobrien 694260484Sobrien THUMB_SET_FUNC (sym, 1); 694360484Sobrien 694460484Sobrien label_is_thumb_function_name = false; 694560484Sobrien } 694660484Sobrien} 694760484Sobrien 694860484Sobrien/* Adjust the symbol table. This marks Thumb symbols as distinct from 694960484Sobrien ARM ones. */ 695060484Sobrien 695160484Sobrienvoid 695260484Sobrienarm_adjust_symtab () 695360484Sobrien{ 695460484Sobrien#ifdef OBJ_COFF 695560484Sobrien symbolS * sym; 695660484Sobrien 695760484Sobrien for (sym = symbol_rootP; sym != NULL; sym = symbol_next (sym)) 695860484Sobrien { 695960484Sobrien if (ARM_IS_THUMB (sym)) 696060484Sobrien { 696160484Sobrien if (THUMB_IS_FUNC (sym)) 696260484Sobrien { 696360484Sobrien /* Mark the symbol as a Thumb function. */ 696460484Sobrien if ( S_GET_STORAGE_CLASS (sym) == C_STAT 696560484Sobrien || S_GET_STORAGE_CLASS (sym) == C_LABEL) /* This can happen! */ 696660484Sobrien S_SET_STORAGE_CLASS (sym, C_THUMBSTATFUNC); 696760484Sobrien 696860484Sobrien else if (S_GET_STORAGE_CLASS (sym) == C_EXT) 696960484Sobrien S_SET_STORAGE_CLASS (sym, C_THUMBEXTFUNC); 697060484Sobrien else 697160484Sobrien as_bad (_("%s: unexpected function type: %d"), 697260484Sobrien S_GET_NAME (sym), S_GET_STORAGE_CLASS (sym)); 697360484Sobrien } 697460484Sobrien else switch (S_GET_STORAGE_CLASS (sym)) 697560484Sobrien { 697660484Sobrien case C_EXT: 697760484Sobrien S_SET_STORAGE_CLASS (sym, C_THUMBEXT); 697860484Sobrien break; 697960484Sobrien case C_STAT: 698060484Sobrien S_SET_STORAGE_CLASS (sym, C_THUMBSTAT); 698160484Sobrien break; 698260484Sobrien case C_LABEL: 698360484Sobrien S_SET_STORAGE_CLASS (sym, C_THUMBLABEL); 698460484Sobrien break; 698560484Sobrien default: /* do nothing */ 698660484Sobrien break; 698760484Sobrien } 698860484Sobrien } 698960484Sobrien 699060484Sobrien if (ARM_IS_INTERWORK (sym)) 699160484Sobrien coffsymbol (symbol_get_bfdsym (sym))->native->u.syment.n_flags = 0xFF; 699260484Sobrien } 699360484Sobrien#endif 699460484Sobrien#ifdef OBJ_ELF 699560484Sobrien symbolS * sym; 699660484Sobrien char bind; 699760484Sobrien 699860484Sobrien for (sym = symbol_rootP; sym != NULL; sym = symbol_next (sym)) 699960484Sobrien { 700060484Sobrien if (ARM_IS_THUMB (sym)) 700160484Sobrien { 700260484Sobrien elf_symbol_type * elf_sym; 700360484Sobrien 700460484Sobrien elf_sym = elf_symbol (symbol_get_bfdsym (sym)); 700560484Sobrien bind = ELF_ST_BIND (elf_sym); 700660484Sobrien 700760484Sobrien /* If it's a .thumb_func, declare it as so, 700860484Sobrien otherwise tag label as .code 16. */ 700960484Sobrien if (THUMB_IS_FUNC (sym)) 701060484Sobrien elf_sym->internal_elf_sym.st_info = 701160484Sobrien ELF_ST_INFO (bind, STT_ARM_TFUNC); 701260484Sobrien else 701360484Sobrien elf_sym->internal_elf_sym.st_info = 701460484Sobrien ELF_ST_INFO (bind, STT_ARM_16BIT); 701560484Sobrien } 701660484Sobrien } 701760484Sobrien#endif 701860484Sobrien} 701960484Sobrien 702060484Sobrienint 702160484Sobrienarm_data_in_code () 702260484Sobrien{ 702360484Sobrien if (thumb_mode && ! strncmp (input_line_pointer + 1, "data:", 5)) 702460484Sobrien { 702560484Sobrien *input_line_pointer = '/'; 702660484Sobrien input_line_pointer += 5; 702760484Sobrien *input_line_pointer = 0; 702860484Sobrien return 1; 702960484Sobrien } 703060484Sobrien 703160484Sobrien return 0; 703260484Sobrien} 703360484Sobrien 703460484Sobrienchar * 703560484Sobrienarm_canonicalize_symbol_name (name) 703660484Sobrien char * name; 703760484Sobrien{ 703860484Sobrien int len; 703960484Sobrien 704060484Sobrien if (thumb_mode && (len = strlen (name)) > 5 704160484Sobrien && streq (name + len - 5, "/data")) 704260484Sobrien *(name + len - 5) = 0; 704360484Sobrien 704460484Sobrien return name; 704560484Sobrien} 704660484Sobrien 704760484Sobrienboolean 704860484Sobrienarm_validate_fix (fixP) 704960484Sobrien fixS * fixP; 705060484Sobrien{ 705160484Sobrien /* If the destination of the branch is a defined symbol which does not have 705260484Sobrien the THUMB_FUNC attribute, then we must be calling a function which has 705360484Sobrien the (interfacearm) attribute. We look for the Thumb entry point to that 705460484Sobrien function and change the branch to refer to that function instead. */ 705560484Sobrien if ( fixP->fx_r_type == BFD_RELOC_THUMB_PCREL_BRANCH23 705660484Sobrien && fixP->fx_addsy != NULL 705760484Sobrien && S_IS_DEFINED (fixP->fx_addsy) 705860484Sobrien && ! THUMB_IS_FUNC (fixP->fx_addsy)) 705960484Sobrien { 706060484Sobrien fixP->fx_addsy = find_real_start (fixP->fx_addsy); 706160484Sobrien return true; 706260484Sobrien } 706360484Sobrien 706460484Sobrien return false; 706560484Sobrien} 706660484Sobrien 706760484Sobrien#ifdef OBJ_ELF 706860484Sobrien/* Relocations against Thumb function names must be left unadjusted, 706960484Sobrien so that the linker can use this information to correctly set the 707060484Sobrien bottom bit of their addresses. The MIPS version of this function 707160484Sobrien also prevents relocations that are mips-16 specific, but I do not 707260484Sobrien know why it does this. 707360484Sobrien 707460484Sobrien FIXME: 707560484Sobrien There is one other problem that ought to be addressed here, but 707660484Sobrien which currently is not: Taking the address of a label (rather 707760484Sobrien than a function) and then later jumping to that address. Such 707860484Sobrien addresses also ought to have their bottom bit set (assuming that 707960484Sobrien they reside in Thumb code), but at the moment they will not. */ 708060484Sobrien 708160484Sobrienboolean 708260484Sobrienarm_fix_adjustable (fixP) 708360484Sobrien fixS * fixP; 708460484Sobrien{ 708560484Sobrien if (fixP->fx_addsy == NULL) 708660484Sobrien return 1; 708760484Sobrien 708860484Sobrien /* Prevent all adjustments to global symbols. */ 708960484Sobrien if (S_IS_EXTERN (fixP->fx_addsy)) 709060484Sobrien return 0; 709160484Sobrien 709260484Sobrien if (S_IS_WEAK (fixP->fx_addsy)) 709360484Sobrien return 0; 709460484Sobrien 709560484Sobrien if (THUMB_IS_FUNC (fixP->fx_addsy) 709660484Sobrien && fixP->fx_subsy == NULL) 709760484Sobrien return 0; 709860484Sobrien 709960484Sobrien /* We need the symbol name for the VTABLE entries */ 710060484Sobrien if ( fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT 710160484Sobrien || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY) 710260484Sobrien return 0; 710360484Sobrien 710460484Sobrien return 1; 710560484Sobrien} 710660484Sobrien 710760484Sobrienconst char * 710860484Sobrienelf32_arm_target_format () 710960484Sobrien{ 711060484Sobrien if (target_big_endian) 711160484Sobrien if (target_oabi) 711260484Sobrien return "elf32-bigarm-oabi"; 711360484Sobrien else 711460484Sobrien return "elf32-bigarm"; 711560484Sobrien else 711660484Sobrien if (target_oabi) 711760484Sobrien return "elf32-littlearm-oabi"; 711860484Sobrien else 711960484Sobrien return "elf32-littlearm"; 712060484Sobrien} 712160484Sobrien 712260484Sobrienvoid 712360484Sobrienarmelf_frob_symbol (symp, puntp) 712460484Sobrien symbolS * symp; 712560484Sobrien int * puntp; 712660484Sobrien{ 712760484Sobrien elf_frob_symbol (symp, puntp); 712860484Sobrien} 712960484Sobrien 713060484Sobrienint 713160484Sobrienarm_force_relocation (fixp) 713260484Sobrien struct fix * fixp; 713360484Sobrien{ 713460484Sobrien if ( fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT 713560484Sobrien || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY 713660484Sobrien || fixp->fx_r_type == BFD_RELOC_ARM_PCREL_BRANCH 713760484Sobrien || fixp->fx_r_type == BFD_RELOC_THUMB_PCREL_BRANCH23) 713860484Sobrien return 1; 713960484Sobrien 714060484Sobrien return 0; 714160484Sobrien} 714260484Sobrien 714360484Sobrienstatic bfd_reloc_code_real_type 714460484Sobrienarm_parse_reloc () 714560484Sobrien{ 714660484Sobrien char id[16]; 714760484Sobrien char * ip; 714860484Sobrien unsigned int i; 714960484Sobrien static struct 715060484Sobrien { 715160484Sobrien char * str; 715260484Sobrien int len; 715360484Sobrien bfd_reloc_code_real_type reloc; 715460484Sobrien } 715560484Sobrien reloc_map[] = 715660484Sobrien { 715760484Sobrien#define MAP(str,reloc) { str, sizeof (str)-1, reloc } 715860484Sobrien MAP ("(got)", BFD_RELOC_ARM_GOT32), 715960484Sobrien MAP ("(gotoff)", BFD_RELOC_ARM_GOTOFF), 716060484Sobrien /* ScottB: Jan 30, 1998 */ 716160484Sobrien /* Added support for parsing "var(PLT)" branch instructions */ 716260484Sobrien /* generated by GCC for PLT relocs */ 716360484Sobrien MAP ("(plt)", BFD_RELOC_ARM_PLT32), 716460484Sobrien { NULL, 0, BFD_RELOC_UNUSED } 716560484Sobrien#undef MAP 716660484Sobrien }; 716760484Sobrien 716860484Sobrien for (i = 0, ip = input_line_pointer; 716960484Sobrien i < sizeof (id) && (isalnum (*ip) || ispunct (*ip)); 717060484Sobrien i++, ip++) 717160484Sobrien id[i] = tolower (*ip); 717260484Sobrien 717360484Sobrien for (i = 0; reloc_map[i].str; i++) 717460484Sobrien if (strncmp (id, reloc_map[i].str, reloc_map[i].len) == 0) 717560484Sobrien break; 717660484Sobrien 717760484Sobrien input_line_pointer += reloc_map[i].len; 717860484Sobrien 717960484Sobrien return reloc_map[i].reloc; 718060484Sobrien} 718160484Sobrien 718260484Sobrienstatic void 718360484Sobriens_arm_elf_cons (nbytes) 718460484Sobrien int nbytes; 718560484Sobrien{ 718660484Sobrien expressionS exp; 718760484Sobrien 718860484Sobrien#ifdef md_flush_pending_output 718960484Sobrien md_flush_pending_output (); 719060484Sobrien#endif 719160484Sobrien 719260484Sobrien if (is_it_end_of_statement ()) 719360484Sobrien { 719460484Sobrien demand_empty_rest_of_line (); 719560484Sobrien return; 719660484Sobrien } 719760484Sobrien 719860484Sobrien#ifdef md_cons_align 719960484Sobrien md_cons_align (nbytes); 720060484Sobrien#endif 720160484Sobrien 720260484Sobrien do 720360484Sobrien { 720460484Sobrien bfd_reloc_code_real_type reloc; 720560484Sobrien 720660484Sobrien expression (& exp); 720760484Sobrien 720860484Sobrien if (exp.X_op == O_symbol 720960484Sobrien && * input_line_pointer == '(' 721060484Sobrien && (reloc = arm_parse_reloc()) != BFD_RELOC_UNUSED) 721160484Sobrien { 721260484Sobrien reloc_howto_type * howto = bfd_reloc_type_lookup (stdoutput, reloc); 721360484Sobrien int size = bfd_get_reloc_size (howto); 721460484Sobrien 721560484Sobrien if (size > nbytes) 721660484Sobrien as_bad ("%s relocations do not fit in %d bytes", 721760484Sobrien howto->name, nbytes); 721860484Sobrien else 721960484Sobrien { 722060484Sobrien register char * p = frag_more ((int) nbytes); 722160484Sobrien int offset = nbytes - size; 722260484Sobrien 722360484Sobrien fix_new_exp (frag_now, p - frag_now->fr_literal + offset, size, 722460484Sobrien & exp, 0, reloc); 722560484Sobrien } 722660484Sobrien } 722760484Sobrien else 722860484Sobrien emit_expr (& exp, (unsigned int) nbytes); 722960484Sobrien } 723060484Sobrien while (*input_line_pointer++ == ','); 723160484Sobrien 723260484Sobrien input_line_pointer--; /* Put terminator back into stream. */ 723360484Sobrien demand_empty_rest_of_line (); 723460484Sobrien} 723560484Sobrien 723660484Sobrien#endif /* OBJ_ELF */ 7237