VirtualBox

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

Last change on this file since 100763 was 99334, checked in by vboxsync, 21 months ago

Diassembler: Updates to the ARMv8 disassembler, bugref:10394

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.5 KB
Line 
1/* $Id: DisasmCore-armv8.cpp 99334 2023-04-07 10:10:07Z 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/assert.h>
36#include <iprt/errcore.h>
37#include <iprt/param.h>
38#include <iprt/string.h>
39#include <iprt/stdarg.h>
40#include "DisasmInternal-armv8.h"
41
42
43/*********************************************************************************************************************************
44* Structures and Typedefs *
45*********************************************************************************************************************************/
46
47/** Parser callback.
48 * @remark no DECLCALLBACK() here because it's considered to be internal and
49 * there is no point in enforcing CDECL. */
50typedef int FNDISPARSEARMV8(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8INSNCLASS pInsnClass, PDISOPPARAM pParam, PCDISARMV8INSNPARAM pInsnParm, bool f64Bit);
51/** Pointer to a disassembler parser function. */
52typedef FNDISPARSEARMV8 *PFNDISPARSEARMV8;
53
54
55/** Opcode decoder callback.
56 * @remark no DECLCALLBACK() here because it's considered to be internal and
57 * there is no point in enforcing CDECL. */
58typedef uint32_t FNDISDECODEARMV8(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8INSNCLASS pInsnClass);
59/** Pointer to a disassembler parser function. */
60typedef FNDISDECODEARMV8 *PFNDISDECODEARMV8;
61
62
63/*********************************************************************************************************************************
64* Defined Constants And Macros *
65*********************************************************************************************************************************/
66
67
68/*********************************************************************************************************************************
69* Internal Functions *
70*********************************************************************************************************************************/
71/** @name Parsers
72 * @{ */
73static FNDISPARSEARMV8 disArmV8ParseIllegal;
74static FNDISPARSEARMV8 disArmV8ParseImm;
75static FNDISPARSEARMV8 disArmV8ParseImmRel;
76static FNDISPARSEARMV8 disArmV8ParseImmAdr;
77static FNDISPARSEARMV8 disArmV8ParseReg;
78static FNDISPARSEARMV8 disArmV8ParseImmsImmrN;
79static FNDISPARSEARMV8 disArmV8ParseHw;
80static FNDISPARSEARMV8 disArmV8ParseCond;
81static FNDISPARSEARMV8 disArmV8ParsePState;
82/** @} */
83
84
85/** @name Decoders
86 * @{ */
87static FNDISDECODEARMV8 disArmV8DecodeIllegal;
88static FNDISDECODEARMV8 disArmV8DecodeLookup;
89/** @} */
90
91
92/*********************************************************************************************************************************
93* Global Variables *
94*********************************************************************************************************************************/
95/** Parser opcode table for full disassembly. */
96static PFNDISPARSEARMV8 const g_apfnDisasm[kDisParmParseMax] =
97{
98 disArmV8ParseIllegal,
99 disArmV8ParseImm,
100 disArmV8ParseImmRel,
101 disArmV8ParseImmAdr,
102 disArmV8ParseReg,
103 disArmV8ParseImmsImmrN,
104 disArmV8ParseHw,
105 disArmV8ParseCond,
106 disArmV8ParsePState,
107};
108
109
110/** Opcode decoder table. */
111static PFNDISDECODEARMV8 const g_apfnOpcDecode[kDisArmV8OpcDecodeMax] =
112{
113 disArmV8DecodeIllegal,
114 disArmV8DecodeLookup,
115};
116
117
118DECLINLINE(uint32_t) disArmV8ExtractBitVecFromInsn(uint32_t u32Insn, uint8_t idxBitStart, uint8_t cBits)
119{
120 uint32_t fMask = RT_BIT_32(idxBitStart + cBits) - 1;
121 return (u32Insn & fMask) >> idxBitStart;
122}
123
124
125DECLINLINE(int32_t) disArmV8ExtractBitVecFromInsnSignExtend(uint32_t u32Insn, uint8_t idxBitStart, uint8_t cBits)
126{
127 uint32_t fMask = RT_BIT_32(idxBitStart + cBits) - 1;
128 uint32_t fSign = ~(UINT32_MAX & (RT_BIT_32(cBits - 1) - 1));
129 uint32_t fValue = (u32Insn & fMask) >> idxBitStart;
130 if (fValue & fSign)
131 return (int32_t)(fValue | fSign);
132
133 return (int32_t)fValue;
134}
135
136
137static int disArmV8ParseIllegal(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8INSNCLASS pInsnClass, PDISOPPARAM pParam, PCDISARMV8INSNPARAM pInsnParm, bool f64Bit)
138{
139 RT_NOREF(pDis, u32Insn, pInsnClass, pParam, pInsnParm, f64Bit);
140 AssertFailed();
141 return VERR_INTERNAL_ERROR;
142}
143
144
145static int disArmV8ParseImm(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8INSNCLASS pInsnClass, PDISOPPARAM pParam, PCDISARMV8INSNPARAM pInsnParm, bool f64Bit)
146{
147 RT_NOREF(pDis, pInsnClass, f64Bit);
148
149 AssertReturn(pInsnParm->idxBitStart + pInsnParm->cBits < 32, VERR_INTERNAL_ERROR_2);
150
151 pParam->uValue = disArmV8ExtractBitVecFromInsn(u32Insn, pInsnParm->idxBitStart, pInsnParm->cBits);
152 if (pInsnParm->cBits <= 8)
153 {
154 pParam->arch.armv8.cb = sizeof(uint8_t);
155 pParam->fUse |= DISUSE_IMMEDIATE8;
156 }
157 else if (pInsnParm->cBits <= 16)
158 {
159 pParam->arch.armv8.cb = sizeof(uint16_t);
160 pParam->fUse |= DISUSE_IMMEDIATE16;
161 }
162 else if (pInsnParm->cBits <= 32)
163 {
164 pParam->arch.armv8.cb = sizeof(uint32_t);
165 pParam->fUse |= DISUSE_IMMEDIATE32;
166 }
167 else
168 AssertReleaseFailed();
169
170 return VINF_SUCCESS;
171}
172
173
174static int disArmV8ParseImmRel(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8INSNCLASS pInsnClass, PDISOPPARAM pParam, PCDISARMV8INSNPARAM pInsnParm, bool f64Bit)
175{
176 RT_NOREF(pDis, pInsnClass, f64Bit);
177
178 AssertReturn(pInsnParm->idxBitStart + pInsnParm->cBits < 32, VERR_INTERNAL_ERROR_2);
179
180 pParam->uValue = (int64_t)disArmV8ExtractBitVecFromInsnSignExtend(u32Insn, pInsnParm->idxBitStart, pInsnParm->cBits);
181 if (pInsnParm->cBits <= 8)
182 {
183 pParam->arch.armv8.cb = sizeof(int8_t);
184 pParam->fUse |= DISUSE_IMMEDIATE8_REL;
185 }
186 else if (pInsnParm->cBits <= 16)
187 {
188 pParam->arch.armv8.cb = sizeof(int16_t);
189 pParam->fUse |= DISUSE_IMMEDIATE16_REL;
190 }
191 else if (pInsnParm->cBits <= 32)
192 {
193 pParam->arch.armv8.cb = sizeof(int32_t);
194 pParam->fUse |= DISUSE_IMMEDIATE32_REL;
195 }
196 else
197 AssertReleaseFailed();
198
199 return VINF_SUCCESS;
200}
201
202
203static int disArmV8ParseImmAdr(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8INSNCLASS pInsnClass, PDISOPPARAM pParam, PCDISARMV8INSNPARAM pInsnParm, bool f64Bit)
204{
205 RT_NOREF(pDis, pInsnClass, f64Bit, pInsnParm);
206
207 pParam->uValue = disArmV8ExtractBitVecFromInsn(u32Insn, 5, 19);
208 pParam->uValue |= disArmV8ExtractBitVecFromInsn(u32Insn, 29, 2) << 29;
209 pParam->fUse |= DISUSE_IMMEDIATE32;
210 return VINF_SUCCESS;
211}
212
213
214static int disArmV8ParseReg(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8INSNCLASS pInsnClass, PDISOPPARAM pParam, PCDISARMV8INSNPARAM pInsnParm, bool f64Bit)
215{
216 RT_NOREF(pDis, pInsnClass);
217 pParam->arch.armv8.Reg.idxGenReg = disArmV8ExtractBitVecFromInsn(u32Insn, pInsnParm->idxBitStart, pInsnParm->cBits);
218 pParam->arch.armv8.cb = f64Bit ? sizeof(uint64_t) : sizeof(uint32_t);
219 pParam->fUse |= f64Bit ? DISUSE_REG_GEN64 : DISUSE_REG_GEN32;
220 return VINF_SUCCESS;
221}
222
223
224static int disArmV8ParseImmsImmrN(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8INSNCLASS pInsnClass, PDISOPPARAM pParam, PCDISARMV8INSNPARAM pInsnParm, bool f64Bit)
225{
226 RT_NOREF(pDis);
227 AssertReturn(pInsnParm->cBits == 13, VERR_INTERNAL_ERROR_2);
228
229 uint32_t u32ImmRaw = disArmV8ExtractBitVecFromInsn(u32Insn, pInsnParm->idxBitStart, pInsnParm->cBits);
230 /* N bit must be 0 if 32-bit variant is used. */
231 if ( ( (u32ImmRaw & RT_BIT_32(12))
232 && !f64Bit)
233 || ( !(u32ImmRaw & RT_BIT_32(12))
234 && f64Bit
235 && (pInsnClass->fClass & DISARMV8INSNCLASS_F_N_FORCED_1_ON_64BIT)))
236 return VERR_DIS_INVALID_OPCODE;
237
238 /** @todo Decode according to spec. */
239 pParam->uValue = u32ImmRaw;
240 pParam->arch.armv8.cb = sizeof(uint32_t);
241 pParam->fUse |= DISUSE_IMMEDIATE32;
242 return VINF_SUCCESS;
243}
244
245
246static int disArmV8ParseHw(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8INSNCLASS pInsnClass, PDISOPPARAM pParam, PCDISARMV8INSNPARAM pInsnParm, bool f64Bit)
247{
248 RT_NOREF(pDis, u32Insn, pInsnClass, pParam, pInsnParm, f64Bit);
249 AssertFailed();
250 /** @todo */
251 return VINF_SUCCESS;
252}
253
254
255static int disArmV8ParseCond(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8INSNCLASS pInsnClass, PDISOPPARAM pParam, PCDISARMV8INSNPARAM pInsnParm, bool f64Bit)
256{
257 RT_NOREF(pDis, u32Insn, pInsnClass, pParam, pInsnParm, f64Bit);
258 //AssertFailed();
259 /** @todo */
260 return VINF_SUCCESS;
261}
262
263
264static int disArmV8ParsePState(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8INSNCLASS pInsnClass, PDISOPPARAM pParam, PCDISARMV8INSNPARAM pInsnParm, bool f64Bit)
265{
266 RT_NOREF(pDis, u32Insn, pInsnClass, pParam, pInsnParm, f64Bit);
267 //AssertFailed();
268 /** @todo */
269 return VINF_SUCCESS;
270}
271
272
273static uint32_t disArmV8DecodeIllegal(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8INSNCLASS pInsnClass)
274{
275 RT_NOREF(pDis, u32Insn, pInsnClass);
276 AssertFailed();
277 return UINT32_MAX;
278}
279
280
281static uint32_t disArmV8DecodeLookup(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8INSNCLASS pInsnClass)
282{
283 RT_NOREF(pDis);
284
285 for (uint32_t i = 0; i < pInsnClass->Hdr.cDecode; i++)
286 {
287 PCDISARMV8OPCODE pOp = &pInsnClass->paOpcodes[i];
288 if (u32Insn == pOp->fValue)
289 return i;
290 }
291
292 return UINT32_MAX;
293}
294
295
296static int disArmV8A64ParseInstruction(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8OPCODE pOp, PCDISARMV8INSNCLASS pInsnClass)
297{
298 AssertPtr(pOp);
299 AssertPtr(pDis);
300 Assert((u32Insn & pOp->fMask) == pOp->fValue);
301
302 /* Should contain the parameter type on input. */
303 pDis->Param1.arch.armv8.fParam = pOp->Opc.fParam1;
304 pDis->Param2.arch.armv8.fParam = pOp->Opc.fParam2;
305 pDis->Param3.arch.armv8.fParam = pOp->Opc.fParam3;
306 pDis->Param4.arch.armv8.fParam = pOp->Opc.fParam4;
307
308 pDis->pCurInstr = &pOp->Opc;
309 Assert(&pOp->Opc != &g_ArmV8A64InvalidOpcode[0]);
310
311 bool f64Bit = false;
312
313 if (pInsnClass->fClass & DISARMV8INSNCLASS_F_SF)
314 f64Bit = RT_BOOL(u32Insn & RT_BIT_32(31));
315 else if (pInsnClass->fClass & DISARMV8INSNCLASS_F_FORCED_64BIT)
316 f64Bit = true;
317
318 int rc = VINF_SUCCESS;
319 if (pInsnClass->aParms[0].idxParse != kDisParmParseNop)
320 rc = g_apfnDisasm[pInsnClass->aParms[0].idxParse](pDis, u32Insn, pInsnClass, &pDis->Param1, &pInsnClass->aParms[0], f64Bit);
321
322 if ( pInsnClass->aParms[1].idxParse != kDisParmParseNop
323 && RT_SUCCESS(rc))
324 rc = g_apfnDisasm[pInsnClass->aParms[1].idxParse](pDis, u32Insn, pInsnClass, &pDis->Param2, &pInsnClass->aParms[1], f64Bit);
325
326 if ( pInsnClass->aParms[2].idxParse != kDisParmParseNop
327 && RT_SUCCESS(rc))
328 rc = g_apfnDisasm[pInsnClass->aParms[2].idxParse](pDis, u32Insn, pInsnClass, &pDis->Param3, &pInsnClass->aParms[2], f64Bit);
329
330 if ( pInsnClass->aParms[3].idxParse != kDisParmParseNop
331 && RT_SUCCESS(rc))
332 rc = g_apfnDisasm[pInsnClass->aParms[3].idxParse](pDis, u32Insn, pInsnClass, &pDis->Param4, &pInsnClass->aParms[3], f64Bit);
333
334 /* If parameter parsing returned an invalid opcode error the encoding is invalid. */
335 if (rc == VERR_DIS_INVALID_OPCODE)
336 {
337 pDis->pCurInstr = &g_ArmV8A64InvalidOpcode[0];
338
339 pDis->Param1.arch.armv8.fParam = g_ArmV8A64InvalidOpcode[0].fParam1;
340 pDis->Param2.arch.armv8.fParam = g_ArmV8A64InvalidOpcode[0].fParam2;
341 pDis->Param3.arch.armv8.fParam = g_ArmV8A64InvalidOpcode[0].fParam3;
342 pDis->Param4.arch.armv8.fParam = g_ArmV8A64InvalidOpcode[0].fParam4;
343 }
344 pDis->rc = rc;
345 return rc;
346}
347
348
349static int disArmV8A64ParseInvOpcode(PDISSTATE pDis)
350{
351 pDis->pCurInstr = &g_ArmV8A64InvalidOpcode[0];
352 pDis->rc = VERR_DIS_INVALID_OPCODE;
353 return VERR_DIS_INVALID_OPCODE;
354}
355
356
357static int disInstrArmV8DecodeWorker(PDISSTATE pDis, uint32_t u32Insn, PCDISARMV8DECODEHDR pHdr)
358{
359 while ( pHdr
360 && pHdr->enmDecodeType != kDisArmV8DecodeType_InsnClass)
361 {
362 if (pHdr->enmDecodeType == kDisArmV8DecodeType_Map)
363 {
364 PCDISARMV8DECODEMAP pMap = (PCDISARMV8DECODEMAP)pHdr;
365
366 uint32_t idxNext = (u32Insn & pMap->fMask) >> pMap->cShift;
367 if (RT_LIKELY(idxNext < pMap->Hdr.cDecode))
368 pHdr = pMap->papNext[idxNext];
369 else
370 {
371 pHdr = NULL;
372 break;
373 }
374 }
375 else
376 {
377 Assert(pHdr->enmDecodeType == kDisArmV8DecodeType_Table);
378 PCDISARMV8DECODETBL pTbl = (PCDISARMV8DECODETBL)pHdr;
379
380 /* Walk all entries in the table and select the best match. */
381 pHdr = NULL;
382 for (uint32_t i = 0; i < pTbl->Hdr.cDecode; i++)
383 {
384 PCDISARMV8DECODETBLENTRY pEntry = &pTbl->paEntries[i];
385 if ((u32Insn & pEntry->fMask) == pEntry->fValue)
386 {
387 pHdr = pEntry->pHdrNext;
388 break;
389 }
390 }
391 }
392 }
393
394 if (pHdr)
395 {
396 Assert(pHdr->enmDecodeType == kDisArmV8DecodeType_InsnClass);
397 PCDISARMV8INSNCLASS pInsnClass = (PCDISARMV8INSNCLASS)pHdr;
398
399 /* Decode the opcode from the instruction class. */
400 uint32_t uOpcRaw = 0;
401 if (pInsnClass->Hdr.cDecode > 1)
402 {
403 uOpcRaw = (u32Insn & pInsnClass->fMask) >> pInsnClass->cShift;
404 if (pInsnClass->enmOpcDecode != kDisArmV8OpcDecodeNop)
405 uOpcRaw = g_apfnOpcDecode[pInsnClass->enmOpcDecode](pDis, uOpcRaw, pInsnClass);
406 }
407
408 if (uOpcRaw < pInsnClass->Hdr.cDecode)
409 {
410 PCDISARMV8OPCODE pOp = &pInsnClass->paOpcodes[uOpcRaw];
411 return disArmV8A64ParseInstruction(pDis, u32Insn, pOp, pInsnClass);
412 }
413 }
414
415 return disArmV8A64ParseInvOpcode(pDis);
416}
417
418
419/**
420 * Internal worker for DISInstrEx and DISInstrWithPrefetchedBytes.
421 *
422 * @returns VBox status code.
423 * @param pDis Initialized disassembler state.
424 * @param paOneByteMap The one byte opcode map to use.
425 * @param pcbInstr Where to store the instruction size. Can be NULL.
426 */
427DECLHIDDEN(int) disInstrWorkerArmV8(PDISSTATE pDis, PCDISOPCODE paOneByteMap, uint32_t *pcbInstr)
428{
429 RT_NOREF(paOneByteMap);
430
431 if (pDis->uCpuMode == DISCPUMODE_ARMV8_A64)
432 {
433 *pcbInstr = sizeof(uint32_t);
434
435 /* Instructions are always little endian and 4 bytes. */
436 uint32_t u32Insn = disReadDWord(pDis, 0 /*offInstr*/);
437 if (RT_FAILURE(pDis->rc))
438 return pDis->rc;
439
440 pDis->u.u32 = RT_LE2H_U32(u32Insn);
441 pDis->cbInstr = sizeof(u32Insn);
442
443 return disInstrArmV8DecodeWorker(pDis, u32Insn, &g_ArmV8A64DecodeL0.Hdr);
444 }
445
446 AssertReleaseFailed();
447 return VERR_NOT_IMPLEMENTED;
448}
449
450
451/**
452 * Inlined worker that initializes the disassembler state.
453 *
454 * @returns The primary opcode map to use.
455 * @param pDis The disassembler state.
456 * @param uInstrAddr The instruction address.
457 * @param enmCpuMode The CPU mode.
458 * @param fFilter The instruction filter settings.
459 * @param pfnReadBytes The byte reader, can be NULL.
460 * @param pvUser The user data for the reader.
461 */
462DECLHIDDEN(PCDISOPCODE) disInitializeStateArmV8(PDISSTATE pDis, DISCPUMODE enmCpuMode, uint32_t fFilter)
463{
464 RT_NOREF(pDis, enmCpuMode, fFilter);
465 return NULL;
466}
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