VirtualBox

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

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

Disassembler/ARMv8: Implement decoding of the ldp/stp unsigned variant instructions and add testcases, bugref:10394

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 29.0 KB
Line 
1/* $Id: DisasmCore-armv8.cpp 105830 2024-08-22 18:13:37Z 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, PCDISARMV8OPCODE pOp, 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 disArmV8ParseSize;
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;
91static FNDISPARSEARMV8 disArmV8ParseSImmMemOff;
92/** @} */
93
94
95/** @name Decoders
96 * @{ */
97static FNDISDECODEARMV8 disArmV8DecodeIllegal;
98static FNDISDECODEARMV8 disArmV8DecodeLookup;
99static FNDISDECODEARMV8 disArmV8DecodeCollate;
100/** @} */
101
102
103/*********************************************************************************************************************************
104* Global Variables *
105*********************************************************************************************************************************/
106/** Parser opcode table for full disassembly. */
107static PFNDISPARSEARMV8 const g_apfnDisasm[kDisParmParseMax] =
108{
109 disArmV8ParseIllegal,
110 disArmV8ParseSize,
111 disArmV8ParseImm,
112 disArmV8ParseImmRel,
113 disArmV8ParseImmAdr,
114 disArmV8ParseReg,
115 disArmV8ParseImmsImmrN,
116 disArmV8ParseHw,
117 disArmV8ParseCond,
118 disArmV8ParsePState,
119 NULL,
120 disArmV8ParseSysReg,
121 disArmV8ParseSh12,
122 disArmV8ParseImmTbz,
123 disArmV8ParseShift,
124 disArmV8ParseShiftAmount,
125 disArmV8ParseImmMemOff,
126 disArmV8ParseSImmMemOff
127};
128
129
130/** Opcode decoder table. */
131static PFNDISDECODEARMV8 const g_apfnOpcDecode[kDisArmV8OpcDecodeMax] =
132{
133 disArmV8DecodeIllegal,
134 disArmV8DecodeLookup,
135 disArmV8DecodeCollate
136};
137
138
139DECLINLINE(uint32_t) disArmV8ExtractBitVecFromInsn(uint32_t u32Insn, uint8_t idxBitStart, uint8_t cBits)
140{
141 uint32_t fMask = (uint32_t)(RT_BIT_64(idxBitStart + cBits) - 1);
142 return (u32Insn & fMask) >> idxBitStart;
143}
144
145
146DECLINLINE(int32_t) disArmV8ExtractBitVecFromInsnSignExtend(uint32_t u32Insn, uint8_t idxBitStart, uint8_t cBits)
147{
148 uint32_t fMask = RT_BIT_32(idxBitStart + cBits) - 1;
149 uint32_t fSign = ~(UINT32_MAX & (RT_BIT_32(cBits - 1) - 1));
150 uint32_t fValue = (u32Insn & fMask) >> idxBitStart;
151 if (fValue & fSign)
152 return (int32_t)(fValue | fSign);
153
154 return (int32_t)fValue;
155}
156
157
158static int disArmV8ParseIllegal(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8OPCODE pOp, PCDISARMV8INSNCLASS pInsnClass, PDISOPPARAM pParam, PCDISARMV8INSNPARAM pInsnParm, bool *pf64Bit)
159{
160 RT_NOREF(pDis, u32Insn, pOp, pInsnClass, pParam, pInsnParm, pf64Bit);
161 AssertFailed();
162 return VERR_INTERNAL_ERROR;
163}
164
165
166static int disArmV8ParseSize(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8OPCODE pOp, PCDISARMV8INSNCLASS pInsnClass, PDISOPPARAM pParam, PCDISARMV8INSNPARAM pInsnParm, bool *pf64Bit)
167{
168 RT_NOREF(pInsnClass, pParam);
169
170 Assert(pInsnParm->cBits == 2);
171 uint32_t u32Size = disArmV8ExtractBitVecFromInsn(u32Insn, pInsnParm->idxBitStart, pInsnParm->cBits);
172 switch (u32Size)
173 {
174 case 0: pDis->armv8.cbOperand = sizeof(uint8_t); break;
175 case 1: pDis->armv8.cbOperand = sizeof(uint16_t); break;
176 case 2: pDis->armv8.cbOperand = sizeof(uint32_t); break;
177 case 3: pDis->armv8.cbOperand = sizeof(uint64_t); break;
178 default:
179 AssertReleaseFailed();
180 }
181 *pf64Bit = pDis->armv8.cbOperand == sizeof(uint64_t)
182 || (pOp->fFlags & DISARMV8INSNCLASS_F_FORCED_64BIT);
183 return VINF_SUCCESS;
184}
185
186
187static int disArmV8ParseImm(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8OPCODE pOp, PCDISARMV8INSNCLASS pInsnClass, PDISOPPARAM pParam, PCDISARMV8INSNPARAM pInsnParm, bool *pf64Bit)
188{
189 RT_NOREF(pDis, pOp, pInsnClass, pf64Bit);
190
191 AssertReturn(pInsnParm->idxBitStart + pInsnParm->cBits < 32, VERR_INTERNAL_ERROR_2);
192
193 pParam->uValue = disArmV8ExtractBitVecFromInsn(u32Insn, pInsnParm->idxBitStart, pInsnParm->cBits);
194 if (pInsnParm->cBits <= 8)
195 {
196 pParam->armv8.cb = sizeof(uint8_t);
197 pParam->fUse |= DISUSE_IMMEDIATE8;
198 }
199 else if (pInsnParm->cBits <= 16)
200 {
201 pParam->armv8.cb = sizeof(uint16_t);
202 pParam->fUse |= DISUSE_IMMEDIATE16;
203 }
204 else if (pInsnParm->cBits <= 32)
205 {
206 pParam->armv8.cb = sizeof(uint32_t);
207 pParam->fUse |= DISUSE_IMMEDIATE32;
208 }
209 else
210 AssertReleaseFailed();
211
212 return VINF_SUCCESS;
213}
214
215
216static int disArmV8ParseImmRel(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8OPCODE pOp, PCDISARMV8INSNCLASS pInsnClass, PDISOPPARAM pParam, PCDISARMV8INSNPARAM pInsnParm, bool *pf64Bit)
217{
218 RT_NOREF(pDis, pOp, pInsnClass, pf64Bit);
219
220 AssertReturn(pInsnParm->idxBitStart + pInsnParm->cBits < 32, VERR_INTERNAL_ERROR_2);
221
222 pParam->uValue = (int64_t)disArmV8ExtractBitVecFromInsnSignExtend(u32Insn, pInsnParm->idxBitStart, pInsnParm->cBits);
223 if (pInsnParm->cBits <= 8)
224 {
225 pParam->armv8.cb = sizeof(int8_t);
226 pParam->fUse |= DISUSE_IMMEDIATE8_REL;
227 }
228 else if (pInsnParm->cBits <= 16)
229 {
230 pParam->armv8.cb = sizeof(int16_t);
231 pParam->fUse |= DISUSE_IMMEDIATE16_REL;
232 }
233 else if (pInsnParm->cBits <= 32)
234 {
235 pParam->armv8.cb = sizeof(int32_t);
236 pParam->fUse |= DISUSE_IMMEDIATE32_REL;
237 }
238 else
239 AssertReleaseFailed();
240
241 return VINF_SUCCESS;
242}
243
244
245static int disArmV8ParseImmAdr(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8OPCODE pOp, PCDISARMV8INSNCLASS pInsnClass, PDISOPPARAM pParam, PCDISARMV8INSNPARAM pInsnParm, bool *pf64Bit)
246{
247 RT_NOREF(pDis, pOp, pInsnClass, pf64Bit, pInsnParm);
248
249 pParam->uValue = disArmV8ExtractBitVecFromInsn(u32Insn, 5, 19);
250 pParam->uValue |= disArmV8ExtractBitVecFromInsn(u32Insn, 29, 2) << 29;
251 pParam->fUse |= DISUSE_IMMEDIATE32;
252 return VINF_SUCCESS;
253}
254
255
256static int disArmV8ParseReg(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8OPCODE pOp, PCDISARMV8INSNCLASS pInsnClass, PDISOPPARAM pParam, PCDISARMV8INSNPARAM pInsnParm, bool *pf64Bit)
257{
258 RT_NOREF(pDis, pOp, pInsnClass);
259 pParam->armv8.Reg.idxGenReg = disArmV8ExtractBitVecFromInsn(u32Insn, pInsnParm->idxBitStart, pInsnParm->cBits);
260 pParam->armv8.cb = *pf64Bit ? sizeof(uint64_t) : sizeof(uint32_t);
261 pParam->fUse |= (*pf64Bit || (pParam->armv8.enmType == kDisArmv8OpParmAddrInGpr))
262 ? DISUSE_REG_GEN64
263 : DISUSE_REG_GEN32;
264 return VINF_SUCCESS;
265}
266
267
268static int disArmV8ParseImmsImmrN(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8OPCODE pOp, PCDISARMV8INSNCLASS pInsnClass, PDISOPPARAM pParam, PCDISARMV8INSNPARAM pInsnParm, bool *pf64Bit)
269{
270 RT_NOREF(pDis, pOp);
271 AssertReturn(pInsnParm->cBits == 13, VERR_INTERNAL_ERROR_2);
272
273 uint32_t u32ImmRaw = disArmV8ExtractBitVecFromInsn(u32Insn, pInsnParm->idxBitStart, pInsnParm->cBits);
274 /* N bit must be 0 if 32-bit variant is used. */
275 if ( ( (u32ImmRaw & RT_BIT_32(12))
276 && !*pf64Bit)
277 || ( !(u32ImmRaw & RT_BIT_32(12))
278 && *pf64Bit
279 && (pInsnClass->fClass & DISARMV8INSNCLASS_F_N_FORCED_1_ON_64BIT)))
280 return VERR_DIS_INVALID_OPCODE;
281
282 uint32_t uImm7SizeLen = ((u32ImmRaw & RT_BIT_32(12)) >> 6) | (u32ImmRaw & 0x3f);
283 uint32_t uImm6Rotations = (u32ImmRaw >> 6) & 0x3f;
284 pParam->uValue = *pf64Bit
285 ? Armv8A64ConvertImmRImmS2Mask64(uImm7SizeLen, uImm6Rotations)
286 : Armv8A64ConvertImmRImmS2Mask32(uImm7SizeLen, uImm6Rotations);
287 pParam->armv8.cb = pParam->uValue > UINT32_MAX ? sizeof(uint64_t) : sizeof(uint32_t);
288 pParam->fUse |= pParam->uValue > UINT32_MAX ? DISUSE_IMMEDIATE64 : DISUSE_IMMEDIATE32;
289 return VINF_SUCCESS;
290}
291
292
293static int disArmV8ParseHw(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8OPCODE pOp, PCDISARMV8INSNCLASS pInsnClass, PDISOPPARAM pParam, PCDISARMV8INSNPARAM pInsnParm, bool *pf64Bit)
294{
295 RT_NOREF(pDis, pOp, pInsnClass, pParam);
296 Assert(pInsnParm->cBits == 2);
297
298 uint32_t u32 = disArmV8ExtractBitVecFromInsn(u32Insn, pInsnParm->idxBitStart, pInsnParm->cBits);
299 /* hw<1> must be 0 if this is the 32-bit variant. */
300 if ( !*pf64Bit
301 && (u32 & RT_BIT_32(1)))
302 return VERR_DIS_INVALID_OPCODE;
303
304 Assert(pParam->armv8.enmType == kDisArmv8OpParmImm);
305 Assert(pParam->fUse & (DISUSE_IMMEDIATE8 | DISUSE_IMMEDIATE16 | DISUSE_IMMEDIATE32));
306 if (u32)
307 {
308 pParam->armv8.enmShift = kDisArmv8OpParmShiftLeft;
309 pParam->armv8.u.cShift = ((uint8_t)u32 & 0x3) << 4;
310 }
311 return VINF_SUCCESS;
312}
313
314
315static int disArmV8ParseCond(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8OPCODE pOp, PCDISARMV8INSNCLASS pInsnClass, PDISOPPARAM pParam, PCDISARMV8INSNPARAM pInsnParm, bool *pf64Bit)
316{
317 RT_NOREF(pInsnClass, pOp, pParam, pf64Bit);
318 Assert(pInsnParm->cBits <= 4);
319 if (pParam)
320 {
321 /* Conditional as a parameter (CCMP/CCMN). */
322 Assert(pParam->armv8.enmType == kDisArmv8OpParmCond);
323 pParam->armv8.Reg.enmCond = (DISARMV8INSTRCOND)disArmV8ExtractBitVecFromInsn(u32Insn, pInsnParm->idxBitStart, pInsnParm->cBits);
324 }
325 else /* Conditional for the base instruction. */
326 pDis->armv8.enmCond = (DISARMV8INSTRCOND)disArmV8ExtractBitVecFromInsn(u32Insn, pInsnParm->idxBitStart, pInsnParm->cBits);
327 return VINF_SUCCESS;
328}
329
330
331static int disArmV8ParsePState(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8OPCODE pOp, PCDISARMV8INSNCLASS pInsnClass, PDISOPPARAM pParam, PCDISARMV8INSNPARAM pInsnParm, bool *pf64Bit)
332{
333 RT_NOREF(pDis, u32Insn, pOp, pInsnClass, pParam, pInsnParm, pf64Bit);
334 //AssertFailed();
335 /** @todo */
336 return VINF_SUCCESS;
337}
338
339
340static int disArmV8ParseSysReg(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8OPCODE pOp, PCDISARMV8INSNCLASS pInsnClass, PDISOPPARAM pParam, PCDISARMV8INSNPARAM pInsnParm, bool *pf64Bit)
341{
342 RT_NOREF(pDis, pOp, pInsnClass, pf64Bit);
343 AssertReturn(pInsnParm->cBits == 15, VERR_INTERNAL_ERROR_2);
344
345 /* Assumes a op0:op1:CRn:CRm:op2 encoding in the instruction starting at the given bit position. */
346 uint32_t u32ImmRaw = disArmV8ExtractBitVecFromInsn(u32Insn, pInsnParm->idxBitStart, pInsnParm->cBits);
347 pParam->armv8.Reg.idSysReg = ARMV8_AARCH64_SYSREG_ID_CREATE(2 + ((u32ImmRaw >> 14) & 0x1),
348 (u32ImmRaw >> 11) & 0x7,
349 (u32ImmRaw >> 7) & 0xf,
350 (u32ImmRaw >> 3) & 0xf,
351 u32ImmRaw & 0x7);
352 pParam->armv8.cb = 0;
353 pParam->fUse |= DISUSE_REG_SYSTEM;
354 return VINF_SUCCESS;
355}
356
357
358static int disArmV8ParseSh12(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8OPCODE pOp, PCDISARMV8INSNCLASS pInsnClass, PDISOPPARAM pParam, PCDISARMV8INSNPARAM pInsnParm, bool *pf64Bit)
359{
360 RT_NOREF(pDis, pOp, pInsnClass, pf64Bit);
361 Assert(pInsnParm->cBits == 1);
362 if (u32Insn & RT_BIT_32(pInsnParm->idxBitStart))
363 {
364 /* Shift the immediate pointed to. */
365 pParam->uValue <<= 12;
366
367 /* Re-evaluate the immediate data size. */
368 pParam->fUse &= ~(DISUSE_IMMEDIATE8 | DISUSE_IMMEDIATE16 | DISUSE_IMMEDIATE32);
369 if (pParam->uValue <= UINT8_MAX)
370 {
371 pParam->armv8.cb = sizeof(uint8_t);
372 pParam->fUse |= DISUSE_IMMEDIATE8;
373 }
374 else if (pParam->uValue <= UINT16_MAX)
375 {
376 pParam->armv8.cb = sizeof(uint16_t);
377 pParam->fUse |= DISUSE_IMMEDIATE16;
378 }
379 else if (pParam->uValue <= UINT32_MAX)
380 {
381 pParam->armv8.cb = sizeof(uint32_t);
382 pParam->fUse |= DISUSE_IMMEDIATE32;
383 }
384 else
385 AssertReleaseFailed();
386
387 }
388 return VINF_SUCCESS;
389}
390
391
392static int disArmV8ParseImmTbz(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8OPCODE pOp, PCDISARMV8INSNCLASS pInsnClass, PDISOPPARAM pParam, PCDISARMV8INSNPARAM pInsnParm, bool *pf64Bit)
393{
394 RT_NOREF(pDis, pOp, pInsnClass, pf64Bit);
395
396 AssertReturn(!pInsnParm->idxBitStart && !pInsnParm->cBits, VERR_INTERNAL_ERROR_2);
397
398 pParam->uValue = disArmV8ExtractBitVecFromInsn(u32Insn, 19, 5);
399 pParam->uValue |= (u32Insn & RT_BIT_32(31)) >> 26;
400
401 pParam->armv8.cb = sizeof(uint8_t);
402 pParam->fUse |= DISUSE_IMMEDIATE8;
403 return VINF_SUCCESS;
404}
405
406
407static int disArmV8ParseShift(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8OPCODE pOp, PCDISARMV8INSNCLASS pInsnClass, PDISOPPARAM pParam, PCDISARMV8INSNPARAM pInsnParm, bool *pf64Bit)
408{
409 RT_NOREF(pDis, pOp, pInsnClass, pf64Bit);
410
411 AssertReturn(pInsnParm->cBits == 2, VERR_INTERNAL_ERROR_2);
412
413 uint32_t u32Shift = disArmV8ExtractBitVecFromInsn(u32Insn, pInsnParm->idxBitStart, pInsnParm->cBits);
414 switch (u32Shift)
415 {
416 case 0: pParam->armv8.enmShift = kDisArmv8OpParmShiftLeft; break;
417 case 1: pParam->armv8.enmShift = kDisArmv8OpParmShiftRight; break;
418 case 2: pParam->armv8.enmShift = kDisArmv8OpParmShiftArithRight; break;
419 case 3: pParam->armv8.enmShift = kDisArmv8OpParmShiftRotate; break;
420 default:
421 AssertReleaseFailed();
422 }
423 return VINF_SUCCESS;
424}
425
426
427static int disArmV8ParseShiftAmount(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8OPCODE pOp, PCDISARMV8INSNCLASS pInsnClass, PDISOPPARAM pParam, PCDISARMV8INSNPARAM pInsnParm, bool *pf64Bit)
428{
429 RT_NOREF(pDis, pOp, pInsnClass, pf64Bit);
430
431 uint32_t u32Amount = disArmV8ExtractBitVecFromInsn(u32Insn, pInsnParm->idxBitStart, pInsnParm->cBits);
432 /* For a 32-bit operand it is impossible to shift/rotate more than 31 bits. */
433 if ( !*pf64Bit
434 && u32Amount > 31)
435 return VERR_DIS_INVALID_OPCODE;
436
437 Assert(pParam->armv8.enmShift != kDisArmv8OpParmShiftNone);
438 Assert(u32Amount < 64);
439 pParam->armv8.u.cShift = (uint8_t)u32Amount;
440 /* Any shift operation with a 0 is essentially no shift being applied. */
441 if (pParam->armv8.u.cShift == 0)
442 pParam->armv8.enmShift = kDisArmv8OpParmShiftNone;
443 return VINF_SUCCESS;
444}
445
446
447static int disArmV8ParseImmMemOff(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8OPCODE pOp, PCDISARMV8INSNCLASS pInsnClass, PDISOPPARAM pParam, PCDISARMV8INSNPARAM pInsnParm, bool *pf64Bit)
448{
449 RT_NOREF(pInsnClass, pOp, pf64Bit);
450
451 AssertReturn(pInsnParm->cBits <= 12, VERR_INTERNAL_ERROR_2);
452 AssertReturn(pDis->armv8.cbOperand != 0, VERR_INTERNAL_ERROR_2);
453
454 pParam->armv8.u.offBase = disArmV8ExtractBitVecFromInsn(u32Insn, pInsnParm->idxBitStart, pInsnParm->cBits);
455 switch (pDis->armv8.cbOperand)
456 {
457 case sizeof(uint8_t): break;
458 case sizeof(uint16_t): pParam->armv8.u.offBase <<= 1; break;
459 case sizeof(uint32_t): pParam->armv8.u.offBase <<= 2; break;
460 case sizeof(uint64_t): pParam->armv8.u.offBase <<= 3; break;
461 default:
462 AssertReleaseFailed();
463 }
464 pParam->armv8.cb = sizeof(int16_t);
465 return VINF_SUCCESS;
466}
467
468
469static int disArmV8ParseSImmMemOff(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8OPCODE pOp, PCDISARMV8INSNCLASS pInsnClass, PDISOPPARAM pParam, PCDISARMV8INSNPARAM pInsnParm, bool *pf64Bit)
470{
471 RT_NOREF(pInsnClass, pf64Bit);
472
473 AssertReturn(pInsnParm->cBits <= 7, VERR_INTERNAL_ERROR_2);
474 AssertReturn( (pOp->fFlags & DISARMV8INSNCLASS_F_FORCED_32BIT)
475 || (pOp->fFlags & DISARMV8INSNCLASS_F_FORCED_64BIT),
476 VERR_INTERNAL_ERROR_2);
477
478 pParam->armv8.cb = sizeof(int16_t);
479 pParam->armv8.u.offBase = disArmV8ExtractBitVecFromInsnSignExtend(u32Insn, pInsnParm->idxBitStart, pInsnParm->cBits);
480 pParam->armv8.u.offBase <<= (pOp->fFlags & DISARMV8INSNCLASS_F_FORCED_32BIT) ? 2 : 3;
481 pDis->armv8.cbOperand = (pOp->fFlags & DISARMV8INSNCLASS_F_FORCED_32BIT) ? sizeof(uint32_t) : sizeof(uint64_t);
482 return VINF_SUCCESS;
483}
484
485
486static uint32_t disArmV8DecodeIllegal(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8INSNCLASS pInsnClass)
487{
488 RT_NOREF(pDis, u32Insn, pInsnClass);
489 AssertFailed();
490 return UINT32_MAX;
491}
492
493
494static uint32_t disArmV8DecodeLookup(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8INSNCLASS pInsnClass)
495{
496 RT_NOREF(pDis);
497
498 for (uint32_t i = 0; i < pInsnClass->Hdr.cDecode; i++)
499 {
500 PCDISARMV8OPCODE pOp = &pInsnClass->paOpcodes[i];
501 if (u32Insn == pOp->fValue)
502 return i;
503 }
504
505 return UINT32_MAX;
506}
507
508
509static uint32_t disArmV8DecodeCollate(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8INSNCLASS pInsnClass)
510{
511 RT_NOREF(pDis);
512
513 /* Need to build a compact representation of the relevant bits from the mask to create an index. */
514 uint32_t fMask = pInsnClass->fMask >> pInsnClass->cShift;
515
516 /** @todo Optimize. */
517 uint32_t idx = 0;
518 uint32_t cShift = 0;
519 while (fMask)
520 {
521 if (fMask & 0x1)
522 {
523 idx |= (u32Insn & 1) << cShift;
524 cShift++;
525 }
526
527 u32Insn >>= 1;
528 fMask >>= 1;
529 }
530
531 if (RT_LIKELY(idx < pInsnClass->Hdr.cDecode))
532 return idx;
533
534 return UINT32_MAX;
535}
536
537
538/**
539 * Looks for possible alias conversions for the given disassembler state.
540 *
541 * @param pDis The disassembler state to process.
542 */
543static void disArmV8A64InsnAliasesProcess(PDISSTATE pDis)
544{
545#define DIS_ARMV8_ALIAS(a_Name) s_DisArmv8Alias ## a_Name
546#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)
547#define DIS_ARMV8_ALIAS_REF(a_Name) &DIS_ARMV8_ALIAS(a_Name)
548 switch (pDis->pCurInstr->uOpcode)
549 {
550 case OP_ARMV8_A64_ORR:
551 {
552 /* Check for possible MOV conversion for the register variant when: shift is None and the first source is the zero register. */
553 Assert(pDis->aParams[1].armv8.enmType == kDisArmv8OpParmGpr);
554
555 if ( pDis->aParams[2].armv8.enmType == kDisArmv8OpParmGpr
556 && pDis->aParams[2].armv8.enmShift == kDisArmv8OpParmShiftNone
557 && pDis->aParams[1].armv8.Reg.idxGenReg == ARMV8_A64_REG_XZR)
558 {
559 DIS_ARMV8_ALIAS_CREATE(Mov, "mov", OP_ARMV8_A64_MOV, DISOPTYPE_HARMLESS);
560 pDis->pCurInstr = DIS_ARMV8_ALIAS_REF(Mov);
561 pDis->aParams[1] = pDis->aParams[2];
562 pDis->aParams[2].armv8.enmType = kDisArmv8OpParmNone;
563 }
564 /** @todo Immediate variant. */
565 break;
566 }
567 case OP_ARMV8_A64_SUBS:
568 {
569 Assert(pDis->aParams[0].armv8.enmType == kDisArmv8OpParmGpr);
570 if (pDis->aParams[0].armv8.Reg.idxGenReg == ARMV8_A64_REG_XZR)
571 {
572 DIS_ARMV8_ALIAS_CREATE(Cmp, "cmp", OP_ARMV8_A64_CMP, DISOPTYPE_HARMLESS);
573 pDis->pCurInstr = DIS_ARMV8_ALIAS_REF(Cmp);
574 pDis->aParams[0] = pDis->aParams[1];
575 pDis->aParams[1] = pDis->aParams[2];
576 pDis->aParams[2].armv8.enmType = kDisArmv8OpParmNone;
577 }
578 break;
579 }
580 default:
581 break; /* No conversion */
582 }
583#undef DIS_ARMV8_ALIAS_REF
584#undef DIS_ARMV8_ALIAS_CREATE
585#undef DIS_ARMV8_ALIAS
586}
587
588
589static int disArmV8A64ParseInstruction(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8OPCODE pOp, PCDISARMV8INSNCLASS pInsnClass)
590{
591 AssertPtr(pOp);
592 AssertPtr(pDis);
593 Assert((u32Insn & pInsnClass->fFixedInsn) == pOp->fValue);
594
595 /* Should contain the parameter type on input. */
596 pDis->aParams[0].armv8.enmType = pInsnClass->aenmParamTypes[0];
597 pDis->aParams[1].armv8.enmType = pInsnClass->aenmParamTypes[1];
598 pDis->aParams[2].armv8.enmType = pInsnClass->aenmParamTypes[2];
599 pDis->aParams[3].armv8.enmType = pInsnClass->aenmParamTypes[3];
600 pDis->aParams[0].armv8.enmShift = kDisArmv8OpParmShiftNone;
601 pDis->aParams[1].armv8.enmShift = kDisArmv8OpParmShiftNone;
602 pDis->aParams[2].armv8.enmShift = kDisArmv8OpParmShiftNone;
603 pDis->aParams[3].armv8.enmShift = kDisArmv8OpParmShiftNone;
604 pDis->armv8.enmCond = kDisArmv8InstrCond_Al;
605 pDis->armv8.cbOperand = 0;
606
607 pDis->pCurInstr = &pOp->Opc;
608 Assert(&pOp->Opc != &g_ArmV8A64InvalidOpcode[0]);
609
610 bool f64Bit = false;
611
612 /** @todo Get rid of these and move them to the per opcode
613 * (SF can become a decoder step). */
614 if (pInsnClass->fClass & DISARMV8INSNCLASS_F_SF)
615 f64Bit = RT_BOOL(u32Insn & RT_BIT_32(31));
616 else if (pInsnClass->fClass & DISARMV8INSNCLASS_F_FORCED_64BIT)
617 f64Bit = true;
618
619 if (pOp->fFlags & DISARMV8INSNCLASS_F_FORCED_32BIT)
620 f64Bit = false;
621 else if (pOp->fFlags & DISARMV8INSNCLASS_F_FORCED_64BIT)
622 f64Bit = true;
623
624 int rc = VINF_SUCCESS;
625 PCDISARMV8INSNPARAM pDecode = &pInsnClass->paParms[0];
626 while ( (pDecode->idxParse != kDisParmParseNop)
627 && RT_SUCCESS(rc))
628 {
629 rc = g_apfnDisasm[pDecode->idxParse](pDis, u32Insn, pOp, pInsnClass,
630 pDecode->idxParam != DIS_ARMV8_INSN_PARAM_UNSET
631 ? &pDis->aParams[pDecode->idxParam]
632 : NULL,
633 pDecode, &f64Bit);
634 pDecode++;
635 }
636
637 /* If parameter parsing returned an invalid opcode error the encoding is invalid. */
638 if (RT_SUCCESS(rc)) /** @todo Introduce flag to switch alias conversion on/off. */
639 disArmV8A64InsnAliasesProcess(pDis);
640 else if (rc == VERR_DIS_INVALID_OPCODE)
641 {
642 pDis->pCurInstr = &g_ArmV8A64InvalidOpcode[0];
643
644 pDis->aParams[0].armv8.enmType = kDisArmv8OpParmNone;
645 pDis->aParams[1].armv8.enmType = kDisArmv8OpParmNone;
646 pDis->aParams[2].armv8.enmType = kDisArmv8OpParmNone;
647 pDis->aParams[3].armv8.enmType = kDisArmv8OpParmNone;
648 }
649 pDis->rc = rc;
650 return rc;
651}
652
653
654static int disArmV8A64ParseInvOpcode(PDISSTATE pDis)
655{
656 pDis->pCurInstr = &g_ArmV8A64InvalidOpcode[0];
657 pDis->rc = VERR_DIS_INVALID_OPCODE;
658 return VERR_DIS_INVALID_OPCODE;
659}
660
661
662static int disInstrArmV8DecodeWorker(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8DECODEHDR pHdr)
663{
664 while ( pHdr
665 && pHdr->enmDecodeType != kDisArmV8DecodeType_InsnClass)
666 {
667 if (pHdr->enmDecodeType == kDisArmV8DecodeType_Map)
668 {
669 PCDISARMV8DECODEMAP pMap = (PCDISARMV8DECODEMAP)pHdr;
670
671 uint32_t idxNext = (u32Insn & pMap->fMask) >> pMap->cShift;
672 if (RT_LIKELY(idxNext < pMap->Hdr.cDecode))
673 pHdr = pMap->papNext[idxNext];
674 else
675 {
676 pHdr = NULL;
677 break;
678 }
679 }
680 else
681 {
682 Assert(pHdr->enmDecodeType == kDisArmV8DecodeType_Table);
683 PCDISARMV8DECODETBL pTbl = (PCDISARMV8DECODETBL)pHdr;
684
685 /* Walk all entries in the table and select the best match. */
686 pHdr = NULL;
687 for (uint32_t i = 0; i < pTbl->Hdr.cDecode; i++)
688 {
689 PCDISARMV8DECODETBLENTRY pEntry = &pTbl->paEntries[i];
690 if ((u32Insn & pEntry->fMask) == pEntry->fValue)
691 {
692 pHdr = pEntry->pHdrNext;
693 break;
694 }
695 }
696 }
697 }
698
699 if (pHdr)
700 {
701 Assert(pHdr->enmDecodeType == kDisArmV8DecodeType_InsnClass);
702 PCDISARMV8INSNCLASS pInsnClass = (PCDISARMV8INSNCLASS)pHdr;
703
704 /* Decode the opcode from the instruction class. */
705 uint32_t uOpcRaw = 0;
706 if (pInsnClass->Hdr.cDecode > 1)
707 {
708 uOpcRaw = (u32Insn & pInsnClass->fMask) >> pInsnClass->cShift;
709 if (pInsnClass->enmOpcDecode != kDisArmV8OpcDecodeNop)
710 uOpcRaw = g_apfnOpcDecode[pInsnClass->enmOpcDecode](pDis, uOpcRaw, pInsnClass);
711 }
712
713 if (uOpcRaw < pInsnClass->Hdr.cDecode)
714 {
715 PCDISARMV8OPCODE pOp = &pInsnClass->paOpcodes[uOpcRaw];
716 return disArmV8A64ParseInstruction(pDis, u32Insn, pOp, pInsnClass);
717 }
718 }
719
720 return disArmV8A64ParseInvOpcode(pDis);
721}
722
723
724/**
725 * Internal worker for DISInstrEx and DISInstrWithPrefetchedBytes.
726 *
727 * @returns VBox status code.
728 * @param pDis Initialized disassembler state.
729 * @param paOneByteMap The one byte opcode map to use.
730 * @param pcbInstr Where to store the instruction size. Can be NULL.
731 */
732DECLHIDDEN(int) disInstrWorkerArmV8(PDISSTATE pDis, PCDISOPCODE paOneByteMap, uint32_t *pcbInstr)
733{
734 RT_NOREF(paOneByteMap);
735
736 if (pDis->uCpuMode == DISCPUMODE_ARMV8_A64)
737 {
738 *pcbInstr = sizeof(uint32_t);
739
740 /* Instructions are always little endian and 4 bytes. */
741 uint32_t u32Insn = disReadDWord(pDis, 0 /*offInstr*/);
742 if (RT_FAILURE(pDis->rc))
743 return pDis->rc;
744
745 /** @todo r=bird: This is a waste of time if the host is little endian... */
746 pDis->Instr.u32 = RT_LE2H_U32(u32Insn);
747 pDis->cbInstr = sizeof(u32Insn);
748
749 return disInstrArmV8DecodeWorker(pDis, u32Insn, &g_aArmV8A64InsnDecodeL0.Hdr);
750 }
751
752 AssertReleaseFailed();
753 return VERR_NOT_IMPLEMENTED;
754}
755
756
757/**
758 * Inlined worker that initializes the disassembler state.
759 *
760 * @returns The primary opcode map to use.
761 * @param pDis The disassembler state.
762 * @param uInstrAddr The instruction address.
763 * @param enmCpuMode The CPU mode.
764 * @param fFilter The instruction filter settings.
765 * @param pfnReadBytes The byte reader, can be NULL.
766 * @param pvUser The user data for the reader.
767 */
768DECLHIDDEN(PCDISOPCODE) disInitializeStateArmV8(PDISSTATE pDis, DISCPUMODE enmCpuMode, uint32_t fFilter)
769{
770 RT_NOREF(pDis, enmCpuMode, fFilter);
771 return NULL;
772}
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