blob: f5c68d125417c16dfbd3b874edcf44a02c0f8538 [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
295/* Bomb out if we can't handle something. */
296__attribute__ ((noreturn))
297static void unimplemented ( Char* str )
298{
299 vex_printf("armToIR: unimplemented feature\n");
300 vpanic(str);
301}
302
303/* Various simple conversions */
304
305static UInt extend_s_8to32( UInt x )
306{
307 return (UInt)((((Int)x) << 24) >> 24);
308}
309
310static UInt extend_s_16to32 ( UInt x )
311{
312 return (UInt)((((Int)x) << 16) >> 16);
313}
314
cerion19e8a612004-12-10 10:18:58 +0000315static UInt extend_s_24to32 ( UInt x )
316{
317 return (UInt)((((Int)x) << 8) >> 8);
318}
319
sewardjc2c87162004-11-25 13:07:02 +0000320/* Fetch a byte from the guest insn stream. */
321static UChar getIByte ( UInt delta )
322{
323 return guest_code[delta];
324}
325
326/* Get a 8/16/32-bit unsigned value out of the insn stream. */
327
328static UInt getUChar ( UInt delta )
329{
330 UInt v = guest_code[delta+0];
331 return v & 0xFF;
332}
333
334static UInt getUDisp16 ( UInt delta )
335{
336 UInt v = guest_code[delta+1]; v <<= 8;
337 v |= guest_code[delta+0];
338 return v & 0xFFFF;
339}
340
341static UInt getUDisp32 ( UInt delta )
342{
343 UInt v = guest_code[delta+3]; v <<= 8;
344 v |= guest_code[delta+2]; v <<= 8;
345 v |= guest_code[delta+1]; v <<= 8;
346 v |= guest_code[delta+0];
347 return v;
348}
349
350static UInt getUDisp ( Int size, UInt delta )
351{
352 switch (size) {
353 case 4: return getUDisp32(delta);
354 case 2: return getUDisp16(delta);
355 case 1: return getUChar(delta);
cerionc60c01e2004-12-02 20:19:22 +0000356 default: vpanic("getUDisp(ARM)");
sewardjc2c87162004-11-25 13:07:02 +0000357 }
358 return 0; /*notreached*/
359}
360
361
362/* Get a byte value out of the insn stream and sign-extend to 32
363 bits. */
364static UInt getSDisp8 ( UInt delta )
365{
366 return extend_s_8to32( (UInt) (guest_code[delta]) );
367}
368
369static UInt getSDisp16 ( UInt delta0 )
370{
371 UChar* eip = (UChar*)(&guest_code[delta0]);
372 UInt d = *eip++;
373 d |= ((*eip++) << 8);
374 return extend_s_16to32(d);
375}
376
377static UInt getSDisp ( Int size, UInt delta )
378{
379 switch (size) {
380 case 4: return getUDisp32(delta);
381 case 2: return getSDisp16(delta);
382 case 1: return getSDisp8(delta);
cerionc60c01e2004-12-02 20:19:22 +0000383 default: vpanic("getSDisp(ARM)");
sewardjc2c87162004-11-25 13:07:02 +0000384 }
385 return 0; /*notreached*/
386}
387
388
389/*------------------------------------------------------------*/
390/*--- Helpers for constructing IR. ---*/
391/*------------------------------------------------------------*/
392
393/* Create a 1/2/4 byte read of an x86 integer registers. For 16/8 bit
394 register references, we need to take the host endianness into
395 account. Supplied value is 0 .. 7 and in the Intel instruction
396 encoding. */
397
398static IRType szToITy ( Int n )
399{
400 switch (n) {
401 case 1: return Ity_I8;
402 case 2: return Ity_I16;
403 case 4: return Ity_I32;
cerionc60c01e2004-12-02 20:19:22 +0000404 default: vpanic("szToITy(ARM)");
sewardjc2c87162004-11-25 13:07:02 +0000405 }
406}
407
408static Int integerGuestRegOffset ( UInt archreg )
409{
410 vassert(archreg < 16);
411
412 vassert(!host_is_bigendian); //TODO: is this necessary?
sewardjcca71942004-12-02 23:35:18 +0000413 // jrs: probably not; only matters if we reference sub-parts
414 // of the arm registers, but that isn't the case
sewardjc2c87162004-11-25 13:07:02 +0000415 switch (archreg) {
cerionc60c01e2004-12-02 20:19:22 +0000416 case 0: return offsetof(VexGuestARMState, guest_R0);
417 case 1: return offsetof(VexGuestARMState, guest_R1);
418 case 2: return offsetof(VexGuestARMState, guest_R2);
419 case 3: return offsetof(VexGuestARMState, guest_R3);
420 case 4: return offsetof(VexGuestARMState, guest_R4);
421 case 5: return offsetof(VexGuestARMState, guest_R5);
422 case 6: return offsetof(VexGuestARMState, guest_R6);
423 case 7: return offsetof(VexGuestARMState, guest_R7);
424 case 8: return offsetof(VexGuestARMState, guest_R8);
425 case 9: return offsetof(VexGuestARMState, guest_R9);
426 case 10: return offsetof(VexGuestARMState,guest_R10);
427 case 11: return offsetof(VexGuestARMState,guest_R11);
428 case 12: return offsetof(VexGuestARMState,guest_R12);
429 case 13: return offsetof(VexGuestARMState,guest_R13);
430 case 14: return offsetof(VexGuestARMState,guest_R14);
sewardjc2c87162004-11-25 13:07:02 +0000431 case 15: return offsetof(VexGuestARMState,guest_R15);
432 }
433
cerionc60c01e2004-12-02 20:19:22 +0000434 vpanic("integerGuestRegOffset(arm,le)"); /*notreached*/
sewardjc2c87162004-11-25 13:07:02 +0000435}
436
437static IRExpr* getIReg ( UInt archreg )
438{
439 vassert(archreg < 16);
440 return IRExpr_Get( integerGuestRegOffset(archreg), Ity_I32 );
441}
442
443/* Ditto, but write to a reg instead. */
444static void putIReg ( UInt archreg, IRExpr* e )
445{
446 vassert(archreg < 16);
447 stmt( IRStmt_Put(integerGuestRegOffset(archreg), e) );
448}
449
450static void assign ( IRTemp dst, IRExpr* e )
451{
452 stmt( IRStmt_Tmp(dst, e) );
453}
454
455static void storeLE ( IRExpr* addr, IRExpr* data )
456{
457 stmt( IRStmt_STle(addr,data) );
458}
459
460static IRExpr* unop ( IROp op, IRExpr* a )
461{
462 return IRExpr_Unop(op, a);
463}
464
465static IRExpr* binop ( IROp op, IRExpr* a1, IRExpr* a2 )
466{
467 return IRExpr_Binop(op, a1, a2);
468}
469
470static IRExpr* mkexpr ( IRTemp tmp )
471{
472 return IRExpr_Tmp(tmp);
473}
474
475static IRExpr* mkU8 ( UInt i )
476{
477 vassert(i < 256);
478 return IRExpr_Const(IRConst_U8(i));
479}
480
481static IRExpr* mkU16 ( UInt i )
482{
483 vassert(i < 65536);
484 return IRExpr_Const(IRConst_U16(i));
485}
486
487static IRExpr* mkU32 ( UInt i )
488{
489 return IRExpr_Const(IRConst_U32(i));
490}
491
492static IRExpr* mkU ( IRType ty, UInt i )
493{
494 if (ty == Ity_I8) return mkU8(i);
495 if (ty == Ity_I16) return mkU16(i);
496 if (ty == Ity_I32) return mkU32(i);
497 /* If this panics, it usually means you passed a size (1,2,4)
498 value as the IRType, rather than a real IRType. */
cerionc60c01e2004-12-02 20:19:22 +0000499 vpanic("mkU(ARM)");
sewardjc2c87162004-11-25 13:07:02 +0000500}
501
502static IRExpr* loadLE ( IRType ty, IRExpr* data )
503{
504 return IRExpr_LDle(ty,data);
505}
506
507static IROp mkSizedOp ( IRType ty, IROp op8 )
508{
509 Int adj;
510 vassert(ty == Ity_I8 || ty == Ity_I16 || ty == Ity_I32);
511 vassert(op8 == Iop_Add8 || op8 == Iop_Sub8
512 || op8 == Iop_Mul8
513 || op8 == Iop_Or8 || op8 == Iop_And8 || op8 == Iop_Xor8
514 || op8 == Iop_Shl8 || op8 == Iop_Shr8 || op8 == Iop_Sar8
515 || op8 == Iop_CmpEQ8 || op8 == Iop_CmpNE8
516 || op8 == Iop_Not8 );
517 adj = ty==Ity_I8 ? 0 : (ty==Ity_I16 ? 1 : 2);
518 return adj + op8;
519}
520
521static IROp mkWidenOp ( Int szSmall, Int szBig, Bool signd )
522{
523 if (szSmall == 1 && szBig == 4) {
524 return signd ? Iop_8Sto32 : Iop_8Uto32;
525 }
526 if (szSmall == 1 && szBig == 2) {
527 return signd ? Iop_8Sto16 : Iop_8Uto16;
528 }
529 if (szSmall == 2 && szBig == 4) {
530 return signd ? Iop_16Sto32 : Iop_16Uto32;
531 }
cerionc60c01e2004-12-02 20:19:22 +0000532 vpanic("mkWidenOp(ARM,guest)");
sewardjc2c87162004-11-25 13:07:02 +0000533}
534
535
cerionc60c01e2004-12-02 20:19:22 +0000536
537
538
539
540
541
542
543
544
545
546
547
548
sewardjc2c87162004-11-25 13:07:02 +0000549/*------------------------------------------------------------*/
cerionf7da63d2004-12-09 19:04:57 +0000550/*--- Helpers for %flags. ---*/
sewardjc2c87162004-11-25 13:07:02 +0000551/*------------------------------------------------------------*/
cerionc60c01e2004-12-02 20:19:22 +0000552
sewardjc2c87162004-11-25 13:07:02 +0000553/* -------------- Evaluating the flags-thunk. -------------- */
554
cerionf7da63d2004-12-09 19:04:57 +0000555/* Build IR to calculate all the flags from stored
cerionc60c01e2004-12-02 20:19:22 +0000556 CC_OP/CC_DEP1/CC_DEP2/CC_NDEP.
557 Returns an expression :: Ity_I32. */
558static IRExpr* mk_armg_calculate_flags_all ( void )
sewardjc2c87162004-11-25 13:07:02 +0000559{
560 IRExpr** args
cerionc60c01e2004-12-02 20:19:22 +0000561 = mkIRExprVec_3( IRExpr_Get(OFFB_CC_OP, Ity_I32),
sewardjc2c87162004-11-25 13:07:02 +0000562 IRExpr_Get(OFFB_CC_DEP1, Ity_I32),
cerionc60c01e2004-12-02 20:19:22 +0000563 IRExpr_Get(OFFB_CC_DEP2, Ity_I32) );
sewardjc2c87162004-11-25 13:07:02 +0000564 IRExpr* call
565 = mkIRExprCCall(
566 Ity_I32,
567 0/*regparm*/,
cerionc60c01e2004-12-02 20:19:22 +0000568 "armg_calculate_flags_all", &armg_calculate_flags_all,
sewardjc2c87162004-11-25 13:07:02 +0000569 args
570 );
cerionc60c01e2004-12-02 20:19:22 +0000571
572 /* Exclude OP from definedness checking. We're only
sewardjc2c87162004-11-25 13:07:02 +0000573 interested in DEP1 and DEP2. */
cerionc60c01e2004-12-02 20:19:22 +0000574 call->Iex.CCall.cee->mcx_mask = 1;
sewardjc2c87162004-11-25 13:07:02 +0000575 return call;
576}
577
cerionc60c01e2004-12-02 20:19:22 +0000578
cerionf7da63d2004-12-09 19:04:57 +0000579/* Build IR to calculate just the carry flag from stored
580 CC_OP/CC_DEP1/CC_DEP2/CC_NDEP. Returns an expression :: Ity_I32. */
581static IRExpr* mk_armg_calculate_flags_c ( void )
582{
583 IRExpr** args
584 = mkIRExprVec_3( IRExpr_Get(OFFB_CC_OP, Ity_I32),
585 IRExpr_Get(OFFB_CC_DEP1, Ity_I32),
586 IRExpr_Get(OFFB_CC_DEP2, Ity_I32) );
587 IRExpr* call
588 = mkIRExprCCall(
589 Ity_I32,
590 0/*regparm*/,
591 "armg_calculate_flags_c", &armg_calculate_flags_c,
592 args
593 );
594 /* Exclude OP from definedness checking. We're only
595 interested in DEP1 and DEP2. */
596 call->Iex.CCall.cee->mcx_mask = 1;
597 return call;
598}
599
cerionc60c01e2004-12-02 20:19:22 +0000600
sewardjc2c87162004-11-25 13:07:02 +0000601/* Build IR to calculate some particular condition from stored
cerionc60c01e2004-12-02 20:19:22 +0000602 CC_OP/CC_DEP1/CC_DEP2. Returns an expression
sewardjcca71942004-12-02 23:35:18 +0000603 of type Ity_I1.
cerionc60c01e2004-12-02 20:19:22 +0000604*/
605static IRExpr* mk_armg_calculate_condition ( ARMCondcode cond )
sewardjc2c87162004-11-25 13:07:02 +0000606{
607 IRExpr** args
cerionc60c01e2004-12-02 20:19:22 +0000608 = mkIRExprVec_4( mkU32(cond),
sewardjc2c87162004-11-25 13:07:02 +0000609 IRExpr_Get(OFFB_CC_OP, Ity_I32),
610 IRExpr_Get(OFFB_CC_DEP1, Ity_I32),
cerionc60c01e2004-12-02 20:19:22 +0000611 IRExpr_Get(OFFB_CC_DEP2, Ity_I32) );
sewardjc2c87162004-11-25 13:07:02 +0000612 IRExpr* call
613 = mkIRExprCCall(
614 Ity_I32,
615 0/*regparm*/,
cerionc60c01e2004-12-02 20:19:22 +0000616 "armg_calculate_condition", &armg_calculate_condition,
sewardjc2c87162004-11-25 13:07:02 +0000617 args
618 );
cerionc60c01e2004-12-02 20:19:22 +0000619
620 /* Exclude the requested condition and OP from definedness
sewardjc2c87162004-11-25 13:07:02 +0000621 checking. We're only interested in DEP1 and DEP2. */
cerionc60c01e2004-12-02 20:19:22 +0000622 call->Iex.CCall.cee->mcx_mask = (1<<0) | (1<<1);
sewardjc2c87162004-11-25 13:07:02 +0000623 return unop(Iop_32to1, call);
624}
625
cerionc60c01e2004-12-02 20:19:22 +0000626
627
cerionc60c01e2004-12-02 20:19:22 +0000628
629
sewardjc2c87162004-11-25 13:07:02 +0000630
631
632/* -------------- Building the flags-thunk. -------------- */
633
634/* The machinery in this section builds the flag-thunk following a
635 flag-setting operation. Hence the various setFlags_* functions.
636*/
637
638static Bool isAddSub ( IROp op8 )
639{
640 return op8 == Iop_Add8 || op8 == Iop_Sub8;
641}
642
643static Bool isLogic ( IROp op8 )
644{
645 return op8 == Iop_And8 || op8 == Iop_Or8 || op8 == Iop_Xor8;
646}
647
648/* U-widen 8/16/32 bit int expr to 32. */
649static IRExpr* widenUto32 ( IRExpr* e )
650{
651 switch (typeOfIRExpr(irbb->tyenv,e)) {
652 case Ity_I32: return e;
653 case Ity_I16: return unop(Iop_16Uto32,e);
654 case Ity_I8: return unop(Iop_8Uto32,e);
655 default: vpanic("widenUto32");
656 }
657}
658
659/* S-widen 8/16/32 bit int expr to 32. */
660static IRExpr* widenSto32 ( IRExpr* e )
661{
662 switch (typeOfIRExpr(irbb->tyenv,e)) {
663 case Ity_I32: return e;
664 case Ity_I16: return unop(Iop_16Sto32,e);
665 case Ity_I8: return unop(Iop_8Sto32,e);
666 default: vpanic("widenSto32");
667 }
668}
669
670/* Narrow 8/16/32 bit int expr to 8/16/32. Clearly only some
671 of these combinations make sense. */
672static IRExpr* narrowTo ( IRType dst_ty, IRExpr* e )
673{
674 IRType src_ty = typeOfIRExpr(irbb->tyenv,e);
675 if (src_ty == dst_ty)
676 return e;
677 if (src_ty == Ity_I32 && dst_ty == Ity_I16)
678 return unop(Iop_32to16, e);
679 if (src_ty == Ity_I32 && dst_ty == Ity_I8)
680 return unop(Iop_32to8, e);
681
682 vex_printf("\nsrc, dst tys are: ");
683 ppIRType(src_ty);
684 vex_printf(", ");
685 ppIRType(dst_ty);
686 vex_printf("\n");
cerionc60c01e2004-12-02 20:19:22 +0000687 vpanic("narrowTo(ARM)");
sewardjc2c87162004-11-25 13:07:02 +0000688}
689
690
691/* Set the flags thunk OP, DEP1 and DEP2 fields. The supplied op is
692 auto-sized up to the real op. */
693
694static
cerionfd7474a2004-12-03 11:16:42 +0000695void setFlags_DEP1_DEP2 ( IROp op, IRTemp dep1, IRTemp dep2 )
sewardjc2c87162004-11-25 13:07:02 +0000696{
cerionc60c01e2004-12-02 20:19:22 +0000697 stmt( IRStmt_Put( OFFB_CC_OP, mkU32(op)) );
sewardjc2c87162004-11-25 13:07:02 +0000698 stmt( IRStmt_Put( OFFB_CC_DEP1, widenUto32(mkexpr(dep1))) );
699 stmt( IRStmt_Put( OFFB_CC_DEP2, widenUto32(mkexpr(dep2))) );
700}
701
702
703/* Set the OP and DEP1 fields only, and write zero to DEP2. */
704
705static
cerionfd7474a2004-12-03 11:16:42 +0000706void setFlags_DEP1 ( IROp op, IRTemp dep1 )
sewardjc2c87162004-11-25 13:07:02 +0000707{
cerionc60c01e2004-12-02 20:19:22 +0000708 stmt( IRStmt_Put( OFFB_CC_OP, mkU32(op)) );
sewardjc2c87162004-11-25 13:07:02 +0000709 stmt( IRStmt_Put( OFFB_CC_DEP1, widenUto32(mkexpr(dep1))) );
710 stmt( IRStmt_Put( OFFB_CC_DEP2, mkU32(0)) );
711}
712
713
714/* For shift operations, we put in the result and the undershifted
715 result. Except if the shift amount is zero, the thunk is left
716 unchanged. */
717
cerionc60c01e2004-12-02 20:19:22 +0000718static void setFlags_DEP1_DEP2_shift ( IROp op,
sewardjc2c87162004-11-25 13:07:02 +0000719 IRTemp res,
720 IRTemp resUS,
sewardjc2c87162004-11-25 13:07:02 +0000721 IRTemp guard )
722{
cerionc60c01e2004-12-02 20:19:22 +0000723 vassert(guard);
sewardjc2c87162004-11-25 13:07:02 +0000724
725 /* DEP1 contains the result, DEP2 contains the undershifted value. */
726 stmt( IRStmt_Put( OFFB_CC_OP,
727 IRExpr_Mux0X( mkexpr(guard),
728 IRExpr_Get(OFFB_CC_OP,Ity_I32),
cerionc60c01e2004-12-02 20:19:22 +0000729 mkU32(op))) );
sewardjc2c87162004-11-25 13:07:02 +0000730 stmt( IRStmt_Put( OFFB_CC_DEP1,
731 IRExpr_Mux0X( mkexpr(guard),
732 IRExpr_Get(OFFB_CC_DEP1,Ity_I32),
733 widenUto32(mkexpr(res)))) );
734 stmt( IRStmt_Put( OFFB_CC_DEP2,
735 IRExpr_Mux0X( mkexpr(guard),
736 IRExpr_Get(OFFB_CC_DEP2,Ity_I32),
737 widenUto32(mkexpr(resUS)))) );
738}
739
740
cerionc60c01e2004-12-02 20:19:22 +0000741
742
cerionc60c01e2004-12-02 20:19:22 +0000743
sewardjc2c87162004-11-25 13:07:02 +0000744
745
746/* Multiplies are pretty much like add and sub: DEP1 and DEP2 hold the
747 two arguments. */
748
749static
cerionc60c01e2004-12-02 20:19:22 +0000750void setFlags_MUL ( IRTemp arg1, IRTemp arg2, UInt op )
sewardjc2c87162004-11-25 13:07:02 +0000751{
cerionc60c01e2004-12-02 20:19:22 +0000752 stmt( IRStmt_Put( OFFB_CC_OP, mkU32(op) ) );
753 stmt( IRStmt_Put( OFFB_CC_DEP1, widenUto32(mkexpr(arg1)) ));
754 stmt( IRStmt_Put( OFFB_CC_DEP2, widenUto32(mkexpr(arg2)) ));
sewardjc2c87162004-11-25 13:07:02 +0000755}
cerionc60c01e2004-12-02 20:19:22 +0000756
757
758
759
760
761
762
763
sewardjc2c87162004-11-25 13:07:02 +0000764
765
766/* -------------- Condition codes. -------------- */
767
cerionc60c01e2004-12-02 20:19:22 +0000768/* Condition codes, using the ARM encoding. */
sewardjc2c87162004-11-25 13:07:02 +0000769
cerionc60c01e2004-12-02 20:19:22 +0000770// CAB: Just used for debugging printouts ?
sewardjcca71942004-12-02 23:35:18 +0000771// yes, only for debugging
772static HChar* name_ARMCondcode ( ARMCondcode cond )
sewardjc2c87162004-11-25 13:07:02 +0000773{
774 switch (cond) {
cerionc60c01e2004-12-02 20:19:22 +0000775 case ARMCondEQ: return "eq";
776 case ARMCondNE: return "ne";
777 case ARMCondHS: return "hs";
778 case ARMCondLO: return "no";
779 case ARMCondMI: return "mi";
780 case ARMCondPL: return "pl";
781 case ARMCondVS: return "vs";
782 case ARMCondVC: return "vc";
783 case ARMCondHI: return "hi";
784 case ARMCondLS: return "ls";
785 case ARMCondGE: return "ge";
786 case ARMCondLT: return "lt";
787 case ARMCondGT: return "gt";
788 case ARMCondLE: return "le";
789 case ARMCondAL: return "al";
790 case ARMCondNV: return "nv";
791 default: vpanic("name_ARMCondcode");
sewardjc2c87162004-11-25 13:07:02 +0000792 }
793}
794
cerionc60c01e2004-12-02 20:19:22 +0000795
sewardjc2c87162004-11-25 13:07:02 +0000796static
cerionc60c01e2004-12-02 20:19:22 +0000797ARMCondcode positiveIse_ARMCondcode ( ARMCondcode cond,
sewardjc2c87162004-11-25 13:07:02 +0000798 Bool* needInvert )
799{
cerionc60c01e2004-12-02 20:19:22 +0000800 vassert(cond >= ARMCondEQ && cond <= ARMCondNV);
sewardjc2c87162004-11-25 13:07:02 +0000801 if (cond & 1) {
802 *needInvert = True;
803 return cond-1;
804 } else {
805 *needInvert = False;
806 return cond;
807 }
808}
cerionc60c01e2004-12-02 20:19:22 +0000809
810
811
812
813
814
cerionf7da63d2004-12-09 19:04:57 +0000815/*
816 Addressing mode 4 - LOAD/STORE multiple, LDM|STM
817 ARM ARM A5-48
818*/
819static
820void dis_loadstore_mult ( theInstr )
821{
822 UChar flags = (theInstr >> 20) & 0x1F; // theInstr[24:20]
823 UChar Rn_addr = (theInstr >> 16) & 0xF;
824 IRTemp Rn = newTemp(Ity_I32);
825 IRTemp Rn_orig = newTemp(Ity_I32);
826 UInt reg_list = theInstr & 0xFFFF; // each bit addresses a register: R0 to R15
827 UChar L = (flags >> 0) & 1; // Load(1) | Store(0)
828 UChar W = (flags >> 1) & 1; // (W)riteback Rn (incr(U=1) | decr(U=0) by n_bytes)
cerion19e8a612004-12-10 10:18:58 +0000829// UChar S = (flags >> 2) & 1; // Priviledged mode flag - *** CAB TODO ***
cerionf7da63d2004-12-09 19:04:57 +0000830 UChar U = (flags >> 3) & 1; // Txfr ctl: Direction = upwards(1) | downwards(0)
831 UChar PU = (flags >> 3) & 3; // Txfr ctl: Rn within(P=1) | outside(P=0) accessed mem
832
cerion19e8a612004-12-10 10:18:58 +0000833 IRTemp start_addr = newTemp(Ity_I32);
834 IRTemp end_addr = newTemp(Ity_I32);
cerionf7da63d2004-12-09 19:04:57 +0000835 IRTemp data=0;
cerion19e8a612004-12-10 10:18:58 +0000836
cerionf7da63d2004-12-09 19:04:57 +0000837 UInt n_bytes=0;
838 UInt tmp_reg = reg_list;
cerion19e8a612004-12-10 10:18:58 +0000839 UInt reg_idx, offset;
cerionf7da63d2004-12-09 19:04:57 +0000840
841 while (tmp_reg > 0) { // Count num bits in reg_list => num_bytes
842 if (tmp_reg & 1) { n_bytes += 4; }
843 tmp_reg = tmp_reg >> 1;
844 }
845
846 assign( Rn, getIReg(Rn_addr) );
847 assign( Rn_orig, mkexpr(Rn) );
848
cerion19e8a612004-12-10 10:18:58 +0000849 switch (PU) { // <addressing_mode>
cerionf7da63d2004-12-09 19:04:57 +0000850 case 0x0: // Decrement after (DA)
851 assign( start_addr, binop( Iop_Add32, mkexpr(Rn), mkU32(n_bytes + 4) ) );
852 assign( end_addr, mkexpr(Rn) );
853 break;
854
855 case 0x1: // Increment after (IA)
856 assign( start_addr, mkexpr(Rn) );
857 assign( end_addr, binop( Iop_Add32, mkexpr(Rn), mkU32(n_bytes - 4) ) );
858 break;
859
860 case 0x2: // Decrement before (DB)
861 assign( start_addr, binop( Iop_Sub32, mkexpr(Rn), mkU32(n_bytes) ) );
862 assign( end_addr, binop( Iop_Sub32, mkexpr(Rn), mkU32(4) ) );
863 break;
864
865 case 0x3: // Increment before (IB)
866 assign( start_addr, binop( Iop_Add32, mkexpr(Rn), mkU32(4) ) );
867 assign( end_addr, binop( Iop_Add32, mkexpr(Rn), mkU32(n_bytes) ) );
868 break;
869
870 default:
871 vpanic("dis_loadstore_mult(ARM)");
872 return;
873 }
874
875 if (W==1) {
876 if (U==1) { // upwards
cerion19e8a612004-12-10 10:18:58 +0000877 putIReg( Rn_addr, binop( Iop_Add32, mkexpr(Rn), mkU32(n_bytes) ) );
cerionf7da63d2004-12-09 19:04:57 +0000878 } else { // downwards
cerion19e8a612004-12-10 10:18:58 +0000879 putIReg( Rn_addr, binop( Iop_Sub32, mkexpr(Rn), mkU32(n_bytes) ) );
cerionf7da63d2004-12-09 19:04:57 +0000880 }
881 }
882
883
884 /*
885 Loop through register list, LOAD/STORE indicated registers
886
887 lowest numbered reg -> lowest address
888 -> so start with lowest register...
889 reg_idx gives the guest register address
cerion19e8a612004-12-10 10:18:58 +0000890 offset gives current mem offset from start_addr
cerionf7da63d2004-12-09 19:04:57 +0000891 */
cerion19e8a612004-12-10 10:18:58 +0000892 offset=0;
cerionf7da63d2004-12-09 19:04:57 +0000893 for (reg_idx=0; reg_idx < 16; reg_idx++) {
894 if (( reg_list >> reg_idx ) & 1) { // reg_list[i] == 1?
cerion19e8a612004-12-10 10:18:58 +0000895
896 if (L==1) { // LOAD Ri, (start_addr + offset)
cerionf7da63d2004-12-09 19:04:57 +0000897
898 // CAB: TODO
899 if (Rn_addr == reg_idx && W==1) {} // Undefined! - See ARM ARM A4-31
900
cerion19e8a612004-12-10 10:18:58 +0000901 assign( data, loadLE(Ity_I32, binop(Iop_Add32,
902 mkexpr(start_addr), mkU32(offset))) );
cerionf7da63d2004-12-09 19:04:57 +0000903 if (reg_idx == 15) {
904 // assuming architecture < 5: See ARM ARM A4-31
905 putIReg( reg_idx, binop(Iop_And32, mkexpr(data), mkU32(0xFFFFFFFC)) );
906 } else {
907 putIReg( reg_idx, mkexpr(data) );
908 }
cerion19e8a612004-12-10 10:18:58 +0000909 } else { // STORE Ri, (start_addr + offset)
cerionf7da63d2004-12-09 19:04:57 +0000910
911 // ARM ARM A4-85 (Operand restrictions)
cerion19e8a612004-12-10 10:18:58 +0000912 if (reg_idx == Rn_addr && W==1) { // Rn in list && writeback
913 if (offset==0) { // lowest reg in reg_list: Rn_orig is stored
914 storeLE( mkexpr(start_addr), mkexpr(Rn_orig) );
cerionf7da63d2004-12-09 19:04:57 +0000915 } else { // Undefined! - See ARM ARM A4-85
916 // CAB TODO
917 }
918 } else {
cerion19e8a612004-12-10 10:18:58 +0000919 storeLE( binop(Iop_Add32, mkexpr(start_addr), mkU32(offset) ),
920 getIReg(reg_idx) );
cerionf7da63d2004-12-09 19:04:57 +0000921 }
922 }
cerion19e8a612004-12-10 10:18:58 +0000923 offset += 4;
cerionf7da63d2004-12-09 19:04:57 +0000924 }
925 }
926 // CAB TODO:
cerion19e8a612004-12-10 10:18:58 +0000927 // IR assert( end_addr == (start_addr + offset) - 8 )
cerionf7da63d2004-12-09 19:04:57 +0000928
929 return;
930}
931
932
933
934
935/*
936 Addressing mode 2 - LOAD/STORE word or unsigned byte
937 ARM ARM A5-18
938*/
939static
940void dis_loadstore_w_ub ( theInstr )
941{
942 UChar is_reg = (theInstr >> 25) & 0x1; // immediate | register offset/index
943 UInt flags = (theInstr >> 20) & 0x3F; // theInstr[25:20]
944 UChar Rn_addr = (theInstr >> 16) & 0xF;
945 UChar Rd_addr = (theInstr >> 12) & 0xF;
946 UChar Rm_addr = (theInstr >> 00) & 0xF;
947 UChar shift_op = (theInstr >> 04) & 0xFF;
948 UInt offset_12 = (theInstr >> 00) & 0xFFF;
949 IRTemp Rn = newTemp(Ity_I32);
950 IRTemp Rm = newTemp(Ity_I32);
951 UChar shift_imm, shift;
952
953 UChar L = (flags >> 0) & 1; // Load(1) | Store(0)
954 UChar W = (flags >> 1) & 1; // P==0: mem access = normal(W==0) | unprivileged(W==1)
955 // P==1: Rn !updated(W==0) | updated(W==1)
956 UChar B = (flags >> 2) & 1; // access = unsigned byte(1) | word(0)
957 UChar U = (flags >> 3) & 1; // offset is added(1)|subtracted(0) from the base
958 UChar P = (flags >> 4) & 1; // P==0: post-indexed addressing
959 // P==1: W==0: offset addressing: Rn not updated
960 // W==1: pre-indexed addressing: addr -> Rn
961 IRTemp addr = newTemp(Ity_I32);
962 IRTemp indx = newTemp(Ity_I32);
963
964 IRTemp tmp = newTemp(Ity_I32);
965 IRTemp tmp1 = newTemp(Ity_I32);
966 IRTemp tmp2 = newTemp(Ity_I32);
967 IRTemp tmp3 = newTemp(Ity_I32);
968 IRTemp tmp4 = newTemp(Ity_I32);
969 IRExpr* expr;
970
971 IRTemp oldFlagC = newTemp(Ity_I32);
972
973 vassert(((theInstr >> 26) & 0x3) == 0x1);
974
975 assign( Rn, getIReg(Rn_addr) );
976
977 if (Rn_addr == 15) {
978 if (P==1 && W==0) { // offset addressing: Rn not updated
979 // CAB: This right?
980 assign( Rn, binop(Iop_And32, mkexpr(Rn), mkU32(8)) );
981 } else { // Unpredictable: ARM ARM A5-29
982 // CAB TODO
cerion19e8a612004-12-10 10:18:58 +0000983 //illegal instruction exception
984
985// goto decode_failure;
986
cerionf7da63d2004-12-09 19:04:57 +0000987 }
988 }
989
990 /*
991 Post-indexed: Set addr to Rn
992 */
993 if (P==0) {
994 assign( addr, mkexpr(Rn) );
995 }
996
997 /*
998 Retrieve address to load/store
999 */
1000 if (is_reg) {
1001 // CAB TODO
1002 if (Rm_addr == 15) {} // Unpredictable: ARM ARM A5-27
1003 if (Rm_addr == Rn_addr) {} // Unpredictable: ARM ARM A5-27
1004
1005 assign( Rm, getIReg(Rm_addr) );
1006
1007 if (shift_op == 0) {
1008 assign( tmp, mkexpr(Rm) );
1009 } else {
1010 shift_imm = (shift_op >> 3) & 0x1F;
1011 shift = (shift_op >> 1) & 0x3;
1012
1013 switch (shift) {
1014 case 0x0: // LSL
cerion19e8a612004-12-10 10:18:58 +00001015 assign( indx, binop(Iop_Shl32, mkexpr(Rm), mkU8(shift_imm)) );
cerionf7da63d2004-12-09 19:04:57 +00001016 break;
1017
1018 case 0x1: // LSR
1019 if (shift_imm) {
cerion19e8a612004-12-10 10:18:58 +00001020 assign( indx, binop(Iop_Shr32, mkexpr(Rm), mkU8(shift_imm)) );
cerionf7da63d2004-12-09 19:04:57 +00001021 } else {
1022 assign( indx, mkU32(0) );
1023 }
1024 break;
1025
1026 case 0x2: // ASR
1027 if (shift_imm) {
1028 assign( indx, binop(Iop_Sar32, mkexpr(Rm), mkU32(shift_imm)) );
1029 } else {
1030 assign( indx, // Rm[31] ? 0xFFFFFFFF : 0x0
1031 IRExpr_Mux0X( binop(Iop_And32, mkexpr(Rm), mkU32(0x8FFFFFFF)),
1032 mkexpr(0x0), mkexpr(0xFFFFFFFF) ) );
1033 }
1034 break;
1035
1036 case 0x3: // ROR|RRX
1037
1038 // CAB: These right?
1039
1040 assign(oldFlagC, mk_armg_calculate_flags_c());
1041
1042 if (shift_imm == 0) { // RRX (ARM ARM A5-17)
1043 // 33 bit ROR using carry flag as the 33rd bit
1044 // op = Rm >> 1, carry flag replacing vacated bit position.
1045 // indx = (c_flag lsl 31) OR (Rm LSR 1)
cerion19e8a612004-12-10 10:18:58 +00001046 assign( tmp, mkexpr(oldFlagC) );
cerionf7da63d2004-12-09 19:04:57 +00001047 assign( indx, binop( Iop_Or32,
1048 binop( Iop_Shl32, mkexpr(tmp), mkU32(31) ),
cerion19e8a612004-12-10 10:18:58 +00001049 binop( Iop_Shr32, mkexpr(Rm), mkU8(1) ) ) );
cerionf7da63d2004-12-09 19:04:57 +00001050
1051 } else { // ROR
1052 // indx = Rm ROR shift_imm
1053 // = (Rm >> shift_imm) | (Rm << (32-shift_imm))
1054 assign( tmp, binop(Iop_Sub8, mkU8(32), mkU32(shift_imm)) );
1055 assign( indx, binop( Iop_Or32,
cerion19e8a612004-12-10 10:18:58 +00001056 binop( Iop_Shr32, mkexpr(Rm), mkU8(shift_imm) ),
cerionf7da63d2004-12-09 19:04:57 +00001057 binop( Iop_Shl32, mkexpr(Rm), mkexpr(tmp) ) ) );
1058 }
1059 break;
1060
1061 default: break;
1062 }
1063 assign( tmp, mkexpr(indx) );
1064 }
1065 } else { // immediate offset/index
1066 assign( tmp, mkU32(offset_12) );
1067 }
1068
1069 /*
1070 Depending on P,U,W, set addr and write to Rn
1071 */
1072 if (P==1) {
1073 if (U == 1) { // increment
1074 assign( addr, binop( Iop_Add32, mkexpr(Rn), mkexpr(tmp) ) );
1075 } else { // decrement
1076 assign( addr, binop( Iop_Sub32, mkexpr(Rn), mkexpr(tmp) ) );
1077 }
1078 if (W == 1) { // pre-indexed addressing
1079 putIReg( Rn_addr, mkexpr(addr) );
1080 }
1081 } else { // post-indexed addressing
1082 assign( addr, mkexpr(Rn) );
1083 if (U == 1) { // increment
1084 putIReg( Rn_addr, binop( Iop_Add32, mkexpr(Rn), mkexpr(tmp) ) );
1085 } else { // decrement
1086 putIReg( Rn_addr, binop( Iop_Sub32, mkexpr(Rn), mkexpr(tmp) ) );
1087 }
1088 }
1089
1090
1091
1092 /*
1093 LOAD/STORE Rd, address
1094 */
1095 if (L==1) { // LOAD
1096 if (B==1) { // unsigned byte (LDRB): ARM ARM A4-40
1097 putIReg( Rd_addr, loadLE( Ity_I8, mkexpr( addr ) ) );
1098 }
1099 else { // word (LDR): ARM ARM A4-38
1100 expr = binop(Iop_And32, mkexpr(addr), mkU32(0x3));
1101
1102 /* LOAD memory data (4 bytes) */
1103 assign( tmp1, loadLE( Ity_I32, mkexpr( addr ) ) );
1104
1105 // data ROR 8
1106 assign( tmp2, binop(Iop_Sub8, mkU8(32), mkU32(8)) );
1107 assign( tmp2, binop( Iop_Or32,
cerion19e8a612004-12-10 10:18:58 +00001108 binop( Iop_Shr32, mkexpr(tmp1), mkU8(8) ),
cerionf7da63d2004-12-09 19:04:57 +00001109 binop( Iop_Shl32, mkexpr(tmp1), mkexpr(tmp2) ) ) );
1110 // data ROR 16
1111 assign( tmp3, binop(Iop_Sub8, mkU8(32), mkU32(16)) );
1112 assign( tmp3, binop( Iop_Or32,
cerion19e8a612004-12-10 10:18:58 +00001113 binop( Iop_Shr32, mkexpr(tmp1), mkU8(16) ),
cerionf7da63d2004-12-09 19:04:57 +00001114 binop( Iop_Shl32, mkexpr(tmp1), mkexpr(tmp3) ) ) );
1115
1116 // data ROR 24
1117 assign( tmp4, binop(Iop_Sub8, mkU8(32), mkU32(24)) );
1118 assign( tmp4, binop( Iop_Or32,
cerion19e8a612004-12-10 10:18:58 +00001119 binop( Iop_Shr32, mkexpr(tmp1), mkU8(24) ),
cerionf7da63d2004-12-09 19:04:57 +00001120 binop( Iop_Shl32, mkexpr(tmp1), mkexpr(tmp4) ) ) );
1121
1122 /* switch (addr[1:0]) {
1123 0x0:addr;
1124 0x1:addr ROR 8;
1125 0x2:addr ROR 16;
1126 0x3:addr ROR 24 } */
1127 assign( tmp, IRExpr_Mux0X(
1128 binop(Iop_CmpEQ32, expr, mkU32(0x0)),
1129 IRExpr_Mux0X(
1130 binop(Iop_CmpEQ32, expr, mkU32(0x1)),
1131 IRExpr_Mux0X(
1132 binop(Iop_CmpEQ32, expr, mkU32(0x2)),
1133 mkexpr(tmp4),
1134 mkexpr(tmp3) ),
1135 mkexpr(tmp2) ),
1136 mkexpr(tmp1) ) );
1137
1138
1139 if ( Rd_addr == 15 && !(P == 0 && W==1)) { // R15 && not unprivileged...
1140 // assuming architecture < 5: See ARM ARM A4-28
1141 putIReg( Rd_addr, binop(Iop_And32, mkexpr(tmp), mkU32(0xFFFFFFFC)) );
1142 } else {
1143 putIReg( Rd_addr, mkexpr(tmp) );
1144 }
1145
1146 }
1147 } else { // STORE: ARM ARM A4-88
1148 // CAB: these right?
1149
1150 if (B==1) { // unsigned byte
1151 storeLE( mkexpr(addr), unop(Iop_32to8, getIReg(Rd_addr)) ); // Rd[7:0]
1152 } else { // word
1153 storeLE( mkexpr(addr), getIReg(Rd_addr) );
1154 }
1155 }
1156}
cerionc60c01e2004-12-02 20:19:22 +00001157
cerionc60c01e2004-12-02 20:19:22 +00001158
cerionfd7474a2004-12-03 11:16:42 +00001159
1160
1161
1162
1163/*
cerionf7da63d2004-12-09 19:04:57 +00001164 ARMG_CC_OP_LSL, ARMG_CC_OP_LSR, ARMG_CC_OP_ASR
1165 ARM ARM A5-9...
1166
1167 carry = carry_out[0]
cerionfd7474a2004-12-03 11:16:42 +00001168*/
ceriona70a37b2004-12-03 18:54:08 +00001169static
cerionf7da63d2004-12-09 19:04:57 +00001170IRExpr* dis_shift( UInt theInstr, IRTemp* carry_out )
ceriona70a37b2004-12-03 18:54:08 +00001171{
cerionf7da63d2004-12-09 19:04:57 +00001172 UChar Rn_addr = (theInstr >> 16) & 0xF;
1173 UChar Rd_addr = (theInstr >> 12) & 0xF;
1174 UChar Rs_addr = (theInstr >> 8) & 0xF;
1175 UChar Rm_addr = (theInstr >> 0) & 0xF;
1176 UChar by_reg = (theInstr >> 4) & 0x1; // instr[4]
cerion19e8a612004-12-10 10:18:58 +00001177 UChar shift_imm = (theInstr >> 7) & 0x1F; // instr[11:7]
1178 UChar shift_op = (theInstr >> 4) & 0xF; // instr[7:4]
cerionf7da63d2004-12-09 19:04:57 +00001179 IRTemp Rm = newTemp(Ity_I32);
1180 IRTemp Rs = newTemp(Ity_I32);
cerion19e8a612004-12-10 10:18:58 +00001181 IRTemp shift_amt = newTemp(Ity_I8);
1182 IRTemp carry_shift = newTemp(Ity_I8);
cerionf7da63d2004-12-09 19:04:57 +00001183 IRTemp oldFlagC = newTemp(Ity_I32);
1184 IRTemp mux_false = newTemp(Ity_I32);
ceriona70a37b2004-12-03 18:54:08 +00001185 IRExpr* expr;
cerionf7da63d2004-12-09 19:04:57 +00001186 IROp op;
cerionfd7474a2004-12-03 11:16:42 +00001187
cerionf7da63d2004-12-09 19:04:57 +00001188 assign( Rm, getIReg(Rm_addr) );
1189 assign(oldFlagC, mk_armg_calculate_flags_c());
ceriona70a37b2004-12-03 18:54:08 +00001190
cerionf7da63d2004-12-09 19:04:57 +00001191 switch (shift_op) {
1192 case 0x0: case 0x8: case 0x1: op = Iop_Shl32; break;
1193 case 0x2: case 0xA: case 0x3: op = Iop_Shr32; break;
1194 case 0x4: case 0xC: case 0x5: op = Iop_Sar32; break;
1195 default: vpanic("dis_shift"); break;
1196 }
cerionfd7474a2004-12-03 11:16:42 +00001197
cerionf7da63d2004-12-09 19:04:57 +00001198
1199 if (by_reg) { // Register Shift
cerion19e8a612004-12-10 10:18:58 +00001200 vex_printf("shift: reg\n");
1201
cerionf7da63d2004-12-09 19:04:57 +00001202 if (Rd_addr == 15 || Rm_addr == 15 || Rn_addr == 15 || Rs_addr == 15) {
1203 // Unpredictable (ARM ARM A5-10)
1204 // CAB TODO
1205 }
1206
1207 assign( Rs, getIReg((theInstr >> 8) & 0xF) );
1208
1209 // shift_amt = shift_expr & 31 => Rs[5:0]
cerion19e8a612004-12-10 10:18:58 +00001210 assign( shift_amt,
1211 narrowTo(Ity_I8, binop( Iop_And32, mkexpr(Rs), mkU32(0x1F)) ) );
1212
1213 // CAB TODO: support for >31 shift ? (Rs[7:0])
cerionf7da63d2004-12-09 19:04:57 +00001214
1215 switch (shift_op) {
1216 case 0x1: // LSL(reg)
1217 assign( mux_false, mkU32(0) );
cerion19e8a612004-12-10 10:18:58 +00001218 assign( carry_shift, binop(Iop_Add8, mkU8(32), mkexpr(shift_amt)) );
cerionf7da63d2004-12-09 19:04:57 +00001219 break;
1220 case 0x3: // LSR(reg)
1221 assign( mux_false, mkU32(0) );
cerion19e8a612004-12-10 10:18:58 +00001222 assign( carry_shift, binop(Iop_Sub8, mkexpr(shift_amt), mkU8(1)) );
cerionf7da63d2004-12-09 19:04:57 +00001223 break;
1224 case 0x5: // ASR(reg)
1225 // Rs[31] == 0 ? 0x0 : 0xFFFFFFFF
1226 assign( mux_false,
1227 IRExpr_Mux0X(
1228 binop(Iop_CmpLT32U, mkexpr(Rs), mkU32(0x80000000)),
1229 mkU32(0xFFFFFFFF), mkU32(0) ) );
1230 assign( carry_shift,
cerion19e8a612004-12-10 10:18:58 +00001231 binop(Iop_Sub8, mkexpr(shift_amt), mkU8(1)) );
cerionf7da63d2004-12-09 19:04:57 +00001232 break;
cerion19e8a612004-12-10 10:18:58 +00001233 default:
1234 vex_printf("dis_shift(arm): Reg shift: No such case: 0x%x\n", shift_op);
1235 vpanic("dis_shift(ARM): Reg shift");
cerionf7da63d2004-12-09 19:04:57 +00001236 }
cerion19e8a612004-12-10 10:18:58 +00001237
1238 expr = IRExpr_Mux0X(
1239 binop(Iop_CmpLT32U, widenUto32(mkexpr(shift_amt)), mkU32(32)),
1240 mkexpr(mux_false),
1241 binop(op, mkexpr(Rm), mkexpr(shift_amt)) );
cerionf7da63d2004-12-09 19:04:57 +00001242
1243 // shift_amt == 0 ? old_flag_c : Rm >> x
1244 assign( *carry_out,
1245 IRExpr_Mux0X(
cerion19e8a612004-12-10 10:18:58 +00001246 binop(Iop_CmpEQ8, mkexpr(shift_amt), mkU8(0)),
cerionf7da63d2004-12-09 19:04:57 +00001247 binop(Iop_Shr32, mkexpr(Rm), mkexpr(carry_shift)),
cerion19e8a612004-12-10 10:18:58 +00001248 mkexpr(oldFlagC) ) );
ceriona70a37b2004-12-03 18:54:08 +00001249 }
1250 else { // Immediate shift
cerion19e8a612004-12-10 10:18:58 +00001251 vex_printf("shift: imm\n");
cerionf7da63d2004-12-09 19:04:57 +00001252
1253 // CAB: This right? Seems kinda strange... (ARM ARM A5-9)
1254 if (Rm_addr == 15 || Rn_addr == 15) {
1255 assign( Rm, binop(Iop_Add32, getIReg(15), mkU32(8)) );
1256 }
cerionc60c01e2004-12-02 20:19:22 +00001257
cerionf7da63d2004-12-09 19:04:57 +00001258 if (shift_imm == 0) {
1259 switch (shift_op) {
1260 case 0x0: case 0x8: // LSL(imm)
cerion19e8a612004-12-10 10:18:58 +00001261 expr = mkexpr(Rm);
1262 assign( *carry_out, mkexpr(oldFlagC) );
1263// assign( *carry_out, binop(Iop_Shr32, mkexpr(oldFlagC),
1264// mkU32(ARMG_CC_SHIFT_C)) );
cerionf7da63d2004-12-09 19:04:57 +00001265 break;
1266 case 0x2: case 0xA: // LSR(imm)
1267 expr = mkexpr(0);
1268 // Rm >> 31: carry = R[0]
cerion19e8a612004-12-10 10:18:58 +00001269 assign( *carry_out, binop(Iop_Shr32, mkexpr(Rm), mkU8(31)) );
cerionf7da63d2004-12-09 19:04:57 +00001270 break;
1271 case 0x4: case 0xC: // ASR(imm)
1272 // Rs[31] == 0 ? 0x0 : 0xFFFFFFFF
1273 expr = IRExpr_Mux0X(
1274 binop(Iop_CmpLT32U, mkexpr(Rs), mkU32(0x80000000)),
1275 mkU32(0xFFFFFFFF), mkU32(0) );
1276 // Rm >> 31: carry = R[0]
cerion19e8a612004-12-10 10:18:58 +00001277 assign( *carry_out, binop(Iop_Shr32, mkexpr(Rm), mkU8(31)) );
cerionf7da63d2004-12-09 19:04:57 +00001278 break;
cerion19e8a612004-12-10 10:18:58 +00001279 default:
1280 vex_printf("dis_shift(arm): Imm shift: No such case: 0x%x\n", shift_op);
1281 vpanic("dis_shift(ARM): Imm shift");
cerionf7da63d2004-12-09 19:04:57 +00001282 }
cerion19e8a612004-12-10 10:18:58 +00001283 } else {
1284 expr = binop(op, mkexpr(Rm), mkU8(shift_imm));
1285 assign( *carry_out, binop(op, mkexpr(Rm),
1286 binop(Iop_Sub32, mkU32(shift_imm), mkU32(1)) ) );
cerionf7da63d2004-12-09 19:04:57 +00001287 }
ceriona70a37b2004-12-03 18:54:08 +00001288 }
ceriona70a37b2004-12-03 18:54:08 +00001289 return expr;
cerionc60c01e2004-12-02 20:19:22 +00001290}
1291
1292
1293
ceriona70a37b2004-12-03 18:54:08 +00001294
cerionc60c01e2004-12-02 20:19:22 +00001295/*
cerionf7da63d2004-12-09 19:04:57 +00001296 ARMG_CC_OP_ROR
1297 ARM ARM A5-15,16,17
cerionc60c01e2004-12-02 20:19:22 +00001298*/
cerionf7da63d2004-12-09 19:04:57 +00001299static
1300IRExpr* dis_rotate ( UInt theInstr, IRTemp* carry_out )
1301{
1302 UChar Rn_addr = (theInstr >> 16) & 0xF;
1303 UChar Rd_addr = (theInstr >> 12) & 0xF;
1304 UChar Rs_addr = (theInstr >> 8) & 0xF;
1305 UChar Rm_addr = (theInstr >> 0) & 0xF;
1306 UChar by_reg = (theInstr >> 4) & 0x1; // instr[4]
1307 UInt rot_imm = (theInstr >> 7) & 0x1F; // instr[11:7]
1308 IRTemp Rm = newTemp(Ity_I32);
1309 IRTemp Rs = newTemp(Ity_I32);
cerion19e8a612004-12-10 10:18:58 +00001310 IRTemp rot_amt = newTemp(Ity_I8); // Rs[7:0]
1311 IRTemp tmp_8 = newTemp(Ity_I8);
1312 IRTemp tmp_32 = newTemp(Ity_I32);
cerionf7da63d2004-12-09 19:04:57 +00001313 IRTemp oldFlagC = newTemp(Ity_I32);
1314 IRExpr* expr=0;
1315
1316 assign( Rm, getIReg(Rm_addr) );
1317 assign(oldFlagC, mk_armg_calculate_flags_c());
1318
1319 if (by_reg) { // Register rotate
cerion19e8a612004-12-10 10:18:58 +00001320 vex_printf("rotate: reg\n");
1321
cerionf7da63d2004-12-09 19:04:57 +00001322 if (Rd_addr == 15 || Rm_addr == 15 || Rn_addr == 15 || Rs_addr == 15) {
1323 // Unpredictable (ARM ARM A5-10)
1324 // CAB TODO
1325 }
1326
1327 assign( Rs, getIReg((theInstr >> 8) & 0xF) ); // instr[11:8]
cerion19e8a612004-12-10 10:18:58 +00001328 // Rs[4:0]
1329 assign( rot_amt, narrowTo(Ity_I8,
1330 binop(Iop_And32, mkexpr(Rs), mkU32(0x1F))) );
cerionf7da63d2004-12-09 19:04:57 +00001331
1332 // CAB: This right?
1333
1334 // Rs[7:0] == 0 ? oldFlagC : (Rs[4:0] == 0 ? Rm >> 31 : Rm >> rot-1 )
cerion19e8a612004-12-10 10:18:58 +00001335// assign( tmp_32, binop(Iop_Shr32, mkexpr(oldFlagC), mkU32(ARMG_CC_SHIFT_C)) );
1336 assign( tmp_32, mkexpr(oldFlagC) );
cerionf7da63d2004-12-09 19:04:57 +00001337 assign( *carry_out,
1338 IRExpr_Mux0X(
1339 binop(Iop_CmpNE32, mkU32(0),
1340 binop(Iop_And32, mkexpr(Rs), mkU32(0xFF))),
cerion19e8a612004-12-10 10:18:58 +00001341 mkexpr(tmp_32),
cerionf7da63d2004-12-09 19:04:57 +00001342 IRExpr_Mux0X(
cerion19e8a612004-12-10 10:18:58 +00001343 binop(Iop_CmpEQ8, mkexpr(rot_amt), mkU8(0)),
cerionf7da63d2004-12-09 19:04:57 +00001344 binop(Iop_Shr32, mkexpr(Rm),
cerion19e8a612004-12-10 10:18:58 +00001345 binop(Iop_Sub8, mkexpr(rot_amt), mkU8(1))),
cerionf7da63d2004-12-09 19:04:57 +00001346 binop(Iop_Shr32, mkexpr(Rm),
cerion19e8a612004-12-10 10:18:58 +00001347 binop(Iop_Shr32, mkexpr(Rm), mkU8(31))) ) ) );
cerionf7da63d2004-12-09 19:04:57 +00001348
1349
1350 /* expr = (dst0 >> rot_amt) | (dst0 << (wordsize-rot_amt)) */
cerion19e8a612004-12-10 10:18:58 +00001351 assign( tmp_8, binop(Iop_Sub8, mkU8(32), mkexpr(rot_amt)) );
cerionf7da63d2004-12-09 19:04:57 +00001352 expr = binop( Iop_Or32,
1353 binop( Iop_Shr32, mkexpr(Rm), mkexpr(rot_amt) ),
cerion19e8a612004-12-10 10:18:58 +00001354 binop(Iop_Shl32, mkexpr(Rm), mkexpr(tmp_8)) );
cerionc60c01e2004-12-02 20:19:22 +00001355 }
cerionf7da63d2004-12-09 19:04:57 +00001356 else { // Immediate rotate
cerion19e8a612004-12-10 10:18:58 +00001357 vex_printf("rotate: imm\n");
cerionf7da63d2004-12-09 19:04:57 +00001358
1359 // CAB: This right? Seems kinda strange... (ARM ARM A5-9)
1360 if (Rm_addr == 15 || Rn_addr == 15) {
cerion19e8a612004-12-10 10:18:58 +00001361// assign( Rm, binop(Iop_Add32, getIReg(15), mkU32(8)) );
1362 // TODO : Can't re-assign a temp!
cerionf7da63d2004-12-09 19:04:57 +00001363 }
1364
1365 // Rm >> rot-1: carry = R[0]
1366 assign( *carry_out, binop(Iop_Shr32, mkexpr(Rm),
cerion19e8a612004-12-10 10:18:58 +00001367 binop(Iop_Sub8, mkU8(rot_imm), mkU8(1)) ) );
cerionf7da63d2004-12-09 19:04:57 +00001368
1369 if (rot_imm == 0) { // RRX (ARM ARM A5-17)
1370 // 33 bit ROR using carry flag as the 33rd bit
1371 // op = Rm >> 1, carry flag replacing vacated bit position.
1372
1373 // CAB: This right?
cerion19e8a612004-12-10 10:18:58 +00001374 assign( tmp_32, mkexpr(oldFlagC) );
1375// assign( tmp_32, binop(Iop_Shr32, mkexpr(oldFlagC),
1376// mkU32(ARMG_CC_SHIFT_C)) );
cerionf7da63d2004-12-09 19:04:57 +00001377 expr = binop(Iop_Or32,
cerion19e8a612004-12-10 10:18:58 +00001378 binop( Iop_Shl32, mkexpr(tmp_32), mkU8(31) ),
1379 binop( Iop_Shr32, mkexpr(Rm), mkU8(1) ) );
cerionf7da63d2004-12-09 19:04:57 +00001380 } else {
cerion19e8a612004-12-10 10:18:58 +00001381 assign( tmp_8, binop(Iop_Sub8, mkU8(32), mkU8(rot_imm)) );
cerionf7da63d2004-12-09 19:04:57 +00001382 expr = binop(Iop_Or32,
cerion19e8a612004-12-10 10:18:58 +00001383 binop( Iop_Shr32, mkexpr(Rm), mkU8(rot_imm) ),
1384 binop( Iop_Shl32, mkexpr(Rm), mkexpr(tmp_8) ) );
cerionf7da63d2004-12-09 19:04:57 +00001385 }
1386 }
1387 return expr;
cerionc60c01e2004-12-02 20:19:22 +00001388}
1389
1390
1391
1392
cerionf7da63d2004-12-09 19:04:57 +00001393/*
1394 CAB TODO:
1395 - Not all shifts by 0 leave c_flag unchanged, so guard_expr is more difficult...
1396 assign( flags_guard, binop( Iop_CmpEQ32, mkexpr(shift_amt), mkU32(0) ) );
1397 setFlags_DEP1_DEP2_shift( ARMG_CC_OP_LSL, Rm, shift_op, flags_guard );
1398*/
cerionc60c01e2004-12-02 20:19:22 +00001399
cerionc60c01e2004-12-02 20:19:22 +00001400
cerionf7da63d2004-12-09 19:04:57 +00001401
1402
1403/* Addressing mode 1 - Data Processing ops
1404 General syntax: <opcode>{<cond>}{S} <Rd>, <Rn>, <shifter_operand>
1405 Returns <shifter_operand> expression
cerionc60c01e2004-12-02 20:19:22 +00001406*/
1407static
cerionf7da63d2004-12-09 19:04:57 +00001408IRExpr* dis_shifter_op ( UInt theInstr, IRTemp* carry_out)
cerionc60c01e2004-12-02 20:19:22 +00001409{
cerionf7da63d2004-12-09 19:04:57 +00001410 UChar is_immed = (theInstr >> 25) & 1; // immediate / register shift
1411 UChar shift_op = (theInstr >> 4) & 0xF; // second byte
ceriona70a37b2004-12-03 18:54:08 +00001412 UInt immed_8, rot_imm;
1413 UInt imm;
cerionf7da63d2004-12-09 19:04:57 +00001414 IRTemp oldFlagC = newTemp(Ity_I32);
cerionc60c01e2004-12-02 20:19:22 +00001415
cerionf7da63d2004-12-09 19:04:57 +00001416 // CAB TODO: Check what can do with R15... strict limits apply (ARM A5-9)
1417
ceriona70a37b2004-12-03 18:54:08 +00001418 if (is_immed) { // ARM ARM A5-2
cerion19e8a612004-12-10 10:18:58 +00001419 vex_printf("shifter_op: imm\n");
1420
ceriona70a37b2004-12-03 18:54:08 +00001421 immed_8 = theInstr & 0xFF;
1422 rot_imm = (theInstr >> 8) & 0xF;
1423 imm = immed_8 << (rot_imm << 1);
cerionf7da63d2004-12-09 19:04:57 +00001424 vex_printf("imm: %,b\n", imm);
1425
cerionf7da63d2004-12-09 19:04:57 +00001426 if (rot_imm == 0) {
cerion19e8a612004-12-10 10:18:58 +00001427 assign(oldFlagC, mk_armg_calculate_flags_c());
1428
1429 assign( *carry_out, mkexpr(oldFlagC) );
cerionf7da63d2004-12-09 19:04:57 +00001430 } else {
cerion19e8a612004-12-10 10:18:58 +00001431 assign( *carry_out, binop(Iop_Shr32, mkU32(imm), mkU8(31)) );
cerionf7da63d2004-12-09 19:04:57 +00001432 }
cerion19e8a612004-12-10 10:18:58 +00001433 return mkU32(imm);
ceriona70a37b2004-12-03 18:54:08 +00001434 } else {
cerion19e8a612004-12-10 10:18:58 +00001435 vex_printf("shifter_op: shift\n");
1436
cerionf7da63d2004-12-09 19:04:57 +00001437 // We shouldn't have any 'op' with bits 4=1 and 7=1 : 1xx1
1438 switch (shift_op) {
1439 case 0x0: case 0x8: case 0x1:
1440 case 0x2: case 0xA: case 0x3:
1441 case 0x4: case 0xC: case 0x5: return dis_shift(theInstr, carry_out);
1442 case 0x6: case 0xE: case 0x7: return dis_rotate(theInstr, carry_out);
1443 default: // Error: Any other value shouldn't be here.
1444 vpanic("dis_shifter_op(ARM)");
1445 return mkexpr(0);
1446 }
ceriona70a37b2004-12-03 18:54:08 +00001447 }
cerionc60c01e2004-12-02 20:19:22 +00001448}
1449
1450
1451
1452
1453
cerionf7da63d2004-12-09 19:04:57 +00001454/* -------------- Helper for DPI's. --------------
ceriona70a37b2004-12-03 18:54:08 +00001455*/
ceriona70a37b2004-12-03 18:54:08 +00001456static
cerionf7da63d2004-12-09 19:04:57 +00001457void dis_dataproc ( UInt theInstr )
ceriona70a37b2004-12-03 18:54:08 +00001458{
cerionf7da63d2004-12-09 19:04:57 +00001459 UChar opc = (theInstr >> 21) & 0xF;
1460 UChar set_flags = (theInstr >> 20) & 1;
1461 UChar Rn_addr = (theInstr >> 16) & 0xF;
1462 UChar Rd_addr = (theInstr >> 12) & 0xF;
1463 IRTemp Rn = newTemp(Ity_I32);
1464 IRTemp Rd = newTemp(Ity_I32);
1465 IRTemp shifter_op = newTemp(Ity_I32);
1466 IRTemp carry_out = newTemp(Ity_I32);
1467 IROp op = ARMG_CC_OP_LOGIC;
1468 Bool check_r15 = True;
ceriona70a37b2004-12-03 18:54:08 +00001469
cerionf7da63d2004-12-09 19:04:57 +00001470 assign( shifter_op, dis_shifter_op( theInstr, &carry_out ) );
1471 assign( Rd, getIReg(Rd_addr) );
1472 assign( Rn, getIReg(Rn_addr) );
ceriona70a37b2004-12-03 18:54:08 +00001473
cerionf7da63d2004-12-09 19:04:57 +00001474 switch (opc) {
1475 case 0x0: // AND
cerion19e8a612004-12-10 10:18:58 +00001476 vex_printf("OPCODE: AND\n");
cerionf7da63d2004-12-09 19:04:57 +00001477 putIReg( Rd_addr, binop(Iop_And32, getIReg(Rn_addr), mkexpr(shifter_op)) );
ceriona70a37b2004-12-03 18:54:08 +00001478 break;
1479
cerionf7da63d2004-12-09 19:04:57 +00001480 case 0x1: // EOR
cerion19e8a612004-12-10 10:18:58 +00001481 vex_printf("OPCODE: EOR\n");
cerionf7da63d2004-12-09 19:04:57 +00001482 putIReg( Rd_addr, binop(Iop_Xor32, getIReg(Rn_addr), mkexpr(shifter_op)) );
ceriona70a37b2004-12-03 18:54:08 +00001483 break;
1484
cerionf7da63d2004-12-09 19:04:57 +00001485 case 0x2: // SUB
cerion19e8a612004-12-10 10:18:58 +00001486 vex_printf("OPCODE: SUB\n");
cerionf7da63d2004-12-09 19:04:57 +00001487 putIReg( Rd_addr, binop( Iop_Sub32, getIReg(Rn_addr), mkexpr(shifter_op) ) );
1488 op = ARMG_CC_OP_SUB;
ceriona70a37b2004-12-03 18:54:08 +00001489 break;
1490
cerionf7da63d2004-12-09 19:04:57 +00001491 case 0x3: // RSB
cerion19e8a612004-12-10 10:18:58 +00001492 vex_printf("OPCODE: RSB\n");
cerionf7da63d2004-12-09 19:04:57 +00001493 putIReg( Rd_addr, binop( Iop_Sub32, mkexpr(shifter_op), getIReg(Rn_addr) ) );
1494 op = ARMG_CC_OP_SUB;
1495 break;
1496
1497 case 0x4: // ADD
cerion19e8a612004-12-10 10:18:58 +00001498 vex_printf("OPCODE: ADD\n");
cerionf7da63d2004-12-09 19:04:57 +00001499 putIReg( Rd_addr, binop( Iop_Add32, getIReg(Rn_addr), mkexpr(shifter_op) ) );
1500 op = ARMG_CC_OP_ADD;
1501 break;
1502
1503 case 0x5: // x ADC
1504 case 0x6: // x SBC
1505 case 0x7: // x RSC
1506 goto decode_failure;
1507
1508 case 0x8: // TST
cerion19e8a612004-12-10 10:18:58 +00001509 vex_printf("OPCODE: TST\n");
cerionf7da63d2004-12-09 19:04:57 +00001510 vassert(set_flags==1);
1511 assign( Rd, binop(Iop_And32, getIReg(Rn_addr), mkexpr(shifter_op)) );
1512 check_r15 = False;
1513 break;
1514
1515 case 0x9: // TEQ
cerion19e8a612004-12-10 10:18:58 +00001516 vex_printf("OPCODE: TEQ\n");
cerionf7da63d2004-12-09 19:04:57 +00001517 vassert(set_flags==1);
1518 assign( Rd, binop(Iop_Xor32, getIReg(Rn_addr), mkexpr(shifter_op)) );
1519 check_r15 = False;
1520 break;
1521
1522 case 0xA: // CMP
cerion19e8a612004-12-10 10:18:58 +00001523 vex_printf("OPCODE: CMP\n");
cerionf7da63d2004-12-09 19:04:57 +00001524 vassert(set_flags==1);
1525 op = ARMG_CC_OP_SUB;
1526 check_r15 = False;
1527 break;
1528
1529 case 0xB: // CMN
cerion19e8a612004-12-10 10:18:58 +00001530 vex_printf("OPCODE: CMN\n");
cerionf7da63d2004-12-09 19:04:57 +00001531 vassert(set_flags==1);
1532 op = ARMG_CC_OP_ADD;
1533 check_r15 = False;
1534 break;
1535
1536 case 0xC: // ORR
cerion19e8a612004-12-10 10:18:58 +00001537 vex_printf("OPCODE: ORR\n");
cerionf7da63d2004-12-09 19:04:57 +00001538 putIReg( Rd_addr, binop(Iop_Or32, getIReg(Rn_addr), mkexpr(shifter_op)) );
1539 break;
1540
1541 case 0xD: // MOV
cerion19e8a612004-12-10 10:18:58 +00001542 vex_printf("OPCODE: MOV\n");
cerionf7da63d2004-12-09 19:04:57 +00001543 putIReg( Rd_addr, mkexpr(shifter_op) );
1544 break;
1545
1546 case 0xE: // BIC
cerion19e8a612004-12-10 10:18:58 +00001547 vex_printf("OPCODE: BIC\n");
cerionf7da63d2004-12-09 19:04:57 +00001548 putIReg( Rd_addr, binop(Iop_And32, getIReg(Rn_addr),
1549 unop( Iop_Not32, mkexpr(shifter_op))) );
1550 break;
1551
1552 case 0xF: // MVN
cerion19e8a612004-12-10 10:18:58 +00001553 vex_printf("OPCODE: MVN\n");
cerionf7da63d2004-12-09 19:04:57 +00001554 putIReg( Rd_addr, unop(Iop_Not32, mkexpr(shifter_op)) );
ceriona70a37b2004-12-03 18:54:08 +00001555 break;
1556
1557 default:
cerionf7da63d2004-12-09 19:04:57 +00001558 decode_failure:
1559 /* All decode failures end up here. */
1560 vex_printf("dis_dataproc(arm): unhandled instruction: 0x%x\n", theInstr);
1561 vpanic("armToIR: unimplemented insn");
ceriona70a37b2004-12-03 18:54:08 +00001562 }
1563
cerionf7da63d2004-12-09 19:04:57 +00001564 if (set_flags) {
1565 if ( check_r15 && Rd_addr == 15) { // dest reg == PC
1566 // CPSR = SPSR: Unpredictable in User | System mode (no SPSR!)
1567 // Unpredictable: Only supporting user mode.
1568 // CAB TODO
1569
ceriona70a37b2004-12-03 18:54:08 +00001570 } else {
cerionf7da63d2004-12-09 19:04:57 +00001571 if (op == ARMG_CC_OP_LOGIC) {
1572 setFlags_DEP1_DEP2( op, Rd, carry_out );
ceriona70a37b2004-12-03 18:54:08 +00001573 } else {
cerionf7da63d2004-12-09 19:04:57 +00001574 setFlags_DEP1_DEP2( op, Rn, shifter_op );
ceriona70a37b2004-12-03 18:54:08 +00001575 }
ceriona70a37b2004-12-03 18:54:08 +00001576 }
1577 }
ceriona70a37b2004-12-03 18:54:08 +00001578}
1579
1580
1581
cerionf7da63d2004-12-09 19:04:57 +00001582
1583/* -------------- Helper for Branch. --------------
1584*/
1585static
1586void dis_branch ( UInt theInstr )
1587{
1588 UChar link = (theInstr >> 24) & 1;
cerion19e8a612004-12-10 10:18:58 +00001589 UInt signed_immed_24 = theInstr & 0xFFFFFF;
1590 UInt branch_offset;
cerionf7da63d2004-12-09 19:04:57 +00001591 IRTemp addr = newTemp(Ity_I32);
1592
1593 if (link) { // LR (R14) = addr of instr after branch instr
cerion19e8a612004-12-10 10:18:58 +00001594 assign( addr, binop(Iop_Add32, getIReg(15), mkU32(4)) );
1595 putIReg( 14, mkexpr(addr) );
cerionf7da63d2004-12-09 19:04:57 +00001596 }
1597
1598 // PC = PC + (SignExtend(signed_immed_24) << 2)
cerion19e8a612004-12-10 10:18:58 +00001599 branch_offset = extend_s_24to32( signed_immed_24 ) << 2;
cerionf7da63d2004-12-09 19:04:57 +00001600 putIReg( 15, binop(Iop_Add32, getIReg(15), mkU32(branch_offset)) );
cerion19e8a612004-12-10 10:18:58 +00001601
1602 irbb->jumpkind = link ? Ijk_Call : Ijk_Boring;
1603 irbb->next = mkU32(branch_offset);
cerionf7da63d2004-12-09 19:04:57 +00001604}
1605
1606
cerion19e8a612004-12-10 10:18:58 +00001607// whatNext = Dis_StopHere;
cerionf7da63d2004-12-09 19:04:57 +00001608
1609
ceriona70a37b2004-12-03 18:54:08 +00001610
1611
1612
1613
1614
1615
1616
cerionc60c01e2004-12-02 20:19:22 +00001617
1618
1619
sewardjc2c87162004-11-25 13:07:02 +00001620
1621
1622/*------------------------------------------------------------*/
1623/*--- Disassemble a single instruction ---*/
1624/*------------------------------------------------------------*/
1625
1626/* Disassemble a single instruction into IR. The instruction
1627 is located in host memory at &guest_code[delta].
1628 Set *size to be the size of the instruction.
1629 If the returned value is Dis_Resteer,
1630 the next guest address is assigned to *whereNext. If resteerOK
1631 is False, disInstr may not return Dis_Resteer. */
1632
1633static DisResult disInstr ( /*IN*/ Bool resteerOK,
1634 /*IN*/ Bool (*resteerOkFn) ( Addr64 ),
1635 /*IN*/ UInt delta,
1636 /*OUT*/ UInt* size,
1637 /*OUT*/ Addr64* whereNext )
1638{
sewardjfb183d22004-12-03 11:55:29 +00001639 // IRType ty;
1640 // IRTemp addr, t1, t2;
1641 // Int alen;
cerionf7da63d2004-12-09 19:04:57 +00001642 UChar opc1, opc2, opc_tmp; //, modrm, abyte;
cerionc60c01e2004-12-02 20:19:22 +00001643 ARMCondcode cond;
sewardjfb183d22004-12-03 11:55:29 +00001644 // UInt d32;
1645 // UChar dis_buf[50];
1646 // Int am_sz, d_sz;
sewardjc2c87162004-11-25 13:07:02 +00001647 DisResult whatNext = Dis_Continue;
1648 UInt theInstr;
1649
sewardjc2c87162004-11-25 13:07:02 +00001650 /* At least this is simple on ARM: insns are all 4 bytes long, and
1651 4-aligned. So just fish the whole thing out of memory right now
1652 and have done. */
1653
1654 /* We will set *size to 4 if the insn is successfully decoded.
1655 Setting it to 0 by default makes bbToIR_ARM abort if we fail the
1656 decode. */
1657 *size = 0;
1658
1659 theInstr = *(UInt*)(&guest_code[delta]);
1660
ceriona70a37b2004-12-03 18:54:08 +00001661 vex_printf("START: 0x%x, %,b\n", theInstr, theInstr );
cerionc60c01e2004-12-02 20:19:22 +00001662
sewardjc2c87162004-11-25 13:07:02 +00001663 DIP("\t0x%x: ", guest_pc_bbstart+delta);
1664
cerionc60c01e2004-12-02 20:19:22 +00001665
1666
sewardjc2c87162004-11-25 13:07:02 +00001667 // TODO: fix the client-request stuff, else nothing will work
cerionc60c01e2004-12-02 20:19:22 +00001668
sewardjc2c87162004-11-25 13:07:02 +00001669 /* Spot the client-request magic sequence. */
cerionc60c01e2004-12-02 20:19:22 +00001670 // Essentially a v. unlikely sequence of noops that we can catch
sewardjc2c87162004-11-25 13:07:02 +00001671 {
sewardjcca71942004-12-02 23:35:18 +00001672 UInt* code = (UInt*)(guest_code + delta);
cerionf7da63d2004-12-09 19:04:57 +00001673
1674 // CAB: easy way to rotate left?
1675
1676 /* Spot this:
cerionc60c01e2004-12-02 20:19:22 +00001677 E1A00EE0 mov r0, r0, ror #29
1678 E1A001E0 mov r0, r0, ror #3
1679 E1A00DE0 mov r0, r0, ror #27
1680 E1A002E0 mov r0, r0, ror #5
1681 E1A006E0 mov r0, r0, ror #13
1682 E1A009E0 mov r0, r0, ror #19
sewardjc2c87162004-11-25 13:07:02 +00001683 */
sewardj6cd91632004-12-02 23:36:20 +00001684 /* I suspect these will have to be turned the other way round to
1685 work on little-endian arm. */
sewardjcca71942004-12-02 23:35:18 +00001686 if (code[0] == 0xE1A00EE0 &&
1687 code[1] == 0xE1A001E0 &&
1688 code[2] == 0xE1A00DE0 &&
1689 code[3] == 0xE1A002E0 &&
1690 code[4] == 0xE1A006E0 &&
1691 code[5] == 0xE1A009E0) {
1692
1693 // uh ... I'll figure this out later. possibly r0 = client_request(r0) */
cerionc60c01e2004-12-02 20:19:22 +00001694 DIP("?CAB? = client_request ( ?CAB? )\n");
1695
sewardjcca71942004-12-02 23:35:18 +00001696 *size = 24;
cerionc60c01e2004-12-02 20:19:22 +00001697
cerionc60c01e2004-12-02 20:19:22 +00001698 irbb->next = mkU32(guest_pc_bbstart+delta);
1699 irbb->jumpkind = Ijk_ClientReq;
1700
sewardjc2c87162004-11-25 13:07:02 +00001701 whatNext = Dis_StopHere;
1702 goto decode_success;
1703 }
1704 }
cerionc60c01e2004-12-02 20:19:22 +00001705
1706
1707
1708
cerionf7da63d2004-12-09 19:04:57 +00001709
cerionc60c01e2004-12-02 20:19:22 +00001710 /*
1711 Deal with condition first
1712 */
1713 cond = (theInstr >> 28) & 0xF; /* opcode: bits 31:28 */
ceriona70a37b2004-12-03 18:54:08 +00001714 vex_printf("\ndisInstr(arm): cond: 0x%x, %b\n", cond, cond );
cerionfd7474a2004-12-03 11:16:42 +00001715
cerionc60c01e2004-12-02 20:19:22 +00001716 switch (cond) {
1717 case 0xF: // => Illegal instruction prior to v5 (see ARM ARM A3-5)
1718 vex_printf("disInstr(arm): illegal condition\n");
1719 goto decode_failure;
1720
1721 case 0xE: // => Unconditional: go translate the instruction
1722 break;
1723
cerionf7da63d2004-12-09 19:04:57 +00001724 default:
1725 // => Valid condition: translate the condition test first
cerionc60c01e2004-12-02 20:19:22 +00001726 stmt( IRStmt_Exit( mk_armg_calculate_condition(cond),
1727 Ijk_Boring,
1728 IRConst_U32(guest_pc_bbstart+delta+4) ) );
cerionf7da63d2004-12-09 19:04:57 +00001729 //irbb->next = mkU32(guest_pc_bbstart+delta+4);
1730 //irbb->jumpkind = Ijk_Boring;
cerionc60c01e2004-12-02 20:19:22 +00001731 }
1732
1733
1734
cerionf7da63d2004-12-09 19:04:57 +00001735 /* Primary opcode is roughly bits 27:20 (ARM ARM(v2) A3-2)
1736 secondary opcode is bits 4:0 */
1737 opc1 = (theInstr >> 20) & 0xFF; /* opcode1: bits 27:20 */
1738 opc2 = (theInstr >> 4 ) & 0xF; /* opcode2: bits 7:4 */
1739 vex_printf("disInstr(arm): opcode1: 0x%2x, %,09b\n", opc1, opc1 );
cerion19e8a612004-12-10 10:18:58 +00001740 vex_printf("disInstr(arm): opcode2: 0x%02x, %,04b\n", opc2, opc2 );
cerionc60c01e2004-12-02 20:19:22 +00001741
cerionf7da63d2004-12-09 19:04:57 +00001742 switch (opc1 >> 4) { // instr[27:24]
1743 case 0x0:
1744 case 0x1:
1745 /*
1746 Multiplies, extra load/store instructions: ARM ARM A3-3
cerionc60c01e2004-12-02 20:19:22 +00001747 */
cerionf7da63d2004-12-09 19:04:57 +00001748 if ( (opc1 & 0xE0) == 0x0 && (opc2 & 0x9) == 0x9 ) { // 000xxxxx && 1xx1
1749 if (opc2 == 0x9) {
1750 if ((opc1 & 0x1C) == 0x00) { // multiply (accumulate)
1751 goto decode_failure;
1752 }
1753 if ((opc1 & 0x18) == 0x08) { // multiply (accumulate) long
1754 goto decode_failure;
1755 }
1756 if ((opc1 & 0x1B) == 0x10) { // swap/swap byte
1757 goto decode_failure;
1758 }
1759 }
1760 if ( opc2 == 0xB ) {
1761 if ((opc1 & 0x04) == 0x00) { // load/store 1/2word reg offset
1762 goto decode_failure;
1763 } else { // load/store 1/2word imm offset
1764 goto decode_failure;
1765 }
1766 }
1767 if ((opc2 & 0xD) == 0xD) {
1768 if ((opc1 & 0x05) == 0x00) { // load/store 2 words reg offset
1769 goto decode_failure;
1770 }
1771 if ((opc1 & 0x05) == 0x04) { // load/store 2 words imm offset
1772 goto decode_failure;
1773 }
1774 if ((opc1 & 0x05) == 0x01) { // load/store signed 1/2word/byte reg offset
1775 goto decode_failure;
1776 }
1777 if ((opc1 & 0x05) == 0x05) { // load/store signed 1/2word/byte imm offset
1778 goto decode_failure;
1779 }
1780 }
1781 } /* endif: Multiplies, extra load/store... */
cerionc60c01e2004-12-02 20:19:22 +00001782
cerionf7da63d2004-12-09 19:04:57 +00001783 /*
1784 'Misc' Instructions: ARM ARM A3-4
1785 */
1786 if ((opc1 & 0xF9) == 0x10) { // 0001 0xx0
1787 opc_tmp = (opc1 >> 1) & 0x3;
1788 switch (opc2) {
1789 case 0x0:
1790 if ((opc_tmp & 0x1) == 0x0) { // move stat reg -> reg
1791 goto decode_failure;
1792 } else { // move reg -> stat reg
1793 goto decode_failure;
1794 }
cerionc60c01e2004-12-02 20:19:22 +00001795
cerionf7da63d2004-12-09 19:04:57 +00001796 case 0x1:
1797 if (opc_tmp == 0x1) { // branch/exchange instr set
1798 goto decode_failure;
1799 }
1800 if (opc_tmp == 0x3) { // count leading zeros
1801 goto decode_failure;
1802 }
1803 break;
1804
1805 case 0x3:
1806 if (opc_tmp == 0x1) { // branch & link/exchange instr set
1807 goto decode_failure;
1808 }
1809 break;
1810
1811 case 0x5: // enhanced dsp add/subtracts
1812 goto decode_failure;
1813
1814 case 0x7:
1815 if (opc_tmp == 0x1) { // software breakpoint
1816 if (cond != 0xE) { goto decode_failure; } // (unpredictable ARM ARM A3-4)
1817 goto decode_failure;
1818 }
1819 break;
1820
1821 case 0x8: case 0x9: case 0xA: // enhanced dsp multiplies
1822 case 0xB: case 0xC: case 0xD: case 0xE:
1823 goto decode_failure;
1824
1825 default: break;
1826 }
1827 } /* endif: 'Misc' Instructions... */
1828 // fall through...
1829
1830 case 0x2:
1831 case 0x3:
1832 if ((opc1 & 0xFB) == 0x30) goto decode_failure; // 0011 0x00 - (undefined)
1833
1834 /*
1835 A lonely 'MOV imm to status reg':
cerionc60c01e2004-12-02 20:19:22 +00001836 */
cerionf7da63d2004-12-09 19:04:57 +00001837 if ((opc1 & 0xFB) == 0x32) { // 0011 0x10
1838 goto decode_failure;
1839 }
cerionc60c01e2004-12-02 20:19:22 +00001840
cerionf7da63d2004-12-09 19:04:57 +00001841 /*
1842 Data Processing Instructions
cerion19e8a612004-12-10 10:18:58 +00001843 (if we get here, it's a valid dpi)
cerionc60c01e2004-12-02 20:19:22 +00001844 */
cerionf7da63d2004-12-09 19:04:57 +00001845 vex_printf("OPCODE: DPI\n");
1846 dis_dataproc( theInstr );
1847 break;
1848
1849
1850 /*
1851 Load/Store word | unsigned byte
1852 */
1853 case 0x6: case 0x7: // LOAD/STORE reg offset
1854 if ((opc2 & 0x1) == 0x1) { // Undefined: ARM ARM A3-2
1855 // CAB TODO (what todo?!)
1856 }
1857 case 0x4: case 0x5: // LOAD/STORE imm offset
1858 dis_loadstore_w_ub(theInstr);
1859 break;
1860
1861 /*
1862 Load/Store multiple
1863 */
1864 case 0x8: case 0x9:
1865 vex_printf("OPCODE: LOAD/STORE mult\n");
1866 dis_loadstore_mult(theInstr);
1867 break;
1868
1869
1870 /*
1871 Branch, Branch and Link
1872 */
1873 case 0xA: case 0xB: // B, BL
1874 // B(L): L=1 => return address stored in link register (R14)
1875 vex_printf("OPCODE: B(L)\n");
1876 dis_branch(theInstr);
cerion19e8a612004-12-10 10:18:58 +00001877 whatNext = Dis_StopHere;
cerionf7da63d2004-12-09 19:04:57 +00001878 break;
1879
1880
1881 /*
1882 Co-processor instructions
1883 */
1884 case 0xC: case 0xD: // co-pro load/store & double reg trxfrs
cerionc60c01e2004-12-02 20:19:22 +00001885 goto decode_failure;
cerionf7da63d2004-12-09 19:04:57 +00001886
1887 case 0xE:
1888 if ((opc2 & 0x1) == 0x0) { // co-pro data processing
1889 goto decode_failure;
1890 } else { // co-pro register transfers
1891 goto decode_failure;
1892 }
cerionc60c01e2004-12-02 20:19:22 +00001893
ceriona70a37b2004-12-03 18:54:08 +00001894
cerionf7da63d2004-12-09 19:04:57 +00001895 /*
1896 Software Interrupt
1897 */
1898 case 0xF: // swi
1899 goto decode_failure;
ceriona70a37b2004-12-03 18:54:08 +00001900
sewardjc2c87162004-11-25 13:07:02 +00001901 default:
1902 decode_failure:
1903 /* All decode failures end up here. */
1904 vex_printf("disInstr(arm): unhandled instruction: "
cerionfd7474a2004-12-03 11:16:42 +00001905 "0x%x\n", theInstr);
sewardjc2c87162004-11-25 13:07:02 +00001906 vpanic("armToIR: unimplemented insn");
1907
1908 } /* switch (opc) for the main (primary) opcode switch. */
1909
1910 decode_success:
1911 /* All decode successes end up here. */
cerionc60c01e2004-12-02 20:19:22 +00001912 vex_printf("disInstr(arm): success");
sewardjc2c87162004-11-25 13:07:02 +00001913 DIP("\n");
1914
1915 *size = 4;
1916 return whatNext;
1917}
1918
1919#undef DIP
1920#undef DIS
1921
1922/*--------------------------------------------------------------------*/
1923/*--- end guest-arm/toIR.c ---*/
1924/*--------------------------------------------------------------------*/