[PATCH 06/18] ntdll: tests: Add tests for RtlVirtualUnwind for arm.

Martin Storsjö martin at martin.st
Tue Nov 9 08:05:43 CST 2021


Signed-off-by: Martin Storsjö <martin at 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




More information about the wine-devel mailing list