blob: 8fc32f463ab83497a7eac7d169778828e4166587 [file] [log] [blame]
sewardjc2c87162004-11-25 13:07:02 +00001
2/*--------------------------------------------------------------------*/
3/*--- ---*/
4/*--- This file (guest-arm/toIR.c) is ---*/
5/*--- Copyright (c) 2004 OpenWorks LLP. All rights reserved. ---*/
6/*--- ---*/
7/*--------------------------------------------------------------------*/
8
9/*
10 This file is part of LibVEX, a library for dynamic binary
11 instrumentation and translation.
12
13 Copyright (C) 2004 OpenWorks, LLP.
14
15 This program is free software; you can redistribute it and/or modify
16 it under the terms of the GNU General Public License as published by
17 the Free Software Foundation; Version 2 dated June 1991 of the
18 license.
19
20 This program is distributed in the hope that it will be useful,
21 but WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or liability
23 for damages. See the GNU General Public License for more details.
24
25 Neither the names of the U.S. Department of Energy nor the
26 University of California nor the names of its contributors may be
27 used to endorse or promote products derived from this software
28 without prior written permission.
29
30 You should have received a copy of the GNU General Public License
31 along with this program; if not, write to the Free Software
32 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
33 USA.
34*/
35
36/* Translates ARM(v4) code to IR. */
37
38#include "libvex_basictypes.h"
39#include "libvex_ir.h"
40#include "libvex.h"
41#include "libvex_guest_arm.h"
42
43#include "main/vex_util.h"
44#include "main/vex_globals.h"
45#include "guest-arm/gdefs.h"
46
47
48/*------------------------------------------------------------*/
49/*--- Globals ---*/
50/*------------------------------------------------------------*/
51
52/* These are set at the start of the translation of a BB, so that we
53 don't have to pass them around endlessly. CONST means does not
54 change during translation of a bb.
55*/
56
57/* We need to know this to do sub-register accesses correctly. */
58/* CONST */
59static Bool host_is_bigendian;
60
61/* Pointer to the guest code area. */
62/* CONST */
63static UChar* guest_code;
64
65/* The guest address corresponding to guest_code[0]. */
66/* CONST */
67static Addr32 guest_pc_bbstart;
68
69/* The IRBB* into which we're generating code. */
70static IRBB* irbb;
71
72
73/*------------------------------------------------------------*/
74/*--- Debugging output ---*/
75/*------------------------------------------------------------*/
76
77#define DIP(format, args...) \
78 if (vex_traceflags & VEX_TRACE_FE) \
79 vex_printf(format, ## args)
80
81#define DIS(buf, format, args...) \
82 if (vex_traceflags & VEX_TRACE_FE) \
83 vex_sprintf(buf, format, ## args)
84
85
ceriona70a37b2004-12-03 18:54:08 +000086
87
sewardjc2c87162004-11-25 13:07:02 +000088/*------------------------------------------------------------*/
89/*--- Offsets of various parts of the arm guest state. ---*/
90/*------------------------------------------------------------*/
91
92#define OFFB_R0 offsetof(VexGuestARMState,guest_R0)
cerionc60c01e2004-12-02 20:19:22 +000093#define OFFB_R1 offsetof(VexGuestARMState,guest_R1)
94#define OFFB_R2 offsetof(VexGuestARMState,guest_R2)
95#define OFFB_R3 offsetof(VexGuestARMState,guest_R3)
96#define OFFB_R4 offsetof(VexGuestARMState,guest_R4)
97#define OFFB_R5 offsetof(VexGuestARMState,guest_R5)
98#define OFFB_R6 offsetof(VexGuestARMState,guest_R6)
99#define OFFB_R7 offsetof(VexGuestARMState,guest_R7)
100#define OFFB_R8 offsetof(VexGuestARMState,guest_R8)
101#define OFFB_R9 offsetof(VexGuestARMState,guest_R9)
102#define OFFB_R10 offsetof(VexGuestARMState,guest_R10)
103#define OFFB_R11 offsetof(VexGuestARMState,guest_R11)
104#define OFFB_R12 offsetof(VexGuestARMState,guest_R12)
105#define OFFB_R13 offsetof(VexGuestARMState,guest_R13)
106#define OFFB_R14 offsetof(VexGuestARMState,guest_R14)
sewardjc2c87162004-11-25 13:07:02 +0000107#define OFFB_R15 offsetof(VexGuestARMState,guest_R15)
108
cerionc60c01e2004-12-02 20:19:22 +0000109// CAB: ? guest_SYSCALLNO;
110
111#define OFFB_CC_OP offsetof(VexGuestARMState,guest_CC_OP)
112#define OFFB_CC_DEP1 offsetof(VexGuestARMState,guest_CC_DEP1)
113#define OFFB_CC_DEP2 offsetof(VexGuestARMState,guest_CC_DEP2)
114
115// CAB: ? guest_EMWARN;
116
sewardjc2c87162004-11-25 13:07:02 +0000117
118/*------------------------------------------------------------*/
119/*--- Disassemble an entire basic block ---*/
120/*------------------------------------------------------------*/
121
122/* The results of disassembling an instruction. There are three
123 possible outcomes. For Dis_Resteer, the disassembler _must_
124 continue at the specified address. For Dis_StopHere, the
125 disassembler _must_ terminate the BB. For Dis_Continue, we may at
126 our option either disassemble the next insn, or terminate the BB;
127 but in the latter case we must set the bb's ->next field to point
128 to the next instruction. */
129
130typedef
131 enum {
132 Dis_StopHere, /* this insn terminates the BB; we must stop. */
133 Dis_Continue, /* we can optionally continue into the next insn */
134 Dis_Resteer /* followed a branch; continue at the spec'd addr */
135 }
136 DisResult;
137
138
139/* forward decls .. */
140static IRExpr* mkU32 ( UInt i );
141static void stmt ( IRStmt* st );
142
143
144/* disInstr disassembles an instruction located at &guest_code[delta],
145 and sets *size to its size. If the returned value is Dis_Resteer,
146 the next guest address is assigned to *whereNext. disInstr is not
147 permitted to return Dis_Resteer if either (1) resteerOK is False,
148 or (2) resteerOkFn, when applied to the address which it wishes to
149 resteer into, returns False. */
150
151static DisResult disInstr ( /*IN*/ Bool resteerOK,
152 /*IN*/ Bool (*resteerOkFn) ( Addr64 ),
153 /*IN*/ UInt delta,
154 /*OUT*/ UInt* size,
155 /*OUT*/ Addr64* whereNext );
156
157
158/* This is the main (only, in fact) entry point for this module. */
159
160/* Disassemble a complete basic block, starting at guest_pc_start, and
161 dumping the IR into global irbb. Returns the size, in bytes, of
162 the basic block.
163*/
164IRBB* bbToIR_ARM ( UChar* armCode,
165 Addr64 guest_pc_start,
166 Int* guest_bytes_read,
167 Bool (*byte_accessible)(Addr64),
168 Bool (*chase_into_ok)(Addr64),
169 Bool host_bigendian )
170{
171 UInt delta;
172 Int i, n_instrs, size, first_stmt_idx;
173 Addr64 guest_next;
174 Bool resteerOK;
175 DisResult dres;
176 static Int n_resteers = 0;
177 Int d_resteers = 0;
178
179 /* check sanity .. */
180 vassert(vex_control.guest_max_insns >= 1);
181 vassert(vex_control.guest_max_insns < 1000);
182 vassert(vex_control.guest_chase_thresh >= 0);
183 vassert(vex_control.guest_chase_thresh < vex_control.guest_max_insns);
184
185 /* Set up globals. */
186 host_is_bigendian = host_bigendian;
187 guest_code = armCode;
188 guest_pc_bbstart = (Addr32)guest_pc_start;
189 irbb = emptyIRBB();
190
191 vassert((guest_pc_start >> 32) == 0);
192
193 /* Delta keeps track of how far along the x86code array we
194 have so far gone. */
195 delta = 0;
196 n_instrs = 0;
197 *guest_bytes_read = 0;
198
199 while (True) {
200 vassert(n_instrs < vex_control.guest_max_insns);
201
202 guest_next = 0;
203 resteerOK = n_instrs < vex_control.guest_chase_thresh;
204 first_stmt_idx = irbb->stmts_used;
205
206 if (n_instrs > 0) {
207 /* for the first insn, the dispatch loop will have set
208 R15, but for all the others we have to do it ourselves. */
209 stmt( IRStmt_Put( OFFB_R15, mkU32(guest_pc_bbstart + delta)) );
210 }
211
212 dres = disInstr( resteerOK, chase_into_ok,
213 delta, &size, &guest_next );
214
215 /* Print the resulting IR, if needed. */
216 if (vex_traceflags & VEX_TRACE_FE) {
217 for (i = first_stmt_idx; i < irbb->stmts_used; i++) {
218 vex_printf(" ");
219 ppIRStmt(irbb->stmts[i]);
220 vex_printf("\n");
221 }
222 }
223
224 if (dres == Dis_StopHere) {
225 vassert(irbb->next != NULL);
226 if (vex_traceflags & VEX_TRACE_FE) {
227 vex_printf(" ");
228 vex_printf( "goto {");
229 ppIRJumpKind(irbb->jumpkind);
230 vex_printf( "} ");
231 ppIRExpr( irbb->next );
232 vex_printf( "\n");
233 }
234 }
235
236 delta += size;
237 *guest_bytes_read += size;
238 n_instrs++;
239 DIP("\n");
240
241 vassert(size > 0 && size <= 18);
242 if (!resteerOK)
243 vassert(dres != Dis_Resteer);
244 if (dres != Dis_Resteer)
245 vassert(guest_next == 0);
246
247 switch (dres) {
248 case Dis_Continue:
249 vassert(irbb->next == NULL);
250 if (n_instrs < vex_control.guest_max_insns) {
251 /* keep going */
252 } else {
253 irbb->next = mkU32(((Addr32)guest_pc_start)+delta);
254 return irbb;
255 }
256 break;
257 case Dis_StopHere:
258 vassert(irbb->next != NULL);
259 return irbb;
260 case Dis_Resteer:
261 vassert(irbb->next == NULL);
262 /* figure out a new delta to continue at. */
263 vassert(chase_into_ok(guest_next));
264 delta = (UInt)(guest_next - guest_pc_start);
265 n_resteers++;
266 d_resteers++;
267 if (0 && (n_resteers & 0xFF) == 0)
268 vex_printf("resteer[%d,%d] to %p (delta = %d)\n",
269 n_resteers, d_resteers,
270 (void*)(UInt)(guest_next), delta);
271 break;
272 }
273 }
274}
275
276
277/*------------------------------------------------------------*/
278/*--- Helper bits and pieces for deconstructing the ---*/
279/*--- x86 insn stream. ---*/
280/*------------------------------------------------------------*/
281
282/* Add a statement to the list held by "irbb". */
283static void stmt ( IRStmt* st )
284{
285 addStmtToIRBB( irbb, st );
286}
287
288/* Generate a new temporary of the given type. */
289static IRTemp newTemp ( IRType ty )
290{
291 vassert(isPlausibleType(ty));
292 return newIRTemp( irbb->tyenv, ty );
293}
294
sewardjfa89fdc2004-12-15 12:21:28 +0000295#if 0
sewardjc2c87162004-11-25 13:07:02 +0000296/* Bomb out if we can't handle something. */
297__attribute__ ((noreturn))
298static void unimplemented ( Char* str )
299{
300 vex_printf("armToIR: unimplemented feature\n");
301 vpanic(str);
302}
sewardjfa89fdc2004-12-15 12:21:28 +0000303#endif
sewardjc2c87162004-11-25 13:07:02 +0000304
305/* Various simple conversions */
306
sewardjfa89fdc2004-12-15 12:21:28 +0000307#if 0
sewardjc2c87162004-11-25 13:07:02 +0000308static UInt extend_s_8to32( UInt x )
309{
310 return (UInt)((((Int)x) << 24) >> 24);
311}
312
313static UInt extend_s_16to32 ( UInt x )
314{
315 return (UInt)((((Int)x) << 16) >> 16);
316}
sewardjfa89fdc2004-12-15 12:21:28 +0000317#endif
sewardjc2c87162004-11-25 13:07:02 +0000318
cerion19e8a612004-12-10 10:18:58 +0000319static UInt extend_s_24to32 ( UInt x )
320{
321 return (UInt)((((Int)x) << 8) >> 8);
322}
323
sewardjfa89fdc2004-12-15 12:21:28 +0000324#if 0
sewardjc2c87162004-11-25 13:07:02 +0000325/* Fetch a byte from the guest insn stream. */
326static UChar getIByte ( UInt delta )
327{
328 return guest_code[delta];
329}
sewardjfa89fdc2004-12-15 12:21:28 +0000330#endif
sewardjc2c87162004-11-25 13:07:02 +0000331
332/* Get a 8/16/32-bit unsigned value out of the insn stream. */
333
sewardjfa89fdc2004-12-15 12:21:28 +0000334#if 0
sewardjc2c87162004-11-25 13:07:02 +0000335static UInt getUChar ( UInt delta )
336{
337 UInt v = guest_code[delta+0];
338 return v & 0xFF;
339}
sewardjfa89fdc2004-12-15 12:21:28 +0000340#endif
sewardjc2c87162004-11-25 13:07:02 +0000341
sewardjfa89fdc2004-12-15 12:21:28 +0000342#if 0
sewardjc2c87162004-11-25 13:07:02 +0000343static UInt getUDisp16 ( UInt delta )
344{
345 UInt v = guest_code[delta+1]; v <<= 8;
346 v |= guest_code[delta+0];
347 return v & 0xFFFF;
348}
sewardjfa89fdc2004-12-15 12:21:28 +0000349#endif
sewardjc2c87162004-11-25 13:07:02 +0000350
sewardjfa89fdc2004-12-15 12:21:28 +0000351#if 0
sewardjc2c87162004-11-25 13:07:02 +0000352static UInt getUDisp32 ( UInt delta )
353{
354 UInt v = guest_code[delta+3]; v <<= 8;
355 v |= guest_code[delta+2]; v <<= 8;
356 v |= guest_code[delta+1]; v <<= 8;
357 v |= guest_code[delta+0];
358 return v;
359}
sewardjfa89fdc2004-12-15 12:21:28 +0000360#endif
sewardjc2c87162004-11-25 13:07:02 +0000361
sewardjfa89fdc2004-12-15 12:21:28 +0000362#if 0
sewardjc2c87162004-11-25 13:07:02 +0000363static UInt getUDisp ( Int size, UInt delta )
364{
365 switch (size) {
366 case 4: return getUDisp32(delta);
367 case 2: return getUDisp16(delta);
368 case 1: return getUChar(delta);
cerionc60c01e2004-12-02 20:19:22 +0000369 default: vpanic("getUDisp(ARM)");
sewardjc2c87162004-11-25 13:07:02 +0000370 }
371 return 0; /*notreached*/
372}
sewardjfa89fdc2004-12-15 12:21:28 +0000373#endif
sewardjc2c87162004-11-25 13:07:02 +0000374
sewardjfa89fdc2004-12-15 12:21:28 +0000375#if 0
sewardjc2c87162004-11-25 13:07:02 +0000376/* Get a byte value out of the insn stream and sign-extend to 32
377 bits. */
378static UInt getSDisp8 ( UInt delta )
379{
380 return extend_s_8to32( (UInt) (guest_code[delta]) );
381}
sewardjfa89fdc2004-12-15 12:21:28 +0000382#endif
sewardjc2c87162004-11-25 13:07:02 +0000383
sewardjfa89fdc2004-12-15 12:21:28 +0000384#if 0
sewardjc2c87162004-11-25 13:07:02 +0000385static UInt getSDisp16 ( UInt delta0 )
386{
387 UChar* eip = (UChar*)(&guest_code[delta0]);
388 UInt d = *eip++;
389 d |= ((*eip++) << 8);
390 return extend_s_16to32(d);
391}
sewardjfa89fdc2004-12-15 12:21:28 +0000392#endif
sewardjc2c87162004-11-25 13:07:02 +0000393
sewardjfa89fdc2004-12-15 12:21:28 +0000394#if 0
sewardjc2c87162004-11-25 13:07:02 +0000395static UInt getSDisp ( Int size, UInt delta )
396{
397 switch (size) {
398 case 4: return getUDisp32(delta);
399 case 2: return getSDisp16(delta);
400 case 1: return getSDisp8(delta);
cerionc60c01e2004-12-02 20:19:22 +0000401 default: vpanic("getSDisp(ARM)");
sewardjc2c87162004-11-25 13:07:02 +0000402 }
403 return 0; /*notreached*/
404}
sewardjfa89fdc2004-12-15 12:21:28 +0000405#endif
sewardjc2c87162004-11-25 13:07:02 +0000406
407
408/*------------------------------------------------------------*/
409/*--- Helpers for constructing IR. ---*/
410/*------------------------------------------------------------*/
411
412/* Create a 1/2/4 byte read of an x86 integer registers. For 16/8 bit
413 register references, we need to take the host endianness into
414 account. Supplied value is 0 .. 7 and in the Intel instruction
415 encoding. */
416
sewardjfa89fdc2004-12-15 12:21:28 +0000417#if 0
sewardjc2c87162004-11-25 13:07:02 +0000418static IRType szToITy ( Int n )
419{
420 switch (n) {
421 case 1: return Ity_I8;
422 case 2: return Ity_I16;
423 case 4: return Ity_I32;
cerionc60c01e2004-12-02 20:19:22 +0000424 default: vpanic("szToITy(ARM)");
sewardjc2c87162004-11-25 13:07:02 +0000425 }
426}
sewardjfa89fdc2004-12-15 12:21:28 +0000427#endif
sewardjc2c87162004-11-25 13:07:02 +0000428
429static Int integerGuestRegOffset ( UInt archreg )
430{
431 vassert(archreg < 16);
432
433 vassert(!host_is_bigendian); //TODO: is this necessary?
sewardjcca71942004-12-02 23:35:18 +0000434 // jrs: probably not; only matters if we reference sub-parts
435 // of the arm registers, but that isn't the case
sewardjc2c87162004-11-25 13:07:02 +0000436 switch (archreg) {
cerionc60c01e2004-12-02 20:19:22 +0000437 case 0: return offsetof(VexGuestARMState, guest_R0);
438 case 1: return offsetof(VexGuestARMState, guest_R1);
439 case 2: return offsetof(VexGuestARMState, guest_R2);
440 case 3: return offsetof(VexGuestARMState, guest_R3);
441 case 4: return offsetof(VexGuestARMState, guest_R4);
442 case 5: return offsetof(VexGuestARMState, guest_R5);
443 case 6: return offsetof(VexGuestARMState, guest_R6);
444 case 7: return offsetof(VexGuestARMState, guest_R7);
445 case 8: return offsetof(VexGuestARMState, guest_R8);
446 case 9: return offsetof(VexGuestARMState, guest_R9);
447 case 10: return offsetof(VexGuestARMState,guest_R10);
448 case 11: return offsetof(VexGuestARMState,guest_R11);
449 case 12: return offsetof(VexGuestARMState,guest_R12);
450 case 13: return offsetof(VexGuestARMState,guest_R13);
451 case 14: return offsetof(VexGuestARMState,guest_R14);
sewardjc2c87162004-11-25 13:07:02 +0000452 case 15: return offsetof(VexGuestARMState,guest_R15);
453 }
454
cerionc60c01e2004-12-02 20:19:22 +0000455 vpanic("integerGuestRegOffset(arm,le)"); /*notreached*/
sewardjc2c87162004-11-25 13:07:02 +0000456}
457
458static IRExpr* getIReg ( UInt archreg )
459{
460 vassert(archreg < 16);
461 return IRExpr_Get( integerGuestRegOffset(archreg), Ity_I32 );
462}
463
464/* Ditto, but write to a reg instead. */
465static void putIReg ( UInt archreg, IRExpr* e )
466{
467 vassert(archreg < 16);
468 stmt( IRStmt_Put(integerGuestRegOffset(archreg), e) );
469}
470
471static void assign ( IRTemp dst, IRExpr* e )
472{
473 stmt( IRStmt_Tmp(dst, e) );
474}
475
476static void storeLE ( IRExpr* addr, IRExpr* data )
477{
478 stmt( IRStmt_STle(addr,data) );
479}
480
481static IRExpr* unop ( IROp op, IRExpr* a )
482{
483 return IRExpr_Unop(op, a);
484}
485
486static IRExpr* binop ( IROp op, IRExpr* a1, IRExpr* a2 )
487{
488 return IRExpr_Binop(op, a1, a2);
489}
490
491static IRExpr* mkexpr ( IRTemp tmp )
492{
493 return IRExpr_Tmp(tmp);
494}
495
496static IRExpr* mkU8 ( UInt i )
497{
498 vassert(i < 256);
499 return IRExpr_Const(IRConst_U8(i));
500}
501
sewardjfa89fdc2004-12-15 12:21:28 +0000502#if 0
sewardjc2c87162004-11-25 13:07:02 +0000503static IRExpr* mkU16 ( UInt i )
504{
505 vassert(i < 65536);
506 return IRExpr_Const(IRConst_U16(i));
507}
sewardjfa89fdc2004-12-15 12:21:28 +0000508#endif
sewardjc2c87162004-11-25 13:07:02 +0000509
510static IRExpr* mkU32 ( UInt i )
511{
512 return IRExpr_Const(IRConst_U32(i));
513}
514
sewardjfa89fdc2004-12-15 12:21:28 +0000515#if 0
sewardjc2c87162004-11-25 13:07:02 +0000516static IRExpr* mkU ( IRType ty, UInt i )
517{
518 if (ty == Ity_I8) return mkU8(i);
519 if (ty == Ity_I16) return mkU16(i);
520 if (ty == Ity_I32) return mkU32(i);
521 /* If this panics, it usually means you passed a size (1,2,4)
522 value as the IRType, rather than a real IRType. */
cerionc60c01e2004-12-02 20:19:22 +0000523 vpanic("mkU(ARM)");
sewardjc2c87162004-11-25 13:07:02 +0000524}
sewardjfa89fdc2004-12-15 12:21:28 +0000525#endif
sewardjc2c87162004-11-25 13:07:02 +0000526
527static IRExpr* loadLE ( IRType ty, IRExpr* data )
528{
529 return IRExpr_LDle(ty,data);
530}
531
sewardjfa89fdc2004-12-15 12:21:28 +0000532#if 0
sewardjc2c87162004-11-25 13:07:02 +0000533static IROp mkSizedOp ( IRType ty, IROp op8 )
534{
535 Int adj;
536 vassert(ty == Ity_I8 || ty == Ity_I16 || ty == Ity_I32);
537 vassert(op8 == Iop_Add8 || op8 == Iop_Sub8
538 || op8 == Iop_Mul8
539 || op8 == Iop_Or8 || op8 == Iop_And8 || op8 == Iop_Xor8
540 || op8 == Iop_Shl8 || op8 == Iop_Shr8 || op8 == Iop_Sar8
541 || op8 == Iop_CmpEQ8 || op8 == Iop_CmpNE8
542 || op8 == Iop_Not8 );
543 adj = ty==Ity_I8 ? 0 : (ty==Ity_I16 ? 1 : 2);
544 return adj + op8;
545}
sewardjfa89fdc2004-12-15 12:21:28 +0000546#endif
sewardjc2c87162004-11-25 13:07:02 +0000547
sewardjfa89fdc2004-12-15 12:21:28 +0000548#if 0
sewardjc2c87162004-11-25 13:07:02 +0000549static IROp mkWidenOp ( Int szSmall, Int szBig, Bool signd )
550{
551 if (szSmall == 1 && szBig == 4) {
552 return signd ? Iop_8Sto32 : Iop_8Uto32;
553 }
554 if (szSmall == 1 && szBig == 2) {
555 return signd ? Iop_8Sto16 : Iop_8Uto16;
556 }
557 if (szSmall == 2 && szBig == 4) {
558 return signd ? Iop_16Sto32 : Iop_16Uto32;
559 }
cerionc60c01e2004-12-02 20:19:22 +0000560 vpanic("mkWidenOp(ARM,guest)");
sewardjc2c87162004-11-25 13:07:02 +0000561}
sewardjfa89fdc2004-12-15 12:21:28 +0000562#endif
sewardjc2c87162004-11-25 13:07:02 +0000563
cerionc60c01e2004-12-02 20:19:22 +0000564
565
566
567
568
569
570
571
572
573
574
575
576
sewardjc2c87162004-11-25 13:07:02 +0000577/*------------------------------------------------------------*/
cerionf7da63d2004-12-09 19:04:57 +0000578/*--- Helpers for %flags. ---*/
sewardjc2c87162004-11-25 13:07:02 +0000579/*------------------------------------------------------------*/
cerionc60c01e2004-12-02 20:19:22 +0000580
sewardjc2c87162004-11-25 13:07:02 +0000581/* -------------- Evaluating the flags-thunk. -------------- */
582
sewardjfa89fdc2004-12-15 12:21:28 +0000583#if 0
cerionf7da63d2004-12-09 19:04:57 +0000584/* Build IR to calculate all the flags from stored
cerionc60c01e2004-12-02 20:19:22 +0000585 CC_OP/CC_DEP1/CC_DEP2/CC_NDEP.
586 Returns an expression :: Ity_I32. */
587static IRExpr* mk_armg_calculate_flags_all ( void )
sewardjc2c87162004-11-25 13:07:02 +0000588{
589 IRExpr** args
cerionc60c01e2004-12-02 20:19:22 +0000590 = mkIRExprVec_3( IRExpr_Get(OFFB_CC_OP, Ity_I32),
sewardjc2c87162004-11-25 13:07:02 +0000591 IRExpr_Get(OFFB_CC_DEP1, Ity_I32),
cerionc60c01e2004-12-02 20:19:22 +0000592 IRExpr_Get(OFFB_CC_DEP2, Ity_I32) );
sewardjc2c87162004-11-25 13:07:02 +0000593 IRExpr* call
594 = mkIRExprCCall(
595 Ity_I32,
596 0/*regparm*/,
cerionc60c01e2004-12-02 20:19:22 +0000597 "armg_calculate_flags_all", &armg_calculate_flags_all,
sewardjc2c87162004-11-25 13:07:02 +0000598 args
599 );
cerionc60c01e2004-12-02 20:19:22 +0000600
601 /* Exclude OP from definedness checking. We're only
sewardjc2c87162004-11-25 13:07:02 +0000602 interested in DEP1 and DEP2. */
cerionc60c01e2004-12-02 20:19:22 +0000603 call->Iex.CCall.cee->mcx_mask = 1;
sewardjc2c87162004-11-25 13:07:02 +0000604 return call;
605}
sewardjfa89fdc2004-12-15 12:21:28 +0000606#endif
cerionc60c01e2004-12-02 20:19:22 +0000607
cerionf7da63d2004-12-09 19:04:57 +0000608/* Build IR to calculate just the carry flag from stored
609 CC_OP/CC_DEP1/CC_DEP2/CC_NDEP. Returns an expression :: Ity_I32. */
610static IRExpr* mk_armg_calculate_flags_c ( void )
611{
612 IRExpr** args
613 = mkIRExprVec_3( IRExpr_Get(OFFB_CC_OP, Ity_I32),
614 IRExpr_Get(OFFB_CC_DEP1, Ity_I32),
615 IRExpr_Get(OFFB_CC_DEP2, Ity_I32) );
616 IRExpr* call
617 = mkIRExprCCall(
618 Ity_I32,
619 0/*regparm*/,
620 "armg_calculate_flags_c", &armg_calculate_flags_c,
621 args
622 );
623 /* Exclude OP from definedness checking. We're only
624 interested in DEP1 and DEP2. */
625 call->Iex.CCall.cee->mcx_mask = 1;
626 return call;
627}
628
cerionc60c01e2004-12-02 20:19:22 +0000629
sewardjc2c87162004-11-25 13:07:02 +0000630/* Build IR to calculate some particular condition from stored
cerionc60c01e2004-12-02 20:19:22 +0000631 CC_OP/CC_DEP1/CC_DEP2. Returns an expression
sewardjcca71942004-12-02 23:35:18 +0000632 of type Ity_I1.
cerionc60c01e2004-12-02 20:19:22 +0000633*/
634static IRExpr* mk_armg_calculate_condition ( ARMCondcode cond )
sewardjc2c87162004-11-25 13:07:02 +0000635{
636 IRExpr** args
cerionc60c01e2004-12-02 20:19:22 +0000637 = mkIRExprVec_4( mkU32(cond),
sewardjc2c87162004-11-25 13:07:02 +0000638 IRExpr_Get(OFFB_CC_OP, Ity_I32),
639 IRExpr_Get(OFFB_CC_DEP1, Ity_I32),
cerionc60c01e2004-12-02 20:19:22 +0000640 IRExpr_Get(OFFB_CC_DEP2, Ity_I32) );
sewardjc2c87162004-11-25 13:07:02 +0000641 IRExpr* call
642 = mkIRExprCCall(
643 Ity_I32,
644 0/*regparm*/,
cerionc60c01e2004-12-02 20:19:22 +0000645 "armg_calculate_condition", &armg_calculate_condition,
sewardjc2c87162004-11-25 13:07:02 +0000646 args
647 );
cerionc60c01e2004-12-02 20:19:22 +0000648
649 /* Exclude the requested condition and OP from definedness
sewardjc2c87162004-11-25 13:07:02 +0000650 checking. We're only interested in DEP1 and DEP2. */
cerionc60c01e2004-12-02 20:19:22 +0000651 call->Iex.CCall.cee->mcx_mask = (1<<0) | (1<<1);
sewardjc2c87162004-11-25 13:07:02 +0000652 return unop(Iop_32to1, call);
653}
654
cerionc60c01e2004-12-02 20:19:22 +0000655
656
cerionc60c01e2004-12-02 20:19:22 +0000657
658
sewardjc2c87162004-11-25 13:07:02 +0000659
660
661/* -------------- Building the flags-thunk. -------------- */
662
663/* The machinery in this section builds the flag-thunk following a
664 flag-setting operation. Hence the various setFlags_* functions.
665*/
666
sewardjfa89fdc2004-12-15 12:21:28 +0000667#if 0
sewardjc2c87162004-11-25 13:07:02 +0000668static Bool isAddSub ( IROp op8 )
669{
670 return op8 == Iop_Add8 || op8 == Iop_Sub8;
671}
sewardjfa89fdc2004-12-15 12:21:28 +0000672#endif
sewardjc2c87162004-11-25 13:07:02 +0000673
sewardjfa89fdc2004-12-15 12:21:28 +0000674#if 0
sewardjc2c87162004-11-25 13:07:02 +0000675static Bool isLogic ( IROp op8 )
676{
677 return op8 == Iop_And8 || op8 == Iop_Or8 || op8 == Iop_Xor8;
678}
sewardjfa89fdc2004-12-15 12:21:28 +0000679#endif
sewardjc2c87162004-11-25 13:07:02 +0000680
681/* U-widen 8/16/32 bit int expr to 32. */
682static IRExpr* widenUto32 ( IRExpr* e )
683{
684 switch (typeOfIRExpr(irbb->tyenv,e)) {
685 case Ity_I32: return e;
686 case Ity_I16: return unop(Iop_16Uto32,e);
687 case Ity_I8: return unop(Iop_8Uto32,e);
688 default: vpanic("widenUto32");
689 }
690}
691
sewardjfa89fdc2004-12-15 12:21:28 +0000692#if 0
sewardjc2c87162004-11-25 13:07:02 +0000693/* S-widen 8/16/32 bit int expr to 32. */
694static IRExpr* widenSto32 ( IRExpr* e )
695{
696 switch (typeOfIRExpr(irbb->tyenv,e)) {
697 case Ity_I32: return e;
698 case Ity_I16: return unop(Iop_16Sto32,e);
699 case Ity_I8: return unop(Iop_8Sto32,e);
700 default: vpanic("widenSto32");
701 }
702}
sewardjfa89fdc2004-12-15 12:21:28 +0000703#endif
sewardjc2c87162004-11-25 13:07:02 +0000704
705/* Narrow 8/16/32 bit int expr to 8/16/32. Clearly only some
706 of these combinations make sense. */
707static IRExpr* narrowTo ( IRType dst_ty, IRExpr* e )
708{
709 IRType src_ty = typeOfIRExpr(irbb->tyenv,e);
710 if (src_ty == dst_ty)
711 return e;
712 if (src_ty == Ity_I32 && dst_ty == Ity_I16)
713 return unop(Iop_32to16, e);
714 if (src_ty == Ity_I32 && dst_ty == Ity_I8)
715 return unop(Iop_32to8, e);
716
717 vex_printf("\nsrc, dst tys are: ");
718 ppIRType(src_ty);
719 vex_printf(", ");
720 ppIRType(dst_ty);
721 vex_printf("\n");
cerionc60c01e2004-12-02 20:19:22 +0000722 vpanic("narrowTo(ARM)");
sewardjc2c87162004-11-25 13:07:02 +0000723}
724
725
726/* Set the flags thunk OP, DEP1 and DEP2 fields. The supplied op is
727 auto-sized up to the real op. */
728
729static
cerionfd7474a2004-12-03 11:16:42 +0000730void setFlags_DEP1_DEP2 ( IROp op, IRTemp dep1, IRTemp dep2 )
sewardjc2c87162004-11-25 13:07:02 +0000731{
cerionc60c01e2004-12-02 20:19:22 +0000732 stmt( IRStmt_Put( OFFB_CC_OP, mkU32(op)) );
sewardjc2c87162004-11-25 13:07:02 +0000733 stmt( IRStmt_Put( OFFB_CC_DEP1, widenUto32(mkexpr(dep1))) );
734 stmt( IRStmt_Put( OFFB_CC_DEP2, widenUto32(mkexpr(dep2))) );
735}
736
737
738/* Set the OP and DEP1 fields only, and write zero to DEP2. */
739
sewardjfa89fdc2004-12-15 12:21:28 +0000740#if 0
sewardjc2c87162004-11-25 13:07:02 +0000741static
cerionfd7474a2004-12-03 11:16:42 +0000742void setFlags_DEP1 ( IROp op, IRTemp dep1 )
sewardjc2c87162004-11-25 13:07:02 +0000743{
cerionc60c01e2004-12-02 20:19:22 +0000744 stmt( IRStmt_Put( OFFB_CC_OP, mkU32(op)) );
sewardjc2c87162004-11-25 13:07:02 +0000745 stmt( IRStmt_Put( OFFB_CC_DEP1, widenUto32(mkexpr(dep1))) );
746 stmt( IRStmt_Put( OFFB_CC_DEP2, mkU32(0)) );
747}
sewardjfa89fdc2004-12-15 12:21:28 +0000748#endif
sewardjc2c87162004-11-25 13:07:02 +0000749
sewardjfa89fdc2004-12-15 12:21:28 +0000750#if 0
sewardjc2c87162004-11-25 13:07:02 +0000751/* For shift operations, we put in the result and the undershifted
752 result. Except if the shift amount is zero, the thunk is left
753 unchanged. */
754
cerionc60c01e2004-12-02 20:19:22 +0000755static void setFlags_DEP1_DEP2_shift ( IROp op,
sewardjc2c87162004-11-25 13:07:02 +0000756 IRTemp res,
757 IRTemp resUS,
sewardjc2c87162004-11-25 13:07:02 +0000758 IRTemp guard )
759{
cerionc60c01e2004-12-02 20:19:22 +0000760 vassert(guard);
sewardjc2c87162004-11-25 13:07:02 +0000761
762 /* DEP1 contains the result, DEP2 contains the undershifted value. */
763 stmt( IRStmt_Put( OFFB_CC_OP,
764 IRExpr_Mux0X( mkexpr(guard),
765 IRExpr_Get(OFFB_CC_OP,Ity_I32),
cerionc60c01e2004-12-02 20:19:22 +0000766 mkU32(op))) );
sewardjc2c87162004-11-25 13:07:02 +0000767 stmt( IRStmt_Put( OFFB_CC_DEP1,
768 IRExpr_Mux0X( mkexpr(guard),
769 IRExpr_Get(OFFB_CC_DEP1,Ity_I32),
770 widenUto32(mkexpr(res)))) );
771 stmt( IRStmt_Put( OFFB_CC_DEP2,
772 IRExpr_Mux0X( mkexpr(guard),
773 IRExpr_Get(OFFB_CC_DEP2,Ity_I32),
774 widenUto32(mkexpr(resUS)))) );
775}
sewardjfa89fdc2004-12-15 12:21:28 +0000776#endif
sewardjc2c87162004-11-25 13:07:02 +0000777
778
cerionc60c01e2004-12-02 20:19:22 +0000779
780
cerionc60c01e2004-12-02 20:19:22 +0000781
sewardjfa89fdc2004-12-15 12:21:28 +0000782#if 0
sewardjc2c87162004-11-25 13:07:02 +0000783/* Multiplies are pretty much like add and sub: DEP1 and DEP2 hold the
784 two arguments. */
785
786static
cerionc60c01e2004-12-02 20:19:22 +0000787void setFlags_MUL ( IRTemp arg1, IRTemp arg2, UInt op )
sewardjc2c87162004-11-25 13:07:02 +0000788{
cerionc60c01e2004-12-02 20:19:22 +0000789 stmt( IRStmt_Put( OFFB_CC_OP, mkU32(op) ) );
790 stmt( IRStmt_Put( OFFB_CC_DEP1, widenUto32(mkexpr(arg1)) ));
791 stmt( IRStmt_Put( OFFB_CC_DEP2, widenUto32(mkexpr(arg2)) ));
sewardjc2c87162004-11-25 13:07:02 +0000792}
sewardjfa89fdc2004-12-15 12:21:28 +0000793#endif
cerionc60c01e2004-12-02 20:19:22 +0000794
795
796
797
798
799
800
sewardjc2c87162004-11-25 13:07:02 +0000801
802
803/* -------------- Condition codes. -------------- */
804
cerionc60c01e2004-12-02 20:19:22 +0000805/* Condition codes, using the ARM encoding. */
sewardjc2c87162004-11-25 13:07:02 +0000806
sewardjcca71942004-12-02 23:35:18 +0000807static HChar* name_ARMCondcode ( ARMCondcode cond )
sewardjc2c87162004-11-25 13:07:02 +0000808{
809 switch (cond) {
cerion397f9e52004-12-15 13:04:06 +0000810 case ARMCondEQ: return "{eq}";
811 case ARMCondNE: return "{ne}";
812 case ARMCondHS: return "{hs}"; // or 'cs'
813 case ARMCondLO: return "{lo}"; // or 'cc'
814 case ARMCondMI: return "{mi}";
815 case ARMCondPL: return "{pl}";
816 case ARMCondVS: return "{vs}";
817 case ARMCondVC: return "{vc}";
818 case ARMCondHI: return "{hi}";
819 case ARMCondLS: return "{ls}";
820 case ARMCondGE: return "{ge}";
821 case ARMCondLT: return "{lt}";
822 case ARMCondGT: return "{gt}";
823 case ARMCondLE: return "{le}";
824 case ARMCondAL: return ""; // {al}: default, doesn't need specifying
825 case ARMCondNV: return "{nv}";
cerionc60c01e2004-12-02 20:19:22 +0000826 default: vpanic("name_ARMCondcode");
sewardjc2c87162004-11-25 13:07:02 +0000827 }
828}
829
sewardjfa89fdc2004-12-15 12:21:28 +0000830#if 0
sewardjc2c87162004-11-25 13:07:02 +0000831static
cerionc60c01e2004-12-02 20:19:22 +0000832ARMCondcode positiveIse_ARMCondcode ( ARMCondcode cond,
sewardjc2c87162004-11-25 13:07:02 +0000833 Bool* needInvert )
834{
cerionc60c01e2004-12-02 20:19:22 +0000835 vassert(cond >= ARMCondEQ && cond <= ARMCondNV);
sewardjc2c87162004-11-25 13:07:02 +0000836 if (cond & 1) {
837 *needInvert = True;
838 return cond-1;
839 } else {
840 *needInvert = False;
841 return cond;
842 }
843}
sewardjfa89fdc2004-12-15 12:21:28 +0000844#endif
cerionc60c01e2004-12-02 20:19:22 +0000845
846
cerion397f9e52004-12-15 13:04:06 +0000847/* Addressing Mode 1 - DP ops
848 Addressing Mode 2 - Load/Store word/ubyte (scaled)
849 */
850static HChar* name_ARMShiftOp ( UChar shift_op, UChar imm_val )
851{
852 switch (shift_op) {
853 case 0x0: case 0x1: case 0x8: return "lsl";
854 case 0x2: case 0x3: case 0xA: return "lsr";
855 case 0x4: case 0x5: case 0xC: return "asr";
856 case 0x6: return (imm_val==0) ? "rrx" : "ror";
857 case 0x7: case 0xE: return "ror";
858 default: vpanic("name_ARMShiftcode");
859 }
860}
861
862
863/* Addressing Mode 4 - Load/Store Multiple */
864static HChar* name_ARMAddrMode4 ( UChar mode )
865{
866 /* See ARM ARM A5-55 for alternative names for stack operations
867 ldmfa (full ascending), etc. */
868 switch (mode) {
869 case 0x0: return "da"; // Decrement after
870 case 0x1: return "ia"; // Increment after
871 case 0x2: return "db"; // Decrement before
872 case 0x3: return "ib"; // Increment before
873 default: vpanic("name_ARMAddrMode4");
874 }
875}
876
877/* Data Processing ops */
878static HChar* name_ARMDataProcOp ( UChar opc )
879{
880 switch (opc) {
881 case 0x0: return "and";
882 case 0x1: return "eor";
883 case 0x2: return "sub";
884 case 0x3: return "rsb";
885 case 0x4: return "add";
886 case 0x5: return "adc";
887 case 0x6: return "sbc";
888 case 0x7: return "rsc";
889 case 0x8: return "tst";
890 case 0x9: return "teq";
891 case 0xA: return "cmp";
892 case 0xB: return "cmn";
893 case 0xC: return "orr";
894 case 0xD: return "mov";
895 case 0xE: return "bic";
896 case 0xF: return "mvn";
897 default: vpanic("name_ARMDataProcOp");
898 }
899}
cerionc60c01e2004-12-02 20:19:22 +0000900
901
902
cerionf7da63d2004-12-09 19:04:57 +0000903/*
904 Addressing mode 4 - LOAD/STORE multiple, LDM|STM
905 ARM ARM A5-48
906*/
907static
ceriona512a652004-12-10 11:43:10 +0000908Bool dis_loadstore_mult ( theInstr )
cerionf7da63d2004-12-09 19:04:57 +0000909{
910 UChar flags = (theInstr >> 20) & 0x1F; // theInstr[24:20]
911 UChar Rn_addr = (theInstr >> 16) & 0xF;
912 IRTemp Rn = newTemp(Ity_I32);
913 IRTemp Rn_orig = newTemp(Ity_I32);
cerion060c5422004-12-10 15:28:43 +0000914 UInt reg_list = theInstr & 0xFFFF; // each bit addresses a register: R15 to R0
915
cerionf7da63d2004-12-09 19:04:57 +0000916 UChar L = (flags >> 0) & 1; // Load(1) | Store(0)
917 UChar W = (flags >> 1) & 1; // (W)riteback Rn (incr(U=1) | decr(U=0) by n_bytes)
cerion397f9e52004-12-15 13:04:06 +0000918 UChar S = (flags >> 2) & 1; // Priviledged mode flag - *** CAB TODO ***
cerionf7da63d2004-12-09 19:04:57 +0000919 UChar U = (flags >> 3) & 1; // Txfr ctl: Direction = upwards(1) | downwards(0)
920 UChar PU = (flags >> 3) & 3; // Txfr ctl: Rn within(P=1) | outside(P=0) accessed mem
921
cerion19e8a612004-12-10 10:18:58 +0000922 IRTemp start_addr = newTemp(Ity_I32);
923 IRTemp end_addr = newTemp(Ity_I32);
cerionf7da63d2004-12-09 19:04:57 +0000924 IRTemp data=0;
925 UInt n_bytes=0;
926 UInt tmp_reg = reg_list;
cerion19e8a612004-12-10 10:18:58 +0000927 UInt reg_idx, offset;
ceriona512a652004-12-10 11:43:10 +0000928 Bool decode_ok = True;
cerionf7da63d2004-12-09 19:04:57 +0000929
cerion397f9e52004-12-15 13:04:06 +0000930 HChar* cond_name = name_ARMCondcode( (theInstr >> 28) & 0xF );
931 HChar reg_names[70];
932 UInt buf_offset;
933
cerionf7da63d2004-12-09 19:04:57 +0000934 while (tmp_reg > 0) { // Count num bits in reg_list => num_bytes
935 if (tmp_reg & 1) { n_bytes += 4; }
936 tmp_reg = tmp_reg >> 1;
937 }
938
939 assign( Rn, getIReg(Rn_addr) );
940 assign( Rn_orig, mkexpr(Rn) );
941
cerion19e8a612004-12-10 10:18:58 +0000942 switch (PU) { // <addressing_mode>
cerionf7da63d2004-12-09 19:04:57 +0000943 case 0x0: // Decrement after (DA)
944 assign( start_addr, binop( Iop_Add32, mkexpr(Rn), mkU32(n_bytes + 4) ) );
945 assign( end_addr, mkexpr(Rn) );
946 break;
947
948 case 0x1: // Increment after (IA)
949 assign( start_addr, mkexpr(Rn) );
950 assign( end_addr, binop( Iop_Add32, mkexpr(Rn), mkU32(n_bytes - 4) ) );
951 break;
952
953 case 0x2: // Decrement before (DB)
954 assign( start_addr, binop( Iop_Sub32, mkexpr(Rn), mkU32(n_bytes) ) );
955 assign( end_addr, binop( Iop_Sub32, mkexpr(Rn), mkU32(4) ) );
956 break;
957
958 case 0x3: // Increment before (IB)
959 assign( start_addr, binop( Iop_Add32, mkexpr(Rn), mkU32(4) ) );
960 assign( end_addr, binop( Iop_Add32, mkexpr(Rn), mkU32(n_bytes) ) );
961 break;
962
963 default:
ceriona512a652004-12-10 11:43:10 +0000964 vex_printf("dis_loadstore_mult(ARM): No such case: 0x%x", PU);
965 return False;
cerionf7da63d2004-12-09 19:04:57 +0000966 }
967
968 if (W==1) {
969 if (U==1) { // upwards
cerion19e8a612004-12-10 10:18:58 +0000970 putIReg( Rn_addr, binop( Iop_Add32, mkexpr(Rn), mkU32(n_bytes) ) );
cerionf7da63d2004-12-09 19:04:57 +0000971 } else { // downwards
cerion19e8a612004-12-10 10:18:58 +0000972 putIReg( Rn_addr, binop( Iop_Sub32, mkexpr(Rn), mkU32(n_bytes) ) );
cerionf7da63d2004-12-09 19:04:57 +0000973 }
974 }
975
976
977 /*
978 Loop through register list, LOAD/STORE indicated registers
cerion060c5422004-12-10 15:28:43 +0000979 Lowest numbered reg -> lowest address, so start with lowest register
980 reg_idx: guest register address
981 offset : current mem offset from start_addr
cerionf7da63d2004-12-09 19:04:57 +0000982 */
cerion397f9e52004-12-15 13:04:06 +0000983 reg_names[0] = '\0';
984 buf_offset=0;
cerion19e8a612004-12-10 10:18:58 +0000985 offset=0;
cerionf7da63d2004-12-09 19:04:57 +0000986 for (reg_idx=0; reg_idx < 16; reg_idx++) {
987 if (( reg_list >> reg_idx ) & 1) { // reg_list[i] == 1?
cerion19e8a612004-12-10 10:18:58 +0000988
989 if (L==1) { // LOAD Ri, (start_addr + offset)
cerionf7da63d2004-12-09 19:04:57 +0000990
ceriona512a652004-12-10 11:43:10 +0000991 if (Rn_addr == reg_idx && W==1) { // Undefined - ARM ARM A4-31
992 decode_ok=False;
993 break;
994 }
cerionf7da63d2004-12-09 19:04:57 +0000995
cerion19e8a612004-12-10 10:18:58 +0000996 assign( data, loadLE(Ity_I32, binop(Iop_Add32,
cerion060c5422004-12-10 15:28:43 +0000997 mkexpr(start_addr),
998 mkU32(offset))) );
cerionf7da63d2004-12-09 19:04:57 +0000999 if (reg_idx == 15) {
1000 // assuming architecture < 5: See ARM ARM A4-31
1001 putIReg( reg_idx, binop(Iop_And32, mkexpr(data), mkU32(0xFFFFFFFC)) );
1002 } else {
1003 putIReg( reg_idx, mkexpr(data) );
1004 }
cerion19e8a612004-12-10 10:18:58 +00001005 } else { // STORE Ri, (start_addr + offset)
cerionf7da63d2004-12-09 19:04:57 +00001006
1007 // ARM ARM A4-85 (Operand restrictions)
ceriona512a652004-12-10 11:43:10 +00001008 if (reg_idx == Rn_addr && W==1) { // Rn in reg_list && writeback
1009 if (offset != 0) { // Undefined - See ARM ARM A4-85
1010 decode_ok=False;
1011 break;
cerionf7da63d2004-12-09 19:04:57 +00001012 }
ceriona512a652004-12-10 11:43:10 +00001013 // is lowest reg in reg_list: store Rn_orig
1014 storeLE( mkexpr(start_addr), mkexpr(Rn_orig) );
cerionf7da63d2004-12-09 19:04:57 +00001015 } else {
cerion19e8a612004-12-10 10:18:58 +00001016 storeLE( binop(Iop_Add32, mkexpr(start_addr), mkU32(offset) ),
1017 getIReg(reg_idx) );
cerionf7da63d2004-12-09 19:04:57 +00001018 }
1019 }
cerion19e8a612004-12-10 10:18:58 +00001020 offset += 4;
cerion397f9e52004-12-15 13:04:06 +00001021
1022 reg_names[buf_offset++] = 'R';
1023 if (reg_idx > 9) {
1024 reg_names[buf_offset++] = '1';
1025 reg_names[buf_offset++] = 38 + reg_idx;
1026 } else {
1027 reg_names[buf_offset++] = 48 + reg_idx;
1028 }
1029 reg_names[buf_offset++] = ',';
1030 // CAB: Eugh! Where's strcpy?!
cerionf7da63d2004-12-09 19:04:57 +00001031 }
1032 }
cerion397f9e52004-12-15 13:04:06 +00001033 if (buf_offset > 0) {
1034 reg_names[buf_offset-1] = '\0';
1035 }
1036 DIP("%s%s%s R%d%s, {%s}%s\n", (L==1) ? "ldm":"stm", cond_name,
1037 name_ARMAddrMode4( PU ), Rn_addr, (W==1) ? "!" : "",
1038 reg_names, (S==1) ? "^" : "");
1039
cerionf7da63d2004-12-09 19:04:57 +00001040 // CAB TODO:
cerion19e8a612004-12-10 10:18:58 +00001041 // IR assert( end_addr == (start_addr + offset) - 8 )
cerionf7da63d2004-12-09 19:04:57 +00001042
cerion060c5422004-12-10 15:28:43 +00001043 if (offset == 0) { // Unpredictable - ARM ARM A5-21
1044 vex_printf("dis_loadstore_mult(arm): Unpredictable - offset==0\n");
1045 decode_ok = False;
1046 }
1047
ceriona512a652004-12-10 11:43:10 +00001048 return decode_ok;
cerionf7da63d2004-12-09 19:04:57 +00001049}
1050
1051
1052
1053
cerion060c5422004-12-10 15:28:43 +00001054
cerionf7da63d2004-12-09 19:04:57 +00001055static
cerion397f9e52004-12-15 13:04:06 +00001056Bool dis_loadstore_w_ub_address ( UInt theInstr, IRTemp* address, HChar* buf )
cerionf7da63d2004-12-09 19:04:57 +00001057{
1058 UChar is_reg = (theInstr >> 25) & 0x1; // immediate | register offset/index
1059 UInt flags = (theInstr >> 20) & 0x3F; // theInstr[25:20]
1060 UChar Rn_addr = (theInstr >> 16) & 0xF;
cerionf7da63d2004-12-09 19:04:57 +00001061 UChar Rm_addr = (theInstr >> 00) & 0xF;
1062 UChar shift_op = (theInstr >> 04) & 0xFF;
1063 UInt offset_12 = (theInstr >> 00) & 0xFFF;
1064 IRTemp Rn = newTemp(Ity_I32);
1065 IRTemp Rm = newTemp(Ity_I32);
1066 UChar shift_imm, shift;
1067
cerion060c5422004-12-10 15:28:43 +00001068 UChar W = (flags >> 1) & 1; // base register writeback flag - See *Note
cerionf7da63d2004-12-09 19:04:57 +00001069 UChar U = (flags >> 3) & 1; // offset is added(1)|subtracted(0) from the base
cerion060c5422004-12-10 15:28:43 +00001070 UChar P = (flags >> 4) & 1; // addressing mode flag - See *Note
1071 /* *Note
1072 P==0: post-indexed addressing: addr -> Rn
1073 W==0: normal mem access
1074 W==1: unprivileged mem access
1075 P==1: W==0: offset addressing: Rn not updated - ARM ARM A5-20
1076 W==1: pre-indexed addressing: addr -> Rn
1077 */
cerionf7da63d2004-12-09 19:04:57 +00001078
cerion060c5422004-12-10 15:28:43 +00001079 IRTemp scaled_index = newTemp(Ity_I32);
1080 IRTemp reg_offset = newTemp(Ity_I32);
cerionf7da63d2004-12-09 19:04:57 +00001081
1082 IRTemp oldFlagC = newTemp(Ity_I32);
1083
cerion397f9e52004-12-15 13:04:06 +00001084 HChar buf2[30];
1085 HChar buf3[20];
1086 buf3[0] = '\0';
1087
cerionf7da63d2004-12-09 19:04:57 +00001088 if (Rn_addr == 15) {
cerion060c5422004-12-10 15:28:43 +00001089 if (P==1 && W==0) { // offset addressing
ceriona512a652004-12-10 11:43:10 +00001090 // CAB: This right?
cerion060c5422004-12-10 15:28:43 +00001091 assign( Rn, binop(Iop_And32, getIReg(15), mkU32(8)) );
1092 } else { // Unpredictable - ARM ARM A5-25,29...
1093 vex_printf("dis_loadstore_w_ub_address(arm): Unpredictable - Rn_addr==15\n");
ceriona512a652004-12-10 11:43:10 +00001094 return False;
cerionf7da63d2004-12-09 19:04:57 +00001095 }
cerion060c5422004-12-10 15:28:43 +00001096 } else {
1097 assign( Rn, getIReg(Rn_addr) );
cerionf7da63d2004-12-09 19:04:57 +00001098 }
1099
1100 /*
cerion060c5422004-12-10 15:28:43 +00001101 Retrieve / Calculate reg_offset
cerionf7da63d2004-12-09 19:04:57 +00001102 */
1103 if (is_reg) {
cerion060c5422004-12-10 15:28:43 +00001104 if (Rm_addr == 15) { // Unpredictable - ARM ARM A5-21
1105 vex_printf("dis_loadstore_w_ub_address(arm): Unpredictable - Rm_addr==15\n");
ceriona512a652004-12-10 11:43:10 +00001106 return False;
1107 }
cerion060c5422004-12-10 15:28:43 +00001108 if (P==0 || W==1) { // pre|post-indexed addressing
1109 if (Rm_addr == Rn_addr) { // Unpredictable - ARM ARM A5-25
1110 vex_printf("dis_loadstore_w_ub_address(arm): Unpredictable - Rm_addr==Rn_addr\n");
1111 return False;
1112 }
1113 }
cerionf7da63d2004-12-09 19:04:57 +00001114 assign( Rm, getIReg(Rm_addr) );
1115
cerion060c5422004-12-10 15:28:43 +00001116 if (shift_op == 0) { // Register addressing
1117 assign( reg_offset, mkexpr(Rm) );
1118 } else { // Scaled Register addressing
cerionf7da63d2004-12-09 19:04:57 +00001119 shift_imm = (shift_op >> 3) & 0x1F;
1120 shift = (shift_op >> 1) & 0x3;
1121
1122 switch (shift) {
1123 case 0x0: // LSL
cerion060c5422004-12-10 15:28:43 +00001124 assign( scaled_index, binop(Iop_Shl32, mkexpr(Rm), mkU8(shift_imm)) );
cerionf7da63d2004-12-09 19:04:57 +00001125 break;
1126
1127 case 0x1: // LSR
1128 if (shift_imm) {
cerion060c5422004-12-10 15:28:43 +00001129 assign( scaled_index, binop(Iop_Shr32, mkexpr(Rm), mkU8(shift_imm)) );
cerionf7da63d2004-12-09 19:04:57 +00001130 } else {
cerion060c5422004-12-10 15:28:43 +00001131 assign( scaled_index, mkU32(0) );
cerionf7da63d2004-12-09 19:04:57 +00001132 }
1133 break;
1134
1135 case 0x2: // ASR
1136 if (shift_imm) {
cerion060c5422004-12-10 15:28:43 +00001137 assign( scaled_index, binop(Iop_Sar32, mkexpr(Rm), mkU32(shift_imm)) );
cerionf7da63d2004-12-09 19:04:57 +00001138 } else {
cerion060c5422004-12-10 15:28:43 +00001139 assign( scaled_index, // Rm[31] ? 0xFFFFFFFF : 0x0
1140 IRExpr_Mux0X(binop(Iop_And32, mkexpr(Rm), mkU32(0x8FFFFFFF)),
1141 mkexpr(0x0), mkexpr(0xFFFFFFFF)) );
cerionf7da63d2004-12-09 19:04:57 +00001142 }
1143 break;
1144
1145 case 0x3: // ROR|RRX
ceriondee08672004-12-14 12:08:09 +00001146 assign( oldFlagC, binop(Iop_Shr32,
1147 mk_armg_calculate_flags_c(),
cerion397f9e52004-12-15 13:04:06 +00001148 mkU8(ARMG_CC_SHIFT_C)) );
cerionf7da63d2004-12-09 19:04:57 +00001149
1150 if (shift_imm == 0) { // RRX (ARM ARM A5-17)
1151 // 33 bit ROR using carry flag as the 33rd bit
1152 // op = Rm >> 1, carry flag replacing vacated bit position.
ceriondee08672004-12-14 12:08:09 +00001153 // scaled_index = (c_flag << 31) | (Rm >> 1)
cerion060c5422004-12-10 15:28:43 +00001154 assign( scaled_index, binop(Iop_Or32,
1155 binop(Iop_Shl32, mkexpr(oldFlagC), mkU32(31)),
1156 binop(Iop_Shr32, mkexpr(Rm), mkU8(1))) );
cerionf7da63d2004-12-09 19:04:57 +00001157
1158 } else { // ROR
cerion060c5422004-12-10 15:28:43 +00001159 // scaled_index = Rm ROR shift_imm
1160 // = (Rm >> shift_imm) | (Rm << (32-shift_imm))
1161 assign( scaled_index,
1162 binop(Iop_Or32,
1163 binop(Iop_Shr32, mkexpr(Rm), mkU8(shift_imm)),
1164 binop(Iop_Shl32, mkexpr(Rm),
1165 binop(Iop_Sub8, mkU8(32), mkU32(shift_imm)))) );
cerionf7da63d2004-12-09 19:04:57 +00001166 }
1167 break;
1168
cerion060c5422004-12-10 15:28:43 +00001169 default:
1170 vex_printf("dis_loadstore_w_ub(ARM): No such case: 0x%x", shift);
1171 return False;
cerionf7da63d2004-12-09 19:04:57 +00001172 }
cerion060c5422004-12-10 15:28:43 +00001173 assign( reg_offset, mkexpr(scaled_index) );
cerion397f9e52004-12-15 13:04:06 +00001174
1175 if (shift == 0x3 && shift_imm == 0) {
1176 DIS(buf3, ", %s", name_ARMShiftOp(shift_op * 2, shift_imm));
1177 } else {
1178 DIS(buf3, ", %s #%d", name_ARMShiftOp(shift_op * 2, shift_imm), shift_imm);
1179 }
cerionf7da63d2004-12-09 19:04:57 +00001180 }
cerion397f9e52004-12-15 13:04:06 +00001181 DIS(buf2, "%cR%d%s", (U==1) ? '+' : '-', Rm_addr, buf3);
cerion060c5422004-12-10 15:28:43 +00001182 } else { // immediate
1183 assign( reg_offset, mkU32(offset_12) );
cerion397f9e52004-12-15 13:04:06 +00001184
1185 DIS(buf2, "#%c%d", (U==1) ? '+' : '-', offset_12);
cerionf7da63d2004-12-09 19:04:57 +00001186 }
cerion397f9e52004-12-15 13:04:06 +00001187 DIS(buf, "[R%d%s, %s%s", Rn_addr,
1188 (P==0) ? "]" : "", buf2,
1189 (P==1) ? ((W==1) ? "]!" : "]") : "");
1190
cerionf7da63d2004-12-09 19:04:57 +00001191 /*
cerion060c5422004-12-10 15:28:43 +00001192 Depending on P,U,W, write to Rn and set address to load/store
cerionf7da63d2004-12-09 19:04:57 +00001193 */
cerion060c5422004-12-10 15:28:43 +00001194 if (P==1) { // offset | pre-indexed addressing
1195 if (U == 1) { // - increment
1196 assign( *address, binop(Iop_Add32, mkexpr(Rn), mkexpr(reg_offset)) );
1197 } else { // - decrement
1198 assign( *address, binop(Iop_Sub32, mkexpr(Rn), mkexpr(reg_offset)) );
cerionf7da63d2004-12-09 19:04:57 +00001199 }
cerion060c5422004-12-10 15:28:43 +00001200 if (W == 1) { // pre-indexed addressing, base register writeback
1201 putIReg( Rn_addr, mkexpr(*address) );
cerionf7da63d2004-12-09 19:04:57 +00001202 }
1203 } else { // post-indexed addressing
cerion060c5422004-12-10 15:28:43 +00001204 assign( *address, mkexpr(Rn) );
1205 if (U == 1) { // - increment
1206 putIReg( Rn_addr, binop( Iop_Add32, mkexpr(Rn), mkexpr(reg_offset) ) );
1207 } else { // - decrement
1208 putIReg( Rn_addr, binop( Iop_Sub32, mkexpr(Rn), mkexpr(reg_offset) ) );
cerionf7da63d2004-12-09 19:04:57 +00001209 }
1210 }
cerion060c5422004-12-10 15:28:43 +00001211 return True;
1212}
cerionf7da63d2004-12-09 19:04:57 +00001213
1214
1215
cerion060c5422004-12-10 15:28:43 +00001216
1217/*
1218 Addressing mode 2 - LOAD/STORE word or unsigned byte
1219 ARM ARM A5-18
1220*/
1221static
1222Bool dis_loadstore_w_ub ( UInt theInstr )
1223{
1224 UInt flags = (theInstr >> 20) & 0x3F; // theInstr[25:20]
1225 UChar Rn_addr = (theInstr >> 16) & 0xF;
1226 UChar Rd_addr = (theInstr >> 12) & 0xF;
1227 IRTemp address = newTemp(Ity_I32);
1228
1229 UChar L = (flags >> 0) & 1; // Load(1) | Store(0)
1230 UChar W = (flags >> 1) & 1; // base register writeback
1231 UChar B = (flags >> 2) & 1; // access = unsigned byte(1) | word(0)
1232
1233 IRTemp value = newTemp(Ity_I32);
1234 IRTemp data = newTemp(Ity_I32);
1235 IRTemp data_ror8 = newTemp(Ity_I32);
1236 IRTemp data_ror16 = newTemp(Ity_I32);
1237 IRTemp data_ror24 = newTemp(Ity_I32);
1238 IRExpr* expr_addr_10;
cerion397f9e52004-12-15 13:04:06 +00001239 HChar* cond_name = name_ARMCondcode( (theInstr >> 28) & 0xF );
1240 HChar dis_buf[50];
cerion060c5422004-12-10 15:28:43 +00001241
1242
1243 vassert(((theInstr >> 26) & 0x3) == 0x1);
1244
1245 // Get the address to load/store
cerion397f9e52004-12-15 13:04:06 +00001246 if (!dis_loadstore_w_ub_address(theInstr, &address, dis_buf)) { return False; }
cerion060c5422004-12-10 15:28:43 +00001247
cerion397f9e52004-12-15 13:04:06 +00001248 DIP("%s%s%s R%d, %s\n", (L==1) ? "ldr" : "str", cond_name,
1249 (B==1) ? "b" : "", Rd_addr, dis_buf);
cerion060c5422004-12-10 15:28:43 +00001250
1251 if (Rd_addr == Rn_addr && W==1) { // Unpredictable - ARM ARM A4-39,41,89,91
1252 vex_printf("dis_loadstore_w_ub(arm): Unpredictable - Rd_addr==Rn_addr\n");
1253 return False;
1254 }
1255
cerionf7da63d2004-12-09 19:04:57 +00001256 /*
1257 LOAD/STORE Rd, address
1258 */
1259 if (L==1) { // LOAD
1260 if (B==1) { // unsigned byte (LDRB): ARM ARM A4-40
cerion060c5422004-12-10 15:28:43 +00001261 if (Rd_addr == 15) { // Unpredictable - ARM ARM A4-40
1262 vex_printf("dis_loadstore_w_ub(arm): Unpredictable - Rd_addr==15\n");
1263 return False;
1264 }
1265 putIReg( Rd_addr, loadLE( Ity_I8, mkexpr( address ) ) );
cerionf7da63d2004-12-09 19:04:57 +00001266 }
1267 else { // word (LDR): ARM ARM A4-38
cerion060c5422004-12-10 15:28:43 +00001268 expr_addr_10 = binop(Iop_And32, mkexpr(address), mkU32(0x3));
1269
1270 /*
1271 CAB TODO
1272 if (Rd_addr == 15 && address[1:0] == 0) => Unpredictable
1273 How to bomb out using IR?
1274 */
cerionf7da63d2004-12-09 19:04:57 +00001275
1276 /* LOAD memory data (4 bytes) */
cerion060c5422004-12-10 15:28:43 +00001277 assign( data, loadLE( Ity_I32, mkexpr( address ) ) );
cerionf7da63d2004-12-09 19:04:57 +00001278
1279 // data ROR 8
cerion060c5422004-12-10 15:28:43 +00001280 assign( data_ror8, binop(Iop_Sub8, mkU8(32), mkU32(8)) );
1281 assign( data_ror8, binop( Iop_Or32,
1282 binop( Iop_Shr32, mkexpr(data), mkU8(8) ),
1283 binop( Iop_Shl32, mkexpr(data), mkexpr(data_ror8) ) ) );
cerionf7da63d2004-12-09 19:04:57 +00001284 // data ROR 16
cerion060c5422004-12-10 15:28:43 +00001285 assign( data_ror16, binop(Iop_Sub8, mkU8(32), mkU32(16)) );
1286 assign( data_ror16, binop( Iop_Or32,
1287 binop( Iop_Shr32, mkexpr(data), mkU8(16) ),
1288 binop( Iop_Shl32, mkexpr(data), mkexpr(data_ror16) ) ) );
cerionf7da63d2004-12-09 19:04:57 +00001289
1290 // data ROR 24
cerion060c5422004-12-10 15:28:43 +00001291 assign( data_ror24, binop(Iop_Sub8, mkU8(32), mkU32(24)) );
1292 assign( data_ror24, binop( Iop_Or32,
1293 binop( Iop_Shr32, mkexpr(data), mkU8(24) ),
1294 binop( Iop_Shl32, mkexpr(data), mkexpr(data_ror24) ) ) );
cerionf7da63d2004-12-09 19:04:57 +00001295
cerion060c5422004-12-10 15:28:43 +00001296 /* switch (address[1:0]) {
1297 0x0: value = data;
1298 0x1: value = data ROR 8;
1299 0x2: value = data ROR 16;
1300 0x3: value = data ROR 24; } */
1301 assign( value, IRExpr_Mux0X(
1302 binop(Iop_CmpEQ32, expr_addr_10, mkU32(0x0)),
cerionf7da63d2004-12-09 19:04:57 +00001303 IRExpr_Mux0X(
cerion060c5422004-12-10 15:28:43 +00001304 binop(Iop_CmpEQ32, expr_addr_10, mkU32(0x1)),
cerionf7da63d2004-12-09 19:04:57 +00001305 IRExpr_Mux0X(
cerion060c5422004-12-10 15:28:43 +00001306 binop(Iop_CmpEQ32, expr_addr_10, mkU32(0x2)),
1307 mkexpr(data_ror24),
1308 mkexpr(data_ror16) ),
1309 mkexpr(data_ror8) ),
1310 mkexpr(data) ) );
cerionf7da63d2004-12-09 19:04:57 +00001311
cerion060c5422004-12-10 15:28:43 +00001312 if (Rd_addr == 15) {
cerionf7da63d2004-12-09 19:04:57 +00001313 // assuming architecture < 5: See ARM ARM A4-28
cerion060c5422004-12-10 15:28:43 +00001314 putIReg( Rd_addr, binop(Iop_And32, mkexpr(value), mkU32(0xFFFFFFFC)) );
1315
1316 // CAB: Need to tell vex we're doing a jump here?
1317 // irbb->jumpkind = Ijk_Boring;
1318 // irbb->next = mkexpr(value);
cerionf7da63d2004-12-09 19:04:57 +00001319 } else {
cerion060c5422004-12-10 15:28:43 +00001320 putIReg( Rd_addr, mkexpr(value) );
cerionf7da63d2004-12-09 19:04:57 +00001321 }
1322
1323 }
1324 } else { // STORE: ARM ARM A4-88
cerionf7da63d2004-12-09 19:04:57 +00001325 if (B==1) { // unsigned byte
cerion060c5422004-12-10 15:28:43 +00001326 if (Rd_addr == 15) { // Unpredictable - ARM ARM A4-90
1327 vex_printf("dis_loadstore_w_ub(arm): Unpredictable - Rd_addr==15\n");
1328 return False;
1329 }
1330 storeLE( mkexpr(address), unop(Iop_32to8, getIReg(Rd_addr)) ); // Rd[7:0]
cerionf7da63d2004-12-09 19:04:57 +00001331 } else { // word
cerion060c5422004-12-10 15:28:43 +00001332
1333 if (Rd_addr == 15) { // Implementation Defined - ARM ARM A4-88
1334 vex_printf("dis_loadstore_w_ub(arm): Implementation Defined - Rd_addr==15\n");
1335 return False;
1336 // CAB TODO: What to do here?
1337 }
1338 storeLE( mkexpr(address), getIReg(Rd_addr) );
cerionf7da63d2004-12-09 19:04:57 +00001339 }
1340 }
ceriona512a652004-12-10 11:43:10 +00001341 return True;
cerionf7da63d2004-12-09 19:04:57 +00001342}
cerionc60c01e2004-12-02 20:19:22 +00001343
cerionc60c01e2004-12-02 20:19:22 +00001344
cerionfd7474a2004-12-03 11:16:42 +00001345
1346
1347
1348
1349/*
cerionf7da63d2004-12-09 19:04:57 +00001350 ARMG_CC_OP_LSL, ARMG_CC_OP_LSR, ARMG_CC_OP_ASR
1351 ARM ARM A5-9...
1352
1353 carry = carry_out[0]
cerionfd7474a2004-12-03 11:16:42 +00001354*/
ceriona70a37b2004-12-03 18:54:08 +00001355static
cerion397f9e52004-12-15 13:04:06 +00001356IRExpr* dis_shift( Bool* decode_ok, UInt theInstr, IRTemp* carry_out, HChar* buf )
ceriona70a37b2004-12-03 18:54:08 +00001357{
cerion060c5422004-12-10 15:28:43 +00001358 UChar Rn_addr = (theInstr >> 16) & 0xF;
1359 UChar Rd_addr = (theInstr >> 12) & 0xF;
1360 UChar Rs_addr = (theInstr >> 8) & 0xF;
1361 UChar Rm_addr = (theInstr >> 0) & 0xF;
1362 UChar by_reg = (theInstr >> 4) & 0x1; // instr[4]
1363 UChar shift_imm = (theInstr >> 7) & 0x1F; // instr[11:7]
1364 UChar shift_op = (theInstr >> 4) & 0xF; // instr[7:4]
cerionf7da63d2004-12-09 19:04:57 +00001365 IRTemp Rm = newTemp(Ity_I32);
1366 IRTemp Rs = newTemp(Ity_I32);
cerion19e8a612004-12-10 10:18:58 +00001367 IRTemp shift_amt = newTemp(Ity_I8);
1368 IRTemp carry_shift = newTemp(Ity_I8);
cerionf7da63d2004-12-09 19:04:57 +00001369 IRTemp oldFlagC = newTemp(Ity_I32);
1370 IRTemp mux_false = newTemp(Ity_I32);
ceriona70a37b2004-12-03 18:54:08 +00001371 IRExpr* expr;
cerionf7da63d2004-12-09 19:04:57 +00001372 IROp op;
cerionfd7474a2004-12-03 11:16:42 +00001373
ceriondee08672004-12-14 12:08:09 +00001374 assign( oldFlagC, binop(Iop_Shr32,
1375 mk_armg_calculate_flags_c(),
1376 mkU8(ARMG_CC_SHIFT_C)) );
cerion060c5422004-12-10 15:28:43 +00001377
cerionf7da63d2004-12-09 19:04:57 +00001378 switch (shift_op) {
ceriona512a652004-12-10 11:43:10 +00001379 case 0x0: case 0x8: case 0x1: op = Iop_Shl32; break;
1380 case 0x2: case 0xA: case 0x3: op = Iop_Shr32; break;
1381 case 0x4: case 0xC: case 0x5: op = Iop_Sar32; break;
1382 default:
1383 vex_printf("dis_shift(arm): No such case: 0x%x\n", shift_op);
1384 *decode_ok = False;
1385 return mkU32(0);
cerionf7da63d2004-12-09 19:04:57 +00001386 }
cerionfd7474a2004-12-03 11:16:42 +00001387
cerionf7da63d2004-12-09 19:04:57 +00001388
1389 if (by_reg) { // Register Shift
cerion060c5422004-12-10 15:28:43 +00001390 assign( Rm, getIReg(Rm_addr) );
1391
1392 if (Rd_addr == 15 || Rm_addr == 15 ||
1393 Rn_addr == 15 || Rs_addr == 15) { // Unpredictable (ARM ARM A5-10)
1394 vex_printf("dis_shift(arm): Unpredictable - Rd|Rm|Rn|Rs == R15\n");
ceriona512a652004-12-10 11:43:10 +00001395 *decode_ok = False;
1396 return mkU32(0);
cerionf7da63d2004-12-09 19:04:57 +00001397 }
1398
cerion060c5422004-12-10 15:28:43 +00001399 assign( Rs, getIReg((theInstr >> 8) & 0xF) ); // instr[11:8]
cerionf7da63d2004-12-09 19:04:57 +00001400
1401 // shift_amt = shift_expr & 31 => Rs[5:0]
cerion19e8a612004-12-10 10:18:58 +00001402 assign( shift_amt,
1403 narrowTo(Ity_I8, binop( Iop_And32, mkexpr(Rs), mkU32(0x1F)) ) );
cerion060c5422004-12-10 15:28:43 +00001404
cerion19e8a612004-12-10 10:18:58 +00001405 // CAB TODO: support for >31 shift ? (Rs[7:0])
cerionf7da63d2004-12-09 19:04:57 +00001406
1407 switch (shift_op) {
1408 case 0x1: // LSL(reg)
1409 assign( mux_false, mkU32(0) );
cerion19e8a612004-12-10 10:18:58 +00001410 assign( carry_shift, binop(Iop_Add8, mkU8(32), mkexpr(shift_amt)) );
cerionf7da63d2004-12-09 19:04:57 +00001411 break;
cerion060c5422004-12-10 15:28:43 +00001412
cerionf7da63d2004-12-09 19:04:57 +00001413 case 0x3: // LSR(reg)
1414 assign( mux_false, mkU32(0) );
cerion19e8a612004-12-10 10:18:58 +00001415 assign( carry_shift, binop(Iop_Sub8, mkexpr(shift_amt), mkU8(1)) );
cerionf7da63d2004-12-09 19:04:57 +00001416 break;
cerion060c5422004-12-10 15:28:43 +00001417
cerionf7da63d2004-12-09 19:04:57 +00001418 case 0x5: // ASR(reg)
1419 // Rs[31] == 0 ? 0x0 : 0xFFFFFFFF
1420 assign( mux_false,
1421 IRExpr_Mux0X(
1422 binop(Iop_CmpLT32U, mkexpr(Rs), mkU32(0x80000000)),
1423 mkU32(0xFFFFFFFF), mkU32(0) ) );
1424 assign( carry_shift,
cerion19e8a612004-12-10 10:18:58 +00001425 binop(Iop_Sub8, mkexpr(shift_amt), mkU8(1)) );
cerionf7da63d2004-12-09 19:04:57 +00001426 break;
cerion060c5422004-12-10 15:28:43 +00001427
cerion19e8a612004-12-10 10:18:58 +00001428 default:
1429 vex_printf("dis_shift(arm): Reg shift: No such case: 0x%x\n", shift_op);
ceriona512a652004-12-10 11:43:10 +00001430 *decode_ok = False;
1431 return mkU32(0);
cerionf7da63d2004-12-09 19:04:57 +00001432 }
cerion19e8a612004-12-10 10:18:58 +00001433
1434 expr = IRExpr_Mux0X(
1435 binop(Iop_CmpLT32U, widenUto32(mkexpr(shift_amt)), mkU32(32)),
1436 mkexpr(mux_false),
1437 binop(op, mkexpr(Rm), mkexpr(shift_amt)) );
cerionf7da63d2004-12-09 19:04:57 +00001438
1439 // shift_amt == 0 ? old_flag_c : Rm >> x
1440 assign( *carry_out,
1441 IRExpr_Mux0X(
cerion19e8a612004-12-10 10:18:58 +00001442 binop(Iop_CmpEQ8, mkexpr(shift_amt), mkU8(0)),
cerionf7da63d2004-12-09 19:04:57 +00001443 binop(Iop_Shr32, mkexpr(Rm), mkexpr(carry_shift)),
cerion19e8a612004-12-10 10:18:58 +00001444 mkexpr(oldFlagC) ) );
cerion397f9e52004-12-15 13:04:06 +00001445
1446 DIS(buf, "R%d, %s R%d", Rm_addr, name_ARMShiftOp(shift_op, 0), Rs_addr);
ceriona70a37b2004-12-03 18:54:08 +00001447 }
1448 else { // Immediate shift
cerion397f9e52004-12-15 13:04:06 +00001449
cerion060c5422004-12-10 15:28:43 +00001450 // CAB: This right?
1451 // "the value used is the address of the current intruction plus 8"
1452 if (Rm_addr == 15 || Rn_addr == 15) { // ARM ARM A5-9
cerionf7da63d2004-12-09 19:04:57 +00001453 assign( Rm, binop(Iop_Add32, getIReg(15), mkU32(8)) );
cerion060c5422004-12-10 15:28:43 +00001454 } else {
1455 assign( Rm, getIReg(Rm_addr) );
cerionf7da63d2004-12-09 19:04:57 +00001456 }
cerionc60c01e2004-12-02 20:19:22 +00001457
cerionf7da63d2004-12-09 19:04:57 +00001458 if (shift_imm == 0) {
1459 switch (shift_op) {
1460 case 0x0: case 0x8: // LSL(imm)
cerion19e8a612004-12-10 10:18:58 +00001461 expr = mkexpr(Rm);
1462 assign( *carry_out, mkexpr(oldFlagC) );
cerionf7da63d2004-12-09 19:04:57 +00001463 break;
cerion060c5422004-12-10 15:28:43 +00001464
cerionf7da63d2004-12-09 19:04:57 +00001465 case 0x2: case 0xA: // LSR(imm)
1466 expr = mkexpr(0);
1467 // Rm >> 31: carry = R[0]
cerion19e8a612004-12-10 10:18:58 +00001468 assign( *carry_out, binop(Iop_Shr32, mkexpr(Rm), mkU8(31)) );
cerionf7da63d2004-12-09 19:04:57 +00001469 break;
cerion060c5422004-12-10 15:28:43 +00001470
cerionf7da63d2004-12-09 19:04:57 +00001471 case 0x4: case 0xC: // ASR(imm)
1472 // Rs[31] == 0 ? 0x0 : 0xFFFFFFFF
1473 expr = IRExpr_Mux0X(
1474 binop(Iop_CmpLT32U, mkexpr(Rs), mkU32(0x80000000)),
1475 mkU32(0xFFFFFFFF), mkU32(0) );
1476 // Rm >> 31: carry = R[0]
cerion19e8a612004-12-10 10:18:58 +00001477 assign( *carry_out, binop(Iop_Shr32, mkexpr(Rm), mkU8(31)) );
cerionf7da63d2004-12-09 19:04:57 +00001478 break;
cerion060c5422004-12-10 15:28:43 +00001479
cerion19e8a612004-12-10 10:18:58 +00001480 default:
1481 vex_printf("dis_shift(arm): Imm shift: No such case: 0x%x\n", shift_op);
ceriona512a652004-12-10 11:43:10 +00001482 *decode_ok = False;
1483 return mkU32(0);
cerionf7da63d2004-12-09 19:04:57 +00001484 }
cerion397f9e52004-12-15 13:04:06 +00001485 DIS(buf, "R%d", Rm_addr);
cerion19e8a612004-12-10 10:18:58 +00001486 } else {
1487 expr = binop(op, mkexpr(Rm), mkU8(shift_imm));
1488 assign( *carry_out, binop(op, mkexpr(Rm),
1489 binop(Iop_Sub32, mkU32(shift_imm), mkU32(1)) ) );
cerion397f9e52004-12-15 13:04:06 +00001490
1491 DIS(buf, "R%d, %s #%d", Rm_addr, name_ARMShiftOp(shift_op, 0), shift_imm);
cerionf7da63d2004-12-09 19:04:57 +00001492 }
ceriona70a37b2004-12-03 18:54:08 +00001493 }
ceriona70a37b2004-12-03 18:54:08 +00001494 return expr;
cerionc60c01e2004-12-02 20:19:22 +00001495}
1496
1497
1498
ceriona70a37b2004-12-03 18:54:08 +00001499
cerionc60c01e2004-12-02 20:19:22 +00001500/*
cerionf7da63d2004-12-09 19:04:57 +00001501 ARMG_CC_OP_ROR
1502 ARM ARM A5-15,16,17
cerionc60c01e2004-12-02 20:19:22 +00001503*/
cerionf7da63d2004-12-09 19:04:57 +00001504static
cerion397f9e52004-12-15 13:04:06 +00001505IRExpr* dis_rotate ( Bool* decode_ok, UInt theInstr, IRTemp* carry_out, HChar* buf )
cerionf7da63d2004-12-09 19:04:57 +00001506{
1507 UChar Rn_addr = (theInstr >> 16) & 0xF;
1508 UChar Rd_addr = (theInstr >> 12) & 0xF;
1509 UChar Rs_addr = (theInstr >> 8) & 0xF;
1510 UChar Rm_addr = (theInstr >> 0) & 0xF;
1511 UChar by_reg = (theInstr >> 4) & 0x1; // instr[4]
1512 UInt rot_imm = (theInstr >> 7) & 0x1F; // instr[11:7]
1513 IRTemp Rm = newTemp(Ity_I32);
1514 IRTemp Rs = newTemp(Ity_I32);
cerion19e8a612004-12-10 10:18:58 +00001515 IRTemp rot_amt = newTemp(Ity_I8); // Rs[7:0]
cerionf7da63d2004-12-09 19:04:57 +00001516 IRTemp oldFlagC = newTemp(Ity_I32);
1517 IRExpr* expr=0;
1518
ceriondee08672004-12-14 12:08:09 +00001519 assign( oldFlagC, binop(Iop_Shr32,
1520 mk_armg_calculate_flags_c(),
1521 mkU8(ARMG_CC_SHIFT_C)) );
cerionf7da63d2004-12-09 19:04:57 +00001522
1523 if (by_reg) { // Register rotate
cerion060c5422004-12-10 15:28:43 +00001524 assign( Rm, getIReg(Rm_addr) );
1525
1526 if (Rd_addr == 15 || Rm_addr == 15 ||
1527 Rn_addr == 15 || Rs_addr == 15) { // Unpredictable (ARM ARM A5-10)
1528 vex_printf("dis_rotate(arm): Unpredictable - Rd|Rm|Rn|Rs == R15\n");
ceriona512a652004-12-10 11:43:10 +00001529 *decode_ok = False;
1530 return mkU32(0);
cerionf7da63d2004-12-09 19:04:57 +00001531 }
1532
1533 assign( Rs, getIReg((theInstr >> 8) & 0xF) ); // instr[11:8]
cerion19e8a612004-12-10 10:18:58 +00001534 // Rs[4:0]
1535 assign( rot_amt, narrowTo(Ity_I8,
1536 binop(Iop_And32, mkexpr(Rs), mkU32(0x1F))) );
cerionf7da63d2004-12-09 19:04:57 +00001537
1538 // CAB: This right?
cerionf7da63d2004-12-09 19:04:57 +00001539 // Rs[7:0] == 0 ? oldFlagC : (Rs[4:0] == 0 ? Rm >> 31 : Rm >> rot-1 )
cerionf7da63d2004-12-09 19:04:57 +00001540 assign( *carry_out,
1541 IRExpr_Mux0X(
1542 binop(Iop_CmpNE32, mkU32(0),
1543 binop(Iop_And32, mkexpr(Rs), mkU32(0xFF))),
ceriondee08672004-12-14 12:08:09 +00001544 mkexpr(oldFlagC),
cerionf7da63d2004-12-09 19:04:57 +00001545 IRExpr_Mux0X(
cerion19e8a612004-12-10 10:18:58 +00001546 binop(Iop_CmpEQ8, mkexpr(rot_amt), mkU8(0)),
cerionf7da63d2004-12-09 19:04:57 +00001547 binop(Iop_Shr32, mkexpr(Rm),
cerion19e8a612004-12-10 10:18:58 +00001548 binop(Iop_Sub8, mkexpr(rot_amt), mkU8(1))),
cerionf7da63d2004-12-09 19:04:57 +00001549 binop(Iop_Shr32, mkexpr(Rm),
cerion19e8a612004-12-10 10:18:58 +00001550 binop(Iop_Shr32, mkexpr(Rm), mkU8(31))) ) ) );
cerionf7da63d2004-12-09 19:04:57 +00001551
1552
1553 /* expr = (dst0 >> rot_amt) | (dst0 << (wordsize-rot_amt)) */
ceriondee08672004-12-14 12:08:09 +00001554 expr = binop(Iop_Or32,
1555 binop(Iop_Shr32, mkexpr(Rm), mkexpr(rot_amt)),
1556 binop(Iop_Shl32, mkexpr(Rm),
1557 binop(Iop_Sub8, mkU8(32), mkexpr(rot_amt))));
cerion397f9e52004-12-15 13:04:06 +00001558
1559 DIS(buf, "R%d, ror R%d", Rm_addr, Rs_addr);
cerionc60c01e2004-12-02 20:19:22 +00001560 }
cerionf7da63d2004-12-09 19:04:57 +00001561 else { // Immediate rotate
1562
cerion060c5422004-12-10 15:28:43 +00001563 // CAB: This right?
1564 // "the value used is the address of the current intruction plus 8"
1565 if (Rm_addr == 15 || Rn_addr == 15) { // ARM ARM A5-9
1566 assign( Rm, binop(Iop_Add32, getIReg(15), mkU32(8)) );
1567 } else {
1568 assign( Rm, getIReg(Rm_addr) );
cerionf7da63d2004-12-09 19:04:57 +00001569 }
1570
1571 // Rm >> rot-1: carry = R[0]
1572 assign( *carry_out, binop(Iop_Shr32, mkexpr(Rm),
cerion19e8a612004-12-10 10:18:58 +00001573 binop(Iop_Sub8, mkU8(rot_imm), mkU8(1)) ) );
cerionf7da63d2004-12-09 19:04:57 +00001574
cerion060c5422004-12-10 15:28:43 +00001575 if (rot_imm == 0) { // RRX (ARM ARM A5-17)
cerionf7da63d2004-12-09 19:04:57 +00001576 // 33 bit ROR using carry flag as the 33rd bit
1577 // op = Rm >> 1, carry flag replacing vacated bit position.
1578
1579 // CAB: This right?
cerionf7da63d2004-12-09 19:04:57 +00001580 expr = binop(Iop_Or32,
ceriondee08672004-12-14 12:08:09 +00001581 binop(Iop_Shl32, mkexpr(oldFlagC), mkU8(31)),
1582 binop(Iop_Shr32, mkexpr(Rm), mkU8(1)));
cerion397f9e52004-12-15 13:04:06 +00001583 DIS(buf, "R%d, rrx", Rm_addr);
cerionf7da63d2004-12-09 19:04:57 +00001584 } else {
cerionf7da63d2004-12-09 19:04:57 +00001585 expr = binop(Iop_Or32,
ceriondee08672004-12-14 12:08:09 +00001586 binop(Iop_Shr32, mkexpr(Rm), mkU8(rot_imm)),
1587 binop(Iop_Shl32, mkexpr(Rm),
1588 binop(Iop_Sub8, mkU8(32), mkU8(rot_imm))));
cerion397f9e52004-12-15 13:04:06 +00001589
1590 DIS(buf, "R%d, ror #%d", Rm_addr, rot_imm);
cerionf7da63d2004-12-09 19:04:57 +00001591 }
1592 }
1593 return expr;
cerionc60c01e2004-12-02 20:19:22 +00001594}
1595
1596
1597
1598
cerionf7da63d2004-12-09 19:04:57 +00001599/*
1600 CAB TODO:
1601 - Not all shifts by 0 leave c_flag unchanged, so guard_expr is more difficult...
1602 assign( flags_guard, binop( Iop_CmpEQ32, mkexpr(shift_amt), mkU32(0) ) );
1603 setFlags_DEP1_DEP2_shift( ARMG_CC_OP_LSL, Rm, shift_op, flags_guard );
1604*/
cerionc60c01e2004-12-02 20:19:22 +00001605
cerionc60c01e2004-12-02 20:19:22 +00001606
cerionf7da63d2004-12-09 19:04:57 +00001607
1608
1609/* Addressing mode 1 - Data Processing ops
1610 General syntax: <opcode>{<cond>}{S} <Rd>, <Rn>, <shifter_operand>
1611 Returns <shifter_operand> expression
cerionc60c01e2004-12-02 20:19:22 +00001612*/
1613static
cerion397f9e52004-12-15 13:04:06 +00001614IRExpr* dis_shifter_op ( Bool *decode_ok, UInt theInstr, IRTemp* carry_out, HChar* buf )
cerionc60c01e2004-12-02 20:19:22 +00001615{
cerionf7da63d2004-12-09 19:04:57 +00001616 UChar is_immed = (theInstr >> 25) & 1; // immediate / register shift
1617 UChar shift_op = (theInstr >> 4) & 0xF; // second byte
ceriona70a37b2004-12-03 18:54:08 +00001618 UInt immed_8, rot_imm;
1619 UInt imm;
cerionf7da63d2004-12-09 19:04:57 +00001620 IRTemp oldFlagC = newTemp(Ity_I32);
cerionc60c01e2004-12-02 20:19:22 +00001621
ceriona70a37b2004-12-03 18:54:08 +00001622 if (is_immed) { // ARM ARM A5-2
1623 immed_8 = theInstr & 0xFF;
1624 rot_imm = (theInstr >> 8) & 0xF;
1625 imm = immed_8 << (rot_imm << 1);
cerionf7da63d2004-12-09 19:04:57 +00001626
cerionf7da63d2004-12-09 19:04:57 +00001627 if (rot_imm == 0) {
ceriondee08672004-12-14 12:08:09 +00001628 assign( oldFlagC, binop(Iop_Shr32,
1629 mk_armg_calculate_flags_c(),
1630 mkU8(ARMG_CC_SHIFT_C)) );
cerion19e8a612004-12-10 10:18:58 +00001631 assign( *carry_out, mkexpr(oldFlagC) );
cerionf7da63d2004-12-09 19:04:57 +00001632 } else {
cerion19e8a612004-12-10 10:18:58 +00001633 assign( *carry_out, binop(Iop_Shr32, mkU32(imm), mkU8(31)) );
cerionf7da63d2004-12-09 19:04:57 +00001634 }
cerion397f9e52004-12-15 13:04:06 +00001635 DIS(buf, "#%d", imm);
cerion19e8a612004-12-10 10:18:58 +00001636 return mkU32(imm);
ceriona70a37b2004-12-03 18:54:08 +00001637 } else {
cerion19e8a612004-12-10 10:18:58 +00001638
cerionf7da63d2004-12-09 19:04:57 +00001639 // We shouldn't have any 'op' with bits 4=1 and 7=1 : 1xx1
1640 switch (shift_op) {
1641 case 0x0: case 0x8: case 0x1:
1642 case 0x2: case 0xA: case 0x3:
ceriona512a652004-12-10 11:43:10 +00001643 case 0x4: case 0xC: case 0x5:
cerion397f9e52004-12-15 13:04:06 +00001644 return dis_shift( decode_ok, theInstr, carry_out, buf );
ceriona512a652004-12-10 11:43:10 +00001645
1646 case 0x6: case 0xE: case 0x7:
cerion397f9e52004-12-15 13:04:06 +00001647 return dis_rotate( decode_ok, theInstr, carry_out, buf );
ceriona512a652004-12-10 11:43:10 +00001648
cerionf7da63d2004-12-09 19:04:57 +00001649 default: // Error: Any other value shouldn't be here.
ceriona512a652004-12-10 11:43:10 +00001650 *decode_ok = False;
1651 vex_printf("dis_shifter_op(arm): shift: No such case: 0x%x\n", shift_op);
1652 return mkU32(0);
cerionf7da63d2004-12-09 19:04:57 +00001653 }
ceriona70a37b2004-12-03 18:54:08 +00001654 }
cerionc60c01e2004-12-02 20:19:22 +00001655}
1656
1657
1658
1659
1660
cerionf7da63d2004-12-09 19:04:57 +00001661/* -------------- Helper for DPI's. --------------
ceriona70a37b2004-12-03 18:54:08 +00001662*/
ceriona70a37b2004-12-03 18:54:08 +00001663static
ceriona512a652004-12-10 11:43:10 +00001664Bool dis_dataproc ( UInt theInstr )
ceriona70a37b2004-12-03 18:54:08 +00001665{
cerionf7da63d2004-12-09 19:04:57 +00001666 UChar opc = (theInstr >> 21) & 0xF;
1667 UChar set_flags = (theInstr >> 20) & 1;
1668 UChar Rn_addr = (theInstr >> 16) & 0xF;
1669 UChar Rd_addr = (theInstr >> 12) & 0xF;
1670 IRTemp Rn = newTemp(Ity_I32);
1671 IRTemp Rd = newTemp(Ity_I32);
cerion060c5422004-12-10 15:28:43 +00001672 IRTemp alu_out = newTemp(Ity_I32);
cerionf7da63d2004-12-09 19:04:57 +00001673 IRTemp shifter_op = newTemp(Ity_I32);
1674 IRTemp carry_out = newTemp(Ity_I32);
cerion397f9e52004-12-15 13:04:06 +00001675 IROp op_set_flags = ARMG_CC_OP_LOGIC;
cerion060c5422004-12-10 15:28:43 +00001676 Bool testing_instr = False;
ceriona512a652004-12-10 11:43:10 +00001677 Bool decode_ok = True;
cerion397f9e52004-12-15 13:04:06 +00001678 HChar* cond_name = name_ARMCondcode( (theInstr >> 28) & 0xF );
1679 HChar* ch_set_flags = (set_flags == 1) ? "S" : "";
1680 HChar dis_buf[50];
ceriona70a37b2004-12-03 18:54:08 +00001681
cerion397f9e52004-12-15 13:04:06 +00001682 assign( shifter_op, dis_shifter_op( &decode_ok, theInstr, &carry_out, dis_buf ) );
ceriona512a652004-12-10 11:43:10 +00001683 if (!decode_ok) return False;
1684
cerionf7da63d2004-12-09 19:04:57 +00001685 assign( Rd, getIReg(Rd_addr) );
1686 assign( Rn, getIReg(Rn_addr) );
ceriona70a37b2004-12-03 18:54:08 +00001687
cerion397f9e52004-12-15 13:04:06 +00001688
1689 switch (opc) {
1690 case 0x0: case 0x1: case 0x2: case 0x3: case 0x4:
1691 case 0xC: case 0xE:
1692 DIP("%s%s%s R%d, R%d, %s\n", name_ARMDataProcOp(opc),
1693 cond_name, ch_set_flags, Rd_addr, Rn_addr, dis_buf);
1694 break;
1695 case 0x5: case 0x6: case 0x7:
1696 // CAB: Unimplemented
1697 break;
1698 case 0x8: case 0x9: case 0xA: case 0xB:
1699 DIP("%s%s R%d, %s\n", name_ARMDataProcOp(opc),
1700 cond_name, Rn_addr, dis_buf);
1701 break;
1702 case 0xD: case 0xF:
1703 DIP("%s%s%s R%d, %s\n", name_ARMDataProcOp(opc),
1704 cond_name, ch_set_flags, Rd_addr, dis_buf);
1705 break;
1706 default:break;
1707 }
1708
1709
cerionf7da63d2004-12-09 19:04:57 +00001710 switch (opc) {
1711 case 0x0: // AND
cerion060c5422004-12-10 15:28:43 +00001712 assign( alu_out, binop(Iop_And32, getIReg(Rn_addr), mkexpr(shifter_op)) );
ceriona70a37b2004-12-03 18:54:08 +00001713 break;
1714
cerionf7da63d2004-12-09 19:04:57 +00001715 case 0x1: // EOR
cerion060c5422004-12-10 15:28:43 +00001716 assign( alu_out, binop(Iop_Xor32, getIReg(Rn_addr), mkexpr(shifter_op)) );
ceriona70a37b2004-12-03 18:54:08 +00001717 break;
1718
cerionf7da63d2004-12-09 19:04:57 +00001719 case 0x2: // SUB
cerion060c5422004-12-10 15:28:43 +00001720 assign( alu_out, binop( Iop_Sub32, getIReg(Rn_addr), mkexpr(shifter_op) ) );
cerion397f9e52004-12-15 13:04:06 +00001721 op_set_flags = ARMG_CC_OP_SUB;
ceriona70a37b2004-12-03 18:54:08 +00001722 break;
1723
cerionf7da63d2004-12-09 19:04:57 +00001724 case 0x3: // RSB
cerion060c5422004-12-10 15:28:43 +00001725 assign( alu_out, binop( Iop_Sub32, mkexpr(shifter_op), getIReg(Rn_addr) ) );
cerion397f9e52004-12-15 13:04:06 +00001726 op_set_flags = ARMG_CC_OP_SUB;
1727 /* set_flags(), below, switches the args for this case */
cerionf7da63d2004-12-09 19:04:57 +00001728 break;
1729
1730 case 0x4: // ADD
cerion060c5422004-12-10 15:28:43 +00001731 assign( alu_out, binop( Iop_Add32, getIReg(Rn_addr), mkexpr(shifter_op) ) );
cerion397f9e52004-12-15 13:04:06 +00001732 op_set_flags = ARMG_CC_OP_ADD;
cerionf7da63d2004-12-09 19:04:57 +00001733 break;
1734
cerion397f9e52004-12-15 13:04:06 +00001735 case 0x5: // ADC // CAB: Unimplemented
1736 case 0x6: // SBC // CAB: Unimplemented
1737 case 0x7: // RSC // CAB: Unimplemented
cerionf7da63d2004-12-09 19:04:57 +00001738 goto decode_failure;
1739
1740 case 0x8: // TST
1741 vassert(set_flags==1);
cerion060c5422004-12-10 15:28:43 +00001742 assign( alu_out, binop(Iop_And32, getIReg(Rn_addr), mkexpr(shifter_op)) );
1743 testing_instr = True;
cerionf7da63d2004-12-09 19:04:57 +00001744 break;
1745
1746 case 0x9: // TEQ
1747 vassert(set_flags==1);
cerion060c5422004-12-10 15:28:43 +00001748 assign( alu_out, binop(Iop_Xor32, getIReg(Rn_addr), mkexpr(shifter_op)) );
1749 testing_instr = True;
cerionf7da63d2004-12-09 19:04:57 +00001750 break;
1751
1752 case 0xA: // CMP
1753 vassert(set_flags==1);
cerion397f9e52004-12-15 13:04:06 +00001754 op_set_flags = ARMG_CC_OP_SUB;
cerion060c5422004-12-10 15:28:43 +00001755 testing_instr = True;
cerionf7da63d2004-12-09 19:04:57 +00001756 break;
1757
1758 case 0xB: // CMN
1759 vassert(set_flags==1);
cerion397f9e52004-12-15 13:04:06 +00001760 op_set_flags = ARMG_CC_OP_ADD;
cerion060c5422004-12-10 15:28:43 +00001761 testing_instr = True;
cerionf7da63d2004-12-09 19:04:57 +00001762 break;
1763
1764 case 0xC: // ORR
cerion060c5422004-12-10 15:28:43 +00001765 assign( alu_out, binop(Iop_Or32, getIReg(Rn_addr), mkexpr(shifter_op)) );
cerionf7da63d2004-12-09 19:04:57 +00001766 break;
1767
1768 case 0xD: // MOV
cerion060c5422004-12-10 15:28:43 +00001769 assign( alu_out, mkexpr(shifter_op) );
cerionf7da63d2004-12-09 19:04:57 +00001770 break;
1771
1772 case 0xE: // BIC
cerion060c5422004-12-10 15:28:43 +00001773 assign( alu_out, binop(Iop_And32, getIReg(Rn_addr),
1774 unop( Iop_Not32, mkexpr(shifter_op))) );
cerionf7da63d2004-12-09 19:04:57 +00001775 break;
1776
1777 case 0xF: // MVN
cerion060c5422004-12-10 15:28:43 +00001778 assign( alu_out, unop(Iop_Not32, mkexpr(shifter_op)) );
ceriona70a37b2004-12-03 18:54:08 +00001779 break;
1780
1781 default:
cerionf7da63d2004-12-09 19:04:57 +00001782 decode_failure:
ceriona512a652004-12-10 11:43:10 +00001783 vex_printf("dis_dataproc(arm): unhandled opcode: 0x%x\n", opc);
1784 return False;
ceriona70a37b2004-12-03 18:54:08 +00001785 }
1786
cerion060c5422004-12-10 15:28:43 +00001787 if (!testing_instr) {
1788 if ( Rd_addr == 15) { // dest reg == PC
cerionf7da63d2004-12-09 19:04:57 +00001789 // CPSR = SPSR: Unpredictable in User | System mode (no SPSR!)
cerion060c5422004-12-10 15:28:43 +00001790 // Unpredictable - We're only supporting user mode...
1791 vex_printf("dis_dataproc(arm): Unpredictable - Rd_addr==15\n");
1792 return False;
1793 }
1794 putIReg( Rd_addr, mkexpr(alu_out) );
1795 }
1796
1797 if (set_flags) {
cerion397f9e52004-12-15 13:04:06 +00001798 if (op_set_flags == ARMG_CC_OP_LOGIC) {
1799 setFlags_DEP1_DEP2( op_set_flags, alu_out, carry_out );
ceriona70a37b2004-12-03 18:54:08 +00001800 } else {
cerion397f9e52004-12-15 13:04:06 +00001801 if (opc == 0x3) {
1802 setFlags_DEP1_DEP2( op_set_flags, shifter_op, Rn );
1803 } else {
1804 setFlags_DEP1_DEP2( op_set_flags, Rn, shifter_op );
1805 }
ceriona70a37b2004-12-03 18:54:08 +00001806 }
1807 }
ceriona512a652004-12-10 11:43:10 +00001808 return decode_ok;
ceriona70a37b2004-12-03 18:54:08 +00001809}
1810
1811
1812
cerionf7da63d2004-12-09 19:04:57 +00001813
1814/* -------------- Helper for Branch. --------------
1815*/
1816static
1817void dis_branch ( UInt theInstr )
1818{
1819 UChar link = (theInstr >> 24) & 1;
cerion19e8a612004-12-10 10:18:58 +00001820 UInt signed_immed_24 = theInstr & 0xFFFFFF;
1821 UInt branch_offset;
cerionf7da63d2004-12-09 19:04:57 +00001822 IRTemp addr = newTemp(Ity_I32);
1823
cerion397f9e52004-12-15 13:04:06 +00001824 DIP("b%c%s 0x%x\n", link ? 'l' : ' ',
1825 name_ARMCondcode( (theInstr >> 28) & 0xF ),
1826 signed_immed_24);
1827
cerionf7da63d2004-12-09 19:04:57 +00001828 if (link) { // LR (R14) = addr of instr after branch instr
cerion19e8a612004-12-10 10:18:58 +00001829 assign( addr, binop(Iop_Add32, getIReg(15), mkU32(4)) );
1830 putIReg( 14, mkexpr(addr) );
cerionf7da63d2004-12-09 19:04:57 +00001831 }
1832
1833 // PC = PC + (SignExtend(signed_immed_24) << 2)
cerion19e8a612004-12-10 10:18:58 +00001834 branch_offset = extend_s_24to32( signed_immed_24 ) << 2;
cerionf7da63d2004-12-09 19:04:57 +00001835 putIReg( 15, binop(Iop_Add32, getIReg(15), mkU32(branch_offset)) );
cerion19e8a612004-12-10 10:18:58 +00001836
1837 irbb->jumpkind = link ? Ijk_Call : Ijk_Boring;
1838 irbb->next = mkU32(branch_offset);
cerionf7da63d2004-12-09 19:04:57 +00001839}
1840
1841
cerionf7da63d2004-12-09 19:04:57 +00001842
1843
ceriona70a37b2004-12-03 18:54:08 +00001844
1845
1846
1847
1848
1849
1850
cerionc60c01e2004-12-02 20:19:22 +00001851
1852
1853
sewardjc2c87162004-11-25 13:07:02 +00001854
1855
1856/*------------------------------------------------------------*/
1857/*--- Disassemble a single instruction ---*/
1858/*------------------------------------------------------------*/
1859
1860/* Disassemble a single instruction into IR. The instruction
1861 is located in host memory at &guest_code[delta].
1862 Set *size to be the size of the instruction.
1863 If the returned value is Dis_Resteer,
1864 the next guest address is assigned to *whereNext. If resteerOK
1865 is False, disInstr may not return Dis_Resteer. */
1866
1867static DisResult disInstr ( /*IN*/ Bool resteerOK,
1868 /*IN*/ Bool (*resteerOkFn) ( Addr64 ),
1869 /*IN*/ UInt delta,
1870 /*OUT*/ UInt* size,
1871 /*OUT*/ Addr64* whereNext )
1872{
sewardjfb183d22004-12-03 11:55:29 +00001873 // IRType ty;
1874 // IRTemp addr, t1, t2;
1875 // Int alen;
cerionf7da63d2004-12-09 19:04:57 +00001876 UChar opc1, opc2, opc_tmp; //, modrm, abyte;
cerionc60c01e2004-12-02 20:19:22 +00001877 ARMCondcode cond;
sewardjfb183d22004-12-03 11:55:29 +00001878 // UInt d32;
1879 // UChar dis_buf[50];
1880 // Int am_sz, d_sz;
sewardjc2c87162004-11-25 13:07:02 +00001881 DisResult whatNext = Dis_Continue;
1882 UInt theInstr;
ceriona512a652004-12-10 11:43:10 +00001883
sewardjc2c87162004-11-25 13:07:02 +00001884
sewardjc2c87162004-11-25 13:07:02 +00001885 /* At least this is simple on ARM: insns are all 4 bytes long, and
1886 4-aligned. So just fish the whole thing out of memory right now
1887 and have done. */
1888
1889 /* We will set *size to 4 if the insn is successfully decoded.
1890 Setting it to 0 by default makes bbToIR_ARM abort if we fail the
1891 decode. */
1892 *size = 0;
1893
1894 theInstr = *(UInt*)(&guest_code[delta]);
1895
cerion397f9e52004-12-15 13:04:06 +00001896// vex_printf("START: 0x%x, %,b\n", theInstr, theInstr );
cerionc60c01e2004-12-02 20:19:22 +00001897
sewardjc2c87162004-11-25 13:07:02 +00001898 DIP("\t0x%x: ", guest_pc_bbstart+delta);
1899
cerionc60c01e2004-12-02 20:19:22 +00001900
1901
sewardjc2c87162004-11-25 13:07:02 +00001902 // TODO: fix the client-request stuff, else nothing will work
cerionc60c01e2004-12-02 20:19:22 +00001903
sewardjc2c87162004-11-25 13:07:02 +00001904 /* Spot the client-request magic sequence. */
cerionc60c01e2004-12-02 20:19:22 +00001905 // Essentially a v. unlikely sequence of noops that we can catch
sewardjc2c87162004-11-25 13:07:02 +00001906 {
sewardjcca71942004-12-02 23:35:18 +00001907 UInt* code = (UInt*)(guest_code + delta);
cerionf7da63d2004-12-09 19:04:57 +00001908
1909 // CAB: easy way to rotate left?
1910
1911 /* Spot this:
cerionc60c01e2004-12-02 20:19:22 +00001912 E1A00EE0 mov r0, r0, ror #29
1913 E1A001E0 mov r0, r0, ror #3
1914 E1A00DE0 mov r0, r0, ror #27
1915 E1A002E0 mov r0, r0, ror #5
1916 E1A006E0 mov r0, r0, ror #13
1917 E1A009E0 mov r0, r0, ror #19
sewardjc2c87162004-11-25 13:07:02 +00001918 */
sewardj6cd91632004-12-02 23:36:20 +00001919 /* I suspect these will have to be turned the other way round to
1920 work on little-endian arm. */
sewardjcca71942004-12-02 23:35:18 +00001921 if (code[0] == 0xE1A00EE0 &&
1922 code[1] == 0xE1A001E0 &&
1923 code[2] == 0xE1A00DE0 &&
1924 code[3] == 0xE1A002E0 &&
1925 code[4] == 0xE1A006E0 &&
1926 code[5] == 0xE1A009E0) {
1927
1928 // uh ... I'll figure this out later. possibly r0 = client_request(r0) */
cerionc60c01e2004-12-02 20:19:22 +00001929 DIP("?CAB? = client_request ( ?CAB? )\n");
1930
sewardjcca71942004-12-02 23:35:18 +00001931 *size = 24;
cerionc60c01e2004-12-02 20:19:22 +00001932
cerionc60c01e2004-12-02 20:19:22 +00001933 irbb->next = mkU32(guest_pc_bbstart+delta);
1934 irbb->jumpkind = Ijk_ClientReq;
1935
sewardjc2c87162004-11-25 13:07:02 +00001936 whatNext = Dis_StopHere;
1937 goto decode_success;
1938 }
1939 }
cerionc60c01e2004-12-02 20:19:22 +00001940
1941
1942
1943
cerionf7da63d2004-12-09 19:04:57 +00001944
cerionc60c01e2004-12-02 20:19:22 +00001945 /*
1946 Deal with condition first
1947 */
1948 cond = (theInstr >> 28) & 0xF; /* opcode: bits 31:28 */
cerion397f9e52004-12-15 13:04:06 +00001949// vex_printf("\ndisInstr(arm): cond: 0x%x, %b\n", cond, cond );
cerionfd7474a2004-12-03 11:16:42 +00001950
cerionc60c01e2004-12-02 20:19:22 +00001951 switch (cond) {
1952 case 0xF: // => Illegal instruction prior to v5 (see ARM ARM A3-5)
1953 vex_printf("disInstr(arm): illegal condition\n");
1954 goto decode_failure;
1955
1956 case 0xE: // => Unconditional: go translate the instruction
1957 break;
1958
cerionf7da63d2004-12-09 19:04:57 +00001959 default:
1960 // => Valid condition: translate the condition test first
cerionc60c01e2004-12-02 20:19:22 +00001961 stmt( IRStmt_Exit( mk_armg_calculate_condition(cond),
1962 Ijk_Boring,
1963 IRConst_U32(guest_pc_bbstart+delta+4) ) );
cerionf7da63d2004-12-09 19:04:57 +00001964 //irbb->next = mkU32(guest_pc_bbstart+delta+4);
1965 //irbb->jumpkind = Ijk_Boring;
cerionc60c01e2004-12-02 20:19:22 +00001966 }
1967
1968
1969
cerionf7da63d2004-12-09 19:04:57 +00001970 /* Primary opcode is roughly bits 27:20 (ARM ARM(v2) A3-2)
1971 secondary opcode is bits 4:0 */
1972 opc1 = (theInstr >> 20) & 0xFF; /* opcode1: bits 27:20 */
1973 opc2 = (theInstr >> 4 ) & 0xF; /* opcode2: bits 7:4 */
cerion397f9e52004-12-15 13:04:06 +00001974// vex_printf("disInstr(arm): opcode1: 0x%2x, %,09b\n", opc1, opc1 );
1975// vex_printf("disInstr(arm): opcode2: 0x%02x, %,04b\n", opc2, opc2 );
cerionc60c01e2004-12-02 20:19:22 +00001976
cerionf7da63d2004-12-09 19:04:57 +00001977 switch (opc1 >> 4) { // instr[27:24]
1978 case 0x0:
1979 case 0x1:
1980 /*
1981 Multiplies, extra load/store instructions: ARM ARM A3-3
cerionc60c01e2004-12-02 20:19:22 +00001982 */
cerionf7da63d2004-12-09 19:04:57 +00001983 if ( (opc1 & 0xE0) == 0x0 && (opc2 & 0x9) == 0x9 ) { // 000xxxxx && 1xx1
1984 if (opc2 == 0x9) {
1985 if ((opc1 & 0x1C) == 0x00) { // multiply (accumulate)
1986 goto decode_failure;
1987 }
1988 if ((opc1 & 0x18) == 0x08) { // multiply (accumulate) long
1989 goto decode_failure;
1990 }
1991 if ((opc1 & 0x1B) == 0x10) { // swap/swap byte
1992 goto decode_failure;
1993 }
1994 }
1995 if ( opc2 == 0xB ) {
1996 if ((opc1 & 0x04) == 0x00) { // load/store 1/2word reg offset
1997 goto decode_failure;
1998 } else { // load/store 1/2word imm offset
1999 goto decode_failure;
2000 }
2001 }
2002 if ((opc2 & 0xD) == 0xD) {
2003 if ((opc1 & 0x05) == 0x00) { // load/store 2 words reg offset
2004 goto decode_failure;
2005 }
2006 if ((opc1 & 0x05) == 0x04) { // load/store 2 words imm offset
2007 goto decode_failure;
2008 }
2009 if ((opc1 & 0x05) == 0x01) { // load/store signed 1/2word/byte reg offset
2010 goto decode_failure;
2011 }
2012 if ((opc1 & 0x05) == 0x05) { // load/store signed 1/2word/byte imm offset
2013 goto decode_failure;
2014 }
2015 }
2016 } /* endif: Multiplies, extra load/store... */
cerionc60c01e2004-12-02 20:19:22 +00002017
cerionf7da63d2004-12-09 19:04:57 +00002018 /*
2019 'Misc' Instructions: ARM ARM A3-4
2020 */
2021 if ((opc1 & 0xF9) == 0x10) { // 0001 0xx0
2022 opc_tmp = (opc1 >> 1) & 0x3;
2023 switch (opc2) {
2024 case 0x0:
2025 if ((opc_tmp & 0x1) == 0x0) { // move stat reg -> reg
2026 goto decode_failure;
2027 } else { // move reg -> stat reg
2028 goto decode_failure;
2029 }
cerionc60c01e2004-12-02 20:19:22 +00002030
cerionf7da63d2004-12-09 19:04:57 +00002031 case 0x1:
2032 if (opc_tmp == 0x1) { // branch/exchange instr set
2033 goto decode_failure;
2034 }
2035 if (opc_tmp == 0x3) { // count leading zeros
2036 goto decode_failure;
2037 }
2038 break;
2039
2040 case 0x3:
2041 if (opc_tmp == 0x1) { // branch & link/exchange instr set
2042 goto decode_failure;
2043 }
2044 break;
2045
2046 case 0x5: // enhanced dsp add/subtracts
2047 goto decode_failure;
2048
2049 case 0x7:
2050 if (opc_tmp == 0x1) { // software breakpoint
cerion060c5422004-12-10 15:28:43 +00002051 if (cond != 0xE) { // Unpredictable - ARM ARM A3-4
2052 vex_printf("disInstr(arm): Unpredictable instruction\n");
2053 goto decode_failure;
2054 }
cerionf7da63d2004-12-09 19:04:57 +00002055 goto decode_failure;
2056 }
2057 break;
2058
2059 case 0x8: case 0x9: case 0xA: // enhanced dsp multiplies
2060 case 0xB: case 0xC: case 0xD: case 0xE:
2061 goto decode_failure;
2062
2063 default: break;
2064 }
2065 } /* endif: 'Misc' Instructions... */
2066 // fall through...
2067
2068 case 0x2:
2069 case 0x3:
ceriona512a652004-12-10 11:43:10 +00002070 if ((opc1 & 0xFB) == 0x30) goto decode_failure; // Undefined - ARM ARM A3-2
cerionf7da63d2004-12-09 19:04:57 +00002071
2072 /*
2073 A lonely 'MOV imm to status reg':
cerionc60c01e2004-12-02 20:19:22 +00002074 */
cerionf7da63d2004-12-09 19:04:57 +00002075 if ((opc1 & 0xFB) == 0x32) { // 0011 0x10
2076 goto decode_failure;
2077 }
cerionc60c01e2004-12-02 20:19:22 +00002078
cerionf7da63d2004-12-09 19:04:57 +00002079 /*
2080 Data Processing Instructions
cerion060c5422004-12-10 15:28:43 +00002081 (if we get here, it's a dpi)
cerionc60c01e2004-12-02 20:19:22 +00002082 */
ceriona512a652004-12-10 11:43:10 +00002083 if (!dis_dataproc( theInstr )) { goto decode_failure; }
cerionf7da63d2004-12-09 19:04:57 +00002084 break;
2085
2086
2087 /*
2088 Load/Store word | unsigned byte
2089 */
2090 case 0x6: case 0x7: // LOAD/STORE reg offset
ceriona512a652004-12-10 11:43:10 +00002091 if ((opc2 & 0x1) == 0x1) goto decode_failure; // Undefined - ARM ARM A3-2
2092
cerionf7da63d2004-12-09 19:04:57 +00002093 case 0x4: case 0x5: // LOAD/STORE imm offset
ceriona512a652004-12-10 11:43:10 +00002094 if (!dis_loadstore_w_ub(theInstr)) { goto decode_failure; }
cerionf7da63d2004-12-09 19:04:57 +00002095 break;
2096
2097 /*
2098 Load/Store multiple
2099 */
2100 case 0x8: case 0x9:
ceriona512a652004-12-10 11:43:10 +00002101 if (!dis_loadstore_mult(theInstr)) { goto decode_failure; }
cerionf7da63d2004-12-09 19:04:57 +00002102 break;
2103
2104
2105 /*
2106 Branch, Branch and Link
2107 */
2108 case 0xA: case 0xB: // B, BL
2109 // B(L): L=1 => return address stored in link register (R14)
cerionf7da63d2004-12-09 19:04:57 +00002110 dis_branch(theInstr);
cerion19e8a612004-12-10 10:18:58 +00002111 whatNext = Dis_StopHere;
cerionf7da63d2004-12-09 19:04:57 +00002112 break;
2113
2114
2115 /*
2116 Co-processor instructions
2117 */
2118 case 0xC: case 0xD: // co-pro load/store & double reg trxfrs
cerionc60c01e2004-12-02 20:19:22 +00002119 goto decode_failure;
cerionf7da63d2004-12-09 19:04:57 +00002120
2121 case 0xE:
2122 if ((opc2 & 0x1) == 0x0) { // co-pro data processing
2123 goto decode_failure;
2124 } else { // co-pro register transfers
2125 goto decode_failure;
2126 }
cerionc60c01e2004-12-02 20:19:22 +00002127
ceriona70a37b2004-12-03 18:54:08 +00002128
cerionf7da63d2004-12-09 19:04:57 +00002129 /*
2130 Software Interrupt
2131 */
2132 case 0xF: // swi
2133 goto decode_failure;
ceriona70a37b2004-12-03 18:54:08 +00002134
sewardjc2c87162004-11-25 13:07:02 +00002135 default:
2136 decode_failure:
2137 /* All decode failures end up here. */
2138 vex_printf("disInstr(arm): unhandled instruction: "
cerionfd7474a2004-12-03 11:16:42 +00002139 "0x%x\n", theInstr);
sewardjc2c87162004-11-25 13:07:02 +00002140 vpanic("armToIR: unimplemented insn");
2141
2142 } /* switch (opc) for the main (primary) opcode switch. */
2143
2144 decode_success:
2145 /* All decode successes end up here. */
cerion397f9e52004-12-15 13:04:06 +00002146// vex_printf("disInstr(arm): success");
sewardjc2c87162004-11-25 13:07:02 +00002147 DIP("\n");
2148
2149 *size = 4;
2150 return whatNext;
2151}
2152
2153#undef DIP
2154#undef DIS
2155
2156/*--------------------------------------------------------------------*/
2157/*--- end guest-arm/toIR.c ---*/
2158/*--------------------------------------------------------------------*/