VirtualBox

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

Last change on this file since 105889 was 105858, checked in by vboxsync, 3 months ago

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

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