VirtualBox

source: vbox/trunk/src/VBox/Disassembler/testcase/tstDisasmArmv8-1.cpp@ 105748

Last change on this file since 105748 was 105748, checked in by vboxsync, 5 months ago

Disassembler/testcase: Add tstDisasmArmv8-1 testcase for testing the ARMv8 disassembler by checking the disassembled output against the original source for semantic equality, bugref:10394

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.4 KB
Line 
1/* $Id: tstDisasmArmv8-1.cpp 105748 2024-08-21 07:37:51Z vboxsync $ */
2/** @file
3 * VBox disassembler - Testcase for ARMv8 A64
4 */
5
6/*
7 * Copyright (C) 2024 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#include <VBox/dis.h>
33#include <iprt/test.h>
34#include <iprt/ctype.h>
35#include <iprt/string.h>
36#include <iprt/err.h>
37#include <iprt/script.h>
38#include <iprt/stream.h>
39
40#include "tstDisasmArmv8-1-tests.h"
41
42typedef struct TESTRDR
43{
44 const char *pb;
45 unsigned cb;
46} TESTRDR;
47typedef TESTRDR *PTESTRDR;
48
49DECLASM(int) TestProcA64(void);
50DECLASM(int) TestProcA64_EndProc(void);
51
52
53static DECLCALLBACK(int) rtScriptLexParseNumber(RTSCRIPTLEX hScriptLex, char ch, PRTSCRIPTLEXTOKEN pToken, void *pvUser)
54{
55 RT_NOREF(ch, pvUser);
56 return RTScriptLexScanNumber(hScriptLex, 0 /*uBase*/, false /*fAllowReal*/, pToken);
57}
58
59
60static const char *s_aszSingleStart[] =
61{
62 ";",
63 NULL
64};
65
66
67static const RTSCRIPTLEXTOKMATCH s_aMatches[] =
68{
69 /* Begin of stuff which will get ignored in the semantic matching. */
70 { RT_STR_TUPLE(".private_extern"), RTSCRIPTLEXTOKTYPE_KEYWORD, true, 0 },
71 { RT_STR_TUPLE("_testproca64"), RTSCRIPTLEXTOKTYPE_KEYWORD, true, 0 },
72 { RT_STR_TUPLE("_testproca64_endproc"), RTSCRIPTLEXTOKTYPE_KEYWORD, true, 0 },
73 { RT_STR_TUPLE(":"), RTSCRIPTLEXTOKTYPE_KEYWORD, true, 0 },
74 /* End of stuff which will get ignored in the semantic matching. */
75
76 { RT_STR_TUPLE(","), RTSCRIPTLEXTOKTYPE_PUNCTUATOR, false, 0 },
77 { RT_STR_TUPLE("."), RTSCRIPTLEXTOKTYPE_PUNCTUATOR, false, 0 },
78 { RT_STR_TUPLE("["), RTSCRIPTLEXTOKTYPE_PUNCTUATOR, false, 0 },
79 { RT_STR_TUPLE("]"), RTSCRIPTLEXTOKTYPE_PUNCTUATOR, false, 0 },
80 { NULL, 0, RTSCRIPTLEXTOKTYPE_INVALID, false, 0 }
81};
82
83
84static const RTSCRIPTLEXRULE s_aRules[] =
85{
86 { '#', '#', RTSCRIPT_LEX_RULE_CONSUME, rtScriptLexParseNumber, NULL},
87 { '0', '9', RTSCRIPT_LEX_RULE_CONSUME, rtScriptLexParseNumber, NULL},
88 { 'a', 'z', RTSCRIPT_LEX_RULE_CONSUME, RTScriptLexScanIdentifier, NULL},
89 { 'A', 'Z', RTSCRIPT_LEX_RULE_CONSUME, RTScriptLexScanIdentifier, NULL},
90 { '_', '_', RTSCRIPT_LEX_RULE_CONSUME, RTScriptLexScanIdentifier, NULL},
91 { '\0', '\0', RTSCRIPT_LEX_RULE_DEFAULT, NULL, NULL}
92};
93
94
95static const RTSCRIPTLEXCFG s_LexCfg =
96{
97 /** pszName */
98 "ARMv8Dis",
99 /** pszDesc */
100 "ARMv8 disassembler lexer",
101 /** fFlags */
102 RTSCRIPT_LEX_CFG_F_CASE_INSENSITIVE,
103 /** pszWhitespace */
104 NULL,
105 /** pszNewline */
106 NULL,
107 /** papszCommentMultiStart */
108 NULL,
109 /** papszCommentMultiEnd */
110 NULL,
111 /** papszCommentSingleStart */
112 s_aszSingleStart,
113 /** paTokMatches */
114 s_aMatches,
115 /** paRules */
116 s_aRules,
117 /** pfnProdDef */
118 NULL,
119 /** pfnProdDefUser */
120 NULL
121};
122
123
124static DECLCALLBACK(int) testDisasmLexerRead(RTSCRIPTLEX hScriptLex, size_t offBuf, char *pchCur,
125 size_t cchBuf, size_t *pcchRead, void *pvUser)
126{
127 RT_NOREF(hScriptLex);
128
129 PTESTRDR pRdr = (PTESTRDR)pvUser;
130 size_t cbCopy = RT_MIN(cchBuf / sizeof(char), pRdr->cb - offBuf);
131 int rc = VINF_SUCCESS;
132
133 *pcchRead = cbCopy * sizeof(char);
134
135 if (cbCopy)
136 memcpy(pchCur, &pRdr->pb[offBuf], cbCopy);
137 else
138 rc = VINF_EOF;
139
140 return rc;
141}
142
143
144static void testDisas(const char *pszSub, uint8_t const *pabInstrs, uintptr_t uEndPtr, DISCPUMODE enmDisCpuMode,
145 const unsigned char *pbSrc, unsigned cbSrc)
146{
147 RTTestISub(pszSub);
148
149 RTSCRIPTLEX hLexSource = NULL;
150 TESTRDR Rdr;
151
152 Rdr.pb = (const char *)pbSrc;
153 Rdr.cb = cbSrc;
154 int rc = RTScriptLexCreateFromReader(&hLexSource, testDisasmLexerRead,
155 NULL /*pfnDtor*/, &Rdr /*pvUser*/, cbSrc,
156 NULL /*phStrCacheId*/, NULL /*phStrCacheStringLit*/,
157 &s_LexCfg);
158 RTTESTI_CHECK_RC(rc, VINF_SUCCESS);
159 if (RT_FAILURE(rc))
160 return; /* Can't do our work if this fails. */
161
162 PCRTSCRIPTLEXTOKEN pTokSource;
163 rc = RTScriptLexQueryToken(hLexSource, &pTokSource);
164 RTTESTI_CHECK_RC(rc, VINF_SUCCESS);
165
166 size_t const cbInstrs = uEndPtr - (uintptr_t)pabInstrs;
167 for (size_t off = 0; off < cbInstrs;)
168 {
169 DISSTATE Dis;
170 uint32_t cb = 1;
171#ifndef DIS_CORE_ONLY
172 uint32_t const cErrBefore = RTTestIErrorCount();
173 char szOutput[256] = {0};
174
175 /*
176 * Can't use DISInstrToStr() here as it would add addresses and opcode bytes
177 * which would trip the semantic matching later on.
178 */
179 rc = DISInstrEx((uintptr_t)&pabInstrs[off], enmDisCpuMode, DISOPTYPE_ALL,
180 NULL /*pfnReadBytes*/, NULL /*pvUser*/, &Dis, &cb);
181 RTTESTI_CHECK_RC(rc, VINF_SUCCESS);
182 RTTESTI_CHECK(cb == Dis.cbInstr);
183 RTTESTI_CHECK(cb == sizeof(uint32_t));
184
185 if (RT_SUCCESS(rc))
186 {
187 size_t cch = 0;
188
189 switch (enmDisCpuMode)
190 {
191 case DISCPUMODE_ARMV8_A64:
192 case DISCPUMODE_ARMV8_A32:
193 case DISCPUMODE_ARMV8_T32:
194 cch = DISFormatArmV8Ex(&Dis, &szOutput[0], sizeof(szOutput),
195 DIS_FMT_FLAGS_RELATIVE_BRANCH,
196 NULL /*pfnGetSymbol*/, NULL /*pvUser*/);
197 break;
198 default:
199 AssertReleaseFailed(); /* Testcase error. */
200 break;
201 }
202
203 szOutput[cch] = '\0';
204 RTStrStripR(szOutput);
205 RTTESTI_CHECK(szOutput[0]);
206 if (szOutput[0])
207 {
208 /* Build the lexer and compare that it semantically is equal to the source input. */
209 RTSCRIPTLEX hLexDis = NULL;
210 rc = RTScriptLexCreateFromString(&hLexDis, szOutput, NULL /*phStrCacheId*/,
211 NULL /*phStrCacheStringLit*/, &s_LexCfg);
212 RTTESTI_CHECK_RC(rc, VINF_SUCCESS);
213 if (RT_SUCCESS(rc))
214 {
215 PCRTSCRIPTLEXTOKEN pTokDis;
216 rc = RTScriptLexQueryToken(hLexDis, &pTokDis);
217 RTTESTI_CHECK_RC(rc, VINF_SUCCESS);
218
219 /*
220 * Skip over any keyword tokens in the source lexer because these
221 * are not part of the disassembly.
222 */
223 while (pTokSource->enmType == RTSCRIPTLEXTOKTYPE_KEYWORD)
224 pTokSource = RTScriptLexConsumeToken(hLexSource);
225
226 /* Now compare the token streams until we hit EOS in the disassembly lexer. */
227 do
228 {
229 RTTESTI_CHECK(pTokSource->enmType == pTokDis->enmType);
230 if (pTokSource->enmType == pTokDis->enmType)
231 {
232 switch (pTokSource->enmType)
233 {
234 case RTSCRIPTLEXTOKTYPE_IDENTIFIER:
235 {
236 int iCmp = strcmp(pTokSource->Type.Id.pszIde, pTokDis->Type.Id.pszIde);
237 RTTESTI_CHECK(!iCmp);
238 if (iCmp)
239 RTTestIFailureDetails("<IDE{%u.%u, %u.%u}, %s != %s>\n",
240 pTokSource->PosStart.iLine, pTokSource->PosStart.iCh,
241 pTokSource->PosEnd.iLine, pTokSource->PosEnd.iCh,
242 pTokSource->Type.Id.pszIde, pTokDis->Type.Id.pszIde);
243 break;
244 }
245 case RTSCRIPTLEXTOKTYPE_NUMBER:
246 RTTESTI_CHECK(pTokSource->Type.Number.enmType == pTokDis->Type.Number.enmType);
247 if (pTokSource->Type.Number.enmType == pTokDis->Type.Number.enmType)
248 {
249 switch (pTokSource->Type.Number.enmType)
250 {
251 case RTSCRIPTLEXTOKNUMTYPE_NATURAL:
252 {
253 RTTESTI_CHECK(pTokSource->Type.Number.Type.u64 == pTokDis->Type.Number.Type.u64);
254 if (pTokSource->Type.Number.Type.u64 != pTokDis->Type.Number.Type.u64)
255 RTTestIFailureDetails("<NUM{%u.%u, %u.%u} %RU64 != %RU64>\n",
256 pTokSource->PosStart.iLine, pTokSource->PosStart.iCh,
257 pTokSource->PosEnd.iLine, pTokSource->PosEnd.iCh,
258 pTokSource->Type.Number.Type.u64, pTokDis->Type.Number.Type.u64);
259 break;
260 }
261 case RTSCRIPTLEXTOKNUMTYPE_INTEGER:
262 {
263 RTTESTI_CHECK(pTokSource->Type.Number.Type.i64 == pTokDis->Type.Number.Type.i64);
264 if (pTokSource->Type.Number.Type.i64 != pTokDis->Type.Number.Type.i64)
265 RTTestIFailureDetails("<NUM{%u.%u, %u.%u} %RI64 != %RI64>\n",
266 pTokSource->PosStart.iLine, pTokSource->PosStart.iCh,
267 pTokSource->PosEnd.iLine, pTokSource->PosEnd.iCh,
268 pTokSource->Type.Number.Type.i64, pTokDis->Type.Number.Type.i64);
269 break;
270 }
271 case RTSCRIPTLEXTOKNUMTYPE_REAL:
272 default:
273 AssertReleaseFailed();
274 }
275 }
276 else
277 RTTestIFailureDetails("<NUM{%u.%u, %u.%u} %u != %u>\n",
278 pTokSource->PosStart.iLine, pTokSource->PosStart.iCh,
279 pTokSource->PosEnd.iLine, pTokSource->PosEnd.iCh,
280 pTokSource->Type.Number.enmType, pTokDis->Type.Number.enmType);
281 break;
282 case RTSCRIPTLEXTOKTYPE_PUNCTUATOR:
283 {
284 int iCmp = strcmp(pTokSource->Type.Punctuator.pPunctuator->pszMatch,
285 pTokDis->Type.Punctuator.pPunctuator->pszMatch);
286 RTTESTI_CHECK(!iCmp);
287 if (iCmp)
288 RTTestIFailureDetails("<PUNCTUATOR{%u.%u, %u.%u}, %s != %s>\n",
289 pTokSource->PosStart.iLine, pTokSource->PosStart.iCh,
290 pTokSource->PosEnd.iLine, pTokSource->PosEnd.iCh,
291 pTokSource->Type.Punctuator.pPunctuator->pszMatch,
292 pTokDis->Type.Punctuator.pPunctuator->pszMatch);
293 break;
294 }
295
296 /* These should never occur and indicate an issue in the lexer. */
297 case RTSCRIPTLEXTOKTYPE_KEYWORD:
298 RTTestIFailureDetails("<KEYWORD{%u.%u, %u.%u}, %s>\n",
299 pTokSource->PosStart.iLine, pTokSource->PosStart.iCh,
300 pTokSource->PosEnd.iLine, pTokSource->PosEnd.iCh,
301 pTokSource->Type.Keyword.pKeyword->pszMatch);
302 break;
303 case RTSCRIPTLEXTOKTYPE_STRINGLIT:
304 RTTestIFailureDetails("<STRINGLIT{%u.%u, %u.%u}, %s>\n",
305 pTokSource->PosStart.iLine, pTokSource->PosStart.iCh,
306 pTokSource->PosEnd.iLine, pTokSource->PosEnd.iCh,
307 pTokSource->Type.StringLit.pszString);
308 break;
309 case RTSCRIPTLEXTOKTYPE_OPERATOR:
310 RTTestIFailureDetails("<OPERATOR{%u.%u, %u.%u}, %s>\n",
311 pTokSource->PosStart.iLine, pTokSource->PosStart.iCh,
312 pTokSource->PosEnd.iLine, pTokSource->PosEnd.iCh,
313 pTokSource->Type.Operator.pOp->pszMatch);
314 break;
315 case RTSCRIPTLEXTOKTYPE_INVALID:
316 RTTestIFailureDetails("<INVALID>\n");
317 break;
318 case RTSCRIPTLEXTOKTYPE_ERROR:
319 RTTestIFailureDetails("<ERROR{%u.%u, %u.%u}> %s\n",
320 pTokSource->PosStart.iLine, pTokSource->PosStart.iCh,
321 pTokSource->PosEnd.iLine, pTokSource->PosEnd.iCh,
322 pTokSource->Type.Error.pErr->pszMsg);
323 break;
324 case RTSCRIPTLEXTOKTYPE_EOS:
325 RTTestIFailureDetails("<EOS>\n");
326 break;
327 default:
328 AssertFailed();
329 }
330 }
331 else
332 RTTestIFailureDetails("pTokSource->enmType=%u pTokDis->enmType=%u\n",
333 pTokSource->enmType, pTokDis->enmType);
334
335 /*
336 * Abort on error as the streams are now out of sync and matching will not work
337 * anymore producing lots of noise.
338 */
339 if (cErrBefore != RTTestIErrorCount())
340 break;
341
342 /* Advance to the next token. */
343 pTokDis = RTScriptLexConsumeToken(hLexDis);
344 Assert(pTokDis);
345
346 pTokSource = RTScriptLexConsumeToken(hLexSource);
347 Assert(pTokSource);
348 } while (pTokDis->enmType != RTSCRIPTLEXTOKTYPE_EOS);
349
350 RTScriptLexDestroy(hLexDis);
351 }
352 }
353 if (cErrBefore != RTTestIErrorCount())
354 {
355 RTTestIFailureDetails("rc=%Rrc, off=%#x (%u) cbInstr=%u enmDisCpuMode=%d\n",
356 rc, off, off, Dis.cbInstr, enmDisCpuMode);
357 RTTestIPrintf(RTTESTLVL_ALWAYS, "%s\n", szOutput);
358 break;
359 }
360
361 /* Do the output formatting again, now with all the addresses and opcode bytes. */
362 DISFormatArmV8Ex(&Dis, szOutput, sizeof(szOutput),
363 DIS_FMT_FLAGS_BYTES_LEFT | DIS_FMT_FLAGS_BYTES_BRACKETS | DIS_FMT_FLAGS_BYTES_SPACED
364 | DIS_FMT_FLAGS_RELATIVE_BRANCH | DIS_FMT_FLAGS_ADDR_LEFT,
365 NULL /*pfnGetSymbol*/, NULL /*pvUser*/);
366 RTStrStripR(szOutput);
367 RTTESTI_CHECK(szOutput[0]);
368 RTTestIPrintf(RTTESTLVL_ALWAYS, "%s\n", szOutput);
369 }
370
371 /* Check with size-only. */
372 uint32_t cbOnly = 1;
373 DISSTATE DisOnly;
374 rc = DISInstrWithPrefetchedBytes((uintptr_t)&pabInstrs[off], enmDisCpuMode, 0 /*fFilter - none */,
375 Dis.Instr.ab, Dis.cbCachedInstr, NULL, NULL, &DisOnly, &cbOnly);
376
377 RTTESTI_CHECK_RC(rc, VINF_SUCCESS);
378 RTTESTI_CHECK(cbOnly == DisOnly.cbInstr);
379 RTTESTI_CHECK_MSG(cbOnly == cb, ("%#x vs %#x\n", cbOnly, cb));
380
381#else /* DIS_CORE_ONLY */
382 rc = DISInstr(&pabInstrs[off], enmDisCpuMode, &Dis, &cb);
383 RTTESTI_CHECK_RC(rc, VINF_SUCCESS);
384 RTTESTI_CHECK(cb == Dis.cbInstr);
385#endif /* DIS_CORE_ONLY */
386
387 off += cb;
388 }
389
390 RTScriptLexDestroy(hLexSource);
391}
392
393
394int main(int argc, char **argv)
395{
396 RT_NOREF2(argc, argv);
397 RTTEST hTest;
398 RTEXITCODE rcExit = RTTestInitAndCreate("tstDisasm", &hTest);
399 if (rcExit)
400 return rcExit;
401 RTTestBanner(hTest);
402
403 static const struct
404 {
405 const char *pszDesc;
406 uint8_t const *pbStart;
407 uintptr_t uEndPtr;
408 DISCPUMODE enmCpuMode;
409 const unsigned char *pbSrc;
410 unsigned cbSrc;
411 } aSnippets[] =
412 {
413#ifndef RT_OS_OS2
414 { "64-bit", (uint8_t const *)(uintptr_t)TestProcA64, (uintptr_t)&TestProcA64_EndProc, DISCPUMODE_ARMV8_A64,
415 g_abtstDisasmArmv8_1, g_cbtstDisasmArmv8_1 },
416#endif
417 };
418
419 for (unsigned i = 0; i < RT_ELEMENTS(aSnippets); i++)
420 testDisas(aSnippets[i].pszDesc, aSnippets[i].pbStart, aSnippets[i].uEndPtr, aSnippets[i].enmCpuMode,
421 aSnippets[i].pbSrc, aSnippets[i].cbSrc);
422
423 return RTTestSummaryAndDestroy(hTest);
424}
425
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