VirtualBox

source: vbox/trunk/src/VBox/Disassembler/DisasmCore-armv8.cpp@ 105810

Last change on this file since 105810 was 105810, checked in by vboxsync, 8 months ago

Disassembler/ARMv8: Implement disassembly of ccmp/ccmn register variants and add testcases, bugref:10394

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 25.8 KB
Line 
1/* $Id: DisasmCore-armv8.cpp 105810 2024-08-22 08:26:22Z vboxsync $ */
2/** @file
3 * VBox Disassembler - Core Components.
4 */
5
6/*
7 * Copyright (C) 2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_DIS
33#include <VBox/dis.h>
34#include <VBox/log.h>
35#include <iprt/asm.h> /* Required to get Armv8A64ConvertImmRImmS2Mask64() from armv8.h. */
36#include <iprt/armv8.h>
37#include <iprt/assert.h>
38#include <iprt/errcore.h>
39#include <iprt/param.h>
40#include <iprt/string.h>
41#include <iprt/stdarg.h>
42#include "DisasmInternal-armv8.h"
43
44
45/*********************************************************************************************************************************
46* Structures and Typedefs *
47*********************************************************************************************************************************/
48
49/** Parser callback.
50 * @remark no DECLCALLBACK() here because it's considered to be internal and
51 * there is no point in enforcing CDECL. */
52typedef int FNDISPARSEARMV8(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8INSNCLASS pInsnClass, PDISOPPARAM pParam, PCDISARMV8INSNPARAM pInsnParm, bool *pf64Bit);
53/** Pointer to a disassembler parser function. */
54typedef FNDISPARSEARMV8 *PFNDISPARSEARMV8;
55
56
57/** Opcode decoder callback.
58 * @remark no DECLCALLBACK() here because it's considered to be internal and
59 * there is no point in enforcing CDECL. */
60typedef uint32_t FNDISDECODEARMV8(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8INSNCLASS pInsnClass);
61/** Pointer to a disassembler parser function. */
62typedef FNDISDECODEARMV8 *PFNDISDECODEARMV8;
63
64
65/*********************************************************************************************************************************
66* Defined Constants And Macros *
67*********************************************************************************************************************************/
68
69
70/*********************************************************************************************************************************
71* Internal Functions *
72*********************************************************************************************************************************/
73/** @name Parsers
74 * @{ */
75static FNDISPARSEARMV8 disArmV8ParseIllegal;
76static FNDISPARSEARMV8 disArmV8ParseIs32Bit;
77static FNDISPARSEARMV8 disArmV8ParseImm;
78static FNDISPARSEARMV8 disArmV8ParseImmRel;
79static FNDISPARSEARMV8 disArmV8ParseImmAdr;
80static FNDISPARSEARMV8 disArmV8ParseReg;
81static FNDISPARSEARMV8 disArmV8ParseImmsImmrN;
82static FNDISPARSEARMV8 disArmV8ParseHw;
83static FNDISPARSEARMV8 disArmV8ParseCond;
84static FNDISPARSEARMV8 disArmV8ParsePState;
85static FNDISPARSEARMV8 disArmV8ParseSysReg;
86static FNDISPARSEARMV8 disArmV8ParseSh12;
87static FNDISPARSEARMV8 disArmV8ParseImmTbz;
88static FNDISPARSEARMV8 disArmV8ParseShift;
89static FNDISPARSEARMV8 disArmV8ParseShiftAmount;
90static FNDISPARSEARMV8 disArmV8ParseImmMemOff;
91/** @} */
92
93
94/** @name Decoders
95 * @{ */
96static FNDISDECODEARMV8 disArmV8DecodeIllegal;
97static FNDISDECODEARMV8 disArmV8DecodeLookup;
98/** @} */
99
100
101/*********************************************************************************************************************************
102* Global Variables *
103*********************************************************************************************************************************/
104/** Parser opcode table for full disassembly. */
105static PFNDISPARSEARMV8 const g_apfnDisasm[kDisParmParseMax] =
106{
107 disArmV8ParseIllegal,
108 disArmV8ParseIs32Bit,
109 disArmV8ParseImm,
110 disArmV8ParseImmRel,
111 disArmV8ParseImmAdr,
112 disArmV8ParseReg,
113 disArmV8ParseImmsImmrN,
114 disArmV8ParseHw,
115 disArmV8ParseCond,
116 disArmV8ParsePState,
117 NULL,
118 disArmV8ParseSysReg,
119 disArmV8ParseSh12,
120 disArmV8ParseImmTbz,
121 disArmV8ParseShift,
122 disArmV8ParseShiftAmount,
123 disArmV8ParseImmMemOff
124};
125
126
127/** Opcode decoder table. */
128static PFNDISDECODEARMV8 const g_apfnOpcDecode[kDisArmV8OpcDecodeMax] =
129{
130 disArmV8DecodeIllegal,
131 disArmV8DecodeLookup,
132};
133
134
135DECLINLINE(uint32_t) disArmV8ExtractBitVecFromInsn(uint32_t u32Insn, uint8_t idxBitStart, uint8_t cBits)
136{
137 uint32_t fMask = RT_BIT_32(idxBitStart + cBits) - 1;
138 return (u32Insn & fMask) >> idxBitStart;
139}
140
141
142DECLINLINE(int32_t) disArmV8ExtractBitVecFromInsnSignExtend(uint32_t u32Insn, uint8_t idxBitStart, uint8_t cBits)
143{
144 uint32_t fMask = RT_BIT_32(idxBitStart + cBits) - 1;
145 uint32_t fSign = ~(UINT32_MAX & (RT_BIT_32(cBits - 1) - 1));
146 uint32_t fValue = (u32Insn & fMask) >> idxBitStart;
147 if (fValue & fSign)
148 return (int32_t)(fValue | fSign);
149
150 return (int32_t)fValue;
151}
152
153
154static int disArmV8ParseIllegal(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8INSNCLASS pInsnClass, PDISOPPARAM pParam, PCDISARMV8INSNPARAM pInsnParm, bool *pf64Bit)
155{
156 RT_NOREF(pDis, u32Insn, pInsnClass, pParam, pInsnParm, pf64Bit);
157 AssertFailed();
158 return VERR_INTERNAL_ERROR;
159}
160
161
162static int disArmV8ParseIs32Bit(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8INSNCLASS pInsnClass, PDISOPPARAM pParam, PCDISARMV8INSNPARAM pInsnParm, bool *pf64Bit)
163{
164 RT_NOREF(pDis, pInsnClass, pParam);
165
166 AssertReturn(pInsnParm->idxBitStart < 32 && pInsnParm->cBits == 1, VERR_INTERNAL_ERROR_2);
167 *pf64Bit = RT_BOOL(u32Insn & RT_BIT_32(pInsnParm->idxBitStart));
168 return VINF_SUCCESS;
169}
170
171
172static int disArmV8ParseImm(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8INSNCLASS pInsnClass, PDISOPPARAM pParam, PCDISARMV8INSNPARAM pInsnParm, bool *pf64Bit)
173{
174 RT_NOREF(pDis, pInsnClass, pf64Bit);
175
176 AssertReturn(pInsnParm->idxBitStart + pInsnParm->cBits < 32, VERR_INTERNAL_ERROR_2);
177
178 pParam->uValue = disArmV8ExtractBitVecFromInsn(u32Insn, pInsnParm->idxBitStart, pInsnParm->cBits);
179 if (pInsnParm->cBits <= 8)
180 {
181 pParam->armv8.cb = sizeof(uint8_t);
182 pParam->fUse |= DISUSE_IMMEDIATE8;
183 }
184 else if (pInsnParm->cBits <= 16)
185 {
186 pParam->armv8.cb = sizeof(uint16_t);
187 pParam->fUse |= DISUSE_IMMEDIATE16;
188 }
189 else if (pInsnParm->cBits <= 32)
190 {
191 pParam->armv8.cb = sizeof(uint32_t);
192 pParam->fUse |= DISUSE_IMMEDIATE32;
193 }
194 else
195 AssertReleaseFailed();
196
197 return VINF_SUCCESS;
198}
199
200
201static int disArmV8ParseImmRel(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8INSNCLASS pInsnClass, PDISOPPARAM pParam, PCDISARMV8INSNPARAM pInsnParm, bool *pf64Bit)
202{
203 RT_NOREF(pDis, pInsnClass, pf64Bit);
204
205 AssertReturn(pInsnParm->idxBitStart + pInsnParm->cBits < 32, VERR_INTERNAL_ERROR_2);
206
207 pParam->uValue = (int64_t)disArmV8ExtractBitVecFromInsnSignExtend(u32Insn, pInsnParm->idxBitStart, pInsnParm->cBits);
208 if (pInsnParm->cBits <= 8)
209 {
210 pParam->armv8.cb = sizeof(int8_t);
211 pParam->fUse |= DISUSE_IMMEDIATE8_REL;
212 }
213 else if (pInsnParm->cBits <= 16)
214 {
215 pParam->armv8.cb = sizeof(int16_t);
216 pParam->fUse |= DISUSE_IMMEDIATE16_REL;
217 }
218 else if (pInsnParm->cBits <= 32)
219 {
220 pParam->armv8.cb = sizeof(int32_t);
221 pParam->fUse |= DISUSE_IMMEDIATE32_REL;
222 }
223 else
224 AssertReleaseFailed();
225
226 return VINF_SUCCESS;
227}
228
229
230static int disArmV8ParseImmAdr(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8INSNCLASS pInsnClass, PDISOPPARAM pParam, PCDISARMV8INSNPARAM pInsnParm, bool *pf64Bit)
231{
232 RT_NOREF(pDis, pInsnClass, pf64Bit, pInsnParm);
233
234 pParam->uValue = disArmV8ExtractBitVecFromInsn(u32Insn, 5, 19);
235 pParam->uValue |= disArmV8ExtractBitVecFromInsn(u32Insn, 29, 2) << 29;
236 pParam->fUse |= DISUSE_IMMEDIATE32;
237 return VINF_SUCCESS;
238}
239
240
241static int disArmV8ParseReg(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8INSNCLASS pInsnClass, PDISOPPARAM pParam, PCDISARMV8INSNPARAM pInsnParm, bool *pf64Bit)
242{
243 RT_NOREF(pDis, pInsnClass);
244 pParam->armv8.Reg.idxGenReg = disArmV8ExtractBitVecFromInsn(u32Insn, pInsnParm->idxBitStart, pInsnParm->cBits);
245 pParam->armv8.cb = *pf64Bit ? sizeof(uint64_t) : sizeof(uint32_t);
246 pParam->fUse |= (*pf64Bit || (pParam->armv8.enmType == kDisArmv8OpParmAddrInGpr))
247 ? DISUSE_REG_GEN64
248 : DISUSE_REG_GEN32;
249 return VINF_SUCCESS;
250}
251
252
253static int disArmV8ParseImmsImmrN(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8INSNCLASS pInsnClass, PDISOPPARAM pParam, PCDISARMV8INSNPARAM pInsnParm, bool *pf64Bit)
254{
255 RT_NOREF(pDis);
256 AssertReturn(pInsnParm->cBits == 13, VERR_INTERNAL_ERROR_2);
257
258 uint32_t u32ImmRaw = disArmV8ExtractBitVecFromInsn(u32Insn, pInsnParm->idxBitStart, pInsnParm->cBits);
259 /* N bit must be 0 if 32-bit variant is used. */
260 if ( ( (u32ImmRaw & RT_BIT_32(12))
261 && !*pf64Bit)
262 || ( !(u32ImmRaw & RT_BIT_32(12))
263 && *pf64Bit
264 && (pInsnClass->fClass & DISARMV8INSNCLASS_F_N_FORCED_1_ON_64BIT)))
265 return VERR_DIS_INVALID_OPCODE;
266
267 uint32_t uImm7SizeLen = ((u32ImmRaw & RT_BIT_32(12)) >> 6) | (u32ImmRaw & 0x3f);
268 uint32_t uImm6Rotations = (u32ImmRaw >> 6) & 0x3f;
269 pParam->uValue = *pf64Bit
270 ? Armv8A64ConvertImmRImmS2Mask64(uImm7SizeLen, uImm6Rotations)
271 : Armv8A64ConvertImmRImmS2Mask32(uImm7SizeLen, uImm6Rotations);
272 pParam->armv8.cb = pParam->uValue > UINT32_MAX ? sizeof(uint64_t) : sizeof(uint32_t);
273 pParam->fUse |= pParam->uValue > UINT32_MAX ? DISUSE_IMMEDIATE64 : DISUSE_IMMEDIATE32;
274 return VINF_SUCCESS;
275}
276
277
278static int disArmV8ParseHw(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8INSNCLASS pInsnClass, PDISOPPARAM pParam, PCDISARMV8INSNPARAM pInsnParm, bool *pf64Bit)
279{
280 RT_NOREF(pDis, pInsnClass, pParam);
281 Assert(pInsnParm->cBits == 2);
282
283 uint32_t u32 = disArmV8ExtractBitVecFromInsn(u32Insn, pInsnParm->idxBitStart, pInsnParm->cBits);
284 /* hw<1> must be 0 if this is the 32-bit variant. */
285 if ( !*pf64Bit
286 && (u32 & RT_BIT_32(1)))
287 return VERR_DIS_INVALID_OPCODE;
288
289 Assert(pParam->armv8.enmType == kDisArmv8OpParmImm);
290 Assert(pParam->fUse & (DISUSE_IMMEDIATE8 | DISUSE_IMMEDIATE16 | DISUSE_IMMEDIATE32));
291 if (u32)
292 {
293 pParam->armv8.enmShift = kDisArmv8OpParmShiftLeft;
294 pParam->armv8.u.cShift = ((uint8_t)u32 & 0x3) << 4;
295 }
296 return VINF_SUCCESS;
297}
298
299
300static int disArmV8ParseCond(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8INSNCLASS pInsnClass, PDISOPPARAM pParam, PCDISARMV8INSNPARAM pInsnParm, bool *pf64Bit)
301{
302 RT_NOREF(pInsnClass, pParam, pf64Bit);
303 Assert(pInsnParm->cBits <= 4);
304 if (pParam)
305 {
306 /* Conditional as a parameter (CCMP/CCMN). */
307 Assert(pParam->armv8.enmType == kDisArmv8OpParmCond);
308 pParam->armv8.Reg.enmCond = (DISARMV8INSTRCOND)disArmV8ExtractBitVecFromInsn(u32Insn, pInsnParm->idxBitStart, pInsnParm->cBits);
309 }
310 else /* Conditional for the base instruction. */
311 pDis->armv8.enmCond = (DISARMV8INSTRCOND)disArmV8ExtractBitVecFromInsn(u32Insn, pInsnParm->idxBitStart, pInsnParm->cBits);
312 return VINF_SUCCESS;
313}
314
315
316static int disArmV8ParsePState(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8INSNCLASS pInsnClass, PDISOPPARAM pParam, PCDISARMV8INSNPARAM pInsnParm, bool *pf64Bit)
317{
318 RT_NOREF(pDis, u32Insn, pInsnClass, pParam, pInsnParm, pf64Bit);
319 //AssertFailed();
320 /** @todo */
321 return VINF_SUCCESS;
322}
323
324
325static int disArmV8ParseSysReg(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8INSNCLASS pInsnClass, PDISOPPARAM pParam, PCDISARMV8INSNPARAM pInsnParm, bool *pf64Bit)
326{
327 RT_NOREF(pDis, pInsnClass, pf64Bit);
328 AssertReturn(pInsnParm->cBits == 15, VERR_INTERNAL_ERROR_2);
329
330 /* Assumes a op0:op1:CRn:CRm:op2 encoding in the instruction starting at the given bit position. */
331 uint32_t u32ImmRaw = disArmV8ExtractBitVecFromInsn(u32Insn, pInsnParm->idxBitStart, pInsnParm->cBits);
332 pParam->armv8.Reg.idSysReg = ARMV8_AARCH64_SYSREG_ID_CREATE(2 + ((u32ImmRaw >> 14) & 0x1),
333 (u32ImmRaw >> 11) & 0x7,
334 (u32ImmRaw >> 7) & 0xf,
335 (u32ImmRaw >> 3) & 0xf,
336 u32ImmRaw & 0x7);
337 pParam->armv8.cb = 0;
338 pParam->fUse |= DISUSE_REG_SYSTEM;
339 return VINF_SUCCESS;
340}
341
342
343static int disArmV8ParseSh12(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8INSNCLASS pInsnClass, PDISOPPARAM pParam, PCDISARMV8INSNPARAM pInsnParm, bool *pf64Bit)
344{
345 RT_NOREF(pDis, pInsnClass, pf64Bit);
346 Assert(pInsnParm->cBits == 1);
347 if (u32Insn & RT_BIT_32(pInsnParm->idxBitStart))
348 {
349 /* Shift the immediate pointed to. */
350 pParam->uValue <<= 12;
351
352 /* Re-evaluate the immediate data size. */
353 pParam->fUse &= ~(DISUSE_IMMEDIATE8 | DISUSE_IMMEDIATE16 | DISUSE_IMMEDIATE32);
354 if (pParam->uValue <= UINT8_MAX)
355 {
356 pParam->armv8.cb = sizeof(uint8_t);
357 pParam->fUse |= DISUSE_IMMEDIATE8;
358 }
359 else if (pParam->uValue <= UINT16_MAX)
360 {
361 pParam->armv8.cb = sizeof(uint16_t);
362 pParam->fUse |= DISUSE_IMMEDIATE16;
363 }
364 else if (pParam->uValue <= UINT32_MAX)
365 {
366 pParam->armv8.cb = sizeof(uint32_t);
367 pParam->fUse |= DISUSE_IMMEDIATE32;
368 }
369 else
370 AssertReleaseFailed();
371
372 }
373 return VINF_SUCCESS;
374}
375
376
377static int disArmV8ParseImmTbz(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8INSNCLASS pInsnClass, PDISOPPARAM pParam, PCDISARMV8INSNPARAM pInsnParm, bool *pf64Bit)
378{
379 RT_NOREF(pDis, pInsnClass, pf64Bit);
380
381 AssertReturn(!pInsnParm->idxBitStart && !pInsnParm->cBits, VERR_INTERNAL_ERROR_2);
382
383 pParam->uValue = disArmV8ExtractBitVecFromInsn(u32Insn, 19, 5);
384 pParam->uValue |= (u32Insn & RT_BIT_32(31)) >> 26;
385
386 pParam->armv8.cb = sizeof(uint8_t);
387 pParam->fUse |= DISUSE_IMMEDIATE8;
388 return VINF_SUCCESS;
389}
390
391
392static int disArmV8ParseShift(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8INSNCLASS pInsnClass, PDISOPPARAM pParam, PCDISARMV8INSNPARAM pInsnParm, bool *pf64Bit)
393{
394 RT_NOREF(pDis, pInsnClass, pf64Bit);
395
396 AssertReturn(pInsnParm->cBits == 2, VERR_INTERNAL_ERROR_2);
397
398 uint32_t u32Shift = disArmV8ExtractBitVecFromInsn(u32Insn, pInsnParm->idxBitStart, pInsnParm->cBits);
399 switch (u32Shift)
400 {
401 case 0: pParam->armv8.enmShift = kDisArmv8OpParmShiftLeft; break;
402 case 1: pParam->armv8.enmShift = kDisArmv8OpParmShiftRight; break;
403 case 2: pParam->armv8.enmShift = kDisArmv8OpParmShiftArithRight; break;
404 case 3: pParam->armv8.enmShift = kDisArmv8OpParmShiftRotate; break;
405 default:
406 AssertReleaseFailed();
407 }
408 return VINF_SUCCESS;
409}
410
411
412static int disArmV8ParseShiftAmount(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8INSNCLASS pInsnClass, PDISOPPARAM pParam, PCDISARMV8INSNPARAM pInsnParm, bool *pf64Bit)
413{
414 RT_NOREF(pDis, pInsnClass, pf64Bit);
415
416 uint32_t u32Amount = disArmV8ExtractBitVecFromInsn(u32Insn, pInsnParm->idxBitStart, pInsnParm->cBits);
417 /* For a 32-bit operand it is impossible to shift/rotate more than 31 bits. */
418 if ( !*pf64Bit
419 && u32Amount > 31)
420 return VERR_DIS_INVALID_OPCODE;
421
422 Assert(pParam->armv8.enmShift != kDisArmv8OpParmShiftNone);
423 Assert(u32Amount < 64);
424 pParam->armv8.u.cShift = (uint8_t)u32Amount;
425 /* Any shift operation with a 0 is essentially no shift being applied. */
426 if (pParam->armv8.u.cShift == 0)
427 pParam->armv8.enmShift = kDisArmv8OpParmShiftNone;
428 return VINF_SUCCESS;
429}
430
431
432static int disArmV8ParseImmMemOff(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8INSNCLASS pInsnClass, PDISOPPARAM pParam, PCDISARMV8INSNPARAM pInsnParm, bool *pf64Bit)
433{
434 RT_NOREF(pDis, pInsnClass, pf64Bit);
435
436 AssertReturn(pInsnParm->cBits <= 12, VERR_INTERNAL_ERROR_2);
437
438 uint8_t const uScale = *pf64Bit ? 8 : 4;
439 pParam->armv8.u.offBase = disArmV8ExtractBitVecFromInsn(u32Insn, pInsnParm->idxBitStart, pInsnParm->cBits) * uScale;
440 pParam->armv8.cb = sizeof(uint16_t);
441 return VINF_SUCCESS;
442}
443
444
445static uint32_t disArmV8DecodeIllegal(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8INSNCLASS pInsnClass)
446{
447 RT_NOREF(pDis, u32Insn, pInsnClass);
448 AssertFailed();
449 return UINT32_MAX;
450}
451
452
453static uint32_t disArmV8DecodeLookup(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8INSNCLASS pInsnClass)
454{
455 RT_NOREF(pDis);
456
457 for (uint32_t i = 0; i < pInsnClass->Hdr.cDecode; i++)
458 {
459 PCDISARMV8OPCODE pOp = &pInsnClass->paOpcodes[i];
460 if (u32Insn == pOp->fValue)
461 return i;
462 }
463
464 return UINT32_MAX;
465}
466
467
468/**
469 * Looks for possible alias conversions for the given disassembler state.
470 *
471 * @param pDis The disassembler state to process.
472 */
473static void disArmV8A64InsnAliasesProcess(PDISSTATE pDis)
474{
475#define DIS_ARMV8_ALIAS(a_Name) s_DisArmv8Alias ## a_Name
476#define DIS_ARMV8_ALIAS_CREATE(a_Name, a_szOpcode, a_uOpcode, a_fOpType) static const DISOPCODE DIS_ARMV8_ALIAS(a_Name) = OP(a_szOpcode, 0, 0, 0, a_uOpcode, 0, 0, 0, a_fOpType)
477#define DIS_ARMV8_ALIAS_REF(a_Name) &DIS_ARMV8_ALIAS(a_Name)
478 switch (pDis->pCurInstr->uOpcode)
479 {
480 case OP_ARMV8_A64_ORR:
481 {
482 /* Check for possible MOV conversion for the register variant when: shift is None and the first source is the zero register. */
483 Assert(pDis->aParams[1].armv8.enmType == kDisArmv8OpParmGpr);
484
485 if ( pDis->aParams[2].armv8.enmType == kDisArmv8OpParmGpr
486 && pDis->aParams[2].armv8.enmShift == kDisArmv8OpParmShiftNone
487 && pDis->aParams[1].armv8.Reg.idxGenReg == ARMV8_A64_REG_XZR)
488 {
489 DIS_ARMV8_ALIAS_CREATE(Mov, "mov", OP_ARMV8_A64_MOV, DISOPTYPE_HARMLESS);
490 pDis->pCurInstr = DIS_ARMV8_ALIAS_REF(Mov);
491 pDis->aParams[1] = pDis->aParams[2];
492 pDis->aParams[2].armv8.enmType = kDisArmv8OpParmNone;
493 }
494 /** @todo Immediate variant. */
495 break;
496 }
497 case OP_ARMV8_A64_SUBS:
498 {
499 Assert(pDis->aParams[0].armv8.enmType == kDisArmv8OpParmGpr);
500 if (pDis->aParams[0].armv8.Reg.idxGenReg == ARMV8_A64_REG_XZR)
501 {
502 DIS_ARMV8_ALIAS_CREATE(Cmp, "cmp", OP_ARMV8_A64_CMP, DISOPTYPE_HARMLESS);
503 pDis->pCurInstr = DIS_ARMV8_ALIAS_REF(Cmp);
504 pDis->aParams[0] = pDis->aParams[1];
505 pDis->aParams[1] = pDis->aParams[2];
506 pDis->aParams[2].armv8.enmType = kDisArmv8OpParmNone;
507 }
508 break;
509 }
510 default:
511 break; /* No conversion */
512 }
513#undef DIS_ARMV8_ALIAS_REF
514#undef DIS_ARMV8_ALIAS_CREATE
515#undef DIS_ARMV8_ALIAS
516}
517
518
519static int disArmV8A64ParseInstruction(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8OPCODE pOp, PCDISARMV8INSNCLASS pInsnClass)
520{
521 AssertPtr(pOp);
522 AssertPtr(pDis);
523 Assert((u32Insn & pInsnClass->fFixedInsn) == pOp->fValue);
524
525 /* Should contain the parameter type on input. */
526 pDis->aParams[0].armv8.enmType = pInsnClass->aenmParamTypes[0];
527 pDis->aParams[1].armv8.enmType = pInsnClass->aenmParamTypes[1];
528 pDis->aParams[2].armv8.enmType = pInsnClass->aenmParamTypes[2];
529 pDis->aParams[3].armv8.enmType = pInsnClass->aenmParamTypes[3];
530 pDis->aParams[0].armv8.enmShift = kDisArmv8OpParmShiftNone;
531 pDis->aParams[1].armv8.enmShift = kDisArmv8OpParmShiftNone;
532 pDis->aParams[2].armv8.enmShift = kDisArmv8OpParmShiftNone;
533 pDis->aParams[3].armv8.enmShift = kDisArmv8OpParmShiftNone;
534 pDis->armv8.enmCond = kDisArmv8InstrCond_Al;
535
536 pDis->pCurInstr = &pOp->Opc;
537 Assert(&pOp->Opc != &g_ArmV8A64InvalidOpcode[0]);
538
539 bool f64Bit = false;
540
541 if (pInsnClass->fClass & DISARMV8INSNCLASS_F_SF)
542 f64Bit = RT_BOOL(u32Insn & RT_BIT_32(31));
543 else if (pInsnClass->fClass & DISARMV8INSNCLASS_F_FORCED_64BIT)
544 f64Bit = true;
545
546 int rc = VINF_SUCCESS;
547 PCDISARMV8INSNPARAM pDecode = &pInsnClass->paParms[0];
548 while ( (pDecode->idxParse != kDisParmParseNop)
549 && RT_SUCCESS(rc))
550 {
551 rc = g_apfnDisasm[pDecode->idxParse](pDis, u32Insn, pInsnClass,
552 pDecode->idxParam != DIS_ARMV8_INSN_PARAM_UNSET
553 ? &pDis->aParams[pDecode->idxParam]
554 : NULL,
555 pDecode, &f64Bit);
556 pDecode++;
557 }
558
559 /* If parameter parsing returned an invalid opcode error the encoding is invalid. */
560 if (RT_SUCCESS(rc)) /** @todo Introduce flag to switch alias conversion on/off. */
561 disArmV8A64InsnAliasesProcess(pDis);
562 else if (rc == VERR_DIS_INVALID_OPCODE)
563 {
564 pDis->pCurInstr = &g_ArmV8A64InvalidOpcode[0];
565
566 pDis->aParams[0].armv8.enmType = kDisArmv8OpParmNone;
567 pDis->aParams[1].armv8.enmType = kDisArmv8OpParmNone;
568 pDis->aParams[2].armv8.enmType = kDisArmv8OpParmNone;
569 pDis->aParams[3].armv8.enmType = kDisArmv8OpParmNone;
570 }
571 pDis->rc = rc;
572 return rc;
573}
574
575
576static int disArmV8A64ParseInvOpcode(PDISSTATE pDis)
577{
578 pDis->pCurInstr = &g_ArmV8A64InvalidOpcode[0];
579 pDis->rc = VERR_DIS_INVALID_OPCODE;
580 return VERR_DIS_INVALID_OPCODE;
581}
582
583
584static int disInstrArmV8DecodeWorker(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8DECODEHDR pHdr)
585{
586 while ( pHdr
587 && pHdr->enmDecodeType != kDisArmV8DecodeType_InsnClass)
588 {
589 if (pHdr->enmDecodeType == kDisArmV8DecodeType_Map)
590 {
591 PCDISARMV8DECODEMAP pMap = (PCDISARMV8DECODEMAP)pHdr;
592
593 uint32_t idxNext = (u32Insn & pMap->fMask) >> pMap->cShift;
594 if (RT_LIKELY(idxNext < pMap->Hdr.cDecode))
595 pHdr = pMap->papNext[idxNext];
596 else
597 {
598 pHdr = NULL;
599 break;
600 }
601 }
602 else
603 {
604 Assert(pHdr->enmDecodeType == kDisArmV8DecodeType_Table);
605 PCDISARMV8DECODETBL pTbl = (PCDISARMV8DECODETBL)pHdr;
606
607 /* Walk all entries in the table and select the best match. */
608 pHdr = NULL;
609 for (uint32_t i = 0; i < pTbl->Hdr.cDecode; i++)
610 {
611 PCDISARMV8DECODETBLENTRY pEntry = &pTbl->paEntries[i];
612 if ((u32Insn & pEntry->fMask) == pEntry->fValue)
613 {
614 pHdr = pEntry->pHdrNext;
615 break;
616 }
617 }
618 }
619 }
620
621 if (pHdr)
622 {
623 Assert(pHdr->enmDecodeType == kDisArmV8DecodeType_InsnClass);
624 PCDISARMV8INSNCLASS pInsnClass = (PCDISARMV8INSNCLASS)pHdr;
625
626 /* Decode the opcode from the instruction class. */
627 uint32_t uOpcRaw = 0;
628 if (pInsnClass->Hdr.cDecode > 1)
629 {
630 uOpcRaw = (u32Insn & pInsnClass->fMask) >> pInsnClass->cShift;
631 if (pInsnClass->enmOpcDecode != kDisArmV8OpcDecodeNop)
632 uOpcRaw = g_apfnOpcDecode[pInsnClass->enmOpcDecode](pDis, uOpcRaw, pInsnClass);
633 }
634
635 if (uOpcRaw < pInsnClass->Hdr.cDecode)
636 {
637 PCDISARMV8OPCODE pOp = &pInsnClass->paOpcodes[uOpcRaw];
638 return disArmV8A64ParseInstruction(pDis, u32Insn, pOp, pInsnClass);
639 }
640 }
641
642 return disArmV8A64ParseInvOpcode(pDis);
643}
644
645
646/**
647 * Internal worker for DISInstrEx and DISInstrWithPrefetchedBytes.
648 *
649 * @returns VBox status code.
650 * @param pDis Initialized disassembler state.
651 * @param paOneByteMap The one byte opcode map to use.
652 * @param pcbInstr Where to store the instruction size. Can be NULL.
653 */
654DECLHIDDEN(int) disInstrWorkerArmV8(PDISSTATE pDis, PCDISOPCODE paOneByteMap, uint32_t *pcbInstr)
655{
656 RT_NOREF(paOneByteMap);
657
658 if (pDis->uCpuMode == DISCPUMODE_ARMV8_A64)
659 {
660 *pcbInstr = sizeof(uint32_t);
661
662 /* Instructions are always little endian and 4 bytes. */
663 uint32_t u32Insn = disReadDWord(pDis, 0 /*offInstr*/);
664 if (RT_FAILURE(pDis->rc))
665 return pDis->rc;
666
667 /** @todo r=bird: This is a waste of time if the host is little endian... */
668 pDis->Instr.u32 = RT_LE2H_U32(u32Insn);
669 pDis->cbInstr = sizeof(u32Insn);
670
671 return disInstrArmV8DecodeWorker(pDis, u32Insn, &g_aArmV8A64InsnDecodeL0.Hdr);
672 }
673
674 AssertReleaseFailed();
675 return VERR_NOT_IMPLEMENTED;
676}
677
678
679/**
680 * Inlined worker that initializes the disassembler state.
681 *
682 * @returns The primary opcode map to use.
683 * @param pDis The disassembler state.
684 * @param uInstrAddr The instruction address.
685 * @param enmCpuMode The CPU mode.
686 * @param fFilter The instruction filter settings.
687 * @param pfnReadBytes The byte reader, can be NULL.
688 * @param pvUser The user data for the reader.
689 */
690DECLHIDDEN(PCDISOPCODE) disInitializeStateArmV8(PDISSTATE pDis, DISCPUMODE enmCpuMode, uint32_t fFilter)
691{
692 RT_NOREF(pDis, enmCpuMode, fFilter);
693 return NULL;
694}
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette