Wine-Devel
By thread
wine-devel@list.winehq.org
By month
Messages by month
- ----- 2026 -----
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2006 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2005 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2004 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2003 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2002 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2001 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
November 2021
- 83 participants
- 2620 messages
[PATCH 07/18] winedump: Print the whole multibyte unwind opcode for arm.
by Martin Storsjö
Signed-off-by: Martin Storsjö <martin(a)martin.st>
---
tools/winedump/pe.c | 18 +++++++++++++++++-
1 file changed, 17 insertions(+), 1 deletion(-)
diff --git a/tools/winedump/pe.c b/tools/winedump/pe.c
index 04e01755171..4629c3082f9 100644
--- a/tools/winedump/pe.c
+++ b/tools/winedump/pe.c
@@ -764,6 +764,18 @@ static void dump_x86_64_unwind_info( const struct runtime_function_x86_64 *funct
(ULONG)(function->UnwindData + (const char *)(&handler_data->handler + 1) - (const char *)info ));
}
+static const BYTE armnt_code_lengths[256] =
+{
+/* 00 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+/* 20 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+/* 40 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+/* 60 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+/* 80 */ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+/* a0 */ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+/* c0 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+/* e0 */ 1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,4,3,4,1,1,1,1,1
+};
+
static void dump_armnt_unwind_info( const struct runtime_function_armnt *fnc )
{
const struct unwind_info_armnt *info;
@@ -990,6 +1002,7 @@ static void dump_armnt_unwind_info( const struct runtime_function_armnt *fnc )
for (b = 0; b < words * sizeof(*codes); b++)
{
BYTE code = bytes[b];
+ BYTE len = armnt_code_lengths[code];
if (info->e && b == count)
{
@@ -1007,7 +1020,10 @@ static void dump_armnt_unwind_info( const struct runtime_function_armnt *fnc )
}
}
- printf( " Unwind Code %x\t", code );
+ printf( " Unwind Code");
+ for (i = 0; i < len; i++)
+ printf( " %02x", bytes[b+i] );
+ printf( "\t" );
if (code == 0x00)
printf( "\n" );
--
2.25.1
Nov. 9, 2021
[PATCH 06/18] ntdll: tests: Add tests for RtlVirtualUnwind for arm.
by Martin Storsjö
Signed-off-by: Martin Storsjö <martin(a)martin.st>
---
This maybe should come before the actual implementation, but then
we'd have to load the RtlVirtualUnwind function pointer using
GetProcAddress, as it's not in the ntdll import library before the
preceding commit.
---
dlls/ntdll/tests/exception.c | 1350 ++++++++++++++++++++++++++++++++++
1 file changed, 1350 insertions(+)
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c
index c57d7c01d86..a41cc82905c 100644
--- a/dlls/ntdll/tests/exception.c
+++ b/dlls/ntdll/tests/exception.c
@@ -4918,6 +4918,1352 @@ static void test_unwind_from_apc(void)
}
#elif defined(__arm__)
+#define UNW_FLAG_NHANDLER 0
+#define UNW_FLAG_EHANDLER 1
+#define UNW_FLAG_UHANDLER 2
+
+#define UWOP_TWOBYTES(x) (((x) >> 8) & 0xff), ((x) & 0xff)
+#define UWOP_THREEBYTES(x) (((x) >> 16) & 0xff), (((x) >> 8) & 0xff), ((x) & 0xff)
+#define UWOP_FOURBYTES(x) (((x) >> 24) & 0xff), (((x) >> 16) & 0xff), (((x) >> 8) & 0xff), ((x) & 0xff)
+
+#define UWOP_ALLOC_SMALL(size) (0x00 | (size/4)) /* Max 0x7f * 4 */
+#define UWOP_SAVE_REGSW(regmask) UWOP_TWOBYTES((0x80 << 8) | (regmask))
+#define UWOP_SET_FP(reg) (0xC0 | reg)
+#define UWOP_SAVE_RANGE_4_7_LR(reg,lr) (0xD0 | (reg - 4) | ((lr) ? 0x04 : 0))
+#define UWOP_SAVE_RANGE_4_11_LR(reg,lr)(0xD8 | (reg - 8) | ((lr) ? 0x04 : 0))
+#define UWOP_SAVE_D8_RANGE(reg) (0xE0 | (reg - 8))
+#define UWOP_ALLOC_MEDIUMW(size) UWOP_TWOBYTES((0xE8 << 8) | (size/4)) /* Max 0x3ff * 4 */
+#define UWOP_SAVE_REGS(regmask) UWOP_TWOBYTES((0xEC << 8) | ((regmask) & 0xFF) | (((regmask) & (1<<lr)) ? 0x100 : 0))
+#define UWOP_SAVE_LR(offset) UWOP_TWOBYTES((0xEF << 8) | (offset/4))
+#define UWOP_SAVE_D0_RANGE(first,last) UWOP_TWOBYTES((0xF5 << 8) | (first << 4) | (last))
+#define UWOP_SAVE_D16_RANGE(first,last)UWOP_TWOBYTES((0xF6 << 8) | ((first - 16) << 4) | (last - 16))
+#define UWOP_ALLOC_LARGE(size) UWOP_THREEBYTES((0xF7 << 16) | (size/4))
+#define UWOP_ALLOC_HUGE(size) UWOP_FOURBYTES((0xF8 << 24) | (size/4))
+#define UWOP_ALLOC_LARGEW(size) UWOP_THREEBYTES((0xF9 << 16) | (size/4))
+#define UWOP_ALLOC_HUGEW(size) UWOP_FOURBYTES((0xFA << 24) | (size/4))
+#define UWOP_NOP16 0xFB
+#define UWOP_NOP32 0xFC
+#define UWOP_END_NOP16 0xFD
+#define UWOP_END_NOP32 0xFE
+#define UWOP_END 0xFF
+
+struct results
+{
+ int pc_offset; /* pc offset from code start */
+ int fp_offset; /* fp offset from stack pointer */
+ int handler; /* expect handler to be set? */
+ ULONG_PTR pc; /* expected final pc value */
+ int frame; /* expected frame return value */
+ int frame_offset; /* whether the frame return value is an offset or an absolute value */
+ LONGLONG regs[47][2];/* expected values for registers */
+};
+
+struct unwind_test
+{
+ const BYTE *function;
+ size_t function_size;
+ const BYTE *unwind_info;
+ size_t unwind_size;
+ const struct results *results;
+ unsigned int nb_results;
+};
+
+enum regs
+{
+ /* Note, lr and sp are swapped to allow using 'lr' in register bitmasks. */
+ r0, r1, r2, r3, r4, r5, r6, r7,
+ r8, r9, r10, r11, r12, lr, sp,
+ d0, d1, d2, d3, d4, d5, d6, d7,
+ d8, d9, d10, d11, d12, d13, d14, d15,
+ d16, d17, d18, d19, d20, d21, d22, d23,
+ d24, d25, d26, d27, d28, d29, d30, d31,
+};
+
+static const char * const reg_names[47] =
+{
+ "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
+ "r8", "r9", "r10", "r11", "r12", "lr", "sp",
+ "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
+ "d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15",
+ "d16", "d17", "d18", "d19", "d20", "d21", "d22", "d23",
+ "d24", "d25", "d26", "d27", "d28", "d29", "d30", "d31",
+};
+
+#define ORIG_LR 0xCCCCCCCC
+
+static void call_virtual_unwind( int testnum, const struct unwind_test *test )
+{
+ static const int code_offset = 1024;
+ static const int unwind_offset = 2048;
+ void *handler, *data;
+ CONTEXT context;
+ RUNTIME_FUNCTION runtime_func;
+ KNONVOLATILE_CONTEXT_POINTERS ctx_ptr;
+ UINT i, j, k;
+ ULONG fake_stack[256];
+ ULONG_PTR frame, orig_pc, orig_fp, unset_reg, sp_offset = 0;
+ ULONGLONG unset_reg64;
+ static const UINT nb_regs = ARRAY_SIZE(test->results[i].regs);
+
+ memcpy( (char *)code_mem + code_offset, test->function, test->function_size );
+ memcpy( (char *)code_mem + unwind_offset, test->unwind_info, test->unwind_size );
+
+ runtime_func.BeginAddress = code_offset;
+ if (test->unwind_size)
+ runtime_func.UnwindData = unwind_offset;
+ else
+ memcpy(&runtime_func.UnwindData, test->unwind_info, 4);
+
+ trace( "code: %p stack: %p\n", code_mem, fake_stack );
+
+ for (i = 0; i < test->nb_results; i++)
+ {
+ memset( &ctx_ptr, 0, sizeof(ctx_ptr) );
+ memset( &context, 0x55, sizeof(context) );
+ memset( &unset_reg, 0x55, sizeof(unset_reg) );
+ memset( &unset_reg64, 0x55, sizeof(unset_reg64) );
+ for (j = 0; j < 256; j++) fake_stack[j] = j * 4;
+
+ context.Sp = (ULONG_PTR)fake_stack;
+ context.Lr = (ULONG_PTR)ORIG_LR;
+ context.R11 = (ULONG_PTR)fake_stack + test->results[i].fp_offset;
+ orig_fp = context.R11;
+ orig_pc = (ULONG64)code_mem + code_offset + test->results[i].pc_offset;
+
+ trace( "%u/%u: pc=%p (%02x) fp=%p sp=%p\n", testnum, i,
+ (void *)orig_pc, *(DWORD *)orig_pc, (void *)orig_fp, (void *)context.Sp );
+
+ data = (void *)0xdeadbeef;
+ handler = RtlVirtualUnwind( UNW_FLAG_EHANDLER, (ULONG)code_mem, orig_pc,
+ &runtime_func, &context, &data, &frame, &ctx_ptr );
+ if (test->results[i].handler > 0)
+ {
+ /* Yet untested */
+ ok( (char *)handler == (char *)code_mem + 0x200,
+ "%u/%u: wrong handler %p/%p\n", testnum, i, handler, (char *)code_mem + 0x200 );
+ if (handler) ok( *(DWORD *)data == 0x08070605,
+ "%u/%u: wrong handler data %p\n", testnum, i, data );
+ }
+ else
+ {
+ ok( handler == NULL, "%u/%u: handler %p instead of NULL\n", testnum, i, handler );
+ ok( data == (test->results[i].handler < 0 ?
+ (void *)0xdeadbeef : NULL),
+ "%u/%u: handler data set to %p/%p\n", testnum, i, data,
+ (test->results[i].handler < 0 ? (void *)0xdeadbeef : NULL) );
+ }
+
+ ok( context.Pc == test->results[i].pc, "%u/%u: wrong pc %p/%p\n",
+ testnum, i, (void *)context.Pc, (void*)test->results[i].pc );
+ ok( frame == (test->results[i].frame_offset ? (ULONG)fake_stack : 0) + test->results[i].frame, "%u/%u: wrong frame %x/%x\n",
+ testnum, i, (int)((char *)frame - (char *)(test->results[i].frame_offset ? fake_stack : NULL)), test->results[i].frame );
+
+ sp_offset = 0;
+ for (k = 0; k < nb_regs; k++)
+ {
+ if (test->results[i].regs[k][0] == -1)
+ break;
+ if (test->results[i].regs[k][0] == sp) {
+ /* If sp is part of the registers list, treat it as an offset
+ * between the returned frame pointer and the sp register. */
+ sp_offset = test->results[i].regs[k][1];
+ break;
+ }
+ }
+ ok( frame - sp_offset == context.Sp, "%u/%u: wrong sp %p/%p\n",
+ testnum, i, (void *)(frame - sp_offset), (void *)context.Sp);
+
+ for (j = 0; j < 47; j++)
+ {
+ if (j == sp) continue; /* Handling sp separately above */
+
+ for (k = 0; k < nb_regs; k++)
+ {
+ if (test->results[i].regs[k][0] == -1)
+ {
+ k = nb_regs;
+ break;
+ }
+ if (test->results[i].regs[k][0] == j) break;
+ }
+
+ if (j >= 4 && j <= 11 && (&ctx_ptr.R4)[j - 4])
+ {
+ ok( k < nb_regs, "%u/%u: register %s should not be set to %x\n",
+ testnum, i, reg_names[j], (&context.R0)[j] );
+ if (k < nb_regs)
+ ok( (&context.R0)[j] == test->results[i].regs[k][1],
+ "%u/%u: register %s wrong %p/%x\n",
+ testnum, i, reg_names[j], (void *)(&context.R0)[j], (int)test->results[i].regs[k][1] );
+ }
+ else if (j == lr && ctx_ptr.Lr)
+ {
+ ok( k < nb_regs, "%u/%u: register %s should not be set to %x\n",
+ testnum, i, reg_names[j], context.Lr );
+ if (k < nb_regs)
+ ok( context.Lr == test->results[i].regs[k][1],
+ "%u/%u: register %s wrong %p/%x\n",
+ testnum, i, reg_names[j], (void *)context.Lr, (int)test->results[i].regs[k][1] );
+ }
+ else if (j >= d8 && j <= d15 && (&ctx_ptr.D8)[j - d8])
+ {
+ ok( k < nb_regs, "%u/%u: register %s should not be set to %llx\n",
+ testnum, i, reg_names[j], context.D[j - d0] );
+ if (k < nb_regs)
+ ok( context.D[j - d0] == test->results[i].regs[k][1],
+ "%u/%u: register %s wrong %llx/%llx\n",
+ testnum, i, reg_names[j], context.D[j - d0], test->results[i].regs[k][1] );
+ }
+ else if (k < nb_regs)
+ {
+ if (j <= r12)
+ ok( (&context.R0)[j] == test->results[i].regs[k][1],
+ "%u/%u: register %s wrong %p/%x\n",
+ testnum, i, reg_names[j], (void *)(&context.R0)[j], (int)test->results[i].regs[k][1] );
+ else if (j == lr)
+ ok( context.Lr == test->results[i].regs[k][1],
+ "%u/%u: register %s wrong %p/%x\n",
+ testnum, i, reg_names[j], (void *)context.Lr, (int)test->results[i].regs[k][1] );
+ else
+ ok( context.D[j - d0] == test->results[i].regs[k][1],
+ "%u/%u: register %s wrong %llx/%llx\n",
+ testnum, i, reg_names[j], context.D[j - d0], test->results[i].regs[k][1] );
+ }
+ else
+ {
+ ok( k == nb_regs, "%u/%u: register %s should be set\n", testnum, i, reg_names[j] );
+ if (j == lr)
+ ok( context.Lr == ORIG_LR, "%u/%u: register lr wrong %p/unset\n",
+ testnum, i, (void *)context.Lr );
+ else if (j == r11)
+ ok( context.R11 == orig_fp, "%u/%u: register fp wrong %p/unset\n",
+ testnum, i, (void *)context.R11 );
+ else if (j < d0)
+ ok( (&context.R0)[j] == unset_reg,
+ "%u/%u: register %s wrong %p/unset\n",
+ testnum, i, reg_names[j], (void *)(&context.R0)[j]);
+ else
+ ok( context.D[j - d0] == unset_reg64,
+ "%u/%u: register %s wrong %llx/unset\n",
+ testnum, i, reg_names[j], context.D[j - d0]);
+ }
+ }
+ }
+}
+
+#define DW(dword) ((dword >> 0) & 0xff), ((dword >> 8) & 0xff), ((dword >> 16) & 0xff), ((dword >> 24) & 0xff)
+
+static void test_virtual_unwind(void)
+{
+
+ static const BYTE function_0[] =
+ {
+ 0x70, 0xb5, /* 00: push {r4-r6, lr} */
+ 0x88, 0xb0, /* 02: sub sp, sp, #32 */
+ 0x2d, 0xed, 0x06, 0x8b, /* 04: vpush {d8-d10} */
+ 0x00, 0xbf, /* 08: nop */
+ 0x2d, 0xed, 0x06, 0x3b, /* 0a: vpush {d3-d5} */
+ 0xaf, 0x3f, 0x00, 0x80, /* 0e: nop.w */
+ 0x6d, 0xed, 0x06, 0x1b, /* 12: vpush {d17-d19} */
+ 0x2d, 0xe9, 0x00, 0x15, /* 16: push.w {r8, r10, r12} */
+ 0xeb, 0x46, /* 1a: mov r11, sp */
+ 0x00, 0xbf, /* 1c: nop */
+ 0xbd, 0xec, 0x06, 0x8b, /* 1e: vpop {d8-d10} */
+ 0xdd, 0x46, /* 22: mov sp, r11 */
+ 0x08, 0xb0, /* 24: add sp, sp, #32 */
+ 0x70, 0xbd, /* 26: pop {r4-r6, pc} */
+ };
+
+ static const DWORD unwind_info_0_header =
+ (sizeof(function_0)/2) | /* function length */
+ (0 << 20) | /* X */
+ (0 << 21) | /* E */
+ (0 << 22) | /* F */
+ (1 << 23) | /* epilog */
+ (5 << 28); /* codes, (sizeof(unwind_info_0)-headers+3)/4 */
+ static const DWORD unwind_info_0_epilog0 =
+ (15 << 0) | /* offset = 0x1e / 2 = 15 */
+ (0xE << 20) | /* condition, 0xE = always */
+ (13 << 24); /* index, byte offset to epilog opcodes */
+
+ static const BYTE unwind_info_0[] =
+ {
+ DW(unwind_info_0_header),
+ DW(unwind_info_0_epilog0),
+
+ UWOP_SET_FP(11), /* mov r11, sp */
+ UWOP_SAVE_REGSW((1<<r8)|(1<<r10)|(1<<r12)), /* push.w {r8, r10, r12} */
+ UWOP_SAVE_D16_RANGE(17,19), /* vpush {d17-d19} */
+ UWOP_NOP32, /* nop.w */
+ UWOP_SAVE_D0_RANGE(3,5), /* vpush {d3-d5} */
+ UWOP_NOP16, /* nop */
+ UWOP_SAVE_D8_RANGE(10), /* vpush {d8-d10} */
+ UWOP_ALLOC_SMALL(32), /* sub sp, sp, #32 */
+ UWOP_SAVE_RANGE_4_7_LR(6, 1), /* push {r4-r6,lr} */
+ UWOP_END,
+
+ UWOP_SAVE_D8_RANGE(10), /* vpop {d8-d10} */
+ UWOP_SET_FP(11), /* mov sp, r11 */
+ UWOP_ALLOC_SMALL(32), /* add sp, sp, #32 */
+ UWOP_SAVE_RANGE_4_7_LR(6, 1), /* pop {r4-r6,pc} */
+ UWOP_END,
+ };
+
+ static const struct results results_0[] =
+ {
+ /* offset fp handler pc frame offset registers */
+ { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
+ { 0x02, 0x10, 0, 0x0c, 0x010, TRUE, { {r4,0x00}, {r5,0x04}, {r6,0x08}, {lr,0x0c}, {-1,-1} }},
+ { 0x04, 0x10, 0, 0x2c, 0x030, TRUE, { {r4,0x20}, {r5,0x24}, {r6,0x28}, {lr,0x2c}, {-1,-1} }},
+ { 0x08, 0x10, 0, 0x44, 0x048, TRUE, { {r4,0x38}, {r5,0x3c}, {r6,0x40}, {lr,0x44}, {d8, 0x400000000}, {d9, 0xc00000008}, {d10, 0x1400000010}, {-1,-1} }},
+ { 0x0a, 0x10, 0, 0x44, 0x048, TRUE, { {r4,0x38}, {r5,0x3c}, {r6,0x40}, {lr,0x44}, {d8, 0x400000000}, {d9, 0xc00000008}, {d10, 0x1400000010}, {-1,-1} }},
+ { 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} }},
+ { 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} }},
+ { 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} }},
+ { 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} }},
+ { 0x1c, 0x10, 0, 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} }},
+ { 0x1e, 0x10, 0, 0x3c, 0x040, TRUE, { {r4,0x30}, {r5,0x34}, {r6,0x38}, {lr,0x3c}, {d8, 0x400000000}, {d9, 0xc00000008}, {d10, 0x1400000010}, {-1,-1} }},
+ { 0x22, 0x10, 0, 0x3c, 0x040, TRUE, { {r4,0x30}, {r5,0x34}, {r6,0x38}, {lr,0x3c}, {-1,-1} }},
+ { 0x24, 0x10, 0, 0x2c, 0x030, TRUE, { {r4,0x20}, {r5,0x24}, {r6,0x28}, {lr,0x2c}, {-1,-1} }},
+ { 0x26, 0x10, 0, 0x0c, 0x010, TRUE, { {r4,0x00}, {r5,0x04}, {r6,0x08}, {lr,0x0c}, {-1,-1} }},
+ };
+
+ static const BYTE function_1[] =
+ {
+ 0x30, 0xb4, /* 00: push {r4-r5} */
+ 0x4d, 0xf8, 0x20, 0xed, /* 02: str lr, [sp, #-32]! */
+ 0x00, 0xbf, /* 06: nop */
+ 0x5d, 0xf8, 0x20, 0xeb, /* 08: ldr lr, [sp], #32 */
+ 0x30, 0xbc, /* 0c: pop {r4-r5} */
+ 0x70, 0x47, /* 0e: bx lr */
+ };
+
+ static const DWORD unwind_info_1_header =
+ (sizeof(function_1)/2) | /* function length */
+ (0 << 20) | /* X */
+ (0 << 21) | /* E */
+ (0 << 22) | /* F */
+ (0 << 23) | /* epilog */
+ (0 << 28); /* codes */
+ static const DWORD unwind_info_1_header2 =
+ (1 << 0) | /* epilog */
+ (2 << 16); /* codes, (sizeof(unwind_info_1)-headers+3)/4 */
+ static const DWORD unwind_info_1_epilog0 =
+ (4 << 0) | /* offset = 0x08 / 2 = 4 */
+ (0xE << 20) | /* condition, 0xE = always */
+ (4 << 24); /* index, byte offset to epilog opcodes */
+
+ static const BYTE unwind_info_1[] = {
+ DW(unwind_info_1_header),
+ DW(unwind_info_1_header2),
+ DW(unwind_info_1_epilog0),
+
+ UWOP_SAVE_LR(32), /* str lr, [sp, #-32]! */
+ UWOP_SAVE_RANGE_4_7_LR(5, 0), /* push {r4-r5} */
+ UWOP_END_NOP16,
+
+ UWOP_SAVE_LR(32), /* ldr lr, [sp], #32 */
+ UWOP_SAVE_RANGE_4_7_LR(5, 0), /* pop {r4-r5} */
+ UWOP_END_NOP16, /* bx lr */
+ };
+
+ static const struct results results_1[] =
+ {
+ /* offset fp handler pc frame offset registers */
+ { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
+ { 0x02, 0x00, 0, ORIG_LR, 0x008, TRUE, { {r4,0x00}, {r5,0x04}, {-1,-1} }},
+ { 0x06, 0x00, 0, 0x00, 0x028, TRUE, { {r4,0x20}, {r5,0x24}, {lr,0x00}, {-1,-1} }},
+ { 0x08, 0x00, 0, 0x00, 0x028, TRUE, { {r4,0x20}, {r5,0x24}, {lr,0x00}, {-1,-1} }},
+ { 0x0c, 0x00, 0, ORIG_LR, 0x008, TRUE, { {r4,0x00}, {r5,0x04}, {-1,-1} }},
+ { 0x0e, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
+ };
+
+ static const BYTE function_2[] =
+ {
+ 0x6f, 0x46, /* 00: mov r7, sp */
+ 0x80, 0xb4, /* 02: push {r7} */
+ 0x84, 0xb0, /* 04: sub sp, sp, #16 */
+ 0x00, 0xbf, /* 06: nop */
+ 0x04, 0xb0, /* 08: add sp, sp, #16 */
+ 0x80, 0xbc, /* 0a: push {r7} */
+ 0xbd, 0x46, /* 0c: mov sp, r7 */
+ 0x00, 0xf0, 0x00, 0xb8, /* 0e: b tailcall */
+ };
+
+ static const DWORD unwind_info_2_header =
+ (sizeof(function_2)/2) | /* function length */
+ (0 << 20) | /* X */
+ (1 << 21) | /* E */
+ (0 << 22) | /* F */
+ (0 << 23) | /* epilog */
+ (2 << 28); /* codes, (sizeof(unwind_info_2)-headers+3)/4 */
+
+ static const BYTE unwind_info_2[] =
+ {
+ DW(unwind_info_2_header),
+
+ UWOP_ALLOC_SMALL(16), /* sub sp, sp, #16 */
+ UWOP_SAVE_REGS((1<<r7)), /* push {r7} */
+ UWOP_SET_FP(7), /* mov r7, sp */
+ UWOP_END_NOP32, /* b tailcall */
+ };
+
+ static const struct results results_2[] =
+ {
+ /* offset fp handler pc frame offset registers */
+ { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
+ { 0x02, 0x00, 0, ORIG_LR, 0x55555555, FALSE, { {-1,-1} }},
+ { 0x04, 0x00, 0, ORIG_LR, 0x000, FALSE, { {r7,0x00}, {-1,-1} }},
+ { 0x06, 0x00, 0, ORIG_LR, 0x010, FALSE, { {r7,0x10}, {-1,-1} }},
+ { 0x08, 0x00, 0, ORIG_LR, 0x010, FALSE, { {r7,0x10}, {-1,-1} }},
+ { 0x0a, 0x00, 0, ORIG_LR, 0x000, FALSE, { {r7,0x00}, {-1,-1} }},
+ { 0x0c, 0x00, 0, ORIG_LR, 0x55555555, FALSE, { {-1,-1} }},
+ { 0x0e, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
+ };
+
+ static const BYTE function_3[] =
+ {
+ 0xaf, 0x3f, 0x00, 0x80, /* 00: nop.w */
+ 0x00, 0xbf, /* 04: nop */
+ 0x00, 0xbf, /* 06: nop */
+ 0x04, 0xb0, /* 08: add sp, sp, #16 */
+ 0xbd, 0xe8, 0xf0, 0x8f, /* 0a: pop.w {r4-r11,pc} */
+ };
+
+ /* Testing F=1, no prologue */
+ static const DWORD unwind_info_3_header =
+ (sizeof(function_3)/2) | /* function length */
+ (0 << 20) | /* X */
+ (1 << 21) | /* E */
+ (1 << 22) | /* F */
+ (0 << 23) | /* epilog */
+ (1 << 28); /* codes, (sizeof(unwind_info_3)-headers+3)/4 */
+
+ static const BYTE unwind_info_3[] =
+ {
+ DW(unwind_info_3_header),
+
+ UWOP_ALLOC_SMALL(16), /* sub sp, sp, #16 */
+ UWOP_SAVE_RANGE_4_11_LR(11, 1), /* pop.w {r4-r11,pc} */
+ UWOP_END,
+ };
+
+ static const struct results results_3[] =
+ {
+ /* offset fp handler pc frame offset registers */
+ { 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} }},
+ { 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} }},
+ { 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} }},
+ { 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} }},
+ { 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} }},
+ };
+
+ static const BYTE function_4[] =
+ {
+ 0x2d, 0xe9, 0x00, 0x55, /* 00: push.w {r8, r10, r12, lr} */
+ 0x50, 0xb4, /* 04: push {r4, r6} */
+ 0x00, 0xbf, /* 06: nop */
+ 0x50, 0xbc, /* 08: pop {r4, r6} */
+ 0xbd, 0xe8, 0x00, 0x95, /* 0a: pop.w {r8, r10, r12, pc} */
+ };
+
+ static const DWORD unwind_info_4_header =
+ (sizeof(function_4)/2) | /* function length */
+ (0 << 20) | /* X */
+ (1 << 21) | /* E */
+ (0 << 22) | /* F */
+ (0 << 23) | /* epilog */
+ (2 << 28); /* codes, (sizeof(unwind_info_4)-headers+3)/4 */
+
+ static const BYTE unwind_info_4[] =
+ {
+ DW(unwind_info_4_header),
+
+ UWOP_SAVE_REGS((1<<r4)|(1<<r6)), /* push {r4, r6} */
+ UWOP_SAVE_REGSW((1<<r8)|(1<<r10)|(1<<r12)|(1<<lr)), /* push.w {r8, r10, r12, lr} */
+ UWOP_END,
+ };
+
+ static const struct results results_4[] =
+ {
+ /* offset fp handler pc frame offset registers */
+ { 0x00, 0x10, 0, ORIG_LR, 0x00000, TRUE, { {-1,-1} }},
+ { 0x04, 0x10, 0, 0x0c, 0x00010, TRUE, { {r8,0x00}, {r10,0x04}, {r12,0x08}, {lr,0x0c}, {-1,-1} }},
+ { 0x06, 0x10, 0, 0x14, 0x00018, TRUE, { {r8,0x08}, {r10,0x0c}, {r12,0x10}, {lr,0x14}, {r4,0x00}, {r6,0x04}, {-1,-1} }},
+ { 0x08, 0x10, 0, 0x14, 0x00018, TRUE, { {r8,0x08}, {r10,0x0c}, {r12,0x10}, {lr,0x14}, {r4,0x00}, {r6,0x04}, {-1,-1} }},
+ { 0x0a, 0x10, 0, 0x0c, 0x00010, TRUE, { {r8,0x00}, {r10,0x04}, {r12,0x08}, {lr,0x0c}, {-1,-1} }},
+ };
+
+ static const BYTE function_5[] =
+ {
+ 0x50, 0xb5, /* 00: push {r4, r6, lr} */
+ 0xad, 0xf2, 0x08, 0x0d, /* 02: subw sp, sp, #8 */
+ 0x84, 0xb0, /* 06: sub sp, sp, #16 */
+ 0x88, 0xb0, /* 08: sub sp, sp, #32 */
+ 0xad, 0xf2, 0x40, 0x0d, /* 0a: subw sp, sp, #64 */
+ 0xad, 0xf2, 0x80, 0x0d, /* 0e: subw sp, sp, #128 */
+ 0x00, 0xbf, /* 12: nop */
+ 0x50, 0xbd, /* 14: pop {r4, r6, pc} */
+ };
+
+ static const DWORD unwind_info_5_header =
+ (sizeof(function_5)/2) | /* function length */
+ (0 << 20) | /* X */
+ (1 << 21) | /* E */
+ (0 << 22) | /* F */
+ (16 << 23) | /* epilog */
+ (5 << 28); /* codes, (sizeof(unwind_info_4)-headers+3)/4 */
+
+ static const BYTE unwind_info_5[] =
+ {
+ DW(unwind_info_5_header),
+
+ UWOP_ALLOC_HUGEW(128), /* subw sp, sp, #128 */
+ UWOP_ALLOC_LARGEW(64), /* subw sp, sp, #64 */
+ UWOP_ALLOC_HUGE(32), /* sub sp, sp, #32 */
+ UWOP_ALLOC_LARGE(16), /* sub sp, sp, #16 */
+ UWOP_ALLOC_MEDIUMW(8), /* subw sp, sp, #8 */
+ UWOP_SAVE_REGS((1<<r4)|(1<<r6)|(1<<lr)), /* push {r4, r6, lr} */
+ UWOP_END,
+ };
+
+ static const struct results results_5[] =
+ {
+ /* offset fp handler pc frame offset registers */
+ { 0x00, 0x00, 0, ORIG_LR, 0x00000, TRUE, { {-1,-1} }},
+ { 0x02, 0x00, 0, 0x008, 0x0000c, TRUE, { {r4,0x00}, {r6,0x04}, {lr,0x08}, {-1,-1} }},
+ { 0x06, 0x00, 0, 0x010, 0x00014, TRUE, { {r4,0x08}, {r6,0x0c}, {lr,0x10}, {-1,-1} }},
+ { 0x08, 0x00, 0, 0x020, 0x00024, TRUE, { {r4,0x18}, {r6,0x1c}, {lr,0x20}, {-1,-1} }},
+ { 0x0a, 0x00, 0, 0x040, 0x00044, TRUE, { {r4,0x38}, {r6,0x3c}, {lr,0x40}, {-1,-1} }},
+ { 0x0e, 0x00, 0, 0x080, 0x00084, TRUE, { {r4,0x78}, {r6,0x7c}, {lr,0x80}, {-1,-1} }},
+ { 0x12, 0x00, 0, 0x100, 0x00104, TRUE, { {r4,0xf8}, {r6,0xfc}, {lr,0x100}, {-1,-1} }},
+ { 0x14, 0x00, 0, 0x008, 0x0000c, TRUE, { {r4,0x00}, {r6,0x04}, {lr,0x08}, {-1,-1} }},
+ };
+
+ static const BYTE function_6[] =
+ {
+ 0x00, 0xbf, /* 00: nop */
+ 0x00, 0xbf, /* 02: nop */
+ 0x00, 0xbf, /* 04: nop */
+ 0x70, 0x47, /* 06: bx lr */
+ };
+
+ static const DWORD unwind_info_6_packed =
+ (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
+ (sizeof(function_6)/2 << 2) | /* FunctionLength */
+ (1 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
+ (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
+ (7 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
+ (1 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
+ (0 << 20) | /* L, push LR */
+ (0 << 21) | /* C - hook up r11 */
+ (0 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
+
+ static const BYTE unwind_info_6[] = { DW(unwind_info_6_packed) };
+
+ static const struct results results_6[] =
+ {
+ /* offset fp handler pc frame offset registers */
+ { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
+ { 0x02, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
+ { 0x04, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
+ { 0x06, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
+ };
+
+ static const BYTE function_7[] =
+ {
+ 0x10, 0xb4, /* 00: push {r4} */
+ 0x00, 0xbf, /* 02: nop */
+ 0x10, 0xbc, /* 04: pop {r4} */
+ 0x70, 0x47, /* 06: bx lr */
+ };
+
+ static const DWORD unwind_info_7_packed =
+ (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
+ (sizeof(function_7)/2 << 2) | /* FunctionLength */
+ (1 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
+ (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
+ (0 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
+ (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
+ (0 << 20) | /* L, push LR */
+ (0 << 21) | /* C - hook up r11 */
+ (0 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
+
+ static const BYTE unwind_info_7[] = { DW(unwind_info_7_packed) };
+
+ static const struct results results_7[] =
+ {
+ /* offset fp handler pc frame offset registers */
+ { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
+ { 0x02, 0x00, 0, ORIG_LR, 0x004, TRUE, { {r4,0x00}, {-1,-1} }},
+ { 0x04, 0x00, 0, ORIG_LR, 0x004, TRUE, { {r4,0x00}, {-1,-1} }},
+ { 0x06, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
+ };
+
+ static const BYTE function_8[] =
+ {
+ 0x10, 0xb5, /* 00: push {r4, lr} */
+ 0x00, 0xbf, /* 02: nop */
+ 0xbd, 0xe8, 0x10, 0x40, /* 04: pop {r4, lr} */
+ 0x70, 0x47, /* 08: bx lr */
+ };
+
+ static const DWORD unwind_info_8_packed =
+ (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
+ (sizeof(function_8)/2 << 2) | /* FunctionLength */
+ (1 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
+ (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
+ (0 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
+ (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
+ (1 << 20) | /* L, push LR */
+ (0 << 21) | /* C - hook up r11 */
+ (0 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
+
+ static const BYTE unwind_info_8[] = { DW(unwind_info_8_packed) };
+
+ static const struct results results_8[] =
+ {
+ /* offset fp handler pc frame offset registers */
+ { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
+ { 0x02, 0x00, 0, 0x004, 0x008, TRUE, { {r4,0x00}, {lr,0x04}, {-1,-1} }},
+ { 0x04, 0x00, 0, 0x004, 0x008, TRUE, { {r4,0x00}, {lr,0x04}, {-1,-1} }},
+ { 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. */
+ { 0x08, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
+ };
+
+ static const BYTE function_9[] =
+ {
+ 0x00, 0xb5, /* 00: push {lr} */
+ 0x2d, 0xed, 0x02, 0x8b, /* 02: vpush {d8} */
+ 0x88, 0xb0, /* 06: sub sp, sp, #32 */
+ 0x00, 0xbf, /* 08: nop */
+ 0x08, 0xb0, /* 0a: add sp, sp, #32 */
+ 0xbd, 0xec, 0x02, 0x8b, /* 0c: vpop {d8} */
+ 0x5d, 0xf8, 0x04, 0xeb, /* 10: ldr lr, [sp], #4 */
+ 0x00, 0xf0, 0x00, 0xb8, /* 14: b tailcall */
+ };
+
+ static const DWORD unwind_info_9_packed =
+ (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
+ (sizeof(function_9)/2 << 2) | /* FunctionLength */
+ (2 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
+ (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
+ (0 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
+ (1 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
+ (1 << 20) | /* L, push LR */
+ (0 << 21) | /* C - hook up r11 */
+ (8 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
+
+ static const BYTE unwind_info_9[] = { DW(unwind_info_9_packed) };
+
+ static const struct results results_9[] =
+ {
+ /* offset fp handler pc frame offset registers */
+ { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
+ { 0x02, 0x00, 0, 0x00, 0x004, TRUE, { {lr,0x00}, {-1,-1} }},
+ { 0x06, 0x00, 0, 0x08, 0x00c, TRUE, { {lr,0x08}, {d8,0x400000000}, {-1,-1} }},
+ { 0x08, 0x00, 0, 0x28, 0x02c, TRUE, { {lr,0x28}, {d8,0x2400000020}, {-1,-1} }},
+ { 0x0a, 0x00, 0, 0x28, 0x02c, TRUE, { {lr,0x28}, {d8,0x2400000020}, {-1,-1} }},
+#if 0
+ /* L=1, R=1, Ret>0 seems to get incorrect handling of the epilogue */
+ { 0x0c, 0x00, 0, ORIG_LR, 0x008, TRUE, { {d8,0x400000000}, {-1,-1} }},
+ { 0x10, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
+#endif
+ { 0x14, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
+ };
+
+ static const BYTE function_10[] =
+ {
+ 0x2d, 0xe9, 0x00, 0x48, /* 00: push.w {r11, lr} */
+ 0xeb, 0x46, /* 04: mov r11, sp */
+ 0x2d, 0xed, 0x04, 0x8b, /* 06: vpush {d8-d9} */
+ 0x84, 0xb0, /* 0a: sub sp, sp, #16 */
+ 0x00, 0xbf, /* 0c: nop */
+ 0x04, 0xb0, /* 0e: add sp, sp, #16 */
+ 0xbd, 0xec, 0x04, 0x8b, /* 10: vpop {d8-d9} */
+ 0xbd, 0xe8, 0x00, 0x48, /* 14: pop.w {r11, lr} */
+ 0x70, 0x47, /* 18: bx lr */
+ };
+
+ static const DWORD unwind_info_10_packed =
+ (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
+ (sizeof(function_10)/2 << 2) | /* FunctionLength */
+ (1 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
+ (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
+ (1 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
+ (1 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
+ (1 << 20) | /* L, push LR */
+ (1 << 21) | /* C - hook up r11 */
+ (4 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
+
+ static const BYTE unwind_info_10[] = { DW(unwind_info_10_packed) };
+
+ static const struct results results_10[] =
+ {
+ /* offset fp handler pc frame offset registers */
+ { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
+ { 0x04, 0x00, 0, 0x04, 0x008, TRUE, { {r11,0x00}, {lr,0x04}, {-1,-1} }},
+ { 0x06, 0x00, 0, 0x04, 0x008, TRUE, { {r11,0x00}, {lr,0x04}, {-1,-1} }},
+ { 0x0a, 0x00, 0, 0x14, 0x018, TRUE, { {r11,0x10}, {lr,0x14}, {d8,0x400000000}, {d9,0xc00000008}, {-1,-1} }},
+ { 0x0c, 0x00, 0, 0x24, 0x028, TRUE, { {r11,0x20}, {lr,0x24}, {d8,0x1400000010}, {d9,0x1c00000018}, {-1,-1} }},
+ { 0x0e, 0x00, 0, 0x24, 0x028, TRUE, { {r11,0x20}, {lr,0x24}, {d8,0x1400000010}, {d9,0x1c00000018}, {-1,-1} }},
+#if 0
+ /* L=1, R=1, Ret>0 seems to get incorrect handling of the epilogue */
+ { 0x10, 0x00, 0, 0x14, 0x018, TRUE, { {r11,0x10}, {lr,0x14}, {d8,0x400000000}, {d9,0xc00000008}, {-1,-1} }},
+ { 0x14, 0x00, 0, 0x04, 0x008, TRUE, { {r11,0x00}, {lr,0x04}, {-1,-1} }},
+#endif
+ { 0x18, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
+ };
+
+ static const BYTE function_11[] =
+ {
+ 0x2d, 0xe9, 0x00, 0x48, /* 00: push.w {r11, lr} */
+ 0xeb, 0x46, /* 04: mov r11, sp */
+ 0x2d, 0xed, 0x04, 0x8b, /* 06: vpush {d8-d9} */
+ 0x84, 0xb0, /* 0a: sub sp, sp, #16 */
+ 0x00, 0xbf, /* 0c: nop */
+ 0x04, 0xb0, /* 0e: add sp, sp, #16 */
+ 0xbd, 0xec, 0x04, 0x8b, /* 10: vpop {d8-d9} */
+ 0xbd, 0xe8, 0x00, 0x88, /* 14: pop.w {r11, pc} */
+ };
+
+ static const DWORD unwind_info_11_packed =
+ (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
+ (sizeof(function_11)/2 << 2) | /* FunctionLength */
+ (0 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
+ (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
+ (1 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
+ (1 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
+ (1 << 20) | /* L, push LR */
+ (1 << 21) | /* C - hook up r11 */
+ (4 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
+
+ static const BYTE unwind_info_11[] = { DW(unwind_info_11_packed) };
+
+ static const struct results results_11[] =
+ {
+ /* offset fp handler pc frame offset registers */
+ { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
+ { 0x04, 0x00, 0, 0x04, 0x008, TRUE, { {r11,0x00}, {lr,0x04}, {-1,-1} }},
+ { 0x06, 0x00, 0, 0x04, 0x008, TRUE, { {r11,0x00}, {lr,0x04}, {-1,-1} }},
+ { 0x0a, 0x00, 0, 0x14, 0x018, TRUE, { {r11,0x10}, {lr,0x14}, {d8,0x400000000}, {d9,0xc00000008}, {-1,-1} }},
+ { 0x0c, 0x00, 0, 0x24, 0x028, TRUE, { {r11,0x20}, {lr,0x24}, {d8,0x1400000010}, {d9,0x1c00000018}, {-1,-1} }},
+ { 0x0e, 0x00, 0, 0x24, 0x028, TRUE, { {r11,0x20}, {lr,0x24}, {d8,0x1400000010}, {d9,0x1c00000018}, {-1,-1} }},
+ { 0x10, 0x00, 0, 0x14, 0x018, TRUE, { {r11,0x10}, {lr,0x14}, {d8,0x400000000}, {d9,0xc00000008}, {-1,-1} }},
+ { 0x14, 0x00, 0, 0x04, 0x008, TRUE, { {r11,0x00}, {lr,0x04}, {-1,-1} }},
+ };
+
+ static const BYTE function_12[] =
+ {
+ 0x2d, 0xed, 0x0e, 0x8b, /* 00: vpush {d8-d14} */
+ 0x84, 0xb0, /* 04: sub sp, sp, #16 */
+ 0x00, 0xbf, /* 06: nop */
+ 0x04, 0xb0, /* 08: add sp, sp, #16 */
+ 0xbd, 0xec, 0x0e, 0x8b, /* 0a: vpop {d8-d14} */
+ 0x00, 0xf0, 0x00, 0xb8, /* 0e: b tailcall */
+ };
+
+ static const DWORD unwind_info_12_packed =
+ (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
+ (sizeof(function_12)/2 << 2) | /* FunctionLength */
+ (2 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
+ (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
+ (6 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
+ (1 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
+ (0 << 20) | /* L, push LR */
+ (0 << 21) | /* C - hook up r11 */
+ (4 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
+
+ static const BYTE unwind_info_12[] = { DW(unwind_info_12_packed) };
+
+ static const struct results results_12[] =
+ {
+ /* offset fp handler pc frame offset registers */
+ { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
+ { 0x04, 0x00, 0, ORIG_LR, 0x038, TRUE, { {d8,0x400000000}, {d9,0xc00000008}, {d10,0x1400000010}, {d11,0x1c00000018}, {d12,0x2400000020}, {d13,0x2c00000028}, {d14,0x3400000030}, {-1,-1} }},
+ { 0x06, 0x00, 0, ORIG_LR, 0x048, TRUE, { {d8,0x1400000010}, {d9,0x1c00000018}, {d10,0x2400000020}, {d11,0x2c00000028}, {d12,0x3400000030}, {d13,0x3c00000038}, {d14,0x4400000040}, {-1,-1} }},
+ { 0x08, 0x00, 0, ORIG_LR, 0x048, TRUE, { {d8,0x1400000010}, {d9,0x1c00000018}, {d10,0x2400000020}, {d11,0x2c00000028}, {d12,0x3400000030}, {d13,0x3c00000038}, {d14,0x4400000040}, {-1,-1} }},
+ { 0x0a, 0x00, 0, ORIG_LR, 0x038, TRUE, { {d8,0x400000000}, {d9,0xc00000008}, {d10,0x1400000010}, {d11,0x1c00000018}, {d12,0x2400000020}, {d13,0x2c00000028}, {d14,0x3400000030}, {-1,-1} }},
+ { 0x0e, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
+ };
+
+ static const BYTE function_13[] =
+ {
+ 0x2d, 0xe9, 0xf0, 0x4f, /* 00: push.w {r4-r11, lr} */
+ 0x0d, 0xf1, 0x1c, 0x0b, /* 04: add.w r11, sp, #28 */
+ 0x85, 0xb0, /* 08: sub sp, sp, #20 */
+ 0x00, 0xbf, /* 0a: nop */
+ 0x05, 0xb0, /* 0c: add sp, sp, #20 */
+ 0x2d, 0xe8, 0xf0, 0x8f, /* 0e: pop.w {r4-r11, lr} */
+ };
+
+ static const DWORD unwind_info_13_packed =
+ (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
+ (sizeof(function_13)/2 << 2) | /* FunctionLength */
+ (0 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
+ (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
+ (6 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
+ (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
+ (1 << 20) | /* L, push LR */
+ (1 << 21) | /* C - hook up r11 */
+ (5 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
+
+ static const BYTE unwind_info_13[] = { DW(unwind_info_13_packed) };
+
+ static const struct results results_13[] =
+ {
+ /* offset fp handler pc frame offset registers */
+ { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
+ { 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} }},
+ { 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} }},
+ { 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} }},
+ { 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} }},
+ { 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} }},
+ };
+
+ static const BYTE function_14[] =
+ {
+ 0x2d, 0xe9, 0xf0, 0x4f, /* 00: push.w {r4-r11, lr} */
+ 0x85, 0xb0, /* 04: sub sp, sp, #20 */
+ 0x00, 0xbf, /* 06: nop */
+ 0x05, 0xb0, /* 08: add sp, sp, #20 */
+ 0x2d, 0xe8, 0xf0, 0x8f, /* 0a: pop.w {r4-r11, lr} */
+ };
+
+ static const DWORD unwind_info_14_packed =
+ (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
+ (sizeof(function_14)/2 << 2) | /* FunctionLength */
+ (0 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
+ (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
+ (7 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
+ (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
+ (1 << 20) | /* L, push LR */
+ (0 << 21) | /* C - hook up r11 */
+ (5 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
+
+ static const BYTE unwind_info_14[] = { DW(unwind_info_14_packed) };
+
+ static const struct results results_14[] =
+ {
+ /* offset fp handler pc frame offset registers */
+ { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
+ { 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} }},
+ { 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} }},
+ { 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} }},
+ { 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} }},
+ };
+
+ static const BYTE function_15[] =
+ {
+ 0x0f, 0xb4, /* 00: push {r0-r3} */
+ 0x10, 0xb5, /* 02: push {r4,lr} */
+ 0xad, 0xf5, 0x00, 0x7d, /* 04: sub sp, sp, #512 */
+ 0x00, 0xbf, /* 08: nop */
+ 0x0d, 0xf5, 0x00, 0x7d, /* 0a: add sp, sp, #512 */
+ 0x10, 0xb5, /* 0e: pop {r4} */
+ 0x5d, 0xf8, 0x14, 0xfb, /* 10: ldr pc, [sp], #20 */
+ };
+
+ static const DWORD unwind_info_15_packed =
+ (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
+ (sizeof(function_15)/2 << 2) | /* FunctionLength */
+ (0 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
+ (1 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
+ (0 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
+ (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
+ (1 << 20) | /* L, push LR */
+ (0 << 21) | /* C - hook up r11 */
+ (128 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
+
+ static const BYTE unwind_info_15[] = { DW(unwind_info_15_packed) };
+
+ static const struct results results_15[] =
+ {
+ /* offset fp handler pc frame offset registers */
+ { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
+ { 0x02, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }},
+ { 0x04, 0x10, 0, 0x04, 0x018, TRUE, { {r4,0x00}, {lr,0x04}, {-1,-1} }},
+ { 0x08, 0x10, 0, 0x204, 0x218, TRUE, { {r4,0x200}, {lr,0x204}, {-1,-1} }},
+ { 0x0a, 0x10, 0, 0x204, 0x218, TRUE, { {r4,0x200}, {lr,0x204}, {-1,-1} }},
+ { 0x0e, 0x10, 0, 0x04, 0x018, TRUE, { {r4,0x00}, {lr,0x04}, {-1,-1} }},
+ { 0x10, 0x10, 0, 0x00, 0x014, TRUE, { {lr,0x00}, {-1,-1} }},
+ };
+
+ static const BYTE function_16[] =
+ {
+ 0x0f, 0xb4, /* 00: push {r0-r3} */
+ 0x2d, 0xe9, 0x00, 0x48, /* 02: push.w {r11,lr} */
+ 0xeb, 0x46, /* 06: mov r11, sp */
+ 0x00, 0xbf, /* 08: nop */
+ 0xbd, 0xe8, 0x10, 0x40, /* 0a: pop.w {r11,lr} */
+ 0x04, 0xb0, /* 0e: add sp, sp, #16 */
+ 0x00, 0xf0, 0x00, 0xb8, /* 10: b tailcall */
+ };
+
+ static const DWORD unwind_info_16_packed =
+ (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
+ (sizeof(function_16)/2 << 2) | /* FunctionLength */
+ (2 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
+ (1 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
+ (7 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
+ (1 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
+ (1 << 20) | /* L, push LR */
+ (1 << 21) | /* C - hook up r11 */
+ (0 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
+
+ static const BYTE unwind_info_16[] = { DW(unwind_info_16_packed) };
+
+ static const struct results results_16[] =
+ {
+ /* offset fp handler pc frame offset registers */
+ { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
+ { 0x02, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }},
+ { 0x06, 0x10, 0, 0x04, 0x018, TRUE, { {r11,0x00}, {lr,0x04}, {-1,-1} }},
+ { 0x08, 0x10, 0, 0x04, 0x018, TRUE, { {r11,0x00}, {lr,0x04}, {-1,-1} }},
+ { 0x0a, 0x10, 0, 0x04, 0x018, TRUE, { {r11,0x00}, {lr,0x04}, {-1,-1} }},
+ { 0x0e, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }},
+ { 0x10, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
+ };
+
+ static const BYTE function_17[] =
+ {
+ 0x0f, 0xb4, /* 00: push {r0-r3} */
+ 0x10, 0xb4, /* 02: push {r4} */
+ 0xad, 0xf5, 0x00, 0x7d, /* 04: sub sp, sp, #512 */
+ 0x00, 0xbf, /* 08: nop */
+ 0x0d, 0xf5, 0x00, 0x7d, /* 0a: add sp, sp, #512 */
+ 0x10, 0xbc, /* 0e: pop {r4} */
+ 0x04, 0xb0, /* 10: add sp, sp, #16 */
+ 0x70, 0x47, /* 12: bx lr */
+ };
+
+ static const DWORD unwind_info_17_packed =
+ (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
+ (sizeof(function_17)/2 << 2) | /* FunctionLength */
+ (1 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
+ (1 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
+ (0 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
+ (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
+ (0 << 20) | /* L, push LR */
+ (0 << 21) | /* C - hook up r11 */
+ (128 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
+
+ static const BYTE unwind_info_17[] = { DW(unwind_info_17_packed) };
+
+ static const struct results results_17[] =
+ {
+ /* offset fp handler pc frame offset registers */
+ { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
+ { 0x02, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }},
+ { 0x04, 0x10, 0, ORIG_LR, 0x014, TRUE, { {r4,0x00}, {-1,-1} }},
+ { 0x08, 0x10, 0, ORIG_LR, 0x214, TRUE, { {r4,0x200}, {-1,-1} }},
+ { 0x0a, 0x10, 0, ORIG_LR, 0x214, TRUE, { {r4,0x200}, {-1,-1} }},
+ { 0x0e, 0x10, 0, ORIG_LR, 0x014, TRUE, { {r4,0x00}, {-1,-1} }},
+ { 0x10, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }},
+ { 0x12, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
+ };
+
+ static const BYTE function_18[] =
+ {
+ 0x08, 0xb5, /* 00: push {r3,lr} */
+ 0x00, 0xbf, /* 02: nop */
+ 0x08, 0xbd, /* 04: pop {r3,pc} */
+ };
+
+ static const DWORD unwind_info_18_packed =
+ (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
+ (sizeof(function_18)/2 << 2) | /* FunctionLength */
+ (0 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
+ (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
+ (7 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
+ (1 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
+ (1 << 20) | /* L, push LR */
+ (0 << 21) | /* C - hook up r11 */
+ (0x3fc << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
+
+ static const BYTE unwind_info_18[] = { DW(unwind_info_18_packed) };
+
+ static const struct results results_18[] =
+ {
+ /* offset fp handler pc frame offset registers */
+ { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
+ { 0x02, 0x10, 0, 0x04, 0x008, TRUE, { {lr,0x04}, {-1,-1} }},
+ { 0x04, 0x10, 0, 0x04, 0x008, TRUE, { {lr,0x04}, {-1,-1} }},
+ };
+
+ static const BYTE function_19[] =
+ {
+ 0x0f, 0xb4, /* 00: push {r0-r3} */
+ 0x14, 0xb4, /* 02: push {r0-r4} */
+ 0x00, 0xbf, /* 04: nop */
+ 0x1f, 0xbc, /* 06: pop {r0-r4} */
+ 0x04, 0xb0, /* 08: add sp, sp, #16 */
+ 0x70, 0x47, /* 0a: bx lr */
+ };
+
+ static const DWORD unwind_info_19_packed =
+ (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
+ (sizeof(function_19)/2 << 2) | /* FunctionLength */
+ (1 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
+ (1 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
+ (0 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
+ (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
+ (0 << 20) | /* L, push LR */
+ (0 << 21) | /* C - hook up r11 */
+ (0x3ff << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
+
+ static const BYTE unwind_info_19[] = { DW(unwind_info_19_packed) };
+
+ static const struct results results_19[] =
+ {
+ /* offset fp handler pc frame offset registers */
+ { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
+ { 0x02, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }},
+ { 0x04, 0x10, 0, ORIG_LR, 0x024, TRUE, { {r4,0x10}, {-1,-1} }},
+ { 0x06, 0x10, 0, ORIG_LR, 0x024, TRUE, { {r4,0x10}, {-1,-1} }},
+ { 0x08, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }},
+ { 0x0a, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
+ };
+
+ static const BYTE function_20[] =
+ {
+ 0x0f, 0xb4, /* 00: push {r0-r3} */
+ 0x14, 0xb4, /* 02: push {r0-r4} */
+ 0x00, 0xbf, /* 04: nop */
+ 0x04, 0xb0, /* 06: add sp, sp, #16 */
+ 0x10, 0xbc, /* 08: pop {r4} */
+ 0x04, 0xb0, /* 0a: add sp, sp, #16 */
+ 0x70, 0x47, /* 0c: bx lr */
+ };
+
+ static const DWORD unwind_info_20_packed =
+ (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
+ (sizeof(function_20)/2 << 2) | /* FunctionLength */
+ (1 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
+ (1 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
+ (0 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
+ (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
+ (0 << 20) | /* L, push LR */
+ (0 << 21) | /* C - hook up r11 */
+ (0x3f7 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
+
+ static const BYTE unwind_info_20[] = { DW(unwind_info_20_packed) };
+
+ static const struct results results_20[] =
+ {
+ /* offset fp handler pc frame offset registers */
+ { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
+ { 0x02, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }},
+ { 0x04, 0x10, 0, ORIG_LR, 0x024, TRUE, { {r4,0x10}, {-1,-1} }},
+ { 0x06, 0x10, 0, ORIG_LR, 0x024, TRUE, { {r4,0x10}, {-1,-1} }},
+ { 0x08, 0x10, 0, ORIG_LR, 0x014, TRUE, { {r4,0x00}, {-1,-1} }},
+ { 0x0a, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }},
+ { 0x0c, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
+ };
+
+ static const BYTE function_21[] =
+ {
+ 0x0f, 0xb4, /* 00: push {r0-r3} */
+ 0x10, 0xb4, /* 02: push {r4} */
+ 0x84, 0xb0, /* 04: sub sp, sp, #16 */
+ 0x00, 0xbf, /* 06: nop */
+ 0x1f, 0xbc, /* 08: pop {r0-r4} */
+ 0x04, 0xb0, /* 0a: add sp, sp, #16 */
+ 0x70, 0x47, /* 0c: bx lr */
+ };
+
+ static const DWORD unwind_info_21_packed =
+ (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
+ (sizeof(function_21)/2 << 2) | /* FunctionLength */
+ (1 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
+ (1 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
+ (0 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
+ (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
+ (0 << 20) | /* L, push LR */
+ (0 << 21) | /* C - hook up r11 */
+ (0x3fb << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
+
+ static const BYTE unwind_info_21[] = { DW(unwind_info_21_packed) };
+
+ static const struct results results_21[] =
+ {
+ /* offset fp handler pc frame offset registers */
+ { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
+ { 0x02, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }},
+ { 0x04, 0x10, 0, ORIG_LR, 0x014, TRUE, { {r4,0x00}, {-1,-1} }},
+ { 0x06, 0x10, 0, ORIG_LR, 0x024, TRUE, { {r4,0x10}, {-1,-1} }},
+ { 0x08, 0x10, 0, ORIG_LR, 0x024, TRUE, { {r4,0x10}, {-1,-1} }},
+ { 0x0a, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }},
+ { 0x0c, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
+ };
+
+ static const BYTE function_22[] =
+ {
+ 0x00, 0xbf, /* 00: nop */
+ 0x00, 0xbf, /* 02: nop */
+ 0x0d, 0xf5, 0x00, 0x7d, /* 04: add sp, sp, #512 */
+ 0x10, 0xb5, /* 08: pop {r4} */
+ 0x5d, 0xf8, 0x14, 0xfb, /* 0a: ldr pc, [sp], #20 */
+ };
+
+ static const DWORD unwind_info_22_packed =
+ (2 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
+ (sizeof(function_22)/2 << 2) | /* FunctionLength */
+ (0 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
+ (1 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
+ (0 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
+ (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
+ (1 << 20) | /* L, push LR */
+ (0 << 21) | /* C - hook up r11 */
+ (128 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
+
+ static const BYTE unwind_info_22[] = { DW(unwind_info_22_packed) };
+
+ static const struct results results_22[] =
+ {
+ /* offset fp handler pc frame offset registers */
+ { 0x00, 0x10, 0, 0x204, 0x218, TRUE, { {r4,0x200}, {lr,0x204}, {-1,-1} }},
+ { 0x02, 0x10, 0, 0x204, 0x218, TRUE, { {r4,0x200}, {lr,0x204}, {-1,-1} }},
+ { 0x04, 0x10, 0, 0x204, 0x218, TRUE, { {r4,0x200}, {lr,0x204}, {-1,-1} }},
+ { 0x08, 0x10, 0, 0x04, 0x018, TRUE, { {r4,0x00}, {lr,0x04}, {-1,-1} }},
+ { 0x0a, 0x10, 0, 0x00, 0x014, TRUE, { {lr,0x00}, {-1,-1} }},
+ };
+
+ static const BYTE function_23[] =
+ {
+ 0x0f, 0xb4, /* 00: push {r0-r3} */
+ 0x10, 0xb5, /* 02: push {r4,lr} */
+ 0xad, 0xf5, 0x00, 0x7d, /* 04: sub sp, sp, #512 */
+ 0x00, 0xbf, /* 08: nop */
+ 0x00, 0xbf, /* 0a: nop */
+ };
+
+ static const DWORD unwind_info_23_packed =
+ (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
+ (sizeof(function_23)/2 << 2) | /* FunctionLength */
+ (3 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
+ (1 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
+ (0 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
+ (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
+ (1 << 20) | /* L, push LR */
+ (0 << 21) | /* C - hook up r11 */
+ (128 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
+
+ static const BYTE unwind_info_23[] = { DW(unwind_info_23_packed) };
+
+ static const struct results results_23[] =
+ {
+ /* offset fp handler pc frame offset registers */
+ { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
+ { 0x02, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }},
+ { 0x04, 0x10, 0, 0x04, 0x018, TRUE, { {r4,0x00}, {lr,0x04}, {-1,-1} }},
+ { 0x08, 0x10, 0, 0x204, 0x218, TRUE, { {r4,0x200}, {lr,0x204}, {-1,-1} }},
+ { 0x0a, 0x10, 0, 0x204, 0x218, TRUE, { {r4,0x200}, {lr,0x204}, {-1,-1} }},
+ };
+
+ static const BYTE function_24[] =
+ {
+ 0x2d, 0xe9, 0xfc, 0x48, /* 00: push.w {r2-r7,r11,lr} */
+ 0x0d, 0xf1, 0x18, 0x0b, /* 04: add r11, sp, #24 */
+ 0x00, 0xbf, /* 08: nop */
+ 0x02, 0xb0, /* 0a: add sp, sp, #8 */
+ 0xbd, 0xe8, 0x10, 0x48, /* 0c: pop.w {r4-r7,r11,pc} */
+ };
+
+ static const DWORD unwind_info_24_packed =
+ (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
+ (sizeof(function_24)/2 << 2) | /* FunctionLength */
+ (0 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
+ (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
+ (3 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
+ (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
+ (1 << 20) | /* L, push LR */
+ (1 << 21) | /* C - hook up r11 */
+ (0x3f5 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
+
+ static const BYTE unwind_info_24[] = { DW(unwind_info_24_packed) };
+
+ static const struct results results_24[] =
+ {
+ /* offset fp handler pc frame offset registers */
+ { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
+ { 0x04, 0x10, 0, 0x1c, 0x020, TRUE, { {r4,0x08}, {r5,0x0c}, {r6,0x10}, {r7,0x14}, {r11,0x18}, {lr,0x1c}, {-1,-1} }},
+ { 0x08, 0x10, 0, 0x1c, 0x020, TRUE, { {r4,0x08}, {r5,0x0c}, {r6,0x10}, {r7,0x14}, {r11,0x18}, {lr,0x1c}, {-1,-1} }},
+ { 0x0a, 0x10, 0, 0x1c, 0x020, TRUE, { {r4,0x08}, {r5,0x0c}, {r6,0x10}, {r7,0x14}, {r11,0x18}, {lr,0x1c}, {-1,-1} }},
+ { 0x0c, 0x10, 0, 0x14, 0x018, TRUE, { {r4,0x00}, {r5,0x04}, {r6,0x08}, {r7,0x0c}, {r11,0x10}, {lr,0x14}, {-1,-1} }},
+ };
+
+ static const BYTE function_25[] =
+ {
+ 0x2d, 0xe9, 0xf0, 0x48, /* 00: push.w {r4-r7,r11,lr} */
+ 0x0d, 0xf1, 0x10, 0x0b, /* 04: add r11, sp, #16 */
+ 0x82, 0xb0, /* 08: sub sp, sp, #8 */
+ 0x00, 0xbf, /* 0a: nop */
+ 0xbd, 0xe8, 0xfc, 0x48, /* 0c: pop.w {r2-r7,r11,pc} */
+ };
+
+ static const DWORD unwind_info_25_packed =
+ (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
+ (sizeof(function_25)/2 << 2) | /* FunctionLength */
+ (0 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
+ (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
+ (3 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
+ (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
+ (1 << 20) | /* L, push LR */
+ (1 << 21) | /* C - hook up r11 */
+ (0x3f9 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
+
+ static const BYTE unwind_info_25[] = { DW(unwind_info_25_packed) };
+
+ static const struct results results_25[] =
+ {
+ /* offset fp handler pc frame offset registers */
+ { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
+ { 0x04, 0x10, 0, 0x14, 0x018, TRUE, { {r4,0x00}, {r5,0x04}, {r6,0x08}, {r7,0x0c}, {r11,0x10}, {lr,0x14}, {-1,-1} }},
+ { 0x08, 0x10, 0, 0x14, 0x018, TRUE, { {r4,0x00}, {r5,0x04}, {r6,0x08}, {r7,0x0c}, {r11,0x10}, {lr,0x14}, {-1,-1} }},
+ { 0x0a, 0x10, 0, 0x1c, 0x020, TRUE, { {r4,0x08}, {r5,0x0c}, {r6,0x10}, {r7,0x14}, {r11,0x18}, {lr,0x1c}, {-1,-1} }},
+ { 0x0c, 0x10, 0, 0x1c, 0x020, TRUE, { {r4,0x08}, {r5,0x0c}, {r6,0x10}, {r7,0x14}, {r11,0x18}, {lr,0x1c}, {-1,-1} }},
+ };
+
+ static const BYTE function_26[] =
+ {
+ 0x2d, 0xe9, 0x10, 0x08, /* 00: push.w {r4, r11} */
+ 0x0d, 0xf1, 0x1c, 0x0b, /* 04: add.w r11, sp, #28 */
+ 0x84, 0xb0, /* 08: sub sp, sp, #16 */
+ 0x00, 0xbf, /* 0a: nop */
+ 0x04, 0xb0, /* 0c: add sp, sp, #16 */
+ 0xbd, 0xe8, 0x10, 0x08, /* 0e: pop.w {r4, r11} */
+ 0x70, 0x47, /* 12: bx lr */
+ };
+
+ /* C=1, L=0 is disallowed by doc */
+ static const DWORD unwind_info_26_packed =
+ (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
+ (sizeof(function_26)/2 << 2) | /* FunctionLength */
+ (1 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
+ (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
+ (0 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
+ (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
+ (0 << 20) | /* L, push LR */
+ (1 << 21) | /* C - hook up r11 */
+ (4 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
+
+ static const BYTE unwind_info_26[] = { DW(unwind_info_26_packed) };
+
+ static const struct results results_26[] =
+ {
+ /* offset fp handler pc frame offset registers */
+ { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
+ { 0x04, 0x10, 0, ORIG_LR, 0x008, TRUE, { {r4,0x00}, {r11,0x04}, {-1,-1} }},
+ { 0x08, 0x10, 0, ORIG_LR, 0x008, TRUE, { {r4,0x00}, {r11,0x04}, {-1,-1} }},
+ { 0x0a, 0x10, 0, ORIG_LR, 0x018, TRUE, { {r4,0x10}, {r11,0x14}, {-1,-1} }},
+ { 0x0c, 0x10, 0, ORIG_LR, 0x018, TRUE, { {r4,0x10}, {r11,0x14}, {-1,-1} }},
+ { 0x0e, 0x10, 0, ORIG_LR, 0x008, TRUE, { {r4,0x00}, {r11,0x04}, {-1,-1} }},
+ { 0x12, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
+ };
+
+ static const BYTE function_27[] =
+ {
+ 0x0e, 0xb4, /* 00: push {r1-r3} */
+ 0x00, 0xbf, /* 02: nop */
+ 0x03, 0xb0, /* 04: add sp, sp, #12 */
+ 0x70, 0x47, /* 06: bx lr */
+ };
+
+ static const DWORD unwind_info_27_packed =
+ (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
+ (sizeof(function_27)/2 << 2) | /* FunctionLength */
+ (1 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
+ (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
+ (7 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
+ (1 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
+ (0 << 20) | /* L, push LR */
+ (0 << 21) | /* C - hook up r11 */
+ (0x3f6 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
+
+ static const BYTE unwind_info_27[] = { DW(unwind_info_27_packed) };
+
+ static const struct results results_27[] =
+ {
+ /* offset fp handler pc frame offset registers */
+ { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
+ { 0x02, 0x10, 0, ORIG_LR, 0x00c, TRUE, { {-1,-1} }},
+ { 0x04, 0x10, 0, ORIG_LR, 0x00c, TRUE, { {-1,-1} }},
+ { 0x06, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
+ };
+
+ static const BYTE function_28[] =
+ {
+ 0x0e, 0xb4, /* 00: push {r1-r3} */
+ 0x00, 0xbf, /* 02: nop */
+ 0x03, 0xb0, /* 04: add sp, sp, #12 */
+ 0x70, 0x47, /* 06: bx lr */
+ };
+
+ static const DWORD unwind_info_28_packed =
+ (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */
+ (sizeof(function_28)/2 << 2) | /* FunctionLength */
+ (1 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */
+ (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */
+ (7 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */
+ (1 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */
+ (0 << 20) | /* L, push LR */
+ (0 << 21) | /* C - hook up r11 */
+ (0x3fa << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */
+
+ static const BYTE unwind_info_28[] = { DW(unwind_info_28_packed) };
+
+ static const struct results results_28[] =
+ {
+ /* offset fp handler pc frame offset registers */
+ { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
+ { 0x02, 0x10, 0, ORIG_LR, 0x00c, TRUE, { {-1,-1} }},
+ { 0x04, 0x10, 0, ORIG_LR, 0x00c, TRUE, { {-1,-1} }},
+ { 0x06, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
+ };
+
+ static const struct unwind_test tests[] =
+ {
+#define TEST(func, unwind, unwind_packed, results) \
+ { func, sizeof(func), unwind, unwind_packed ? 0 : sizeof(unwind), results, ARRAY_SIZE(results) }
+ TEST(function_0, unwind_info_0, 0, results_0),
+ TEST(function_1, unwind_info_1, 0, results_1),
+ TEST(function_2, unwind_info_2, 0, results_2),
+ TEST(function_3, unwind_info_3, 0, results_3),
+ TEST(function_4, unwind_info_4, 0, results_4),
+ TEST(function_5, unwind_info_5, 0, results_5),
+ TEST(function_6, unwind_info_6, 1, results_6),
+ TEST(function_7, unwind_info_7, 1, results_7),
+ TEST(function_8, unwind_info_8, 1, results_8),
+ TEST(function_9, unwind_info_9, 1, results_9),
+ TEST(function_10, unwind_info_10, 1, results_10),
+ TEST(function_11, unwind_info_11, 1, results_11),
+ TEST(function_12, unwind_info_12, 1, results_12),
+ TEST(function_13, unwind_info_13, 1, results_13),
+ TEST(function_14, unwind_info_14, 1, results_14),
+ TEST(function_15, unwind_info_15, 1, results_15),
+ TEST(function_16, unwind_info_16, 1, results_16),
+ TEST(function_17, unwind_info_17, 1, results_17),
+ TEST(function_18, unwind_info_18, 1, results_18),
+ TEST(function_19, unwind_info_19, 1, results_19),
+ TEST(function_20, unwind_info_20, 1, results_20),
+ TEST(function_21, unwind_info_21, 1, results_21),
+ TEST(function_22, unwind_info_22, 1, results_22),
+ TEST(function_23, unwind_info_23, 1, results_23),
+ TEST(function_24, unwind_info_24, 1, results_24),
+ TEST(function_25, unwind_info_25, 1, results_25),
+ TEST(function_26, unwind_info_26, 1, results_26),
+ TEST(function_27, unwind_info_27, 1, results_27),
+ TEST(function_28, unwind_info_28, 1, results_28),
+#undef TEST
+ };
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(tests); i++)
+ call_virtual_unwind( i, &tests[i] );
+}
+
+
static void test_thread_context(void)
{
CONTEXT context;
@@ -9316,6 +10662,10 @@ START_TEST(exception)
test_virtual_unwind();
+#elif defined(__arm__)
+
+ test_virtual_unwind();
+
#endif
test_debugger(DBG_EXCEPTION_HANDLED);
--
2.25.1
Nov. 9, 2021
[PATCH 05/18] ntdll: Implement RtlVirtualUnwind for arm.
by Martin Storsjö
Signed-off-by: Martin Storsjö <martin(a)martin.st>
---
.../api-ms-win-core-rtlsupport-l1-1-0.spec | 2 +-
.../api-ms-win-core-rtlsupport-l1-2-0.spec | 2 +-
dlls/kernel32/kernel32.spec | 2 +-
dlls/ntdll/ntdll.spec | 2 +-
dlls/ntdll/signal_arm.c | 524 ++++++++++++++++++
5 files changed, 528 insertions(+), 4 deletions(-)
diff --git a/dlls/api-ms-win-core-rtlsupport-l1-1-0/api-ms-win-core-rtlsupport-l1-1-0.spec b/dlls/api-ms-win-core-rtlsupport-l1-1-0/api-ms-win-core-rtlsupport-l1-1-0.spec
index ac166e2c541..fefac9dc400 100644
--- a/dlls/api-ms-win-core-rtlsupport-l1-1-0/api-ms-win-core-rtlsupport-l1-1-0.spec
+++ b/dlls/api-ms-win-core-rtlsupport-l1-1-0/api-ms-win-core-rtlsupport-l1-1-0.spec
@@ -12,4 +12,4 @@
@ cdecl -arch=arm64,x86_64 RtlRestoreContext(ptr ptr) ntdll.RtlRestoreContext
@ stdcall -norelay RtlUnwind(ptr ptr ptr ptr) ntdll.RtlUnwind
@ stdcall -arch=arm64,x86_64 RtlUnwindEx(ptr ptr ptr ptr ptr ptr) ntdll.RtlUnwindEx
-@ stdcall -arch=arm64,x86_64 RtlVirtualUnwind(long long long ptr ptr ptr ptr ptr) ntdll.RtlVirtualUnwind
+@ stdcall -arch=arm,arm64,x86_64 RtlVirtualUnwind(long long long ptr ptr ptr ptr ptr) ntdll.RtlVirtualUnwind
diff --git a/dlls/api-ms-win-core-rtlsupport-l1-2-0/api-ms-win-core-rtlsupport-l1-2-0.spec b/dlls/api-ms-win-core-rtlsupport-l1-2-0/api-ms-win-core-rtlsupport-l1-2-0.spec
index dd49448348d..bff03b1d2ae 100644
--- a/dlls/api-ms-win-core-rtlsupport-l1-2-0/api-ms-win-core-rtlsupport-l1-2-0.spec
+++ b/dlls/api-ms-win-core-rtlsupport-l1-2-0/api-ms-win-core-rtlsupport-l1-2-0.spec
@@ -13,4 +13,4 @@
@ cdecl -arch=arm64,x86_64 RtlRestoreContext(ptr ptr) ntdll.RtlRestoreContext
@ stdcall -norelay RtlUnwind(ptr ptr ptr ptr) ntdll.RtlUnwind
@ stdcall -arch=arm64,x86_64 RtlUnwindEx(ptr ptr ptr ptr ptr ptr) ntdll.RtlUnwindEx
-@ stdcall -arch=arm64,x86_64 RtlVirtualUnwind(long long long ptr ptr ptr ptr ptr) ntdll.RtlVirtualUnwind
+@ stdcall -arch=arm,arm64,x86_64 RtlVirtualUnwind(long long long ptr ptr ptr ptr ptr) ntdll.RtlVirtualUnwind
diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec
index 13e4df3c99f..18ec187a683 100644
--- a/dlls/kernel32/kernel32.spec
+++ b/dlls/kernel32/kernel32.spec
@@ -1314,7 +1314,7 @@
@ cdecl -arch=arm64,x86_64 RtlRestoreContext(ptr ptr) ntdll.RtlRestoreContext
@ stdcall RtlUnwind(ptr ptr ptr long) ntdll.RtlUnwind
@ stdcall -arch=arm64,x86_64 RtlUnwindEx(long long ptr long ptr) ntdll.RtlUnwindEx
-@ stdcall -arch=arm64,x86_64 RtlVirtualUnwind(long long long ptr ptr ptr ptr ptr) ntdll.RtlVirtualUnwind
+@ stdcall -arch=arm,arm64,x86_64 RtlVirtualUnwind(long long long ptr ptr ptr ptr ptr) ntdll.RtlVirtualUnwind
@ stdcall RtlZeroMemory(ptr long) ntdll.RtlZeroMemory
@ stdcall -i386 -private -norelay SMapLS() krnl386.exe16.SMapLS
@ stdcall -i386 -private -norelay SMapLS_IP_EBP_12() krnl386.exe16.SMapLS_IP_EBP_12
diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec
index 13e65f65139..070b57efb59 100644
--- a/dlls/ntdll/ntdll.spec
+++ b/dlls/ntdll/ntdll.spec
@@ -1067,7 +1067,7 @@
@ stub RtlValidateProcessHeaps
# @ stub RtlValidateUnicodeString
@ stdcall RtlVerifyVersionInfo(ptr long int64)
-@ stdcall -arch=arm64,x86_64 RtlVirtualUnwind(long long long ptr ptr ptr ptr ptr)
+@ stdcall -arch=arm,arm64,x86_64 RtlVirtualUnwind(long long long ptr ptr ptr ptr ptr)
@ stdcall RtlWaitOnAddress(ptr ptr long ptr)
@ stdcall RtlWakeAddressAll(ptr)
@ stdcall RtlWakeAddressSingle(ptr)
diff --git a/dlls/ntdll/signal_arm.c b/dlls/ntdll/signal_arm.c
index 0ef210a0331..27e27deabfe 100644
--- a/dlls/ntdll/signal_arm.c
+++ b/dlls/ntdll/signal_arm.c
@@ -217,6 +217,530 @@ void WINAPI KiUserCallbackDispatcher( ULONG id, void *args, ULONG len )
}
+/***********************************************************************
+ * Definitions for Win32 unwind tables
+ */
+
+struct unwind_info
+{
+ DWORD function_length : 18;
+ DWORD version : 2;
+ DWORD x : 1;
+ DWORD e : 1;
+ DWORD f : 1;
+ DWORD epilog : 5;
+ DWORD codes : 4;
+};
+
+struct unwind_info_ext
+{
+ WORD epilog;
+ BYTE codes;
+ BYTE reserved;
+};
+
+struct unwind_info_epilog
+{
+ DWORD offset : 18;
+ DWORD res : 2;
+ DWORD cond : 4;
+ DWORD index : 8;
+};
+
+static const BYTE unwind_code_len[256] =
+{
+/* 00 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+/* 20 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+/* 40 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+/* 60 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+/* 80 */ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+/* a0 */ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+/* c0 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+/* e0 */ 1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,4,3,4,1,1,1,1,1
+};
+
+static const BYTE unwind_instr_len[256] =
+{
+/* 00 */ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+/* 20 */ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+/* 40 */ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+/* 60 */ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+/* 80 */ 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+/* a0 */ 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+/* c0 */ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,
+/* e0 */ 4,4,4,4,4,4,4,4,4,4,4,4,2,2,2,4,0,0,0,0,0,4,4,2,2,4,4,2,4,2,4,0
+};
+
+/***********************************************************************
+ * get_sequence_len
+ */
+static unsigned int get_sequence_len( BYTE *ptr, BYTE *end, int include_end )
+{
+ unsigned int ret = 0;
+
+ while (ptr < end)
+ {
+ if (*ptr >= 0xfd)
+ {
+ if (*ptr <= 0xfe && include_end)
+ ret += unwind_instr_len[*ptr];
+ break;
+ }
+ ret += unwind_instr_len[*ptr];
+ ptr += unwind_code_len[*ptr];
+ }
+ return ret;
+}
+
+
+/***********************************************************************
+ * pop_regs_mask
+ */
+static void pop_regs_mask( int mask, CONTEXT *context,
+ KNONVOLATILE_CONTEXT_POINTERS *ptrs )
+{
+ int i;
+ for (i = 0; i <= 12; i++)
+ {
+ if (!(mask & (1 << i))) continue;
+ if (ptrs && i >= 4 && i <= 11) (&ptrs->R4)[i - 4] = (DWORD *)context->Sp;
+ if (i >= 4) (&context->R0)[i] = *(DWORD *)context->Sp;
+ context->Sp += 4;
+ }
+}
+
+
+/***********************************************************************
+ * pop_regs_range
+ */
+static void pop_regs_range( int last, CONTEXT *context,
+ KNONVOLATILE_CONTEXT_POINTERS *ptrs )
+{
+ int i;
+ for (i = 4; i <= last; i++)
+ {
+ if (ptrs) (&ptrs->R4)[i - 4] = (DWORD *)context->Sp;
+ (&context->R0)[i] = *(DWORD *)context->Sp;
+ context->Sp += 4;
+ }
+}
+
+
+/***********************************************************************
+ * pop_lr
+ */
+static void pop_lr( int increment, CONTEXT *context,
+ KNONVOLATILE_CONTEXT_POINTERS *ptrs )
+{
+ if (ptrs) ptrs->Lr = (DWORD *)context->Sp;
+ context->Lr = *(DWORD *)context->Sp;
+ context->Sp += increment;
+}
+
+
+/***********************************************************************
+ * pop_fpregs_range
+ */
+static void pop_fpregs_range( int first, int last, CONTEXT *context,
+ KNONVOLATILE_CONTEXT_POINTERS *ptrs )
+{
+ int i;
+ for (i = first; i <= last; i++)
+ {
+ if (ptrs && i >= 8 && i <= 15) (&ptrs->D8)[i - 8] = (ULONGLONG *)context->Sp;
+ context->u.D[i] = *(ULONGLONG *)context->Sp;
+ context->Sp += 8;
+ }
+}
+
+
+/***********************************************************************
+ * process_unwind_codes
+ */
+static void process_unwind_codes( BYTE *ptr, BYTE *end, CONTEXT *context,
+ KNONVOLATILE_CONTEXT_POINTERS *ptrs, int skip )
+{
+ unsigned int val, len;
+ unsigned int i;
+
+ /* skip codes */
+ while (ptr < end && skip)
+ {
+ if (*ptr >= 0xfd) break;
+ skip -= unwind_instr_len[*ptr];
+ ptr += unwind_code_len[*ptr];
+ }
+
+ while (ptr < end)
+ {
+ len = unwind_code_len[*ptr];
+ if (ptr + len > end) break;
+ val = 0;
+ for (i = 0; i < len; i++)
+ val = (val << 8) | ptr[i];
+
+ if (*ptr <= 0x7f) /* add sp, sp, #x */
+ context->Sp += 4 * (val & 0x7f);
+ else if (*ptr <= 0xbf) /* pop {r0-r12,lr} */
+ {
+ pop_regs_mask( val & 0x1fff, context, ptrs );
+ if (val & 0x2000)
+ pop_lr( 4, context, ptrs );
+ }
+ else if (*ptr <= 0xcf) /* mov sp, rX */
+ context->Sp = (&context->R0)[val & 0x0f];
+ else if (*ptr <= 0xd7) /* pop {r4-rX,lr} */
+ {
+ pop_regs_range( (val & 0x03) + 4, context, ptrs );
+ if (val & 0x04)
+ pop_lr( 4, context, ptrs );
+ }
+ else if (*ptr <= 0xdf) /* pop {r4-rX,lr} */
+ {
+ pop_regs_range( (val & 0x03) + 8, context, ptrs );
+ if (val & 0x04)
+ pop_lr( 4, context, ptrs );
+ }
+ else if (*ptr <= 0xe7) /* vpop {d8-dX} */
+ pop_fpregs_range( 8, (val & 0x07) + 8, context, ptrs );
+ else if (*ptr <= 0xeb) /* add sp, sp, #x */
+ context->Sp += 4 * (val & 0x3ff);
+ else if (*ptr <= 0xed) /* pop {r0-r12,lr} */
+ {
+ pop_regs_mask( val & 0xff, context, ptrs );
+ if (val & 0x100)
+ pop_lr( 4, context, ptrs );
+ }
+ else if (*ptr <= 0xee) /* Microsoft-specific 0x00-0x0f, Available 0x10-0xff */
+ WARN( "unsupported code %02x\n", *ptr );
+ else if (*ptr <= 0xef && ((val & 0xff) <= 0x0f)) /* ldr lr, [sp], #x */
+ pop_lr( 4 * (val & 0x0f), context, ptrs );
+ else if (*ptr <= 0xf4) /* Available */
+ WARN( "unsupported code %02x\n", *ptr );
+ else if (*ptr <= 0xf5) /* vpop {dS-dE} */
+ pop_fpregs_range( (val & 0xf0) >> 4, (val & 0x0f), context, ptrs );
+ else if (*ptr <= 0xf6) /* vpop {dS-dE} */
+ pop_fpregs_range( ((val & 0xf0) >> 4) + 16, (val & 0x0f) + 16, context, ptrs );
+ else if (*ptr == 0xf7 || *ptr == 0xf9) /* add sp, sp, #x */
+ context->Sp += 4 * (val & 0xffff);
+ else if (*ptr == 0xf8 || *ptr == 0xfa) /* add sp, sp, #x */
+ context->Sp += 4 * (val & 0xffffff);
+ else if (*ptr <= 0xfc) /* nop */
+ /* nop */ ;
+ else /* end */
+ break;
+
+ ptr += len;
+ }
+}
+
+
+/***********************************************************************
+ * unwind_packed_data
+ */
+static void *unwind_packed_data( ULONG_PTR base, ULONG_PTR pc, RUNTIME_FUNCTION *func,
+ CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *ptrs )
+{
+ int i, pos = 0;
+ int pf = 0, ef = 0, fpoffset = 0, stack = func->u.s.StackAdjust;
+ int prologue_regmask = 0;
+ int epilogue_regmask = 0;
+ unsigned int offset, len;
+ BYTE prologue[10], *prologue_end, epilogue[20], *epilogue_end;
+
+ TRACE( "function %lx-%lx: len=%#x flag=%x ret=%u H=%u reg=%u R=%u L=%u C=%u stackadjust=%x\n",
+ base + func->BeginAddress, base + func->BeginAddress + func->u.s.FunctionLength * 2,
+ func->u.s.FunctionLength, func->u.s.Flag, func->u.s.Ret,
+ func->u.s.H, func->u.s.Reg, func->u.s.R, func->u.s.L, func->u.s.C, func->u.s.StackAdjust );
+
+ offset = (pc - base) - func->BeginAddress;
+ if (func->u.s.StackAdjust >= 0x03f4)
+ {
+ pf = func->u.s.StackAdjust & 0x04;
+ ef = func->u.s.StackAdjust & 0x08;
+ stack = (func->u.s.StackAdjust & 3) + 1;
+ }
+
+ if (!func->u.s.R || pf)
+ {
+ int first = 4, last = func->u.s.Reg + 4;
+ if (pf)
+ {
+ first = (~func->u.s.StackAdjust) & 3;
+ if (func->u.s.R)
+ last = 3;
+ }
+ for (i = first; i <= last; i++)
+ prologue_regmask |= 1 << i;
+ fpoffset = last + 1 - first;
+ }
+
+ if (!func->u.s.R || ef)
+ {
+ int first = 4, last = func->u.s.Reg + 4;
+ if (ef)
+ {
+ first = (~func->u.s.StackAdjust) & 3;
+ if (func->u.s.R)
+ last = 3;
+ }
+ for (i = first; i <= last; i++)
+ epilogue_regmask |= 1 << i;
+ }
+
+ if (func->u.s.C)
+ {
+ prologue_regmask |= 1 << 11;
+ epilogue_regmask |= 1 << 11;
+ }
+
+ if (func->u.s.L)
+ {
+ prologue_regmask |= 1 << 14; /* lr */
+ if (func->u.s.Ret != 0)
+ epilogue_regmask |= 1 << 14; /* lr */
+ else if (!func->u.s.H)
+ epilogue_regmask |= 1 << 15; /* pc */
+ }
+
+ /* Synthesize prologue opcodes */
+ if (stack && !pf)
+ {
+ if (stack <= 0x7f)
+ {
+ prologue[pos++] = stack; /* sub sp, sp, #x */
+ }
+ else
+ {
+ prologue[pos++] = 0xe8 | (stack >> 8); /* sub.w sp, sp, #x */
+ prologue[pos++] = stack & 0xff;
+ }
+ }
+
+ if (func->u.s.R && func->u.s.Reg != 7)
+ prologue[pos++] = 0xe0 | func->u.s.Reg; /* vpush {d8-dX} */
+
+ if (func->u.s.C && fpoffset == 0)
+ prologue[pos++] = 0xfb; /* mov r11, sp - handled as nop16 */
+ else if (func->u.s.C)
+ prologue[pos++] = 0xfc; /* add r11, sp, #x - handled as nop32 */
+
+ if (prologue_regmask & 0xf00) /* r8-r11 set */
+ {
+ int bitmask = prologue_regmask & 0x1fff;
+ if (prologue_regmask & (1 << 14)) /* lr */
+ bitmask |= 0x2000;
+ prologue[pos++] = 0x80 | (bitmask >> 8); /* push.w {r0-r12,lr} */
+ prologue[pos++] = bitmask & 0xff;
+ }
+ else if (prologue_regmask) /* r0-r7, lr set */
+ {
+ int bitmask = prologue_regmask & 0xff;
+ if (prologue_regmask & (1 << 14)) /* lr */
+ bitmask |= 0x100;
+ prologue[pos++] = 0xec | (bitmask >> 8); /* push {r0-r7,lr} */
+ prologue[pos++] = bitmask & 0xff;
+ }
+
+ if (func->u.s.H)
+ prologue[pos++] = 0x04; /* push {r0-r3} - handled as sub sp, sp, #16 */
+
+ prologue[pos++] = 0xff; /* end */
+ prologue_end = &prologue[pos];
+
+ /* Synthesize epilogue opcodes */
+ pos = 0;
+ if (stack && !ef)
+ {
+ if (stack <= 0x7f)
+ {
+ epilogue[pos++] = stack; /* sub sp, sp, #x */
+ }
+ else
+ {
+ epilogue[pos++] = 0xe8 | (stack >> 8); /* sub.w sp, sp, #x */
+ epilogue[pos++] = stack & 0xff;
+ }
+ }
+
+ if (func->u.s.R && func->u.s.Reg != 7)
+ epilogue[pos++] = 0xe0 | func->u.s.Reg; /* vpush {d8-dX} */
+
+ if (epilogue_regmask & 0x7f00) /* r8-r11, lr set */
+ {
+ int bitmask = epilogue_regmask & 0x1fff;
+ if (epilogue_regmask & (3 << 14)) /* lr or pc */
+ bitmask |= 0x2000;
+ epilogue[pos++] = 0x80 | (bitmask >> 8); /* push.w {r0-r12,lr} */
+ epilogue[pos++] = bitmask & 0xff;
+ }
+ else if (epilogue_regmask) /* r0-r7, pc set */
+ {
+ int bitmask = epilogue_regmask & 0xff;
+ if (epilogue_regmask & (1 << 15)) /* pc */
+ bitmask |= 0x100; /* lr */
+ epilogue[pos++] = 0xec | (bitmask >> 8); /* push {r0-r7,lr} */
+ epilogue[pos++] = bitmask & 0xff;
+ }
+
+ if (func->u.s.H && !(func->u.s.L && func->u.s.Ret == 0))
+ epilogue[pos++] = 0x04; /* add sp, sp, #16 */
+ else if (func->u.s.H && (func->u.s.L && func->u.s.Ret == 0))
+ {
+ epilogue[pos++] = 0xef; /* ldr lr, [sp], #20 */
+ epilogue[pos++] = 5;
+ }
+
+ if (func->u.s.Ret == 1)
+ epilogue[pos++] = 0xfd; /* bx lr */
+ else if (func->u.s.Ret == 2)
+ epilogue[pos++] = 0xfe; /* b address */
+ else
+ epilogue[pos++] = 0xff; /* end */
+ epilogue_end = &epilogue[pos];
+
+ if (func->u.s.Flag == 1 && offset < 4 * (prologue_end - prologue)) {
+ /* Check prologue */
+ len = get_sequence_len( prologue, prologue_end, 0 );
+ if (offset < len)
+ {
+ process_unwind_codes( prologue, prologue_end, context, ptrs, len - offset );
+ return NULL;
+ }
+ }
+
+ if (func->u.s.Ret != 3 && 2 * func->u.s.FunctionLength - offset <= 4 * (epilogue_end - epilogue)) {
+ /* Check epilogue */
+ len = get_sequence_len( epilogue, epilogue_end, 1 );
+ if (offset >= 2 * func->u.s.FunctionLength - len)
+ {
+ process_unwind_codes( epilogue, epilogue_end, context, ptrs, offset - (2 * func->u.s.FunctionLength - len) );
+ return NULL;
+ }
+ }
+
+ /* Execute full prologue */
+ process_unwind_codes( prologue, prologue_end, context, ptrs, 0 );
+
+ return NULL;
+}
+
+
+/***********************************************************************
+ * unwind_full_data
+ */
+static void *unwind_full_data( ULONG_PTR base, ULONG_PTR pc, RUNTIME_FUNCTION *func,
+ CONTEXT *context, PVOID *handler_data, KNONVOLATILE_CONTEXT_POINTERS *ptrs )
+{
+ struct unwind_info *info;
+ struct unwind_info_epilog *info_epilog;
+ unsigned int i, codes, epilogs, len, offset;
+ void *data;
+ BYTE *end;
+
+ info = (struct unwind_info *)((char *)base + func->u.UnwindData);
+ data = info + 1;
+ epilogs = info->epilog;
+ codes = info->codes;
+ if (!codes && !epilogs)
+ {
+ struct unwind_info_ext *infoex = data;
+ codes = infoex->codes;
+ epilogs = infoex->epilog;
+ data = infoex + 1;
+ }
+ info_epilog = data;
+ if (!info->e) data = info_epilog + epilogs;
+
+ offset = (pc - base) - func->BeginAddress;
+ end = (BYTE *)data + codes * 4;
+
+ TRACE( "function %lx-%lx: len=%#x ver=%u X=%u E=%u F=%u epilogs=%u codes=%u\n",
+ base + func->BeginAddress, base + func->BeginAddress + info->function_length * 4,
+ info->function_length, info->version, info->x, info->e, info->f, epilogs, codes * 4 );
+
+ /* check for prolog */
+ if (offset < codes * 4 * 4 && !info->f)
+ {
+ len = get_sequence_len( data, end, 0 );
+ if (offset < len)
+ {
+ process_unwind_codes( data, end, context, ptrs, len - offset );
+ return NULL;
+ }
+ }
+
+ /* check for epilog */
+ if (!info->e)
+ {
+ for (i = 0; i < epilogs; i++)
+ {
+ /* TODO: Currently not checking epilogue conditions. */
+ if (offset < 2 * info_epilog[i].offset) break;
+ if (offset - 2 * info_epilog[i].offset < (codes * 4 - info_epilog[i].index) * 4)
+ {
+ BYTE *ptr = (BYTE *)data + info_epilog[i].index;
+ len = get_sequence_len( ptr, end, 1 );
+ if (offset <= 2 * info_epilog[i].offset + len)
+ {
+ process_unwind_codes( ptr, end, context, ptrs, offset - 2 * info_epilog[i].offset );
+ return NULL;
+ }
+ }
+ }
+ }
+ else if (2 * info->function_length - offset <= (codes * 4 - epilogs) * 4)
+ {
+ BYTE *ptr = (BYTE *)data + epilogs;
+ len = get_sequence_len( ptr, end, 1 );
+ if (offset >= 2 * info->function_length - len)
+ {
+ process_unwind_codes( ptr, end, context, ptrs, offset - (2 * info->function_length - len) );
+ return NULL;
+ }
+ }
+
+ process_unwind_codes( data, end, context, ptrs, 0 );
+
+ /* get handler since we are inside the main code */
+ if (info->x)
+ {
+ DWORD *handler_rva = (DWORD *)data + codes;
+ *handler_data = handler_rva + 1;
+ return (char *)base + *handler_rva;
+ }
+ return NULL;
+}
+
+/***********************************************************************
+ * RtlVirtualUnwind (NTDLL.@)
+ */
+PVOID WINAPI RtlVirtualUnwind( ULONG type, ULONG_PTR base, ULONG_PTR pc,
+ RUNTIME_FUNCTION *func, CONTEXT *context,
+ PVOID *handler_data, ULONG_PTR *frame_ret,
+ KNONVOLATILE_CONTEXT_POINTERS *ctx_ptr )
+{
+ void *handler;
+
+ TRACE( "type %x pc %lx sp %x func %lx\n", type, pc, context->Sp, base + func->BeginAddress );
+
+ *handler_data = NULL;
+
+ context->Pc = 0;
+ if (func->u.s.Flag)
+ handler = unwind_packed_data( base, pc, func, context, ctx_ptr );
+ else
+ handler = unwind_full_data( base, pc, func, context, handler_data, ctx_ptr );
+
+ TRACE( "ret: lr=%x sp=%x handler=%p\n", context->Lr, context->Sp, handler );
+ if (!context->Pc)
+ context->Pc = context->Lr;
+ context->ContextFlags |= CONTEXT_UNWOUND_TO_CALL;
+ *frame_ret = context->Sp;
+ return handler;
+}
+
+
/***********************************************************************
* RtlUnwind (NTDLL.@)
*/
--
2.25.1
Nov. 9, 2021
[PATCH 04/18] kernel32: Update arch availability for Rtl* forwards to ntdll.
by Martin Storsjö
Some functions aren't available in real kernel32.dll on i386, even if
they are available in ntdll (and thus are available on x86_64,arm,arm64),
some should be available on more architectures but aren't implemented
in ntdll yet.
Signed-off-by: Martin Storsjö <martin(a)martin.st>
---
dlls/kernel32/kernel32.spec | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec
index 90b503af19f..13e4df3c99f 100644
--- a/dlls/kernel32/kernel32.spec
+++ b/dlls/kernel32/kernel32.spec
@@ -1302,16 +1302,16 @@
@ cdecl -arch=arm,arm64,x86_64 RtlAddFunctionTable(ptr long long) ntdll.RtlAddFunctionTable
@ stdcall -norelay RtlCaptureContext(ptr) ntdll.RtlCaptureContext
@ stdcall RtlCaptureStackBackTrace(long long ptr ptr) ntdll.RtlCaptureStackBackTrace
-@ stdcall -arch=x86_64 RtlCompareMemory(ptr ptr long) ntdll.RtlCompareMemory
+@ stdcall -arch=arm,arm64,x86_64 RtlCompareMemory(ptr ptr long) ntdll.RtlCompareMemory
@ stdcall -arch=x86_64 RtlCopyMemory(ptr ptr long) ntdll.RtlCopyMemory
@ cdecl -arch=arm,arm64,x86_64 RtlDeleteFunctionTable(ptr) ntdll.RtlDeleteFunctionTable
@ stdcall RtlFillMemory(ptr long long) ntdll.RtlFillMemory
@ cdecl -arch=arm,arm64,x86_64 RtlInstallFunctionTableCallback(long long long ptr ptr ptr) ntdll.RtlInstallFunctionTableCallback
@ stdcall -arch=arm,arm64,x86_64 RtlLookupFunctionEntry(long ptr ptr) ntdll.RtlLookupFunctionEntry
@ stdcall RtlMoveMemory(ptr ptr long) ntdll.RtlMoveMemory
-@ stdcall -arch=x86_64,arm,arm64 RtlPcToFileHeader(ptr ptr) ntdll.RtlPcToFileHeader
-@ stdcall -arch=arm -norelay RtlRaiseException(ptr) ntdll.RtlRaiseException
-@ cdecl -arch=x86_64 RtlRestoreContext(ptr ptr) ntdll.RtlRestoreContext
+@ stdcall RtlPcToFileHeader(ptr ptr) ntdll.RtlPcToFileHeader
+@ stdcall -arch=arm,arm64,x86_64 -norelay RtlRaiseException(ptr) ntdll.RtlRaiseException
+@ cdecl -arch=arm64,x86_64 RtlRestoreContext(ptr ptr) ntdll.RtlRestoreContext
@ stdcall RtlUnwind(ptr ptr ptr long) ntdll.RtlUnwind
@ stdcall -arch=arm64,x86_64 RtlUnwindEx(long long ptr long ptr) ntdll.RtlUnwindEx
@ stdcall -arch=arm64,x86_64 RtlVirtualUnwind(long long long ptr ptr ptr ptr ptr) ntdll.RtlVirtualUnwind
--
2.25.1
Nov. 9, 2021
[PATCH 03/18] ntdll: Don't blindly increment Sp by 8 in leaf functions on arm64.
by Martin Storsjö
This doesn't fix (or break) any case known to me, but the code seems
wrong.
Leaf functions on arm (either 32 or 64) don't generally have any
default/implicit stack allocation.
Signed-off-by: Martin Storsjö <martin(a)martin.st>
---
dlls/ntdll/unix/signal_arm64.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/dlls/ntdll/unix/signal_arm64.c b/dlls/ntdll/unix/signal_arm64.c
index 50939125846..a52e9bccc49 100644
--- a/dlls/ntdll/unix/signal_arm64.c
+++ b/dlls/ntdll/unix/signal_arm64.c
@@ -224,7 +224,6 @@ NTSTATUS CDECL unwind_builtin_dll( ULONG type, DISPATCHER_CONTEXT *dispatch, CON
dispatch->LanguageHandler = NULL;
dispatch->EstablisherFrame = context->Sp;
context->Pc = context->u.s.Lr;
- context->Sp = context->Sp + sizeof(ULONG64);
context->ContextFlags |= CONTEXT_UNWOUND_TO_CALL;
return STATUS_SUCCESS;
}
--
2.25.1
Nov. 9, 2021
[PATCH 02/18] ntdll: Print an error if unable to unwind due to missing libunwind on arm64.
by Martin Storsjö
Signed-off-by: Martin Storsjö <martin(a)martin.st>
---
dlls/ntdll/unix/signal_arm64.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/dlls/ntdll/unix/signal_arm64.c b/dlls/ntdll/unix/signal_arm64.c
index 0f72bf3c89d..50939125846 100644
--- a/dlls/ntdll/unix/signal_arm64.c
+++ b/dlls/ntdll/unix/signal_arm64.c
@@ -319,6 +319,7 @@ NTSTATUS CDECL unwind_builtin_dll( ULONG type, DISPATCHER_CONTEXT *dispatch, CON
context->u.s.X28, context->u.s.Fp, context->u.s.Lr, context->Sp );
return STATUS_SUCCESS;
#else
+ ERR("libunwind not available, unable to unwind\n");
return STATUS_INVALID_DISPOSITION;
#endif
}
--
2.25.1
Nov. 9, 2021
[PATCH 01/18] ntdll: tests: Remove an accidental, unused macro in arm64 exception tests.
by Martin Storsjö
Signed-off-by: Martin Storsjö <martin(a)martin.st>
---
dlls/ntdll/tests/exception.c | 2 --
1 file changed, 2 deletions(-)
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c
index 6af68317732..c57d7c01d86 100644
--- a/dlls/ntdll/tests/exception.c
+++ b/dlls/ntdll/tests/exception.c
@@ -5320,8 +5320,6 @@ static const char * const reg_names[48] =
#define ORIG_LR 0xCCCCCCCC
-#define UWOP(code,info) (UWOP_##code | ((info) << 4))
-
static void call_virtual_unwind( int testnum, const struct unwind_test *test )
{
static const int code_offset = 1024;
--
2.25.1
Nov. 9, 2021
Re: [PATCH 4/5] win32u: Move null user driver implementation from user32.
by Marvin
Hi,
While running your changed tests, I think I found new failures.
Being a bot and all I'm not very good at pattern recognition, so I might be
wrong, but could you please double-check?
Full results can be found at:
https://testbot.winehq.org/JobDetails.pl?Key=101534
Your paranoid android.
=== debiant2 (32 bit report) ===
user32:
menu.c:2337: Test failed: test 25
Nov. 9, 2021
Re: [PATCH 2/5] user32: Use user_driver_funcs to expose user driver function from drivers.
by Marvin
Hi,
While running your changed tests, I think I found new failures.
Being a bot and all I'm not very good at pattern recognition, so I might be
wrong, but could you please double-check?
Full results can be found at:
https://testbot.winehq.org/JobDetails.pl?Key=101532
Your paranoid android.
=== debiant2 (32 bit WoW report) ===
user32:
input.c:1823: Test failed: expected non-zero
input.c:1829: Test failed: expected -1, got 0
input.c:1830: Test failed: expected 122, got 0
input.c:1831: Test failed: expected non-zero
input.c:1835: Test failed: expected non-zero
input.c:1970: Test failed: expected non-zero
=== debiant2 (64 bit WoW report) ===
user32:
input.c:1823: Test failed: expected non-zero
input.c:1829: Test failed: expected -1, got 0
input.c:1830: Test failed: expected 122, got 0
input.c:1831: Test failed: expected non-zero
input.c:1835: Test failed: expected non-zero
input.c:1970: Test failed: expected non-zero
Nov. 9, 2021
[PATCH v2 vkd3d] vkd3d-shader/hlsl: Allocate samplers.
by Matteo Bruni
From: Zebediah Figura <zfigura(a)codeweavers.com>
Signed-off-by: Matteo Bruni <mbruni(a)codeweavers.com>
---
Now it should actually apply on top of 219117.
libs/vkd3d-shader/hlsl_codegen.c | 59 ++++++++++++++++++++++++--------
1 file changed, 44 insertions(+), 15 deletions(-)
diff --git a/libs/vkd3d-shader/hlsl_codegen.c b/libs/vkd3d-shader/hlsl_codegen.c
index 0bf18c2b..7c0b0f95 100644
--- a/libs/vkd3d-shader/hlsl_codegen.c
+++ b/libs/vkd3d-shader/hlsl_codegen.c
@@ -1202,60 +1202,88 @@ static void allocate_buffers(struct hlsl_ctx *ctx)
}
}
-static const struct hlsl_ir_var *get_reserved_texture(struct hlsl_ctx *ctx, uint32_t index)
+static const struct hlsl_ir_var *get_reserved_object(struct hlsl_ctx *ctx, char type, uint32_t index)
{
const struct hlsl_ir_var *var;
LIST_FOR_EACH_ENTRY(var, &ctx->extern_vars, const struct hlsl_ir_var, extern_entry)
{
- if (var->last_read && var->reg_reservation.type == 't' && var->reg_reservation.index == index)
+ if (var->last_read && var->reg_reservation.type == type && var->reg_reservation.index == index)
return var;
}
return NULL;
}
-static void allocate_textures(struct hlsl_ctx *ctx)
+static const struct object_type_info
{
+ enum hlsl_base_type type;
+ char *name;
+ char reg_name;
+}
+object_types[] =
+{
+ { HLSL_TYPE_SAMPLER, "sampler", 's' },
+ { HLSL_TYPE_TEXTURE, "texture", 't' },
+};
+
+static const struct object_type_info *get_object_type_info(enum hlsl_base_type type)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(object_types); ++i)
+ if (type == object_types[i].type)
+ return &object_types[i];
+
+ WARN("No type info for object type %u.\n", type);
+ return NULL;
+}
+
+static void allocate_objects(struct hlsl_ctx *ctx, enum hlsl_base_type type)
+{
+ const struct object_type_info *type_info = get_object_type_info(type);
struct hlsl_ir_var *var;
uint32_t index = 0;
LIST_FOR_EACH_ENTRY(var, &ctx->extern_vars, struct hlsl_ir_var, extern_entry)
{
if (!var->last_read || var->data_type->type != HLSL_CLASS_OBJECT
- || var->data_type->base_type != HLSL_TYPE_TEXTURE)
+ || var->data_type->base_type != type)
continue;
- if (var->reg_reservation.type == 't')
+ if (var->reg_reservation.type == type_info->reg_name)
{
- const struct hlsl_ir_var *reserved_texture = get_reserved_texture(ctx, var->reg_reservation.index);
+ const struct hlsl_ir_var *reserved_object = get_reserved_object(ctx, type_info->reg_name,
+ var->reg_reservation.index);
- if (reserved_texture && reserved_texture != var)
+ if (reserved_object && reserved_object != var)
{
hlsl_error(ctx, var->loc, VKD3D_SHADER_ERROR_HLSL_OVERLAPPING_RESERVATIONS,
- "Multiple textures bound to t%u.", var->reg_reservation.index);
- hlsl_note(ctx, reserved_texture->loc, VKD3D_SHADER_LOG_ERROR,
- "Texture '%s' is already bound to t%u.", reserved_texture->name,
+ "Multiple %ss bound to %c%u.", type_info->name, type_info->reg_name,
var->reg_reservation.index);
+ hlsl_note(ctx, reserved_object->loc, VKD3D_SHADER_LOG_ERROR,
+ "Object '%s' is already bound to %c%u.", reserved_object->name,
+ type_info->reg_name, var->reg_reservation.index);
}
var->reg.id = var->reg_reservation.index;
var->reg.allocated = true;
- TRACE("Allocated reserved %s to t%u.\n", var->name, var->reg_reservation.index);
+ TRACE("Allocated reserved %s to %c%u.\n", var->name, type_info->reg_name, var->reg_reservation.index);
}
else if (!var->reg_reservation.type)
{
- while (get_reserved_texture(ctx, index))
+ while (get_reserved_object(ctx, type_info->reg_name, index))
++index;
var->reg.id = index;
var->reg.allocated = true;
- TRACE("Allocated %s to t%u.\n", var->name, index);
+ TRACE("Allocated %s to %c%u.\n", var->name, type_info->reg_name, index);
++index;
}
else
{
hlsl_error(ctx, var->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_RESERVATION,
- "Textures must be bound to register type 't'.");
+ "Object of type '%s' must be bound to register type '%c'.",
+ type_info->name, type_info->reg_name);
}
}
}
@@ -1376,9 +1404,10 @@ int hlsl_emit_dxbc(struct hlsl_ctx *ctx, struct hlsl_ir_function_decl *entry_fun
else
{
allocate_buffers(ctx);
- allocate_textures(ctx);
+ allocate_objects(ctx, HLSL_TYPE_TEXTURE);
}
allocate_semantic_registers(ctx);
+ allocate_objects(ctx, HLSL_TYPE_SAMPLER);
if (ctx->result)
return ctx->result;
--
2.26.3
Nov. 9, 2021