VirtualBox

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

Last change on this file since 99319 was 99319, checked in by vboxsync, 2 years ago

Disassember: Continue work on the ARMv8 disassember, defining the instruction table layout and intermediate structures for decoding, bugref:10394

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