ntdll/tests: Run RtlVirtualUnwind tests on ARM64EC.
[wine.git] / dlls / ntdll / tests / unwind.c
blob93f2cd0d018fe854534a7f467c1a80217eb79e7b
1 /*
2 * Unit test suite for exception unwinding
4 * Copyright 2009, 2024 Alexandre Julliard
5 * Copyright 2020, 2021 Martin Storsjö
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #include <stdarg.h>
23 #include <stdio.h>
25 #include "ntstatus.h"
26 #define WIN32_NO_STATUS
27 #include "windef.h"
28 #include "winbase.h"
29 #include "winnt.h"
30 #include "winreg.h"
31 #include "winnt.h"
32 #include "winternl.h"
33 #include "wine/test.h"
35 #ifndef __i386__
37 static void *code_mem;
38 static HMODULE ntdll;
40 static PRUNTIME_FUNCTION (WINAPI *pRtlLookupFunctionEntry)(ULONG_PTR, ULONG_PTR*, UNWIND_HISTORY_TABLE*);
41 static PRUNTIME_FUNCTION (WINAPI *pRtlLookupFunctionTable)(ULONG_PTR, ULONG_PTR*, ULONG*);
42 static BOOLEAN (CDECL *pRtlInstallFunctionTableCallback)(DWORD64, DWORD64, DWORD, PGET_RUNTIME_FUNCTION_CALLBACK, PVOID, PCWSTR);
43 static BOOLEAN (CDECL *pRtlAddFunctionTable)(RUNTIME_FUNCTION*, DWORD, DWORD64);
44 static BOOLEAN (CDECL *pRtlDeleteFunctionTable)(RUNTIME_FUNCTION*);
45 static DWORD (WINAPI *pRtlAddGrowableFunctionTable)(void**, RUNTIME_FUNCTION*, DWORD, DWORD, ULONG_PTR, ULONG_PTR);
46 static void (WINAPI *pRtlGrowFunctionTable)(void*, DWORD);
47 static void (WINAPI *pRtlDeleteGrowableFunctionTable)(void*);
48 static NTSTATUS (WINAPI *pNtAllocateVirtualMemoryEx)(HANDLE,PVOID*,SIZE_T*,ULONG,ULONG,MEM_EXTENDED_PARAMETER*,ULONG);
50 #ifdef __arm__
52 #define UWOP_TWOBYTES(x) (((x) >> 8) & 0xff), ((x) & 0xff)
53 #define UWOP_THREEBYTES(x) (((x) >> 16) & 0xff), (((x) >> 8) & 0xff), ((x) & 0xff)
54 #define UWOP_FOURBYTES(x) (((x) >> 24) & 0xff), (((x) >> 16) & 0xff), (((x) >> 8) & 0xff), ((x) & 0xff)
56 #define UWOP_ALLOC_SMALL(size) (0x00 | (size/4)) /* Max 0x7f * 4 */
57 #define UWOP_SAVE_REGSW(regmask) UWOP_TWOBYTES((0x80 << 8) | (regmask))
58 #define UWOP_SET_FP(reg) (0xC0 | reg)
59 #define UWOP_SAVE_RANGE_4_7_LR(reg,lr) (0xD0 | (reg - 4) | ((lr) ? 0x04 : 0))
60 #define UWOP_SAVE_RANGE_4_11_LR(reg,lr)(0xD8 | (reg - 8) | ((lr) ? 0x04 : 0))
61 #define UWOP_SAVE_D8_RANGE(reg) (0xE0 | (reg - 8))
62 #define UWOP_ALLOC_MEDIUMW(size) UWOP_TWOBYTES((0xE8 << 8) | (size/4)) /* Max 0x3ff * 4 */
63 #define UWOP_SAVE_REGS(regmask) UWOP_TWOBYTES((0xEC << 8) | ((regmask) & 0xFF) | (((regmask) & (1<<lr)) ? 0x100 : 0))
64 #define UWOP_SAVE_LR(offset) UWOP_TWOBYTES((0xEF << 8) | (offset/4))
65 #define UWOP_SAVE_D0_RANGE(first,last) UWOP_TWOBYTES((0xF5 << 8) | (first << 4) | (last))
66 #define UWOP_SAVE_D16_RANGE(first,last)UWOP_TWOBYTES((0xF6 << 8) | ((first - 16) << 4) | (last - 16))
67 #define UWOP_ALLOC_LARGE(size) UWOP_THREEBYTES((0xF7 << 16) | (size/4))
68 #define UWOP_ALLOC_HUGE(size) UWOP_FOURBYTES((0xF8u << 24) | (size/4))
69 #define UWOP_ALLOC_LARGEW(size) UWOP_THREEBYTES((0xF9 << 16) | (size/4))
70 #define UWOP_ALLOC_HUGEW(size) UWOP_FOURBYTES((0xFAu << 24) | (size/4))
71 #define UWOP_MSFT_OP_MACHINE_FRAME 0xEE,0x01
72 #define UWOP_MSFT_OP_CONTEXT 0xEE,0x02
73 #define UWOP_NOP16 0xFB
74 #define UWOP_NOP32 0xFC
75 #define UWOP_END_NOP16 0xFD
76 #define UWOP_END_NOP32 0xFE
77 #define UWOP_END 0xFF
79 struct results_arm
81 int pc_offset; /* pc offset from code start */
82 int fp_offset; /* fp offset from stack pointer */
83 int handler; /* expect handler to be set? */
84 ULONG_PTR pc; /* expected final pc value */
85 int frame; /* expected frame return value */
86 int frame_offset; /* whether the frame return value is an offset or an absolute value */
87 LONGLONG regs[47][2];/* expected values for registers */
90 struct unwind_test_arm
92 const BYTE *function;
93 size_t function_size;
94 const BYTE *unwind_info;
95 size_t unwind_size;
96 const struct results_arm *results;
97 unsigned int nb_results;
100 enum regs
102 /* Note, lr and sp are swapped to allow using 'lr' in register bitmasks. */
103 r0, r1, r2, r3, r4, r5, r6, r7,
104 r8, r9, r10, r11, r12, lr, sp,
105 d0, d1, d2, d3, d4, d5, d6, d7,
106 d8, d9, d10, d11, d12, d13, d14, d15,
107 d16, d17, d18, d19, d20, d21, d22, d23,
108 d24, d25, d26, d27, d28, d29, d30, d31,
111 static const char * const reg_names_arm[47] =
113 "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
114 "r8", "r9", "r10", "r11", "r12", "lr", "sp",
115 "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
116 "d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15",
117 "d16", "d17", "d18", "d19", "d20", "d21", "d22", "d23",
118 "d24", "d25", "d26", "d27", "d28", "d29", "d30", "d31",
121 #define ORIG_LR 0xCCCCCCCC
123 static void call_virtual_unwind_arm( int testnum, const struct unwind_test_arm *test )
125 static const int code_offset = 1024;
126 static const int unwind_offset = 2048;
127 void *handler, *data;
128 CONTEXT context;
129 RUNTIME_FUNCTION runtime_func;
130 KNONVOLATILE_CONTEXT_POINTERS ctx_ptr;
131 UINT i, j, k;
132 ULONG fake_stack[256];
133 ULONG_PTR frame, orig_pc, orig_fp, unset_reg;
134 ULONGLONG unset_reg64;
135 static const UINT nb_regs = ARRAY_SIZE(test->results[i].regs);
137 memcpy( (char *)code_mem + code_offset, test->function, test->function_size );
138 memcpy( (char *)code_mem + unwind_offset, test->unwind_info, test->unwind_size );
140 runtime_func.BeginAddress = code_offset;
141 if (test->unwind_size)
142 runtime_func.UnwindData = unwind_offset;
143 else
144 memcpy(&runtime_func.UnwindData, test->unwind_info, 4);
146 trace( "code: %p stack: %p\n", code_mem, fake_stack );
148 for (i = 0; i < test->nb_results; i++)
150 memset( &ctx_ptr, 0, sizeof(ctx_ptr) );
151 memset( &context, 0x55, sizeof(context) );
152 memset( &unset_reg, 0x55, sizeof(unset_reg) );
153 memset( &unset_reg64, 0x55, sizeof(unset_reg64) );
154 for (j = 0; j < 256; j++) fake_stack[j] = j * 4;
156 context.Sp = (ULONG_PTR)fake_stack;
157 context.Lr = (ULONG_PTR)ORIG_LR;
158 context.R11 = (ULONG_PTR)fake_stack + test->results[i].fp_offset;
159 orig_fp = context.R11;
160 orig_pc = (ULONG_PTR)code_mem + code_offset + test->results[i].pc_offset;
162 trace( "%u/%u: pc=%p (%02x) fp=%p sp=%p\n", testnum, i,
163 (void *)orig_pc, *(UINT *)orig_pc, (void *)orig_fp, (void *)context.Sp );
165 data = (void *)0xdeadbeef;
166 handler = RtlVirtualUnwind( UNW_FLAG_EHANDLER, (ULONG)code_mem, orig_pc,
167 &runtime_func, &context, &data, &frame, &ctx_ptr );
168 if (test->results[i].handler > 0)
170 ok( (char *)handler == (char *)code_mem + 0x200,
171 "%u/%u: wrong handler %p/%p\n", testnum, i, handler, (char *)code_mem + 0x200 );
172 if (handler) ok( *(DWORD *)data == 0x08070605,
173 "%u/%u: wrong handler data %lx\n", testnum, i, *(DWORD *)data );
175 else
177 ok( handler == NULL, "%u/%u: handler %p instead of NULL\n", testnum, i, handler );
178 ok( data == (test->results[i].handler < 0 ?
179 (void *)0xdeadbeef : NULL),
180 "%u/%u: handler data set to %p/%p\n", testnum, i, data,
181 (test->results[i].handler < 0 ? (void *)0xdeadbeef : NULL) );
184 ok( context.Pc == test->results[i].pc, "%u/%u: wrong pc %p/%p\n",
185 testnum, i, (void *)context.Pc, (void*)test->results[i].pc );
186 ok( frame == (test->results[i].frame_offset ? (ULONG)fake_stack : 0) + test->results[i].frame, "%u/%u: wrong frame %x/%x\n",
187 testnum, i, (int)((char *)frame - (char *)(test->results[i].frame_offset ? fake_stack : NULL)), test->results[i].frame );
189 for (j = 0; j < 47; j++)
191 for (k = 0; k < nb_regs; k++)
193 if (test->results[i].regs[k][0] == -1)
195 k = nb_regs;
196 break;
198 if (test->results[i].regs[k][0] == j) break;
201 if (j >= 4 && j <= 11 && (&ctx_ptr.R4)[j - 4])
203 ok( k < nb_regs, "%u/%u: register %s should not be set to %lx\n",
204 testnum, i, reg_names_arm[j], (&context.R0)[j] );
205 if (k < nb_regs)
206 ok( (&context.R0)[j] == test->results[i].regs[k][1],
207 "%u/%u: register %s wrong %p/%x\n",
208 testnum, i, reg_names_arm[j], (void *)(&context.R0)[j], (int)test->results[i].regs[k][1] );
210 else if (j == lr && ctx_ptr.Lr)
212 ok( k < nb_regs, "%u/%u: register %s should not be set to %lx\n",
213 testnum, i, reg_names_arm[j], context.Lr );
214 if (k < nb_regs)
215 ok( context.Lr == test->results[i].regs[k][1],
216 "%u/%u: register %s wrong %p/%x\n",
217 testnum, i, reg_names_arm[j], (void *)context.Lr, (int)test->results[i].regs[k][1] );
219 else if (j == sp)
221 if (k < nb_regs)
222 ok( context.Sp == test->results[i].regs[k][1],
223 "%u/%u: register %s wrong %p/%x\n",
224 testnum, i, reg_names_arm[j], (void *)context.Sp, (int)test->results[i].regs[k][1] );
225 else
226 ok( context.Sp == frame, "%u/%u: wrong sp %p/%p\n",
227 testnum, i, (void *)context.Sp, (void *)frame);
229 else if (j >= d8 && j <= d15 && (&ctx_ptr.D8)[j - d8])
231 ok( k < nb_regs, "%u/%u: register %s should not be set to %llx\n",
232 testnum, i, reg_names_arm[j], context.D[j - d0] );
233 if (k < nb_regs)
234 ok( context.D[j - d0] == test->results[i].regs[k][1],
235 "%u/%u: register %s wrong %llx/%llx\n",
236 testnum, i, reg_names_arm[j], context.D[j - d0], test->results[i].regs[k][1] );
238 else if (k < nb_regs)
240 if (j <= r12)
241 ok( (&context.R0)[j] == test->results[i].regs[k][1],
242 "%u/%u: register %s wrong %p/%x\n",
243 testnum, i, reg_names_arm[j], (void *)(&context.R0)[j], (int)test->results[i].regs[k][1] );
244 else if (j == lr)
245 ok( context.Lr == test->results[i].regs[k][1],
246 "%u/%u: register %s wrong %p/%x\n",
247 testnum, i, reg_names_arm[j], (void *)context.Lr, (int)test->results[i].regs[k][1] );
248 else
249 ok( context.D[j - d0] == test->results[i].regs[k][1],
250 "%u/%u: register %s wrong %llx/%llx\n",
251 testnum, i, reg_names_arm[j], context.D[j - d0], test->results[i].regs[k][1] );
253 else
255 ok( k == nb_regs, "%u/%u: register %s should be set\n", testnum, i, reg_names_arm[j] );
256 if (j == lr)
257 ok( context.Lr == ORIG_LR, "%u/%u: register lr wrong %p/unset\n",
258 testnum, i, (void *)context.Lr );
259 else if (j == r11)
260 ok( context.R11 == orig_fp, "%u/%u: register fp wrong %p/unset\n",
261 testnum, i, (void *)context.R11 );
262 else if (j < d0)
263 ok( (&context.R0)[j] == unset_reg,
264 "%u/%u: register %s wrong %p/unset\n",
265 testnum, i, reg_names_arm[j], (void *)(&context.R0)[j]);
266 else
267 ok( context.D[j - d0] == unset_reg64,
268 "%u/%u: register %s wrong %llx/unset\n",
269 testnum, i, reg_names_arm[j], context.D[j - d0]);
275 #define DW(dword) ((dword >> 0) & 0xff), ((dword >> 8) & 0xff), ((dword >> 16) & 0xff), ((dword >> 24) & 0xff)
277 static void test_virtual_unwind_arm(void)
280 static const BYTE function_0[] =
282 0x70, 0xb5, /* 00: push {r4-r6, lr} */
283 0x88, 0xb0, /* 02: sub sp, sp, #32 */
284 0x2d, 0xed, 0x06, 0x8b, /* 04: vpush {d8-d10} */
285 0x00, 0xbf, /* 08: nop */
286 0x2d, 0xed, 0x06, 0x3b, /* 0a: vpush {d3-d5} */
287 0xaf, 0x3f, 0x00, 0x80, /* 0e: nop.w */
288 0x6d, 0xed, 0x06, 0x1b, /* 12: vpush {d17-d19} */
289 0x2d, 0xe9, 0x00, 0x15, /* 16: push.w {r8, r10, r12} */
290 0xeb, 0x46, /* 1a: mov r11, sp */
291 0x00, 0xbf, /* 1c: nop */
292 0xbd, 0xec, 0x06, 0x8b, /* 1e: vpop {d8-d10} */
293 0xdd, 0x46, /* 22: mov sp, r11 */
294 0x08, 0xb0, /* 24: add sp, sp, #32 */
295 0x70, 0xbd, /* 26: pop {r4-r6, pc} */
298 static const DWORD unwind_info_0_header =
299 (sizeof(function_0)/2) | /* function length */
300 (1 << 20) | /* X */
301 (0 << 21) | /* E */
302 (0 << 22) | /* F */
303 (1 << 23) | /* epilog */
304 (5 << 28); /* codes, (sizeof(unwind_info_0)-headers+3)/4 */
305 static const DWORD unwind_info_0_epilog0 =
306 (15 << 0) | /* offset = 0x1e / 2 = 15 */
307 (0xE << 20) | /* condition, 0xE = always */
308 (13 << 24); /* index, byte offset to epilog opcodes */
310 static const BYTE unwind_info_0[] =
312 DW(unwind_info_0_header),
313 DW(unwind_info_0_epilog0),
315 UWOP_SET_FP(11), /* mov r11, sp */
316 UWOP_SAVE_REGSW((1<<r8)|(1<<r10)|(1<<r12)), /* push.w {r8, r10, r12} */
317 UWOP_SAVE_D16_RANGE(17,19), /* vpush {d17-d19} */
318 UWOP_NOP32, /* nop.w */
319 UWOP_SAVE_D0_RANGE(3,5), /* vpush {d3-d5} */
320 UWOP_NOP16, /* nop */
321 UWOP_SAVE_D8_RANGE(10), /* vpush {d8-d10} */
322 UWOP_ALLOC_SMALL(32), /* sub sp, sp, #32 */
323 UWOP_SAVE_RANGE_4_7_LR(6, 1), /* push {r4-r6,lr} */
324 UWOP_END,
326 UWOP_SAVE_D8_RANGE(10), /* vpop {d8-d10} */
327 UWOP_SET_FP(11), /* mov sp, r11 */
328 UWOP_ALLOC_SMALL(32), /* add sp, sp, #32 */
329 UWOP_SAVE_RANGE_4_7_LR(6, 1), /* pop {r4-r6,pc} */
330 UWOP_END,
332 0, 0, /* align */
333 0x00, 0x02, 0x00, 0x00, /* handler */
334 0x05, 0x06, 0x07, 0x08, /* data */
337 static const struct results_arm results_0[] =
339 /* offset fp handler pc frame offset registers */
340 { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
341 { 0x02, 0x10, 0, 0x0c, 0x010, TRUE, { {r4,0x00}, {r5,0x04}, {r6,0x08}, {lr,0x0c}, {-1,-1} }},
342 { 0x04, 0x10, 0, 0x2c, 0x030, TRUE, { {r4,0x20}, {r5,0x24}, {r6,0x28}, {lr,0x2c}, {-1,-1} }},
343 { 0x08, 0x10, 0, 0x44, 0x048, TRUE, { {r4,0x38}, {r5,0x3c}, {r6,0x40}, {lr,0x44}, {d8, 0x400000000}, {d9, 0xc00000008}, {d10, 0x1400000010}, {-1,-1} }},
344 { 0x0a, 0x10, 0, 0x44, 0x048, TRUE, { {r4,0x38}, {r5,0x3c}, {r6,0x40}, {lr,0x44}, {d8, 0x400000000}, {d9, 0xc00000008}, {d10, 0x1400000010}, {-1,-1} }},
345 { 0x0e, 0x10, 0, 0x5c, 0x060, TRUE, { {r4,0x50}, {r5,0x54}, {r6,0x58}, {lr,0x5c}, {d8, 0x1c00000018}, {d9, 0x2400000020}, {d10, 0x2c00000028}, {d3, 0x400000000}, {d4, 0xc00000008}, {d5, 0x1400000010}, {-1,-1} }},
346 { 0x12, 0x10, 0, 0x5c, 0x060, TRUE, { {r4,0x50}, {r5,0x54}, {r6,0x58}, {lr,0x5c}, {d8, 0x1c00000018}, {d9, 0x2400000020}, {d10, 0x2c00000028}, {d3, 0x400000000}, {d4, 0xc00000008}, {d5, 0x1400000010}, {-1,-1} }},
347 { 0x16, 0x10, 0, 0x74, 0x078, TRUE, { {r4,0x68}, {r5,0x6c}, {r6,0x70}, {lr,0x74}, {d8, 0x3400000030}, {d9, 0x3c00000038}, {d10, 0x4400000040}, {d3, 0x1c00000018}, {d4, 0x2400000020}, {d5, 0x2c00000028}, {d17, 0x400000000}, {d18, 0xc00000008}, {d19, 0x1400000010}, {-1,-1} }},
348 { 0x1a, 0x10, 0, 0x80, 0x084, TRUE, { {r4,0x74}, {r5,0x78}, {r6,0x7c}, {lr,0x80}, {d8, 0x400000003c}, {d9, 0x4800000044}, {d10, 0x500000004c}, {d3, 0x2800000024}, {d4, 0x300000002c}, {d5, 0x3800000034}, {d17, 0x100000000c}, {d18, 0x1800000014}, {d19, 0x200000001c}, {r8,0x00}, {r10,0x04}, {r12,0x08}, {-1,-1} }},
349 { 0x1c, 0x10, 1, 0x90, 0x094, TRUE, { {r4,0x84}, {r5,0x88}, {r6,0x8c}, {lr,0x90}, {d8, 0x500000004c}, {d9, 0x5800000054}, {d10, 0x600000005c}, {d3, 0x3800000034}, {d4, 0x400000003c}, {d5, 0x4800000044}, {d17, 0x200000001c}, {d18, 0x2800000024}, {d19, 0x300000002c}, {r8,0x10}, {r10,0x14}, {r12,0x18}, {-1,-1} }},
350 { 0x1e, 0x10, 0, 0x3c, 0x040, TRUE, { {r4,0x30}, {r5,0x34}, {r6,0x38}, {lr,0x3c}, {d8, 0x400000000}, {d9, 0xc00000008}, {d10, 0x1400000010}, {-1,-1} }},
351 { 0x22, 0x10, 0, 0x3c, 0x040, TRUE, { {r4,0x30}, {r5,0x34}, {r6,0x38}, {lr,0x3c}, {-1,-1} }},
352 { 0x24, 0x10, 0, 0x2c, 0x030, TRUE, { {r4,0x20}, {r5,0x24}, {r6,0x28}, {lr,0x2c}, {-1,-1} }},
353 { 0x26, 0x10, 0, 0x0c, 0x010, TRUE, { {r4,0x00}, {r5,0x04}, {r6,0x08}, {lr,0x0c}, {-1,-1} }},
356 static const BYTE function_1[] =
358 0x30, 0xb4, /* 00: push {r4-r5} */
359 0x4d, 0xf8, 0x20, 0xed, /* 02: str lr, [sp, #-32]! */
360 0x00, 0xbf, /* 06: nop */
361 0x5d, 0xf8, 0x20, 0xeb, /* 08: ldr lr, [sp], #32 */
362 0x30, 0xbc, /* 0c: pop {r4-r5} */
363 0x70, 0x47, /* 0e: bx lr */
366 static const DWORD unwind_info_1_header =
367 (sizeof(function_1)/2) | /* function length */
368 (0 << 20) | /* X */
369 (0 << 21) | /* E */
370 (0 << 22) | /* F */
371 (0 << 23) | /* epilog */
372 (0 << 28); /* codes */
373 static const DWORD unwind_info_1_header2 =
374 (1 << 0) | /* epilog */
375 (2 << 16); /* codes, (sizeof(unwind_info_1)-headers+3)/4 */
376 static const DWORD unwind_info_1_epilog0 =
377 (4 << 0) | /* offset = 0x08 / 2 = 4 */
378 (0xE << 20) | /* condition, 0xE = always */
379 (4 << 24); /* index, byte offset to epilog opcodes */
381 static const BYTE unwind_info_1[] = {
382 DW(unwind_info_1_header),
383 DW(unwind_info_1_header2),
384 DW(unwind_info_1_epilog0),
386 UWOP_SAVE_LR(32), /* str lr, [sp, #-32]! */
387 UWOP_SAVE_RANGE_4_7_LR(5, 0), /* push {r4-r5} */
388 UWOP_END_NOP16,
390 UWOP_SAVE_LR(32), /* ldr lr, [sp], #32 */
391 UWOP_SAVE_RANGE_4_7_LR(5, 0), /* pop {r4-r5} */
392 UWOP_END_NOP16, /* bx lr */
395 static const struct results_arm results_1[] =
397 /* offset fp handler pc frame offset registers */
398 { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
399 { 0x02, 0x00, 0, ORIG_LR, 0x008, TRUE, { {r4,0x00}, {r5,0x04}, {-1,-1} }},
400 { 0x06, 0x00, 0, 0x00, 0x028, TRUE, { {r4,0x20}, {r5,0x24}, {lr,0x00}, {-1,-1} }},
401 { 0x08, 0x00, 0, 0x00, 0x028, TRUE, { {r4,0x20}, {r5,0x24}, {lr,0x00}, {-1,-1} }},
402 { 0x0c, 0x00, 0, ORIG_LR, 0x008, TRUE, { {r4,0x00}, {r5,0x04}, {-1,-1} }},
403 { 0x0e, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
406 static const BYTE function_2[] =
408 0x6f, 0x46, /* 00: mov r7, sp */
409 0x80, 0xb4, /* 02: push {r7} */
410 0x84, 0xb0, /* 04: sub sp, sp, #16 */
411 0x00, 0xbf, /* 06: nop */
412 0x04, 0xb0, /* 08: add sp, sp, #16 */
413 0x80, 0xbc, /* 0a: push {r7} */
414 0xbd, 0x46, /* 0c: mov sp, r7 */
415 0x00, 0xf0, 0x00, 0xb8, /* 0e: b tailcall */
418 static const DWORD unwind_info_2_header =
419 (sizeof(function_2)/2) | /* function length */
420 (0 << 20) | /* X */
421 (1 << 21) | /* E */
422 (0 << 22) | /* F */
423 (0 << 23) | /* epilog */
424 (2 << 28); /* codes, (sizeof(unwind_info_2)-headers+3)/4 */
426 static const BYTE unwind_info_2[] =
428 DW(unwind_info_2_header),
430 UWOP_ALLOC_SMALL(16), /* sub sp, sp, #16 */
431 UWOP_SAVE_REGS((1<<r7)), /* push {r7} */
432 UWOP_SET_FP(7), /* mov r7, sp */
433 UWOP_END_NOP32, /* b tailcall */
436 static const struct results_arm results_2[] =
438 /* offset fp handler pc frame offset registers */
439 { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
440 { 0x02, 0x00, 0, ORIG_LR, 0x55555555, FALSE, { {-1,-1} }},
441 { 0x04, 0x00, 0, ORIG_LR, 0x000, FALSE, { {r7,0x00}, {-1,-1} }},
442 { 0x06, 0x00, 0, ORIG_LR, 0x010, FALSE, { {r7,0x10}, {-1,-1} }},
443 { 0x08, 0x00, 0, ORIG_LR, 0x010, FALSE, { {r7,0x10}, {-1,-1} }},
444 { 0x0a, 0x00, 0, ORIG_LR, 0x000, FALSE, { {r7,0x00}, {-1,-1} }},
445 { 0x0c, 0x00, 0, ORIG_LR, 0x55555555, FALSE, { {-1,-1} }},
446 { 0x0e, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
449 static const BYTE function_3[] =
451 0xaf, 0x3f, 0x00, 0x80, /* 00: nop.w */
452 0x00, 0xbf, /* 04: nop */
453 0x00, 0xbf, /* 06: nop */
454 0x04, 0xb0, /* 08: add sp, sp, #16 */
455 0xbd, 0xe8, 0xf0, 0x8f, /* 0a: pop.w {r4-r11,pc} */
458 /* Testing F=1, no prologue */
459 static const DWORD unwind_info_3_header =
460 (sizeof(function_3)/2) | /* function length */
461 (0 << 20) | /* X */
462 (1 << 21) | /* E */
463 (1 << 22) | /* F */
464 (0 << 23) | /* epilog */
465 (1 << 28); /* codes, (sizeof(unwind_info_3)-headers+3)/4 */
467 static const BYTE unwind_info_3[] =
469 DW(unwind_info_3_header),
471 UWOP_ALLOC_SMALL(16), /* sub sp, sp, #16 */
472 UWOP_SAVE_RANGE_4_11_LR(11, 1), /* pop.w {r4-r11,pc} */
473 UWOP_END,
476 static const struct results_arm results_3[] =
478 /* offset fp handler pc frame offset registers */
479 { 0x00, 0x00, 0, 0x30, 0x034, TRUE, { {r4,0x10}, {r5,0x14}, {r6,0x18}, {r7,0x1c}, {r8,0x20}, {r9,0x24}, {r10,0x28}, {r11,0x2c}, {lr,0x30}, {-1,-1} }},
480 { 0x04, 0x00, 0, 0x30, 0x034, TRUE, { {r4,0x10}, {r5,0x14}, {r6,0x18}, {r7,0x1c}, {r8,0x20}, {r9,0x24}, {r10,0x28}, {r11,0x2c}, {lr,0x30}, {-1,-1} }},
481 { 0x06, 0x00, 0, 0x30, 0x034, TRUE, { {r4,0x10}, {r5,0x14}, {r6,0x18}, {r7,0x1c}, {r8,0x20}, {r9,0x24}, {r10,0x28}, {r11,0x2c}, {lr,0x30}, {-1,-1} }},
482 { 0x08, 0x00, 0, 0x30, 0x034, TRUE, { {r4,0x10}, {r5,0x14}, {r6,0x18}, {r7,0x1c}, {r8,0x20}, {r9,0x24}, {r10,0x28}, {r11,0x2c}, {lr,0x30}, {-1,-1} }},
483 { 0x0a, 0x00, 0, 0x20, 0x024, TRUE, { {r4,0x00}, {r5,0x04}, {r6,0x08}, {r7,0x0c}, {r8,0x10}, {r9,0x14}, {r10,0x18}, {r11,0x1c}, {lr,0x20}, {-1,-1} }},
486 static const BYTE function_4[] =
488 0x2d, 0xe9, 0x00, 0x55, /* 00: push.w {r8, r10, r12, lr} */
489 0x50, 0xb4, /* 04: push {r4, r6} */
490 0x00, 0xbf, /* 06: nop */
491 0x50, 0xbc, /* 08: pop {r4, r6} */
492 0xbd, 0xe8, 0x00, 0x95, /* 0a: pop.w {r8, r10, r12, pc} */
495 static const DWORD unwind_info_4_header =
496 (sizeof(function_4)/2) | /* function length */
497 (0 << 20) | /* X */
498 (1 << 21) | /* E */
499 (0 << 22) | /* F */
500 (0 << 23) | /* epilog */
501 (2 << 28); /* codes, (sizeof(unwind_info_4)-headers+3)/4 */
503 static const BYTE unwind_info_4[] =
505 DW(unwind_info_4_header),
507 UWOP_SAVE_REGS((1<<r4)|(1<<r6)), /* push {r4, r6} */
508 UWOP_SAVE_REGSW((1<<r8)|(1<<r10)|(1<<r12)|(1<<lr)), /* push.w {r8, r10, r12, lr} */
509 UWOP_END,
512 static const struct results_arm results_4[] =
514 /* offset fp handler pc frame offset registers */
515 { 0x00, 0x10, 0, ORIG_LR, 0x00000, TRUE, { {-1,-1} }},
516 { 0x04, 0x10, 0, 0x0c, 0x00010, TRUE, { {r8,0x00}, {r10,0x04}, {r12,0x08}, {lr,0x0c}, {-1,-1} }},
517 { 0x06, 0x10, 0, 0x14, 0x00018, TRUE, { {r8,0x08}, {r10,0x0c}, {r12,0x10}, {lr,0x14}, {r4,0x00}, {r6,0x04}, {-1,-1} }},
518 { 0x08, 0x10, 0, 0x14, 0x00018, TRUE, { {r8,0x08}, {r10,0x0c}, {r12,0x10}, {lr,0x14}, {r4,0x00}, {r6,0x04}, {-1,-1} }},
519 { 0x0a, 0x10, 0, 0x0c, 0x00010, TRUE, { {r8,0x00}, {r10,0x04}, {r12,0x08}, {lr,0x0c}, {-1,-1} }},
522 static const BYTE function_5[] =
524 0x50, 0xb5, /* 00: push {r4, r6, lr} */
525 0xad, 0xf2, 0x08, 0x0d, /* 02: subw sp, sp, #8 */
526 0x84, 0xb0, /* 06: sub sp, sp, #16 */
527 0x88, 0xb0, /* 08: sub sp, sp, #32 */
528 0xad, 0xf2, 0x40, 0x0d, /* 0a: subw sp, sp, #64 */
529 0xad, 0xf2, 0x80, 0x0d, /* 0e: subw sp, sp, #128 */
530 0x00, 0xbf, /* 12: nop */
531 0x50, 0xbd, /* 14: pop {r4, r6, pc} */
534 static const DWORD unwind_info_5_header =
535 (sizeof(function_5)/2) | /* function length */
536 (0 << 20) | /* X */
537 (1 << 21) | /* E */
538 (0 << 22) | /* F */
539 (16 << 23) | /* epilog */
540 (5 << 28); /* codes, (sizeof(unwind_info_4)-headers+3)/4 */
542 static const BYTE unwind_info_5[] =
544 DW(unwind_info_5_header),
546 UWOP_ALLOC_HUGEW(128), /* subw sp, sp, #128 */
547 UWOP_ALLOC_LARGEW(64), /* subw sp, sp, #64 */
548 UWOP_ALLOC_HUGE(32), /* sub sp, sp, #32 */
549 UWOP_ALLOC_LARGE(16), /* sub sp, sp, #16 */
550 UWOP_ALLOC_MEDIUMW(8), /* subw sp, sp, #8 */
551 UWOP_SAVE_REGS((1<<r4)|(1<<r6)|(1<<lr)), /* push {r4, r6, lr} */
552 UWOP_END,
555 static const struct results_arm results_5[] =
557 /* offset fp handler pc frame offset registers */
558 { 0x00, 0x00, 0, ORIG_LR, 0x00000, TRUE, { {-1,-1} }},
559 { 0x02, 0x00, 0, 0x008, 0x0000c, TRUE, { {r4,0x00}, {r6,0x04}, {lr,0x08}, {-1,-1} }},
560 { 0x06, 0x00, 0, 0x010, 0x00014, TRUE, { {r4,0x08}, {r6,0x0c}, {lr,0x10}, {-1,-1} }},
561 { 0x08, 0x00, 0, 0x020, 0x00024, TRUE, { {r4,0x18}, {r6,0x1c}, {lr,0x20}, {-1,-1} }},
562 { 0x0a, 0x00, 0, 0x040, 0x00044, TRUE, { {r4,0x38}, {r6,0x3c}, {lr,0x40}, {-1,-1} }},
563 { 0x0e, 0x00, 0, 0x080, 0x00084, TRUE, { {r4,0x78}, {r6,0x7c}, {lr,0x80}, {-1,-1} }},
564 { 0x12, 0x00, 0, 0x100, 0x00104, TRUE, { {r4,0xf8}, {r6,0xfc}, {lr,0x100}, {-1,-1} }},
565 { 0x14, 0x00, 0, 0x008, 0x0000c, TRUE, { {r4,0x00}, {r6,0x04}, {lr,0x08}, {-1,-1} }},
568 static const BYTE function_6[] =
570 0x00, 0xbf, /* 00: nop */
571 0x00, 0xbf, /* 02: nop */
572 0x00, 0xbf, /* 04: nop */
573 0x70, 0x47, /* 06: bx lr */
576 static const DWORD unwind_info_6_packed =
577 (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
578 (sizeof(function_6)/2 << 2) | /* FunctionLength */
579 (1 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
580 (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
581 (7 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
582 (1 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
583 (0 << 20) | /* L, push LR */
584 (0 << 21) | /* C - hook up r11 */
585 (0 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
587 static const BYTE unwind_info_6[] = { DW(unwind_info_6_packed) };
589 static const struct results_arm results_6[] =
591 /* offset fp handler pc frame offset registers */
592 { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
593 { 0x02, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
594 { 0x04, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
595 { 0x06, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
598 static const BYTE function_7[] =
600 0x10, 0xb4, /* 00: push {r4} */
601 0x00, 0xbf, /* 02: nop */
602 0x10, 0xbc, /* 04: pop {r4} */
603 0x70, 0x47, /* 06: bx lr */
606 static const DWORD unwind_info_7_packed =
607 (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
608 (sizeof(function_7)/2 << 2) | /* FunctionLength */
609 (1 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
610 (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
611 (0 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
612 (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
613 (0 << 20) | /* L, push LR */
614 (0 << 21) | /* C - hook up r11 */
615 (0 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
617 static const BYTE unwind_info_7[] = { DW(unwind_info_7_packed) };
619 static const struct results_arm results_7[] =
621 /* offset fp handler pc frame offset registers */
622 { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
623 { 0x02, 0x00, 0, ORIG_LR, 0x004, TRUE, { {r4,0x00}, {-1,-1} }},
624 { 0x04, 0x00, 0, ORIG_LR, 0x004, TRUE, { {r4,0x00}, {-1,-1} }},
625 { 0x06, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
628 static const BYTE function_8[] =
630 0x10, 0xb5, /* 00: push {r4, lr} */
631 0x00, 0xbf, /* 02: nop */
632 0xbd, 0xe8, 0x10, 0x40, /* 04: pop {r4, lr} */
633 0x70, 0x47, /* 08: bx lr */
636 static const DWORD unwind_info_8_packed =
637 (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
638 (sizeof(function_8)/2 << 2) | /* FunctionLength */
639 (1 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
640 (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
641 (0 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
642 (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
643 (1 << 20) | /* L, push LR */
644 (0 << 21) | /* C - hook up r11 */
645 (0 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
647 static const BYTE unwind_info_8[] = { DW(unwind_info_8_packed) };
649 static const struct results_arm results_8[] =
651 /* offset fp handler pc frame offset registers */
652 { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
653 { 0x02, 0x00, 0, 0x004, 0x008, TRUE, { {r4,0x00}, {lr,0x04}, {-1,-1} }},
654 { 0x04, 0x00, 0, 0x004, 0x008, TRUE, { {r4,0x00}, {lr,0x04}, {-1,-1} }},
655 { 0x06, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, /* Note, there's no instruction at 0x06, but the pop is surprisingly a 4 byte instruction. */
656 { 0x08, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
659 static const BYTE function_9[] =
661 0x00, 0xb5, /* 00: push {lr} */
662 0x2d, 0xed, 0x02, 0x8b, /* 02: vpush {d8} */
663 0x88, 0xb0, /* 06: sub sp, sp, #32 */
664 0x00, 0xbf, /* 08: nop */
665 0x08, 0xb0, /* 0a: add sp, sp, #32 */
666 0xbd, 0xec, 0x02, 0x8b, /* 0c: vpop {d8} */
667 0x5d, 0xf8, 0x04, 0xeb, /* 10: ldr lr, [sp], #4 */
668 0x00, 0xf0, 0x00, 0xb8, /* 14: b tailcall */
671 static const DWORD unwind_info_9_packed =
672 (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
673 (sizeof(function_9)/2 << 2) | /* FunctionLength */
674 (2 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
675 (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
676 (0 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
677 (1 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
678 (1 << 20) | /* L, push LR */
679 (0 << 21) | /* C - hook up r11 */
680 (8 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
682 static const BYTE unwind_info_9[] = { DW(unwind_info_9_packed) };
684 static const struct results_arm results_9[] =
686 /* offset fp handler pc frame offset registers */
687 { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
688 { 0x02, 0x00, 0, 0x00, 0x004, TRUE, { {lr,0x00}, {-1,-1} }},
689 { 0x06, 0x00, 0, 0x08, 0x00c, TRUE, { {lr,0x08}, {d8,0x400000000}, {-1,-1} }},
690 { 0x08, 0x00, 0, 0x28, 0x02c, TRUE, { {lr,0x28}, {d8,0x2400000020}, {-1,-1} }},
691 { 0x0a, 0x00, 0, 0x28, 0x02c, TRUE, { {lr,0x28}, {d8,0x2400000020}, {-1,-1} }},
692 #if 0
693 /* L=1, R=1, Ret>0 seems to get incorrect handling of the epilogue */
694 { 0x0c, 0x00, 0, ORIG_LR, 0x008, TRUE, { {d8,0x400000000}, {-1,-1} }},
695 { 0x10, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
696 #endif
697 { 0x14, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
700 static const BYTE function_10[] =
702 0x2d, 0xe9, 0x00, 0x48, /* 00: push.w {r11, lr} */
703 0xeb, 0x46, /* 04: mov r11, sp */
704 0x2d, 0xed, 0x04, 0x8b, /* 06: vpush {d8-d9} */
705 0x84, 0xb0, /* 0a: sub sp, sp, #16 */
706 0x00, 0xbf, /* 0c: nop */
707 0x04, 0xb0, /* 0e: add sp, sp, #16 */
708 0xbd, 0xec, 0x04, 0x8b, /* 10: vpop {d8-d9} */
709 0xbd, 0xe8, 0x00, 0x48, /* 14: pop.w {r11, lr} */
710 0x70, 0x47, /* 18: bx lr */
713 static const DWORD unwind_info_10_packed =
714 (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
715 (sizeof(function_10)/2 << 2) | /* FunctionLength */
716 (1 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
717 (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
718 (1 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
719 (1 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
720 (1 << 20) | /* L, push LR */
721 (1 << 21) | /* C - hook up r11 */
722 (4 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
724 static const BYTE unwind_info_10[] = { DW(unwind_info_10_packed) };
726 static const struct results_arm results_10[] =
728 /* offset fp handler pc frame offset registers */
729 { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
730 { 0x04, 0x00, 0, 0x04, 0x008, TRUE, { {r11,0x00}, {lr,0x04}, {-1,-1} }},
731 { 0x06, 0x00, 0, 0x04, 0x008, TRUE, { {r11,0x00}, {lr,0x04}, {-1,-1} }},
732 { 0x0a, 0x00, 0, 0x14, 0x018, TRUE, { {r11,0x10}, {lr,0x14}, {d8,0x400000000}, {d9,0xc00000008}, {-1,-1} }},
733 { 0x0c, 0x00, 0, 0x24, 0x028, TRUE, { {r11,0x20}, {lr,0x24}, {d8,0x1400000010}, {d9,0x1c00000018}, {-1,-1} }},
734 { 0x0e, 0x00, 0, 0x24, 0x028, TRUE, { {r11,0x20}, {lr,0x24}, {d8,0x1400000010}, {d9,0x1c00000018}, {-1,-1} }},
735 #if 0
736 /* L=1, R=1, Ret>0 seems to get incorrect handling of the epilogue */
737 { 0x10, 0x00, 0, 0x14, 0x018, TRUE, { {r11,0x10}, {lr,0x14}, {d8,0x400000000}, {d9,0xc00000008}, {-1,-1} }},
738 { 0x14, 0x00, 0, 0x04, 0x008, TRUE, { {r11,0x00}, {lr,0x04}, {-1,-1} }},
739 #endif
740 { 0x18, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
743 static const BYTE function_11[] =
745 0x2d, 0xe9, 0x00, 0x48, /* 00: push.w {r11, lr} */
746 0xeb, 0x46, /* 04: mov r11, sp */
747 0x2d, 0xed, 0x04, 0x8b, /* 06: vpush {d8-d9} */
748 0x84, 0xb0, /* 0a: sub sp, sp, #16 */
749 0x00, 0xbf, /* 0c: nop */
750 0x04, 0xb0, /* 0e: add sp, sp, #16 */
751 0xbd, 0xec, 0x04, 0x8b, /* 10: vpop {d8-d9} */
752 0xbd, 0xe8, 0x00, 0x88, /* 14: pop.w {r11, pc} */
755 static const DWORD unwind_info_11_packed =
756 (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
757 (sizeof(function_11)/2 << 2) | /* FunctionLength */
758 (0 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
759 (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
760 (1 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
761 (1 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
762 (1 << 20) | /* L, push LR */
763 (1 << 21) | /* C - hook up r11 */
764 (4 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
766 static const BYTE unwind_info_11[] = { DW(unwind_info_11_packed) };
768 static const struct results_arm results_11[] =
770 /* offset fp handler pc frame offset registers */
771 { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
772 { 0x04, 0x00, 0, 0x04, 0x008, TRUE, { {r11,0x00}, {lr,0x04}, {-1,-1} }},
773 { 0x06, 0x00, 0, 0x04, 0x008, TRUE, { {r11,0x00}, {lr,0x04}, {-1,-1} }},
774 { 0x0a, 0x00, 0, 0x14, 0x018, TRUE, { {r11,0x10}, {lr,0x14}, {d8,0x400000000}, {d9,0xc00000008}, {-1,-1} }},
775 { 0x0c, 0x00, 0, 0x24, 0x028, TRUE, { {r11,0x20}, {lr,0x24}, {d8,0x1400000010}, {d9,0x1c00000018}, {-1,-1} }},
776 { 0x0e, 0x00, 0, 0x24, 0x028, TRUE, { {r11,0x20}, {lr,0x24}, {d8,0x1400000010}, {d9,0x1c00000018}, {-1,-1} }},
777 { 0x10, 0x00, 0, 0x14, 0x018, TRUE, { {r11,0x10}, {lr,0x14}, {d8,0x400000000}, {d9,0xc00000008}, {-1,-1} }},
778 { 0x14, 0x00, 0, 0x04, 0x008, TRUE, { {r11,0x00}, {lr,0x04}, {-1,-1} }},
781 static const BYTE function_12[] =
783 0x2d, 0xed, 0x0e, 0x8b, /* 00: vpush {d8-d14} */
784 0x84, 0xb0, /* 04: sub sp, sp, #16 */
785 0x00, 0xbf, /* 06: nop */
786 0x04, 0xb0, /* 08: add sp, sp, #16 */
787 0xbd, 0xec, 0x0e, 0x8b, /* 0a: vpop {d8-d14} */
788 0x00, 0xf0, 0x00, 0xb8, /* 0e: b tailcall */
791 static const DWORD unwind_info_12_packed =
792 (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
793 (sizeof(function_12)/2 << 2) | /* FunctionLength */
794 (2 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
795 (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
796 (6 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
797 (1 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
798 (0 << 20) | /* L, push LR */
799 (0 << 21) | /* C - hook up r11 */
800 (4 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
802 static const BYTE unwind_info_12[] = { DW(unwind_info_12_packed) };
804 static const struct results_arm results_12[] =
806 /* offset fp handler pc frame offset registers */
807 { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
808 { 0x04, 0x00, 0, ORIG_LR, 0x038, TRUE, { {d8,0x400000000}, {d9,0xc00000008}, {d10,0x1400000010}, {d11,0x1c00000018}, {d12,0x2400000020}, {d13,0x2c00000028}, {d14,0x3400000030}, {-1,-1} }},
809 { 0x06, 0x00, 0, ORIG_LR, 0x048, TRUE, { {d8,0x1400000010}, {d9,0x1c00000018}, {d10,0x2400000020}, {d11,0x2c00000028}, {d12,0x3400000030}, {d13,0x3c00000038}, {d14,0x4400000040}, {-1,-1} }},
810 { 0x08, 0x00, 0, ORIG_LR, 0x048, TRUE, { {d8,0x1400000010}, {d9,0x1c00000018}, {d10,0x2400000020}, {d11,0x2c00000028}, {d12,0x3400000030}, {d13,0x3c00000038}, {d14,0x4400000040}, {-1,-1} }},
811 { 0x0a, 0x00, 0, ORIG_LR, 0x038, TRUE, { {d8,0x400000000}, {d9,0xc00000008}, {d10,0x1400000010}, {d11,0x1c00000018}, {d12,0x2400000020}, {d13,0x2c00000028}, {d14,0x3400000030}, {-1,-1} }},
812 { 0x0e, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
815 static const BYTE function_13[] =
817 0x2d, 0xe9, 0xf0, 0x4f, /* 00: push.w {r4-r11, lr} */
818 0x0d, 0xf1, 0x1c, 0x0b, /* 04: add.w r11, sp, #28 */
819 0x85, 0xb0, /* 08: sub sp, sp, #20 */
820 0x00, 0xbf, /* 0a: nop */
821 0x05, 0xb0, /* 0c: add sp, sp, #20 */
822 0x2d, 0xe8, 0xf0, 0x8f, /* 0e: pop.w {r4-r11, lr} */
825 static const DWORD unwind_info_13_packed =
826 (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
827 (sizeof(function_13)/2 << 2) | /* FunctionLength */
828 (0 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
829 (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
830 (6 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
831 (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
832 (1 << 20) | /* L, push LR */
833 (1 << 21) | /* C - hook up r11 */
834 (5 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
836 static const BYTE unwind_info_13[] = { DW(unwind_info_13_packed) };
838 static const struct results_arm results_13[] =
840 /* offset fp handler pc frame offset registers */
841 { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
842 { 0x04, 0x10, 0, 0x20, 0x024, TRUE, { {r4,0x00}, {r5,0x04}, {r6,0x08}, {r7,0x0c}, {r8,0x10}, {r9,0x14}, {r10,0x18}, {r11,0x1c}, {lr,0x20}, {-1,-1} }},
843 { 0x08, 0x10, 0, 0x20, 0x024, TRUE, { {r4,0x00}, {r5,0x04}, {r6,0x08}, {r7,0x0c}, {r8,0x10}, {r9,0x14}, {r10,0x18}, {r11,0x1c}, {lr,0x20}, {-1,-1} }},
844 { 0x0a, 0x10, 0, 0x34, 0x038, TRUE, { {r4,0x14}, {r5,0x18}, {r6,0x1c}, {r7,0x20}, {r8,0x24}, {r9,0x28}, {r10,0x2c}, {r11,0x30}, {lr,0x34}, {-1,-1} }},
845 { 0x0c, 0x10, 0, 0x34, 0x038, TRUE, { {r4,0x14}, {r5,0x18}, {r6,0x1c}, {r7,0x20}, {r8,0x24}, {r9,0x28}, {r10,0x2c}, {r11,0x30}, {lr,0x34}, {-1,-1} }},
846 { 0x0e, 0x10, 0, 0x20, 0x024, TRUE, { {r4,0x00}, {r5,0x04}, {r6,0x08}, {r7,0x0c}, {r8,0x10}, {r9,0x14}, {r10,0x18}, {r11,0x1c}, {lr,0x20}, {-1,-1} }},
849 static const BYTE function_14[] =
851 0x2d, 0xe9, 0xf0, 0x4f, /* 00: push.w {r4-r11, lr} */
852 0x85, 0xb0, /* 04: sub sp, sp, #20 */
853 0x00, 0xbf, /* 06: nop */
854 0x05, 0xb0, /* 08: add sp, sp, #20 */
855 0x2d, 0xe8, 0xf0, 0x8f, /* 0a: pop.w {r4-r11, lr} */
858 static const DWORD unwind_info_14_packed =
859 (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
860 (sizeof(function_14)/2 << 2) | /* FunctionLength */
861 (0 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
862 (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
863 (7 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
864 (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
865 (1 << 20) | /* L, push LR */
866 (0 << 21) | /* C - hook up r11 */
867 (5 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
869 static const BYTE unwind_info_14[] = { DW(unwind_info_14_packed) };
871 static const struct results_arm results_14[] =
873 /* offset fp handler pc frame offset registers */
874 { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
875 { 0x04, 0x10, 0, 0x20, 0x024, TRUE, { {r4,0x00}, {r5,0x04}, {r6,0x08}, {r7,0x0c}, {r8,0x10}, {r9,0x14}, {r10,0x18}, {r11,0x1c}, {lr,0x20}, {-1,-1} }},
876 { 0x06, 0x10, 0, 0x34, 0x038, TRUE, { {r4,0x14}, {r5,0x18}, {r6,0x1c}, {r7,0x20}, {r8,0x24}, {r9,0x28}, {r10,0x2c}, {r11,0x30}, {lr,0x34}, {-1,-1} }},
877 { 0x08, 0x10, 0, 0x34, 0x038, TRUE, { {r4,0x14}, {r5,0x18}, {r6,0x1c}, {r7,0x20}, {r8,0x24}, {r9,0x28}, {r10,0x2c}, {r11,0x30}, {lr,0x34}, {-1,-1} }},
878 { 0x0a, 0x10, 0, 0x20, 0x024, TRUE, { {r4,0x00}, {r5,0x04}, {r6,0x08}, {r7,0x0c}, {r8,0x10}, {r9,0x14}, {r10,0x18}, {r11,0x1c}, {lr,0x20}, {-1,-1} }},
881 static const BYTE function_15[] =
883 0x0f, 0xb4, /* 00: push {r0-r3} */
884 0x10, 0xb5, /* 02: push {r4,lr} */
885 0xad, 0xf5, 0x00, 0x7d, /* 04: sub sp, sp, #512 */
886 0x00, 0xbf, /* 08: nop */
887 0x0d, 0xf5, 0x00, 0x7d, /* 0a: add sp, sp, #512 */
888 0x10, 0xb5, /* 0e: pop {r4} */
889 0x5d, 0xf8, 0x14, 0xfb, /* 10: ldr pc, [sp], #20 */
892 static const DWORD unwind_info_15_packed =
893 (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
894 (sizeof(function_15)/2 << 2) | /* FunctionLength */
895 (0 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
896 (1 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
897 (0 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
898 (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
899 (1 << 20) | /* L, push LR */
900 (0 << 21) | /* C - hook up r11 */
901 (128 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
903 static const BYTE unwind_info_15[] = { DW(unwind_info_15_packed) };
905 static const struct results_arm results_15[] =
907 /* offset fp handler pc frame offset registers */
908 { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
909 { 0x02, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }},
910 { 0x04, 0x10, 0, 0x04, 0x018, TRUE, { {r4,0x00}, {lr,0x04}, {-1,-1} }},
911 { 0x08, 0x10, 0, 0x204, 0x218, TRUE, { {r4,0x200}, {lr,0x204}, {-1,-1} }},
912 { 0x0a, 0x10, 0, 0x204, 0x218, TRUE, { {r4,0x200}, {lr,0x204}, {-1,-1} }},
913 { 0x0e, 0x10, 0, 0x04, 0x018, TRUE, { {r4,0x00}, {lr,0x04}, {-1,-1} }},
914 { 0x10, 0x10, 0, 0x00, 0x014, TRUE, { {lr,0x00}, {-1,-1} }},
917 static const BYTE function_16[] =
919 0x0f, 0xb4, /* 00: push {r0-r3} */
920 0x2d, 0xe9, 0x00, 0x48, /* 02: push.w {r11,lr} */
921 0xeb, 0x46, /* 06: mov r11, sp */
922 0x00, 0xbf, /* 08: nop */
923 0xbd, 0xe8, 0x10, 0x40, /* 0a: pop.w {r11,lr} */
924 0x04, 0xb0, /* 0e: add sp, sp, #16 */
925 0x00, 0xf0, 0x00, 0xb8, /* 10: b tailcall */
928 static const DWORD unwind_info_16_packed =
929 (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
930 (sizeof(function_16)/2 << 2) | /* FunctionLength */
931 (2 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
932 (1 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
933 (7 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
934 (1 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
935 (1 << 20) | /* L, push LR */
936 (1 << 21) | /* C - hook up r11 */
937 (0 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
939 static const BYTE unwind_info_16[] = { DW(unwind_info_16_packed) };
941 static const struct results_arm results_16[] =
943 /* offset fp handler pc frame offset registers */
944 { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
945 { 0x02, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }},
946 { 0x06, 0x10, 0, 0x04, 0x018, TRUE, { {r11,0x00}, {lr,0x04}, {-1,-1} }},
947 { 0x08, 0x10, 0, 0x04, 0x018, TRUE, { {r11,0x00}, {lr,0x04}, {-1,-1} }},
948 { 0x0a, 0x10, 0, 0x04, 0x018, TRUE, { {r11,0x00}, {lr,0x04}, {-1,-1} }},
949 { 0x0e, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }},
950 { 0x10, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
953 static const BYTE function_17[] =
955 0x0f, 0xb4, /* 00: push {r0-r3} */
956 0x10, 0xb4, /* 02: push {r4} */
957 0xad, 0xf5, 0x00, 0x7d, /* 04: sub sp, sp, #512 */
958 0x00, 0xbf, /* 08: nop */
959 0x0d, 0xf5, 0x00, 0x7d, /* 0a: add sp, sp, #512 */
960 0x10, 0xbc, /* 0e: pop {r4} */
961 0x04, 0xb0, /* 10: add sp, sp, #16 */
962 0x70, 0x47, /* 12: bx lr */
965 static const DWORD unwind_info_17_packed =
966 (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
967 (sizeof(function_17)/2 << 2) | /* FunctionLength */
968 (1 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
969 (1 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
970 (0 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
971 (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
972 (0 << 20) | /* L, push LR */
973 (0 << 21) | /* C - hook up r11 */
974 (128 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
976 static const BYTE unwind_info_17[] = { DW(unwind_info_17_packed) };
978 static const struct results_arm results_17[] =
980 /* offset fp handler pc frame offset registers */
981 { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
982 { 0x02, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }},
983 { 0x04, 0x10, 0, ORIG_LR, 0x014, TRUE, { {r4,0x00}, {-1,-1} }},
984 { 0x08, 0x10, 0, ORIG_LR, 0x214, TRUE, { {r4,0x200}, {-1,-1} }},
985 { 0x0a, 0x10, 0, ORIG_LR, 0x214, TRUE, { {r4,0x200}, {-1,-1} }},
986 { 0x0e, 0x10, 0, ORIG_LR, 0x014, TRUE, { {r4,0x00}, {-1,-1} }},
987 { 0x10, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }},
988 { 0x12, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
991 static const BYTE function_18[] =
993 0x08, 0xb5, /* 00: push {r3,lr} */
994 0x00, 0xbf, /* 02: nop */
995 0x08, 0xbd, /* 04: pop {r3,pc} */
998 static const DWORD unwind_info_18_packed =
999 (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
1000 (sizeof(function_18)/2 << 2) | /* FunctionLength */
1001 (0 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
1002 (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
1003 (7 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
1004 (1 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
1005 (1 << 20) | /* L, push LR */
1006 (0 << 21) | /* C - hook up r11 */
1007 (0x3fcu << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
1009 static const BYTE unwind_info_18[] = { DW(unwind_info_18_packed) };
1011 static const struct results_arm results_18[] =
1013 /* offset fp handler pc frame offset registers */
1014 { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
1015 { 0x02, 0x10, 0, 0x04, 0x008, TRUE, { {lr,0x04}, {-1,-1} }},
1016 { 0x04, 0x10, 0, 0x04, 0x008, TRUE, { {lr,0x04}, {-1,-1} }},
1019 static const BYTE function_19[] =
1021 0x0f, 0xb4, /* 00: push {r0-r3} */
1022 0x14, 0xb4, /* 02: push {r0-r4} */
1023 0x00, 0xbf, /* 04: nop */
1024 0x1f, 0xbc, /* 06: pop {r0-r4} */
1025 0x04, 0xb0, /* 08: add sp, sp, #16 */
1026 0x70, 0x47, /* 0a: bx lr */
1029 static const DWORD unwind_info_19_packed =
1030 (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
1031 (sizeof(function_19)/2 << 2) | /* FunctionLength */
1032 (1 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
1033 (1 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
1034 (0 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
1035 (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
1036 (0 << 20) | /* L, push LR */
1037 (0 << 21) | /* C - hook up r11 */
1038 (0x3ffu << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
1040 static const BYTE unwind_info_19[] = { DW(unwind_info_19_packed) };
1042 static const struct results_arm results_19[] =
1044 /* offset fp handler pc frame offset registers */
1045 { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
1046 { 0x02, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }},
1047 { 0x04, 0x10, 0, ORIG_LR, 0x024, TRUE, { {r4,0x10}, {-1,-1} }},
1048 { 0x06, 0x10, 0, ORIG_LR, 0x024, TRUE, { {r4,0x10}, {-1,-1} }},
1049 { 0x08, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }},
1050 { 0x0a, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
1053 static const BYTE function_20[] =
1055 0x0f, 0xb4, /* 00: push {r0-r3} */
1056 0x14, 0xb4, /* 02: push {r0-r4} */
1057 0x00, 0xbf, /* 04: nop */
1058 0x04, 0xb0, /* 06: add sp, sp, #16 */
1059 0x10, 0xbc, /* 08: pop {r4} */
1060 0x04, 0xb0, /* 0a: add sp, sp, #16 */
1061 0x70, 0x47, /* 0c: bx lr */
1064 static const DWORD unwind_info_20_packed =
1065 (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
1066 (sizeof(function_20)/2 << 2) | /* FunctionLength */
1067 (1 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
1068 (1 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
1069 (0 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
1070 (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
1071 (0 << 20) | /* L, push LR */
1072 (0 << 21) | /* C - hook up r11 */
1073 (0x3f7u << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
1075 static const BYTE unwind_info_20[] = { DW(unwind_info_20_packed) };
1077 static const struct results_arm results_20[] =
1079 /* offset fp handler pc frame offset registers */
1080 { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
1081 { 0x02, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }},
1082 { 0x04, 0x10, 0, ORIG_LR, 0x024, TRUE, { {r4,0x10}, {-1,-1} }},
1083 { 0x06, 0x10, 0, ORIG_LR, 0x024, TRUE, { {r4,0x10}, {-1,-1} }},
1084 { 0x08, 0x10, 0, ORIG_LR, 0x014, TRUE, { {r4,0x00}, {-1,-1} }},
1085 { 0x0a, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }},
1086 { 0x0c, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
1089 static const BYTE function_21[] =
1091 0x0f, 0xb4, /* 00: push {r0-r3} */
1092 0x10, 0xb4, /* 02: push {r4} */
1093 0x84, 0xb0, /* 04: sub sp, sp, #16 */
1094 0x00, 0xbf, /* 06: nop */
1095 0x1f, 0xbc, /* 08: pop {r0-r4} */
1096 0x04, 0xb0, /* 0a: add sp, sp, #16 */
1097 0x70, 0x47, /* 0c: bx lr */
1100 static const DWORD unwind_info_21_packed =
1101 (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
1102 (sizeof(function_21)/2 << 2) | /* FunctionLength */
1103 (1 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
1104 (1 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
1105 (0 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
1106 (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
1107 (0 << 20) | /* L, push LR */
1108 (0 << 21) | /* C - hook up r11 */
1109 (0x3fbu << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
1111 static const BYTE unwind_info_21[] = { DW(unwind_info_21_packed) };
1113 static const struct results_arm results_21[] =
1115 /* offset fp handler pc frame offset registers */
1116 { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
1117 { 0x02, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }},
1118 { 0x04, 0x10, 0, ORIG_LR, 0x014, TRUE, { {r4,0x00}, {-1,-1} }},
1119 { 0x06, 0x10, 0, ORIG_LR, 0x024, TRUE, { {r4,0x10}, {-1,-1} }},
1120 { 0x08, 0x10, 0, ORIG_LR, 0x024, TRUE, { {r4,0x10}, {-1,-1} }},
1121 { 0x0a, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }},
1122 { 0x0c, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
1125 static const BYTE function_22[] =
1127 0x00, 0xbf, /* 00: nop */
1128 0x00, 0xbf, /* 02: nop */
1129 0x0d, 0xf5, 0x00, 0x7d, /* 04: add sp, sp, #512 */
1130 0x10, 0xb5, /* 08: pop {r4} */
1131 0x5d, 0xf8, 0x14, 0xfb, /* 0a: ldr pc, [sp], #20 */
1134 static const DWORD unwind_info_22_packed =
1135 (2 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
1136 (sizeof(function_22)/2 << 2) | /* FunctionLength */
1137 (0 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
1138 (1 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
1139 (0 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
1140 (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
1141 (1 << 20) | /* L, push LR */
1142 (0 << 21) | /* C - hook up r11 */
1143 (128 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
1145 static const BYTE unwind_info_22[] = { DW(unwind_info_22_packed) };
1147 static const struct results_arm results_22[] =
1149 /* offset fp handler pc frame offset registers */
1150 { 0x00, 0x10, 0, 0x204, 0x218, TRUE, { {r4,0x200}, {lr,0x204}, {-1,-1} }},
1151 { 0x02, 0x10, 0, 0x204, 0x218, TRUE, { {r4,0x200}, {lr,0x204}, {-1,-1} }},
1152 { 0x04, 0x10, 0, 0x204, 0x218, TRUE, { {r4,0x200}, {lr,0x204}, {-1,-1} }},
1153 { 0x08, 0x10, 0, 0x04, 0x018, TRUE, { {r4,0x00}, {lr,0x04}, {-1,-1} }},
1154 { 0x0a, 0x10, 0, 0x00, 0x014, TRUE, { {lr,0x00}, {-1,-1} }},
1157 static const BYTE function_23[] =
1159 0x0f, 0xb4, /* 00: push {r0-r3} */
1160 0x10, 0xb5, /* 02: push {r4,lr} */
1161 0xad, 0xf5, 0x00, 0x7d, /* 04: sub sp, sp, #512 */
1162 0x00, 0xbf, /* 08: nop */
1163 0x00, 0xbf, /* 0a: nop */
1166 static const DWORD unwind_info_23_packed =
1167 (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
1168 (sizeof(function_23)/2 << 2) | /* FunctionLength */
1169 (3 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
1170 (1 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
1171 (0 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
1172 (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
1173 (1 << 20) | /* L, push LR */
1174 (0 << 21) | /* C - hook up r11 */
1175 (128 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
1177 static const BYTE unwind_info_23[] = { DW(unwind_info_23_packed) };
1179 static const struct results_arm results_23[] =
1181 /* offset fp handler pc frame offset registers */
1182 { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
1183 { 0x02, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }},
1184 { 0x04, 0x10, 0, 0x04, 0x018, TRUE, { {r4,0x00}, {lr,0x04}, {-1,-1} }},
1185 { 0x08, 0x10, 0, 0x204, 0x218, TRUE, { {r4,0x200}, {lr,0x204}, {-1,-1} }},
1186 { 0x0a, 0x10, 0, 0x204, 0x218, TRUE, { {r4,0x200}, {lr,0x204}, {-1,-1} }},
1189 static const BYTE function_24[] =
1191 0x2d, 0xe9, 0xfc, 0x48, /* 00: push.w {r2-r7,r11,lr} */
1192 0x0d, 0xf1, 0x18, 0x0b, /* 04: add r11, sp, #24 */
1193 0x00, 0xbf, /* 08: nop */
1194 0x02, 0xb0, /* 0a: add sp, sp, #8 */
1195 0xbd, 0xe8, 0x10, 0x48, /* 0c: pop.w {r4-r7,r11,pc} */
1198 static const DWORD unwind_info_24_packed =
1199 (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
1200 (sizeof(function_24)/2 << 2) | /* FunctionLength */
1201 (0 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
1202 (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
1203 (3 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
1204 (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
1205 (1 << 20) | /* L, push LR */
1206 (1 << 21) | /* C - hook up r11 */
1207 (0x3f5u << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
1209 static const BYTE unwind_info_24[] = { DW(unwind_info_24_packed) };
1211 static const struct results_arm results_24[] =
1213 /* offset fp handler pc frame offset registers */
1214 { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
1215 { 0x04, 0x10, 0, 0x1c, 0x020, TRUE, { {r4,0x08}, {r5,0x0c}, {r6,0x10}, {r7,0x14}, {r11,0x18}, {lr,0x1c}, {-1,-1} }},
1216 { 0x08, 0x10, 0, 0x1c, 0x020, TRUE, { {r4,0x08}, {r5,0x0c}, {r6,0x10}, {r7,0x14}, {r11,0x18}, {lr,0x1c}, {-1,-1} }},
1217 { 0x0a, 0x10, 0, 0x1c, 0x020, TRUE, { {r4,0x08}, {r5,0x0c}, {r6,0x10}, {r7,0x14}, {r11,0x18}, {lr,0x1c}, {-1,-1} }},
1218 { 0x0c, 0x10, 0, 0x14, 0x018, TRUE, { {r4,0x00}, {r5,0x04}, {r6,0x08}, {r7,0x0c}, {r11,0x10}, {lr,0x14}, {-1,-1} }},
1221 static const BYTE function_25[] =
1223 0x2d, 0xe9, 0xf0, 0x48, /* 00: push.w {r4-r7,r11,lr} */
1224 0x0d, 0xf1, 0x10, 0x0b, /* 04: add r11, sp, #16 */
1225 0x82, 0xb0, /* 08: sub sp, sp, #8 */
1226 0x00, 0xbf, /* 0a: nop */
1227 0xbd, 0xe8, 0xfc, 0x48, /* 0c: pop.w {r2-r7,r11,pc} */
1230 static const DWORD unwind_info_25_packed =
1231 (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
1232 (sizeof(function_25)/2 << 2) | /* FunctionLength */
1233 (0 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
1234 (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
1235 (3 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
1236 (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
1237 (1 << 20) | /* L, push LR */
1238 (1 << 21) | /* C - hook up r11 */
1239 (0x3f9u << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
1241 static const BYTE unwind_info_25[] = { DW(unwind_info_25_packed) };
1243 static const struct results_arm results_25[] =
1245 /* offset fp handler pc frame offset registers */
1246 { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
1247 { 0x04, 0x10, 0, 0x14, 0x018, TRUE, { {r4,0x00}, {r5,0x04}, {r6,0x08}, {r7,0x0c}, {r11,0x10}, {lr,0x14}, {-1,-1} }},
1248 { 0x08, 0x10, 0, 0x14, 0x018, TRUE, { {r4,0x00}, {r5,0x04}, {r6,0x08}, {r7,0x0c}, {r11,0x10}, {lr,0x14}, {-1,-1} }},
1249 { 0x0a, 0x10, 0, 0x1c, 0x020, TRUE, { {r4,0x08}, {r5,0x0c}, {r6,0x10}, {r7,0x14}, {r11,0x18}, {lr,0x1c}, {-1,-1} }},
1250 { 0x0c, 0x10, 0, 0x1c, 0x020, TRUE, { {r4,0x08}, {r5,0x0c}, {r6,0x10}, {r7,0x14}, {r11,0x18}, {lr,0x1c}, {-1,-1} }},
1253 static const BYTE function_26[] =
1255 0x2d, 0xe9, 0x10, 0x08, /* 00: push.w {r4, r11} */
1256 0x0d, 0xf1, 0x1c, 0x0b, /* 04: add.w r11, sp, #28 */
1257 0x84, 0xb0, /* 08: sub sp, sp, #16 */
1258 0x00, 0xbf, /* 0a: nop */
1259 0x04, 0xb0, /* 0c: add sp, sp, #16 */
1260 0xbd, 0xe8, 0x10, 0x08, /* 0e: pop.w {r4, r11} */
1261 0x70, 0x47, /* 12: bx lr */
1264 /* C=1, L=0 is disallowed by doc */
1265 static const DWORD unwind_info_26_packed =
1266 (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
1267 (sizeof(function_26)/2 << 2) | /* FunctionLength */
1268 (1 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
1269 (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
1270 (0 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
1271 (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
1272 (0 << 20) | /* L, push LR */
1273 (1 << 21) | /* C - hook up r11 */
1274 (4 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
1276 static const BYTE unwind_info_26[] = { DW(unwind_info_26_packed) };
1278 static const struct results_arm results_26[] =
1280 /* offset fp handler pc frame offset registers */
1281 { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
1282 { 0x04, 0x10, 0, ORIG_LR, 0x008, TRUE, { {r4,0x00}, {r11,0x04}, {-1,-1} }},
1283 { 0x08, 0x10, 0, ORIG_LR, 0x008, TRUE, { {r4,0x00}, {r11,0x04}, {-1,-1} }},
1284 { 0x0a, 0x10, 0, ORIG_LR, 0x018, TRUE, { {r4,0x10}, {r11,0x14}, {-1,-1} }},
1285 { 0x0c, 0x10, 0, ORIG_LR, 0x018, TRUE, { {r4,0x10}, {r11,0x14}, {-1,-1} }},
1286 { 0x0e, 0x10, 0, ORIG_LR, 0x008, TRUE, { {r4,0x00}, {r11,0x04}, {-1,-1} }},
1287 { 0x12, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
1290 static const BYTE function_27[] =
1292 0x0e, 0xb4, /* 00: push {r1-r3} */
1293 0x00, 0xbf, /* 02: nop */
1294 0x03, 0xb0, /* 04: add sp, sp, #12 */
1295 0x70, 0x47, /* 06: bx lr */
1298 static const DWORD unwind_info_27_packed =
1299 (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
1300 (sizeof(function_27)/2 << 2) | /* FunctionLength */
1301 (1 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
1302 (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
1303 (7 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
1304 (1 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
1305 (0 << 20) | /* L, push LR */
1306 (0 << 21) | /* C - hook up r11 */
1307 (0x3f6u << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
1309 static const BYTE unwind_info_27[] = { DW(unwind_info_27_packed) };
1311 static const struct results_arm results_27[] =
1313 /* offset fp handler pc frame offset registers */
1314 { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
1315 { 0x02, 0x10, 0, ORIG_LR, 0x00c, TRUE, { {-1,-1} }},
1316 { 0x04, 0x10, 0, ORIG_LR, 0x00c, TRUE, { {-1,-1} }},
1317 { 0x06, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
1320 static const BYTE function_28[] =
1322 0x0e, 0xb4, /* 00: push {r1-r3} */
1323 0x00, 0xbf, /* 02: nop */
1324 0x03, 0xb0, /* 04: add sp, sp, #12 */
1325 0x70, 0x47, /* 06: bx lr */
1328 static const DWORD unwind_info_28_packed =
1329 (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
1330 (sizeof(function_28)/2 << 2) | /* FunctionLength */
1331 (1 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
1332 (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
1333 (7 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
1334 (1 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
1335 (0 << 20) | /* L, push LR */
1336 (0 << 21) | /* C - hook up r11 */
1337 (0x3fau << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
1339 static const BYTE unwind_info_28[] = { DW(unwind_info_28_packed) };
1341 static const struct results_arm results_28[] =
1343 /* offset fp handler pc frame offset registers */
1344 { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
1345 { 0x02, 0x10, 0, ORIG_LR, 0x00c, TRUE, { {-1,-1} }},
1346 { 0x04, 0x10, 0, ORIG_LR, 0x00c, TRUE, { {-1,-1} }},
1347 { 0x06, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
1350 static const BYTE function_29[] =
1352 0x00, 0xbf, /* 00: nop */
1353 0x00, 0xbf, /* 02: nop */
1356 static const DWORD unwind_info_29_header =
1357 (sizeof(function_29)/2) | /* function length */
1358 (0 << 20) | /* X */
1359 (0 << 21) | /* E */
1360 (0 << 22) | /* F */
1361 (0 << 23) | /* epilog */
1362 (1 << 28); /* codes, (sizeof(unwind_info_29)-headers+3)/4 */
1364 static const BYTE unwind_info_29[] =
1366 DW(unwind_info_29_header),
1367 UWOP_MSFT_OP_CONTEXT,
1368 UWOP_END,
1371 static const struct results_arm results_29[] =
1373 /* offset fp handler pc frame offset registers */
1374 { 0x00, 0x10, 0, 0x40, 0x38, FALSE, { {r0,0x04}, {r1,0x08}, {r2,0x0c}, {r3,0x10}, {r4,0x14}, {r5,0x18}, {r6,0x1c}, {r7,0x20}, {r8,0x24}, {r9,0x28}, {r10,0x2c}, {r11,0x30}, {r12,0x34}, {sp,0x38}, {lr,0x3c},
1375 {d0,0x5400000050}, {d1,0x5c00000058}, {d2,0x6400000060}, {d3,0x6c00000068}, {d4,0x7400000070}, {d5,0x7c00000078}, {d6,0x8400000080}, {d7,0x8c00000088},
1376 {d8,0x9400000090}, {d9,0x9c00000098}, {d10,0xa4000000a0}, {d11,0xac000000a8}, {d12,0xb4000000b0}, {d13,0xbc000000b8}, {d14,0xc4000000c0}, {d15,0xcc000000c8},
1377 {d16,0xd4000000d0}, {d17,0xdc000000d8}, {d18,0xe4000000e0}, {d19,0xec000000e8}, {d20,0xf4000000f0}, {d21,0xfc000000f8}, {d22,0x10400000100}, {d23,0x10c00000108},
1378 {d24,0x11400000110}, {d25,0x11c00000118}, {d26,0x12400000120}, {d27,0x12c00000128}, {d28,0x13400000130}, {d29,0x13c00000138}, {d30,0x14400000140}, {d31,0x14c00000148} }},
1381 static const BYTE function_30[] =
1383 0x00, 0xbf, /* 00: nop */
1384 0x00, 0xbf, /* 02: nop */
1385 0x00, 0xbf, /* 04: nop */
1386 0x00, 0xbf, /* 06: nop */
1389 static const DWORD unwind_info_30_header =
1390 (sizeof(function_30)/2) | /* function length */
1391 (0 << 20) | /* X */
1392 (0 << 21) | /* E */
1393 (0 << 22) | /* F */
1394 (0 << 23) | /* epilog */
1395 (2 << 28); /* codes, (sizeof(unwind_info_30)-headers+3)/4 */
1397 static const BYTE unwind_info_30[] =
1399 DW(unwind_info_30_header),
1400 UWOP_ALLOC_SMALL(12), /* sub sp, sp, #12 */
1401 UWOP_SAVE_REGS((1<<lr)), /* push {lr} */
1402 UWOP_MSFT_OP_MACHINE_FRAME,
1403 UWOP_END,
1406 static const struct results_arm results_30[] =
1408 /* offset fp handler pc frame offset registers */
1409 { 0x00, 0x10, 0, 0x04, 0x00, FALSE, { {sp,0x00}, {-1,-1} }},
1410 { 0x02, 0x10, 0, 0x08, 0x04, FALSE, { {lr,0x00}, {sp,0x04}, {-1,-1} }},
1411 { 0x04, 0x10, 0, 0x14, 0x10, FALSE, { {lr,0x0c}, {sp,0x10}, {-1,-1} }},
1414 static const struct unwind_test_arm tests[] =
1416 #define TEST(func, unwind, unwind_packed, results) \
1417 { func, sizeof(func), unwind, unwind_packed ? 0 : sizeof(unwind), results, ARRAY_SIZE(results) }
1418 TEST(function_0, unwind_info_0, 0, results_0),
1419 TEST(function_1, unwind_info_1, 0, results_1),
1420 TEST(function_2, unwind_info_2, 0, results_2),
1421 TEST(function_3, unwind_info_3, 0, results_3),
1422 TEST(function_4, unwind_info_4, 0, results_4),
1423 TEST(function_5, unwind_info_5, 0, results_5),
1424 TEST(function_6, unwind_info_6, 1, results_6),
1425 TEST(function_7, unwind_info_7, 1, results_7),
1426 TEST(function_8, unwind_info_8, 1, results_8),
1427 TEST(function_9, unwind_info_9, 1, results_9),
1428 TEST(function_10, unwind_info_10, 1, results_10),
1429 TEST(function_11, unwind_info_11, 1, results_11),
1430 TEST(function_12, unwind_info_12, 1, results_12),
1431 TEST(function_13, unwind_info_13, 1, results_13),
1432 TEST(function_14, unwind_info_14, 1, results_14),
1433 TEST(function_15, unwind_info_15, 1, results_15),
1434 TEST(function_16, unwind_info_16, 1, results_16),
1435 TEST(function_17, unwind_info_17, 1, results_17),
1436 TEST(function_18, unwind_info_18, 1, results_18),
1437 TEST(function_19, unwind_info_19, 1, results_19),
1438 TEST(function_20, unwind_info_20, 1, results_20),
1439 TEST(function_21, unwind_info_21, 1, results_21),
1440 TEST(function_22, unwind_info_22, 1, results_22),
1441 TEST(function_23, unwind_info_23, 1, results_23),
1442 TEST(function_24, unwind_info_24, 1, results_24),
1443 TEST(function_25, unwind_info_25, 1, results_25),
1444 TEST(function_26, unwind_info_26, 1, results_26),
1445 TEST(function_27, unwind_info_27, 1, results_27),
1446 TEST(function_28, unwind_info_28, 1, results_28),
1447 TEST(function_29, unwind_info_29, 0, results_29),
1448 TEST(function_30, unwind_info_30, 0, results_30),
1449 #undef TEST
1451 unsigned int i;
1453 for (i = 0; i < ARRAY_SIZE(tests); i++)
1454 call_virtual_unwind_arm( i, &tests[i] );
1457 #endif /* __arm__ */
1459 #if defined(__aarch64__) || defined(__x86_64__)
1461 #define UWOP_TWOBYTES(x) (((x) >> 8) & 0xff), ((x) & 0xff)
1463 #define UWOP_ALLOC_SMALL(size) (0x00 | (size/16))
1464 #define UWOP_SAVE_R19R20_X(offset) (0x20 | (offset/8))
1465 #define UWOP_SAVE_FPLR(offset) (0x40 | (offset/8))
1466 #define UWOP_SAVE_FPLR_X(offset) (0x80 | (offset/8 - 1))
1467 #define UWOP_ALLOC_MEDIUM(size) UWOP_TWOBYTES((0xC0 << 8) | (size/16))
1468 #define UWOP_SAVE_REGP(reg, offset) UWOP_TWOBYTES((0xC8 << 8) | ((reg - 19) << 6) | (offset/8))
1469 #define UWOP_SAVE_REGP_X(reg, offset) UWOP_TWOBYTES((0xCC << 8) | ((reg - 19) << 6) | (offset/8 - 1))
1470 #define UWOP_SAVE_REG(reg, offset) UWOP_TWOBYTES((0xD0 << 8) | ((reg - 19) << 6) | (offset/8))
1471 #define UWOP_SAVE_REG_X(reg, offset) UWOP_TWOBYTES((0xD4 << 8) | ((reg - 19) << 5) | (offset/8 - 1))
1472 #define UWOP_SAVE_LRP(reg, offset) UWOP_TWOBYTES((0xD6 << 8) | ((reg - 19)/2 << 6) | (offset/8))
1473 #define UWOP_SAVE_FREGP(reg, offset) UWOP_TWOBYTES((0xD8 << 8) | ((reg - 8) << 6) | (offset/8))
1474 #define UWOP_SAVE_FREGP_X(reg, offset) UWOP_TWOBYTES((0xDA << 8) | ((reg - 8) << 6) | (offset/8 - 1))
1475 #define UWOP_SAVE_FREG(reg, offset) UWOP_TWOBYTES((0xDC << 8) | ((reg - 8) << 6) | (offset/8))
1476 #define UWOP_SAVE_FREG_X(reg, offset) UWOP_TWOBYTES((0xDE << 8) | ((reg - 8) << 5) | (offset/8 - 1))
1477 #define UWOP_ALLOC_LARGE(size) UWOP_TWOBYTES((0xE0 << 8) | ((size/16) >> 16)), UWOP_TWOBYTES(size/16)
1478 #define UWOP_SET_FP 0xE1
1479 #define UWOP_ADD_FP(offset) UWOP_TWOBYTES((0xE2 << 8) | (offset/8))
1480 #define UWOP_NOP 0xE3
1481 #define UWOP_END 0xE4
1482 #define UWOP_END_C 0xE5
1483 #define UWOP_SAVE_NEXT 0xE6
1484 #define UWOP_SAVE_ANY_REG(reg,offset) 0xE7,(reg),(offset)
1485 #define UWOP_TRAP_FRAME 0xE8
1486 #define UWOP_MACHINE_FRAME 0xE9
1487 #define UWOP_CONTEXT 0xEA
1488 #define UWOP_EC_CONTEXT 0xEB
1489 #define UWOP_CLEAR_UNWOUND_TO_CALL 0xEC
1491 struct results_arm64
1493 int pc_offset; /* pc offset from code start */
1494 int fp_offset; /* fp offset from stack pointer */
1495 int handler; /* expect handler to be set? */
1496 ULONG_PTR pc; /* expected final pc value */
1497 int frame; /* expected frame return value */
1498 int frame_offset; /* whether the frame return value is an offset or an absolute value */
1499 ULONG_PTR regs[48][2]; /* expected values for registers */
1502 struct unwind_test_arm64
1504 const BYTE *function;
1505 size_t function_size;
1506 const BYTE *unwind_info;
1507 size_t unwind_size;
1508 const struct results_arm64 *results;
1509 unsigned int nb_results;
1510 int unwound_clear;
1511 int last_set_reg_ptr;
1514 enum regs_arm64
1516 x0, x1, x2, x3, x4, x5, x6, x7,
1517 x8, x9, x10, x11, x12, x13, x14, x15,
1518 x16, x17, x18, x19, x20, x21, x22, x23,
1519 x24, x25, x26, x27, x28, x29, lr, sp,
1520 d0, d1, d2, d3, d4, d5, d6, d7,
1521 d8, d9, d10, d11, d12, d13, d14, d15
1524 static const char * const reg_names_arm64[48] =
1526 "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7",
1527 "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15",
1528 "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23",
1529 "x24", "x25", "x26", "x27", "x28", "x29", "lr", "sp",
1530 "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
1531 "d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15",
1534 #define ORIG_LR 0xCCCCCCCC
1536 static void call_virtual_unwind_arm64( void *code_mem, int testnum, const struct unwind_test_arm64 *test )
1538 static const int code_offset = 1024;
1539 static const int unwind_offset = 2048;
1540 void *handler, *data;
1541 #ifdef __x86_64__
1542 ARM64EC_NT_CONTEXT context;
1543 #else
1544 ARM64_NT_CONTEXT context;
1545 #endif
1546 ARM64_RUNTIME_FUNCTION runtime_func;
1547 KNONVOLATILE_CONTEXT_POINTERS ctx_ptr;
1548 UINT i, j, k;
1549 ULONG64 fake_stack[256];
1550 ULONG64 frame, orig_pc, orig_fp, unset_reg, sp_offset = 0, regval, *regptr;
1551 static const UINT nb_regs = ARRAY_SIZE(test->results[i].regs);
1553 memcpy( (char *)code_mem + code_offset, test->function, test->function_size );
1554 memcpy( (char *)code_mem + unwind_offset, test->unwind_info, test->unwind_size );
1556 runtime_func.BeginAddress = code_offset;
1557 if (test->unwind_size)
1558 runtime_func.UnwindData = unwind_offset;
1559 else
1560 memcpy(&runtime_func.UnwindData, test->unwind_info, 4);
1562 for (i = 0; i < test->nb_results; i++)
1564 winetest_push_context( "%u/%u", testnum, i );
1565 memset( &ctx_ptr, 0x55, sizeof(ctx_ptr) );
1566 memset( &context, 0x55, sizeof(context) );
1567 memset( &unset_reg, 0x55, sizeof(unset_reg) );
1568 for (j = 0; j < 256; j++) fake_stack[j] = j * 8;
1570 context.Sp = (ULONG_PTR)fake_stack;
1571 context.Lr = (ULONG_PTR)ORIG_LR;
1572 context.Fp = (ULONG_PTR)fake_stack + test->results[i].fp_offset;
1573 context.ContextFlags = 0xcccc;
1574 if (test->unwound_clear) context.ContextFlags |= CONTEXT_ARM64_UNWOUND_TO_CALL;
1576 orig_fp = context.Fp;
1577 orig_pc = (ULONG64)code_mem + code_offset + test->results[i].pc_offset;
1579 trace( "pc=%p (%02x) fp=%p sp=%p\n", (void *)orig_pc, *(UINT *)orig_pc, (void *)orig_fp, (void *)context.Sp );
1581 data = (void *)0xdeadbeef;
1582 handler = RtlVirtualUnwind( UNW_FLAG_EHANDLER, (ULONG64)code_mem, orig_pc,
1583 (RUNTIME_FUNCTION *)&runtime_func,
1584 (CONTEXT *)&context, &data, &frame, &ctx_ptr );
1585 if (test->results[i].handler > 0)
1587 ok( (char *)handler == (char *)code_mem + 0x200,
1588 "wrong handler %p/%p\n", handler, (char *)code_mem + 0x200 );
1589 if (handler) ok( *(DWORD *)data == 0x08070605,
1590 "wrong handler data %lx\n", *(DWORD *)data );
1592 else
1594 ok( handler == NULL, "handler %p instead of NULL\n", handler );
1595 ok( data == (test->results[i].handler < 0 ? (void *)0xdeadbeef : NULL),
1596 "handler data set to %p/%p\n", data,
1597 (test->results[i].handler < 0 ? (void *)0xdeadbeef : NULL) );
1600 ok( context.Pc == test->results[i].pc, "wrong pc %p/%p\n",
1601 (void *)context.Pc, (void*)test->results[i].pc );
1602 ok( frame == (test->results[i].frame_offset ? (ULONG64)fake_stack : 0) + test->results[i].frame, "wrong frame %p/%p\n",
1603 (void *)frame, (char *)(test->results[i].frame_offset ? fake_stack : NULL) + test->results[i].frame );
1604 if (!test->unwound_clear || i < test->unwound_clear)
1605 ok( context.ContextFlags == (0xcccc | CONTEXT_ARM64_UNWOUND_TO_CALL),
1606 "wrong flags %lx\n", context.ContextFlags );
1607 else
1608 ok( context.ContextFlags == 0xcccc,
1609 "wrong flags %lx\n", context.ContextFlags );
1611 sp_offset = 0;
1612 for (k = 0; k < nb_regs; k++)
1614 if (test->results[i].regs[k][0] == -1)
1615 break;
1616 if (test->results[i].regs[k][0] == sp) {
1617 /* If sp is part of the registers list, treat it as an offset
1618 * between the returned frame pointer and the sp register. */
1619 sp_offset = test->results[i].regs[k][1];
1620 break;
1623 ok( frame - sp_offset == context.Sp, "wrong sp %p/%p\n",
1624 (void *)(frame - sp_offset), (void *)context.Sp);
1626 #ifdef __x86_64__
1627 for (j = 0; j < sizeof(ctx_ptr)/sizeof(void*); j++)
1628 ok( ((void **)&ctx_ptr)[j] == (void *)unset_reg,
1629 "ctx_ptr %u set to %p\n", j, ((void **)&ctx_ptr)[j] );
1630 #endif
1632 for (j = 0; j < 48; j++)
1634 switch (j)
1636 #define GET(i) case i: regval = context.X##i; break
1637 GET(0); GET(1); GET(2); GET(3); GET(4); GET(5); GET(6); GET(7);
1638 GET(8); GET(9); GET(10); GET(11); GET(12);
1639 GET(15); GET(19); GET(20); GET(21); GET(22); GET(25); GET(26); GET(27);
1640 #ifdef __x86_64__
1641 case x13: case x14: continue;
1642 case x16: regval = context.X16_0 | ((DWORD64)context.X16_1 << 16) | ((DWORD64)context.X16_2 << 32) | ((DWORD64)context.X16_3 << 48); break;
1643 case x17: regval = context.X17_0 | ((DWORD64)context.X17_1 << 16) | ((DWORD64)context.X17_2 << 32) | ((DWORD64)context.X17_3 << 48); break;
1644 case x18: case x23: case x24: case x28: continue;
1645 #else
1646 GET(13); GET(14); GET(16); GET(17); GET(18); GET(23); GET(24); GET(28);
1647 #endif
1648 #undef GET
1649 case x29: regval = context.Fp; break;
1650 case lr: regval = context.Lr; break;
1651 case sp: continue; /* Handling sp separately above */
1652 default: regval = context.V[j - d0].Low; break;
1655 regptr = NULL;
1656 #ifndef __x86_64__
1657 if (j >= 19 && j <= 30) regptr = (&ctx_ptr.X19)[j - 19];
1658 else if (j >= d8 && j <= d15) regptr = (&ctx_ptr.D8)[j - d8];
1659 #endif
1661 for (k = 0; k < nb_regs; k++)
1663 if (test->results[i].regs[k][0] == -1)
1665 k = nb_regs;
1666 break;
1668 if (test->results[i].regs[k][0] == j) break;
1671 if (k < nb_regs)
1673 ok( regval == test->results[i].regs[k][1],
1674 "register %s wrong %llx/%llx\n", reg_names_arm64[j], regval, test->results[i].regs[k][1] );
1675 if (regptr)
1677 if (test->last_set_reg_ptr && j > test->last_set_reg_ptr && j <= 30)
1678 ok( regptr == (void *)unset_reg, "register %s should not have pointer set\n", reg_names_arm64[j] );
1679 else
1681 ok( regptr != (void *)unset_reg, "register %s should have pointer set\n", reg_names_arm64[j] );
1682 if (regptr != (void *)unset_reg)
1683 ok( *regptr == regval, "register %s should have reg pointer to %llx / %llx\n",
1684 reg_names_arm64[j], *regptr, regval );
1688 else
1690 ok( k == nb_regs, "register %s should be set\n", reg_names_arm64[j] );
1691 ok( !regptr || regptr == (void *)unset_reg, "register %s should not have pointer set\n", reg_names_arm64[j] );
1692 if (j == lr)
1693 ok( context.Lr == ORIG_LR, "register lr wrong %llx/unset\n", context.Lr );
1694 else if (j == x29)
1695 ok( context.Fp == orig_fp, "register fp wrong %llx/unset\n", context.Fp );
1696 else
1697 ok( regval == unset_reg, "register %s wrong %llx/unset\n", reg_names_arm64[j], regval);
1700 winetest_pop_context();
1704 #define DW(dword) ((dword >> 0) & 0xff), ((dword >> 8) & 0xff), ((dword >> 16) & 0xff), ((dword >> 24) & 0xff)
1706 static void test_virtual_unwind_arm64(void)
1708 static const BYTE function_0[] =
1710 0xff, 0x83, 0x00, 0xd1, /* 00: sub sp, sp, #32 */
1711 0xf3, 0x53, 0x01, 0xa9, /* 04: stp x19, x20, [sp, #16] */
1712 0x1f, 0x20, 0x03, 0xd5, /* 08: nop */
1713 0xf3, 0x53, 0x41, 0xa9, /* 0c: ldp x19, x20, [sp, #16] */
1714 0xff, 0x83, 0x00, 0x91, /* 10: add sp, sp, #32 */
1715 0xc0, 0x03, 0x5f, 0xd6, /* 14: ret */
1718 static const DWORD unwind_info_0_header =
1719 (sizeof(function_0)/4) | /* function length */
1720 (1 << 20) | /* X */
1721 (0 << 21) | /* E */
1722 (1 << 22) | /* epilog */
1723 (2 << 27); /* codes */
1724 static const DWORD unwind_info_0_epilog0 =
1725 (3 << 0) | /* offset */
1726 (4 << 22); /* index */
1728 static const BYTE unwind_info_0[] =
1730 DW(unwind_info_0_header),
1731 DW(unwind_info_0_epilog0),
1733 UWOP_SAVE_REGP(19, 16), /* stp x19, x20, [sp, #16] */
1734 UWOP_ALLOC_SMALL(32), /* sub sp, sp, #32 */
1735 UWOP_END,
1737 UWOP_SAVE_REGP(19, 16), /* stp x19, x20, [sp, #16] */
1738 UWOP_ALLOC_SMALL(32), /* sub sp, sp, #32 */
1739 UWOP_END,
1741 0x00, 0x02, 0x00, 0x00, /* handler */
1742 0x05, 0x06, 0x07, 0x08, /* data */
1745 static const struct results_arm64 results_0[] =
1747 /* offset fp handler pc frame offset registers */
1748 { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
1749 { 0x04, 0x00, 0, ORIG_LR, 0x020, TRUE, { {-1,-1} }},
1750 { 0x08, 0x00, 1, ORIG_LR, 0x020, TRUE, { {x19,0x10}, {x20,0x18}, {-1,-1} }},
1751 { 0x0c, 0x00, 0, ORIG_LR, 0x020, TRUE, { {x19,0x10}, {x20,0x18}, {-1,-1} }},
1752 { 0x10, 0x00, 0, ORIG_LR, 0x020, TRUE, { {-1,-1} }},
1753 { 0x14, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
1757 static const BYTE function_1[] =
1759 0xf3, 0x53, 0xbe, 0xa9, /* 00: stp x19, x20, [sp, #-32]! */
1760 0xfe, 0x0b, 0x00, 0xf9, /* 04: str x30, [sp, #16] */
1761 0xff, 0x43, 0x00, 0xd1, /* 08: sub sp, sp, #16 */
1762 0x1f, 0x20, 0x03, 0xd5, /* 0c: nop */
1763 0xff, 0x43, 0x00, 0x91, /* 10: add sp, sp, #16 */
1764 0xfe, 0x0b, 0x40, 0xf9, /* 14: ldr x30, [sp, #16] */
1765 0xf3, 0x53, 0xc2, 0xa8, /* 18: ldp x19, x20, [sp], #32 */
1766 0xc0, 0x03, 0x5f, 0xd6, /* 1c: ret */
1769 static const DWORD unwind_info_1_packed =
1770 (1 << 0) | /* Flag */
1771 (sizeof(function_1)/4 << 2) | /* FunctionLength */
1772 (0 << 13) | /* RegF */
1773 (2 << 16) | /* RegI */
1774 (0 << 20) | /* H */
1775 (1 << 21) | /* CR */
1776 (3 << 23); /* FrameSize */
1778 static const BYTE unwind_info_1[] = { DW(unwind_info_1_packed) };
1780 static const struct results_arm64 results_1[] =
1782 /* offset fp handler pc frame offset registers */
1783 { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
1784 { 0x04, 0x00, 0, ORIG_LR, 0x020, TRUE, { {x19,0x00}, {x20,0x08}, {-1,-1} }},
1785 { 0x08, 0x00, 0, 0x10, 0x020, TRUE, { {x19,0x00}, {x20,0x08}, {lr,0x10}, {-1,-1} }},
1786 { 0x0c, 0x00, 0, 0x20, 0x030, TRUE, { {x19,0x10}, {x20,0x18}, {lr,0x20}, {-1,-1} }},
1787 { 0x10, 0x00, 0, 0x20, 0x030, TRUE, { {x19,0x10}, {x20,0x18}, {lr,0x20}, {-1,-1} }},
1788 { 0x14, 0x00, 0, 0x10, 0x020, TRUE, { {x19,0x00}, {x20,0x08}, {lr,0x10}, {-1,-1} }},
1789 { 0x18, 0x00, 0, ORIG_LR, 0x020, TRUE, { {x19,0x00}, {x20,0x08}, {-1,-1} }},
1790 { 0x1c, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
1793 static const BYTE function_2[] =
1795 0xff, 0x43, 0x00, 0xd1, /* 00: sub sp, sp, #16 */
1796 0x1f, 0x20, 0x03, 0xd5, /* 04: nop */
1797 0xff, 0x43, 0x00, 0xd1, /* 08: sub sp, sp, #16 */
1798 0x1f, 0x20, 0x03, 0xd5, /* 0c: nop */
1799 0xc0, 0x03, 0x5f, 0xd6, /* 10: ret */
1802 static const DWORD unwind_info_2_header =
1803 (sizeof(function_2)/4) | /* function length */
1804 (0 << 20) | /* X */
1805 (0 << 21) | /* E */
1806 (0 << 22) | /* epilog */
1807 (1 << 27); /* codes */
1809 static const BYTE unwind_info_2[] =
1811 DW(unwind_info_2_header),
1813 UWOP_ALLOC_SMALL(16), /* sub sp, sp, #16 */
1814 UWOP_MACHINE_FRAME,
1815 UWOP_ALLOC_SMALL(16), /* sub sp, sp, #16 */
1816 UWOP_END,
1819 /* Partial prologues with the custom frame opcodes (machine frame,
1820 * context) behave like there's one less instruction to skip, because the
1821 * custom frame is set up externally without an explicit instruction. */
1822 static const struct results_arm64 results_2[] =
1824 /* offset fp handler pc frame offset registers */
1825 { 0x00, 0x00, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }},
1826 { 0x04, 0x00, 0, 0x0008, 0x010, FALSE, { {-1,-1} }},
1827 { 0x08, 0x00, 0, 0x0018, 0x020, FALSE, { {-1,-1} }},
1828 { 0x0c, 0x00, 0, 0x0018, 0x020, FALSE, { {-1,-1} }},
1829 { 0x10, 0x00, 0, 0x0018, 0x020, FALSE, { {-1,-1} }},
1832 static const BYTE function_3[] =
1834 0xff, 0x43, 0x00, 0xd1, /* 00: sub sp, sp, #16 */
1835 0x1f, 0x20, 0x03, 0xd5, /* 04: nop */
1836 0xff, 0x43, 0x00, 0xd1, /* 08: sub sp, sp, #16 */
1837 0x1f, 0x20, 0x03, 0xd5, /* 0c: nop */
1838 0xc0, 0x03, 0x5f, 0xd6, /* 10: ret */
1841 static const DWORD unwind_info_3_header =
1842 (sizeof(function_3)/4) | /* function length */
1843 (0 << 20) | /* X */
1844 (0 << 21) | /* E */
1845 (0 << 22) | /* epilog */
1846 (1 << 27); /* codes */
1848 static const BYTE unwind_info_3[] =
1850 DW(unwind_info_3_header),
1852 UWOP_ALLOC_SMALL(16), /* sub sp, sp, #16 */
1853 UWOP_CONTEXT,
1854 UWOP_ALLOC_SMALL(16), /* sub sp, sp, #16 */
1855 UWOP_END,
1858 static const struct results_arm64 results_3[] =
1860 /* offset fp handler pc frame offset registers */
1861 { 0x00, 0x00, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }},
1862 { 0x04, 0x00, 0 , 0x0108, 0x110, FALSE, { {x0, 0x08}, {x1, 0x10}, {x2, 0x18}, {x3, 0x20}, {x4, 0x28}, {x5, 0x30}, {x6, 0x38}, {x7, 0x40}, {x8, 0x48}, {x9, 0x50}, {x10, 0x58}, {x11, 0x60}, {x12, 0x68}, {x13, 0x70}, {x14, 0x78}, {x15, 0x80}, {x16, 0x88}, {x17, 0x90}, {x18, 0x98}, {x19, 0xA0}, {x20, 0xA8}, {x21, 0xB0}, {x22, 0xB8}, {x23, 0xC0}, {x24, 0xC8}, {x25, 0xD0}, {x26, 0xD8}, {x27, 0xE0}, {x28, 0xE8}, {x29, 0xF0}, {lr, 0xF8}, {d0, 0x110}, {d1, 0x120}, {d2, 0x130}, {d3, 0x140}, {d4, 0x150}, {d5, 0x160}, {d6, 0x170}, {d7, 0x180}, {d8, 0x190}, {d9, 0x1a0}, {d10, 0x1b0}, {d11, 0x1c0}, {d12, 0x1d0}, {d13, 0x1e0}, {d14, 0x1f0}, {d15, 0x200}, {-1,-1} }},
1863 { 0x08, 0x00, 0 , 0x0118, 0x120, FALSE, { {x0, 0x18}, {x1, 0x20}, {x2, 0x28}, {x3, 0x30}, {x4, 0x38}, {x5, 0x40}, {x6, 0x48}, {x7, 0x50}, {x8, 0x58}, {x9, 0x60}, {x10, 0x68}, {x11, 0x70}, {x12, 0x78}, {x13, 0x80}, {x14, 0x88}, {x15, 0x90}, {x16, 0x98}, {x17, 0xA0}, {x18, 0xA8}, {x19, 0xB0}, {x20, 0xB8}, {x21, 0xC0}, {x22, 0xC8}, {x23, 0xD0}, {x24, 0xD8}, {x25, 0xE0}, {x26, 0xE8}, {x27, 0xF0}, {x28, 0xF8}, {x29, 0x100}, {lr, 0x108}, {d0, 0x120}, {d1, 0x130}, {d2, 0x140}, {d3, 0x150}, {d4, 0x160}, {d5, 0x170}, {d6, 0x180}, {d7, 0x190}, {d8, 0x1a0}, {d9, 0x1b0}, {d10, 0x1c0}, {d11, 0x1d0}, {d12, 0x1e0}, {d13, 0x1f0}, {d14, 0x200}, {d15, 0x210}, {-1,-1} }},
1864 { 0x0c, 0x00, 0 , 0x0118, 0x120, FALSE, { {x0, 0x18}, {x1, 0x20}, {x2, 0x28}, {x3, 0x30}, {x4, 0x38}, {x5, 0x40}, {x6, 0x48}, {x7, 0x50}, {x8, 0x58}, {x9, 0x60}, {x10, 0x68}, {x11, 0x70}, {x12, 0x78}, {x13, 0x80}, {x14, 0x88}, {x15, 0x90}, {x16, 0x98}, {x17, 0xA0}, {x18, 0xA8}, {x19, 0xB0}, {x20, 0xB8}, {x21, 0xC0}, {x22, 0xC8}, {x23, 0xD0}, {x24, 0xD8}, {x25, 0xE0}, {x26, 0xE8}, {x27, 0xF0}, {x28, 0xF8}, {x29, 0x100}, {lr, 0x108}, {d0, 0x120}, {d1, 0x130}, {d2, 0x140}, {d3, 0x150}, {d4, 0x160}, {d5, 0x170}, {d6, 0x180}, {d7, 0x190}, {d8, 0x1a0}, {d9, 0x1b0}, {d10, 0x1c0}, {d11, 0x1d0}, {d12, 0x1e0}, {d13, 0x1f0}, {d14, 0x200}, {d15, 0x210}, {-1,-1} }},
1865 { 0x10, 0x00, 0 , 0x0118, 0x120, FALSE, { {x0, 0x18}, {x1, 0x20}, {x2, 0x28}, {x3, 0x30}, {x4, 0x38}, {x5, 0x40}, {x6, 0x48}, {x7, 0x50}, {x8, 0x58}, {x9, 0x60}, {x10, 0x68}, {x11, 0x70}, {x12, 0x78}, {x13, 0x80}, {x14, 0x88}, {x15, 0x90}, {x16, 0x98}, {x17, 0xA0}, {x18, 0xA8}, {x19, 0xB0}, {x20, 0xB8}, {x21, 0xC0}, {x22, 0xC8}, {x23, 0xD0}, {x24, 0xD8}, {x25, 0xE0}, {x26, 0xE8}, {x27, 0xF0}, {x28, 0xF8}, {x29, 0x100}, {lr, 0x108}, {d0, 0x120}, {d1, 0x130}, {d2, 0x140}, {d3, 0x150}, {d4, 0x160}, {d5, 0x170}, {d6, 0x180}, {d7, 0x190}, {d8, 0x1a0}, {d9, 0x1b0}, {d10, 0x1c0}, {d11, 0x1d0}, {d12, 0x1e0}, {d13, 0x1f0}, {d14, 0x200}, {d15, 0x210}, {-1,-1} }},
1868 static const BYTE function_4[] =
1870 0xff, 0x43, 0x00, 0xd1, /* 00: sub sp, sp, #16 */
1871 0xff, 0x03, 0x08, 0xd1, /* 04: sub sp, sp, #512 */
1872 0xff, 0x43, 0x40, 0xd1, /* 08: sub sp, sp, #65536 */
1873 0xfd, 0x03, 0x00, 0x91, /* 0c: mov x29, sp */
1874 0xf3, 0x53, 0xbe, 0xa9, /* 10: stp x19, x20, [sp, #-32]! */
1875 0xf5, 0x5b, 0x01, 0xa9, /* 14: stp x21, x22, [sp, #16] */
1876 0xf7, 0x0f, 0x1e, 0xf8, /* 18: str x23, [sp, #-32]! */
1877 0xf8, 0x07, 0x00, 0xf9, /* 1c: str x24, [sp, #8] */
1878 0xf9, 0x7b, 0x01, 0xa9, /* 20: stp x25, x30, [sp, #16] */
1879 0xfd, 0x7b, 0x03, 0xa9, /* 24: stp x29, x30, [sp, #48] */
1880 0xfd, 0x7b, 0xbe, 0xa9, /* 28: stp x29, x30, [sp, #-32]! */
1881 0xf3, 0x53, 0xbe, 0xa9, /* 2c: stp x19, x20, [sp, #-32]! */
1882 0xe8, 0x27, 0xbe, 0x6d, /* 30: stp d8, d9, [sp, #-32]! */
1883 0xea, 0x2f, 0x01, 0x6d, /* 34: stp d10, d11, [sp, #16] */
1884 0xec, 0x0f, 0x1e, 0xfc, /* 38: str d12, [sp, #-32]! */
1885 0xed, 0x07, 0x00, 0xfd, /* 3c: str d13, [sp, #8] */
1886 0xfd, 0x43, 0x00, 0x91, /* 40: add x29, sp, #16 */
1887 0xc0, 0x03, 0x5f, 0xd6, /* 44: ret */
1890 static const DWORD unwind_info_4_header =
1891 (sizeof(function_4)/4) | /* function length */
1892 (0 << 20) | /* X */
1893 (0 << 21) | /* E */
1894 (0 << 22) | /* epilog */
1895 (8 << 27); /* codes */
1897 static const BYTE unwind_info_4[] =
1899 DW(unwind_info_4_header),
1901 UWOP_ADD_FP(16), /* 40: add x29, sp, #16 */
1902 UWOP_SAVE_FREG(13, 8), /* 3c: str d13, [sp, #8] */
1903 UWOP_SAVE_FREG_X(12, 32), /* 38: str d12, [sp, #-32]! */
1904 UWOP_SAVE_FREGP(10, 16), /* 34: stp d10, d11, [sp, #16] */
1905 UWOP_SAVE_FREGP_X(8, 32), /* 30: stp d8, d9, [sp, #-32]! */
1906 UWOP_SAVE_R19R20_X(32), /* 2c: stp x19, x20, [sp, #-32]! */
1907 UWOP_SAVE_FPLR_X(32), /* 28: stp x29, x30, [sp, #-32]! */
1908 UWOP_SAVE_FPLR(16), /* 24: stp x29, x30, [sp, #16] */
1909 UWOP_SAVE_LRP(25, 16), /* 20: stp x25, x30, [sp, #16] */
1910 UWOP_SAVE_REG(24, 8), /* 1c: str x24, [sp, #8] */
1911 UWOP_SAVE_REG_X(23, 32), /* 18: str x23, [sp, #-32]! */
1912 UWOP_SAVE_REGP(21, 16), /* 14: stp x21, x22, [sp, #16] */
1913 UWOP_SAVE_REGP_X(19, 32), /* 10: stp x19, x20, [sp, #-32]! */
1914 UWOP_SET_FP, /* 0c: mov x29, sp */
1915 UWOP_ALLOC_LARGE(65536), /* 08: sub sp, sp, #65536 */
1916 UWOP_ALLOC_MEDIUM(512), /* 04: sub sp, sp, #512 */
1917 UWOP_ALLOC_SMALL(16), /* 00: sub sp, sp, #16 */
1918 UWOP_END,
1921 static const struct results_arm64 results_4[] =
1923 /* offset fp handler pc frame offset registers */
1924 { 0x00, 0x10, 0, ORIG_LR, 0x00000, TRUE, { {-1,-1} }},
1925 { 0x04, 0x10, 0, ORIG_LR, 0x00010, TRUE, { {-1,-1} }},
1926 { 0x08, 0x10, 0, ORIG_LR, 0x00210, TRUE, { {-1,-1} }},
1927 { 0x0c, 0x10, 0, ORIG_LR, 0x10210, TRUE, { {-1,-1} }},
1928 { 0x14, 0x00, 0, ORIG_LR, 0x10210, TRUE, { {x19, 0x00}, {x20, 0x08}, {-1,-1} }},
1929 { 0x18, 0x00, 0, ORIG_LR, 0x10210, TRUE, { {x19, 0x00}, {x20, 0x08}, {x21, 0x10}, {x22, 0x18}, {-1,-1} }},
1930 { 0x1c, 0x00, 0, ORIG_LR, 0x10210, TRUE, { {x19, 0x20}, {x20, 0x28}, {x21, 0x30}, {x22, 0x38}, {x23, 0x00}, {-1,-1} }},
1931 { 0x20, 0x00, 0, ORIG_LR, 0x10210, TRUE, { {x19, 0x20}, {x20, 0x28}, {x21, 0x30}, {x22, 0x38}, {x23, 0x00}, {x24, 0x08}, {-1,-1} }},
1932 { 0x24, 0x00, 0, 0x0018, 0x10210, TRUE, { {x19, 0x20}, {x20, 0x28}, {x21, 0x30}, {x22, 0x38}, {x23, 0x00}, {x24, 0x08}, {x25, 0x10}, {lr, 0x18}, {-1,-1} }},
1933 { 0x28, 0x00, 0, 0x0018, 0x10220, FALSE, { {x19, 0x20}, {x20, 0x28}, {x21, 0x30}, {x22, 0x38}, {x23, 0x00}, {x24, 0x08}, {x25, 0x10}, {lr, 0x18}, {x29, 0x10}, {-1,-1} }},
1934 { 0x2c, 0x00, 0, 0x0038, 0x10240, FALSE, { {x19, 0x40}, {x20, 0x48}, {x21, 0x50}, {x22, 0x58}, {x23, 0x20}, {x24, 0x28}, {x25, 0x30}, {lr, 0x38}, {x29, 0x30}, {-1,-1} }},
1935 { 0x30, 0x00, 0, 0x0058, 0x10260, FALSE, { {x19, 0x60}, {x20, 0x68}, {x21, 0x70}, {x22, 0x78}, {x23, 0x40}, {x24, 0x48}, {x25, 0x50}, {lr, 0x58}, {x29, 0x50}, {-1,-1} }},
1936 { 0x34, 0x00, 0, 0x0078, 0x10280, FALSE, { {x19, 0x80}, {x20, 0x88}, {x21, 0x90}, {x22, 0x98}, {x23, 0x60}, {x24, 0x68}, {x25, 0x70}, {lr, 0x78}, {x29, 0x70}, {d8, 0x00}, {d9, 0x08}, {-1,-1} }},
1937 { 0x38, 0x00, 0, 0x0078, 0x10280, FALSE, { {x19, 0x80}, {x20, 0x88}, {x21, 0x90}, {x22, 0x98}, {x23, 0x60}, {x24, 0x68}, {x25, 0x70}, {lr, 0x78}, {x29, 0x70}, {d8, 0x00}, {d9, 0x08}, {d10, 0x10}, {d11, 0x18}, {-1,-1} }},
1938 { 0x3c, 0x00, 0, 0x0098, 0x102a0, FALSE, { {x19, 0xa0}, {x20, 0xa8}, {x21, 0xb0}, {x22, 0xb8}, {x23, 0x80}, {x24, 0x88}, {x25, 0x90}, {lr, 0x98}, {x29, 0x90}, {d8, 0x20}, {d9, 0x28}, {d10, 0x30}, {d11, 0x38}, {d12, 0x00}, {-1,-1} }},
1939 { 0x40, 0x00, 0, 0x0098, 0x102a0, FALSE, { {x19, 0xa0}, {x20, 0xa8}, {x21, 0xb0}, {x22, 0xb8}, {x23, 0x80}, {x24, 0x88}, {x25, 0x90}, {lr, 0x98}, {x29, 0x90}, {d8, 0x20}, {d9, 0x28}, {d10, 0x30}, {d11, 0x38}, {d12, 0x00}, {d13, 0x08}, {-1,-1} }},
1940 { 0x44, 0x20, 0, 0x00a8, 0x102b0, FALSE, { {x19, 0xb0}, {x20, 0xb8}, {x21, 0xc0}, {x22, 0xc8}, {x23, 0x90}, {x24, 0x98}, {x25, 0xa0}, {lr, 0xa8}, {x29, 0xa0}, {d8, 0x30}, {d9, 0x38}, {d10, 0x40}, {d11, 0x48}, {d12, 0x10}, {d13, 0x18}, {-1,-1} }},
1943 static const BYTE function_5[] =
1945 0xf3, 0x53, 0xbe, 0xa9, /* 00: stp x19, x20, [sp, #-32]! */
1946 0xf5, 0x5b, 0x01, 0xa9, /* 04: stp x21, x22, [sp, #16] */
1947 0xf7, 0x63, 0xbc, 0xa9, /* 08: stp x23, x24, [sp, #-64]! */
1948 0xf9, 0x6b, 0x01, 0xa9, /* 0c: stp x25, x26, [sp, #16] */
1949 0xfb, 0x73, 0x02, 0xa9, /* 10: stp x27, x28, [sp, #32] */
1950 0xfd, 0x7b, 0x03, 0xa9, /* 14: stp x29, x30, [sp, #48] */
1951 0xe8, 0x27, 0xbc, 0x6d, /* 18: stp d8, d9, [sp, #-64]! */
1952 0xea, 0x2f, 0x01, 0x6d, /* 1c: stp d10, d11, [sp, #16] */
1953 0xec, 0x37, 0x02, 0x6d, /* 20: stp d12, d13, [sp, #32] */
1954 0xee, 0x3f, 0x03, 0x6d, /* 24: stp d14, d15, [sp, #48] */
1955 0xc0, 0x03, 0x5f, 0xd6, /* 28: ret */
1958 static const DWORD unwind_info_5_header =
1959 (sizeof(function_5)/4) | /* function length */
1960 (0 << 20) | /* X */
1961 (0 << 21) | /* E */
1962 (0 << 22) | /* epilog */
1963 (4 << 27); /* codes */
1965 static const BYTE unwind_info_5[] =
1967 DW(unwind_info_5_header),
1969 UWOP_SAVE_NEXT, /* 24: stp d14, d15, [sp, #48] */
1970 UWOP_SAVE_FREGP(12, 32), /* 20: stp d12, d13, [sp, #32] */
1971 UWOP_SAVE_NEXT, /* 1c: stp d10, d11, [sp, #16] */
1972 UWOP_SAVE_FREGP_X(8, 64), /* 18: stp d8, d9, [sp, #-64]! */
1973 UWOP_SAVE_NEXT, /* 14: stp x29, x30, [sp, #48] */
1974 UWOP_SAVE_REGP(27, 32), /* 10: stp x27, x28, [sp, #32] */
1975 UWOP_SAVE_NEXT, /* 0c: stp x25, x26, [sp, #16] */
1976 UWOP_SAVE_REGP_X(23, 64), /* 08: stp x23, x24, [sp, #-64]! */
1977 UWOP_SAVE_NEXT, /* 04: stp x21, x22, [sp, #16] */
1978 UWOP_SAVE_R19R20_X(32), /* 00: stp x19, x20, [sp, #-32]! */
1979 UWOP_END,
1980 UWOP_NOP /* padding */
1983 /* Windows seems to only save one register for UWOP_SAVE_NEXT for
1984 * float registers, contrary to what the documentation says. The tests
1985 * for those cases are commented out; they succeed in wine but fail
1986 * on native windows. */
1987 static const struct results_arm64 results_5[] =
1989 /* offset fp handler pc frame offset registers */
1990 { 0x00, 0x00, 0, ORIG_LR, 0x00000, TRUE, { {-1,-1} }},
1991 { 0x04, 0x00, 0, ORIG_LR, 0x00020, TRUE, { {x19, 0x00}, {x20, 0x08}, {-1,-1} }},
1992 { 0x08, 0x00, 0, ORIG_LR, 0x00020, TRUE, { {x19, 0x00}, {x20, 0x08}, {x21, 0x10}, {x22, 0x18}, {-1,-1} }},
1993 { 0x0c, 0x00, 0, ORIG_LR, 0x00060, TRUE, { {x19, 0x40}, {x20, 0x48}, {x21, 0x50}, {x22, 0x58}, {x23, 0x00}, {x24, 0x08}, {-1,-1} }},
1994 { 0x10, 0x00, 0, ORIG_LR, 0x00060, TRUE, { {x19, 0x40}, {x20, 0x48}, {x21, 0x50}, {x22, 0x58}, {x23, 0x00}, {x24, 0x08}, {x25, 0x10}, {x26, 0x18}, {-1,-1} }},
1995 { 0x14, 0x00, 0, ORIG_LR, 0x00060, TRUE, { {x19, 0x40}, {x20, 0x48}, {x21, 0x50}, {x22, 0x58}, {x23, 0x00}, {x24, 0x08}, {x25, 0x10}, {x26, 0x18}, {x27, 0x20}, {x28, 0x28}, {-1,-1} }},
1996 { 0x18, 0x00, 0, 0x38, 0x00060, TRUE, { {x19, 0x40}, {x20, 0x48}, {x21, 0x50}, {x22, 0x58}, {x23, 0x00}, {x24, 0x08}, {x25, 0x10}, {x26, 0x18}, {x27, 0x20}, {x28, 0x28}, {x29, 0x30}, {lr, 0x38}, {-1,-1} }},
1997 { 0x1c, 0x00, 0, 0x78, 0x000a0, TRUE, { {x19, 0x80}, {x20, 0x88}, {x21, 0x90}, {x22, 0x98}, {x23, 0x40}, {x24, 0x48}, {x25, 0x50}, {x26, 0x58}, {x27, 0x60}, {x28, 0x68}, {x29, 0x70}, {lr, 0x78}, {d8, 0x00}, {d9, 0x08}, {-1,-1} }},
1998 #if 0
1999 { 0x20, 0x00, 0, 0x78, 0x000a0, TRUE, { {x19, 0x80}, {x20, 0x88}, {x21, 0x90}, {x22, 0x98}, {x23, 0x40}, {x24, 0x48}, {x25, 0x50}, {x26, 0x58}, {x27, 0x60}, {x28, 0x68}, {x29, 0x70}, {lr, 0x78}, {d8, 0x00}, {d9, 0x08}, {d10, 0x10}, {d11, 0x18}, {-1,-1} }},
2000 { 0x24, 0x00, 0, 0x78, 0x000a0, TRUE, { {x19, 0x80}, {x20, 0x88}, {x21, 0x90}, {x22, 0x98}, {x23, 0x40}, {x24, 0x48}, {x25, 0x50}, {x26, 0x58}, {x27, 0x60}, {x28, 0x68}, {x29, 0x70}, {lr, 0x78}, {d8, 0x00}, {d9, 0x08}, {d10, 0x10}, {d11, 0x18}, {d12, 0x20}, {d13, 0x28}, {-1,-1} }},
2001 { 0x28, 0x00, 0, 0x78, 0x000a0, TRUE, { {x19, 0x80}, {x20, 0x88}, {x21, 0x90}, {x22, 0x98}, {x23, 0x40}, {x24, 0x48}, {x25, 0x50}, {x26, 0x58}, {x27, 0x60}, {x28, 0x68}, {x29, 0x70}, {lr, 0x78}, {d8, 0x00}, {d9, 0x08}, {d10, 0x10}, {d11, 0x18}, {d12, 0x20}, {d13, 0x28}, {d14, 0x30}, {d15, 0x38}, {-1,-1} }},
2002 #endif
2005 static const BYTE function_6[] =
2007 0xf3, 0x53, 0xbd, 0xa9, /* 00: stp x19, x20, [sp, #-48]! */
2008 0xf5, 0x0b, 0x00, 0xf9, /* 04: str x21, [sp, #16] */
2009 0xe8, 0xa7, 0x01, 0x6d, /* 08: stp d8, d9, [sp, #24] */
2010 0xea, 0x17, 0x00, 0xfd, /* 0c: str d10, [sp, #40] */
2011 0xff, 0x03, 0x00, 0xd1, /* 10: sub sp, sp, #0 */
2012 0x1f, 0x20, 0x03, 0xd5, /* 14: nop */
2013 0xff, 0x03, 0x00, 0x91, /* 18: add sp, sp, #0 */
2014 0xea, 0x17, 0x40, 0xfd, /* 1c: ldr d10, [sp, #40] */
2015 0xe8, 0xa7, 0x41, 0x6d, /* 20: ldp d8, d9, [sp, #24] */
2016 0xf5, 0x0b, 0x40, 0xf9, /* 24: ldr x21, [sp, #16] */
2017 0xf3, 0x53, 0xc3, 0xa8, /* 28: ldp x19, x20, [sp], #48 */
2018 0xc0, 0x03, 0x5f, 0xd6, /* 2c: ret */
2021 static const DWORD unwind_info_6_packed =
2022 (1 << 0) | /* Flag */
2023 (sizeof(function_6)/4 << 2) | /* FunctionLength */
2024 (2 << 13) | /* RegF */
2025 (3 << 16) | /* RegI */
2026 (0 << 20) | /* H */
2027 (0 << 21) | /* CR */
2028 (3 << 23); /* FrameSize */
2030 static const BYTE unwind_info_6[] = { DW(unwind_info_6_packed) };
2032 static const struct results_arm64 results_6[] =
2034 /* offset fp handler pc frame offset registers */
2035 { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
2036 { 0x04, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19,0x00}, {x20,0x08}, {-1,-1} }},
2037 { 0x08, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19,0x00}, {x20,0x08}, {x21, 0x10}, {-1,-1} }},
2038 { 0x0c, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19,0x00}, {x20,0x08}, {x21, 0x10}, {d8, 0x18}, {d9, 0x20}, {-1,-1} }},
2039 { 0x10, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19,0x00}, {x20,0x08}, {x21, 0x10}, {d8, 0x18}, {d9, 0x20}, {d10, 0x28}, {-1,-1} }},
2040 { 0x14, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19,0x00}, {x20,0x08}, {x21, 0x10}, {d8, 0x18}, {d9, 0x20}, {d10, 0x28}, {-1,-1} }},
2041 { 0x18, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19,0x00}, {x20,0x08}, {x21, 0x10}, {d8, 0x18}, {d9, 0x20}, {d10, 0x28}, {-1,-1} }},
2042 { 0x1c, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19,0x00}, {x20,0x08}, {x21, 0x10}, {d8, 0x18}, {d9, 0x20}, {d10, 0x28}, {-1,-1} }},
2043 { 0x20, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19,0x00}, {x20,0x08}, {x21, 0x10}, {d8, 0x18}, {d9, 0x20}, {-1,-1} }},
2044 { 0x24, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19,0x00}, {x20,0x08}, {x21, 0x10}, {-1,-1} }},
2045 { 0x28, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19,0x00}, {x20,0x08}, {-1,-1} }},
2046 { 0x2c, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
2049 static const BYTE function_7[] =
2051 0xf3, 0x0f, 0x1d, 0xf8, /* 00: str x19, [sp, #-48]! */
2052 0xe8, 0xa7, 0x00, 0x6d, /* 04: stp d8, d9, [sp, #8] */
2053 0xea, 0xaf, 0x01, 0x6d, /* 08: stp d10, d11, [sp, #24] */
2054 0xff, 0x03, 0x00, 0xd1, /* 0c: sub sp, sp, #0 */
2055 0x1f, 0x20, 0x03, 0xd5, /* 10: nop */
2056 0xff, 0x03, 0x00, 0x91, /* 14: add sp, sp, #0 */
2057 0xea, 0xaf, 0x41, 0x6d, /* 18: ldp d10, d11, [sp, #24] */
2058 0xe8, 0xa7, 0x40, 0x6d, /* 1c: ldp d8, d9, [sp, #8] */
2059 0xf3, 0x07, 0x43, 0xf8, /* 20: ldr x19, [sp], #48 */
2060 0xc0, 0x03, 0x5f, 0xd6, /* 24: ret */
2063 static const DWORD unwind_info_7_packed =
2064 (1 << 0) | /* Flag */
2065 (sizeof(function_7)/4 << 2) | /* FunctionLength */
2066 (3 << 13) | /* RegF */
2067 (1 << 16) | /* RegI */
2068 (0 << 20) | /* H */
2069 (0 << 21) | /* CR */
2070 (3 << 23); /* FrameSize */
2072 static const BYTE unwind_info_7[] = { DW(unwind_info_7_packed) };
2074 static const struct results_arm64 results_7[] =
2076 /* offset fp handler pc frame offset registers */
2077 { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
2078 { 0x04, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19, 0x00}, {-1,-1} }},
2079 { 0x08, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19, 0x00}, {d8, 0x08}, {d9, 0x10}, {-1,-1} }},
2080 { 0x0c, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19, 0x00}, {d8, 0x08}, {d9, 0x10}, {d10, 0x18}, {d11, 0x20}, {-1,-1} }},
2081 { 0x10, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19, 0x00}, {d8, 0x08}, {d9, 0x10}, {d10, 0x18}, {d11, 0x20}, {-1,-1} }},
2082 { 0x14, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19, 0x00}, {d8, 0x08}, {d9, 0x10}, {d10, 0x18}, {d11, 0x20}, {-1,-1} }},
2083 { 0x18, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19, 0x00}, {d8, 0x08}, {d9, 0x10}, {d10, 0x18}, {d11, 0x20}, {-1,-1} }},
2084 { 0x1c, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19, 0x00}, {d8, 0x08}, {d9, 0x10}, {-1,-1} }},
2085 { 0x20, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19, 0x00}, {-1,-1} }},
2086 { 0x24, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
2089 static const BYTE function_8[] =
2091 0xe8, 0x27, 0xbf, 0x6d, /* 00: stp d8, d9, [sp, #-16]! */
2092 0xff, 0x83, 0x00, 0xd1, /* 04: sub sp, sp, #32 */
2093 0x1f, 0x20, 0x03, 0xd5, /* 08: nop */
2094 0xff, 0x83, 0x00, 0x91, /* 0c: add sp, sp, #32 */
2095 0xe8, 0x27, 0xc1, 0x6c, /* 10: ldp d8, d9, [sp], #16 */
2096 0xc0, 0x03, 0x5f, 0xd6, /* 14: ret */
2099 static const DWORD unwind_info_8_packed =
2100 (1 << 0) | /* Flag */
2101 (sizeof(function_8)/4 << 2) | /* FunctionLength */
2102 (1 << 13) | /* RegF */
2103 (0 << 16) | /* RegI */
2104 (0 << 20) | /* H */
2105 (0 << 21) | /* CR */
2106 (3 << 23); /* FrameSize */
2108 static const BYTE unwind_info_8[] = { DW(unwind_info_8_packed) };
2110 static const struct results_arm64 results_8[] =
2112 /* offset fp handler pc frame offset registers */
2113 { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
2114 { 0x04, 0x00, 0, ORIG_LR, 0x010, TRUE, { {d8, 0x00}, {d9, 0x08}, {-1,-1} }},
2115 { 0x08, 0x00, 0, ORIG_LR, 0x030, TRUE, { {d8, 0x20}, {d9, 0x28}, {-1,-1} }},
2116 { 0x0c, 0x00, 0, ORIG_LR, 0x030, TRUE, { {d8, 0x20}, {d9, 0x28}, {-1,-1} }},
2117 { 0x10, 0x00, 0, ORIG_LR, 0x010, TRUE, { {d8, 0x00}, {d9, 0x08}, {-1,-1} }},
2118 { 0x14, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
2121 static const BYTE function_9[] =
2123 0xf3, 0x0f, 0x1b, 0xf8, /* 00: str x19, [sp, #-80]! */
2124 0xe0, 0x87, 0x00, 0xa9, /* 04: stp x0, x1, [sp, #8] */
2125 0xe2, 0x8f, 0x01, 0xa9, /* 08: stp x2, x3, [sp, #24] */
2126 0xe4, 0x97, 0x02, 0xa9, /* 0c: stp x4, x5, [sp, #40] */
2127 0xe6, 0x9f, 0x03, 0xa9, /* 10: stp x6, x7, [sp, #56] */
2128 0xff, 0x83, 0x00, 0xd1, /* 14: sub sp, sp, #32 */
2129 0x1f, 0x20, 0x03, 0xd5, /* 18: nop */
2130 0xff, 0x83, 0x00, 0x91, /* 1c: add sp, sp, #32 */
2131 0x1f, 0x20, 0x03, 0xd5, /* 20: nop */
2132 0x1f, 0x20, 0x03, 0xd5, /* 24: nop */
2133 0x1f, 0x20, 0x03, 0xd5, /* 28: nop */
2134 0x1f, 0x20, 0x03, 0xd5, /* 2c: nop */
2135 0xf3, 0x0f, 0x1b, 0xf8, /* 30: ldr x19, [sp], #80 */
2136 0xc0, 0x03, 0x5f, 0xd6, /* 34: ret */
2139 static const DWORD unwind_info_9_packed =
2140 (1 << 0) | /* Flag */
2141 (sizeof(function_9)/4 << 2) | /* FunctionLength */
2142 (0 << 13) | /* RegF */
2143 (1 << 16) | /* RegI */
2144 (1 << 20) | /* H */
2145 (0 << 21) | /* CR */
2146 (7 << 23); /* FrameSize */
2148 static const BYTE unwind_info_9[] = { DW(unwind_info_9_packed) };
2150 static const struct results_arm64 results_9[] =
2152 /* offset fp handler pc frame offset registers */
2153 { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
2154 { 0x04, 0x00, 0, ORIG_LR, 0x050, TRUE, { {x19, 0x00}, {-1,-1} }},
2155 { 0x08, 0x00, 0, ORIG_LR, 0x050, TRUE, { {x19, 0x00}, {-1,-1} }},
2156 { 0x0c, 0x00, 0, ORIG_LR, 0x050, TRUE, { {x19, 0x00}, {-1,-1} }},
2157 { 0x10, 0x00, 0, ORIG_LR, 0x050, TRUE, { {x19, 0x00}, {-1,-1} }},
2158 { 0x14, 0x00, 0, ORIG_LR, 0x050, TRUE, { {x19, 0x00}, {-1,-1} }},
2159 { 0x18, 0x00, 0, ORIG_LR, 0x070, TRUE, { {x19, 0x20}, {-1,-1} }},
2160 { 0x1c, 0x00, 0, ORIG_LR, 0x070, TRUE, { {x19, 0x20}, {-1,-1} }},
2161 { 0x20, 0x00, 0, ORIG_LR, 0x070, TRUE, { {x19, 0x20}, {-1,-1} }},
2162 { 0x24, 0x00, 0, ORIG_LR, 0x070, TRUE, { {x19, 0x20}, {-1,-1} }},
2163 { 0x28, 0x00, 0, ORIG_LR, 0x070, TRUE, { {x19, 0x20}, {-1,-1} }},
2164 { 0x2c, 0x00, 0, ORIG_LR, 0x070, TRUE, { {x19, 0x20}, {-1,-1} }},
2165 { 0x30, 0x00, 0, ORIG_LR, 0x050, TRUE, { {x19, 0x00}, {-1,-1} }},
2166 { 0x34, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
2169 static const BYTE function_10[] =
2171 0xfe, 0x0f, 0x1f, 0xf8, /* 00: str lr, [sp, #-16]! */
2172 0xff, 0x43, 0x00, 0xd1, /* 04: sub sp, sp, #16 */
2173 0x1f, 0x20, 0x03, 0xd5, /* 08: nop */
2174 0xff, 0x43, 0x00, 0x91, /* 0c: add sp, sp, #16 */
2175 0xfe, 0x07, 0x41, 0xf8, /* 10: ldr lr, [sp], #16 */
2176 0xc0, 0x03, 0x5f, 0xd6, /* 14: ret */
2179 static const DWORD unwind_info_10_packed =
2180 (1 << 0) | /* Flag */
2181 (sizeof(function_10)/4 << 2) | /* FunctionLength */
2182 (0 << 13) | /* RegF */
2183 (0 << 16) | /* RegI */
2184 (0 << 20) | /* H */
2185 (1 << 21) | /* CR */
2186 (2 << 23); /* FrameSize */
2188 static const BYTE unwind_info_10[] = { DW(unwind_info_10_packed) };
2190 static const struct results_arm64 results_10[] =
2192 /* offset fp handler pc frame offset registers */
2193 { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
2194 { 0x04, 0x00, 0, 0x00, 0x010, TRUE, { {lr, 0x00}, {-1,-1} }},
2195 { 0x08, 0x00, 0, 0x10, 0x020, TRUE, { {lr, 0x10}, {-1,-1} }},
2196 { 0x0c, 0x00, 0, 0x10, 0x020, TRUE, { {lr, 0x10}, {-1,-1} }},
2197 { 0x10, 0x00, 0, 0x00, 0x010, TRUE, { {lr, 0x00}, {-1,-1} }},
2198 { 0x14, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
2201 static const BYTE function_11[] =
2203 0xf3, 0x53, 0xbe, 0xa9, /* 00: stp x19, x20, [sp, #-32]! */
2204 0xf5, 0x7b, 0x01, 0xa9, /* 04: stp x21, lr, [sp, #16] */
2205 0xff, 0x43, 0x00, 0xd1, /* 08: sub sp, sp, #16 */
2206 0x1f, 0x20, 0x03, 0xd5, /* 0c: nop */
2207 0xff, 0x43, 0x00, 0x91, /* 10: add sp, sp, #16 */
2208 0xf5, 0x7b, 0x41, 0xa9, /* 14: ldp x21, lr, [sp, #16] */
2209 0xf3, 0x53, 0xc2, 0xa8, /* 18: ldp x19, x20, [sp], #32 */
2210 0xc0, 0x03, 0x5f, 0xd6, /* 1c: ret */
2213 static const DWORD unwind_info_11_packed =
2214 (1 << 0) | /* Flag */
2215 (sizeof(function_11)/4 << 2) | /* FunctionLength */
2216 (0 << 13) | /* RegF */
2217 (3 << 16) | /* RegI */
2218 (0 << 20) | /* H */
2219 (1 << 21) | /* CR */
2220 (3 << 23); /* FrameSize */
2222 static const BYTE unwind_info_11[] = { DW(unwind_info_11_packed) };
2224 static const struct results_arm64 results_11[] =
2226 /* offset fp handler pc frame offset registers */
2227 { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
2228 { 0x04, 0x00, 0, ORIG_LR, 0x020, TRUE, { {x19, 0x00}, {x20, 0x08}, {-1,-1} }},
2229 { 0x08, 0x00, 0, 0x18, 0x020, TRUE, { {x19, 0x00}, {x20, 0x08}, {x21, 0x10}, {lr, 0x18}, {-1,-1} }},
2230 { 0x0c, 0x00, 0, 0x28, 0x030, TRUE, { {x19, 0x10}, {x20, 0x18}, {x21, 0x20}, {lr, 0x28}, {-1,-1} }},
2231 { 0x10, 0x00, 0, 0x28, 0x030, TRUE, { {x19, 0x10}, {x20, 0x18}, {x21, 0x20}, {lr, 0x28}, {-1,-1} }},
2232 { 0x14, 0x00, 0, 0x18, 0x020, TRUE, { {x19, 0x00}, {x20, 0x08}, {x21, 0x10}, {lr, 0x18}, {-1,-1} }},
2233 { 0x18, 0x00, 0, ORIG_LR, 0x020, TRUE, { {x19, 0x00}, {x20, 0x08}, {-1,-1} }},
2234 { 0x1c, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
2237 static const BYTE function_12[] =
2239 0xf3, 0x53, 0xbf, 0xa9, /* 00: stp x19, x20, [sp, #-16]! */
2240 0xfd, 0x7b, 0xbe, 0xa9, /* 04: stp x29, lr, [sp, #-32]! */
2241 0xfd, 0x03, 0x00, 0x91, /* 08: mov x29, sp */
2242 0x1f, 0x20, 0x03, 0xd5, /* 0c: nop */
2243 0xbf, 0x03, 0x00, 0x91, /* 10: mov sp, x29 */
2244 0xfd, 0x7b, 0xc2, 0xa8, /* 14: ldp x29, lr, [sp], #32 */
2245 0xf3, 0x53, 0xc1, 0xa8, /* 18: ldp x19, x20, [sp], #16 */
2246 0xc0, 0x03, 0x5f, 0xd6, /* 1c: ret */
2249 static const DWORD unwind_info_12_packed =
2250 (1 << 0) | /* Flag */
2251 (sizeof(function_12)/4 << 2) | /* FunctionLength */
2252 (0 << 13) | /* RegF */
2253 (2 << 16) | /* RegI */
2254 (0 << 20) | /* H */
2255 (3 << 21) | /* CR */
2256 (3 << 23); /* FrameSize */
2258 static const BYTE unwind_info_12[] = { DW(unwind_info_12_packed) };
2260 static const struct results_arm64 results_12[] =
2262 /* offset fp handler pc frame offset registers */
2263 { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
2264 { 0x04, 0x10, 0, ORIG_LR, 0x010, TRUE, { {x19, 0x00}, {x20, 0x08}, {-1,-1} }},
2265 { 0x08, 0x10, 0, 0x08, 0x030, TRUE, { {x19, 0x20}, {x20, 0x28}, {x29, 0x00}, {lr, 0x08}, {-1,-1} }},
2266 { 0x0c, 0x10, 0, 0x18, 0x040, TRUE, { {x19, 0x30}, {x20, 0x38}, {x29, 0x10}, {lr, 0x18}, {-1,-1} }},
2267 { 0x10, 0x10, 0, 0x18, 0x040, TRUE, { {x19, 0x30}, {x20, 0x38}, {x29, 0x10}, {lr, 0x18}, {-1,-1} }},
2268 { 0x14, 0x10, 0, 0x08, 0x030, TRUE, { {x19, 0x20}, {x20, 0x28}, {x29, 0x00}, {lr, 0x08}, {-1,-1} }},
2269 { 0x18, 0x10, 0, ORIG_LR, 0x010, TRUE, { {x19, 0x00}, {x20, 0x08}, {-1,-1} }},
2270 { 0x1c, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
2273 static const BYTE function_13[] =
2275 0xf3, 0x53, 0xbf, 0xa9, /* 00: stp x19, x20, [sp, #-16]! */
2276 0xff, 0x43, 0x08, 0xd1, /* 04: sub sp, sp, #528 */
2277 0xfd, 0x7b, 0x00, 0xd1, /* 08: stp x29, lr, [sp] */
2278 0xfd, 0x03, 0x00, 0x91, /* 0c: mov x29, sp */
2279 0x1f, 0x20, 0x03, 0xd5, /* 10: nop */
2280 0xbf, 0x03, 0x00, 0x91, /* 14: mov sp, x29 */
2281 0xfd, 0x7b, 0x40, 0xa9, /* 18: ldp x29, lr, [sp] */
2282 0xff, 0x43, 0x08, 0x91, /* 1c: add sp, sp, #528 */
2283 0xf3, 0x53, 0xc1, 0xa8, /* 20: ldp x19, x20, [sp], #16 */
2284 0xc0, 0x03, 0x5f, 0xd6, /* 24: ret */
2287 static const DWORD unwind_info_13_packed =
2288 (1 << 0) | /* Flag */
2289 (sizeof(function_13)/4 << 2) | /* FunctionLength */
2290 (0 << 13) | /* RegF */
2291 (2 << 16) | /* RegI */
2292 (0 << 20) | /* H */
2293 (3 << 21) | /* CR */
2294 (34 << 23); /* FrameSize */
2296 static const BYTE unwind_info_13[] = { DW(unwind_info_13_packed) };
2298 static const struct results_arm64 results_13[] =
2300 /* offset fp handler pc frame offset registers */
2301 { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
2302 { 0x04, 0x10, 0, ORIG_LR, 0x010, TRUE, { {x19, 0x00}, {x20, 0x08}, {-1,-1} }},
2303 { 0x08, 0x10, 0, ORIG_LR, 0x220, TRUE, { {x19, 0x210}, {x20, 0x218}, {-1,-1} }},
2304 { 0x0c, 0x10, 0, 0x08, 0x220, TRUE, { {x19, 0x210}, {x20, 0x218}, {x29, 0x00}, {lr, 0x08}, {-1,-1} }},
2305 { 0x10, 0x10, 0, 0x18, 0x230, TRUE, { {x19, 0x220}, {x20, 0x228}, {x29, 0x10}, {lr, 0x18}, {-1,-1} }},
2306 { 0x14, 0x10, 0, 0x18, 0x230, TRUE, { {x19, 0x220}, {x20, 0x228}, {x29, 0x10}, {lr, 0x18}, {-1,-1} }},
2307 { 0x18, 0x10, 0, 0x08, 0x220, TRUE, { {x19, 0x210}, {x20, 0x218}, {x29, 0x00}, {lr, 0x08}, {-1,-1} }},
2308 { 0x1c, 0x10, 0, ORIG_LR, 0x220, TRUE, { {x19, 0x210}, {x20, 0x218}, {-1,-1} }},
2309 { 0x20, 0x10, 0, ORIG_LR, 0x010, TRUE, { {x19, 0x00}, {x20, 0x08}, {-1,-1} }},
2310 { 0x24, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
2313 static const BYTE function_14[] =
2315 0xe6, 0x9f, 0xba, 0xad, /* 00: stp q6, q7, [sp, #-0xb0]! */
2316 0xe8, 0x27, 0x01, 0xad, /* 04: stp q8, q9, [sp, #0x20] */
2317 0xea, 0x2f, 0x02, 0xad, /* 08: stp q10, q11, [sp, #0x40] */
2318 0xec, 0x37, 0x03, 0xad, /* 0c: stp q12, q13, [sp, #0x60] */
2319 0xee, 0x3f, 0x04, 0xad, /* 10: stp q14, q15, [sp, #0x80] */
2320 0xfd, 0x7b, 0x0a, 0xa9, /* 14: stp x29, x30, [sp, #0xa0] */
2321 0xfd, 0x83, 0x02, 0x91, /* 18: add x29, sp, #0xa0 */
2322 0x1f, 0x20, 0x03, 0xd5, /* 1c: nop */
2323 0xfd, 0x7b, 0x4a, 0xa9, /* 20: ldp x29, x30, [sp, #0xa0] */
2324 0xee, 0x3f, 0x44, 0xad, /* 24: ldp q14, q15, [sp, #0x80] */
2325 0xec, 0x37, 0x43, 0xad, /* 28: ldp q12, q13, [sp, #0x60] */
2326 0xea, 0x2f, 0x42, 0xad, /* 2c: ldp q10, q11, [sp, #0x40] */
2327 0xe8, 0x27, 0x41, 0xad, /* 30: ldp q8, q9, [sp, #0x20] */
2328 0xe6, 0x9f, 0xc5, 0xac, /* 34: ldp q6, q7, [sp], #0xb0 */
2329 0xc0, 0x03, 0x5f, 0xd6, /* 38: ret */
2332 static const DWORD unwind_info_14_header =
2333 (sizeof(function_14)/4) | /* function length */
2334 (0 << 20) | /* X */
2335 (1 << 21) | /* E */
2336 (2 << 22) | /* epilog */
2337 (5 << 27); /* codes */
2339 static const BYTE unwind_info_14[] =
2341 DW(unwind_info_14_header),
2342 UWOP_ADD_FP(0xa0), /* 18: add x29, sp, #0xa0 */
2343 UWOP_SAVE_FPLR(0xa0), /* 14: stp x29, x30, [sp, #0xa0] */
2344 UWOP_SAVE_ANY_REG(0x4e,0x88), /* 10: stp q14, q15, [sp, #0x80] */
2345 UWOP_SAVE_NEXT, /* 0c: stp q12, q13, [sp, #0x60] */
2346 UWOP_SAVE_ANY_REG(0x4a,0x84), /* 08: stp q10, q11, [sp, #0x40] */
2347 UWOP_SAVE_ANY_REG(0x48,0x82), /* 04: stp q8, q9, [sp, #0x20] */
2348 UWOP_SAVE_ANY_REG(0x66,0x8a), /* 00: stp q6, q7, [sp, #-0xb0]! */
2349 UWOP_END,
2350 UWOP_NOP /* padding */
2353 static const struct results_arm64 results_14[] =
2355 /* offset fp handler pc frame offset registers */
2356 { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
2357 { 0x04, 0x00, 0, ORIG_LR, 0x0b0, TRUE, { {d6, 0x00}, {d7, 0x10}, {-1,-1} }},
2358 { 0x08, 0x00, 0, ORIG_LR, 0x0b0, TRUE, { {d6, 0x00}, {d7, 0x10}, {d8, 0x20}, {d9, 0x30}, {-1,-1} }},
2359 { 0x0c, 0x00, 0, ORIG_LR, 0x0b0, TRUE, { {d6, 0x00}, {d7, 0x10}, {d8, 0x20}, {d9, 0x30}, {d10, 0x40}, {d11, 0x50}, {-1,-1} }},
2360 { 0x10, 0x00, 0, ORIG_LR, 0x0b0, TRUE, { {d6, 0x00}, {d7, 0x10}, {d8, 0x20}, {d9, 0x30}, {d10, 0x40}, {d11, 0x50}, {d12, 0x60}, {d13, 0x70}, {-1,-1} }},
2361 { 0x14, 0x00, 0, ORIG_LR, 0x0b0, TRUE, { {d6, 0x00}, {d7, 0x10}, {d8, 0x20}, {d9, 0x30}, {d10, 0x40}, {d11, 0x50}, {d12, 0x60}, {d13, 0x70}, {d14, 0x80}, {d15, 0x90}, {-1,-1} }},
2362 { 0x18, 0x00, 0, 0xa8, 0x0b0, TRUE, { {d6, 0x00}, {d7, 0x10}, {d8, 0x20}, {d9, 0x30}, {d10, 0x40}, {d11, 0x50}, {d12, 0x60}, {d13, 0x70}, {d14, 0x80}, {d15, 0x90}, {lr, 0xa8}, {x29, 0xa0}, {-1,-1} }},
2363 { 0x1c, 0xa0, 0, 0xa8, 0x0b0, TRUE, { {d6, 0x00}, {d7, 0x10}, {d8, 0x20}, {d9, 0x30}, {d10, 0x40}, {d11, 0x50}, {d12, 0x60}, {d13, 0x70}, {d14, 0x80}, {d15, 0x90}, {lr, 0xa8}, {x29, 0xa0}, {-1,-1} }},
2366 static const BYTE function_15[] =
2368 0x1f, 0x20, 0x03, 0xd5, /* 00: nop */
2369 0x1f, 0x20, 0x03, 0xd5, /* 04: nop */
2370 0x1f, 0x20, 0x03, 0xd5, /* 08: nop */
2371 0x1f, 0x20, 0x03, 0xd5, /* 0c: nop */
2372 0x1f, 0x20, 0x03, 0xd5, /* 10: nop */
2373 0xc0, 0x03, 0x5f, 0xd6, /* 14: ret */
2376 static const DWORD unwind_info_15_header =
2377 (sizeof(function_15)/4) | /* function length */
2378 (0 << 20) | /* X */
2379 (0 << 21) | /* E */
2380 (0 << 22) | /* epilog */
2381 (2 << 27); /* codes */
2383 static const BYTE unwind_info_15[] =
2385 DW(unwind_info_15_header),
2386 UWOP_END_C,
2387 UWOP_SET_FP, /* mov x29, sp */
2388 UWOP_SAVE_REGP(19, 0x10), /* stp r19, r20, [sp, #0x10] */
2389 UWOP_SAVE_FPLR_X(0x20), /* stp r29, lr, [sp,-#0x20]! */
2390 UWOP_END,
2391 UWOP_NOP, /* padding */
2392 UWOP_NOP, /* padding */
2395 static const struct results_arm64 results_15[] =
2397 /* offset fp handler pc frame offset registers */
2398 { 0x00, 0x00, 0, 0x08, 0x020, TRUE, { {x29, 0x00}, {lr, 0x08}, {x19,0x10}, {x20,0x18}, {-1,-1} }},
2399 { 0x04, 0x00, 0, 0x08, 0x020, TRUE, { {x29, 0x00}, {lr, 0x08}, {x19,0x10}, {x20,0x18}, {-1,-1} }},
2400 { 0x08, 0x00, 0, 0x08, 0x020, TRUE, { {x29, 0x00}, {lr, 0x08}, {x19,0x10}, {x20,0x18}, {-1,-1} }},
2401 { 0x0c, 0x00, 0, 0x08, 0x020, TRUE, { {x29, 0x00}, {lr, 0x08}, {x19,0x10}, {x20,0x18}, {-1,-1} }},
2402 { 0x10, 0x00, 0, 0x08, 0x020, TRUE, { {x29, 0x00}, {lr, 0x08}, {x19,0x10}, {x20,0x18}, {-1,-1} }},
2403 { 0x14, 0x00, 0, 0x08, 0x020, TRUE, { {x29, 0x00}, {lr, 0x08}, {x19,0x10}, {x20,0x18}, {-1,-1} }},
2406 static const BYTE function_16[] =
2408 0xff, 0x43, 0x00, 0xd1, /* 00: sub sp, sp, #16 */
2409 0x1f, 0x20, 0x03, 0xd5, /* 04: nop */
2410 0xff, 0x43, 0x00, 0xd1, /* 08: sub sp, sp, #16 */
2411 0x1f, 0x20, 0x03, 0xd5, /* 0c: nop */
2412 0xc0, 0x03, 0x5f, 0xd6, /* 10: ret */
2415 static const DWORD unwind_info_16_header =
2416 (sizeof(function_16)/4) | /* function length */
2417 (0 << 20) | /* X */
2418 (0 << 21) | /* E */
2419 (0 << 22) | /* epilog */
2420 (1 << 27); /* codes */
2422 static const BYTE unwind_info_16[] =
2424 DW(unwind_info_16_header),
2426 UWOP_ALLOC_SMALL(16), /* sub sp, sp, #16 */
2427 UWOP_EC_CONTEXT,
2428 UWOP_ALLOC_SMALL(16), /* sub sp, sp, #16 */
2429 UWOP_END,
2432 static const struct results_arm64 results_16[] =
2434 /* offset fp handler pc frame offset registers */
2435 { 0x00, 0x00, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }},
2436 { 0x04, 0x00, 0 , 0x00f8, 0x0a8, FALSE, { {x0, 0x80}, {x1, 0x88}, {x2, 0xb8}, {x3, 0xc0}, {x4, 0xc8}, {x5, 0xd0}, {x6, 0x130}, {x7, 0x140}, {x8, 0x78}, {x9, 0x150}, {x10, 0x160}, {x11, 0x170}, {x12, 0x180}, {x13, 0}, {x14, 0}, {x15, 0x190}, {x16, 0x0158014801380128}, {x17, 0x0198018801780168}, {x18, 0}, {x19, 0xd8}, {x20, 0xe0}, {x21, 0xe8}, {x22, 0x0f0}, {x23, 0}, {x24, 0}, {x25, 0xa8}, {x26, 0xb0}, {x27, 0x90}, {x28, 0}, {x29, 0xa0}, {lr, 0x120}, {d0, 0x1a0}, {d1, 0x1b0}, {d2, 0x1c0}, {d3, 0x1d0}, {d4, 0x1e0}, {d5, 0x1f0}, {d6, 0x200}, {d7, 0x210}, {d8, 0x220}, {d9, 0x230}, {d10, 0x240}, {d11, 0x250}, {d12, 0x260}, {d13, 0x270}, {d14, 0x280}, {d15, 0x290}, {-1,-1} }},
2437 { 0x08, 0x00, 0 , 0x0108, 0x0b8, FALSE, { {x0, 0x90}, {x1, 0x98}, {x2, 0xc8}, {x3, 0xd0}, {x4, 0xd8}, {x5, 0xe0}, {x6, 0x140}, {x7, 0x150}, {x8, 0x88}, {x9, 0x160}, {x10, 0x170}, {x11, 0x180}, {x12, 0x190}, {x13, 0}, {x14, 0}, {x15, 0x1a0}, {x16, 0x0168015801480138}, {x17, 0x01a8019801880178}, {x18, 0}, {x19, 0xe8}, {x20, 0xf0}, {x21, 0xf8}, {x22, 0x100}, {x23, 0}, {x24, 0}, {x25, 0xb8}, {x26, 0xc0}, {x27, 0xa0}, {x28, 0}, {x29, 0xb0}, {lr, 0x130}, {d0, 0x1b0}, {d1, 0x1c0}, {d2, 0x1d0}, {d3, 0x1e0}, {d4, 0x1f0}, {d5, 0x200}, {d6, 0x210}, {d7, 0x220}, {d8, 0x230}, {d9, 0x240}, {d10, 0x250}, {d11, 0x260}, {d12, 0x270}, {d13, 0x280}, {d14, 0x290}, {d15, 0x2a0}, {-1,-1} }},
2438 { 0x0c, 0x00, 0 , 0x0108, 0x0b8, FALSE, { {x0, 0x90}, {x1, 0x98}, {x2, 0xc8}, {x3, 0xd0}, {x4, 0xd8}, {x5, 0xe0}, {x6, 0x140}, {x7, 0x150}, {x8, 0x88}, {x9, 0x160}, {x10, 0x170}, {x11, 0x180}, {x12, 0x190}, {x13, 0}, {x14, 0}, {x15, 0x1a0}, {x16, 0x0168015801480138}, {x17, 0x01a8019801880178}, {x18, 0}, {x19, 0xe8}, {x20, 0xf0}, {x21, 0xf8}, {x22, 0x100}, {x23, 0}, {x24, 0}, {x25, 0xb8}, {x26, 0xc0}, {x27, 0xa0}, {x28, 0}, {x29, 0xb0}, {lr, 0x130}, {d0, 0x1b0}, {d1, 0x1c0}, {d2, 0x1d0}, {d3, 0x1e0}, {d4, 0x1f0}, {d5, 0x200}, {d6, 0x210}, {d7, 0x220}, {d8, 0x230}, {d9, 0x240}, {d10, 0x250}, {d11, 0x260}, {d12, 0x270}, {d13, 0x280}, {d14, 0x290}, {d15, 0x2a0}, {-1,-1} }},
2439 { 0x10, 0x00, 0 , 0x0108, 0x0b8, FALSE, { {x0, 0x90}, {x1, 0x98}, {x2, 0xc8}, {x3, 0xd0}, {x4, 0xd8}, {x5, 0xe0}, {x6, 0x140}, {x7, 0x150}, {x8, 0x88}, {x9, 0x160}, {x10, 0x170}, {x11, 0x180}, {x12, 0x190}, {x13, 0}, {x14, 0}, {x15, 0x1a0}, {x16, 0x0168015801480138}, {x17, 0x01a8019801880178}, {x18, 0}, {x19, 0xe8}, {x20, 0xf0}, {x21, 0xf8}, {x22, 0x100}, {x23, 0}, {x24, 0}, {x25, 0xb8}, {x26, 0xc0}, {x27, 0xa0}, {x28, 0}, {x29, 0xb0}, {lr, 0x130}, {d0, 0x1b0}, {d1, 0x1c0}, {d2, 0x1d0}, {d3, 0x1e0}, {d4, 0x1f0}, {d5, 0x200}, {d6, 0x210}, {d7, 0x220}, {d8, 0x230}, {d9, 0x240}, {d10, 0x250}, {d11, 0x260}, {d12, 0x270}, {d13, 0x280}, {d14, 0x290}, {d15, 0x2a0}, {-1,-1} }},
2442 static const BYTE function_17[] =
2444 0xff, 0x43, 0x00, 0xd1, /* 00: sub sp, sp, #16 */
2445 0xff, 0x43, 0x00, 0xd1, /* 04: sub sp, sp, #16 */
2446 0x1f, 0x20, 0x03, 0xd5, /* 08: nop */
2447 0xc0, 0x03, 0x5f, 0xd6, /* 0c: ret */
2450 static const DWORD unwind_info_17_header =
2451 (sizeof(function_17)/4) | /* function length */
2452 (0 << 20) | /* X */
2453 (0 << 21) | /* E */
2454 (0 << 22) | /* epilog */
2455 (1 << 27); /* codes */
2457 static const BYTE unwind_info_17[] =
2459 DW(unwind_info_17_header),
2461 UWOP_CLEAR_UNWOUND_TO_CALL,
2462 UWOP_ALLOC_SMALL(16), /* sub sp, sp, #16 */
2463 UWOP_ALLOC_SMALL(16), /* sub sp, sp, #16 */
2464 UWOP_END,
2467 static const struct results_arm64 results_17[] =
2469 /* offset fp handler pc frame offset registers */
2470 { 0x00, 0x00, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }},
2471 { 0x04, 0x00, 0, ORIG_LR, 0x020, TRUE, { {-1,-1} }},
2472 { 0x08, 0x00, 0, ORIG_LR, 0x020, TRUE, { {-1,-1} }},
2473 { 0x0c, 0x00, 0, ORIG_LR, 0x020, TRUE, { {-1,-1} }},
2476 static const struct unwind_test_arm64 tests[] =
2478 #define TEST(func, unwind, unwind_packed, results, unwound_clear, last_ptr) \
2479 { func, sizeof(func), unwind, unwind_packed ? 0 : sizeof(unwind), results, ARRAY_SIZE(results), unwound_clear, last_ptr }
2480 TEST(function_0, unwind_info_0, 0, results_0, 0, 0),
2481 TEST(function_1, unwind_info_1, 1, results_1, 0, 0),
2482 TEST(function_2, unwind_info_2, 0, results_2, 1, 0),
2483 TEST(function_3, unwind_info_3, 0, results_3, 1, x28),
2484 TEST(function_4, unwind_info_4, 0, results_4, 0, 0),
2485 TEST(function_5, unwind_info_5, 0, results_5, 0, 0),
2486 TEST(function_6, unwind_info_6, 1, results_6, 0, 0),
2487 TEST(function_7, unwind_info_7, 1, results_7, 0, 0),
2488 TEST(function_8, unwind_info_8, 1, results_8, 0, 0),
2489 TEST(function_9, unwind_info_9, 1, results_9, 0, 0),
2490 TEST(function_10, unwind_info_10, 1, results_10, 0, 0),
2491 TEST(function_11, unwind_info_11, 1, results_11, 0, 0),
2492 TEST(function_12, unwind_info_12, 1, results_12, 0, 0),
2493 TEST(function_13, unwind_info_13, 1, results_13, 0, 0),
2494 TEST(function_14, unwind_info_14, 0, results_14, 0, 0),
2495 TEST(function_15, unwind_info_15, 0, results_15, 0, 0),
2496 TEST(function_16, unwind_info_16, 0, results_16, 1, x18),
2497 TEST(function_17, unwind_info_17, 0, results_17, 2, 0),
2498 #undef TEST
2500 unsigned int i;
2502 #ifdef __x86_64__
2503 void *code_mem = NULL;
2504 SIZE_T code_size = 0x10000;
2505 MEM_EXTENDED_PARAMETER param = { 0 };
2507 param.Type = MemExtendedParameterAttributeFlags;
2508 param.ULong64 = MEM_EXTENDED_PARAMETER_EC_CODE;
2509 if (!pNtAllocateVirtualMemoryEx ||
2510 pNtAllocateVirtualMemoryEx( GetCurrentProcess(), &code_mem, &code_size, MEM_RESERVE | MEM_COMMIT,
2511 PAGE_EXECUTE_READWRITE, &param, 1 ))
2512 return;
2513 trace( "running arm64ec tests\n" );
2514 #endif
2516 for (i = 0; i < ARRAY_SIZE(tests); i++)
2517 call_virtual_unwind_arm64( code_mem, i, &tests[i] );
2520 #undef UWOP_ALLOC_SMALL
2521 #undef UWOP_ALLOC_LARGE
2523 #endif /* __aarch64__ || __x86_64__ */
2525 #ifdef __x86_64__
2527 #define UWOP_PUSH_NONVOL 0
2528 #define UWOP_ALLOC_LARGE 1
2529 #define UWOP_ALLOC_SMALL 2
2530 #define UWOP_SET_FPREG 3
2531 #define UWOP_SAVE_NONVOL 4
2532 #define UWOP_SAVE_NONVOL_FAR 5
2533 #define UWOP_SAVE_XMM128 8
2534 #define UWOP_SAVE_XMM128_FAR 9
2535 #define UWOP_PUSH_MACHFRAME 10
2537 struct results_x86
2539 int rip_offset; /* rip offset from code start */
2540 int rbp_offset; /* rbp offset from stack pointer */
2541 int handler; /* expect handler to be set? */
2542 int rip; /* expected final rip value */
2543 int frame; /* expected frame return value */
2544 int regs[8][2]; /* expected values for registers */
2547 struct unwind_test_x86
2549 const BYTE *function;
2550 size_t function_size;
2551 const BYTE *unwind_info;
2552 const struct results_x86 *results;
2553 unsigned int nb_results;
2554 const struct results_x86 *broken_results;
2557 enum regs
2559 rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi,
2560 r8, r9, r10, r11, r12, r13, r14, r15
2563 static const char * const reg_names_x86[16] =
2565 "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
2566 "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"
2569 #define UWOP(code,info) (UWOP_##code | ((info) << 4))
2571 static void call_virtual_unwind_x86( int testnum, const struct unwind_test_x86 *test )
2573 static const int code_offset = 1024;
2574 static const int unwind_offset = 2048;
2575 void *handler, *data;
2576 CONTEXT context;
2577 RUNTIME_FUNCTION runtime_func;
2578 KNONVOLATILE_CONTEXT_POINTERS ctx_ptr;
2579 UINT i, j, k, broken_k;
2580 ULONG64 fake_stack[256];
2581 ULONG64 frame, orig_rip, orig_rbp, unset_reg;
2582 UINT unwind_size = 4 + 2 * test->unwind_info[2] + 8;
2583 void *expected_handler, *broken_handler;
2585 memcpy( (char *)code_mem + code_offset, test->function, test->function_size );
2586 memcpy( (char *)code_mem + unwind_offset, test->unwind_info, unwind_size );
2588 runtime_func.BeginAddress = code_offset;
2589 runtime_func.EndAddress = code_offset + test->function_size;
2590 runtime_func.UnwindData = unwind_offset;
2592 trace( "code: %p stack: %p\n", code_mem, fake_stack );
2594 for (i = 0; i < test->nb_results; i++)
2596 memset( &ctx_ptr, 0, sizeof(ctx_ptr) );
2597 memset( &context, 0x55, sizeof(context) );
2598 memset( &unset_reg, 0x55, sizeof(unset_reg) );
2599 for (j = 0; j < 256; j++) fake_stack[j] = j * 8;
2601 context.Rsp = (ULONG_PTR)fake_stack;
2602 context.Rbp = (ULONG_PTR)fake_stack + test->results[i].rbp_offset;
2603 orig_rbp = context.Rbp;
2604 orig_rip = (ULONG64)code_mem + code_offset + test->results[i].rip_offset;
2606 trace( "%u/%u: rip=%p (%02x) rbp=%p rsp=%p\n", testnum, i,
2607 (void *)orig_rip, *(BYTE *)orig_rip, (void *)orig_rbp, (void *)context.Rsp );
2609 data = (void *)0xdeadbeef;
2610 handler = RtlVirtualUnwind( UNW_FLAG_EHANDLER, (ULONG64)code_mem, orig_rip,
2611 &runtime_func, &context, &data, &frame, &ctx_ptr );
2613 expected_handler = test->results[i].handler ? (char *)code_mem + 0x200 : NULL;
2614 broken_handler = test->broken_results && test->broken_results[i].handler ? (char *)code_mem + 0x200 : NULL;
2616 ok( handler == expected_handler || broken( test->broken_results && handler == broken_handler ),
2617 "%u/%u: wrong handler %p/%p\n", testnum, i, handler, expected_handler );
2618 if (handler)
2619 ok( *(DWORD *)data == 0x08070605, "%u/%u: wrong handler data %lx\n", testnum, i, *(DWORD *)data );
2620 else
2621 ok( data == (void *)0xdeadbeef, "%u/%u: handler data set to %p\n", testnum, i, data );
2623 ok( context.Rip == test->results[i].rip
2624 || broken( test->broken_results && context.Rip == test->broken_results[i].rip ),
2625 "%u/%u: wrong rip %p/%x\n", testnum, i, (void *)context.Rip, test->results[i].rip );
2626 ok( frame == (ULONG64)fake_stack + test->results[i].frame
2627 || broken( test->broken_results && frame == (ULONG64)fake_stack + test->broken_results[i].frame ),
2628 "%u/%u: wrong frame %p/%p\n",
2629 testnum, i, (void *)frame, (char *)fake_stack + test->results[i].frame );
2631 for (j = 0; j < 16; j++)
2633 static const UINT nb_regs = ARRAY_SIZE(test->results[i].regs);
2635 for (k = 0; k < nb_regs; k++)
2637 if (test->results[i].regs[k][0] == -1)
2639 k = nb_regs;
2640 break;
2642 if (test->results[i].regs[k][0] == j) break;
2645 if (test->broken_results)
2647 for (broken_k = 0; broken_k < nb_regs; broken_k++)
2649 if (test->broken_results[i].regs[broken_k][0] == -1)
2651 broken_k = nb_regs;
2652 break;
2654 if (test->broken_results[i].regs[broken_k][0] == j)
2655 break;
2658 else
2660 broken_k = k;
2663 if (j == rsp) /* rsp is special */
2665 ULONG64 expected_rsp, broken_rsp;
2667 ok( !ctx_ptr.IntegerContext[j],
2668 "%u/%u: rsp should not be set in ctx_ptr\n", testnum, i );
2669 expected_rsp = test->results[i].regs[k][1] < 0
2670 ? -test->results[i].regs[k][1] : (ULONG64)fake_stack + test->results[i].regs[k][1];
2671 if (test->broken_results)
2672 broken_rsp = test->broken_results[i].regs[k][1] < 0
2673 ? -test->broken_results[i].regs[k][1]
2674 : (ULONG64)fake_stack + test->broken_results[i].regs[k][1];
2675 else
2676 broken_rsp = expected_rsp;
2678 ok( context.Rsp == expected_rsp || broken( context.Rsp == broken_rsp ),
2679 "%u/%u: register rsp wrong %p/%p\n",
2680 testnum, i, (void *)context.Rsp, (void *)expected_rsp );
2681 continue;
2684 if (ctx_ptr.IntegerContext[j])
2686 ok( k < nb_regs || broken( broken_k < nb_regs ), "%u/%u: register %s should not be set to %Ix\n",
2687 testnum, i, reg_names_x86[j], *(&context.Rax + j) );
2688 ok( k == nb_regs || *(&context.Rax + j) == test->results[i].regs[k][1]
2689 || broken( broken_k == nb_regs || *(&context.Rax + j)
2690 == test->broken_results[i].regs[broken_k][1] ),
2691 "%u/%u: register %s wrong %p/%x\n",
2692 testnum, i, reg_names_x86[j], (void *)*(&context.Rax + j), test->results[i].regs[k][1] );
2694 else
2696 ok( k == nb_regs || broken( broken_k == nb_regs ), "%u/%u: register %s should be set\n",
2697 testnum, i, reg_names_x86[j] );
2698 if (j == rbp)
2699 ok( context.Rbp == orig_rbp, "%u/%u: register rbp wrong %p/unset\n",
2700 testnum, i, (void *)context.Rbp );
2701 else
2702 ok( *(&context.Rax + j) == unset_reg,
2703 "%u/%u: register %s wrong %p/unset\n",
2704 testnum, i, reg_names_x86[j], (void *)*(&context.Rax + j));
2710 static void test_virtual_unwind_x86(void)
2712 static const BYTE function_0[] =
2714 0xff, 0xf5, /* 00: push %rbp */
2715 0x48, 0x81, 0xec, 0x10, 0x01, 0x00, 0x00, /* 02: sub $0x110,%rsp */
2716 0x48, 0x8d, 0x6c, 0x24, 0x30, /* 09: lea 0x30(%rsp),%rbp */
2717 0x48, 0x89, 0x9d, 0xf0, 0x00, 0x00, 0x00, /* 0e: mov %rbx,0xf0(%rbp) */
2718 0x48, 0x89, 0xb5, 0xf8, 0x00, 0x00, 0x00, /* 15: mov %rsi,0xf8(%rbp) */
2719 0x90, /* 1c: nop */
2720 0x48, 0x8b, 0x9d, 0xf0, 0x00, 0x00, 0x00, /* 1d: mov 0xf0(%rbp),%rbx */
2721 0x48, 0x8b, 0xb5, 0xf8, 0x00, 0x00, 0x00, /* 24: mov 0xf8(%rbp),%rsi */
2722 0x48, 0x8d, 0xa5, 0xe0, 0x00, 0x00, 0x00, /* 2b: lea 0xe0(%rbp),%rsp */
2723 0x5d, /* 32: pop %rbp */
2724 0xc3 /* 33: ret */
2727 static const BYTE unwind_info_0[] =
2729 1 | (UNW_FLAG_EHANDLER << 3), /* version + flags */
2730 0x1c, /* prolog size */
2731 8, /* opcode count */
2732 (0x03 << 4) | rbp, /* frame reg rbp offset 0x30 */
2734 0x1c, UWOP(SAVE_NONVOL, rsi), 0x25, 0, /* 1c: mov %rsi,0x128(%rsp) */
2735 0x15, UWOP(SAVE_NONVOL, rbx), 0x24, 0, /* 15: mov %rbx,0x120(%rsp) */
2736 0x0e, UWOP(SET_FPREG, rbp), /* 0e: lea 0x30(%rsp),rbp */
2737 0x09, UWOP(ALLOC_LARGE, 0), 0x22, 0, /* 09: sub $0x110,%rsp */
2738 0x02, UWOP(PUSH_NONVOL, rbp), /* 02: push %rbp */
2740 0x00, 0x02, 0x00, 0x00, /* handler */
2741 0x05, 0x06, 0x07, 0x08, /* data */
2744 static const struct results_x86 results_0[] =
2746 /* offset rbp handler rip frame registers */
2747 { 0x00, 0x40, FALSE, 0x000, 0x000, { {rsp,0x008}, {-1,-1} }},
2748 { 0x02, 0x40, FALSE, 0x008, 0x000, { {rsp,0x010}, {rbp,0x000}, {-1,-1} }},
2749 { 0x09, 0x40, FALSE, 0x118, 0x000, { {rsp,0x120}, {rbp,0x110}, {-1,-1} }},
2750 { 0x0e, 0x40, FALSE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {-1,-1} }},
2751 { 0x15, 0x40, FALSE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {-1,-1} }},
2752 { 0x1c, 0x40, TRUE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {rsi,0x138}, {-1,-1}}},
2753 { 0x1d, 0x40, TRUE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {rsi,0x138}, {-1,-1}}},
2754 { 0x24, 0x40, TRUE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {rsi,0x138}, {-1,-1}}},
2755 { 0x2b, 0x40, FALSE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {-1,-1}}},
2756 { 0x32, 0x40, FALSE, 0x008, 0x010, { {rsp,0x010}, {rbp,0x000}, {-1,-1}}},
2757 { 0x33, 0x40, FALSE, 0x000, 0x010, { {rsp,0x008}, {-1,-1}}},
2760 static const struct results_x86 broken_results_0[] =
2762 /* offset rbp handler rip frame registers */
2763 { 0x00, 0x40, FALSE, 0x000, 0x000, { {rsp,0x008}, {-1,-1} }},
2764 { 0x02, 0x40, FALSE, 0x008, 0x000, { {rsp,0x010}, {rbp,0x000}, {-1,-1} }},
2765 { 0x09, 0x40, FALSE, 0x118, 0x000, { {rsp,0x120}, {rbp,0x110}, {-1,-1} }},
2766 { 0x0e, 0x40, FALSE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {-1,-1} }},
2767 { 0x15, 0x40, FALSE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {-1,-1} }},
2768 { 0x1c, 0x40, TRUE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {rsi,0x138}, {-1,-1}}},
2769 { 0x1d, 0x40, TRUE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {rsi,0x138}, {-1,-1}}},
2770 { 0x24, 0x40, TRUE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {rsi,0x138}, {-1,-1}}},
2771 /* On Win11 output frame in epilogue corresponds to context->Rsp - 0x8 when fpreg is set. */
2772 { 0x2b, 0x40, FALSE, 0x128, 0x128, { {rsp,0x130}, {rbp,0x120}, {-1,-1}}},
2773 { 0x32, 0x40, FALSE, 0x008, 0x008, { {rsp,0x010}, {rbp,0x000}, {-1,-1}}},
2774 { 0x33, 0x40, FALSE, 0x000, 0x000, { {rsp,0x008}, {-1,-1}}},
2777 static const BYTE function_1[] =
2779 0x53, /* 00: push %rbx */
2780 0x55, /* 01: push %rbp */
2781 0x56, /* 02: push %rsi */
2782 0x57, /* 03: push %rdi */
2783 0x41, 0x54, /* 04: push %r12 */
2784 0x48, 0x83, 0xec, 0x30, /* 06: sub $0x30,%rsp */
2785 0x90, 0x90, /* 0a: nop; nop */
2786 0x48, 0x83, 0xc4, 0x30, /* 0c: add $0x30,%rsp */
2787 0x41, 0x5c, /* 10: pop %r12 */
2788 0x5f, /* 12: pop %rdi */
2789 0x5e, /* 13: pop %rsi */
2790 0x5d, /* 14: pop %rbp */
2791 0x5b, /* 15: pop %rbx */
2792 0xc3 /* 16: ret */
2795 static const BYTE unwind_info_1[] =
2797 1 | (UNW_FLAG_EHANDLER << 3), /* version + flags */
2798 0x0a, /* prolog size */
2799 6, /* opcode count */
2800 0, /* frame reg */
2802 0x0a, UWOP(ALLOC_SMALL, 5), /* 0a: sub $0x30,%rsp */
2803 0x06, UWOP(PUSH_NONVOL, r12), /* 06: push %r12 */
2804 0x04, UWOP(PUSH_NONVOL, rdi), /* 04: push %rdi */
2805 0x03, UWOP(PUSH_NONVOL, rsi), /* 03: push %rsi */
2806 0x02, UWOP(PUSH_NONVOL, rbp), /* 02: push %rbp */
2807 0x01, UWOP(PUSH_NONVOL, rbx), /* 01: push %rbx */
2809 0x00, 0x02, 0x00, 0x00, /* handler */
2810 0x05, 0x06, 0x07, 0x08, /* data */
2813 static const struct results_x86 results_1[] =
2815 /* offset rbp handler rip frame registers */
2816 { 0x00, 0x50, FALSE, 0x000, 0x000, { {rsp,0x008}, {-1,-1} }},
2817 { 0x01, 0x50, FALSE, 0x008, 0x000, { {rsp,0x010}, {rbx,0x000}, {-1,-1} }},
2818 { 0x02, 0x50, FALSE, 0x010, 0x000, { {rsp,0x018}, {rbx,0x008}, {rbp,0x000}, {-1,-1} }},
2819 { 0x03, 0x50, FALSE, 0x018, 0x000, { {rsp,0x020}, {rbx,0x010}, {rbp,0x008}, {rsi,0x000}, {-1,-1} }},
2820 { 0x04, 0x50, FALSE, 0x020, 0x000, { {rsp,0x028}, {rbx,0x018}, {rbp,0x010}, {rsi,0x008}, {rdi,0x000}, {-1,-1} }},
2821 { 0x06, 0x50, FALSE, 0x028, 0x000, { {rsp,0x030}, {rbx,0x020}, {rbp,0x018}, {rsi,0x010}, {rdi,0x008}, {r12,0x000}, {-1,-1} }},
2822 { 0x0a, 0x50, TRUE, 0x058, 0x000, { {rsp,0x060}, {rbx,0x050}, {rbp,0x048}, {rsi,0x040}, {rdi,0x038}, {r12,0x030}, {-1,-1} }},
2823 { 0x0c, 0x50, FALSE, 0x058, 0x000, { {rsp,0x060}, {rbx,0x050}, {rbp,0x048}, {rsi,0x040}, {rdi,0x038}, {r12,0x030}, {-1,-1} }},
2824 { 0x10, 0x50, FALSE, 0x028, 0x000, { {rsp,0x030}, {rbx,0x020}, {rbp,0x018}, {rsi,0x010}, {rdi,0x008}, {r12,0x000}, {-1,-1} }},
2825 { 0x12, 0x50, FALSE, 0x020, 0x000, { {rsp,0x028}, {rbx,0x018}, {rbp,0x010}, {rsi,0x008}, {rdi,0x000}, {-1,-1} }},
2826 { 0x13, 0x50, FALSE, 0x018, 0x000, { {rsp,0x020}, {rbx,0x010}, {rbp,0x008}, {rsi,0x000}, {-1,-1} }},
2827 { 0x14, 0x50, FALSE, 0x010, 0x000, { {rsp,0x018}, {rbx,0x008}, {rbp,0x000}, {-1,-1} }},
2828 { 0x15, 0x50, FALSE, 0x008, 0x000, { {rsp,0x010}, {rbx,0x000}, {-1,-1} }},
2829 { 0x16, 0x50, FALSE, 0x000, 0x000, { {rsp,0x008}, {-1,-1} }},
2832 static const BYTE function_2[] =
2834 0x55, /* 00: push %rbp */
2835 0x90, 0x90, /* 01: nop; nop */
2836 0x5d, /* 03: pop %rbp */
2837 0xc3 /* 04: ret */
2840 static const BYTE unwind_info_2[] =
2842 1 | (UNW_FLAG_EHANDLER << 3), /* version + flags */
2843 0x0, /* prolog size */
2844 2, /* opcode count */
2845 0, /* frame reg */
2847 0x01, UWOP(PUSH_NONVOL, rbp), /* 02: push %rbp */
2848 0x00, UWOP(PUSH_MACHFRAME, 0), /* 00 */
2850 0x00, 0x02, 0x00, 0x00, /* handler */
2851 0x05, 0x06, 0x07, 0x08, /* data */
2854 static const struct results_x86 results_2[] =
2856 /* offset rbp handler rip frame registers */
2857 { 0x01, 0x50, TRUE, 0x008, 0x000, { {rsp,-0x020}, {rbp,0x000}, {-1,-1} }},
2860 static const BYTE unwind_info_3[] =
2862 1 | (UNW_FLAG_EHANDLER << 3), /* version + flags */
2863 0x0, /* prolog size */
2864 2, /* opcode count */
2865 0, /* frame reg */
2867 0x01, UWOP(PUSH_NONVOL, rbp), /* 02: push %rbp */
2868 0x00, UWOP(PUSH_MACHFRAME, 1), /* 00 */
2870 0x00, 0x02, 0x00, 0x00, /* handler */
2871 0x05, 0x06, 0x07, 0x08, /* data */
2874 static const struct results_x86 results_3[] =
2876 /* offset rbp handler rip frame registers */
2877 { 0x01, 0x50, TRUE, 0x010, 0x000, { {rsp,-0x028}, {rbp,0x000}, {-1,-1} }},
2880 static const BYTE function_4[] =
2882 0x55, /* 00: push %rbp */
2883 0x5d, /* 01: pop %rbp */
2884 0xc3 /* 02: ret */
2887 static const BYTE unwind_info_4[] =
2889 1 | (UNW_FLAG_EHANDLER << 3), /* version + flags */
2890 0x0, /* prolog size */
2891 0, /* opcode count */
2892 0, /* frame reg */
2894 0x00, 0x02, 0x00, 0x00, /* handler */
2895 0x05, 0x06, 0x07, 0x08, /* data */
2898 static const struct results_x86 results_4[] =
2900 /* offset rbp handler rip frame registers */
2901 { 0x01, 0x50, TRUE, 0x000, 0x000, { {rsp,0x008}, {-1,-1} }},
2904 static const struct results_x86 broken_results_4[] =
2906 /* offset rbp handler rip frame registers */
2907 { 0x01, 0x50, FALSE, 0x008, 0x000, { {rsp,0x010}, {rbp,0x000}, {-1,-1} }},
2910 static const struct unwind_test_x86 tests[] =
2912 { function_0, sizeof(function_0), unwind_info_0, results_0, ARRAY_SIZE(results_0), broken_results_0 },
2913 { function_1, sizeof(function_1), unwind_info_1, results_1, ARRAY_SIZE(results_1) },
2914 { function_2, sizeof(function_2), unwind_info_2, results_2, ARRAY_SIZE(results_2) },
2915 { function_2, sizeof(function_2), unwind_info_3, results_3, ARRAY_SIZE(results_3) },
2917 /* Broken before Win10 1809. */
2918 { function_4, sizeof(function_4), unwind_info_4, results_4, ARRAY_SIZE(results_4), broken_results_4 },
2920 unsigned int i;
2922 for (i = 0; i < ARRAY_SIZE(tests); i++)
2923 call_virtual_unwind_x86( i, &tests[i] );
2926 #endif /* __x86_64__ */
2928 #ifdef __x86_64__
2929 #define SET_RUNTIME_FUNC_LEN(func,len) do { (func)->EndAddress = (func)->BeginAddress + (len); } while(0)
2930 #elif defined(__arm__)
2931 #define SET_RUNTIME_FUNC_LEN(func,len) do { (func)->FunctionLength = len / 2; (func)->Flag = 1; } while(0)
2932 #else
2933 #define SET_RUNTIME_FUNC_LEN(func,len) do { (func)->FunctionLength = len / 4; (func)->Flag = 1; } while(0)
2934 #endif
2936 static RUNTIME_FUNCTION * CALLBACK dynamic_unwind_callback( DWORD_PTR pc, PVOID context )
2938 static const int code_offset = 1024;
2939 static RUNTIME_FUNCTION runtime_func;
2940 (*(DWORD *)context)++;
2942 runtime_func.BeginAddress = code_offset + 16;
2943 runtime_func.UnwindData = 0;
2944 SET_RUNTIME_FUNC_LEN( &runtime_func, 16 );
2945 return &runtime_func;
2948 static void test_dynamic_unwind(void)
2950 static const int code_offset = 1024;
2951 char buf[2 * sizeof(RUNTIME_FUNCTION) + 4];
2952 MEM_EXTENDED_PARAMETER param = { 0 };
2953 RUNTIME_FUNCTION *runtime_func, *func;
2954 ULONG_PTR table, base, ec_code;
2955 void *growable_table, *ptr;
2956 NTSTATUS status;
2957 SIZE_T size = 0x1000;
2958 DWORD count;
2959 ULONG len, len2;
2961 if (!pRtlInstallFunctionTableCallback || !pRtlLookupFunctionEntry)
2963 win_skip( "Dynamic unwind functions not found\n" );
2964 return;
2967 /* Test RtlAddFunctionTable with aligned RUNTIME_FUNCTION pointer */
2968 runtime_func = (RUNTIME_FUNCTION *)buf;
2969 runtime_func->BeginAddress = code_offset;
2970 runtime_func->UnwindData = 0;
2971 SET_RUNTIME_FUNC_LEN( runtime_func, 16 );
2972 ok( pRtlAddFunctionTable( runtime_func, 1, (ULONG_PTR)code_mem ),
2973 "RtlAddFunctionTable failed for runtime_func = %p (aligned)\n", runtime_func );
2975 /* Lookup function outside of any function table */
2976 base = 0xdeadbeef;
2977 func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 16, &base, NULL );
2978 ok( func == NULL,
2979 "RtlLookupFunctionEntry returned unexpected function, expected: NULL, got: %p\n", func );
2980 ok( !base || broken(base == 0xdeadbeef),
2981 "RtlLookupFunctionEntry modified base address, expected: 0, got: %Ix\n", base );
2983 /* Test with pointer inside of our function */
2984 base = 0xdeadbeef;
2985 func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 8, &base, NULL );
2986 ok( func == runtime_func,
2987 "RtlLookupFunctionEntry didn't return expected function, expected: %p, got: %p\n", runtime_func, func );
2988 ok( base == (ULONG_PTR)code_mem,
2989 "RtlLookupFunctionEntry returned invalid base, expected: %Ix, got: %Ix\n", (ULONG_PTR)code_mem, base );
2991 /* Test RtlDeleteFunctionTable */
2992 ok( pRtlDeleteFunctionTable( runtime_func ),
2993 "RtlDeleteFunctionTable failed for runtime_func = %p (aligned)\n", runtime_func );
2994 ok( !pRtlDeleteFunctionTable( runtime_func ),
2995 "RtlDeleteFunctionTable returned success for nonexistent table runtime_func = %p\n", runtime_func );
2997 /* Unaligned RUNTIME_FUNCTION pointer */
2998 runtime_func = (RUNTIME_FUNCTION *)((ULONG_PTR)buf | 0x3);
2999 runtime_func->BeginAddress = code_offset;
3000 runtime_func->UnwindData = 0;
3001 SET_RUNTIME_FUNC_LEN( runtime_func, 16 );
3002 ok( pRtlAddFunctionTable( runtime_func, 1, (ULONG_PTR)code_mem ),
3003 "RtlAddFunctionTable failed for runtime_func = %p (unaligned)\n", runtime_func );
3004 ok( pRtlDeleteFunctionTable( runtime_func ),
3005 "RtlDeleteFunctionTable failed for runtime_func = %p (unaligned)\n", runtime_func );
3007 /* Attempt to insert the same entry twice */
3008 runtime_func = (RUNTIME_FUNCTION *)buf;
3009 runtime_func->BeginAddress = code_offset;
3010 runtime_func->UnwindData = 0;
3011 SET_RUNTIME_FUNC_LEN( runtime_func, 16 );
3012 ok( pRtlAddFunctionTable( runtime_func, 1, (ULONG_PTR)code_mem ),
3013 "RtlAddFunctionTable failed for runtime_func = %p (first attempt)\n", runtime_func );
3014 ok( pRtlAddFunctionTable( runtime_func, 1, (ULONG_PTR)code_mem ),
3015 "RtlAddFunctionTable failed for runtime_func = %p (second attempt)\n", runtime_func );
3016 ok( pRtlDeleteFunctionTable( runtime_func ),
3017 "RtlDeleteFunctionTable failed for runtime_func = %p (first attempt)\n", runtime_func );
3018 ok( pRtlDeleteFunctionTable( runtime_func ),
3019 "RtlDeleteFunctionTable failed for runtime_func = %p (second attempt)\n", runtime_func );
3020 ok( !pRtlDeleteFunctionTable( runtime_func ),
3021 "RtlDeleteFunctionTable returned success for nonexistent table runtime_func = %p\n", runtime_func );
3023 /* Empty table */
3024 ok( pRtlAddFunctionTable( runtime_func, 0, (ULONG_PTR)code_mem ),
3025 "RtlAddFunctionTable failed for empty table\n" );
3026 ok( pRtlDeleteFunctionTable( runtime_func ),
3027 "RtlDeleteFunctionTable failed for empty table\n" );
3028 ok( !pRtlDeleteFunctionTable( runtime_func ),
3029 "RtlDeleteFunctionTable succeeded twice for empty table\n" );
3031 /* Test RtlInstallFunctionTableCallback with both low bits unset */
3032 table = (ULONG_PTR)code_mem;
3033 ok( !pRtlInstallFunctionTableCallback( table, (ULONG_PTR)code_mem, code_offset + 32, &dynamic_unwind_callback, (PVOID*)&count, NULL ),
3034 "RtlInstallFunctionTableCallback returned success for table = %Ix\n", table );
3036 /* Test RtlInstallFunctionTableCallback with both low bits set */
3037 table = (ULONG_PTR)code_mem | 0x3;
3038 ok( pRtlInstallFunctionTableCallback( table, (ULONG_PTR)code_mem, code_offset + 32, &dynamic_unwind_callback, (PVOID*)&count, NULL ),
3039 "RtlInstallFunctionTableCallback failed for table = %Ix\n", table );
3041 /* Lookup function outside of any function table */
3042 count = 0;
3043 base = 0xdeadbeef;
3044 func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 32, &base, NULL );
3045 ok( func == NULL,
3046 "RtlLookupFunctionEntry returned unexpected function, expected: NULL, got: %p\n", func );
3047 ok( !base || broken(base == 0xdeadbeef),
3048 "RtlLookupFunctionEntry modified base address, expected: 0, got: %Ix\n", base );
3049 ok( !count,
3050 "RtlLookupFunctionEntry issued %ld unexpected calls to dynamic_unwind_callback\n", count );
3052 /* Test with pointer inside of our function */
3053 count = 0;
3054 base = 0xdeadbeef;
3055 func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 24, &base, NULL );
3056 ok( count == 1 || broken(!count), /* win10 arm */
3057 "RtlLookupFunctionEntry issued %ld calls to dynamic_unwind_callback, expected: 1\n", count );
3058 if (count)
3060 ok( func != NULL && func->BeginAddress == code_offset + 16,
3061 "RtlLookupFunctionEntry didn't return expected function, got: %p\n", func );
3062 ok( base == (ULONG_PTR)code_mem,
3063 "RtlLookupFunctionEntry returned invalid base: %Ix / %Ix\n", (ULONG_PTR)code_mem, base );
3066 /* Clean up again */
3067 ok( pRtlDeleteFunctionTable( (PRUNTIME_FUNCTION)table ),
3068 "RtlDeleteFunctionTable failed for table = %p\n", (PVOID)table );
3069 ok( !pRtlDeleteFunctionTable( (PRUNTIME_FUNCTION)table ),
3070 "RtlDeleteFunctionTable returned success for nonexistent table = %p\n", (PVOID)table );
3072 if (!pRtlAddGrowableFunctionTable)
3074 win_skip("Growable function tables are not supported.\n");
3075 return;
3078 runtime_func = (RUNTIME_FUNCTION *)buf;
3079 runtime_func->BeginAddress = code_offset;
3080 runtime_func->UnwindData = 0;
3081 SET_RUNTIME_FUNC_LEN( runtime_func, 16 );
3082 runtime_func++;
3083 runtime_func->BeginAddress = code_offset + 16;
3084 runtime_func->UnwindData = 0;
3085 SET_RUNTIME_FUNC_LEN( runtime_func, 16 );
3086 runtime_func = (RUNTIME_FUNCTION *)buf;
3088 growable_table = NULL;
3089 status = pRtlAddGrowableFunctionTable( &growable_table, runtime_func, 1, 1, (ULONG_PTR)code_mem, (ULONG_PTR)code_mem + 64 );
3090 ok(!status, "RtlAddGrowableFunctionTable failed for runtime_func = %p (aligned), %#lx.\n", runtime_func, status );
3091 ok(growable_table != 0, "Unexpected table value.\n");
3092 pRtlDeleteGrowableFunctionTable( growable_table );
3094 growable_table = NULL;
3095 status = pRtlAddGrowableFunctionTable( &growable_table, runtime_func, 2, 2, (ULONG_PTR)code_mem, (ULONG_PTR)code_mem + 64 );
3096 ok(!status, "RtlAddGrowableFunctionTable failed for runtime_func = %p (aligned), %#lx.\n", runtime_func, status );
3097 ok(growable_table != 0, "Unexpected table value.\n");
3098 pRtlDeleteGrowableFunctionTable( growable_table );
3100 growable_table = NULL;
3101 status = pRtlAddGrowableFunctionTable( &growable_table, runtime_func, 1, 2, (ULONG_PTR)code_mem, (ULONG_PTR)code_mem + 64 );
3102 ok(!status, "RtlAddGrowableFunctionTable failed for runtime_func = %p (aligned), %#lx.\n", runtime_func, status );
3103 ok(growable_table != 0, "Unexpected table value.\n");
3104 pRtlDeleteGrowableFunctionTable( growable_table );
3106 growable_table = NULL;
3107 status = pRtlAddGrowableFunctionTable( &growable_table, runtime_func, 0, 2, (ULONG_PTR)code_mem,
3108 (ULONG_PTR)code_mem + code_offset + 64 );
3109 ok(!status, "RtlAddGrowableFunctionTable failed for runtime_func = %p (aligned), %#lx.\n", runtime_func, status );
3110 ok(growable_table != 0, "Unexpected table value.\n");
3112 /* Current count is 0. */
3113 func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 8, &base, NULL );
3114 ok( func == NULL,
3115 "RtlLookupFunctionEntry didn't return expected function, expected: %p, got: %p\n", runtime_func, func );
3117 pRtlGrowFunctionTable( growable_table, 1 );
3119 base = 0xdeadbeef;
3120 func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 8, &base, NULL );
3121 ok( func == runtime_func,
3122 "RtlLookupFunctionEntry didn't return expected function, expected: %p, got: %p\n", runtime_func, func );
3123 ok( base == (ULONG_PTR)code_mem,
3124 "RtlLookupFunctionEntry returned invalid base, expected: %Ix, got: %Ix\n", (ULONG_PTR)code_mem, base );
3126 /* Second function is inaccessible yet. */
3127 base = 0xdeadbeef;
3128 func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 16, &base, NULL );
3129 ok( func == NULL,
3130 "RtlLookupFunctionEntry didn't return expected function, expected: %p, got: %p\n", runtime_func, func );
3132 pRtlGrowFunctionTable( growable_table, 2 );
3134 base = 0xdeadbeef;
3135 func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 16, &base, NULL );
3136 ok( func == runtime_func + 1,
3137 "RtlLookupFunctionEntry didn't return expected function, expected: %p, got: %p\n", runtime_func, func );
3138 ok( base == (ULONG_PTR)code_mem,
3139 "RtlLookupFunctionEntry returned invalid base, expected: %Ix, got: %Ix\n", (ULONG_PTR)code_mem, base );
3141 base = 0xdeadbeef;
3142 func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 32, &base, NULL );
3143 ok( func == NULL, "RtlLookupFunctionEntry got %p\n", func );
3144 ok( base == 0xdeadbeef, "RtlLookupFunctionTable wrong base, got: %Ix\n", base );
3146 base = 0xdeadbeef;
3147 func = pRtlLookupFunctionTable( (ULONG_PTR)code_mem + code_offset + 8, &base, &len );
3148 ok( func == NULL, "RtlLookupFunctionTable wrong table, got: %p\n", func );
3149 ok( base == 0xdeadbeef, "RtlLookupFunctionTable wrong base, got: %Ix\n", base );
3151 base = 0xdeadbeef;
3152 func = pRtlLookupFunctionTable( (ULONG_PTR)pRtlLookupFunctionEntry, &base, &len );
3153 ok( base == (ULONG_PTR)GetModuleHandleA("ntdll.dll"),
3154 "RtlLookupFunctionTable wrong base, got: %Ix / %p\n", base, GetModuleHandleA("ntdll.dll") );
3155 ptr = RtlImageDirectoryEntryToData( (void *)base, TRUE, IMAGE_DIRECTORY_ENTRY_EXCEPTION, &len2 );
3156 ok( func == ptr, "RtlLookupFunctionTable wrong table, got: %p / %p\n", func, ptr );
3157 ok( len == len2, "RtlLookupFunctionTable wrong len, got: %lu / %lu\n", len, len2 );
3159 pRtlDeleteGrowableFunctionTable( growable_table );
3161 param.Type = MemExtendedParameterAttributeFlags;
3162 param.ULong64 = MEM_EXTENDED_PARAMETER_EC_CODE;
3163 ec_code = 0;
3164 if (pNtAllocateVirtualMemoryEx &&
3165 !pNtAllocateVirtualMemoryEx( GetCurrentProcess(), (void **)&ec_code, &size,
3166 MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE, &param, 1 ))
3168 static const BYTE fast_forward[] = { 0x48, 0x8b, 0xc4, 0x48, 0x89, 0x58, 0x20, 0x55, 0x5d, 0xe9 };
3169 IMAGE_ARM64EC_METADATA *metadata;
3170 ARM64_RUNTIME_FUNCTION *arm64func = (ARM64_RUNTIME_FUNCTION *)buf;
3172 trace( "running arm64ec tests\n" );
3174 if (!memcmp( pRtlLookupFunctionEntry, fast_forward, sizeof(fast_forward) ))
3176 ptr = (char *)pRtlLookupFunctionEntry + sizeof(fast_forward);
3177 ptr = (char *)ptr + 4 + *(int *)ptr;
3178 base = 0xdeadbeef;
3179 func = pRtlLookupFunctionTable( (ULONG_PTR)ptr, &base, &len );
3180 ok( base == (ULONG_PTR)GetModuleHandleA("ntdll.dll"),
3181 "RtlLookupFunctionTable wrong base, got: %Ix / %p\n", base, GetModuleHandleA("ntdll.dll") );
3182 ptr = RtlImageDirectoryEntryToData( (void *)base, TRUE, IMAGE_DIRECTORY_ENTRY_EXCEPTION, &len2 );
3183 ok( func != ptr, "RtlLookupFunctionTable wrong table, got: %p / %p\n", func, ptr );
3184 ptr = RtlImageDirectoryEntryToData( (void *)base, TRUE, IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG, &len2 );
3185 metadata = (void *)((IMAGE_LOAD_CONFIG_DIRECTORY *)ptr)->CHPEMetadataPointer;
3186 ok( (char *)func == (char *)base + metadata->ExtraRFETable,
3187 "RtlLookupFunctonTable wrong table, got: %p / %p\n", func, (char *)base + metadata->ExtraRFETable );
3188 ok( len == metadata->ExtraRFETableSize, "RtlLookupFunctionTable wrong len, got: %lu / %lu\n",
3189 len, metadata->ExtraRFETableSize );
3192 arm64func->BeginAddress = code_offset;
3193 arm64func->Flag = 1;
3194 arm64func->FunctionLength = 4;
3195 arm64func->RegF = 1;
3196 arm64func->RegI = 1;
3197 arm64func->H = 1;
3198 arm64func->CR = 1;
3199 arm64func->FrameSize = 1;
3200 arm64func++;
3201 arm64func->BeginAddress = code_offset + 16;
3202 arm64func->Flag = 1;
3203 arm64func->FunctionLength = 4;
3204 arm64func->RegF = 1;
3205 arm64func->RegI = 1;
3206 arm64func->H = 1;
3207 arm64func->CR = 1;
3208 arm64func->FrameSize = 1;
3210 growable_table = NULL;
3211 status = pRtlAddGrowableFunctionTable( &growable_table, (RUNTIME_FUNCTION *)buf,
3212 2, 2, ec_code, ec_code + code_offset + 64 );
3213 ok( !status, "RtlAddGrowableFunctionTable failed %lx\n", status );
3215 base = 0xdeadbeef;
3216 func = pRtlLookupFunctionEntry( ec_code + code_offset + 8, &base, NULL );
3217 ok( func == (RUNTIME_FUNCTION *)buf, "RtlLookupFunctionEntry expected func: %p, got: %p\n",
3218 buf, func );
3219 ok( base == ec_code, "RtlLookupFunctionEntry expected base: %Ix, got: %Ix\n",
3220 ec_code, base );
3222 base = 0xdeadbeef;
3223 func = pRtlLookupFunctionEntry( ec_code + code_offset + 16, &base, NULL );
3224 ok( func == (RUNTIME_FUNCTION *)(buf + sizeof(*arm64func)),
3225 "RtlLookupFunctionEntry expected func: %p, got: %p\n", buf + sizeof(*arm64func), func );
3226 ok( base == ec_code, "RtlLookupFunctionEntry expected base: %Ix, got: %Ix\n", ec_code, base );
3228 base = 0xdeadbeef;
3229 func = pRtlLookupFunctionEntry( ec_code + code_offset + 32, &base, NULL );
3230 ok( !func, "RtlLookupFunctionEntry got: %p\n", func );
3231 ok( base == 0xdeadbeef, "RtlLookupFunctionEntry got: %Ix\n", base );
3233 pRtlDeleteGrowableFunctionTable( growable_table );
3234 VirtualFree( (void *)ec_code, 0, MEM_RELEASE );
3239 START_TEST(unwind)
3241 ntdll = GetModuleHandleA("ntdll.dll");
3242 code_mem = VirtualAlloc( NULL, 65536, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE );
3244 #define X(f) p##f = (void*)GetProcAddress(ntdll, #f)
3245 X(NtAllocateVirtualMemoryEx);
3246 X(RtlAddFunctionTable);
3247 X(RtlAddGrowableFunctionTable);
3248 X(RtlDeleteFunctionTable);
3249 X(RtlDeleteGrowableFunctionTable);
3250 X(RtlGrowFunctionTable);
3251 X(RtlInstallFunctionTableCallback);
3252 X(RtlLookupFunctionEntry);
3253 X(RtlLookupFunctionTable);
3254 #undef X
3256 #ifdef __arm__
3257 test_virtual_unwind_arm();
3258 #elif defined(__aarch64__)
3259 test_virtual_unwind_arm64();
3260 #elif defined(__x86_64__)
3261 test_virtual_unwind_x86();
3262 test_virtual_unwind_arm64();
3263 #endif
3265 test_dynamic_unwind();
3268 #else /* !__i386__ */
3270 START_TEST(unwind)
3274 #endif /* !__i386__ */