ntdll: Buffer pagemap reads in fill_working_set_info().
[wine.git] / dlls / ntdll / tests / unwind.c
blobdaa90c19a11cfd971b24afec82665a012a9fa412
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 "rtlsupportapi.h"
34 #include "wine/test.h"
36 #ifndef __i386__
38 static void *code_mem;
39 static HMODULE ntdll;
41 static PRUNTIME_FUNCTION (WINAPI *pRtlLookupFunctionEntry)(ULONG_PTR, ULONG_PTR*, UNWIND_HISTORY_TABLE*);
42 static PRUNTIME_FUNCTION (WINAPI *pRtlLookupFunctionTable)(ULONG_PTR, ULONG_PTR*, ULONG*);
43 static BOOLEAN (CDECL *pRtlInstallFunctionTableCallback)(DWORD64, DWORD64, DWORD, PGET_RUNTIME_FUNCTION_CALLBACK, PVOID, PCWSTR);
44 static BOOLEAN (CDECL *pRtlAddFunctionTable)(RUNTIME_FUNCTION*, DWORD, DWORD64);
45 static BOOLEAN (CDECL *pRtlDeleteFunctionTable)(RUNTIME_FUNCTION*);
46 static DWORD (WINAPI *pRtlAddGrowableFunctionTable)(void**, RUNTIME_FUNCTION*, DWORD, DWORD, ULONG_PTR, ULONG_PTR);
47 static void (WINAPI *pRtlGrowFunctionTable)(void*, DWORD);
48 static void (WINAPI *pRtlDeleteGrowableFunctionTable)(void*);
49 static NTSTATUS (WINAPI *pRtlVirtualUnwind2)(ULONG,ULONG_PTR,ULONG_PTR,RUNTIME_FUNCTION*,CONTEXT*,BOOLEAN*,void**,ULONG_PTR*,KNONVOLATILE_CONTEXT_POINTERS*,ULONG_PTR*,ULONG_PTR*,PEXCEPTION_ROUTINE*,ULONG);
50 static NTSTATUS (WINAPI *pNtAllocateVirtualMemoryEx)(HANDLE,PVOID*,SIZE_T*,ULONG,ULONG,MEM_EXTENDED_PARAMETER*,ULONG);
52 #ifdef __arm__
54 #define UWOP_TWOBYTES(x) (((x) >> 8) & 0xff), ((x) & 0xff)
55 #define UWOP_THREEBYTES(x) (((x) >> 16) & 0xff), (((x) >> 8) & 0xff), ((x) & 0xff)
56 #define UWOP_FOURBYTES(x) (((x) >> 24) & 0xff), (((x) >> 16) & 0xff), (((x) >> 8) & 0xff), ((x) & 0xff)
58 #define UWOP_ALLOC_SMALL(size) (0x00 | (size/4)) /* Max 0x7f * 4 */
59 #define UWOP_SAVE_REGSW(regmask) UWOP_TWOBYTES((0x80 << 8) | (regmask))
60 #define UWOP_SET_FP(reg) (0xC0 | reg)
61 #define UWOP_SAVE_RANGE_4_7_LR(reg,lr) (0xD0 | (reg - 4) | ((lr) ? 0x04 : 0))
62 #define UWOP_SAVE_RANGE_4_11_LR(reg,lr)(0xD8 | (reg - 8) | ((lr) ? 0x04 : 0))
63 #define UWOP_SAVE_D8_RANGE(reg) (0xE0 | (reg - 8))
64 #define UWOP_ALLOC_MEDIUMW(size) UWOP_TWOBYTES((0xE8 << 8) | (size/4)) /* Max 0x3ff * 4 */
65 #define UWOP_SAVE_REGS(regmask) UWOP_TWOBYTES((0xEC << 8) | ((regmask) & 0xFF) | (((regmask) & (1<<lr)) ? 0x100 : 0))
66 #define UWOP_SAVE_LR(offset) UWOP_TWOBYTES((0xEF << 8) | (offset/4))
67 #define UWOP_SAVE_D0_RANGE(first,last) UWOP_TWOBYTES((0xF5 << 8) | (first << 4) | (last))
68 #define UWOP_SAVE_D16_RANGE(first,last)UWOP_TWOBYTES((0xF6 << 8) | ((first - 16) << 4) | (last - 16))
69 #define UWOP_ALLOC_LARGE(size) UWOP_THREEBYTES((0xF7 << 16) | (size/4))
70 #define UWOP_ALLOC_HUGE(size) UWOP_FOURBYTES((0xF8u << 24) | (size/4))
71 #define UWOP_ALLOC_LARGEW(size) UWOP_THREEBYTES((0xF9 << 16) | (size/4))
72 #define UWOP_ALLOC_HUGEW(size) UWOP_FOURBYTES((0xFAu << 24) | (size/4))
73 #define UWOP_MSFT_OP_MACHINE_FRAME 0xEE,0x01
74 #define UWOP_MSFT_OP_CONTEXT 0xEE,0x02
75 #define UWOP_NOP16 0xFB
76 #define UWOP_NOP32 0xFC
77 #define UWOP_END_NOP16 0xFD
78 #define UWOP_END_NOP32 0xFE
79 #define UWOP_END 0xFF
81 struct results_arm
83 int pc_offset; /* pc offset from code start */
84 int fp_offset; /* fp offset from stack pointer */
85 int handler; /* expect handler to be set? */
86 ULONG_PTR pc; /* expected final pc value */
87 ULONG_PTR frame; /* expected frame return value */
88 int frame_offset; /* whether the frame return value is an offset or an absolute value */
89 LONGLONG regs[47][2];/* expected values for registers */
92 struct unwind_test_arm
94 const BYTE *function;
95 size_t function_size;
96 const BYTE *unwind_info;
97 size_t unwind_size;
98 const struct results_arm *results;
99 unsigned int nb_results;
102 enum regs
104 /* Note, lr and sp are swapped to allow using 'lr' in register bitmasks. */
105 r0, r1, r2, r3, r4, r5, r6, r7,
106 r8, r9, r10, r11, r12, lr, sp,
107 d0, d1, d2, d3, d4, d5, d6, d7,
108 d8, d9, d10, d11, d12, d13, d14, d15,
109 d16, d17, d18, d19, d20, d21, d22, d23,
110 d24, d25, d26, d27, d28, d29, d30, d31,
113 static const char * const reg_names_arm[47] =
115 "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
116 "r8", "r9", "r10", "r11", "r12", "lr", "sp",
117 "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
118 "d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15",
119 "d16", "d17", "d18", "d19", "d20", "d21", "d22", "d23",
120 "d24", "d25", "d26", "d27", "d28", "d29", "d30", "d31",
123 #define ORIG_LR 0xCCCCCCCC
125 static void call_virtual_unwind_arm( int testnum, const struct unwind_test_arm *test )
127 static const int code_offset = 1024;
128 static const int unwind_offset = 2048;
129 void *data;
130 CONTEXT context;
131 NTSTATUS status;
132 PEXCEPTION_ROUTINE handler;
133 RUNTIME_FUNCTION runtime_func;
134 KNONVOLATILE_CONTEXT_POINTERS ctx_ptr;
135 UINT i, j, k;
136 ULONG fake_stack[256];
137 ULONG_PTR frame, orig_pc, orig_fp, unset_reg;
138 ULONGLONG unset_reg64;
139 static const UINT nb_regs = ARRAY_SIZE(test->results[i].regs);
141 memcpy( (char *)code_mem + code_offset, test->function, test->function_size );
142 if (test->unwind_info)
144 memcpy( (char *)code_mem + unwind_offset, test->unwind_info, test->unwind_size );
145 runtime_func.BeginAddress = code_offset;
146 if (test->unwind_size)
147 runtime_func.UnwindData = unwind_offset;
148 else
149 memcpy(&runtime_func.UnwindData, test->unwind_info, 4);
152 for (i = 0; i < test->nb_results; i++)
154 memset( &ctx_ptr, 0, sizeof(ctx_ptr) );
155 memset( &context, 0x55, sizeof(context) );
156 memset( &unset_reg, 0x55, sizeof(unset_reg) );
157 memset( &unset_reg64, 0x55, sizeof(unset_reg64) );
158 for (j = 0; j < 256; j++) fake_stack[j] = j * 4;
160 context.Sp = (ULONG_PTR)fake_stack;
161 context.Lr = (ULONG_PTR)ORIG_LR;
162 context.R11 = (ULONG_PTR)fake_stack + test->results[i].fp_offset;
163 orig_fp = context.R11;
164 orig_pc = (ULONG_PTR)code_mem + code_offset + test->results[i].pc_offset;
166 trace( "%u/%u: pc=%p (%02x) fp=%p sp=%p\n", testnum, i,
167 (void *)orig_pc, *(UINT *)orig_pc, (void *)orig_fp, (void *)context.Sp );
169 if (test->results[i].handler == -2) orig_pc = context.Lr;
171 if (pRtlVirtualUnwind2)
173 CONTEXT new_context = context;
175 handler = (void *)0xdeadbeef;
176 data = (void *)0xdeadbeef;
177 frame = 0xdeadbeef;
178 status = pRtlVirtualUnwind2( UNW_FLAG_EHANDLER, (ULONG)code_mem, orig_pc,
179 test->unwind_info ? &runtime_func : NULL, &new_context,
180 NULL, &data, &frame, &ctx_ptr, NULL, NULL, &handler, 0 );
181 if (test->results[i].handler > 0)
183 ok( !status, "RtlVirtualUnwind2 failed %lx\n", status );
184 ok( (char *)handler == (char *)code_mem + 0x200,
185 "%u/%u: wrong handler %p/%p\n", testnum, i, handler, (char *)code_mem + 0x200 );
186 if (handler) ok( *(DWORD *)data == 0x08070605,
187 "%u/%u: wrong handler data %lx\n", testnum, i, *(DWORD *)data );
189 else if (test->results[i].handler < -1)
191 ok( status == STATUS_BAD_FUNCTION_TABLE, "RtlVirtualUnwind2 failed %lx\n", status );
192 ok( handler == (void *)0xdeadbeef, "handler set to %p\n", handler );
193 ok( data == (void *)0xdeadbeef, "handler data set to %p\n", data );
195 else
197 ok( !status, "RtlVirtualUnwind2 failed %lx\n", status );
198 ok( handler == NULL, "handler %p instead of NULL\n", handler );
199 ok( data == NULL, "handler data set to %p\n", data );
203 data = (void *)0xdeadbeef;
204 frame = 0xdeadbeef;
205 handler = RtlVirtualUnwind( UNW_FLAG_EHANDLER, (ULONG)code_mem, orig_pc,
206 test->unwind_info ? &runtime_func : NULL,
207 &context, &data, &frame, &ctx_ptr );
208 if (test->results[i].handler > 0)
210 ok( (char *)handler == (char *)code_mem + 0x200,
211 "%u/%u: wrong handler %p/%p\n", testnum, i, handler, (char *)code_mem + 0x200 );
212 if (handler) ok( *(DWORD *)data == 0x08070605,
213 "%u/%u: wrong handler data %lx\n", testnum, i, *(DWORD *)data );
215 else
217 ok( handler == NULL, "%u/%u: handler %p instead of NULL\n", testnum, i, handler );
218 ok( data == (test->results[i].handler < -1 ? (void *)0xdeadbeef : NULL),
219 "%u/%u: handler data set to %p/%p\n", testnum, i, data,
220 (test->results[i].handler < 0 ? (void *)0xdeadbeef : NULL) );
223 ok( context.Pc == test->results[i].pc, "%u/%u: wrong pc %p/%p\n",
224 testnum, i, (void *)context.Pc, (void*)test->results[i].pc );
225 ok( frame == (test->results[i].frame_offset ? (ULONG)fake_stack : 0) + test->results[i].frame, "%u/%u: wrong frame %x/%x\n",
226 testnum, i, (int)((char *)frame - (char *)(test->results[i].frame_offset ? fake_stack : NULL)), test->results[i].frame );
228 for (j = 0; j < 47; j++)
230 for (k = 0; k < nb_regs; k++)
232 if (test->results[i].regs[k][0] == -1)
234 k = nb_regs;
235 break;
237 if (test->results[i].regs[k][0] == j) break;
240 if (j >= 4 && j <= 11 && (&ctx_ptr.R4)[j - 4])
242 ok( k < nb_regs, "%u/%u: register %s should not be set to %lx\n",
243 testnum, i, reg_names_arm[j], (&context.R0)[j] );
244 if (k < nb_regs)
245 ok( (&context.R0)[j] == test->results[i].regs[k][1],
246 "%u/%u: register %s wrong %p/%x\n",
247 testnum, i, reg_names_arm[j], (void *)(&context.R0)[j], (int)test->results[i].regs[k][1] );
249 else if (j == lr && ctx_ptr.Lr)
251 ok( k < nb_regs, "%u/%u: register %s should not be set to %lx\n",
252 testnum, i, reg_names_arm[j], context.Lr );
253 if (k < nb_regs)
254 ok( context.Lr == test->results[i].regs[k][1],
255 "%u/%u: register %s wrong %p/%x\n",
256 testnum, i, reg_names_arm[j], (void *)context.Lr, (int)test->results[i].regs[k][1] );
258 else if (j == sp)
260 if (k < nb_regs)
261 ok( context.Sp == test->results[i].regs[k][1],
262 "%u/%u: register %s wrong %p/%x\n",
263 testnum, i, reg_names_arm[j], (void *)context.Sp, (int)test->results[i].regs[k][1] );
264 else if (test->results[i].frame == 0xdeadbeef)
265 ok( (void *)context.Sp == fake_stack, "%u/%u: wrong sp %p/%p\n",
266 testnum, i, (void *)context.Sp, fake_stack);
267 else
268 ok( context.Sp == frame, "%u/%u: wrong sp %p/%p\n",
269 testnum, i, (void *)context.Sp, (void *)frame);
271 else if (j >= d8 && j <= d15 && (&ctx_ptr.D8)[j - d8])
273 ok( k < nb_regs, "%u/%u: register %s should not be set to %llx\n",
274 testnum, i, reg_names_arm[j], context.D[j - d0] );
275 if (k < nb_regs)
276 ok( context.D[j - d0] == test->results[i].regs[k][1],
277 "%u/%u: register %s wrong %llx/%llx\n",
278 testnum, i, reg_names_arm[j], context.D[j - d0], test->results[i].regs[k][1] );
280 else if (k < nb_regs)
282 if (j <= r12)
283 ok( (&context.R0)[j] == test->results[i].regs[k][1],
284 "%u/%u: register %s wrong %p/%x\n",
285 testnum, i, reg_names_arm[j], (void *)(&context.R0)[j], (int)test->results[i].regs[k][1] );
286 else if (j == lr)
287 ok( context.Lr == test->results[i].regs[k][1],
288 "%u/%u: register %s wrong %p/%x\n",
289 testnum, i, reg_names_arm[j], (void *)context.Lr, (int)test->results[i].regs[k][1] );
290 else
291 ok( context.D[j - d0] == test->results[i].regs[k][1],
292 "%u/%u: register %s wrong %llx/%llx\n",
293 testnum, i, reg_names_arm[j], context.D[j - d0], test->results[i].regs[k][1] );
295 else
297 ok( k == nb_regs, "%u/%u: register %s should be set\n", testnum, i, reg_names_arm[j] );
298 if (j == lr)
299 ok( context.Lr == ORIG_LR, "%u/%u: register lr wrong %p/unset\n",
300 testnum, i, (void *)context.Lr );
301 else if (j == r11)
302 ok( context.R11 == orig_fp, "%u/%u: register fp wrong %p/unset\n",
303 testnum, i, (void *)context.R11 );
304 else if (j < d0)
305 ok( (&context.R0)[j] == unset_reg,
306 "%u/%u: register %s wrong %p/unset\n",
307 testnum, i, reg_names_arm[j], (void *)(&context.R0)[j]);
308 else
309 ok( context.D[j - d0] == unset_reg64,
310 "%u/%u: register %s wrong %llx/unset\n",
311 testnum, i, reg_names_arm[j], context.D[j - d0]);
317 #define DW(dword) ((dword >> 0) & 0xff), ((dword >> 8) & 0xff), ((dword >> 16) & 0xff), ((dword >> 24) & 0xff)
319 static void test_virtual_unwind_arm(void)
322 static const BYTE function_0[] =
324 0x70, 0xb5, /* 00: push {r4-r6, lr} */
325 0x88, 0xb0, /* 02: sub sp, sp, #32 */
326 0x2d, 0xed, 0x06, 0x8b, /* 04: vpush {d8-d10} */
327 0x00, 0xbf, /* 08: nop */
328 0x2d, 0xed, 0x06, 0x3b, /* 0a: vpush {d3-d5} */
329 0xaf, 0x3f, 0x00, 0x80, /* 0e: nop.w */
330 0x6d, 0xed, 0x06, 0x1b, /* 12: vpush {d17-d19} */
331 0x2d, 0xe9, 0x00, 0x15, /* 16: push.w {r8, r10, r12} */
332 0xeb, 0x46, /* 1a: mov r11, sp */
333 0x00, 0xbf, /* 1c: nop */
334 0xbd, 0xec, 0x06, 0x8b, /* 1e: vpop {d8-d10} */
335 0xdd, 0x46, /* 22: mov sp, r11 */
336 0x08, 0xb0, /* 24: add sp, sp, #32 */
337 0x70, 0xbd, /* 26: pop {r4-r6, pc} */
340 static const DWORD unwind_info_0_header =
341 (sizeof(function_0)/2) | /* function length */
342 (1 << 20) | /* X */
343 (0 << 21) | /* E */
344 (0 << 22) | /* F */
345 (1 << 23) | /* epilog */
346 (5 << 28); /* codes, (sizeof(unwind_info_0)-headers+3)/4 */
347 static const DWORD unwind_info_0_epilog0 =
348 (15 << 0) | /* offset = 0x1e / 2 = 15 */
349 (0xE << 20) | /* condition, 0xE = always */
350 (13 << 24); /* index, byte offset to epilog opcodes */
352 static const BYTE unwind_info_0[] =
354 DW(unwind_info_0_header),
355 DW(unwind_info_0_epilog0),
357 UWOP_SET_FP(11), /* mov r11, sp */
358 UWOP_SAVE_REGSW((1<<r8)|(1<<r10)|(1<<r12)), /* push.w {r8, r10, r12} */
359 UWOP_SAVE_D16_RANGE(17,19), /* vpush {d17-d19} */
360 UWOP_NOP32, /* nop.w */
361 UWOP_SAVE_D0_RANGE(3,5), /* vpush {d3-d5} */
362 UWOP_NOP16, /* nop */
363 UWOP_SAVE_D8_RANGE(10), /* vpush {d8-d10} */
364 UWOP_ALLOC_SMALL(32), /* sub sp, sp, #32 */
365 UWOP_SAVE_RANGE_4_7_LR(6, 1), /* push {r4-r6,lr} */
366 UWOP_END,
368 UWOP_SAVE_D8_RANGE(10), /* vpop {d8-d10} */
369 UWOP_SET_FP(11), /* mov sp, r11 */
370 UWOP_ALLOC_SMALL(32), /* add sp, sp, #32 */
371 UWOP_SAVE_RANGE_4_7_LR(6, 1), /* pop {r4-r6,pc} */
372 UWOP_END,
374 0, 0, /* align */
375 0x00, 0x02, 0x00, 0x00, /* handler */
376 0x05, 0x06, 0x07, 0x08, /* data */
379 static const struct results_arm results_0[] =
381 /* offset fp handler pc frame offset registers */
382 { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
383 { 0x02, 0x10, 0, 0x0c, 0x010, TRUE, { {r4,0x00}, {r5,0x04}, {r6,0x08}, {lr,0x0c}, {-1,-1} }},
384 { 0x04, 0x10, 0, 0x2c, 0x030, TRUE, { {r4,0x20}, {r5,0x24}, {r6,0x28}, {lr,0x2c}, {-1,-1} }},
385 { 0x08, 0x10, 0, 0x44, 0x048, TRUE, { {r4,0x38}, {r5,0x3c}, {r6,0x40}, {lr,0x44}, {d8, 0x400000000}, {d9, 0xc00000008}, {d10, 0x1400000010}, {-1,-1} }},
386 { 0x0a, 0x10, 0, 0x44, 0x048, TRUE, { {r4,0x38}, {r5,0x3c}, {r6,0x40}, {lr,0x44}, {d8, 0x400000000}, {d9, 0xc00000008}, {d10, 0x1400000010}, {-1,-1} }},
387 { 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} }},
388 { 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} }},
389 { 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} }},
390 { 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} }},
391 { 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} }},
392 { 0x1e, 0x10, 0, 0x3c, 0x040, TRUE, { {r4,0x30}, {r5,0x34}, {r6,0x38}, {lr,0x3c}, {d8, 0x400000000}, {d9, 0xc00000008}, {d10, 0x1400000010}, {-1,-1} }},
393 { 0x22, 0x10, 0, 0x3c, 0x040, TRUE, { {r4,0x30}, {r5,0x34}, {r6,0x38}, {lr,0x3c}, {-1,-1} }},
394 { 0x24, 0x10, 0, 0x2c, 0x030, TRUE, { {r4,0x20}, {r5,0x24}, {r6,0x28}, {lr,0x2c}, {-1,-1} }},
395 { 0x26, 0x10, 0, 0x0c, 0x010, TRUE, { {r4,0x00}, {r5,0x04}, {r6,0x08}, {lr,0x0c}, {-1,-1} }},
398 static const BYTE function_1[] =
400 0x30, 0xb4, /* 00: push {r4-r5} */
401 0x4d, 0xf8, 0x20, 0xed, /* 02: str lr, [sp, #-32]! */
402 0x00, 0xbf, /* 06: nop */
403 0x5d, 0xf8, 0x20, 0xeb, /* 08: ldr lr, [sp], #32 */
404 0x30, 0xbc, /* 0c: pop {r4-r5} */
405 0x70, 0x47, /* 0e: bx lr */
408 static const DWORD unwind_info_1_header =
409 (sizeof(function_1)/2) | /* function length */
410 (0 << 20) | /* X */
411 (0 << 21) | /* E */
412 (0 << 22) | /* F */
413 (0 << 23) | /* epilog */
414 (0 << 28); /* codes */
415 static const DWORD unwind_info_1_header2 =
416 (1 << 0) | /* epilog */
417 (2 << 16); /* codes, (sizeof(unwind_info_1)-headers+3)/4 */
418 static const DWORD unwind_info_1_epilog0 =
419 (4 << 0) | /* offset = 0x08 / 2 = 4 */
420 (0xE << 20) | /* condition, 0xE = always */
421 (4 << 24); /* index, byte offset to epilog opcodes */
423 static const BYTE unwind_info_1[] = {
424 DW(unwind_info_1_header),
425 DW(unwind_info_1_header2),
426 DW(unwind_info_1_epilog0),
428 UWOP_SAVE_LR(32), /* str lr, [sp, #-32]! */
429 UWOP_SAVE_RANGE_4_7_LR(5, 0), /* push {r4-r5} */
430 UWOP_END_NOP16,
432 UWOP_SAVE_LR(32), /* ldr lr, [sp], #32 */
433 UWOP_SAVE_RANGE_4_7_LR(5, 0), /* pop {r4-r5} */
434 UWOP_END_NOP16, /* bx lr */
437 static const struct results_arm results_1[] =
439 /* offset fp handler pc frame offset registers */
440 { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
441 { 0x02, 0x00, 0, ORIG_LR, 0x008, TRUE, { {r4,0x00}, {r5,0x04}, {-1,-1} }},
442 { 0x06, 0x00, 0, 0x00, 0x028, TRUE, { {r4,0x20}, {r5,0x24}, {lr,0x00}, {-1,-1} }},
443 { 0x08, 0x00, 0, 0x00, 0x028, TRUE, { {r4,0x20}, {r5,0x24}, {lr,0x00}, {-1,-1} }},
444 { 0x0c, 0x00, 0, ORIG_LR, 0x008, TRUE, { {r4,0x00}, {r5,0x04}, {-1,-1} }},
445 { 0x0e, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
448 static const BYTE function_2[] =
450 0x6f, 0x46, /* 00: mov r7, sp */
451 0x80, 0xb4, /* 02: push {r7} */
452 0x84, 0xb0, /* 04: sub sp, sp, #16 */
453 0x00, 0xbf, /* 06: nop */
454 0x04, 0xb0, /* 08: add sp, sp, #16 */
455 0x80, 0xbc, /* 0a: push {r7} */
456 0xbd, 0x46, /* 0c: mov sp, r7 */
457 0x00, 0xf0, 0x00, 0xb8, /* 0e: b tailcall */
460 static const DWORD unwind_info_2_header =
461 (sizeof(function_2)/2) | /* function length */
462 (0 << 20) | /* X */
463 (1 << 21) | /* E */
464 (0 << 22) | /* F */
465 (0 << 23) | /* epilog */
466 (2 << 28); /* codes, (sizeof(unwind_info_2)-headers+3)/4 */
468 static const BYTE unwind_info_2[] =
470 DW(unwind_info_2_header),
472 UWOP_ALLOC_SMALL(16), /* sub sp, sp, #16 */
473 UWOP_SAVE_REGS((1<<r7)), /* push {r7} */
474 UWOP_SET_FP(7), /* mov r7, sp */
475 UWOP_END_NOP32, /* b tailcall */
478 static const struct results_arm results_2[] =
480 /* offset fp handler pc frame offset registers */
481 { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
482 { 0x02, 0x00, 0, ORIG_LR, 0x55555555, FALSE, { {-1,-1} }},
483 { 0x04, 0x00, 0, ORIG_LR, 0x000, FALSE, { {r7,0x00}, {-1,-1} }},
484 { 0x06, 0x00, 0, ORIG_LR, 0x010, FALSE, { {r7,0x10}, {-1,-1} }},
485 { 0x08, 0x00, 0, ORIG_LR, 0x010, FALSE, { {r7,0x10}, {-1,-1} }},
486 { 0x0a, 0x00, 0, ORIG_LR, 0x000, FALSE, { {r7,0x00}, {-1,-1} }},
487 { 0x0c, 0x00, 0, ORIG_LR, 0x55555555, FALSE, { {-1,-1} }},
488 { 0x0e, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
491 static const BYTE function_3[] =
493 0xaf, 0x3f, 0x00, 0x80, /* 00: nop.w */
494 0x00, 0xbf, /* 04: nop */
495 0x00, 0xbf, /* 06: nop */
496 0x04, 0xb0, /* 08: add sp, sp, #16 */
497 0xbd, 0xe8, 0xf0, 0x8f, /* 0a: pop.w {r4-r11,pc} */
500 /* Testing F=1, no prologue */
501 static const DWORD unwind_info_3_header =
502 (sizeof(function_3)/2) | /* function length */
503 (0 << 20) | /* X */
504 (1 << 21) | /* E */
505 (1 << 22) | /* F */
506 (0 << 23) | /* epilog */
507 (1 << 28); /* codes, (sizeof(unwind_info_3)-headers+3)/4 */
509 static const BYTE unwind_info_3[] =
511 DW(unwind_info_3_header),
513 UWOP_ALLOC_SMALL(16), /* sub sp, sp, #16 */
514 UWOP_SAVE_RANGE_4_11_LR(11, 1), /* pop.w {r4-r11,pc} */
515 UWOP_END,
518 static const struct results_arm results_3[] =
520 /* offset fp handler pc frame offset registers */
521 { 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} }},
522 { 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} }},
523 { 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} }},
524 { 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} }},
525 { 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} }},
528 static const BYTE function_4[] =
530 0x2d, 0xe9, 0x00, 0x55, /* 00: push.w {r8, r10, r12, lr} */
531 0x50, 0xb4, /* 04: push {r4, r6} */
532 0x00, 0xbf, /* 06: nop */
533 0x50, 0xbc, /* 08: pop {r4, r6} */
534 0xbd, 0xe8, 0x00, 0x95, /* 0a: pop.w {r8, r10, r12, pc} */
537 static const DWORD unwind_info_4_header =
538 (sizeof(function_4)/2) | /* function length */
539 (0 << 20) | /* X */
540 (1 << 21) | /* E */
541 (0 << 22) | /* F */
542 (0 << 23) | /* epilog */
543 (2 << 28); /* codes, (sizeof(unwind_info_4)-headers+3)/4 */
545 static const BYTE unwind_info_4[] =
547 DW(unwind_info_4_header),
549 UWOP_SAVE_REGS((1<<r4)|(1<<r6)), /* push {r4, r6} */
550 UWOP_SAVE_REGSW((1<<r8)|(1<<r10)|(1<<r12)|(1<<lr)), /* push.w {r8, r10, r12, lr} */
551 UWOP_END,
554 static const struct results_arm results_4[] =
556 /* offset fp handler pc frame offset registers */
557 { 0x00, 0x10, 0, ORIG_LR, 0x00000, TRUE, { {-1,-1} }},
558 { 0x04, 0x10, 0, 0x0c, 0x00010, TRUE, { {r8,0x00}, {r10,0x04}, {r12,0x08}, {lr,0x0c}, {-1,-1} }},
559 { 0x06, 0x10, 0, 0x14, 0x00018, TRUE, { {r8,0x08}, {r10,0x0c}, {r12,0x10}, {lr,0x14}, {r4,0x00}, {r6,0x04}, {-1,-1} }},
560 { 0x08, 0x10, 0, 0x14, 0x00018, TRUE, { {r8,0x08}, {r10,0x0c}, {r12,0x10}, {lr,0x14}, {r4,0x00}, {r6,0x04}, {-1,-1} }},
561 { 0x0a, 0x10, 0, 0x0c, 0x00010, TRUE, { {r8,0x00}, {r10,0x04}, {r12,0x08}, {lr,0x0c}, {-1,-1} }},
564 static const BYTE function_5[] =
566 0x50, 0xb5, /* 00: push {r4, r6, lr} */
567 0xad, 0xf2, 0x08, 0x0d, /* 02: subw sp, sp, #8 */
568 0x84, 0xb0, /* 06: sub sp, sp, #16 */
569 0x88, 0xb0, /* 08: sub sp, sp, #32 */
570 0xad, 0xf2, 0x40, 0x0d, /* 0a: subw sp, sp, #64 */
571 0xad, 0xf2, 0x80, 0x0d, /* 0e: subw sp, sp, #128 */
572 0x00, 0xbf, /* 12: nop */
573 0x50, 0xbd, /* 14: pop {r4, r6, pc} */
576 static const DWORD unwind_info_5_header =
577 (sizeof(function_5)/2) | /* function length */
578 (0 << 20) | /* X */
579 (1 << 21) | /* E */
580 (0 << 22) | /* F */
581 (16 << 23) | /* epilog */
582 (5 << 28); /* codes, (sizeof(unwind_info_4)-headers+3)/4 */
584 static const BYTE unwind_info_5[] =
586 DW(unwind_info_5_header),
588 UWOP_ALLOC_HUGEW(128), /* subw sp, sp, #128 */
589 UWOP_ALLOC_LARGEW(64), /* subw sp, sp, #64 */
590 UWOP_ALLOC_HUGE(32), /* sub sp, sp, #32 */
591 UWOP_ALLOC_LARGE(16), /* sub sp, sp, #16 */
592 UWOP_ALLOC_MEDIUMW(8), /* subw sp, sp, #8 */
593 UWOP_SAVE_REGS((1<<r4)|(1<<r6)|(1<<lr)), /* push {r4, r6, lr} */
594 UWOP_END,
597 static const struct results_arm results_5[] =
599 /* offset fp handler pc frame offset registers */
600 { 0x00, 0x00, 0, ORIG_LR, 0x00000, TRUE, { {-1,-1} }},
601 { 0x02, 0x00, 0, 0x008, 0x0000c, TRUE, { {r4,0x00}, {r6,0x04}, {lr,0x08}, {-1,-1} }},
602 { 0x06, 0x00, 0, 0x010, 0x00014, TRUE, { {r4,0x08}, {r6,0x0c}, {lr,0x10}, {-1,-1} }},
603 { 0x08, 0x00, 0, 0x020, 0x00024, TRUE, { {r4,0x18}, {r6,0x1c}, {lr,0x20}, {-1,-1} }},
604 { 0x0a, 0x00, 0, 0x040, 0x00044, TRUE, { {r4,0x38}, {r6,0x3c}, {lr,0x40}, {-1,-1} }},
605 { 0x0e, 0x00, 0, 0x080, 0x00084, TRUE, { {r4,0x78}, {r6,0x7c}, {lr,0x80}, {-1,-1} }},
606 { 0x12, 0x00, 0, 0x100, 0x00104, TRUE, { {r4,0xf8}, {r6,0xfc}, {lr,0x100}, {-1,-1} }},
607 { 0x14, 0x00, 0, 0x008, 0x0000c, TRUE, { {r4,0x00}, {r6,0x04}, {lr,0x08}, {-1,-1} }},
610 static const BYTE function_6[] =
612 0x00, 0xbf, /* 00: nop */
613 0x00, 0xbf, /* 02: nop */
614 0x00, 0xbf, /* 04: nop */
615 0x70, 0x47, /* 06: bx lr */
618 static const DWORD unwind_info_6_packed =
619 (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
620 (sizeof(function_6)/2 << 2) | /* FunctionLength */
621 (1 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
622 (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
623 (7 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
624 (1 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
625 (0 << 20) | /* L, push LR */
626 (0 << 21) | /* C - hook up r11 */
627 (0 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
629 static const BYTE unwind_info_6[] = { DW(unwind_info_6_packed) };
631 static const struct results_arm results_6[] =
633 /* offset fp handler pc frame offset registers */
634 { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
635 { 0x02, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
636 { 0x04, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
637 { 0x06, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
640 static const BYTE function_7[] =
642 0x10, 0xb4, /* 00: push {r4} */
643 0x00, 0xbf, /* 02: nop */
644 0x10, 0xbc, /* 04: pop {r4} */
645 0x70, 0x47, /* 06: bx lr */
648 static const DWORD unwind_info_7_packed =
649 (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
650 (sizeof(function_7)/2 << 2) | /* FunctionLength */
651 (1 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
652 (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
653 (0 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
654 (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
655 (0 << 20) | /* L, push LR */
656 (0 << 21) | /* C - hook up r11 */
657 (0 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
659 static const BYTE unwind_info_7[] = { DW(unwind_info_7_packed) };
661 static const struct results_arm results_7[] =
663 /* offset fp handler pc frame offset registers */
664 { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
665 { 0x02, 0x00, 0, ORIG_LR, 0x004, TRUE, { {r4,0x00}, {-1,-1} }},
666 { 0x04, 0x00, 0, ORIG_LR, 0x004, TRUE, { {r4,0x00}, {-1,-1} }},
667 { 0x06, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
670 static const BYTE function_8[] =
672 0x10, 0xb5, /* 00: push {r4, lr} */
673 0x00, 0xbf, /* 02: nop */
674 0xbd, 0xe8, 0x10, 0x40, /* 04: pop {r4, lr} */
675 0x70, 0x47, /* 08: bx lr */
678 static const DWORD unwind_info_8_packed =
679 (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
680 (sizeof(function_8)/2 << 2) | /* FunctionLength */
681 (1 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
682 (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
683 (0 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
684 (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
685 (1 << 20) | /* L, push LR */
686 (0 << 21) | /* C - hook up r11 */
687 (0 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
689 static const BYTE unwind_info_8[] = { DW(unwind_info_8_packed) };
691 static const struct results_arm results_8[] =
693 /* offset fp handler pc frame offset registers */
694 { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
695 { 0x02, 0x00, 0, 0x004, 0x008, TRUE, { {r4,0x00}, {lr,0x04}, {-1,-1} }},
696 { 0x04, 0x00, 0, 0x004, 0x008, TRUE, { {r4,0x00}, {lr,0x04}, {-1,-1} }},
697 { 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. */
698 { 0x08, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
701 static const BYTE function_9[] =
703 0x00, 0xb5, /* 00: push {lr} */
704 0x2d, 0xed, 0x02, 0x8b, /* 02: vpush {d8} */
705 0x88, 0xb0, /* 06: sub sp, sp, #32 */
706 0x00, 0xbf, /* 08: nop */
707 0x08, 0xb0, /* 0a: add sp, sp, #32 */
708 0xbd, 0xec, 0x02, 0x8b, /* 0c: vpop {d8} */
709 0x5d, 0xf8, 0x04, 0xeb, /* 10: ldr lr, [sp], #4 */
710 0x00, 0xf0, 0x00, 0xb8, /* 14: b tailcall */
713 static const DWORD unwind_info_9_packed =
714 (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
715 (sizeof(function_9)/2 << 2) | /* FunctionLength */
716 (2 << 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 (0 << 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 (0 << 21) | /* C - hook up r11 */
722 (8 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
724 static const BYTE unwind_info_9[] = { DW(unwind_info_9_packed) };
726 static const struct results_arm results_9[] =
728 /* offset fp handler pc frame offset registers */
729 { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
730 { 0x02, 0x00, 0, 0x00, 0x004, TRUE, { {lr,0x00}, {-1,-1} }},
731 { 0x06, 0x00, 0, 0x08, 0x00c, TRUE, { {lr,0x08}, {d8,0x400000000}, {-1,-1} }},
732 { 0x08, 0x00, 0, 0x28, 0x02c, TRUE, { {lr,0x28}, {d8,0x2400000020}, {-1,-1} }},
733 { 0x0a, 0x00, 0, 0x28, 0x02c, TRUE, { {lr,0x28}, {d8,0x2400000020}, {-1,-1} }},
734 #if 0
735 /* L=1, R=1, Ret>0 seems to get incorrect handling of the epilogue */
736 { 0x0c, 0x00, 0, ORIG_LR, 0x008, TRUE, { {d8,0x400000000}, {-1,-1} }},
737 { 0x10, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
738 #endif
739 { 0x14, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
742 static const BYTE function_10[] =
744 0x2d, 0xe9, 0x00, 0x48, /* 00: push.w {r11, lr} */
745 0xeb, 0x46, /* 04: mov r11, sp */
746 0x2d, 0xed, 0x04, 0x8b, /* 06: vpush {d8-d9} */
747 0x84, 0xb0, /* 0a: sub sp, sp, #16 */
748 0x00, 0xbf, /* 0c: nop */
749 0x04, 0xb0, /* 0e: add sp, sp, #16 */
750 0xbd, 0xec, 0x04, 0x8b, /* 10: vpop {d8-d9} */
751 0xbd, 0xe8, 0x00, 0x48, /* 14: pop.w {r11, lr} */
752 0x70, 0x47, /* 18: bx lr */
755 static const DWORD unwind_info_10_packed =
756 (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
757 (sizeof(function_10)/2 << 2) | /* FunctionLength */
758 (1 << 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_10[] = { DW(unwind_info_10_packed) };
768 static const struct results_arm results_10[] =
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 #if 0
778 /* L=1, R=1, Ret>0 seems to get incorrect handling of the epilogue */
779 { 0x10, 0x00, 0, 0x14, 0x018, TRUE, { {r11,0x10}, {lr,0x14}, {d8,0x400000000}, {d9,0xc00000008}, {-1,-1} }},
780 { 0x14, 0x00, 0, 0x04, 0x008, TRUE, { {r11,0x00}, {lr,0x04}, {-1,-1} }},
781 #endif
782 { 0x18, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
785 static const BYTE function_11[] =
787 0x2d, 0xe9, 0x00, 0x48, /* 00: push.w {r11, lr} */
788 0xeb, 0x46, /* 04: mov r11, sp */
789 0x2d, 0xed, 0x04, 0x8b, /* 06: vpush {d8-d9} */
790 0x84, 0xb0, /* 0a: sub sp, sp, #16 */
791 0x00, 0xbf, /* 0c: nop */
792 0x04, 0xb0, /* 0e: add sp, sp, #16 */
793 0xbd, 0xec, 0x04, 0x8b, /* 10: vpop {d8-d9} */
794 0xbd, 0xe8, 0x00, 0x88, /* 14: pop.w {r11, pc} */
797 static const DWORD unwind_info_11_packed =
798 (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
799 (sizeof(function_11)/2 << 2) | /* FunctionLength */
800 (0 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
801 (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
802 (1 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
803 (1 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
804 (1 << 20) | /* L, push LR */
805 (1 << 21) | /* C - hook up r11 */
806 (4 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
808 static const BYTE unwind_info_11[] = { DW(unwind_info_11_packed) };
810 static const struct results_arm results_11[] =
812 /* offset fp handler pc frame offset registers */
813 { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
814 { 0x04, 0x00, 0, 0x04, 0x008, TRUE, { {r11,0x00}, {lr,0x04}, {-1,-1} }},
815 { 0x06, 0x00, 0, 0x04, 0x008, TRUE, { {r11,0x00}, {lr,0x04}, {-1,-1} }},
816 { 0x0a, 0x00, 0, 0x14, 0x018, TRUE, { {r11,0x10}, {lr,0x14}, {d8,0x400000000}, {d9,0xc00000008}, {-1,-1} }},
817 { 0x0c, 0x00, 0, 0x24, 0x028, TRUE, { {r11,0x20}, {lr,0x24}, {d8,0x1400000010}, {d9,0x1c00000018}, {-1,-1} }},
818 { 0x0e, 0x00, 0, 0x24, 0x028, TRUE, { {r11,0x20}, {lr,0x24}, {d8,0x1400000010}, {d9,0x1c00000018}, {-1,-1} }},
819 { 0x10, 0x00, 0, 0x14, 0x018, TRUE, { {r11,0x10}, {lr,0x14}, {d8,0x400000000}, {d9,0xc00000008}, {-1,-1} }},
820 { 0x14, 0x00, 0, 0x04, 0x008, TRUE, { {r11,0x00}, {lr,0x04}, {-1,-1} }},
823 static const BYTE function_12[] =
825 0x2d, 0xed, 0x0e, 0x8b, /* 00: vpush {d8-d14} */
826 0x84, 0xb0, /* 04: sub sp, sp, #16 */
827 0x00, 0xbf, /* 06: nop */
828 0x04, 0xb0, /* 08: add sp, sp, #16 */
829 0xbd, 0xec, 0x0e, 0x8b, /* 0a: vpop {d8-d14} */
830 0x00, 0xf0, 0x00, 0xb8, /* 0e: b tailcall */
833 static const DWORD unwind_info_12_packed =
834 (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
835 (sizeof(function_12)/2 << 2) | /* FunctionLength */
836 (2 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
837 (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
838 (6 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
839 (1 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
840 (0 << 20) | /* L, push LR */
841 (0 << 21) | /* C - hook up r11 */
842 (4 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
844 static const BYTE unwind_info_12[] = { DW(unwind_info_12_packed) };
846 static const struct results_arm results_12[] =
848 /* offset fp handler pc frame offset registers */
849 { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
850 { 0x04, 0x00, 0, ORIG_LR, 0x038, TRUE, { {d8,0x400000000}, {d9,0xc00000008}, {d10,0x1400000010}, {d11,0x1c00000018}, {d12,0x2400000020}, {d13,0x2c00000028}, {d14,0x3400000030}, {-1,-1} }},
851 { 0x06, 0x00, 0, ORIG_LR, 0x048, TRUE, { {d8,0x1400000010}, {d9,0x1c00000018}, {d10,0x2400000020}, {d11,0x2c00000028}, {d12,0x3400000030}, {d13,0x3c00000038}, {d14,0x4400000040}, {-1,-1} }},
852 { 0x08, 0x00, 0, ORIG_LR, 0x048, TRUE, { {d8,0x1400000010}, {d9,0x1c00000018}, {d10,0x2400000020}, {d11,0x2c00000028}, {d12,0x3400000030}, {d13,0x3c00000038}, {d14,0x4400000040}, {-1,-1} }},
853 { 0x0a, 0x00, 0, ORIG_LR, 0x038, TRUE, { {d8,0x400000000}, {d9,0xc00000008}, {d10,0x1400000010}, {d11,0x1c00000018}, {d12,0x2400000020}, {d13,0x2c00000028}, {d14,0x3400000030}, {-1,-1} }},
854 { 0x0e, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
857 static const BYTE function_13[] =
859 0x2d, 0xe9, 0xf0, 0x4f, /* 00: push.w {r4-r11, lr} */
860 0x0d, 0xf1, 0x1c, 0x0b, /* 04: add.w r11, sp, #28 */
861 0x85, 0xb0, /* 08: sub sp, sp, #20 */
862 0x00, 0xbf, /* 0a: nop */
863 0x05, 0xb0, /* 0c: add sp, sp, #20 */
864 0x2d, 0xe8, 0xf0, 0x8f, /* 0e: pop.w {r4-r11, lr} */
867 static const DWORD unwind_info_13_packed =
868 (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
869 (sizeof(function_13)/2 << 2) | /* FunctionLength */
870 (0 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
871 (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
872 (6 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
873 (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
874 (1 << 20) | /* L, push LR */
875 (1 << 21) | /* C - hook up r11 */
876 (5 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
878 static const BYTE unwind_info_13[] = { DW(unwind_info_13_packed) };
880 static const struct results_arm results_13[] =
882 /* offset fp handler pc frame offset registers */
883 { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
884 { 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} }},
885 { 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} }},
886 { 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} }},
887 { 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} }},
888 { 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} }},
891 static const BYTE function_14[] =
893 0x2d, 0xe9, 0xf0, 0x4f, /* 00: push.w {r4-r11, lr} */
894 0x85, 0xb0, /* 04: sub sp, sp, #20 */
895 0x00, 0xbf, /* 06: nop */
896 0x05, 0xb0, /* 08: add sp, sp, #20 */
897 0x2d, 0xe8, 0xf0, 0x8f, /* 0a: pop.w {r4-r11, lr} */
900 static const DWORD unwind_info_14_packed =
901 (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
902 (sizeof(function_14)/2 << 2) | /* FunctionLength */
903 (0 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
904 (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
905 (7 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
906 (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
907 (1 << 20) | /* L, push LR */
908 (0 << 21) | /* C - hook up r11 */
909 (5 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
911 static const BYTE unwind_info_14[] = { DW(unwind_info_14_packed) };
913 static const struct results_arm results_14[] =
915 /* offset fp handler pc frame offset registers */
916 { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
917 { 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} }},
918 { 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} }},
919 { 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} }},
920 { 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} }},
923 static const BYTE function_15[] =
925 0x0f, 0xb4, /* 00: push {r0-r3} */
926 0x10, 0xb5, /* 02: push {r4,lr} */
927 0xad, 0xf5, 0x00, 0x7d, /* 04: sub sp, sp, #512 */
928 0x00, 0xbf, /* 08: nop */
929 0x0d, 0xf5, 0x00, 0x7d, /* 0a: add sp, sp, #512 */
930 0x10, 0xb5, /* 0e: pop {r4} */
931 0x5d, 0xf8, 0x14, 0xfb, /* 10: ldr pc, [sp], #20 */
934 static const DWORD unwind_info_15_packed =
935 (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
936 (sizeof(function_15)/2 << 2) | /* FunctionLength */
937 (0 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
938 (1 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
939 (0 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
940 (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
941 (1 << 20) | /* L, push LR */
942 (0 << 21) | /* C - hook up r11 */
943 (128 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
945 static const BYTE unwind_info_15[] = { DW(unwind_info_15_packed) };
947 static const struct results_arm results_15[] =
949 /* offset fp handler pc frame offset registers */
950 { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
951 { 0x02, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }},
952 { 0x04, 0x10, 0, 0x04, 0x018, TRUE, { {r4,0x00}, {lr,0x04}, {-1,-1} }},
953 { 0x08, 0x10, 0, 0x204, 0x218, TRUE, { {r4,0x200}, {lr,0x204}, {-1,-1} }},
954 { 0x0a, 0x10, 0, 0x204, 0x218, TRUE, { {r4,0x200}, {lr,0x204}, {-1,-1} }},
955 { 0x0e, 0x10, 0, 0x04, 0x018, TRUE, { {r4,0x00}, {lr,0x04}, {-1,-1} }},
956 { 0x10, 0x10, 0, 0x00, 0x014, TRUE, { {lr,0x00}, {-1,-1} }},
959 static const BYTE function_16[] =
961 0x0f, 0xb4, /* 00: push {r0-r3} */
962 0x2d, 0xe9, 0x00, 0x48, /* 02: push.w {r11,lr} */
963 0xeb, 0x46, /* 06: mov r11, sp */
964 0x00, 0xbf, /* 08: nop */
965 0xbd, 0xe8, 0x10, 0x40, /* 0a: pop.w {r11,lr} */
966 0x04, 0xb0, /* 0e: add sp, sp, #16 */
967 0x00, 0xf0, 0x00, 0xb8, /* 10: b tailcall */
970 static const DWORD unwind_info_16_packed =
971 (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
972 (sizeof(function_16)/2 << 2) | /* FunctionLength */
973 (2 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
974 (1 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
975 (7 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
976 (1 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
977 (1 << 20) | /* L, push LR */
978 (1 << 21) | /* C - hook up r11 */
979 (0 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
981 static const BYTE unwind_info_16[] = { DW(unwind_info_16_packed) };
983 static const struct results_arm results_16[] =
985 /* offset fp handler pc frame offset registers */
986 { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
987 { 0x02, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }},
988 { 0x06, 0x10, 0, 0x04, 0x018, TRUE, { {r11,0x00}, {lr,0x04}, {-1,-1} }},
989 { 0x08, 0x10, 0, 0x04, 0x018, TRUE, { {r11,0x00}, {lr,0x04}, {-1,-1} }},
990 { 0x0a, 0x10, 0, 0x04, 0x018, TRUE, { {r11,0x00}, {lr,0x04}, {-1,-1} }},
991 { 0x0e, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }},
992 { 0x10, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
995 static const BYTE function_17[] =
997 0x0f, 0xb4, /* 00: push {r0-r3} */
998 0x10, 0xb4, /* 02: push {r4} */
999 0xad, 0xf5, 0x00, 0x7d, /* 04: sub sp, sp, #512 */
1000 0x00, 0xbf, /* 08: nop */
1001 0x0d, 0xf5, 0x00, 0x7d, /* 0a: add sp, sp, #512 */
1002 0x10, 0xbc, /* 0e: pop {r4} */
1003 0x04, 0xb0, /* 10: add sp, sp, #16 */
1004 0x70, 0x47, /* 12: bx lr */
1007 static const DWORD unwind_info_17_packed =
1008 (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
1009 (sizeof(function_17)/2 << 2) | /* FunctionLength */
1010 (1 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
1011 (1 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
1012 (0 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
1013 (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
1014 (0 << 20) | /* L, push LR */
1015 (0 << 21) | /* C - hook up r11 */
1016 (128 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
1018 static const BYTE unwind_info_17[] = { DW(unwind_info_17_packed) };
1020 static const struct results_arm results_17[] =
1022 /* offset fp handler pc frame offset registers */
1023 { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
1024 { 0x02, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }},
1025 { 0x04, 0x10, 0, ORIG_LR, 0x014, TRUE, { {r4,0x00}, {-1,-1} }},
1026 { 0x08, 0x10, 0, ORIG_LR, 0x214, TRUE, { {r4,0x200}, {-1,-1} }},
1027 { 0x0a, 0x10, 0, ORIG_LR, 0x214, TRUE, { {r4,0x200}, {-1,-1} }},
1028 { 0x0e, 0x10, 0, ORIG_LR, 0x014, TRUE, { {r4,0x00}, {-1,-1} }},
1029 { 0x10, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }},
1030 { 0x12, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
1033 static const BYTE function_18[] =
1035 0x08, 0xb5, /* 00: push {r3,lr} */
1036 0x00, 0xbf, /* 02: nop */
1037 0x08, 0xbd, /* 04: pop {r3,pc} */
1040 static const DWORD unwind_info_18_packed =
1041 (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
1042 (sizeof(function_18)/2 << 2) | /* FunctionLength */
1043 (0 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
1044 (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
1045 (7 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
1046 (1 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
1047 (1 << 20) | /* L, push LR */
1048 (0 << 21) | /* C - hook up r11 */
1049 (0x3fcu << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
1051 static const BYTE unwind_info_18[] = { DW(unwind_info_18_packed) };
1053 static const struct results_arm results_18[] =
1055 /* offset fp handler pc frame offset registers */
1056 { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
1057 { 0x02, 0x10, 0, 0x04, 0x008, TRUE, { {lr,0x04}, {-1,-1} }},
1058 { 0x04, 0x10, 0, 0x04, 0x008, TRUE, { {lr,0x04}, {-1,-1} }},
1061 static const BYTE function_19[] =
1063 0x0f, 0xb4, /* 00: push {r0-r3} */
1064 0x14, 0xb4, /* 02: push {r0-r4} */
1065 0x00, 0xbf, /* 04: nop */
1066 0x1f, 0xbc, /* 06: pop {r0-r4} */
1067 0x04, 0xb0, /* 08: add sp, sp, #16 */
1068 0x70, 0x47, /* 0a: bx lr */
1071 static const DWORD unwind_info_19_packed =
1072 (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
1073 (sizeof(function_19)/2 << 2) | /* FunctionLength */
1074 (1 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
1075 (1 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
1076 (0 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
1077 (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
1078 (0 << 20) | /* L, push LR */
1079 (0 << 21) | /* C - hook up r11 */
1080 (0x3ffu << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
1082 static const BYTE unwind_info_19[] = { DW(unwind_info_19_packed) };
1084 static const struct results_arm results_19[] =
1086 /* offset fp handler pc frame offset registers */
1087 { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
1088 { 0x02, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }},
1089 { 0x04, 0x10, 0, ORIG_LR, 0x024, TRUE, { {r4,0x10}, {-1,-1} }},
1090 { 0x06, 0x10, 0, ORIG_LR, 0x024, TRUE, { {r4,0x10}, {-1,-1} }},
1091 { 0x08, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }},
1092 { 0x0a, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
1095 static const BYTE function_20[] =
1097 0x0f, 0xb4, /* 00: push {r0-r3} */
1098 0x14, 0xb4, /* 02: push {r0-r4} */
1099 0x00, 0xbf, /* 04: nop */
1100 0x04, 0xb0, /* 06: add sp, sp, #16 */
1101 0x10, 0xbc, /* 08: pop {r4} */
1102 0x04, 0xb0, /* 0a: add sp, sp, #16 */
1103 0x70, 0x47, /* 0c: bx lr */
1106 static const DWORD unwind_info_20_packed =
1107 (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
1108 (sizeof(function_20)/2 << 2) | /* FunctionLength */
1109 (1 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
1110 (1 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
1111 (0 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
1112 (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
1113 (0 << 20) | /* L, push LR */
1114 (0 << 21) | /* C - hook up r11 */
1115 (0x3f7u << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
1117 static const BYTE unwind_info_20[] = { DW(unwind_info_20_packed) };
1119 static const struct results_arm results_20[] =
1121 /* offset fp handler pc frame offset registers */
1122 { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
1123 { 0x02, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }},
1124 { 0x04, 0x10, 0, ORIG_LR, 0x024, TRUE, { {r4,0x10}, {-1,-1} }},
1125 { 0x06, 0x10, 0, ORIG_LR, 0x024, TRUE, { {r4,0x10}, {-1,-1} }},
1126 { 0x08, 0x10, 0, ORIG_LR, 0x014, TRUE, { {r4,0x00}, {-1,-1} }},
1127 { 0x0a, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }},
1128 { 0x0c, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
1131 static const BYTE function_21[] =
1133 0x0f, 0xb4, /* 00: push {r0-r3} */
1134 0x10, 0xb4, /* 02: push {r4} */
1135 0x84, 0xb0, /* 04: sub sp, sp, #16 */
1136 0x00, 0xbf, /* 06: nop */
1137 0x1f, 0xbc, /* 08: pop {r0-r4} */
1138 0x04, 0xb0, /* 0a: add sp, sp, #16 */
1139 0x70, 0x47, /* 0c: bx lr */
1142 static const DWORD unwind_info_21_packed =
1143 (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
1144 (sizeof(function_21)/2 << 2) | /* FunctionLength */
1145 (1 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
1146 (1 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
1147 (0 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
1148 (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
1149 (0 << 20) | /* L, push LR */
1150 (0 << 21) | /* C - hook up r11 */
1151 (0x3fbu << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
1153 static const BYTE unwind_info_21[] = { DW(unwind_info_21_packed) };
1155 static const struct results_arm results_21[] =
1157 /* offset fp handler pc frame offset registers */
1158 { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
1159 { 0x02, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }},
1160 { 0x04, 0x10, 0, ORIG_LR, 0x014, TRUE, { {r4,0x00}, {-1,-1} }},
1161 { 0x06, 0x10, 0, ORIG_LR, 0x024, TRUE, { {r4,0x10}, {-1,-1} }},
1162 { 0x08, 0x10, 0, ORIG_LR, 0x024, TRUE, { {r4,0x10}, {-1,-1} }},
1163 { 0x0a, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }},
1164 { 0x0c, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
1167 static const BYTE function_22[] =
1169 0x00, 0xbf, /* 00: nop */
1170 0x00, 0xbf, /* 02: nop */
1171 0x0d, 0xf5, 0x00, 0x7d, /* 04: add sp, sp, #512 */
1172 0x10, 0xb5, /* 08: pop {r4} */
1173 0x5d, 0xf8, 0x14, 0xfb, /* 0a: ldr pc, [sp], #20 */
1176 static const DWORD unwind_info_22_packed =
1177 (2 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
1178 (sizeof(function_22)/2 << 2) | /* FunctionLength */
1179 (0 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
1180 (1 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
1181 (0 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
1182 (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
1183 (1 << 20) | /* L, push LR */
1184 (0 << 21) | /* C - hook up r11 */
1185 (128 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
1187 static const BYTE unwind_info_22[] = { DW(unwind_info_22_packed) };
1189 static const struct results_arm results_22[] =
1191 /* offset fp handler pc frame offset registers */
1192 { 0x00, 0x10, 0, 0x204, 0x218, TRUE, { {r4,0x200}, {lr,0x204}, {-1,-1} }},
1193 { 0x02, 0x10, 0, 0x204, 0x218, TRUE, { {r4,0x200}, {lr,0x204}, {-1,-1} }},
1194 { 0x04, 0x10, 0, 0x204, 0x218, TRUE, { {r4,0x200}, {lr,0x204}, {-1,-1} }},
1195 { 0x08, 0x10, 0, 0x04, 0x018, TRUE, { {r4,0x00}, {lr,0x04}, {-1,-1} }},
1196 { 0x0a, 0x10, 0, 0x00, 0x014, TRUE, { {lr,0x00}, {-1,-1} }},
1199 static const BYTE function_23[] =
1201 0x0f, 0xb4, /* 00: push {r0-r3} */
1202 0x10, 0xb5, /* 02: push {r4,lr} */
1203 0xad, 0xf5, 0x00, 0x7d, /* 04: sub sp, sp, #512 */
1204 0x00, 0xbf, /* 08: nop */
1205 0x00, 0xbf, /* 0a: nop */
1208 static const DWORD unwind_info_23_packed =
1209 (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
1210 (sizeof(function_23)/2 << 2) | /* FunctionLength */
1211 (3 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
1212 (1 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
1213 (0 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
1214 (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
1215 (1 << 20) | /* L, push LR */
1216 (0 << 21) | /* C - hook up r11 */
1217 (128 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
1219 static const BYTE unwind_info_23[] = { DW(unwind_info_23_packed) };
1221 static const struct results_arm results_23[] =
1223 /* offset fp handler pc frame offset registers */
1224 { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
1225 { 0x02, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }},
1226 { 0x04, 0x10, 0, 0x04, 0x018, TRUE, { {r4,0x00}, {lr,0x04}, {-1,-1} }},
1227 { 0x08, 0x10, 0, 0x204, 0x218, TRUE, { {r4,0x200}, {lr,0x204}, {-1,-1} }},
1228 { 0x0a, 0x10, 0, 0x204, 0x218, TRUE, { {r4,0x200}, {lr,0x204}, {-1,-1} }},
1231 static const BYTE function_24[] =
1233 0x2d, 0xe9, 0xfc, 0x48, /* 00: push.w {r2-r7,r11,lr} */
1234 0x0d, 0xf1, 0x18, 0x0b, /* 04: add r11, sp, #24 */
1235 0x00, 0xbf, /* 08: nop */
1236 0x02, 0xb0, /* 0a: add sp, sp, #8 */
1237 0xbd, 0xe8, 0x10, 0x48, /* 0c: pop.w {r4-r7,r11,pc} */
1240 static const DWORD unwind_info_24_packed =
1241 (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
1242 (sizeof(function_24)/2 << 2) | /* FunctionLength */
1243 (0 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
1244 (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
1245 (3 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
1246 (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
1247 (1 << 20) | /* L, push LR */
1248 (1 << 21) | /* C - hook up r11 */
1249 (0x3f5u << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
1251 static const BYTE unwind_info_24[] = { DW(unwind_info_24_packed) };
1253 static const struct results_arm results_24[] =
1255 /* offset fp handler pc frame offset registers */
1256 { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
1257 { 0x04, 0x10, 0, 0x1c, 0x020, TRUE, { {r4,0x08}, {r5,0x0c}, {r6,0x10}, {r7,0x14}, {r11,0x18}, {lr,0x1c}, {-1,-1} }},
1258 { 0x08, 0x10, 0, 0x1c, 0x020, TRUE, { {r4,0x08}, {r5,0x0c}, {r6,0x10}, {r7,0x14}, {r11,0x18}, {lr,0x1c}, {-1,-1} }},
1259 { 0x0a, 0x10, 0, 0x1c, 0x020, TRUE, { {r4,0x08}, {r5,0x0c}, {r6,0x10}, {r7,0x14}, {r11,0x18}, {lr,0x1c}, {-1,-1} }},
1260 { 0x0c, 0x10, 0, 0x14, 0x018, TRUE, { {r4,0x00}, {r5,0x04}, {r6,0x08}, {r7,0x0c}, {r11,0x10}, {lr,0x14}, {-1,-1} }},
1263 static const BYTE function_25[] =
1265 0x2d, 0xe9, 0xf0, 0x48, /* 00: push.w {r4-r7,r11,lr} */
1266 0x0d, 0xf1, 0x10, 0x0b, /* 04: add r11, sp, #16 */
1267 0x82, 0xb0, /* 08: sub sp, sp, #8 */
1268 0x00, 0xbf, /* 0a: nop */
1269 0xbd, 0xe8, 0xfc, 0x48, /* 0c: pop.w {r2-r7,r11,pc} */
1272 static const DWORD unwind_info_25_packed =
1273 (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
1274 (sizeof(function_25)/2 << 2) | /* FunctionLength */
1275 (0 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
1276 (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
1277 (3 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
1278 (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
1279 (1 << 20) | /* L, push LR */
1280 (1 << 21) | /* C - hook up r11 */
1281 (0x3f9u << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
1283 static const BYTE unwind_info_25[] = { DW(unwind_info_25_packed) };
1285 static const struct results_arm results_25[] =
1287 /* offset fp handler pc frame offset registers */
1288 { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
1289 { 0x04, 0x10, 0, 0x14, 0x018, TRUE, { {r4,0x00}, {r5,0x04}, {r6,0x08}, {r7,0x0c}, {r11,0x10}, {lr,0x14}, {-1,-1} }},
1290 { 0x08, 0x10, 0, 0x14, 0x018, TRUE, { {r4,0x00}, {r5,0x04}, {r6,0x08}, {r7,0x0c}, {r11,0x10}, {lr,0x14}, {-1,-1} }},
1291 { 0x0a, 0x10, 0, 0x1c, 0x020, TRUE, { {r4,0x08}, {r5,0x0c}, {r6,0x10}, {r7,0x14}, {r11,0x18}, {lr,0x1c}, {-1,-1} }},
1292 { 0x0c, 0x10, 0, 0x1c, 0x020, TRUE, { {r4,0x08}, {r5,0x0c}, {r6,0x10}, {r7,0x14}, {r11,0x18}, {lr,0x1c}, {-1,-1} }},
1295 static const BYTE function_26[] =
1297 0x2d, 0xe9, 0x10, 0x08, /* 00: push.w {r4, r11} */
1298 0x0d, 0xf1, 0x1c, 0x0b, /* 04: add.w r11, sp, #28 */
1299 0x84, 0xb0, /* 08: sub sp, sp, #16 */
1300 0x00, 0xbf, /* 0a: nop */
1301 0x04, 0xb0, /* 0c: add sp, sp, #16 */
1302 0xbd, 0xe8, 0x10, 0x08, /* 0e: pop.w {r4, r11} */
1303 0x70, 0x47, /* 12: bx lr */
1306 /* C=1, L=0 is disallowed by doc */
1307 static const DWORD unwind_info_26_packed =
1308 (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
1309 (sizeof(function_26)/2 << 2) | /* FunctionLength */
1310 (1 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
1311 (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
1312 (0 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
1313 (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
1314 (0 << 20) | /* L, push LR */
1315 (1 << 21) | /* C - hook up r11 */
1316 (4 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
1318 static const BYTE unwind_info_26[] = { DW(unwind_info_26_packed) };
1320 static const struct results_arm results_26[] =
1322 /* offset fp handler pc frame offset registers */
1323 { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
1324 { 0x04, 0x10, 0, ORIG_LR, 0x008, TRUE, { {r4,0x00}, {r11,0x04}, {-1,-1} }},
1325 { 0x08, 0x10, 0, ORIG_LR, 0x008, TRUE, { {r4,0x00}, {r11,0x04}, {-1,-1} }},
1326 { 0x0a, 0x10, 0, ORIG_LR, 0x018, TRUE, { {r4,0x10}, {r11,0x14}, {-1,-1} }},
1327 { 0x0c, 0x10, 0, ORIG_LR, 0x018, TRUE, { {r4,0x10}, {r11,0x14}, {-1,-1} }},
1328 { 0x0e, 0x10, 0, ORIG_LR, 0x008, TRUE, { {r4,0x00}, {r11,0x04}, {-1,-1} }},
1329 { 0x12, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
1332 static const BYTE function_27[] =
1334 0x0e, 0xb4, /* 00: push {r1-r3} */
1335 0x00, 0xbf, /* 02: nop */
1336 0x03, 0xb0, /* 04: add sp, sp, #12 */
1337 0x70, 0x47, /* 06: bx lr */
1340 static const DWORD unwind_info_27_packed =
1341 (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
1342 (sizeof(function_27)/2 << 2) | /* FunctionLength */
1343 (1 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
1344 (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
1345 (7 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
1346 (1 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
1347 (0 << 20) | /* L, push LR */
1348 (0 << 21) | /* C - hook up r11 */
1349 (0x3f6u << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
1351 static const BYTE unwind_info_27[] = { DW(unwind_info_27_packed) };
1353 static const struct results_arm results_27[] =
1355 /* offset fp handler pc frame offset registers */
1356 { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
1357 { 0x02, 0x10, 0, ORIG_LR, 0x00c, TRUE, { {-1,-1} }},
1358 { 0x04, 0x10, 0, ORIG_LR, 0x00c, TRUE, { {-1,-1} }},
1359 { 0x06, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
1362 static const BYTE function_28[] =
1364 0x0e, 0xb4, /* 00: push {r1-r3} */
1365 0x00, 0xbf, /* 02: nop */
1366 0x03, 0xb0, /* 04: add sp, sp, #12 */
1367 0x70, 0x47, /* 06: bx lr */
1370 static const DWORD unwind_info_28_packed =
1371 (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
1372 (sizeof(function_28)/2 << 2) | /* FunctionLength */
1373 (1 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
1374 (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
1375 (7 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
1376 (1 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
1377 (0 << 20) | /* L, push LR */
1378 (0 << 21) | /* C - hook up r11 */
1379 (0x3fau << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
1381 static const BYTE unwind_info_28[] = { DW(unwind_info_28_packed) };
1383 static const struct results_arm results_28[] =
1385 /* offset fp handler pc frame offset registers */
1386 { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
1387 { 0x02, 0x10, 0, ORIG_LR, 0x00c, TRUE, { {-1,-1} }},
1388 { 0x04, 0x10, 0, ORIG_LR, 0x00c, TRUE, { {-1,-1} }},
1389 { 0x06, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
1392 static const BYTE function_29[] =
1394 0x00, 0xbf, /* 00: nop */
1395 0x00, 0xbf, /* 02: nop */
1398 static const DWORD unwind_info_29_header =
1399 (sizeof(function_29)/2) | /* function length */
1400 (0 << 20) | /* X */
1401 (0 << 21) | /* E */
1402 (0 << 22) | /* F */
1403 (0 << 23) | /* epilog */
1404 (1 << 28); /* codes, (sizeof(unwind_info_29)-headers+3)/4 */
1406 static const BYTE unwind_info_29[] =
1408 DW(unwind_info_29_header),
1409 UWOP_MSFT_OP_CONTEXT,
1410 UWOP_END,
1413 static const struct results_arm results_29[] =
1415 /* offset fp handler pc frame offset registers */
1416 { 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},
1417 {d0,0x5400000050}, {d1,0x5c00000058}, {d2,0x6400000060}, {d3,0x6c00000068}, {d4,0x7400000070}, {d5,0x7c00000078}, {d6,0x8400000080}, {d7,0x8c00000088},
1418 {d8,0x9400000090}, {d9,0x9c00000098}, {d10,0xa4000000a0}, {d11,0xac000000a8}, {d12,0xb4000000b0}, {d13,0xbc000000b8}, {d14,0xc4000000c0}, {d15,0xcc000000c8},
1419 {d16,0xd4000000d0}, {d17,0xdc000000d8}, {d18,0xe4000000e0}, {d19,0xec000000e8}, {d20,0xf4000000f0}, {d21,0xfc000000f8}, {d22,0x10400000100}, {d23,0x10c00000108},
1420 {d24,0x11400000110}, {d25,0x11c00000118}, {d26,0x12400000120}, {d27,0x12c00000128}, {d28,0x13400000130}, {d29,0x13c00000138}, {d30,0x14400000140}, {d31,0x14c00000148} }},
1423 static const BYTE function_30[] =
1425 0x00, 0xbf, /* 00: nop */
1426 0x00, 0xbf, /* 02: nop */
1427 0x00, 0xbf, /* 04: nop */
1428 0x00, 0xbf, /* 06: nop */
1431 static const DWORD unwind_info_30_header =
1432 (sizeof(function_30)/2) | /* function length */
1433 (0 << 20) | /* X */
1434 (0 << 21) | /* E */
1435 (0 << 22) | /* F */
1436 (0 << 23) | /* epilog */
1437 (2 << 28); /* codes, (sizeof(unwind_info_30)-headers+3)/4 */
1439 static const BYTE unwind_info_30[] =
1441 DW(unwind_info_30_header),
1442 UWOP_ALLOC_SMALL(12), /* sub sp, sp, #12 */
1443 UWOP_SAVE_REGS((1<<lr)), /* push {lr} */
1444 UWOP_MSFT_OP_MACHINE_FRAME,
1445 UWOP_END,
1448 static const struct results_arm results_30[] =
1450 /* offset fp handler pc frame offset registers */
1451 { 0x00, 0x10, 0, 0x04, 0x00, FALSE, { {sp,0x00}, {-1,-1} }},
1452 { 0x02, 0x10, 0, 0x08, 0x04, FALSE, { {lr,0x00}, {sp,0x04}, {-1,-1} }},
1453 { 0x04, 0x10, 0, 0x14, 0x10, FALSE, { {lr,0x0c}, {sp,0x10}, {-1,-1} }},
1456 static const BYTE function_31[] =
1458 0x00, 0xbf, /* 00: nop */
1459 0x00, 0xbf, /* 02: nop */
1462 static const struct results_arm results_31[] =
1464 /* offset fp handler pc frame offset registers */
1465 { 0x00, 0, -1, ORIG_LR, 0x00, TRUE, { {-1,-1} }},
1466 { 0x02, 0, -1, ORIG_LR, 0x00, TRUE, { {-1,-1} }},
1467 { 0x04, 0, -2, 0, 0xdeadbeef, FALSE, { {-1,-1} }},
1470 static const struct unwind_test_arm tests[] =
1472 #define TEST(func, unwind, size, results) \
1473 { func, sizeof(func), unwind, size, results, ARRAY_SIZE(results) }
1474 TEST(function_0, unwind_info_0, sizeof(unwind_info_0), results_0),
1475 TEST(function_1, unwind_info_1, sizeof(unwind_info_1), results_1),
1476 TEST(function_2, unwind_info_2, sizeof(unwind_info_2), results_2),
1477 TEST(function_3, unwind_info_3, sizeof(unwind_info_3), results_3),
1478 TEST(function_4, unwind_info_4, sizeof(unwind_info_4), results_4),
1479 TEST(function_5, unwind_info_5, sizeof(unwind_info_5), results_5),
1480 TEST(function_6, unwind_info_6, 0, results_6),
1481 TEST(function_7, unwind_info_7, 0, results_7),
1482 TEST(function_8, unwind_info_8, 0, results_8),
1483 TEST(function_9, unwind_info_9, 0, results_9),
1484 TEST(function_10, unwind_info_10, 0, results_10),
1485 TEST(function_11, unwind_info_11, 0, results_11),
1486 TEST(function_12, unwind_info_12, 0, results_12),
1487 TEST(function_13, unwind_info_13, 0, results_13),
1488 TEST(function_14, unwind_info_14, 0, results_14),
1489 TEST(function_15, unwind_info_15, 0, results_15),
1490 TEST(function_16, unwind_info_16, 0, results_16),
1491 TEST(function_17, unwind_info_17, 0, results_17),
1492 TEST(function_18, unwind_info_18, 0, results_18),
1493 TEST(function_19, unwind_info_19, 0, results_19),
1494 TEST(function_20, unwind_info_20, 0, results_20),
1495 TEST(function_21, unwind_info_21, 0, results_21),
1496 TEST(function_22, unwind_info_22, 0, results_22),
1497 TEST(function_23, unwind_info_23, 0, results_23),
1498 TEST(function_24, unwind_info_24, 0, results_24),
1499 TEST(function_25, unwind_info_25, 0, results_25),
1500 TEST(function_26, unwind_info_26, 0, results_26),
1501 TEST(function_27, unwind_info_27, 0, results_27),
1502 TEST(function_28, unwind_info_28, 0, results_28),
1503 TEST(function_29, unwind_info_29, sizeof(unwind_info_29), results_29),
1504 TEST(function_30, unwind_info_30, sizeof(unwind_info_30), results_30),
1505 TEST(function_31, NULL, 0, results_31),
1506 #undef TEST
1508 unsigned int i;
1510 for (i = 0; i < ARRAY_SIZE(tests); i++)
1511 call_virtual_unwind_arm( i, &tests[i] );
1514 #endif /* __arm__ */
1516 #if defined(__aarch64__) || defined(__x86_64__)
1518 #define UWOP_TWOBYTES(x) (((x) >> 8) & 0xff), ((x) & 0xff)
1520 #define UWOP_ALLOC_SMALL(size) (0x00 | (size/16))
1521 #define UWOP_SAVE_R19R20_X(offset) (0x20 | (offset/8))
1522 #define UWOP_SAVE_FPLR(offset) (0x40 | (offset/8))
1523 #define UWOP_SAVE_FPLR_X(offset) (0x80 | (offset/8 - 1))
1524 #define UWOP_ALLOC_MEDIUM(size) UWOP_TWOBYTES((0xC0 << 8) | (size/16))
1525 #define UWOP_SAVE_REGP(reg, offset) UWOP_TWOBYTES((0xC8 << 8) | ((reg - 19) << 6) | (offset/8))
1526 #define UWOP_SAVE_REGP_X(reg, offset) UWOP_TWOBYTES((0xCC << 8) | ((reg - 19) << 6) | (offset/8 - 1))
1527 #define UWOP_SAVE_REG(reg, offset) UWOP_TWOBYTES((0xD0 << 8) | ((reg - 19) << 6) | (offset/8))
1528 #define UWOP_SAVE_REG_X(reg, offset) UWOP_TWOBYTES((0xD4 << 8) | ((reg - 19) << 5) | (offset/8 - 1))
1529 #define UWOP_SAVE_LRP(reg, offset) UWOP_TWOBYTES((0xD6 << 8) | ((reg - 19)/2 << 6) | (offset/8))
1530 #define UWOP_SAVE_FREGP(reg, offset) UWOP_TWOBYTES((0xD8 << 8) | ((reg - 8) << 6) | (offset/8))
1531 #define UWOP_SAVE_FREGP_X(reg, offset) UWOP_TWOBYTES((0xDA << 8) | ((reg - 8) << 6) | (offset/8 - 1))
1532 #define UWOP_SAVE_FREG(reg, offset) UWOP_TWOBYTES((0xDC << 8) | ((reg - 8) << 6) | (offset/8))
1533 #define UWOP_SAVE_FREG_X(reg, offset) UWOP_TWOBYTES((0xDE << 8) | ((reg - 8) << 5) | (offset/8 - 1))
1534 #define UWOP_ALLOC_LARGE(size) UWOP_TWOBYTES((0xE0 << 8) | ((size/16) >> 16)), UWOP_TWOBYTES(size/16)
1535 #define UWOP_SET_FP 0xE1
1536 #define UWOP_ADD_FP(offset) UWOP_TWOBYTES((0xE2 << 8) | (offset/8))
1537 #define UWOP_NOP 0xE3
1538 #define UWOP_END 0xE4
1539 #define UWOP_END_C 0xE5
1540 #define UWOP_SAVE_NEXT 0xE6
1541 #define UWOP_SAVE_ANY_REG(reg,offset) 0xE7,(reg),(offset)
1542 #define UWOP_TRAP_FRAME 0xE8
1543 #define UWOP_MACHINE_FRAME 0xE9
1544 #define UWOP_CONTEXT 0xEA
1545 #define UWOP_EC_CONTEXT 0xEB
1546 #define UWOP_CLEAR_UNWOUND_TO_CALL 0xEC
1548 struct results_arm64
1550 int pc_offset; /* pc offset from code start */
1551 int fp_offset; /* fp offset from stack pointer */
1552 int handler; /* expect handler to be set? */
1553 ULONG_PTR pc; /* expected final pc value */
1554 ULONG_PTR frame; /* expected frame return value */
1555 int frame_offset; /* whether the frame return value is an offset or an absolute value */
1556 ULONG_PTR regs[48][2]; /* expected values for registers */
1559 struct unwind_test_arm64
1561 const BYTE *function;
1562 size_t function_size;
1563 const BYTE *unwind_info;
1564 size_t unwind_size;
1565 const struct results_arm64 *results;
1566 unsigned int nb_results;
1567 int unwound_clear;
1568 int last_set_reg_ptr;
1569 int stack_value_index;
1570 ULONG64 stack_value;
1573 enum regs_arm64
1575 x0, x1, x2, x3, x4, x5, x6, x7,
1576 x8, x9, x10, x11, x12, x13, x14, x15,
1577 x16, x17, x18, x19, x20, x21, x22, x23,
1578 x24, x25, x26, x27, x28, x29, lr, sp,
1579 d0, d1, d2, d3, d4, d5, d6, d7,
1580 d8, d9, d10, d11, d12, d13, d14, d15
1583 static const char * const reg_names_arm64[48] =
1585 "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7",
1586 "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15",
1587 "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23",
1588 "x24", "x25", "x26", "x27", "x28", "x29", "lr", "sp",
1589 "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
1590 "d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15",
1593 #define ORIG_LR 0xCCCCCCCC
1595 static void call_virtual_unwind_arm64( void *code_mem, int testnum, const struct unwind_test_arm64 *test )
1597 static const int code_offset = 1024;
1598 static const int unwind_offset = 2048;
1599 void *data;
1600 #ifdef __x86_64__
1601 ARM64EC_NT_CONTEXT context, new_context;
1602 #else
1603 ARM64_NT_CONTEXT context, new_context;
1604 #endif
1605 PEXCEPTION_ROUTINE handler;
1606 ARM64_RUNTIME_FUNCTION runtime_func;
1607 KNONVOLATILE_CONTEXT_POINTERS ctx_ptr;
1608 UINT i, j, k;
1609 NTSTATUS status;
1610 ULONG64 fake_stack[256];
1611 ULONG64 frame, orig_pc, orig_fp, unset_reg, sp_offset = 0, regval, *regptr;
1612 static const UINT nb_regs = ARRAY_SIZE(test->results[i].regs);
1614 memcpy( (char *)code_mem + code_offset, test->function, test->function_size );
1615 if (test->unwind_info)
1617 memcpy( (char *)code_mem + unwind_offset, test->unwind_info, test->unwind_size );
1618 runtime_func.BeginAddress = code_offset;
1619 if (test->unwind_size)
1620 runtime_func.UnwindData = unwind_offset;
1621 else
1622 memcpy(&runtime_func.UnwindData, test->unwind_info, 4);
1625 for (i = 0; i < test->nb_results; i++)
1627 #ifdef __x86_64__
1628 if (test->results[i].handler == -2) continue; /* skip invalid leaf function test */
1629 #endif
1630 winetest_push_context( "%u/%u", testnum, i );
1631 memset( &ctx_ptr, 0x55, sizeof(ctx_ptr) );
1632 memset( &context, 0x55, sizeof(context) );
1633 memset( &unset_reg, 0x55, sizeof(unset_reg) );
1634 for (j = 0; j < 256; j++) fake_stack[j] = j * 8;
1635 if (test->stack_value_index != -1) fake_stack[test->stack_value_index] = test->stack_value;
1637 context.Sp = (ULONG_PTR)fake_stack;
1638 context.Lr = (ULONG_PTR)ORIG_LR;
1639 context.Fp = (ULONG_PTR)fake_stack + test->results[i].fp_offset;
1640 context.ContextFlags = 0xcccc;
1641 if (test->unwound_clear) context.ContextFlags |= CONTEXT_ARM64_UNWOUND_TO_CALL;
1643 orig_fp = context.Fp;
1644 orig_pc = (ULONG64)code_mem + code_offset + test->results[i].pc_offset;
1646 trace( "pc=%p (%02x) fp=%p sp=%p\n", (void *)orig_pc, *(UINT *)orig_pc, (void *)orig_fp, (void *)context.Sp );
1648 if (test->results[i].handler == -2) orig_pc = context.Lr;
1650 if (pRtlVirtualUnwind2)
1652 new_context = context;
1653 handler = (void *)0xdeadbeef;
1654 data = (void *)0xdeadbeef;
1655 frame = 0xdeadbeef;
1656 status = pRtlVirtualUnwind2( UNW_FLAG_EHANDLER, (ULONG_PTR)code_mem, orig_pc,
1657 test->unwind_info ? (RUNTIME_FUNCTION *)&runtime_func : NULL,
1658 (CONTEXT *)&new_context, NULL, &data,
1659 &frame, &ctx_ptr, NULL, NULL, &handler, 0 );
1660 if (test->results[i].handler > 0)
1662 ok( !status, "RtlVirtualUnwind2 failed %lx\n", status );
1663 ok( (char *)handler == (char *)code_mem + 0x200,
1664 "wrong handler %p/%p\n", handler, (char *)code_mem + 0x200 );
1665 if (handler) ok( *(DWORD *)data == 0x08070605,
1666 "wrong handler data %lx\n", *(DWORD *)data );
1668 else if (test->results[i].handler < -1)
1670 ok( status == STATUS_BAD_FUNCTION_TABLE, "RtlVirtualUnwind2 failed %lx\n", status );
1671 ok( handler == (void *)0xdeadbeef, "handler set to %p\n", handler );
1672 ok( data == (void *)0xdeadbeef, "handler data set to %p\n", data );
1674 else
1676 ok( !status, "RtlVirtualUnwind2 failed %lx\n", status );
1677 ok( handler == NULL, "handler %p instead of NULL\n", handler );
1678 ok( data == NULL, "handler data set to %p\n", data );
1682 data = (void *)0xdeadbeef;
1683 frame = 0xdeadbeef;
1684 handler = RtlVirtualUnwind( UNW_FLAG_EHANDLER, (ULONG64)code_mem, orig_pc,
1685 test->unwind_info ? (RUNTIME_FUNCTION *)&runtime_func : NULL,
1686 (CONTEXT *)&context, &data, &frame, &ctx_ptr );
1687 if (test->results[i].handler > 0)
1689 ok( (char *)handler == (char *)code_mem + 0x200,
1690 "wrong handler %p/%p\n", handler, (char *)code_mem + 0x200 );
1691 if (handler) ok( *(DWORD *)data == 0x08070605,
1692 "wrong handler data %lx\n", *(DWORD *)data );
1694 else
1696 ok( handler == NULL, "handler %p instead of NULL\n", handler );
1697 ok( data == (test->results[i].handler < -1 ? (void *)0xdeadbeef : NULL),
1698 "handler data set to %p/%p\n", data,
1699 (test->results[i].handler < 0 ? (void *)0xdeadbeef : NULL) );
1702 ok( context.Pc == test->results[i].pc, "wrong pc %p/%p\n",
1703 (void *)context.Pc, (void*)test->results[i].pc );
1704 ok( frame == (test->results[i].frame_offset ? (ULONG64)fake_stack : 0) + test->results[i].frame, "wrong frame %p/%p\n",
1705 (void *)frame, (char *)(test->results[i].frame_offset ? fake_stack : NULL) + test->results[i].frame );
1706 if (test->results[i].handler == -2) /* invalid leaf function */
1708 ok( context.ContextFlags == 0xcccc, "wrong flags %lx\n", context.ContextFlags );
1709 ok( context.Sp == (ULONG_PTR)fake_stack, "wrong sp %p/%p\n", (void *)context.Sp, fake_stack);
1711 else
1713 if (!test->unwound_clear || i < test->unwound_clear)
1714 ok( context.ContextFlags == (0xcccc | CONTEXT_ARM64_UNWOUND_TO_CALL),
1715 "wrong flags %lx\n", context.ContextFlags );
1716 else
1717 ok( context.ContextFlags == 0xcccc,
1718 "wrong flags %lx\n", context.ContextFlags );
1720 sp_offset = 0;
1721 for (k = 0; k < nb_regs; k++)
1723 if (test->results[i].regs[k][0] == -1)
1724 break;
1725 if (test->results[i].regs[k][0] == sp) {
1726 /* If sp is part of the registers list, treat it as an offset
1727 * between the returned frame pointer and the sp register. */
1728 sp_offset = test->results[i].regs[k][1];
1729 break;
1732 ok( frame - sp_offset == context.Sp, "wrong sp %p/%p\n",
1733 (void *)(frame - sp_offset), (void *)context.Sp);
1736 #ifdef __x86_64__
1737 for (j = 0; j < sizeof(ctx_ptr)/sizeof(void*); j++)
1738 ok( ((void **)&ctx_ptr)[j] == (void *)unset_reg,
1739 "ctx_ptr %u set to %p\n", j, ((void **)&ctx_ptr)[j] );
1740 #endif
1742 for (j = 0; j < 48; j++)
1744 switch (j)
1746 #define GET(i) case i: regval = context.X##i; break
1747 GET(0); GET(1); GET(2); GET(3); GET(4); GET(5); GET(6); GET(7);
1748 GET(8); GET(9); GET(10); GET(11); GET(12);
1749 GET(15); GET(19); GET(20); GET(21); GET(22); GET(25); GET(26); GET(27);
1750 #ifdef __x86_64__
1751 case x13: case x14: continue;
1752 case x16: regval = context.X16_0 | ((DWORD64)context.X16_1 << 16) | ((DWORD64)context.X16_2 << 32) | ((DWORD64)context.X16_3 << 48); break;
1753 case x17: regval = context.X17_0 | ((DWORD64)context.X17_1 << 16) | ((DWORD64)context.X17_2 << 32) | ((DWORD64)context.X17_3 << 48); break;
1754 case x18: case x23: case x24: case x28: continue;
1755 #else
1756 GET(13); GET(14); GET(16); GET(17); GET(18); GET(23); GET(24); GET(28);
1757 #endif
1758 #undef GET
1759 case x29: regval = context.Fp; break;
1760 case lr: regval = context.Lr; break;
1761 case sp: continue; /* Handling sp separately above */
1762 default: regval = context.V[j - d0].Low; break;
1765 regptr = NULL;
1766 #ifndef __x86_64__
1767 if (j >= 19 && j <= 30) regptr = (&ctx_ptr.X19)[j - 19];
1768 else if (j >= d8 && j <= d15) regptr = (&ctx_ptr.D8)[j - d8];
1769 #endif
1771 for (k = 0; k < nb_regs; k++)
1773 if (test->results[i].regs[k][0] == -1)
1775 k = nb_regs;
1776 break;
1778 if (test->results[i].regs[k][0] == j) break;
1781 if (k < nb_regs)
1783 ok( regval == test->results[i].regs[k][1],
1784 "register %s wrong %I64x/%I64x\n", reg_names_arm64[j], regval, test->results[i].regs[k][1] );
1785 if (regptr)
1787 if (test->last_set_reg_ptr && j > test->last_set_reg_ptr && j <= 30)
1788 ok( regptr == (void *)unset_reg, "register %s should not have pointer set\n", reg_names_arm64[j] );
1789 else
1791 ok( regptr != (void *)unset_reg, "register %s should have pointer set\n", reg_names_arm64[j] );
1792 if (regptr != (void *)unset_reg)
1793 ok( *regptr == regval, "register %s should have reg pointer to %I64x / %I64x\n",
1794 reg_names_arm64[j], *regptr, regval );
1798 else
1800 ok( k == nb_regs, "register %s should be set\n", reg_names_arm64[j] );
1801 ok( !regptr || regptr == (void *)unset_reg, "register %s should not have pointer set\n", reg_names_arm64[j] );
1802 if (j == lr)
1803 ok( context.Lr == ORIG_LR, "register lr wrong %I64x/unset\n", context.Lr );
1804 else if (j == x29)
1805 ok( context.Fp == orig_fp, "register fp wrong %I64x/unset\n", context.Fp );
1806 else
1807 ok( regval == unset_reg, "register %s wrong %I64x/unset\n", reg_names_arm64[j], regval);
1810 winetest_pop_context();
1814 #define DW(dword) ((dword >> 0) & 0xff), ((dword >> 8) & 0xff), ((dword >> 16) & 0xff), ((dword >> 24) & 0xff)
1816 static void test_virtual_unwind_arm64(void)
1818 static const BYTE function_0[] =
1820 0xff, 0x83, 0x00, 0xd1, /* 00: sub sp, sp, #32 */
1821 0xf3, 0x53, 0x01, 0xa9, /* 04: stp x19, x20, [sp, #16] */
1822 0x1f, 0x20, 0x03, 0xd5, /* 08: nop */
1823 0xf3, 0x53, 0x41, 0xa9, /* 0c: ldp x19, x20, [sp, #16] */
1824 0xff, 0x83, 0x00, 0x91, /* 10: add sp, sp, #32 */
1825 0xc0, 0x03, 0x5f, 0xd6, /* 14: ret */
1828 static const DWORD unwind_info_0_header =
1829 (sizeof(function_0)/4) | /* function length */
1830 (1 << 20) | /* X */
1831 (0 << 21) | /* E */
1832 (1 << 22) | /* epilog */
1833 (2 << 27); /* codes */
1834 static const DWORD unwind_info_0_epilog0 =
1835 (3 << 0) | /* offset */
1836 (4 << 22); /* index */
1838 static const BYTE unwind_info_0[] =
1840 DW(unwind_info_0_header),
1841 DW(unwind_info_0_epilog0),
1843 UWOP_SAVE_REGP(19, 16), /* stp x19, x20, [sp, #16] */
1844 UWOP_ALLOC_SMALL(32), /* sub sp, sp, #32 */
1845 UWOP_END,
1847 UWOP_SAVE_REGP(19, 16), /* stp x19, x20, [sp, #16] */
1848 UWOP_ALLOC_SMALL(32), /* sub sp, sp, #32 */
1849 UWOP_END,
1851 0x00, 0x02, 0x00, 0x00, /* handler */
1852 0x05, 0x06, 0x07, 0x08, /* data */
1855 static const struct results_arm64 results_0[] =
1857 /* offset fp handler pc frame offset registers */
1858 { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
1859 { 0x04, 0x00, 0, ORIG_LR, 0x020, TRUE, { {-1,-1} }},
1860 { 0x08, 0x00, 1, ORIG_LR, 0x020, TRUE, { {x19,0x10}, {x20,0x18}, {-1,-1} }},
1861 { 0x0c, 0x00, 0, ORIG_LR, 0x020, TRUE, { {x19,0x10}, {x20,0x18}, {-1,-1} }},
1862 { 0x10, 0x00, 0, ORIG_LR, 0x020, TRUE, { {-1,-1} }},
1863 { 0x14, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
1867 static const BYTE function_1[] =
1869 0xf3, 0x53, 0xbe, 0xa9, /* 00: stp x19, x20, [sp, #-32]! */
1870 0xfe, 0x0b, 0x00, 0xf9, /* 04: str x30, [sp, #16] */
1871 0xff, 0x43, 0x00, 0xd1, /* 08: sub sp, sp, #16 */
1872 0x1f, 0x20, 0x03, 0xd5, /* 0c: nop */
1873 0xff, 0x43, 0x00, 0x91, /* 10: add sp, sp, #16 */
1874 0xfe, 0x0b, 0x40, 0xf9, /* 14: ldr x30, [sp, #16] */
1875 0xf3, 0x53, 0xc2, 0xa8, /* 18: ldp x19, x20, [sp], #32 */
1876 0xc0, 0x03, 0x5f, 0xd6, /* 1c: ret */
1879 static const DWORD unwind_info_1_packed =
1880 (1 << 0) | /* Flag */
1881 (sizeof(function_1)/4 << 2) | /* FunctionLength */
1882 (0 << 13) | /* RegF */
1883 (2 << 16) | /* RegI */
1884 (0 << 20) | /* H */
1885 (1 << 21) | /* CR */
1886 (3 << 23); /* FrameSize */
1888 static const BYTE unwind_info_1[] = { DW(unwind_info_1_packed) };
1890 static const struct results_arm64 results_1[] =
1892 /* offset fp handler pc frame offset registers */
1893 { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
1894 { 0x04, 0x00, 0, ORIG_LR, 0x020, TRUE, { {x19,0x00}, {x20,0x08}, {-1,-1} }},
1895 { 0x08, 0x00, 0, 0x10, 0x020, TRUE, { {x19,0x00}, {x20,0x08}, {lr,0x10}, {-1,-1} }},
1896 { 0x0c, 0x00, 0, 0x20, 0x030, TRUE, { {x19,0x10}, {x20,0x18}, {lr,0x20}, {-1,-1} }},
1897 { 0x10, 0x00, 0, 0x20, 0x030, TRUE, { {x19,0x10}, {x20,0x18}, {lr,0x20}, {-1,-1} }},
1898 { 0x14, 0x00, 0, 0x10, 0x020, TRUE, { {x19,0x00}, {x20,0x08}, {lr,0x10}, {-1,-1} }},
1899 { 0x18, 0x00, 0, ORIG_LR, 0x020, TRUE, { {x19,0x00}, {x20,0x08}, {-1,-1} }},
1900 { 0x1c, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
1903 static const BYTE function_2[] =
1905 0xff, 0x43, 0x00, 0xd1, /* 00: sub sp, sp, #16 */
1906 0x1f, 0x20, 0x03, 0xd5, /* 04: nop */
1907 0xff, 0x43, 0x00, 0xd1, /* 08: sub sp, sp, #16 */
1908 0x1f, 0x20, 0x03, 0xd5, /* 0c: nop */
1909 0xc0, 0x03, 0x5f, 0xd6, /* 10: ret */
1912 static const DWORD unwind_info_2_header =
1913 (sizeof(function_2)/4) | /* function length */
1914 (0 << 20) | /* X */
1915 (0 << 21) | /* E */
1916 (0 << 22) | /* epilog */
1917 (1 << 27); /* codes */
1919 static const BYTE unwind_info_2[] =
1921 DW(unwind_info_2_header),
1923 UWOP_ALLOC_SMALL(16), /* sub sp, sp, #16 */
1924 UWOP_MACHINE_FRAME,
1925 UWOP_ALLOC_SMALL(16), /* sub sp, sp, #16 */
1926 UWOP_END,
1929 /* Partial prologues with the custom frame opcodes (machine frame,
1930 * context) behave like there's one less instruction to skip, because the
1931 * custom frame is set up externally without an explicit instruction. */
1932 static const struct results_arm64 results_2[] =
1934 /* offset fp handler pc frame offset registers */
1935 { 0x00, 0x00, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }},
1936 { 0x04, 0x00, 0, 0x0008, 0x010, FALSE, { {-1,-1} }},
1937 { 0x08, 0x00, 0, 0x0018, 0x020, FALSE, { {-1,-1} }},
1938 { 0x0c, 0x00, 0, 0x0018, 0x020, FALSE, { {-1,-1} }},
1939 { 0x10, 0x00, 0, 0x0018, 0x020, FALSE, { {-1,-1} }},
1942 static const BYTE function_3[] =
1944 0xff, 0x43, 0x00, 0xd1, /* 00: sub sp, sp, #16 */
1945 0x1f, 0x20, 0x03, 0xd5, /* 04: nop */
1946 0xff, 0x43, 0x00, 0xd1, /* 08: sub sp, sp, #16 */
1947 0x1f, 0x20, 0x03, 0xd5, /* 0c: nop */
1948 0xc0, 0x03, 0x5f, 0xd6, /* 10: ret */
1951 static const DWORD unwind_info_3_header =
1952 (sizeof(function_3)/4) | /* function length */
1953 (0 << 20) | /* X */
1954 (0 << 21) | /* E */
1955 (0 << 22) | /* epilog */
1956 (1 << 27); /* codes */
1958 static const BYTE unwind_info_3[] =
1960 DW(unwind_info_3_header),
1962 UWOP_ALLOC_SMALL(16), /* sub sp, sp, #16 */
1963 UWOP_CONTEXT,
1964 UWOP_ALLOC_SMALL(16), /* sub sp, sp, #16 */
1965 UWOP_END,
1968 static const struct results_arm64 results_3[] =
1970 /* offset fp handler pc frame offset registers */
1971 { 0x00, 0x00, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }},
1972 { 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} }},
1973 { 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} }},
1974 { 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} }},
1975 { 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} }},
1978 static const BYTE function_4[] =
1980 0xff, 0x43, 0x00, 0xd1, /* 00: sub sp, sp, #16 */
1981 0xff, 0x03, 0x08, 0xd1, /* 04: sub sp, sp, #512 */
1982 0xff, 0x43, 0x40, 0xd1, /* 08: sub sp, sp, #65536 */
1983 0xfd, 0x03, 0x00, 0x91, /* 0c: mov x29, sp */
1984 0xf3, 0x53, 0xbe, 0xa9, /* 10: stp x19, x20, [sp, #-32]! */
1985 0xf5, 0x5b, 0x01, 0xa9, /* 14: stp x21, x22, [sp, #16] */
1986 0xf7, 0x0f, 0x1e, 0xf8, /* 18: str x23, [sp, #-32]! */
1987 0xf8, 0x07, 0x00, 0xf9, /* 1c: str x24, [sp, #8] */
1988 0xf9, 0x7b, 0x01, 0xa9, /* 20: stp x25, x30, [sp, #16] */
1989 0xfd, 0x7b, 0x03, 0xa9, /* 24: stp x29, x30, [sp, #48] */
1990 0xfd, 0x7b, 0xbe, 0xa9, /* 28: stp x29, x30, [sp, #-32]! */
1991 0xf3, 0x53, 0xbe, 0xa9, /* 2c: stp x19, x20, [sp, #-32]! */
1992 0xe8, 0x27, 0xbe, 0x6d, /* 30: stp d8, d9, [sp, #-32]! */
1993 0xea, 0x2f, 0x01, 0x6d, /* 34: stp d10, d11, [sp, #16] */
1994 0xec, 0x0f, 0x1e, 0xfc, /* 38: str d12, [sp, #-32]! */
1995 0xed, 0x07, 0x00, 0xfd, /* 3c: str d13, [sp, #8] */
1996 0xfd, 0x43, 0x00, 0x91, /* 40: add x29, sp, #16 */
1997 0xc0, 0x03, 0x5f, 0xd6, /* 44: ret */
2000 static const DWORD unwind_info_4_header =
2001 (sizeof(function_4)/4) | /* function length */
2002 (0 << 20) | /* X */
2003 (0 << 21) | /* E */
2004 (0 << 22) | /* epilog */
2005 (8 << 27); /* codes */
2007 static const BYTE unwind_info_4[] =
2009 DW(unwind_info_4_header),
2011 UWOP_ADD_FP(16), /* 40: add x29, sp, #16 */
2012 UWOP_SAVE_FREG(13, 8), /* 3c: str d13, [sp, #8] */
2013 UWOP_SAVE_FREG_X(12, 32), /* 38: str d12, [sp, #-32]! */
2014 UWOP_SAVE_FREGP(10, 16), /* 34: stp d10, d11, [sp, #16] */
2015 UWOP_SAVE_FREGP_X(8, 32), /* 30: stp d8, d9, [sp, #-32]! */
2016 UWOP_SAVE_R19R20_X(32), /* 2c: stp x19, x20, [sp, #-32]! */
2017 UWOP_SAVE_FPLR_X(32), /* 28: stp x29, x30, [sp, #-32]! */
2018 UWOP_SAVE_FPLR(16), /* 24: stp x29, x30, [sp, #16] */
2019 UWOP_SAVE_LRP(25, 16), /* 20: stp x25, x30, [sp, #16] */
2020 UWOP_SAVE_REG(24, 8), /* 1c: str x24, [sp, #8] */
2021 UWOP_SAVE_REG_X(23, 32), /* 18: str x23, [sp, #-32]! */
2022 UWOP_SAVE_REGP(21, 16), /* 14: stp x21, x22, [sp, #16] */
2023 UWOP_SAVE_REGP_X(19, 32), /* 10: stp x19, x20, [sp, #-32]! */
2024 UWOP_SET_FP, /* 0c: mov x29, sp */
2025 UWOP_ALLOC_LARGE(65536), /* 08: sub sp, sp, #65536 */
2026 UWOP_ALLOC_MEDIUM(512), /* 04: sub sp, sp, #512 */
2027 UWOP_ALLOC_SMALL(16), /* 00: sub sp, sp, #16 */
2028 UWOP_END,
2031 static const struct results_arm64 results_4[] =
2033 /* offset fp handler pc frame offset registers */
2034 { 0x00, 0x10, 0, ORIG_LR, 0x00000, TRUE, { {-1,-1} }},
2035 { 0x04, 0x10, 0, ORIG_LR, 0x00010, TRUE, { {-1,-1} }},
2036 { 0x08, 0x10, 0, ORIG_LR, 0x00210, TRUE, { {-1,-1} }},
2037 { 0x0c, 0x10, 0, ORIG_LR, 0x10210, TRUE, { {-1,-1} }},
2038 { 0x14, 0x00, 0, ORIG_LR, 0x10210, TRUE, { {x19, 0x00}, {x20, 0x08}, {-1,-1} }},
2039 { 0x18, 0x00, 0, ORIG_LR, 0x10210, TRUE, { {x19, 0x00}, {x20, 0x08}, {x21, 0x10}, {x22, 0x18}, {-1,-1} }},
2040 { 0x1c, 0x00, 0, ORIG_LR, 0x10210, TRUE, { {x19, 0x20}, {x20, 0x28}, {x21, 0x30}, {x22, 0x38}, {x23, 0x00}, {-1,-1} }},
2041 { 0x20, 0x00, 0, ORIG_LR, 0x10210, TRUE, { {x19, 0x20}, {x20, 0x28}, {x21, 0x30}, {x22, 0x38}, {x23, 0x00}, {x24, 0x08}, {-1,-1} }},
2042 { 0x24, 0x00, 0, 0x0018, 0x10210, TRUE, { {x19, 0x20}, {x20, 0x28}, {x21, 0x30}, {x22, 0x38}, {x23, 0x00}, {x24, 0x08}, {x25, 0x10}, {lr, 0x18}, {-1,-1} }},
2043 { 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} }},
2044 { 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} }},
2045 { 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} }},
2046 { 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} }},
2047 { 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} }},
2048 { 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} }},
2049 { 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} }},
2050 { 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} }},
2053 static const BYTE function_5[] =
2055 0xf3, 0x53, 0xbe, 0xa9, /* 00: stp x19, x20, [sp, #-32]! */
2056 0xf5, 0x5b, 0x01, 0xa9, /* 04: stp x21, x22, [sp, #16] */
2057 0xf7, 0x63, 0xbc, 0xa9, /* 08: stp x23, x24, [sp, #-64]! */
2058 0xf9, 0x6b, 0x01, 0xa9, /* 0c: stp x25, x26, [sp, #16] */
2059 0xfb, 0x73, 0x02, 0xa9, /* 10: stp x27, x28, [sp, #32] */
2060 0xfd, 0x7b, 0x03, 0xa9, /* 14: stp x29, x30, [sp, #48] */
2061 0xe8, 0x27, 0xbc, 0x6d, /* 18: stp d8, d9, [sp, #-64]! */
2062 0xea, 0x2f, 0x01, 0x6d, /* 1c: stp d10, d11, [sp, #16] */
2063 0xec, 0x37, 0x02, 0x6d, /* 20: stp d12, d13, [sp, #32] */
2064 0xee, 0x3f, 0x03, 0x6d, /* 24: stp d14, d15, [sp, #48] */
2065 0xc0, 0x03, 0x5f, 0xd6, /* 28: ret */
2068 static const DWORD unwind_info_5_header =
2069 (sizeof(function_5)/4) | /* function length */
2070 (0 << 20) | /* X */
2071 (0 << 21) | /* E */
2072 (0 << 22) | /* epilog */
2073 (4 << 27); /* codes */
2075 static const BYTE unwind_info_5[] =
2077 DW(unwind_info_5_header),
2079 UWOP_SAVE_NEXT, /* 24: stp d14, d15, [sp, #48] */
2080 UWOP_SAVE_FREGP(12, 32), /* 20: stp d12, d13, [sp, #32] */
2081 UWOP_SAVE_NEXT, /* 1c: stp d10, d11, [sp, #16] */
2082 UWOP_SAVE_FREGP_X(8, 64), /* 18: stp d8, d9, [sp, #-64]! */
2083 UWOP_SAVE_NEXT, /* 14: stp x29, x30, [sp, #48] */
2084 UWOP_SAVE_REGP(27, 32), /* 10: stp x27, x28, [sp, #32] */
2085 UWOP_SAVE_NEXT, /* 0c: stp x25, x26, [sp, #16] */
2086 UWOP_SAVE_REGP_X(23, 64), /* 08: stp x23, x24, [sp, #-64]! */
2087 UWOP_SAVE_NEXT, /* 04: stp x21, x22, [sp, #16] */
2088 UWOP_SAVE_R19R20_X(32), /* 00: stp x19, x20, [sp, #-32]! */
2089 UWOP_END,
2090 UWOP_NOP /* padding */
2093 /* Windows seems to only save one register for UWOP_SAVE_NEXT for
2094 * float registers, contrary to what the documentation says. The tests
2095 * for those cases are commented out; they succeed in wine but fail
2096 * on native windows. */
2097 static const struct results_arm64 results_5[] =
2099 /* offset fp handler pc frame offset registers */
2100 { 0x00, 0x00, 0, ORIG_LR, 0x00000, TRUE, { {-1,-1} }},
2101 { 0x04, 0x00, 0, ORIG_LR, 0x00020, TRUE, { {x19, 0x00}, {x20, 0x08}, {-1,-1} }},
2102 { 0x08, 0x00, 0, ORIG_LR, 0x00020, TRUE, { {x19, 0x00}, {x20, 0x08}, {x21, 0x10}, {x22, 0x18}, {-1,-1} }},
2103 { 0x0c, 0x00, 0, ORIG_LR, 0x00060, TRUE, { {x19, 0x40}, {x20, 0x48}, {x21, 0x50}, {x22, 0x58}, {x23, 0x00}, {x24, 0x08}, {-1,-1} }},
2104 { 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} }},
2105 { 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} }},
2106 { 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} }},
2107 { 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} }},
2108 #if 0
2109 { 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} }},
2110 { 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} }},
2111 { 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} }},
2112 #endif
2115 static const BYTE function_6[] =
2117 0xf3, 0x53, 0xbd, 0xa9, /* 00: stp x19, x20, [sp, #-48]! */
2118 0xf5, 0x0b, 0x00, 0xf9, /* 04: str x21, [sp, #16] */
2119 0xe8, 0xa7, 0x01, 0x6d, /* 08: stp d8, d9, [sp, #24] */
2120 0xea, 0x17, 0x00, 0xfd, /* 0c: str d10, [sp, #40] */
2121 0xff, 0x03, 0x00, 0xd1, /* 10: sub sp, sp, #0 */
2122 0x1f, 0x20, 0x03, 0xd5, /* 14: nop */
2123 0xff, 0x03, 0x00, 0x91, /* 18: add sp, sp, #0 */
2124 0xea, 0x17, 0x40, 0xfd, /* 1c: ldr d10, [sp, #40] */
2125 0xe8, 0xa7, 0x41, 0x6d, /* 20: ldp d8, d9, [sp, #24] */
2126 0xf5, 0x0b, 0x40, 0xf9, /* 24: ldr x21, [sp, #16] */
2127 0xf3, 0x53, 0xc3, 0xa8, /* 28: ldp x19, x20, [sp], #48 */
2128 0xc0, 0x03, 0x5f, 0xd6, /* 2c: ret */
2131 static const DWORD unwind_info_6_packed =
2132 (1 << 0) | /* Flag */
2133 (sizeof(function_6)/4 << 2) | /* FunctionLength */
2134 (2 << 13) | /* RegF */
2135 (3 << 16) | /* RegI */
2136 (0 << 20) | /* H */
2137 (0 << 21) | /* CR */
2138 (3 << 23); /* FrameSize */
2140 static const BYTE unwind_info_6[] = { DW(unwind_info_6_packed) };
2142 static const struct results_arm64 results_6[] =
2144 /* offset fp handler pc frame offset registers */
2145 { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
2146 { 0x04, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19,0x00}, {x20,0x08}, {-1,-1} }},
2147 { 0x08, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19,0x00}, {x20,0x08}, {x21, 0x10}, {-1,-1} }},
2148 { 0x0c, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19,0x00}, {x20,0x08}, {x21, 0x10}, {d8, 0x18}, {d9, 0x20}, {-1,-1} }},
2149 { 0x10, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19,0x00}, {x20,0x08}, {x21, 0x10}, {d8, 0x18}, {d9, 0x20}, {d10, 0x28}, {-1,-1} }},
2150 { 0x14, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19,0x00}, {x20,0x08}, {x21, 0x10}, {d8, 0x18}, {d9, 0x20}, {d10, 0x28}, {-1,-1} }},
2151 { 0x18, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19,0x00}, {x20,0x08}, {x21, 0x10}, {d8, 0x18}, {d9, 0x20}, {d10, 0x28}, {-1,-1} }},
2152 { 0x1c, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19,0x00}, {x20,0x08}, {x21, 0x10}, {d8, 0x18}, {d9, 0x20}, {d10, 0x28}, {-1,-1} }},
2153 { 0x20, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19,0x00}, {x20,0x08}, {x21, 0x10}, {d8, 0x18}, {d9, 0x20}, {-1,-1} }},
2154 { 0x24, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19,0x00}, {x20,0x08}, {x21, 0x10}, {-1,-1} }},
2155 { 0x28, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19,0x00}, {x20,0x08}, {-1,-1} }},
2156 { 0x2c, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
2159 static const BYTE function_7[] =
2161 0xf3, 0x0f, 0x1d, 0xf8, /* 00: str x19, [sp, #-48]! */
2162 0xe8, 0xa7, 0x00, 0x6d, /* 04: stp d8, d9, [sp, #8] */
2163 0xea, 0xaf, 0x01, 0x6d, /* 08: stp d10, d11, [sp, #24] */
2164 0xff, 0x03, 0x00, 0xd1, /* 0c: sub sp, sp, #0 */
2165 0x1f, 0x20, 0x03, 0xd5, /* 10: nop */
2166 0xff, 0x03, 0x00, 0x91, /* 14: add sp, sp, #0 */
2167 0xea, 0xaf, 0x41, 0x6d, /* 18: ldp d10, d11, [sp, #24] */
2168 0xe8, 0xa7, 0x40, 0x6d, /* 1c: ldp d8, d9, [sp, #8] */
2169 0xf3, 0x07, 0x43, 0xf8, /* 20: ldr x19, [sp], #48 */
2170 0xc0, 0x03, 0x5f, 0xd6, /* 24: ret */
2173 static const DWORD unwind_info_7_packed =
2174 (1 << 0) | /* Flag */
2175 (sizeof(function_7)/4 << 2) | /* FunctionLength */
2176 (3 << 13) | /* RegF */
2177 (1 << 16) | /* RegI */
2178 (0 << 20) | /* H */
2179 (0 << 21) | /* CR */
2180 (3 << 23); /* FrameSize */
2182 static const BYTE unwind_info_7[] = { DW(unwind_info_7_packed) };
2184 static const struct results_arm64 results_7[] =
2186 /* offset fp handler pc frame offset registers */
2187 { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
2188 { 0x04, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19, 0x00}, {-1,-1} }},
2189 { 0x08, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19, 0x00}, {d8, 0x08}, {d9, 0x10}, {-1,-1} }},
2190 { 0x0c, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19, 0x00}, {d8, 0x08}, {d9, 0x10}, {d10, 0x18}, {d11, 0x20}, {-1,-1} }},
2191 { 0x10, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19, 0x00}, {d8, 0x08}, {d9, 0x10}, {d10, 0x18}, {d11, 0x20}, {-1,-1} }},
2192 { 0x14, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19, 0x00}, {d8, 0x08}, {d9, 0x10}, {d10, 0x18}, {d11, 0x20}, {-1,-1} }},
2193 { 0x18, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19, 0x00}, {d8, 0x08}, {d9, 0x10}, {d10, 0x18}, {d11, 0x20}, {-1,-1} }},
2194 { 0x1c, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19, 0x00}, {d8, 0x08}, {d9, 0x10}, {-1,-1} }},
2195 { 0x20, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19, 0x00}, {-1,-1} }},
2196 { 0x24, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
2199 static const BYTE function_8[] =
2201 0xe8, 0x27, 0xbf, 0x6d, /* 00: stp d8, d9, [sp, #-16]! */
2202 0xff, 0x83, 0x00, 0xd1, /* 04: sub sp, sp, #32 */
2203 0x1f, 0x20, 0x03, 0xd5, /* 08: nop */
2204 0xff, 0x83, 0x00, 0x91, /* 0c: add sp, sp, #32 */
2205 0xe8, 0x27, 0xc1, 0x6c, /* 10: ldp d8, d9, [sp], #16 */
2206 0xc0, 0x03, 0x5f, 0xd6, /* 14: ret */
2209 static const DWORD unwind_info_8_packed =
2210 (1 << 0) | /* Flag */
2211 (sizeof(function_8)/4 << 2) | /* FunctionLength */
2212 (1 << 13) | /* RegF */
2213 (0 << 16) | /* RegI */
2214 (0 << 20) | /* H */
2215 (0 << 21) | /* CR */
2216 (3 << 23); /* FrameSize */
2218 static const BYTE unwind_info_8[] = { DW(unwind_info_8_packed) };
2220 static const struct results_arm64 results_8[] =
2222 /* offset fp handler pc frame offset registers */
2223 { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
2224 { 0x04, 0x00, 0, ORIG_LR, 0x010, TRUE, { {d8, 0x00}, {d9, 0x08}, {-1,-1} }},
2225 { 0x08, 0x00, 0, ORIG_LR, 0x030, TRUE, { {d8, 0x20}, {d9, 0x28}, {-1,-1} }},
2226 { 0x0c, 0x00, 0, ORIG_LR, 0x030, TRUE, { {d8, 0x20}, {d9, 0x28}, {-1,-1} }},
2227 { 0x10, 0x00, 0, ORIG_LR, 0x010, TRUE, { {d8, 0x00}, {d9, 0x08}, {-1,-1} }},
2228 { 0x14, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
2231 static const BYTE function_9[] =
2233 0xf3, 0x0f, 0x1b, 0xf8, /* 00: str x19, [sp, #-80]! */
2234 0xe0, 0x87, 0x00, 0xa9, /* 04: stp x0, x1, [sp, #8] */
2235 0xe2, 0x8f, 0x01, 0xa9, /* 08: stp x2, x3, [sp, #24] */
2236 0xe4, 0x97, 0x02, 0xa9, /* 0c: stp x4, x5, [sp, #40] */
2237 0xe6, 0x9f, 0x03, 0xa9, /* 10: stp x6, x7, [sp, #56] */
2238 0xff, 0x83, 0x00, 0xd1, /* 14: sub sp, sp, #32 */
2239 0x1f, 0x20, 0x03, 0xd5, /* 18: nop */
2240 0xff, 0x83, 0x00, 0x91, /* 1c: add sp, sp, #32 */
2241 0x1f, 0x20, 0x03, 0xd5, /* 20: nop */
2242 0x1f, 0x20, 0x03, 0xd5, /* 24: nop */
2243 0x1f, 0x20, 0x03, 0xd5, /* 28: nop */
2244 0x1f, 0x20, 0x03, 0xd5, /* 2c: nop */
2245 0xf3, 0x0f, 0x1b, 0xf8, /* 30: ldr x19, [sp], #80 */
2246 0xc0, 0x03, 0x5f, 0xd6, /* 34: ret */
2249 static const DWORD unwind_info_9_packed =
2250 (1 << 0) | /* Flag */
2251 (sizeof(function_9)/4 << 2) | /* FunctionLength */
2252 (0 << 13) | /* RegF */
2253 (1 << 16) | /* RegI */
2254 (1 << 20) | /* H */
2255 (0 << 21) | /* CR */
2256 (7 << 23); /* FrameSize */
2258 static const BYTE unwind_info_9[] = { DW(unwind_info_9_packed) };
2260 static const struct results_arm64 results_9[] =
2262 /* offset fp handler pc frame offset registers */
2263 { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
2264 { 0x04, 0x00, 0, ORIG_LR, 0x050, TRUE, { {x19, 0x00}, {-1,-1} }},
2265 { 0x08, 0x00, 0, ORIG_LR, 0x050, TRUE, { {x19, 0x00}, {-1,-1} }},
2266 { 0x0c, 0x00, 0, ORIG_LR, 0x050, TRUE, { {x19, 0x00}, {-1,-1} }},
2267 { 0x10, 0x00, 0, ORIG_LR, 0x050, TRUE, { {x19, 0x00}, {-1,-1} }},
2268 { 0x14, 0x00, 0, ORIG_LR, 0x050, TRUE, { {x19, 0x00}, {-1,-1} }},
2269 { 0x18, 0x00, 0, ORIG_LR, 0x070, TRUE, { {x19, 0x20}, {-1,-1} }},
2270 { 0x1c, 0x00, 0, ORIG_LR, 0x070, TRUE, { {x19, 0x20}, {-1,-1} }},
2271 { 0x20, 0x00, 0, ORIG_LR, 0x070, TRUE, { {x19, 0x20}, {-1,-1} }},
2272 { 0x24, 0x00, 0, ORIG_LR, 0x070, TRUE, { {x19, 0x20}, {-1,-1} }},
2273 { 0x28, 0x00, 0, ORIG_LR, 0x070, TRUE, { {x19, 0x20}, {-1,-1} }},
2274 { 0x2c, 0x00, 0, ORIG_LR, 0x070, TRUE, { {x19, 0x20}, {-1,-1} }},
2275 { 0x30, 0x00, 0, ORIG_LR, 0x050, TRUE, { {x19, 0x00}, {-1,-1} }},
2276 { 0x34, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
2279 static const BYTE function_10[] =
2281 0xfe, 0x0f, 0x1f, 0xf8, /* 00: str lr, [sp, #-16]! */
2282 0xff, 0x43, 0x00, 0xd1, /* 04: sub sp, sp, #16 */
2283 0x1f, 0x20, 0x03, 0xd5, /* 08: nop */
2284 0xff, 0x43, 0x00, 0x91, /* 0c: add sp, sp, #16 */
2285 0xfe, 0x07, 0x41, 0xf8, /* 10: ldr lr, [sp], #16 */
2286 0xc0, 0x03, 0x5f, 0xd6, /* 14: ret */
2289 static const DWORD unwind_info_10_packed =
2290 (1 << 0) | /* Flag */
2291 (sizeof(function_10)/4 << 2) | /* FunctionLength */
2292 (0 << 13) | /* RegF */
2293 (0 << 16) | /* RegI */
2294 (0 << 20) | /* H */
2295 (1 << 21) | /* CR */
2296 (2 << 23); /* FrameSize */
2298 static const BYTE unwind_info_10[] = { DW(unwind_info_10_packed) };
2300 static const struct results_arm64 results_10[] =
2302 /* offset fp handler pc frame offset registers */
2303 { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
2304 { 0x04, 0x00, 0, 0x00, 0x010, TRUE, { {lr, 0x00}, {-1,-1} }},
2305 { 0x08, 0x00, 0, 0x10, 0x020, TRUE, { {lr, 0x10}, {-1,-1} }},
2306 { 0x0c, 0x00, 0, 0x10, 0x020, TRUE, { {lr, 0x10}, {-1,-1} }},
2307 { 0x10, 0x00, 0, 0x00, 0x010, TRUE, { {lr, 0x00}, {-1,-1} }},
2308 { 0x14, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
2311 static const BYTE function_11[] =
2313 0xf3, 0x53, 0xbe, 0xa9, /* 00: stp x19, x20, [sp, #-32]! */
2314 0xf5, 0x7b, 0x01, 0xa9, /* 04: stp x21, lr, [sp, #16] */
2315 0xff, 0x43, 0x00, 0xd1, /* 08: sub sp, sp, #16 */
2316 0x1f, 0x20, 0x03, 0xd5, /* 0c: nop */
2317 0xff, 0x43, 0x00, 0x91, /* 10: add sp, sp, #16 */
2318 0xf5, 0x7b, 0x41, 0xa9, /* 14: ldp x21, lr, [sp, #16] */
2319 0xf3, 0x53, 0xc2, 0xa8, /* 18: ldp x19, x20, [sp], #32 */
2320 0xc0, 0x03, 0x5f, 0xd6, /* 1c: ret */
2323 static const DWORD unwind_info_11_packed =
2324 (1 << 0) | /* Flag */
2325 (sizeof(function_11)/4 << 2) | /* FunctionLength */
2326 (0 << 13) | /* RegF */
2327 (3 << 16) | /* RegI */
2328 (0 << 20) | /* H */
2329 (1 << 21) | /* CR */
2330 (3 << 23); /* FrameSize */
2332 static const BYTE unwind_info_11[] = { DW(unwind_info_11_packed) };
2334 static const struct results_arm64 results_11[] =
2336 /* offset fp handler pc frame offset registers */
2337 { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
2338 { 0x04, 0x00, 0, ORIG_LR, 0x020, TRUE, { {x19, 0x00}, {x20, 0x08}, {-1,-1} }},
2339 { 0x08, 0x00, 0, 0x18, 0x020, TRUE, { {x19, 0x00}, {x20, 0x08}, {x21, 0x10}, {lr, 0x18}, {-1,-1} }},
2340 { 0x0c, 0x00, 0, 0x28, 0x030, TRUE, { {x19, 0x10}, {x20, 0x18}, {x21, 0x20}, {lr, 0x28}, {-1,-1} }},
2341 { 0x10, 0x00, 0, 0x28, 0x030, TRUE, { {x19, 0x10}, {x20, 0x18}, {x21, 0x20}, {lr, 0x28}, {-1,-1} }},
2342 { 0x14, 0x00, 0, 0x18, 0x020, TRUE, { {x19, 0x00}, {x20, 0x08}, {x21, 0x10}, {lr, 0x18}, {-1,-1} }},
2343 { 0x18, 0x00, 0, ORIG_LR, 0x020, TRUE, { {x19, 0x00}, {x20, 0x08}, {-1,-1} }},
2344 { 0x1c, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
2347 static const BYTE function_12[] =
2349 0xf3, 0x53, 0xbf, 0xa9, /* 00: stp x19, x20, [sp, #-16]! */
2350 0xfd, 0x7b, 0xbe, 0xa9, /* 04: stp x29, lr, [sp, #-32]! */
2351 0xfd, 0x03, 0x00, 0x91, /* 08: mov x29, sp */
2352 0x1f, 0x20, 0x03, 0xd5, /* 0c: nop */
2353 0xbf, 0x03, 0x00, 0x91, /* 10: mov sp, x29 */
2354 0xfd, 0x7b, 0xc2, 0xa8, /* 14: ldp x29, lr, [sp], #32 */
2355 0xf3, 0x53, 0xc1, 0xa8, /* 18: ldp x19, x20, [sp], #16 */
2356 0xc0, 0x03, 0x5f, 0xd6, /* 1c: ret */
2359 static const DWORD unwind_info_12_packed =
2360 (1 << 0) | /* Flag */
2361 (sizeof(function_12)/4 << 2) | /* FunctionLength */
2362 (0 << 13) | /* RegF */
2363 (2 << 16) | /* RegI */
2364 (0 << 20) | /* H */
2365 (3 << 21) | /* CR */
2366 (3 << 23); /* FrameSize */
2368 static const BYTE unwind_info_12[] = { DW(unwind_info_12_packed) };
2370 static const struct results_arm64 results_12[] =
2372 /* offset fp handler pc frame offset registers */
2373 { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
2374 { 0x04, 0x10, 0, ORIG_LR, 0x010, TRUE, { {x19, 0x00}, {x20, 0x08}, {-1,-1} }},
2375 { 0x08, 0x10, 0, 0x08, 0x030, TRUE, { {x19, 0x20}, {x20, 0x28}, {x29, 0x00}, {lr, 0x08}, {-1,-1} }},
2376 { 0x0c, 0x10, 0, 0x18, 0x040, TRUE, { {x19, 0x30}, {x20, 0x38}, {x29, 0x10}, {lr, 0x18}, {-1,-1} }},
2377 { 0x10, 0x10, 0, 0x18, 0x040, TRUE, { {x19, 0x30}, {x20, 0x38}, {x29, 0x10}, {lr, 0x18}, {-1,-1} }},
2378 { 0x14, 0x10, 0, 0x08, 0x030, TRUE, { {x19, 0x20}, {x20, 0x28}, {x29, 0x00}, {lr, 0x08}, {-1,-1} }},
2379 { 0x18, 0x10, 0, ORIG_LR, 0x010, TRUE, { {x19, 0x00}, {x20, 0x08}, {-1,-1} }},
2380 { 0x1c, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
2383 static const BYTE function_13[] =
2385 0xf3, 0x53, 0xbf, 0xa9, /* 00: stp x19, x20, [sp, #-16]! */
2386 0xff, 0x43, 0x08, 0xd1, /* 04: sub sp, sp, #528 */
2387 0xfd, 0x7b, 0x00, 0xd1, /* 08: stp x29, lr, [sp] */
2388 0xfd, 0x03, 0x00, 0x91, /* 0c: mov x29, sp */
2389 0x1f, 0x20, 0x03, 0xd5, /* 10: nop */
2390 0xbf, 0x03, 0x00, 0x91, /* 14: mov sp, x29 */
2391 0xfd, 0x7b, 0x40, 0xa9, /* 18: ldp x29, lr, [sp] */
2392 0xff, 0x43, 0x08, 0x91, /* 1c: add sp, sp, #528 */
2393 0xf3, 0x53, 0xc1, 0xa8, /* 20: ldp x19, x20, [sp], #16 */
2394 0xc0, 0x03, 0x5f, 0xd6, /* 24: ret */
2397 static const DWORD unwind_info_13_packed =
2398 (1 << 0) | /* Flag */
2399 (sizeof(function_13)/4 << 2) | /* FunctionLength */
2400 (0 << 13) | /* RegF */
2401 (2 << 16) | /* RegI */
2402 (0 << 20) | /* H */
2403 (3 << 21) | /* CR */
2404 (34 << 23); /* FrameSize */
2406 static const BYTE unwind_info_13[] = { DW(unwind_info_13_packed) };
2408 static const struct results_arm64 results_13[] =
2410 /* offset fp handler pc frame offset registers */
2411 { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
2412 { 0x04, 0x10, 0, ORIG_LR, 0x010, TRUE, { {x19, 0x00}, {x20, 0x08}, {-1,-1} }},
2413 { 0x08, 0x10, 0, ORIG_LR, 0x220, TRUE, { {x19, 0x210}, {x20, 0x218}, {-1,-1} }},
2414 { 0x0c, 0x10, 0, 0x08, 0x220, TRUE, { {x19, 0x210}, {x20, 0x218}, {x29, 0x00}, {lr, 0x08}, {-1,-1} }},
2415 { 0x10, 0x10, 0, 0x18, 0x230, TRUE, { {x19, 0x220}, {x20, 0x228}, {x29, 0x10}, {lr, 0x18}, {-1,-1} }},
2416 { 0x14, 0x10, 0, 0x18, 0x230, TRUE, { {x19, 0x220}, {x20, 0x228}, {x29, 0x10}, {lr, 0x18}, {-1,-1} }},
2417 { 0x18, 0x10, 0, 0x08, 0x220, TRUE, { {x19, 0x210}, {x20, 0x218}, {x29, 0x00}, {lr, 0x08}, {-1,-1} }},
2418 { 0x1c, 0x10, 0, ORIG_LR, 0x220, TRUE, { {x19, 0x210}, {x20, 0x218}, {-1,-1} }},
2419 { 0x20, 0x10, 0, ORIG_LR, 0x010, TRUE, { {x19, 0x00}, {x20, 0x08}, {-1,-1} }},
2420 { 0x24, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
2423 static const BYTE function_14[] =
2425 0xe6, 0x9f, 0xba, 0xad, /* 00: stp q6, q7, [sp, #-0xb0]! */
2426 0xe8, 0x27, 0x01, 0xad, /* 04: stp q8, q9, [sp, #0x20] */
2427 0xea, 0x2f, 0x02, 0xad, /* 08: stp q10, q11, [sp, #0x40] */
2428 0xec, 0x37, 0x03, 0xad, /* 0c: stp q12, q13, [sp, #0x60] */
2429 0xee, 0x3f, 0x04, 0xad, /* 10: stp q14, q15, [sp, #0x80] */
2430 0xfd, 0x7b, 0x0a, 0xa9, /* 14: stp x29, x30, [sp, #0xa0] */
2431 0xfd, 0x83, 0x02, 0x91, /* 18: add x29, sp, #0xa0 */
2432 0x1f, 0x20, 0x03, 0xd5, /* 1c: nop */
2433 0xfd, 0x7b, 0x4a, 0xa9, /* 20: ldp x29, x30, [sp, #0xa0] */
2434 0xee, 0x3f, 0x44, 0xad, /* 24: ldp q14, q15, [sp, #0x80] */
2435 0xec, 0x37, 0x43, 0xad, /* 28: ldp q12, q13, [sp, #0x60] */
2436 0xea, 0x2f, 0x42, 0xad, /* 2c: ldp q10, q11, [sp, #0x40] */
2437 0xe8, 0x27, 0x41, 0xad, /* 30: ldp q8, q9, [sp, #0x20] */
2438 0xe6, 0x9f, 0xc5, 0xac, /* 34: ldp q6, q7, [sp], #0xb0 */
2439 0xc0, 0x03, 0x5f, 0xd6, /* 38: ret */
2442 static const DWORD unwind_info_14_header =
2443 (sizeof(function_14)/4) | /* function length */
2444 (0 << 20) | /* X */
2445 (1 << 21) | /* E */
2446 (2 << 22) | /* epilog */
2447 (5 << 27); /* codes */
2449 static const BYTE unwind_info_14[] =
2451 DW(unwind_info_14_header),
2452 UWOP_ADD_FP(0xa0), /* 18: add x29, sp, #0xa0 */
2453 UWOP_SAVE_FPLR(0xa0), /* 14: stp x29, x30, [sp, #0xa0] */
2454 UWOP_SAVE_ANY_REG(0x4e,0x88), /* 10: stp q14, q15, [sp, #0x80] */
2455 UWOP_SAVE_NEXT, /* 0c: stp q12, q13, [sp, #0x60] */
2456 UWOP_SAVE_ANY_REG(0x4a,0x84), /* 08: stp q10, q11, [sp, #0x40] */
2457 UWOP_SAVE_ANY_REG(0x48,0x82), /* 04: stp q8, q9, [sp, #0x20] */
2458 UWOP_SAVE_ANY_REG(0x66,0x8a), /* 00: stp q6, q7, [sp, #-0xb0]! */
2459 UWOP_END,
2460 UWOP_NOP /* padding */
2463 static const struct results_arm64 results_14[] =
2465 /* offset fp handler pc frame offset registers */
2466 { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
2467 { 0x04, 0x00, 0, ORIG_LR, 0x0b0, TRUE, { {d6, 0x00}, {d7, 0x10}, {-1,-1} }},
2468 { 0x08, 0x00, 0, ORIG_LR, 0x0b0, TRUE, { {d6, 0x00}, {d7, 0x10}, {d8, 0x20}, {d9, 0x30}, {-1,-1} }},
2469 { 0x0c, 0x00, 0, ORIG_LR, 0x0b0, TRUE, { {d6, 0x00}, {d7, 0x10}, {d8, 0x20}, {d9, 0x30}, {d10, 0x40}, {d11, 0x50}, {-1,-1} }},
2470 { 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} }},
2471 { 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} }},
2472 { 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} }},
2473 { 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} }},
2476 static const BYTE function_15[] =
2478 0x1f, 0x20, 0x03, 0xd5, /* 00: nop */
2479 0x1f, 0x20, 0x03, 0xd5, /* 04: nop */
2480 0x1f, 0x20, 0x03, 0xd5, /* 08: nop */
2481 0x1f, 0x20, 0x03, 0xd5, /* 0c: nop */
2482 0x1f, 0x20, 0x03, 0xd5, /* 10: nop */
2483 0xc0, 0x03, 0x5f, 0xd6, /* 14: ret */
2486 static const DWORD unwind_info_15_header =
2487 (sizeof(function_15)/4) | /* function length */
2488 (0 << 20) | /* X */
2489 (0 << 21) | /* E */
2490 (0 << 22) | /* epilog */
2491 (2 << 27); /* codes */
2493 static const BYTE unwind_info_15[] =
2495 DW(unwind_info_15_header),
2496 UWOP_END_C,
2497 UWOP_SET_FP, /* mov x29, sp */
2498 UWOP_SAVE_REGP(19, 0x10), /* stp r19, r20, [sp, #0x10] */
2499 UWOP_SAVE_FPLR_X(0x20), /* stp r29, lr, [sp,-#0x20]! */
2500 UWOP_END,
2501 UWOP_NOP, /* padding */
2502 UWOP_NOP, /* padding */
2505 static const struct results_arm64 results_15[] =
2507 /* offset fp handler pc frame offset registers */
2508 { 0x00, 0x00, 0, 0x08, 0x020, TRUE, { {x29, 0x00}, {lr, 0x08}, {x19,0x10}, {x20,0x18}, {-1,-1} }},
2509 { 0x04, 0x00, 0, 0x08, 0x020, TRUE, { {x29, 0x00}, {lr, 0x08}, {x19,0x10}, {x20,0x18}, {-1,-1} }},
2510 { 0x08, 0x00, 0, 0x08, 0x020, TRUE, { {x29, 0x00}, {lr, 0x08}, {x19,0x10}, {x20,0x18}, {-1,-1} }},
2511 { 0x0c, 0x00, 0, 0x08, 0x020, TRUE, { {x29, 0x00}, {lr, 0x08}, {x19,0x10}, {x20,0x18}, {-1,-1} }},
2512 { 0x10, 0x00, 0, 0x08, 0x020, TRUE, { {x29, 0x00}, {lr, 0x08}, {x19,0x10}, {x20,0x18}, {-1,-1} }},
2513 { 0x14, 0x00, 0, 0x08, 0x020, TRUE, { {x29, 0x00}, {lr, 0x08}, {x19,0x10}, {x20,0x18}, {-1,-1} }},
2516 static const BYTE function_16[] =
2518 0xff, 0x43, 0x00, 0xd1, /* 00: sub sp, sp, #16 */
2519 0x1f, 0x20, 0x03, 0xd5, /* 04: nop */
2520 0xff, 0x43, 0x00, 0xd1, /* 08: sub sp, sp, #16 */
2521 0x1f, 0x20, 0x03, 0xd5, /* 0c: nop */
2522 0xc0, 0x03, 0x5f, 0xd6, /* 10: ret */
2525 static const DWORD unwind_info_16_header =
2526 (sizeof(function_16)/4) | /* function length */
2527 (0 << 20) | /* X */
2528 (0 << 21) | /* E */
2529 (0 << 22) | /* epilog */
2530 (1 << 27); /* codes */
2532 static const BYTE unwind_info_16[] =
2534 DW(unwind_info_16_header),
2536 UWOP_ALLOC_SMALL(16), /* sub sp, sp, #16 */
2537 UWOP_EC_CONTEXT,
2538 UWOP_ALLOC_SMALL(16), /* sub sp, sp, #16 */
2539 UWOP_END,
2542 static const struct results_arm64 results_16[] =
2544 /* offset fp handler pc frame offset registers */
2545 { 0x00, 0x00, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }},
2546 { 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} }},
2547 { 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} }},
2548 { 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} }},
2549 { 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} }},
2552 static const BYTE function_17[] =
2554 0xff, 0x43, 0x00, 0xd1, /* 00: sub sp, sp, #16 */
2555 0xff, 0x43, 0x00, 0xd1, /* 04: sub sp, sp, #16 */
2556 0x1f, 0x20, 0x03, 0xd5, /* 08: nop */
2557 0xc0, 0x03, 0x5f, 0xd6, /* 0c: ret */
2560 static const DWORD unwind_info_17_header =
2561 (sizeof(function_17)/4) | /* function length */
2562 (0 << 20) | /* X */
2563 (0 << 21) | /* E */
2564 (0 << 22) | /* epilog */
2565 (1 << 27); /* codes */
2567 static const BYTE unwind_info_17[] =
2569 DW(unwind_info_17_header),
2571 UWOP_CLEAR_UNWOUND_TO_CALL,
2572 UWOP_ALLOC_SMALL(16), /* sub sp, sp, #16 */
2573 UWOP_ALLOC_SMALL(16), /* sub sp, sp, #16 */
2574 UWOP_END,
2577 static const struct results_arm64 results_17[] =
2579 /* offset fp handler pc frame offset registers */
2580 { 0x00, 0x00, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }},
2581 { 0x04, 0x00, 0, ORIG_LR, 0x020, TRUE, { {-1,-1} }},
2582 { 0x08, 0x00, 0, ORIG_LR, 0x020, TRUE, { {-1,-1} }},
2583 { 0x0c, 0x00, 0, ORIG_LR, 0x020, TRUE, { {-1,-1} }},
2586 static const BYTE function_18[] =
2588 0x1f, 0x20, 0x03, 0xd5, /* 00: nop */
2589 0x1f, 0x20, 0x03, 0xd5, /* 04: nop */
2590 0xc0, 0x03, 0x5f, 0xd6, /* 08: ret */
2593 static const struct results_arm64 results_18[] =
2595 /* offset fp handler pc frame offset registers */
2596 { 0x00, 0x00, -1, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
2597 { 0x04, 0x00, -1, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
2598 { 0x08, 0x00, -1, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
2599 { 0x0c, 0x00, -2, 0, 0xdeadbeef, FALSE, { {-1,-1} }},
2602 static const struct unwind_test_arm64 tests[] =
2604 #define TEST(func, unwind, size, results, unwound_clear, last_ptr, stack_value_index, stack_value) \
2605 { func, sizeof(func), unwind, size, results, ARRAY_SIZE(results), unwound_clear, last_ptr, stack_value_index, stack_value }
2606 TEST(function_0, unwind_info_0, sizeof(unwind_info_0), results_0, 0, 0, -1, 0),
2607 TEST(function_1, unwind_info_1, 0, results_1, 0, 0, -1, 0),
2608 TEST(function_2, unwind_info_2, sizeof(unwind_info_2), results_2, 1, 0, -1, 0),
2609 TEST(function_3, unwind_info_3, sizeof(unwind_info_3), results_3, 2, x28, 0, CONTEXT_ARM64_UNWOUND_TO_CALL),
2610 TEST(function_4, unwind_info_4, sizeof(unwind_info_4), results_4, 0, 0, -1, 0),
2611 TEST(function_5, unwind_info_5, sizeof(unwind_info_5), results_5, 0, 0, -1, 0),
2612 TEST(function_6, unwind_info_6, 0, results_6, 0, 0, -1, 0),
2613 TEST(function_7, unwind_info_7, 0, results_7, 0, 0, -1, 0),
2614 TEST(function_8, unwind_info_8, 0, results_8, 0, 0, -1, 0),
2615 TEST(function_9, unwind_info_9, 0, results_9, 0, 0, -1, 0),
2616 TEST(function_10, unwind_info_10, 0, results_10, 0, 0, -1, 0),
2617 TEST(function_11, unwind_info_11, 0, results_11, 0, 0, -1, 0),
2618 TEST(function_12, unwind_info_12, 0, results_12, 0, 0, -1, 0),
2619 TEST(function_13, unwind_info_13, 0, results_13, 0, 0, -1, 0),
2620 TEST(function_14, unwind_info_14, sizeof(unwind_info_14), results_14, 0, 0, -1, 0),
2621 TEST(function_15, unwind_info_15, sizeof(unwind_info_15), results_15, 0, 0, -1, 0),
2622 TEST(function_16, unwind_info_16, sizeof(unwind_info_16), results_16, 2, x18, 6, CONTEXT_ARM64_UNWOUND_TO_CALL),
2623 TEST(function_17, unwind_info_17, sizeof(unwind_info_17), results_17, 2, 0, -1, 0),
2624 TEST(function_18, NULL, 0, results_18, 0, 0, -1, 0),
2625 #undef TEST
2627 unsigned int i;
2629 #ifdef __x86_64__
2630 void *code_mem = NULL;
2631 SIZE_T code_size = 0x10000;
2632 MEM_EXTENDED_PARAMETER param = { 0 };
2634 param.Type = MemExtendedParameterAttributeFlags;
2635 param.ULong64 = MEM_EXTENDED_PARAMETER_EC_CODE;
2636 if (!pNtAllocateVirtualMemoryEx ||
2637 pNtAllocateVirtualMemoryEx( GetCurrentProcess(), &code_mem, &code_size, MEM_RESERVE | MEM_COMMIT,
2638 PAGE_EXECUTE_READWRITE, &param, 1 ))
2639 return;
2640 trace( "running arm64ec tests\n" );
2641 #endif
2643 for (i = 0; i < ARRAY_SIZE(tests); i++)
2644 call_virtual_unwind_arm64( code_mem, i, &tests[i] );
2647 #undef UWOP_ALLOC_SMALL
2648 #undef UWOP_ALLOC_LARGE
2650 #endif /* __aarch64__ || __x86_64__ */
2652 #ifdef __x86_64__
2654 #define UWOP_PUSH_NONVOL 0
2655 #define UWOP_ALLOC_LARGE 1
2656 #define UWOP_ALLOC_SMALL 2
2657 #define UWOP_SET_FPREG 3
2658 #define UWOP_SAVE_NONVOL 4
2659 #define UWOP_SAVE_NONVOL_FAR 5
2660 #define UWOP_SAVE_XMM128 8
2661 #define UWOP_SAVE_XMM128_FAR 9
2662 #define UWOP_PUSH_MACHFRAME 10
2664 struct results_x86
2666 int rip_offset; /* rip offset from code start */
2667 int rbp_offset; /* rbp offset from stack pointer */
2668 int handler; /* expect handler to be set? */
2669 int rip; /* expected final rip value */
2670 int frame; /* expected frame return value */
2671 int regs[8][2]; /* expected values for registers */
2674 struct unwind_test_x86
2676 const BYTE *function;
2677 size_t function_size;
2678 const BYTE *unwind_info;
2679 const struct results_x86 *results;
2680 unsigned int nb_results;
2681 const struct results_x86 *broken_results;
2684 enum regs
2686 rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi,
2687 r8, r9, r10, r11, r12, r13, r14, r15
2690 static const char * const reg_names_x86[16] =
2692 "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
2693 "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"
2696 #define UWOP(code,info) (UWOP_##code | ((info) << 4))
2698 static void call_virtual_unwind_x86( int testnum, const struct unwind_test_x86 *test )
2700 static const int code_offset = 1024;
2701 static const int unwind_offset = 2048;
2702 void *data;
2703 NTSTATUS status;
2704 CONTEXT context;
2705 PEXCEPTION_ROUTINE handler;
2706 RUNTIME_FUNCTION runtime_func;
2707 KNONVOLATILE_CONTEXT_POINTERS ctx_ptr;
2708 UINT i, j, k, broken_k;
2709 ULONG64 fake_stack[256];
2710 ULONG64 frame, orig_rip, orig_rbp, unset_reg;
2711 void *expected_handler, *broken_handler;
2713 memcpy( (char *)code_mem + code_offset, test->function, test->function_size );
2714 if (test->unwind_info)
2716 UINT unwind_size = 4 + 2 * test->unwind_info[2] + 8;
2717 memcpy( (char *)code_mem + unwind_offset, test->unwind_info, unwind_size );
2718 runtime_func.BeginAddress = code_offset;
2719 runtime_func.EndAddress = code_offset + test->function_size;
2720 runtime_func.UnwindData = unwind_offset;
2723 trace( "code: %p stack: %p\n", code_mem, fake_stack );
2725 for (i = 0; i < test->nb_results; i++)
2727 memset( &ctx_ptr, 0, sizeof(ctx_ptr) );
2728 memset( &context, 0x55, sizeof(context) );
2729 memset( &unset_reg, 0x55, sizeof(unset_reg) );
2730 for (j = 0; j < 256; j++) fake_stack[j] = j * 8;
2732 context.Rsp = (ULONG_PTR)fake_stack;
2733 context.Rbp = (ULONG_PTR)fake_stack + test->results[i].rbp_offset;
2734 orig_rbp = context.Rbp;
2735 orig_rip = (ULONG64)code_mem + code_offset + test->results[i].rip_offset;
2737 trace( "%u/%u: rip=%p (%02x) rbp=%p rsp=%p\n", testnum, i,
2738 (void *)orig_rip, *(BYTE *)orig_rip, (void *)orig_rbp, (void *)context.Rsp );
2740 if (!test->unwind_info) fake_stack[0] = 0x1234;
2741 expected_handler = test->results[i].handler ? (char *)code_mem + 0x200 : NULL;
2742 broken_handler = test->broken_results && test->broken_results[i].handler ? (char *)code_mem + 0x200 : NULL;
2744 if (pRtlVirtualUnwind2)
2746 CONTEXT new_context = context;
2748 handler = (void *)0xdeadbeef;
2749 data = (void *)0xdeadbeef;
2750 status = pRtlVirtualUnwind2( UNW_FLAG_EHANDLER, (ULONG_PTR)code_mem, orig_rip,
2751 test->unwind_info ? &runtime_func : NULL, &new_context,
2752 NULL, &data, &frame, &ctx_ptr, NULL, NULL, &handler, 0 );
2753 ok( !status, "RtlVirtualUnwind2 failed %lx\n", status );
2755 ok( handler == expected_handler || broken( test->broken_results && handler == broken_handler ),
2756 "%u/%u: wrong handler %p/%p\n", testnum, i, handler, expected_handler );
2757 if (handler)
2758 ok( *(DWORD *)data == 0x08070605, "%u/%u: wrong handler data %lx\n", testnum, i, *(DWORD *)data );
2759 else
2760 ok( data == (test->unwind_info ? (void *)0xdeadbeef : NULL),
2761 "%u/%u: handler data set to %p\n", testnum, i, data );
2764 data = (void *)0xdeadbeef;
2765 handler = RtlVirtualUnwind( UNW_FLAG_EHANDLER, (ULONG64)code_mem, orig_rip,
2766 test->unwind_info ? &runtime_func : NULL,
2767 &context, &data, &frame, &ctx_ptr );
2769 expected_handler = test->results[i].handler ? (char *)code_mem + 0x200 : NULL;
2770 broken_handler = test->broken_results && test->broken_results[i].handler ? (char *)code_mem + 0x200 : NULL;
2772 ok( handler == expected_handler || broken( test->broken_results && handler == broken_handler ),
2773 "%u/%u: wrong handler %p/%p\n", testnum, i, handler, expected_handler );
2774 if (handler)
2775 ok( *(DWORD *)data == 0x08070605, "%u/%u: wrong handler data %lx\n", testnum, i, *(DWORD *)data );
2776 else
2777 ok( data == (test->unwind_info ? (void *)0xdeadbeef : NULL),
2778 "%u/%u: handler data set to %p\n", testnum, i, data );
2780 ok( context.Rip == test->results[i].rip
2781 || broken( test->broken_results && context.Rip == test->broken_results[i].rip ),
2782 "%u/%u: wrong rip %p/%x\n", testnum, i, (void *)context.Rip, test->results[i].rip );
2783 ok( frame == (ULONG64)fake_stack + test->results[i].frame
2784 || broken( test->broken_results && frame == (ULONG64)fake_stack + test->broken_results[i].frame ),
2785 "%u/%u: wrong frame %p/%p\n",
2786 testnum, i, (void *)frame, (char *)fake_stack + test->results[i].frame );
2788 for (j = 0; j < 16; j++)
2790 static const UINT nb_regs = ARRAY_SIZE(test->results[i].regs);
2792 for (k = 0; k < nb_regs; k++)
2794 if (test->results[i].regs[k][0] == -1)
2796 k = nb_regs;
2797 break;
2799 if (test->results[i].regs[k][0] == j) break;
2802 if (test->broken_results)
2804 for (broken_k = 0; broken_k < nb_regs; broken_k++)
2806 if (test->broken_results[i].regs[broken_k][0] == -1)
2808 broken_k = nb_regs;
2809 break;
2811 if (test->broken_results[i].regs[broken_k][0] == j)
2812 break;
2815 else
2817 broken_k = k;
2820 if (j == rsp) /* rsp is special */
2822 ULONG64 expected_rsp, broken_rsp;
2824 ok( !ctx_ptr.IntegerContext[j],
2825 "%u/%u: rsp should not be set in ctx_ptr\n", testnum, i );
2826 expected_rsp = test->results[i].regs[k][1] < 0
2827 ? -test->results[i].regs[k][1] : (ULONG64)fake_stack + test->results[i].regs[k][1];
2828 if (test->broken_results)
2829 broken_rsp = test->broken_results[i].regs[k][1] < 0
2830 ? -test->broken_results[i].regs[k][1]
2831 : (ULONG64)fake_stack + test->broken_results[i].regs[k][1];
2832 else
2833 broken_rsp = expected_rsp;
2835 ok( context.Rsp == expected_rsp || broken( context.Rsp == broken_rsp ),
2836 "%u/%u: register rsp wrong %p/%p\n",
2837 testnum, i, (void *)context.Rsp, (void *)expected_rsp );
2838 continue;
2841 if (ctx_ptr.IntegerContext[j])
2843 ok( k < nb_regs || broken( broken_k < nb_regs ), "%u/%u: register %s should not be set to %Ix\n",
2844 testnum, i, reg_names_x86[j], *(&context.Rax + j) );
2845 ok( k == nb_regs || *(&context.Rax + j) == test->results[i].regs[k][1]
2846 || broken( broken_k == nb_regs || *(&context.Rax + j)
2847 == test->broken_results[i].regs[broken_k][1] ),
2848 "%u/%u: register %s wrong %p/%x\n",
2849 testnum, i, reg_names_x86[j], (void *)*(&context.Rax + j), test->results[i].regs[k][1] );
2851 else
2853 ok( k == nb_regs || broken( broken_k == nb_regs ), "%u/%u: register %s should be set\n",
2854 testnum, i, reg_names_x86[j] );
2855 if (j == rbp)
2856 ok( context.Rbp == orig_rbp, "%u/%u: register rbp wrong %p/unset\n",
2857 testnum, i, (void *)context.Rbp );
2858 else
2859 ok( *(&context.Rax + j) == unset_reg,
2860 "%u/%u: register %s wrong %p/unset\n",
2861 testnum, i, reg_names_x86[j], (void *)*(&context.Rax + j));
2867 static void test_virtual_unwind_x86(void)
2869 static const BYTE function_0[] =
2871 0xff, 0xf5, /* 00: push %rbp */
2872 0x48, 0x81, 0xec, 0x10, 0x01, 0x00, 0x00, /* 02: sub $0x110,%rsp */
2873 0x48, 0x8d, 0x6c, 0x24, 0x30, /* 09: lea 0x30(%rsp),%rbp */
2874 0x48, 0x89, 0x9d, 0xf0, 0x00, 0x00, 0x00, /* 0e: mov %rbx,0xf0(%rbp) */
2875 0x48, 0x89, 0xb5, 0xf8, 0x00, 0x00, 0x00, /* 15: mov %rsi,0xf8(%rbp) */
2876 0x90, /* 1c: nop */
2877 0x48, 0x8b, 0x9d, 0xf0, 0x00, 0x00, 0x00, /* 1d: mov 0xf0(%rbp),%rbx */
2878 0x48, 0x8b, 0xb5, 0xf8, 0x00, 0x00, 0x00, /* 24: mov 0xf8(%rbp),%rsi */
2879 0x48, 0x8d, 0xa5, 0xe0, 0x00, 0x00, 0x00, /* 2b: lea 0xe0(%rbp),%rsp */
2880 0x5d, /* 32: pop %rbp */
2881 0xc3 /* 33: ret */
2884 static const BYTE unwind_info_0[] =
2886 1 | (UNW_FLAG_EHANDLER << 3), /* version + flags */
2887 0x1c, /* prolog size */
2888 8, /* opcode count */
2889 (0x03 << 4) | rbp, /* frame reg rbp offset 0x30 */
2891 0x1c, UWOP(SAVE_NONVOL, rsi), 0x25, 0, /* 1c: mov %rsi,0x128(%rsp) */
2892 0x15, UWOP(SAVE_NONVOL, rbx), 0x24, 0, /* 15: mov %rbx,0x120(%rsp) */
2893 0x0e, UWOP(SET_FPREG, rbp), /* 0e: lea 0x30(%rsp),rbp */
2894 0x09, UWOP(ALLOC_LARGE, 0), 0x22, 0, /* 09: sub $0x110,%rsp */
2895 0x02, UWOP(PUSH_NONVOL, rbp), /* 02: push %rbp */
2897 0x00, 0x02, 0x00, 0x00, /* handler */
2898 0x05, 0x06, 0x07, 0x08, /* data */
2901 static const struct results_x86 results_0[] =
2903 /* offset rbp handler rip frame registers */
2904 { 0x00, 0x40, FALSE, 0x000, 0x000, { {rsp,0x008}, {-1,-1} }},
2905 { 0x02, 0x40, FALSE, 0x008, 0x000, { {rsp,0x010}, {rbp,0x000}, {-1,-1} }},
2906 { 0x09, 0x40, FALSE, 0x118, 0x000, { {rsp,0x120}, {rbp,0x110}, {-1,-1} }},
2907 { 0x0e, 0x40, FALSE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {-1,-1} }},
2908 { 0x15, 0x40, FALSE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {-1,-1} }},
2909 { 0x1c, 0x40, TRUE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {rsi,0x138}, {-1,-1}}},
2910 { 0x1d, 0x40, TRUE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {rsi,0x138}, {-1,-1}}},
2911 { 0x24, 0x40, TRUE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {rsi,0x138}, {-1,-1}}},
2912 { 0x2b, 0x40, FALSE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {-1,-1}}},
2913 { 0x32, 0x40, FALSE, 0x008, 0x010, { {rsp,0x010}, {rbp,0x000}, {-1,-1}}},
2914 { 0x33, 0x40, FALSE, 0x000, 0x010, { {rsp,0x008}, {-1,-1}}},
2917 static const struct results_x86 broken_results_0[] =
2919 /* offset rbp handler rip frame registers */
2920 { 0x00, 0x40, FALSE, 0x000, 0x000, { {rsp,0x008}, {-1,-1} }},
2921 { 0x02, 0x40, FALSE, 0x008, 0x000, { {rsp,0x010}, {rbp,0x000}, {-1,-1} }},
2922 { 0x09, 0x40, FALSE, 0x118, 0x000, { {rsp,0x120}, {rbp,0x110}, {-1,-1} }},
2923 { 0x0e, 0x40, FALSE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {-1,-1} }},
2924 { 0x15, 0x40, FALSE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {-1,-1} }},
2925 { 0x1c, 0x40, TRUE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {rsi,0x138}, {-1,-1}}},
2926 { 0x1d, 0x40, TRUE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {rsi,0x138}, {-1,-1}}},
2927 { 0x24, 0x40, TRUE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {rsi,0x138}, {-1,-1}}},
2928 /* On Win11 output frame in epilogue corresponds to context->Rsp - 0x8 when fpreg is set. */
2929 { 0x2b, 0x40, FALSE, 0x128, 0x128, { {rsp,0x130}, {rbp,0x120}, {-1,-1}}},
2930 { 0x32, 0x40, FALSE, 0x008, 0x008, { {rsp,0x010}, {rbp,0x000}, {-1,-1}}},
2931 { 0x33, 0x40, FALSE, 0x000, 0x000, { {rsp,0x008}, {-1,-1}}},
2934 static const BYTE function_1[] =
2936 0x53, /* 00: push %rbx */
2937 0x55, /* 01: push %rbp */
2938 0x56, /* 02: push %rsi */
2939 0x57, /* 03: push %rdi */
2940 0x41, 0x54, /* 04: push %r12 */
2941 0x48, 0x83, 0xec, 0x30, /* 06: sub $0x30,%rsp */
2942 0x90, 0x90, /* 0a: nop; nop */
2943 0x48, 0x83, 0xc4, 0x30, /* 0c: add $0x30,%rsp */
2944 0x41, 0x5c, /* 10: pop %r12 */
2945 0x5f, /* 12: pop %rdi */
2946 0x5e, /* 13: pop %rsi */
2947 0x5d, /* 14: pop %rbp */
2948 0x5b, /* 15: pop %rbx */
2949 0xc3 /* 16: ret */
2952 static const BYTE unwind_info_1[] =
2954 1 | (UNW_FLAG_EHANDLER << 3), /* version + flags */
2955 0x0a, /* prolog size */
2956 6, /* opcode count */
2957 0, /* frame reg */
2959 0x0a, UWOP(ALLOC_SMALL, 5), /* 0a: sub $0x30,%rsp */
2960 0x06, UWOP(PUSH_NONVOL, r12), /* 06: push %r12 */
2961 0x04, UWOP(PUSH_NONVOL, rdi), /* 04: push %rdi */
2962 0x03, UWOP(PUSH_NONVOL, rsi), /* 03: push %rsi */
2963 0x02, UWOP(PUSH_NONVOL, rbp), /* 02: push %rbp */
2964 0x01, UWOP(PUSH_NONVOL, rbx), /* 01: push %rbx */
2966 0x00, 0x02, 0x00, 0x00, /* handler */
2967 0x05, 0x06, 0x07, 0x08, /* data */
2970 static const struct results_x86 results_1[] =
2972 /* offset rbp handler rip frame registers */
2973 { 0x00, 0x50, FALSE, 0x000, 0x000, { {rsp,0x008}, {-1,-1} }},
2974 { 0x01, 0x50, FALSE, 0x008, 0x000, { {rsp,0x010}, {rbx,0x000}, {-1,-1} }},
2975 { 0x02, 0x50, FALSE, 0x010, 0x000, { {rsp,0x018}, {rbx,0x008}, {rbp,0x000}, {-1,-1} }},
2976 { 0x03, 0x50, FALSE, 0x018, 0x000, { {rsp,0x020}, {rbx,0x010}, {rbp,0x008}, {rsi,0x000}, {-1,-1} }},
2977 { 0x04, 0x50, FALSE, 0x020, 0x000, { {rsp,0x028}, {rbx,0x018}, {rbp,0x010}, {rsi,0x008}, {rdi,0x000}, {-1,-1} }},
2978 { 0x06, 0x50, FALSE, 0x028, 0x000, { {rsp,0x030}, {rbx,0x020}, {rbp,0x018}, {rsi,0x010}, {rdi,0x008}, {r12,0x000}, {-1,-1} }},
2979 { 0x0a, 0x50, TRUE, 0x058, 0x000, { {rsp,0x060}, {rbx,0x050}, {rbp,0x048}, {rsi,0x040}, {rdi,0x038}, {r12,0x030}, {-1,-1} }},
2980 { 0x0c, 0x50, FALSE, 0x058, 0x000, { {rsp,0x060}, {rbx,0x050}, {rbp,0x048}, {rsi,0x040}, {rdi,0x038}, {r12,0x030}, {-1,-1} }},
2981 { 0x10, 0x50, FALSE, 0x028, 0x000, { {rsp,0x030}, {rbx,0x020}, {rbp,0x018}, {rsi,0x010}, {rdi,0x008}, {r12,0x000}, {-1,-1} }},
2982 { 0x12, 0x50, FALSE, 0x020, 0x000, { {rsp,0x028}, {rbx,0x018}, {rbp,0x010}, {rsi,0x008}, {rdi,0x000}, {-1,-1} }},
2983 { 0x13, 0x50, FALSE, 0x018, 0x000, { {rsp,0x020}, {rbx,0x010}, {rbp,0x008}, {rsi,0x000}, {-1,-1} }},
2984 { 0x14, 0x50, FALSE, 0x010, 0x000, { {rsp,0x018}, {rbx,0x008}, {rbp,0x000}, {-1,-1} }},
2985 { 0x15, 0x50, FALSE, 0x008, 0x000, { {rsp,0x010}, {rbx,0x000}, {-1,-1} }},
2986 { 0x16, 0x50, FALSE, 0x000, 0x000, { {rsp,0x008}, {-1,-1} }},
2989 static const BYTE function_2[] =
2991 0x55, /* 00: push %rbp */
2992 0x90, 0x90, /* 01: nop; nop */
2993 0x5d, /* 03: pop %rbp */
2994 0xc3 /* 04: ret */
2997 static const BYTE unwind_info_2[] =
2999 1 | (UNW_FLAG_EHANDLER << 3), /* version + flags */
3000 0x0, /* prolog size */
3001 2, /* opcode count */
3002 0, /* frame reg */
3004 0x01, UWOP(PUSH_NONVOL, rbp), /* 02: push %rbp */
3005 0x00, UWOP(PUSH_MACHFRAME, 0), /* 00 */
3007 0x00, 0x02, 0x00, 0x00, /* handler */
3008 0x05, 0x06, 0x07, 0x08, /* data */
3011 static const struct results_x86 results_2[] =
3013 /* offset rbp handler rip frame registers */
3014 { 0x01, 0x50, TRUE, 0x008, 0x000, { {rsp,-0x020}, {rbp,0x000}, {-1,-1} }},
3017 static const BYTE unwind_info_3[] =
3019 1 | (UNW_FLAG_EHANDLER << 3), /* version + flags */
3020 0x0, /* prolog size */
3021 2, /* opcode count */
3022 0, /* frame reg */
3024 0x01, UWOP(PUSH_NONVOL, rbp), /* 02: push %rbp */
3025 0x00, UWOP(PUSH_MACHFRAME, 1), /* 00 */
3027 0x00, 0x02, 0x00, 0x00, /* handler */
3028 0x05, 0x06, 0x07, 0x08, /* data */
3031 static const struct results_x86 results_3[] =
3033 /* offset rbp handler rip frame registers */
3034 { 0x01, 0x50, TRUE, 0x010, 0x000, { {rsp,-0x028}, {rbp,0x000}, {-1,-1} }},
3037 static const BYTE function_4[] =
3039 0x55, /* 00: push %rbp */
3040 0x5d, /* 01: pop %rbp */
3041 0xc3 /* 02: ret */
3044 static const BYTE unwind_info_4[] =
3046 1 | (UNW_FLAG_EHANDLER << 3), /* version + flags */
3047 0x0, /* prolog size */
3048 0, /* opcode count */
3049 0, /* frame reg */
3051 0x00, 0x02, 0x00, 0x00, /* handler */
3052 0x05, 0x06, 0x07, 0x08, /* data */
3055 static const struct results_x86 results_4[] =
3057 /* offset rbp handler rip frame registers */
3058 { 0x01, 0x50, TRUE, 0x000, 0x000, { {rsp,0x008}, {-1,-1} }},
3061 static const struct results_x86 broken_results_4[] =
3063 /* offset rbp handler rip frame registers */
3064 { 0x01, 0x50, FALSE, 0x008, 0x000, { {rsp,0x010}, {rbp,0x000}, {-1,-1} }},
3067 #if 0
3068 static const BYTE function_5[] =
3070 0x90, /* 00: nop */
3071 0x90, /* 01: nop */
3072 0xc3 /* 02: ret */
3075 static const struct results_x86 results_5[] =
3077 /* offset rbp handler rip frame registers */
3078 { 0x01, 0x00, FALSE, 0x1234, 0x000, { {rsp,0x08}, {-1,-1} }},
3079 { 0x02, 0x00, FALSE, 0x1234, 0x000, { {rsp,0x08}, {-1,-1} }},
3081 #endif
3083 static const struct unwind_test_x86 tests[] =
3085 { function_0, sizeof(function_0), unwind_info_0, results_0, ARRAY_SIZE(results_0), broken_results_0 },
3086 { function_1, sizeof(function_1), unwind_info_1, results_1, ARRAY_SIZE(results_1) },
3087 { function_2, sizeof(function_2), unwind_info_2, results_2, ARRAY_SIZE(results_2) },
3088 { function_2, sizeof(function_2), unwind_info_3, results_3, ARRAY_SIZE(results_3) },
3090 /* Broken before Win10 1809. */
3091 { function_4, sizeof(function_4), unwind_info_4, results_4, ARRAY_SIZE(results_4), broken_results_4 },
3092 #if 0 /* crashes before Win10 21H2 */
3093 { function_5, sizeof(function_5), NULL, results_5, ARRAY_SIZE(results_5) },
3094 #endif
3096 unsigned int i;
3098 for (i = 0; i < ARRAY_SIZE(tests); i++)
3099 call_virtual_unwind_x86( i, &tests[i] );
3102 #endif /* __x86_64__ */
3104 #ifdef __x86_64__
3105 #define SET_RUNTIME_FUNC_LEN(func,len) do { (func)->EndAddress = (func)->BeginAddress + (len); } while(0)
3106 #elif defined(__arm__)
3107 #define SET_RUNTIME_FUNC_LEN(func,len) do { (func)->FunctionLength = len / 2; (func)->Flag = 1; } while(0)
3108 #else
3109 #define SET_RUNTIME_FUNC_LEN(func,len) do { (func)->FunctionLength = len / 4; (func)->Flag = 1; } while(0)
3110 #endif
3112 static RUNTIME_FUNCTION * CALLBACK dynamic_unwind_callback( DWORD_PTR pc, PVOID context )
3114 static const int code_offset = 1024;
3115 static RUNTIME_FUNCTION runtime_func;
3116 (*(DWORD *)context)++;
3118 runtime_func.BeginAddress = code_offset + 16;
3119 runtime_func.UnwindData = 0;
3120 SET_RUNTIME_FUNC_LEN( &runtime_func, 16 );
3121 return &runtime_func;
3124 static void test_dynamic_unwind(void)
3126 static const int code_offset = 1024;
3127 char buf[2 * sizeof(RUNTIME_FUNCTION) + 4];
3128 MEM_EXTENDED_PARAMETER param = { 0 };
3129 RUNTIME_FUNCTION *runtime_func, *func;
3130 ULONG_PTR table, base, ec_code;
3131 void *growable_table, *ptr;
3132 NTSTATUS status;
3133 SIZE_T size = 0x1000;
3134 DWORD count;
3135 ULONG len, len2;
3137 if (!pRtlInstallFunctionTableCallback || !pRtlLookupFunctionEntry)
3139 win_skip( "Dynamic unwind functions not found\n" );
3140 return;
3143 /* Test RtlAddFunctionTable with aligned RUNTIME_FUNCTION pointer */
3144 runtime_func = (RUNTIME_FUNCTION *)buf;
3145 runtime_func->BeginAddress = code_offset;
3146 runtime_func->UnwindData = 0;
3147 SET_RUNTIME_FUNC_LEN( runtime_func, 16 );
3148 ok( pRtlAddFunctionTable( runtime_func, 1, (ULONG_PTR)code_mem ),
3149 "RtlAddFunctionTable failed for runtime_func = %p (aligned)\n", runtime_func );
3151 /* Lookup function outside of any function table */
3152 base = 0xdeadbeef;
3153 func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 16, &base, NULL );
3154 ok( func == NULL,
3155 "RtlLookupFunctionEntry returned unexpected function, expected: NULL, got: %p\n", func );
3156 ok( !base || broken(base == 0xdeadbeef),
3157 "RtlLookupFunctionEntry modified base address, expected: 0, got: %Ix\n", base );
3159 /* Test with pointer inside of our function */
3160 base = 0xdeadbeef;
3161 func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 8, &base, NULL );
3162 ok( func == runtime_func,
3163 "RtlLookupFunctionEntry didn't return expected function, expected: %p, got: %p\n", runtime_func, func );
3164 ok( base == (ULONG_PTR)code_mem,
3165 "RtlLookupFunctionEntry returned invalid base, expected: %Ix, got: %Ix\n", (ULONG_PTR)code_mem, base );
3167 /* Test RtlDeleteFunctionTable */
3168 ok( pRtlDeleteFunctionTable( runtime_func ),
3169 "RtlDeleteFunctionTable failed for runtime_func = %p (aligned)\n", runtime_func );
3170 ok( !pRtlDeleteFunctionTable( runtime_func ),
3171 "RtlDeleteFunctionTable returned success for nonexistent table runtime_func = %p\n", runtime_func );
3173 /* Unaligned RUNTIME_FUNCTION pointer */
3174 runtime_func = (RUNTIME_FUNCTION *)((ULONG_PTR)buf | 0x3);
3175 runtime_func->BeginAddress = code_offset;
3176 runtime_func->UnwindData = 0;
3177 SET_RUNTIME_FUNC_LEN( runtime_func, 16 );
3178 ok( pRtlAddFunctionTable( runtime_func, 1, (ULONG_PTR)code_mem ),
3179 "RtlAddFunctionTable failed for runtime_func = %p (unaligned)\n", runtime_func );
3180 ok( pRtlDeleteFunctionTable( runtime_func ),
3181 "RtlDeleteFunctionTable failed for runtime_func = %p (unaligned)\n", runtime_func );
3183 /* Attempt to insert the same entry twice */
3184 runtime_func = (RUNTIME_FUNCTION *)buf;
3185 runtime_func->BeginAddress = code_offset;
3186 runtime_func->UnwindData = 0;
3187 SET_RUNTIME_FUNC_LEN( runtime_func, 16 );
3188 ok( pRtlAddFunctionTable( runtime_func, 1, (ULONG_PTR)code_mem ),
3189 "RtlAddFunctionTable failed for runtime_func = %p (first attempt)\n", runtime_func );
3190 ok( pRtlAddFunctionTable( runtime_func, 1, (ULONG_PTR)code_mem ),
3191 "RtlAddFunctionTable failed for runtime_func = %p (second attempt)\n", runtime_func );
3192 ok( pRtlDeleteFunctionTable( runtime_func ),
3193 "RtlDeleteFunctionTable failed for runtime_func = %p (first attempt)\n", runtime_func );
3194 ok( pRtlDeleteFunctionTable( runtime_func ),
3195 "RtlDeleteFunctionTable failed for runtime_func = %p (second attempt)\n", runtime_func );
3196 ok( !pRtlDeleteFunctionTable( runtime_func ),
3197 "RtlDeleteFunctionTable returned success for nonexistent table runtime_func = %p\n", runtime_func );
3199 /* Empty table */
3200 ok( pRtlAddFunctionTable( runtime_func, 0, (ULONG_PTR)code_mem ),
3201 "RtlAddFunctionTable failed for empty table\n" );
3202 ok( pRtlDeleteFunctionTable( runtime_func ),
3203 "RtlDeleteFunctionTable failed for empty table\n" );
3204 ok( !pRtlDeleteFunctionTable( runtime_func ),
3205 "RtlDeleteFunctionTable succeeded twice for empty table\n" );
3207 /* Test RtlInstallFunctionTableCallback with both low bits unset */
3208 table = (ULONG_PTR)code_mem;
3209 ok( !pRtlInstallFunctionTableCallback( table, (ULONG_PTR)code_mem, code_offset + 32, &dynamic_unwind_callback, (PVOID*)&count, NULL ),
3210 "RtlInstallFunctionTableCallback returned success for table = %Ix\n", table );
3212 /* Test RtlInstallFunctionTableCallback with both low bits set */
3213 table = (ULONG_PTR)code_mem | 0x3;
3214 ok( pRtlInstallFunctionTableCallback( table, (ULONG_PTR)code_mem, code_offset + 32, &dynamic_unwind_callback, (PVOID*)&count, NULL ),
3215 "RtlInstallFunctionTableCallback failed for table = %Ix\n", table );
3217 /* Lookup function outside of any function table */
3218 count = 0;
3219 base = 0xdeadbeef;
3220 func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 32, &base, NULL );
3221 ok( func == NULL,
3222 "RtlLookupFunctionEntry returned unexpected function, expected: NULL, got: %p\n", func );
3223 ok( !base || broken(base == 0xdeadbeef),
3224 "RtlLookupFunctionEntry modified base address, expected: 0, got: %Ix\n", base );
3225 ok( !count,
3226 "RtlLookupFunctionEntry issued %ld unexpected calls to dynamic_unwind_callback\n", count );
3228 /* Test with pointer inside of our function */
3229 count = 0;
3230 base = 0xdeadbeef;
3231 func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 24, &base, NULL );
3232 ok( count == 1 || broken(!count), /* win10 arm */
3233 "RtlLookupFunctionEntry issued %ld calls to dynamic_unwind_callback, expected: 1\n", count );
3234 if (count)
3236 ok( func != NULL && func->BeginAddress == code_offset + 16,
3237 "RtlLookupFunctionEntry didn't return expected function, got: %p\n", func );
3238 ok( base == (ULONG_PTR)code_mem,
3239 "RtlLookupFunctionEntry returned invalid base: %Ix / %Ix\n", (ULONG_PTR)code_mem, base );
3242 /* Clean up again */
3243 ok( pRtlDeleteFunctionTable( (PRUNTIME_FUNCTION)table ),
3244 "RtlDeleteFunctionTable failed for table = %p\n", (PVOID)table );
3245 ok( !pRtlDeleteFunctionTable( (PRUNTIME_FUNCTION)table ),
3246 "RtlDeleteFunctionTable returned success for nonexistent table = %p\n", (PVOID)table );
3248 if (!pRtlAddGrowableFunctionTable)
3250 win_skip("Growable function tables are not supported.\n");
3251 return;
3254 runtime_func = (RUNTIME_FUNCTION *)buf;
3255 runtime_func->BeginAddress = code_offset;
3256 runtime_func->UnwindData = 0;
3257 SET_RUNTIME_FUNC_LEN( runtime_func, 16 );
3258 runtime_func++;
3259 runtime_func->BeginAddress = code_offset + 16;
3260 runtime_func->UnwindData = 0;
3261 SET_RUNTIME_FUNC_LEN( runtime_func, 16 );
3262 runtime_func = (RUNTIME_FUNCTION *)buf;
3264 growable_table = NULL;
3265 status = pRtlAddGrowableFunctionTable( &growable_table, runtime_func, 1, 1, (ULONG_PTR)code_mem, (ULONG_PTR)code_mem + 64 );
3266 ok(!status, "RtlAddGrowableFunctionTable failed for runtime_func = %p (aligned), %#lx.\n", runtime_func, status );
3267 ok(growable_table != 0, "Unexpected table value.\n");
3268 pRtlDeleteGrowableFunctionTable( growable_table );
3270 growable_table = NULL;
3271 status = pRtlAddGrowableFunctionTable( &growable_table, runtime_func, 2, 2, (ULONG_PTR)code_mem, (ULONG_PTR)code_mem + 64 );
3272 ok(!status, "RtlAddGrowableFunctionTable failed for runtime_func = %p (aligned), %#lx.\n", runtime_func, status );
3273 ok(growable_table != 0, "Unexpected table value.\n");
3274 pRtlDeleteGrowableFunctionTable( growable_table );
3276 growable_table = NULL;
3277 status = pRtlAddGrowableFunctionTable( &growable_table, runtime_func, 1, 2, (ULONG_PTR)code_mem, (ULONG_PTR)code_mem + 64 );
3278 ok(!status, "RtlAddGrowableFunctionTable failed for runtime_func = %p (aligned), %#lx.\n", runtime_func, status );
3279 ok(growable_table != 0, "Unexpected table value.\n");
3280 pRtlDeleteGrowableFunctionTable( growable_table );
3282 growable_table = NULL;
3283 status = pRtlAddGrowableFunctionTable( &growable_table, runtime_func, 0, 2, (ULONG_PTR)code_mem,
3284 (ULONG_PTR)code_mem + code_offset + 64 );
3285 ok(!status, "RtlAddGrowableFunctionTable failed for runtime_func = %p (aligned), %#lx.\n", runtime_func, status );
3286 ok(growable_table != 0, "Unexpected table value.\n");
3288 /* Current count is 0. */
3289 func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 8, &base, NULL );
3290 ok( func == NULL,
3291 "RtlLookupFunctionEntry didn't return expected function, expected: %p, got: %p\n", runtime_func, func );
3293 pRtlGrowFunctionTable( growable_table, 1 );
3295 base = 0xdeadbeef;
3296 func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 8, &base, NULL );
3297 ok( func == runtime_func,
3298 "RtlLookupFunctionEntry didn't return expected function, expected: %p, got: %p\n", runtime_func, func );
3299 ok( base == (ULONG_PTR)code_mem,
3300 "RtlLookupFunctionEntry returned invalid base, expected: %Ix, got: %Ix\n", (ULONG_PTR)code_mem, base );
3302 /* Second function is inaccessible yet. */
3303 base = 0xdeadbeef;
3304 func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 16, &base, NULL );
3305 ok( func == NULL,
3306 "RtlLookupFunctionEntry didn't return expected function, expected: %p, got: %p\n", runtime_func, func );
3308 pRtlGrowFunctionTable( growable_table, 2 );
3310 base = 0xdeadbeef;
3311 func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 16, &base, NULL );
3312 ok( func == runtime_func + 1,
3313 "RtlLookupFunctionEntry didn't return expected function, expected: %p, got: %p\n", runtime_func, func );
3314 ok( base == (ULONG_PTR)code_mem,
3315 "RtlLookupFunctionEntry returned invalid base, expected: %Ix, got: %Ix\n", (ULONG_PTR)code_mem, base );
3317 base = 0xdeadbeef;
3318 func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 32, &base, NULL );
3319 ok( func == NULL, "RtlLookupFunctionEntry got %p\n", func );
3320 ok( base == 0xdeadbeef, "RtlLookupFunctionTable wrong base, got: %Ix\n", base );
3322 base = 0xdeadbeef;
3323 func = pRtlLookupFunctionTable( (ULONG_PTR)code_mem + code_offset + 8, &base, &len );
3324 ok( func == NULL, "RtlLookupFunctionTable wrong table, got: %p\n", func );
3325 ok( base == 0xdeadbeef, "RtlLookupFunctionTable wrong base, got: %Ix\n", base );
3327 base = 0xdeadbeef;
3328 len = 0xdeadbeef;
3329 func = pRtlLookupFunctionTable( (ULONG_PTR)pRtlLookupFunctionEntry, &base, &len );
3330 ok( base == (ULONG_PTR)GetModuleHandleA("ntdll.dll"),
3331 "RtlLookupFunctionTable wrong base, got: %Ix / %p\n", base, GetModuleHandleA("ntdll.dll") );
3332 ptr = RtlImageDirectoryEntryToData( (void *)base, TRUE, IMAGE_DIRECTORY_ENTRY_EXCEPTION, &len2 );
3333 ok( func == ptr, "RtlLookupFunctionTable wrong table, got: %p / %p\n", func, ptr );
3334 ok( len == len2 || !ptr, "RtlLookupFunctionTable wrong len, got: %lu / %lu\n", len, len2 );
3336 pRtlDeleteGrowableFunctionTable( growable_table );
3338 param.Type = MemExtendedParameterAttributeFlags;
3339 param.ULong64 = MEM_EXTENDED_PARAMETER_EC_CODE;
3340 ec_code = 0;
3341 if (pNtAllocateVirtualMemoryEx &&
3342 !pNtAllocateVirtualMemoryEx( GetCurrentProcess(), (void **)&ec_code, &size,
3343 MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE, &param, 1 ))
3345 static const BYTE fast_forward[] = { 0x48, 0x8b, 0xc4, 0x48, 0x89, 0x58, 0x20, 0x55, 0x5d, 0xe9 };
3346 IMAGE_ARM64EC_METADATA *metadata;
3347 ARM64_RUNTIME_FUNCTION *arm64func = (ARM64_RUNTIME_FUNCTION *)buf;
3349 trace( "running arm64ec tests\n" );
3351 if (!memcmp( pRtlLookupFunctionEntry, fast_forward, sizeof(fast_forward) ))
3353 ptr = (char *)pRtlLookupFunctionEntry + sizeof(fast_forward);
3354 ptr = (char *)ptr + 4 + *(int *)ptr;
3355 base = 0xdeadbeef;
3356 func = pRtlLookupFunctionTable( (ULONG_PTR)ptr, &base, &len );
3357 ok( base == (ULONG_PTR)GetModuleHandleA("ntdll.dll"),
3358 "RtlLookupFunctionTable wrong base, got: %Ix / %p\n", base, GetModuleHandleA("ntdll.dll") );
3359 ptr = RtlImageDirectoryEntryToData( (void *)base, TRUE, IMAGE_DIRECTORY_ENTRY_EXCEPTION, &len2 );
3360 ok( func != ptr, "RtlLookupFunctionTable wrong table, got: %p / %p\n", func, ptr );
3361 ptr = RtlImageDirectoryEntryToData( (void *)base, TRUE, IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG, &len2 );
3362 metadata = (void *)((IMAGE_LOAD_CONFIG_DIRECTORY *)ptr)->CHPEMetadataPointer;
3363 ok( (char *)func == (char *)base + metadata->ExtraRFETable,
3364 "RtlLookupFunctonTable wrong table, got: %p / %p\n", func, (char *)base + metadata->ExtraRFETable );
3365 ok( len == metadata->ExtraRFETableSize, "RtlLookupFunctionTable wrong len, got: %lu / %lu\n",
3366 len, metadata->ExtraRFETableSize );
3369 arm64func->BeginAddress = code_offset;
3370 arm64func->Flag = 1;
3371 arm64func->FunctionLength = 4;
3372 arm64func->RegF = 1;
3373 arm64func->RegI = 1;
3374 arm64func->H = 1;
3375 arm64func->CR = 1;
3376 arm64func->FrameSize = 1;
3377 arm64func++;
3378 arm64func->BeginAddress = code_offset + 16;
3379 arm64func->Flag = 1;
3380 arm64func->FunctionLength = 4;
3381 arm64func->RegF = 1;
3382 arm64func->RegI = 1;
3383 arm64func->H = 1;
3384 arm64func->CR = 1;
3385 arm64func->FrameSize = 1;
3387 growable_table = NULL;
3388 status = pRtlAddGrowableFunctionTable( &growable_table, (RUNTIME_FUNCTION *)buf,
3389 2, 2, ec_code, ec_code + code_offset + 64 );
3390 ok( !status, "RtlAddGrowableFunctionTable failed %lx\n", status );
3392 base = 0xdeadbeef;
3393 func = pRtlLookupFunctionEntry( ec_code + code_offset + 8, &base, NULL );
3394 ok( func == (RUNTIME_FUNCTION *)buf, "RtlLookupFunctionEntry expected func: %p, got: %p\n",
3395 buf, func );
3396 ok( base == ec_code, "RtlLookupFunctionEntry expected base: %Ix, got: %Ix\n",
3397 ec_code, base );
3399 base = 0xdeadbeef;
3400 func = pRtlLookupFunctionEntry( ec_code + code_offset + 16, &base, NULL );
3401 ok( func == (RUNTIME_FUNCTION *)(buf + sizeof(*arm64func)),
3402 "RtlLookupFunctionEntry expected func: %p, got: %p\n", buf + sizeof(*arm64func), func );
3403 ok( base == ec_code, "RtlLookupFunctionEntry expected base: %Ix, got: %Ix\n", ec_code, base );
3405 base = 0xdeadbeef;
3406 func = pRtlLookupFunctionEntry( ec_code + code_offset + 32, &base, NULL );
3407 ok( !func, "RtlLookupFunctionEntry got: %p\n", func );
3408 ok( base == 0xdeadbeef, "RtlLookupFunctionEntry got: %Ix\n", base );
3410 pRtlDeleteGrowableFunctionTable( growable_table );
3411 VirtualFree( (void *)ec_code, 0, MEM_RELEASE );
3416 START_TEST(unwind)
3418 ntdll = GetModuleHandleA("ntdll.dll");
3419 code_mem = VirtualAlloc( NULL, 65536, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE );
3421 #define X(f) p##f = (void*)GetProcAddress(ntdll, #f)
3422 X(NtAllocateVirtualMemoryEx);
3423 X(RtlAddFunctionTable);
3424 X(RtlAddGrowableFunctionTable);
3425 X(RtlDeleteFunctionTable);
3426 X(RtlDeleteGrowableFunctionTable);
3427 X(RtlGrowFunctionTable);
3428 X(RtlInstallFunctionTableCallback);
3429 X(RtlLookupFunctionEntry);
3430 X(RtlLookupFunctionTable);
3431 X(RtlVirtualUnwind2);
3432 #undef X
3434 #ifdef __arm__
3435 test_virtual_unwind_arm();
3436 #elif defined(__aarch64__)
3437 test_virtual_unwind_arm64();
3438 #elif defined(__x86_64__)
3439 test_virtual_unwind_x86();
3440 test_virtual_unwind_arm64();
3441 #endif
3443 test_dynamic_unwind();
3446 #else /* !__i386__ */
3448 START_TEST(unwind)
3452 #endif /* !__i386__ */