Signed-off-by: Martin Storsjö martin@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;
Signed-off-by: Martin Storsjö martin@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 }
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@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; }
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@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
Signed-off-by: Martin Storsjö martin@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.@) */
Signed-off-by: Martin Storsjö martin@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);
Signed-off-by: Martin Storsjö martin@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" );
Signed-off-by: Martin Storsjö martin@martin.st --- tools/winedump/pe.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/tools/winedump/pe.c b/tools/winedump/pe.c index 4629c3082f9..5d325fba3da 100644 --- a/tools/winedump/pe.c +++ b/tools/winedump/pe.c @@ -1171,13 +1171,20 @@ static void dump_armnt_unwind_info( const struct runtime_function_armnt *fnc ) excode = (code << 24) | (excodes[0] << 16) | (excodes[1] << 8) | excodes[2]; printf( "%s sp, sp, #%u\n", inepilogue ? "add" : "sub", (excode & 0xffffff) * 4 ); } - else if (code <= 0xfc) + else if (code <= 0xfb) printf( "nop\n" ); - else if (code <= 0xfe) + else if (code <= 0xfc) + printf( "nop.w\n" ); + else if (code <= 0xfd) { printf( "(end) nop\n" ); inepilogue = TRUE; } + else if (code <= 0xfe) + { + printf( "(end) nop.w\n" ); + inepilogue = TRUE; + } else { printf( "end\n" );
This isn't mentioned in the documentation (which only writes out the instruction that is executed while unwinding, i.e. the mirror form of it), but a prologue version of this instruction would look like this.
Signed-off-by: Martin Storsjö martin@martin.st --- tools/winedump/pe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tools/winedump/pe.c b/tools/winedump/pe.c index 5d325fba3da..43ba0618615 100644 --- a/tools/winedump/pe.c +++ b/tools/winedump/pe.c @@ -1113,7 +1113,7 @@ static void dump_armnt_unwind_info( const struct runtime_function_armnt *fnc ) if (inepilogue) printf( "ldr lr, [sp], #%u\n", (excode & 0x0f) * 4 ); else - printf( "unknown 32\n" ); + printf( "str lr, [sp, #-%u]!\n", (excode & 0x0f) * 4 ); } else printf( "unknown 32\n" );
This differs slightly from the official docs (which is clear in some places, vague in others, and contradictory in some places), based on actual observed behaviour.
Signed-off-by: Martin Storsjö martin@martin.st --- tools/winedump/pe.c | 146 ++++++++++++++++++-------------------------- 1 file changed, 58 insertions(+), 88 deletions(-)
diff --git a/tools/winedump/pe.c b/tools/winedump/pe.c index 43ba0618615..6ff14f95791 100644 --- a/tools/winedump/pe.c +++ b/tools/winedump/pe.c @@ -787,7 +787,7 @@ static void dump_armnt_unwind_info( const struct runtime_function_armnt *fnc ) if (fnc->u.s.Flag) { char intregs[32] = {0}, intregspop[32] = {0}, vfpregs[32] = {0}; - WORD pf = 0, ef = 0, sc = 0; + WORD pf = 0, ef = 0, fpoffset = 0, stack = fnc->u.s.StackAdjust;
printf( "\nFunction %08x-%08x:\n", fnc->BeginAddress & ~1, (fnc->BeginAddress & ~1) + fnc->u.s.FunctionLength * 2 ); @@ -805,86 +805,60 @@ static void dump_armnt_unwind_info( const struct runtime_function_armnt *fnc ) { pf = fnc->u.s.StackAdjust & 0x04; ef = fnc->u.s.StackAdjust & 0x08; + stack = (fnc->u.s.StackAdjust & 3) + 1; }
- if (!fnc->u.s.R && !pf) + if (!fnc->u.s.R || pf) { - if (fnc->u.s.Reg) + int first = 4, last = fnc->u.s.Reg + 4; + if (pf) { - sprintf(intregs, "r4-r%u", fnc->u.s.Reg + 4); - sprintf(intregspop, "r4-r%u", fnc->u.s.Reg + 4); + first = (~fnc->u.s.StackAdjust) & 3; + if (fnc->u.s.R) + last = 3; } + if (first == last) + sprintf(intregs, "r%u", first); else - { - strcpy(intregs, "r4"); - strcpy(intregspop, "r4"); - } - sc = fnc->u.s.Reg + 1; - if (fnc->u.s.C || fnc->u.s.L) - { - strcat(intregs, ", "); - if (fnc->u.s.C || (fnc->u.s.L && !fnc->u.s.H)) - strcat(intregspop, ", "); - } + sprintf(intregs, "r%u-r%u", first, last); + fpoffset = last + 1 - first; } - else if (fnc->u.s.R && pf) + + if (!fnc->u.s.R || ef) { - if (((~fnc->u.s.StackAdjust) & 3) != 3) + int first = 4, last = fnc->u.s.Reg + 4; + if (ef) { - sprintf(intregs, "r%u-r3", (~fnc->u.s.StackAdjust) & 3); - sprintf(intregspop, "r%u-r3", (~fnc->u.s.StackAdjust) & 3); + first = (~fnc->u.s.StackAdjust) & 3; + if (fnc->u.s.R) + last = 3; } + if (first == last) + sprintf(intregspop, "r%u", first); else - { - sprintf(intregs, "r3"); - sprintf(intregspop, "r3"); - } - sc = 4 - ((~fnc->u.s.StackAdjust) & 3); - if (fnc->u.s.C || fnc->u.s.L) - { - strcat(intregs, ", "); - if (fnc->u.s.C || (fnc->u.s.L && !fnc->u.s.H)) - strcat(intregspop, ", "); - } - } - else if (!fnc->u.s.R && pf) - { - sprintf(intregs, "r%u-r%u", (~fnc->u.s.StackAdjust) & 3, fnc->u.s.Reg + 4); - sprintf(intregspop, "r%u-r%u", (~fnc->u.s.StackAdjust) & 3, fnc->u.s.Reg + 4); - sc = fnc->u.s.Reg + 5 - ((~fnc->u.s.StackAdjust) & 3); - if (fnc->u.s.C || fnc->u.s.L) - { - strcat(intregs, ", "); - if (fnc->u.s.C || (fnc->u.s.L && !fnc->u.s.H)) - strcat(intregspop, ", "); - } - } - else if (fnc->u.s.R && !pf) - { - if (!fnc->u.s.C && !fnc->u.s.L) - { - strcpy(intregs, "none"); - strcpy(intregspop, "none"); - } + sprintf(intregspop, "r%u-r%u", first, last); }
- if (fnc->u.s.C && !fnc->u.s.L) + if (fnc->u.s.C) { + if (intregs[0]) + strcat(intregs, ", "); + if (intregspop[0]) + strcat(intregspop, ", "); strcat(intregs, "r11"); strcat(intregspop, "r11"); } - else if (fnc->u.s.C && fnc->u.s.L) - { - strcat(intregs, "r11, lr"); - if (fnc->u.s.H) - strcat(intregspop, "r11"); - else - strcat(intregspop, "r11, pc"); - } - else if (!fnc->u.s.C && fnc->u.s.L) + if (fnc->u.s.L) { + if (intregs[0]) + strcat(intregs, ", "); strcat(intregs, "lr"); - if (!fnc->u.s.H) + + if (intregspop[0] && (fnc->u.s.Ret != 0 || !fnc->u.s.H)) + strcat(intregspop, ", "); + if (fnc->u.s.Ret != 0) + strcat(intregspop, "lr"); + else if (!fnc->u.s.H) strcat(intregspop, "pc"); }
@@ -895,46 +869,42 @@ static void dump_armnt_unwind_info( const struct runtime_function_armnt *fnc ) else strcpy(vfpregs, "d8"); } - else - strcpy(vfpregs, "none");
- if (fnc->u.s.H) - printf( " Unwind Code\tpush {r0-r3}\n" ); + if (fnc->u.s.Flag == 1) { + if (fnc->u.s.H) + printf( " Unwind Code\tpush {r0-r3}\n" );
- if (fnc->u.s.R || fnc->u.s.L || fnc->u.s.C || pf) - printf( " Unwind Code\tpush {%s}\n", intregs ); + if (intregs[0]) + printf( " Unwind Code\tpush {%s}\n", intregs );
- if (fnc->u.s.C && fnc->u.s.R && !fnc->u.s.L && !pf) - printf( " Unwind Code\tmov r11, sp\n" ); - else if (fnc->u.s.C && (!fnc->u.s.R || fnc->u.s.L || pf)) - { - if (fnc->u.s.StackAdjust >= 0x03f4 && !sc) - printf( " Unwind Code\tadd r11, sp, #<unknown>\n"); - else if (fnc->u.s.StackAdjust >= 0x03f4) - printf( " Unwind Code\tadd r11, sp, #%d\n", sc * 4 ); - else - printf( " Unwind Code\tadd r11, sp, #%d\n", fnc->u.s.StackAdjust * 4 ); - } + if (fnc->u.s.C && fpoffset == 0) + printf( " Unwind Code\tmov r11, sp\n" ); + else if (fnc->u.s.C) + printf( " Unwind Code\tadd r11, sp, #%d\n", fpoffset * 4 );
- if (fnc->u.s.R && fnc->u.s.Reg != 0x07) - printf( " Unwind Code\tvpush {%s}\n", vfpregs ); + if (fnc->u.s.R && fnc->u.s.Reg != 0x07) + printf( " Unwind Code\tvpush {%s}\n", vfpregs );
- if (fnc->u.s.StackAdjust < 0x03f4 && !pf) - printf( " Unwind Code\tsub sp, sp, #%d\n", fnc->u.s.StackAdjust * 4 ); + if (stack && !pf) + printf( " Unwind Code\tsub sp, sp, #%d\n", stack * 4 ); + }
+ if (fnc->u.s.Ret == 3) + return; + printf( "Epilogue:\n" );
- if (fnc->u.s.StackAdjust < 0x03f4 && !ef) - printf( " Unwind Code\tadd sp, sp, #%d\n", fnc->u.s.StackAdjust * 4 ); + if (stack && !ef) + printf( " Unwind Code\tadd sp, sp, #%d\n", stack * 4 );
if (fnc->u.s.R && fnc->u.s.Reg != 0x07) printf( " Unwind Code\tvpop {%s}\n", vfpregs );
- if (fnc->u.s.C || !fnc->u.s.R || ef || (fnc->u.s.L && !fnc->u.s.H)) + if (intregspop[0]) printf( " Unwind Code\tpop {%s}\n", intregspop );
- if (fnc->u.s.H && !fnc->u.s.L) + if (fnc->u.s.H && !(fnc->u.s.L && fnc->u.s.Ret == 0)) printf( " Unwind Code\tadd sp, sp, #16\n" ); - else if (fnc->u.s.H && fnc->u.s.L) + else if (fnc->u.s.H && (fnc->u.s.L && fnc->u.s.Ret == 0)) printf( " Unwind Code\tldr pc, [sp], #20\n" );
if (fnc->u.s.Ret == 1)
This does the same as 23b44e8df62847872d036cd88d72e36b5424ee35, but for arm:
Don't call KiUserExceptionDispatcher directly on the stack pointer stored in the CONTEXT, but use the one stored in syscall_frame (which includes the stack allocation in e.g. RtlRaiseException).
Signed-off-by: Martin Storsjö martin@martin.st --- dlls/ntdll/unix/signal_arm.c | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/dlls/ntdll/unix/signal_arm.c b/dlls/ntdll/unix/signal_arm.c index 798d7b9cc87..88718e938a8 100644 --- a/dlls/ntdll/unix/signal_arm.c +++ b/dlls/ntdll/unix/signal_arm.c @@ -563,12 +563,16 @@ void call_raise_user_exception_dispatcher(void) NTSTATUS call_user_exception_dispatcher( EXCEPTION_RECORD *rec, CONTEXT *context ) { struct syscall_frame *frame = arm_thread_data()->syscall_frame; + DWORD lr = frame->lr; + DWORD sp = frame->sp; NTSTATUS status = NtSetContextThread( GetCurrentThread(), context );
if (status) return status; frame->r0 = (DWORD)rec; frame->r1 = (DWORD)context; frame->pc = (DWORD)pKiUserExceptionDispatcher; + frame->lr = lr; + frame->sp = sp; frame->restore_flags |= CONTEXT_INTEGER | CONTEXT_CONTROL; return status; }
RtlCaptureContext doesn't set context->Lr, which is needed for being able to unwind from the context.
This matches what is done in the arm64 version.
Signed-off-by: Martin Storsjö martin@martin.st --- dlls/ntdll/signal_arm.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/dlls/ntdll/signal_arm.c b/dlls/ntdll/signal_arm.c index 27e27deabfe..3bf8e19e126 100644 --- a/dlls/ntdll/signal_arm.c +++ b/dlls/ntdll/signal_arm.c @@ -811,6 +811,7 @@ __ASM_STDCALL_FUNC( RtlRaiseException, 4, "bl " __ASM_NAME("RtlCaptureContext") "\n\t" "ldr r0, [sp, #0x1a0]\n\t" /* rec */ "ldr r1, [sp, #0x1a4]\n\t" + "str r1, [sp, #0x3c]\n\t" /* context->Lr */ "str r1, [sp, #0x40]\n\t" /* context->Pc */ "mrs r2, CPSR\n\t" "bfi r2, r1, #5, #1\n\t" /* Thumb bit */
We ideally should back up all of d0-d31, but when building in ELF form, only d0-d15 are normally available (with common distributions' default compilers), unless object files are built with flags to enable support for d16-d31 (with e.g. -mfpu=neon).
Signed-off-by: Martin Storsjö martin@martin.st --- dlls/ntdll/signal_arm.c | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/dlls/ntdll/signal_arm.c b/dlls/ntdll/signal_arm.c index 3bf8e19e126..a1a732201cf 100644 --- a/dlls/ntdll/signal_arm.c +++ b/dlls/ntdll/signal_arm.c @@ -75,6 +75,10 @@ __ASM_STDCALL_FUNC( RtlCaptureContext, 4, "str r1, [r0, #0x3c]\n\t" /* context->Lr */ "add r0, #0x0c\n\t" "stm r0, {r2-r12}\n\t" /* context->R2..R12 */ +#ifndef __SOFTFP__ + "add r0, #0x44\n\t" /* 0x50 - 0x0c */ + "vstm r0, {d0-d15}\n\t" /* context->D0-D15 */ +#endif "bx lr" )
Store the original stack pointer (on entry to the syscall dispatcher) in syscall_frame; the stack pointer itself is incremented by "pop {r0-r3}" right before calling the syscall itself.
This fixes unwinding from functions set up by syscalls, like KiUserExceptionDispatcher.
Signed-off-by: Martin Storsjö martin@martin.st --- dlls/ntdll/unix/signal_arm.c | 4 +++- tools/winebuild/import.c | 1 - 2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/dlls/ntdll/unix/signal_arm.c b/dlls/ntdll/unix/signal_arm.c index 88718e938a8..1cc34a9218e 100644 --- a/dlls/ntdll/unix/signal_arm.c +++ b/dlls/ntdll/unix/signal_arm.c @@ -1041,7 +1041,8 @@ __ASM_GLOBAL_FUNC( __wine_syscall_dispatcher, "ldr r1, [r1, #0x1d8]\n\t" /* arm_thread_data()->syscall_frame */ "add r0, r1, #0x10\n\t" "stm r0, {r4-r12,lr}\n\t" - "str sp, [r1, #0x38]\n\t" + "add r2, sp, #0x10\n\t" + "str r2, [r1, #0x38]\n\t" "str r3, [r1, #0x3c]\n\t" "mrs r0, CPSR\n\t" "bfi r0, lr, #5, #1\n\t" /* set thumb bit */ @@ -1099,6 +1100,7 @@ __ASM_GLOBAL_FUNC( __wine_syscall_dispatcher, "ldm r8, {r4-r12,pc}\n" "5:\tmovw r0, #0x000d\n\t" /* STATUS_INVALID_PARAMETER */ "movt r0, #0xc000\n\t" + "add sp, sp, #0x10\n\t" "b 4b\n" __ASM_NAME("__wine_syscall_dispatcher_return") ":\n\t" "mov r8, r0\n\t" diff --git a/tools/winebuild/import.c b/tools/winebuild/import.c index 104397716f1..7972fc7b1d9 100644 --- a/tools/winebuild/import.c +++ b/tools/winebuild/import.c @@ -1492,7 +1492,6 @@ void output_syscalls( DLLSPEC *spec ) output( "\tmovw ip, #%u\n", id ); output( "\tmov r3, lr\n" ); output( "\tbl %s\n", asm_name("__wine_syscall") ); - output( "\tadd sp, #16\n" ); output( "\tbx lr\n" ); break; case CPU_ARM64:
This is a very close copy of the arm64 implementation.
Signed-off-by: Martin Storsjö martin@martin.st --- .../api-ms-win-core-rtlsupport-l1-1-0.spec | 4 +- .../api-ms-win-core-rtlsupport-l1-2-0.spec | 4 +- dlls/kernel32/kernel32.spec | 4 +- dlls/ntdll/ntdll.spec | 4 +- dlls/ntdll/signal_arm.c | 599 +++++++++++++++--- dlls/ntdll/unix/signal_arm.c | 107 +++- 6 files changed, 641 insertions(+), 81 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 fefac9dc400..43e95d62dae 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 @@ -9,7 +9,7 @@ @ stdcall -arch=arm,arm64,x86_64 RtlLookupFunctionEntry(long ptr ptr) ntdll.RtlLookupFunctionEntry @ stdcall RtlPcToFileHeader(ptr ptr) ntdll.RtlPcToFileHeader @ stdcall -norelay RtlRaiseException(ptr) ntdll.RtlRaiseException -@ cdecl -arch=arm64,x86_64 RtlRestoreContext(ptr ptr) ntdll.RtlRestoreContext +@ cdecl -arch=arm,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=arm,arm64,x86_64 RtlUnwindEx(ptr ptr ptr ptr ptr ptr) ntdll.RtlUnwindEx @ 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 bff03b1d2ae..31540f6c9f7 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 @@ -10,7 +10,7 @@ @ stdcall -arch=arm,arm64,x86_64 RtlLookupFunctionEntry(long ptr ptr) ntdll.RtlLookupFunctionEntry @ stdcall RtlPcToFileHeader(ptr ptr) ntdll.RtlPcToFileHeader @ stdcall -norelay RtlRaiseException(ptr) ntdll.RtlRaiseException -@ cdecl -arch=arm64,x86_64 RtlRestoreContext(ptr ptr) ntdll.RtlRestoreContext +@ cdecl -arch=arm,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=arm,arm64,x86_64 RtlUnwindEx(ptr ptr ptr ptr ptr ptr) ntdll.RtlUnwindEx @ 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 18ec187a683..7c25caf19d7 100644 --- a/dlls/kernel32/kernel32.spec +++ b/dlls/kernel32/kernel32.spec @@ -1311,9 +1311,9 @@ @ stdcall RtlMoveMemory(ptr ptr long) ntdll.RtlMoveMemory @ 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 +@ cdecl -arch=arm,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=arm,arm64,x86_64 RtlUnwindEx(long long ptr long ptr) ntdll.RtlUnwindEx @ 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 diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec index 070b57efb59..c703219cfbb 100644 --- a/dlls/ntdll/ntdll.spec +++ b/dlls/ntdll/ntdll.spec @@ -941,7 +941,7 @@ @ stdcall RtlRemoveVectoredContinueHandler(ptr) @ stdcall RtlRemoveVectoredExceptionHandler(ptr) @ stdcall RtlResetRtlTranslations(ptr) -@ cdecl -arch=arm64,x86_64 RtlRestoreContext(ptr ptr) +@ cdecl -arch=arm,arm64,x86_64 RtlRestoreContext(ptr ptr) @ stdcall RtlRestoreLastWin32Error(long) RtlSetLastWin32Error @ stub RtlRevertMemoryStream @ stub RtlRunDecodeUnicodeString @@ -1044,7 +1044,7 @@ @ stdcall RtlUnlockHeap(long) # @ stub RtlUnlockMemoryStreamRegion @ stdcall -norelay RtlUnwind(ptr ptr ptr ptr) -@ stdcall -arch=arm64,x86_64 RtlUnwindEx(ptr ptr ptr ptr ptr ptr) +@ stdcall -arch=arm,arm64,x86_64 RtlUnwindEx(ptr ptr ptr ptr ptr ptr) @ stdcall RtlUpcaseUnicodeChar(long) @ stdcall RtlUpcaseUnicodeString(ptr ptr long) @ stdcall RtlUpcaseUnicodeStringToAnsiString(ptr ptr long) diff --git a/dlls/ntdll/signal_arm.c b/dlls/ntdll/signal_arm.c index a1a732201cf..a24cac97df7 100644 --- a/dlls/ntdll/signal_arm.c +++ b/dlls/ntdll/signal_arm.c @@ -38,13 +38,31 @@ WINE_DEFAULT_DEBUG_CHANNEL(seh);
+/* layering violation: the setjmp buffer is defined in msvcrt, but used by RtlUnwindEx */ +struct MSVCRT_JUMP_BUFFER +{ + unsigned long Frame; + unsigned long R4; + unsigned long R5; + unsigned long R6; + unsigned long R7; + unsigned long R8; + unsigned long R9; + unsigned long R10; + unsigned long R11; + unsigned long Sp; + unsigned long Pc; + unsigned long Fpscr; + unsigned long long D[8]; +}; + /******************************************************************* * is_valid_frame */ -static inline BOOL is_valid_frame( void *frame ) +static inline BOOL is_valid_frame( ULONG_PTR frame ) { - if ((ULONG_PTR)frame & 3) return FALSE; - return (frame >= NtCurrentTeb()->Tib.StackLimit && + if (frame & 3) return FALSE; + return ((void *)frame >= NtCurrentTeb()->Tib.StackLimit && (void **)frame < (void **)NtCurrentTeb()->Tib.StackBase - 1); }
@@ -83,54 +101,322 @@ __ASM_STDCALL_FUNC( RtlCaptureContext, 4,
/********************************************************************** - * call_stack_handlers + * virtual_unwind + */ +static NTSTATUS virtual_unwind( ULONG type, DISPATCHER_CONTEXT *dispatch, CONTEXT *context ) +{ + LDR_DATA_TABLE_ENTRY *module; + NTSTATUS status; + + dispatch->ImageBase = 0; + dispatch->ScopeIndex = 0; + dispatch->EstablisherFrame = 0; + dispatch->ControlPc = context->Pc; + /* + * TODO: CONTEXT_UNWOUND_TO_CALL should be cleared if unwound past a + * signal frame. + */ + dispatch->ControlPcIsUnwound = (context->ContextFlags & CONTEXT_UNWOUND_TO_CALL) != 0; + + /* first look for PE exception information */ + + if ((dispatch->FunctionEntry = lookup_function_info( + context->Pc - (dispatch->ControlPcIsUnwound ? 2 : 0), + (ULONG_PTR*)&dispatch->ImageBase, &module ))) + { + dispatch->LanguageHandler = RtlVirtualUnwind( type, dispatch->ImageBase, context->Pc, + dispatch->FunctionEntry, context, + &dispatch->HandlerData, (ULONG_PTR *)&dispatch->EstablisherFrame, + NULL ); + return STATUS_SUCCESS; + } + + /* then look for host system exception information */ + + if (!module || (module->Flags & LDR_WINE_INTERNAL)) + { + status = unix_funcs->unwind_builtin_dll( type, dispatch, context ); + if (status != STATUS_SUCCESS) return status; + + if (dispatch->EstablisherFrame) + { + dispatch->FunctionEntry = NULL; + if (dispatch->LanguageHandler && !module) + { + FIXME( "calling personality routine in system library not supported yet\n" ); + dispatch->LanguageHandler = NULL; + } + return STATUS_SUCCESS; + } + } + else + { + status = context->Pc != context->Lr ? + STATUS_SUCCESS : STATUS_INVALID_DISPOSITION; + WARN( "exception data not found in %s for %p, LR %p, status %x\n", + debugstr_w(module->BaseDllName.Buffer), (void*) context->Pc, + (void*) context->Lr, status ); + dispatch->EstablisherFrame = context->Sp; + dispatch->LanguageHandler = NULL; + context->Pc = context->Lr; + context->ContextFlags |= CONTEXT_UNWOUND_TO_CALL; + return status; + } + + dispatch->EstablisherFrame = context->Sp; + dispatch->LanguageHandler = NULL; + context->Pc = context->Lr; + context->ContextFlags |= CONTEXT_UNWOUND_TO_CALL; + return STATUS_SUCCESS; +} + + +struct unwind_exception_frame +{ + EXCEPTION_REGISTRATION_RECORD frame; + DISPATCHER_CONTEXT *dispatch; +}; + +/********************************************************************** + * unwind_exception_handler + * + * Handler for exceptions happening while calling an unwind handler. + */ +static DWORD __cdecl unwind_exception_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame, + CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher ) +{ + struct unwind_exception_frame *unwind_frame = (struct unwind_exception_frame *)frame; + DISPATCHER_CONTEXT *dispatch = (DISPATCHER_CONTEXT *)dispatcher; + + /* copy the original dispatcher into the current one, except for the TargetIp */ + dispatch->ControlPc = unwind_frame->dispatch->ControlPc; + dispatch->ImageBase = unwind_frame->dispatch->ImageBase; + dispatch->FunctionEntry = unwind_frame->dispatch->FunctionEntry; + dispatch->EstablisherFrame = unwind_frame->dispatch->EstablisherFrame; + dispatch->ContextRecord = unwind_frame->dispatch->ContextRecord; + dispatch->LanguageHandler = unwind_frame->dispatch->LanguageHandler; + dispatch->HandlerData = unwind_frame->dispatch->HandlerData; + dispatch->HistoryTable = unwind_frame->dispatch->HistoryTable; + dispatch->ScopeIndex = unwind_frame->dispatch->ScopeIndex; + TRACE( "detected collided unwind\n" ); + return ExceptionCollidedUnwind; +} + +/********************************************************************** + * call_unwind_handler + * + * Call a single unwind handler. + */ +static DWORD call_unwind_handler( EXCEPTION_RECORD *rec, DISPATCHER_CONTEXT *dispatch ) +{ + struct unwind_exception_frame frame; + DWORD res; + + frame.frame.Handler = unwind_exception_handler; + frame.dispatch = dispatch; + __wine_push_frame( &frame.frame ); + + TRACE( "calling handler %p (rec=%p, frame=0x%x context=%p, dispatch=%p)\n", + dispatch->LanguageHandler, rec, dispatch->EstablisherFrame, dispatch->ContextRecord, dispatch ); + res = dispatch->LanguageHandler( rec, (void *)dispatch->EstablisherFrame, dispatch->ContextRecord, dispatch ); + TRACE( "handler %p returned %x\n", dispatch->LanguageHandler, res ); + + __wine_pop_frame( &frame.frame ); + + switch (res) + { + case ExceptionContinueSearch: + case ExceptionCollidedUnwind: + break; + default: + raise_status( STATUS_INVALID_DISPOSITION, rec ); + break; + } + + return res; +} + + +/********************************************************************** + * call_teb_unwind_handler * - * Call the stack handlers chain. + * Call a single unwind handler from the TEB chain. */ -static NTSTATUS call_stack_handlers( EXCEPTION_RECORD *rec, CONTEXT *context ) +static DWORD call_teb_unwind_handler( EXCEPTION_RECORD *rec, DISPATCHER_CONTEXT *dispatch, + EXCEPTION_REGISTRATION_RECORD *teb_frame ) { - EXCEPTION_REGISTRATION_RECORD *frame, *dispatch, *nested_frame; DWORD res;
- frame = NtCurrentTeb()->Tib.ExceptionList; - nested_frame = NULL; - while (frame != (EXCEPTION_REGISTRATION_RECORD*)~0UL) + TRACE( "calling TEB handler %p (rec=%p, frame=%p context=%p, dispatch=%p)\n", + teb_frame->Handler, rec, teb_frame, dispatch->ContextRecord, dispatch ); + res = teb_frame->Handler( rec, teb_frame, dispatch->ContextRecord, (EXCEPTION_REGISTRATION_RECORD**)dispatch ); + TRACE( "handler at %p returned %u\n", teb_frame->Handler, res ); + + switch (res) { - /* Check frame address */ - if (!is_valid_frame( frame )) + case ExceptionContinueSearch: + case ExceptionCollidedUnwind: + break; + default: + raise_status( STATUS_INVALID_DISPOSITION, rec ); + break; + } + + return res; +} + + +static DWORD __cdecl nested_exception_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame, + CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher ) +{ + if (!(rec->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND))) + rec->ExceptionFlags |= EH_NESTED_CALL; + + return ExceptionContinueSearch; +} + + +/********************************************************************** + * call_handler + * + * Call a single exception handler. + * FIXME: Handle nested exceptions. + */ +static DWORD call_handler( EXCEPTION_RECORD *rec, CONTEXT *context, DISPATCHER_CONTEXT *dispatch ) +{ + EXCEPTION_REGISTRATION_RECORD frame; + DWORD res; + + frame.Handler = nested_exception_handler; + __wine_push_frame( &frame ); + + TRACE( "calling handler %p (rec=%p, frame=0x%x context=%p, dispatch=%p)\n", + dispatch->LanguageHandler, rec, dispatch->EstablisherFrame, dispatch->ContextRecord, dispatch ); + res = dispatch->LanguageHandler( rec, (void *)dispatch->EstablisherFrame, context, dispatch ); + TRACE( "handler at %p returned %u\n", dispatch->LanguageHandler, res ); + + rec->ExceptionFlags &= EH_NONCONTINUABLE; + __wine_pop_frame( &frame ); + return res; +} + + +/********************************************************************** + * call_teb_handler + * + * Call a single exception handler from the TEB chain. + * FIXME: Handle nested exceptions. + */ +static DWORD call_teb_handler( EXCEPTION_RECORD *rec, CONTEXT *context, DISPATCHER_CONTEXT *dispatch, + EXCEPTION_REGISTRATION_RECORD *teb_frame ) +{ + DWORD res; + + TRACE( "calling TEB handler %p (rec=%p, frame=%p context=%p, dispatch=%p)\n", + teb_frame->Handler, rec, teb_frame, dispatch->ContextRecord, dispatch ); + res = teb_frame->Handler( rec, teb_frame, context, (EXCEPTION_REGISTRATION_RECORD**)dispatch ); + TRACE( "handler at %p returned %u\n", teb_frame->Handler, res ); + return res; +} + + +/********************************************************************** + * call_function_handlers + * + * Call the per-function handlers. + */ +static NTSTATUS call_function_handlers( EXCEPTION_RECORD *rec, CONTEXT *orig_context ) +{ + EXCEPTION_REGISTRATION_RECORD *teb_frame = NtCurrentTeb()->Tib.ExceptionList; + UNWIND_HISTORY_TABLE table; + DISPATCHER_CONTEXT dispatch; + CONTEXT context, prev_context; + NTSTATUS status; + + context = *orig_context; + dispatch.TargetPc = 0; + dispatch.ContextRecord = &context; + dispatch.HistoryTable = &table; + prev_context = context; + dispatch.NonVolatileRegisters = (BYTE *)&prev_context.R4; + + for (;;) + { + status = virtual_unwind( UNW_FLAG_EHANDLER, &dispatch, &context ); + if (status != STATUS_SUCCESS) return status; + + unwind_done: + if (!dispatch.EstablisherFrame) break; + + if (!is_valid_frame( dispatch.EstablisherFrame )) { + ERR( "invalid frame %x (%p-%p)\n", dispatch.EstablisherFrame, + NtCurrentTeb()->Tib.StackLimit, NtCurrentTeb()->Tib.StackBase ); rec->ExceptionFlags |= EH_STACK_INVALID; break; }
- /* Call handler */ - TRACE( "calling handler at %p code=%x flags=%x\n", - frame->Handler, rec->ExceptionCode, rec->ExceptionFlags ); - res = frame->Handler( rec, frame, context, &dispatch ); - TRACE( "handler at %p returned %x\n", frame->Handler, res ); - - if (frame == nested_frame) + if (dispatch.LanguageHandler) { - /* no longer nested */ - nested_frame = NULL; - rec->ExceptionFlags &= ~EH_NESTED_CALL; + switch (call_handler( rec, orig_context, &dispatch )) + { + case ExceptionContinueExecution: + if (rec->ExceptionFlags & EH_NONCONTINUABLE) return STATUS_NONCONTINUABLE_EXCEPTION; + return STATUS_SUCCESS; + case ExceptionContinueSearch: + break; + case ExceptionNestedException: + FIXME( "nested exception\n" ); + break; + case ExceptionCollidedUnwind: { + ULONG_PTR frame; + + context = *dispatch.ContextRecord; + dispatch.ContextRecord = &context; + RtlVirtualUnwind( UNW_FLAG_NHANDLER, dispatch.ImageBase, + dispatch.ControlPc, dispatch.FunctionEntry, + &context, (PVOID *)&dispatch.HandlerData, &frame, NULL ); + goto unwind_done; + } + default: + return STATUS_INVALID_DISPOSITION; + } } - - switch(res) + /* hack: call wine handlers registered in the tib list */ + else while ((DWORD)teb_frame < context.Sp) { - case ExceptionContinueExecution: - if (!(rec->ExceptionFlags & EH_NONCONTINUABLE)) return STATUS_SUCCESS; - return STATUS_NONCONTINUABLE_EXCEPTION; - case ExceptionContinueSearch: - break; - case ExceptionNestedException: - if (nested_frame < dispatch) nested_frame = dispatch; - rec->ExceptionFlags |= EH_NESTED_CALL; - break; - default: - return STATUS_INVALID_DISPOSITION; + TRACE( "found wine frame %p rsp %x handler %p\n", + teb_frame, context.Sp, teb_frame->Handler ); + dispatch.EstablisherFrame = (DWORD)teb_frame; + switch (call_teb_handler( rec, orig_context, &dispatch, teb_frame )) + { + case ExceptionContinueExecution: + if (rec->ExceptionFlags & EH_NONCONTINUABLE) return STATUS_NONCONTINUABLE_EXCEPTION; + return STATUS_SUCCESS; + case ExceptionContinueSearch: + break; + case ExceptionNestedException: + FIXME( "nested exception\n" ); + break; + case ExceptionCollidedUnwind: { + ULONG_PTR frame; + + context = *dispatch.ContextRecord; + dispatch.ContextRecord = &context; + RtlVirtualUnwind( UNW_FLAG_NHANDLER, dispatch.ImageBase, + dispatch.ControlPc, dispatch.FunctionEntry, + &context, (PVOID *)&dispatch.HandlerData, &frame, NULL ); + teb_frame = teb_frame->Prev; + goto unwind_done; + } + default: + return STATUS_INVALID_DISPOSITION; + } + teb_frame = teb_frame->Prev; } - frame = frame->Prev; + + if (context.Sp == (DWORD)NtCurrentTeb()->Tib.StackBase) break; + prev_context = context; } return STATUS_UNHANDLED_EXCEPTION; } @@ -191,7 +477,7 @@ NTSTATUS WINAPI KiUserExceptionDispatcher( EXCEPTION_RECORD *rec, CONTEXT *conte if (call_vectored_handlers( rec, context ) == EXCEPTION_CONTINUE_EXECUTION) NtContinue( context, FALSE );
- if ((status = call_stack_handlers( rec, context )) == STATUS_SUCCESS) + if ((status = call_function_handlers( rec, context )) == STATUS_SUCCESS) NtContinue( context, FALSE );
if (status != STATUS_UNHANDLED_EXCEPTION) RtlRaiseStatus( status ); @@ -745,18 +1031,116 @@ PVOID WINAPI RtlVirtualUnwind( ULONG type, ULONG_PTR base, ULONG_PTR pc, }
+/********************************************************************** + * call_consolidate_callback + * + * Wrapper function to call a consolidate callback from a fake frame. + * If the callback executes RtlUnwindEx (like for example done in C++ handlers), + * we have to skip all frames which were already processed. To do that we + * trick the unwinding functions into thinking the call came from somewhere + * else. All CFI instructions are either DW_CFA_def_cfa_expression or + * DW_CFA_expression, and the expressions have the following format: + * + * DW_OP_breg13; sleb128 <OFFSET> | Load SP + struct member offset + * [DW_OP_deref] | Dereference, only for CFA + */ +extern void * WINAPI call_consolidate_callback( CONTEXT *context, + void *(CALLBACK *callback)(EXCEPTION_RECORD *), + EXCEPTION_RECORD *rec ); +__ASM_GLOBAL_FUNC( call_consolidate_callback, + "push {r0-r2,lr}\n\t" + "sub sp, sp, #0x1a0\n\t" + "mov r1, r0\n\t" + "mov r0, sp\n\t" + "mov r2, #0x1a0\n\t" + "bl " __ASM_NAME("memcpy") "\n\t" + __ASM_CFI(".cfi_def_cfa 13, 0\n\t") + __ASM_CFI(".cfi_escape 0x0f,0x04,0x7d,0xb8,0x00,0x06\n\t") /* DW_CFA_def_cfa_expression: DW_OP_breg13 + 56, DW_OP_deref */ + __ASM_CFI(".cfi_escape 0x10,0x04,0x02,0x7d,0x14\n\t") /* DW_CFA_expression: R4 DW_OP_breg13 + 20 */ + __ASM_CFI(".cfi_escape 0x10,0x05,0x02,0x7d,0x18\n\t") /* DW_CFA_expression: R5 DW_OP_breg13 + 24 */ + __ASM_CFI(".cfi_escape 0x10,0x06,0x02,0x7d,0x1c\n\t") /* DW_CFA_expression: R6 DW_OP_breg13 + 28 */ + __ASM_CFI(".cfi_escape 0x10,0x07,0x02,0x7d,0x20\n\t") /* DW_CFA_expression: R7 DW_OP_breg13 + 32 */ + __ASM_CFI(".cfi_escape 0x10,0x08,0x02,0x7d,0x24\n\t") /* DW_CFA_expression: R8 DW_OP_breg13 + 36 */ + __ASM_CFI(".cfi_escape 0x10,0x09,0x02,0x7d,0x28\n\t") /* DW_CFA_expression: R9 DW_OP_breg13 + 40 */ + __ASM_CFI(".cfi_escape 0x10,0x0a,0x02,0x7d,0x2c\n\t") /* DW_CFA_expression: R10 DW_OP_breg13 + 44 */ + __ASM_CFI(".cfi_escape 0x10,0x0b,0x02,0x7d,0x30\n\t") /* DW_CFA_expression: R11 DW_OP_breg13 + 48 */ + __ASM_CFI(".cfi_escape 0x10,0x0e,0x03,0x7d,0xc0,0x00\n\t") /* DW_CFA_expression: LR DW_OP_breg13 + 64 (PC) */ + /* Libunwind doesn't support the registers D8-D15 like this */ +#if 0 + __ASM_CFI(".cfi_escape 0x10,0x88,0x02,0x03,0x7d,0x90,0x01\n\t") /* DW_CFA_expression: D8 DW_OP_breg13 + 144 */ + __ASM_CFI(".cfi_escape 0x10,0x89,0x02,0x03,0x7d,0x98,0x01\n\t") /* DW_CFA_expression: D9 DW_OP_breg13 + 152 */ + __ASM_CFI(".cfi_escape 0x10,0x8a,0x02,0x03,0x7d,0xa0,0x01\n\t") /* DW_CFA_expression: D10 DW_OP_breg13 + 160 */ + __ASM_CFI(".cfi_escape 0x10,0x8b,0x02,0x03,0x7d,0xa8,0x01\n\t") /* DW_CFA_expression: D11 DW_OP_breg13 + 168 */ + __ASM_CFI(".cfi_escape 0x10,0x8c,0x02,0x03,0x7d,0xb0,0x01\n\t") /* DW_CFA_expression: D12 DW_OP_breg13 + 176 */ + __ASM_CFI(".cfi_escape 0x10,0x8d,0x02,0x03,0x7d,0xb8,0x01\n\t") /* DW_CFA_expression: D13 DW_OP_breg13 + 184 */ + __ASM_CFI(".cfi_escape 0x10,0x8e,0x02,0x03,0x7d,0xc0,0x01\n\t") /* DW_CFA_expression: D14 DW_OP_breg13 + 192 */ + __ASM_CFI(".cfi_escape 0x10,0x8f,0x02,0x03,0x7d,0xc8,0x01\n\t") /* DW_CFA_expression: D15 DW_OP_breg13 + 200 */ +#endif + "ldrd r1, r2, [sp, #0x1a4]\n\t" + "mov r0, r2\n\t" + "blx r1\n\t" + "add sp, sp, #0x1ac\n\t" + "pop {pc}\n\t") + + + +/******************************************************************* + * RtlRestoreContext (NTDLL.@) + */ +void CDECL RtlRestoreContext( CONTEXT *context, EXCEPTION_RECORD *rec ) +{ + EXCEPTION_REGISTRATION_RECORD *teb_frame = NtCurrentTeb()->Tib.ExceptionList; + + if (rec && rec->ExceptionCode == STATUS_LONGJUMP && rec->NumberParameters >= 1) + { + struct MSVCRT_JUMP_BUFFER *jmp = (struct MSVCRT_JUMP_BUFFER *)rec->ExceptionInformation[0]; + int i; + + for (i = 4; i <= 11; i++) + (&context->R4)[i-4] = (&jmp->R4)[i-4]; + context->Lr = jmp->Pc; + context->Sp = jmp->Sp; + context->Fpscr = jmp->Fpscr; + + for (i = 0; i < 8; i++) + context->u.D[8+i] = jmp->D[i]; + } + else if (rec && rec->ExceptionCode == STATUS_UNWIND_CONSOLIDATE && rec->NumberParameters >= 1) + { + PVOID (CALLBACK *consolidate)(EXCEPTION_RECORD *) = (void *)rec->ExceptionInformation[0]; + TRACE( "calling consolidate callback %p (rec=%p)\n", consolidate, rec ); + rec->ExceptionInformation[10] = (ULONG_PTR)&context->R4; + + context->Pc = (DWORD)call_consolidate_callback( context, consolidate, rec ); + } + + /* hack: remove no longer accessible TEB frames */ + while ((DWORD)teb_frame < context->Sp) + { + TRACE( "removing TEB frame: %p\n", teb_frame ); + teb_frame = __wine_pop_frame( teb_frame ); + } + + TRACE( "returning to %x stack %x\n", context->Pc, context->Sp ); + NtContinue( context, FALSE ); +} + + /*********************************************************************** - * RtlUnwind (NTDLL.@) + * RtlUnwindEx (NTDLL.@) */ -void WINAPI RtlUnwind( void *endframe, void *target_ip, EXCEPTION_RECORD *rec, void *retval ) +void WINAPI RtlUnwindEx( PVOID end_frame, PVOID target_ip, EXCEPTION_RECORD *rec, + PVOID retval, CONTEXT *context, UNWIND_HISTORY_TABLE *table ) { - CONTEXT context; + EXCEPTION_REGISTRATION_RECORD *teb_frame = NtCurrentTeb()->Tib.ExceptionList; EXCEPTION_RECORD record; - EXCEPTION_REGISTRATION_RECORD *frame, *dispatch; - DWORD res; + DISPATCHER_CONTEXT dispatch; + CONTEXT new_context; + NTSTATUS status; + DWORD i;
- RtlCaptureContext( &context ); - context.R0 = (DWORD)retval; + RtlCaptureContext( context ); + new_context = *context;
/* build an exception record, if we do not have one */ if (!rec) @@ -764,44 +1148,115 @@ void WINAPI RtlUnwind( void *endframe, void *target_ip, EXCEPTION_RECORD *rec, v record.ExceptionCode = STATUS_UNWIND; record.ExceptionFlags = 0; record.ExceptionRecord = NULL; - record.ExceptionAddress = (void *)context.Pc; + record.ExceptionAddress = (void *)context->Pc; record.NumberParameters = 0; rec = &record; }
- rec->ExceptionFlags |= EH_UNWINDING | (endframe ? 0 : EH_EXIT_UNWIND); - - TRACE( "code=%x flags=%x\n", rec->ExceptionCode, rec->ExceptionFlags ); - - /* get chain of exception frames */ - frame = NtCurrentTeb()->Tib.ExceptionList; - while ((frame != (EXCEPTION_REGISTRATION_RECORD*)~0UL) && (frame != endframe)) + rec->ExceptionFlags |= EH_UNWINDING | (end_frame ? 0 : EH_EXIT_UNWIND); + + TRACE( "code=%x flags=%x end_frame=%p target_ip=%p pc=%08x\n", + rec->ExceptionCode, rec->ExceptionFlags, end_frame, target_ip, context->Pc ); + for (i = 0; i < min( EXCEPTION_MAXIMUM_PARAMETERS, rec->NumberParameters ); i++) + TRACE( " info[%d]=%016lx\n", i, rec->ExceptionInformation[i] ); + TRACE(" r0=%08x r1=%08x r2=%08x r3=%08x\n", + context->R0, context->R1, context->R2, context->R3 ); + TRACE(" r4=%08x r5=%08x r6=%08x r7=%08x\n", + context->R4, context->R5, context->R6, context->R7 ); + TRACE(" r8=%08x r9=%08x r10=%08x r11=%08x\n", + context->R8, context->R9, context->R10, context->R11 ); + TRACE(" r12=%08x sp=%08x lr=%08x pc=%08x\n", + context->R12, context->Sp, context->Lr, context->Pc ); + + dispatch.TargetPc = (ULONG_PTR)target_ip; + dispatch.ContextRecord = context; + dispatch.HistoryTable = table; + dispatch.NonVolatileRegisters = (BYTE *)&context->R4; + + for (;;) { - /* Check frame address */ - if (endframe && ((void*)frame > endframe)) - raise_status( STATUS_INVALID_UNWIND_TARGET, rec ); + status = virtual_unwind( UNW_FLAG_UHANDLER, &dispatch, &new_context ); + if (status != STATUS_SUCCESS) raise_status( status, rec );
- if (!is_valid_frame( frame )) raise_status( STATUS_BAD_STACK, rec ); + unwind_done: + if (!dispatch.EstablisherFrame) break;
- /* Call handler */ - TRACE( "calling handler at %p code=%x flags=%x\n", - frame->Handler, rec->ExceptionCode, rec->ExceptionFlags ); - res = frame->Handler(rec, frame, &context, &dispatch); - TRACE( "handler at %p returned %x\n", frame->Handler, res ); - - switch(res) + if (!is_valid_frame( dispatch.EstablisherFrame )) { - case ExceptionContinueSearch: - break; - case ExceptionCollidedUnwind: - frame = dispatch; - break; - default: - raise_status( STATUS_INVALID_DISPOSITION, rec ); + ERR( "invalid frame %x (%p-%p)\n", dispatch.EstablisherFrame, + NtCurrentTeb()->Tib.StackLimit, NtCurrentTeb()->Tib.StackBase ); + rec->ExceptionFlags |= EH_STACK_INVALID; break; } - frame = __wine_pop_frame( frame ); + + if (dispatch.LanguageHandler) + { + if (end_frame && (dispatch.EstablisherFrame > (DWORD)end_frame)) + { + ERR( "invalid end frame %x/%p\n", dispatch.EstablisherFrame, end_frame ); + raise_status( STATUS_INVALID_UNWIND_TARGET, rec ); + } + if (dispatch.EstablisherFrame == (DWORD)end_frame) rec->ExceptionFlags |= EH_TARGET_UNWIND; + if (call_unwind_handler( rec, &dispatch ) == ExceptionCollidedUnwind) + { + ULONG_PTR frame; + + *context = new_context = *dispatch.ContextRecord; + dispatch.ContextRecord = context; + RtlVirtualUnwind( UNW_FLAG_NHANDLER, dispatch.ImageBase, + dispatch.ControlPc, dispatch.FunctionEntry, + &new_context, &dispatch.HandlerData, &frame, + NULL ); + rec->ExceptionFlags |= EH_COLLIDED_UNWIND; + goto unwind_done; + } + rec->ExceptionFlags &= ~EH_COLLIDED_UNWIND; + } + else /* hack: call builtin handlers registered in the tib list */ + { + DWORD64 backup_frame = dispatch.EstablisherFrame; + while ((DWORD)teb_frame < new_context.Sp && (DWORD)teb_frame < (DWORD)end_frame) + { + TRACE( "found builtin frame %p handler %p\n", teb_frame, teb_frame->Handler ); + dispatch.EstablisherFrame = (DWORD)teb_frame; + if (call_teb_unwind_handler( rec, &dispatch, teb_frame ) == ExceptionCollidedUnwind) + { + ULONG_PTR frame; + + teb_frame = __wine_pop_frame( teb_frame ); + + *context = new_context = *dispatch.ContextRecord; + dispatch.ContextRecord = context; + RtlVirtualUnwind( UNW_FLAG_NHANDLER, dispatch.ImageBase, + dispatch.ControlPc, dispatch.FunctionEntry, + &new_context, &dispatch.HandlerData, + &frame, NULL ); + rec->ExceptionFlags |= EH_COLLIDED_UNWIND; + goto unwind_done; + } + teb_frame = __wine_pop_frame( teb_frame ); + } + if ((DWORD)teb_frame == (DWORD)end_frame && (DWORD)end_frame < new_context.Sp) break; + dispatch.EstablisherFrame = backup_frame; + } + + if (dispatch.EstablisherFrame == (DWORD)end_frame) break; + *context = new_context; } + + context->R0 = (DWORD)retval; + context->Pc = (DWORD)target_ip; + RtlRestoreContext(context, rec); +} + + +/*********************************************************************** + * RtlUnwind (NTDLL.@) + */ +void WINAPI RtlUnwind( void *frame, void *target_ip, EXCEPTION_RECORD *rec, void *retval ) +{ + CONTEXT context; + RtlUnwindEx( frame, target_ip, rec, retval, &context, NULL ); }
@@ -811,6 +1266,8 @@ void WINAPI RtlUnwind( void *endframe, void *target_ip, EXCEPTION_RECORD *rec, v __ASM_STDCALL_FUNC( RtlRaiseException, 4, "push {r0, lr}\n\t" "sub sp, sp, #0x1a0\n\t" /* sizeof(CONTEXT) */ + __ASM_CFI(".cfi_adjust_cfa_offset 424\n\t") + __ASM_CFI(".cfi_offset lr, -4\n\t") "mov r0, sp\n\t" /* context */ "bl " __ASM_NAME("RtlCaptureContext") "\n\t" "ldr r0, [sp, #0x1a0]\n\t" /* rec */ diff --git a/dlls/ntdll/unix/signal_arm.c b/dlls/ntdll/unix/signal_arm.c index 1cc34a9218e..fc37f917219 100644 --- a/dlls/ntdll/unix/signal_arm.c +++ b/dlls/ntdll/unix/signal_arm.c @@ -51,6 +51,10 @@ #ifdef HAVE_SYS_UCONTEXT_H # include <sys/ucontext.h> #endif +#ifdef HAVE_LIBUNWIND +# define UNW_LOCAL_ONLY +# include <libunwind.h> +#endif
#define NONAMELESSUNION #define NONAMELESSSTRUCT @@ -217,13 +221,96 @@ static BOOL is_inside_syscall( ucontext_t *sigcontext ) (char *)SP_sig(sigcontext) <= (char *)arm_thread_data()->syscall_frame); }
+extern void raise_func_trampoline( EXCEPTION_RECORD *rec, CONTEXT *context, void *dispatcher );
/*********************************************************************** * unwind_builtin_dll */ NTSTATUS CDECL unwind_builtin_dll( ULONG type, struct _DISPATCHER_CONTEXT *dispatch, CONTEXT *context ) { - return STATUS_UNSUCCESSFUL; +#ifdef HAVE_LIBUNWIND + DWORD ip = context->Pc; + unw_context_t unw_context; + unw_cursor_t cursor; + unw_proc_info_t info; + int rc, i; + + for (i = 0; i <= 12; i++) + unw_context.regs[i] = (&context->R0)[i]; + unw_context.regs[13] = context->Sp; + unw_context.regs[14] = context->Lr; + unw_context.regs[15] = context->Pc; + rc = unw_init_local( &cursor, &unw_context ); + + if (rc != UNW_ESUCCESS) + { + WARN( "setup failed: %d\n", rc ); + return STATUS_INVALID_DISPOSITION; + } + rc = unw_get_proc_info( &cursor, &info ); + if (rc != UNW_ESUCCESS && rc != -UNW_ENOINFO) + { + WARN( "failed to get info: %d\n", rc ); + return STATUS_INVALID_DISPOSITION; + } + if (rc == -UNW_ENOINFO || ip < info.start_ip || ip > info.end_ip) + { + TRACE( "no info found for %x ip %x-%x, assuming leaf function\n", + ip, info.start_ip, info.end_ip ); + dispatch->LanguageHandler = NULL; + dispatch->EstablisherFrame = context->Sp; + context->Pc = context->Lr; + context->ContextFlags |= CONTEXT_UNWOUND_TO_CALL; + return STATUS_SUCCESS; + } + + TRACE( "ip %#x function %#lx-%#lx personality %#lx lsda %#lx fde %#lx\n", + ip, (unsigned long)info.start_ip, (unsigned long)info.end_ip, (unsigned long)info.handler, + (unsigned long)info.lsda, (unsigned long)info.unwind_info ); + + rc = unw_step( &cursor ); + if (rc < 0) + { + WARN( "failed to unwind: %d %d\n", rc, UNW_ENOINFO ); + return STATUS_INVALID_DISPOSITION; + } + + dispatch->LanguageHandler = (void *)info.handler; + dispatch->HandlerData = (void *)info.lsda; + dispatch->EstablisherFrame = context->Sp; + + for (i = 0; i <= 12; i++) + unw_get_reg( &cursor, UNW_ARM_R0 + i, (unw_word_t *)&(&context->R0)[i] ); + unw_get_reg( &cursor, UNW_ARM_R13, (unw_word_t *)&context->Sp ); + unw_get_reg( &cursor, UNW_ARM_R14, (unw_word_t *)&context->Lr ); + unw_get_reg( &cursor, UNW_REG_IP, (unw_word_t *)&context->Pc ); + context->ContextFlags |= CONTEXT_UNWOUND_TO_CALL; + + if ((info.start_ip & ~(unw_word_t)1) == + ((unw_word_t)raise_func_trampoline & ~(unw_word_t)1)) { + /* raise_func_trampoline stores the original Lr at the bottom of the + * stack. The unwinder normally can't restore both Pc and Lr to + * individual values, thus do that manually here. + * (The function we unwind to might be a leaf function that hasn't + * backed up its own original Lr value on the stack.) */ + const DWORD *orig_lr = (const DWORD *) dispatch->EstablisherFrame; + context->Lr = *orig_lr; + } + + TRACE( "next function pc=%08x%s\n", context->Pc, rc ? "" : " (last frame)" ); + TRACE(" r0=%08x r1=%08x r2=%08x r3=%08x\n", + context->R0, context->R1, context->R2, context->R3 ); + TRACE(" r4=%08x r5=%08x r6=%08x r7=%08x\n", + context->R4, context->R5, context->R6, context->R7 ); + TRACE(" r8=%08x r9=%08x r10=%08x r11=%08x\n", + context->R8, context->R9, context->R10, context->R11 ); + TRACE(" r12=%08x sp=%08x lr=%08x pc=%08x\n", + context->R12, context->Sp, context->Lr, context->Pc ); + return STATUS_SUCCESS; +#else + ERR("libunwind not available, unable to unwind\n"); + return STATUS_INVALID_DISPOSITION; +#endif }
@@ -467,6 +554,20 @@ NTSTATUS get_thread_wow64_context( HANDLE handle, void *ctx, ULONG size ) }
+__ASM_GLOBAL_FUNC( raise_func_trampoline, + "push {r12,lr}\n\t" /* (Padding +) Pc in the original frame */ + "ldr r3, [r1, #0x38]\n\t" /* context->Sp */ + "push {r3}\n\t" /* Original Sp */ + __ASM_CFI(".cfi_escape 0x0f,0x03,0x7D,0x04,0x06\n\t") /* CFA, DW_OP_breg13 + 0x04, DW_OP_deref */ + __ASM_CFI(".cfi_escape 0x10,0x0e,0x02,0x7D,0x0c\n\t") /* LR, DW_OP_breg13 + 0x0c */ + /* We can't express restoring both Pc and Lr with CFI + * directives, but we manually load Lr from the stack + * in unwind_builtin_dll above. */ + "ldr r3, [r1, #0x3c]\n\t" /* context->Lr */ + "push {r3}\n\t" /* Original Lr */ + "blx r2\n\t" + "udf #0") + /*********************************************************************** * setup_exception * @@ -500,11 +601,13 @@ static void setup_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec )
/* now modify the sigcontext to return to the raise function */ SP_sig(sigcontext) = (DWORD)stack; - PC_sig(sigcontext) = (DWORD)pKiUserExceptionDispatcher; + LR_sig(sigcontext) = context.Pc; + PC_sig(sigcontext) = (DWORD)raise_func_trampoline; if (PC_sig(sigcontext) & 1) CPSR_sig(sigcontext) |= 0x20; else CPSR_sig(sigcontext) &= ~0x20; REGn_sig(0, sigcontext) = (DWORD)&stack->rec; /* first arg for KiUserExceptionDispatcher */ REGn_sig(1, sigcontext) = (DWORD)&stack->context; /* second arg for KiUserExceptionDispatcher */ + REGn_sig(2, sigcontext) = (DWORD)pKiUserExceptionDispatcher; }
Signed-off-by: Martin Storsjö martin@martin.st --- .../api-ms-win-crt-private-l1-1-0.spec | 3 +- dlls/ntdll/ntdll.spec | 3 +- dlls/ntdll/signal_arm.c | 145 ++++++++++++++++++ dlls/ucrtbase/ucrtbase.spec | 3 +- dlls/vcruntime140/vcruntime140.spec | 3 +- 5 files changed, 153 insertions(+), 4 deletions(-)
diff --git a/dlls/api-ms-win-crt-private-l1-1-0/api-ms-win-crt-private-l1-1-0.spec b/dlls/api-ms-win-crt-private-l1-1-0/api-ms-win-crt-private-l1-1-0.spec index ea6cfa2a7f9..3366a435966 100644 --- a/dlls/api-ms-win-crt-private-l1-1-0/api-ms-win-crt-private-l1-1-0.spec +++ b/dlls/api-ms-win-crt-private-l1-1-0/api-ms-win-crt-private-l1-1-0.spec @@ -14,7 +14,7 @@ @ cdecl __AdjustPointer(ptr ptr) ucrtbase.__AdjustPointer @ stub __BuildCatchObject @ stub __BuildCatchObjectHelper -@ stdcall -arch=x86_64,arm64 __C_specific_handler(ptr long ptr ptr) ucrtbase.__C_specific_handler +@ stdcall -arch=x86_64,arm,arm64 __C_specific_handler(ptr long ptr ptr) ucrtbase.__C_specific_handler @ stub __C_specific_handler_noexcept @ cdecl -arch=i386,x86_64,arm,arm64 __CxxDetectRethrow(ptr) ucrtbase.__CxxDetectRethrow @ cdecl -arch=i386,x86_64,arm,arm64 __CxxExceptionFilter(ptr ptr long ptr) ucrtbase.__CxxExceptionFilter @@ -41,6 +41,7 @@ @ cdecl __intrinsic_abnormal_termination() ucrtbase.__intrinsic_abnormal_termination @ cdecl -arch=i386,x86_64,arm,arm64 -norelay __intrinsic_setjmp(ptr) ucrtbase.__intrinsic_setjmp @ cdecl -arch=x86_64,arm64 -norelay __intrinsic_setjmpex(ptr ptr) ucrtbase.__intrinsic_setjmpex +@ stdcall -arch=arm __jump_unwind(ptr ptr) ucrtbase.__jump_unwind @ cdecl __processing_throw() ucrtbase.__processing_throw @ stub __report_gsfailure @ cdecl __std_exception_copy(ptr ptr) ucrtbase.__std_exception_copy diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec index c703219cfbb..e7c1fa1dcf3 100644 --- a/dlls/ntdll/ntdll.spec +++ b/dlls/ntdll/ntdll.spec @@ -1464,11 +1464,12 @@ @ cdecl -private -arch=i386 _CIpow() @ cdecl -private -arch=i386 _CIsin() @ cdecl -private -arch=i386 _CIsqrt() -@ stdcall -arch=x86_64,arm64 __C_specific_handler(ptr long ptr ptr) +@ stdcall -arch=x86_64,arm,arm64 __C_specific_handler(ptr long ptr ptr) @ cdecl -arch=arm,arm64,x86_64 -norelay __chkstk() @ cdecl __isascii(long) @ cdecl __iscsym(long) @ cdecl __iscsymf(long) +@ stdcall -arch=arm __jump_unwind(ptr ptr) @ cdecl __toascii(long) @ cdecl -norelay -arch=i386 -ret64 _alldiv(int64 int64) @ cdecl -arch=i386 -norelay _alldvrm(int64 int64) diff --git a/dlls/ntdll/signal_arm.c b/dlls/ntdll/signal_arm.c index a24cac97df7..72f3b023e45 100644 --- a/dlls/ntdll/signal_arm.c +++ b/dlls/ntdll/signal_arm.c @@ -37,6 +37,18 @@
WINE_DEFAULT_DEBUG_CHANNEL(seh);
+typedef struct _SCOPE_TABLE +{ + ULONG Count; + struct + { + ULONG BeginAddress; + ULONG EndAddress; + ULONG HandlerAddress; + ULONG JumpTarget; + } ScopeRecord[1]; +} SCOPE_TABLE, *PSCOPE_TABLE; +
/* layering violation: the setjmp buffer is defined in msvcrt, but used by RtlUnwindEx */ struct MSVCRT_JUMP_BUFFER @@ -56,6 +68,20 @@ struct MSVCRT_JUMP_BUFFER unsigned long long D[8]; };
+ +static void dump_scope_table( ULONG64 base, const SCOPE_TABLE *table ) +{ + unsigned int i; + + TRACE( "scope table at %p\n", table ); + for (i = 0; i < table->Count; i++) + TRACE( " %u: %lx-%lx handler %lx target %lx\n", i, + base + table->ScopeRecord[i].BeginAddress, + base + table->ScopeRecord[i].EndAddress, + base + table->ScopeRecord[i].HandlerAddress, + base + table->ScopeRecord[i].JumpTarget ); +} + /******************************************************************* * is_valid_frame */ @@ -1260,6 +1286,125 @@ void WINAPI RtlUnwind( void *frame, void *target_ip, EXCEPTION_RECORD *rec, void }
+/******************************************************************* + * __jump_unwind (NTDLL.@) + */ +void WINAPI __jump_unwind( void *frame, void *target_ip ) +{ + CONTEXT context; + RtlUnwindEx( frame, target_ip, NULL, NULL, &context, NULL ); +} + +extern LONG __C_ExecuteExceptionFilter(PEXCEPTION_POINTERS ptrs, PVOID frame, + PEXCEPTION_FILTER filter, + PUCHAR nonvolatile); +__ASM_GLOBAL_FUNC( __C_ExecuteExceptionFilter, + "push {r4-r11,lr}\n\t" + + __ASM_CFI(".cfi_def_cfa 13, 36\n\t") + __ASM_CFI(".cfi_offset r4, -36\n\t") + __ASM_CFI(".cfi_offset r5, -32\n\t") + __ASM_CFI(".cfi_offset r6, -28\n\t") + __ASM_CFI(".cfi_offset r7, -24\n\t") + __ASM_CFI(".cfi_offset r8, -20\n\t") + __ASM_CFI(".cfi_offset r9, -16\n\t") + __ASM_CFI(".cfi_offset r10, -12\n\t") + __ASM_CFI(".cfi_offset r11, -8\n\t") + __ASM_CFI(".cfi_offset lr, -4\n\t") + + "ldm r3, {r4-r11,lr}\n\t" + "blx r2\n\t" + "pop {r4-r11,pc}\n\t" ) + +extern void __C_ExecuteTerminationHandler(BOOL abnormal, PVOID frame, + PTERMINATION_HANDLER handler, + PUCHAR nonvolatile); +/* This is, implementation wise, identical to __C_ExecuteExceptionFilter. */ +__ASM_GLOBAL_FUNC( __C_ExecuteTerminationHandler, + "b " __ASM_NAME("__C_ExecuteExceptionFilter") "\n\t"); + +/******************************************************************* + * __C_specific_handler (NTDLL.@) + */ +EXCEPTION_DISPOSITION WINAPI __C_specific_handler( EXCEPTION_RECORD *rec, + void *frame, + CONTEXT *context, + struct _DISPATCHER_CONTEXT *dispatch ) +{ + SCOPE_TABLE *table = dispatch->HandlerData; + ULONG i; + DWORD64 ControlPc = dispatch->ControlPc; + + TRACE( "%p %p %p %p\n", rec, frame, context, dispatch ); + if (TRACE_ON(seh)) dump_scope_table( dispatch->ImageBase, table ); + + if (dispatch->ControlPcIsUnwound) + ControlPc -= 2; + + if (rec->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND)) + { + for (i = dispatch->ScopeIndex; i < table->Count; i++) + { + if (ControlPc >= dispatch->ImageBase + table->ScopeRecord[i].BeginAddress && + ControlPc < dispatch->ImageBase + table->ScopeRecord[i].EndAddress) + { + PTERMINATION_HANDLER handler; + + if (table->ScopeRecord[i].JumpTarget) continue; + + if (rec->ExceptionFlags & EH_TARGET_UNWIND && + dispatch->TargetPc >= dispatch->ImageBase + table->ScopeRecord[i].BeginAddress && + dispatch->TargetPc < dispatch->ImageBase + table->ScopeRecord[i].EndAddress) + { + break; + } + + handler = (PTERMINATION_HANDLER)(dispatch->ImageBase + table->ScopeRecord[i].HandlerAddress); + dispatch->ScopeIndex = i+1; + + TRACE( "calling __finally %p frame %p\n", handler, frame ); + __C_ExecuteTerminationHandler( TRUE, frame, handler, + dispatch->NonVolatileRegisters ); + } + } + return ExceptionContinueSearch; + } + + for (i = dispatch->ScopeIndex; i < table->Count; i++) + { + if (ControlPc >= dispatch->ImageBase + table->ScopeRecord[i].BeginAddress && + ControlPc < dispatch->ImageBase + table->ScopeRecord[i].EndAddress) + { + if (!table->ScopeRecord[i].JumpTarget) continue; + if (table->ScopeRecord[i].HandlerAddress != EXCEPTION_EXECUTE_HANDLER) + { + EXCEPTION_POINTERS ptrs; + PEXCEPTION_FILTER filter; + + filter = (PEXCEPTION_FILTER)(dispatch->ImageBase + table->ScopeRecord[i].HandlerAddress); + ptrs.ExceptionRecord = rec; + ptrs.ContextRecord = context; + TRACE( "calling filter %p ptrs %p frame %p\n", filter, &ptrs, frame ); + switch (__C_ExecuteExceptionFilter( &ptrs, frame, filter, + dispatch->NonVolatileRegisters )) + { + case EXCEPTION_EXECUTE_HANDLER: + break; + case EXCEPTION_CONTINUE_SEARCH: + continue; + case EXCEPTION_CONTINUE_EXECUTION: + return ExceptionContinueExecution; + } + } + TRACE( "unwinding to target %lx\n", dispatch->ImageBase + table->ScopeRecord[i].JumpTarget ); + RtlUnwindEx( frame, (char *)dispatch->ImageBase + table->ScopeRecord[i].JumpTarget, + rec, 0, dispatch->ContextRecord, dispatch->HistoryTable ); + } + } + return ExceptionContinueSearch; +} + + /*********************************************************************** * RtlRaiseException (NTDLL.@) */ diff --git a/dlls/ucrtbase/ucrtbase.spec b/dlls/ucrtbase/ucrtbase.spec index d39387d5f00..831bff9eee1 100644 --- a/dlls/ucrtbase/ucrtbase.spec +++ b/dlls/ucrtbase/ucrtbase.spec @@ -48,7 +48,7 @@ @ cdecl __AdjustPointer(ptr ptr) @ stub __BuildCatchObject @ stub __BuildCatchObjectHelper -@ stdcall -arch=x86_64,arm64 __C_specific_handler(ptr long ptr ptr) ntdll.__C_specific_handler +@ stdcall -arch=x86_64,arm,arm64 __C_specific_handler(ptr long ptr ptr) ntdll.__C_specific_handler @ cdecl -arch=i386,x86_64,arm,arm64 __CxxDetectRethrow(ptr) @ cdecl -arch=i386,x86_64,arm,arm64 __CxxExceptionFilter(ptr ptr long ptr) @ cdecl -arch=i386,x86_64,arm,arm64 -norelay __CxxFrameHandler(ptr ptr ptr ptr) @@ -100,6 +100,7 @@ @ cdecl __iscsymf(long) @ stub __iswcsym @ stub __iswcsymf +@ stdcall -arch=arm __jump_unwind(ptr ptr) ntdll.__jump_unwind @ cdecl -arch=i386 -norelay __libm_sse2_acos() @ cdecl -arch=i386 -norelay __libm_sse2_acosf() @ cdecl -arch=i386 -norelay __libm_sse2_asin() diff --git a/dlls/vcruntime140/vcruntime140.spec b/dlls/vcruntime140/vcruntime140.spec index 9ca10cc2a70..7bbb37afcec 100644 --- a/dlls/vcruntime140/vcruntime140.spec +++ b/dlls/vcruntime140/vcruntime140.spec @@ -10,7 +10,7 @@ @ cdecl __AdjustPointer(ptr ptr) ucrtbase.__AdjustPointer @ stub __BuildCatchObject @ stub __BuildCatchObjectHelper -@ stdcall -arch=x86_64,arm64 __C_specific_handler(ptr long ptr ptr) ucrtbase.__C_specific_handler +@ stdcall -arch=x86_64,arm,arm64 __C_specific_handler(ptr long ptr ptr) ucrtbase.__C_specific_handler @ stub __C_specific_handler_noexcept @ cdecl -arch=i386,x86_64,arm,arm64 __CxxDetectRethrow(ptr) ucrtbase.__CxxDetectRethrow @ cdecl -arch=i386,x86_64,arm,arm64 __CxxExceptionFilter(ptr ptr long ptr) ucrtbase.__CxxExceptionFilter @@ -34,6 +34,7 @@ @ cdecl __current_exception_context() ucrtbase.__current_exception_context @ cdecl -arch=i386,x86_64,arm,arm64 -norelay __intrinsic_setjmp(ptr) ucrtbase.__intrinsic_setjmp @ cdecl -arch=x86_64,arm64 -norelay __intrinsic_setjmpex(ptr ptr) ucrtbase.__intrinsic_setjmpex +@ stdcall -arch=arm __jump_unwind(ptr ptr) ucrtbase.__jump_unwind @ cdecl __processing_throw() ucrtbase.__processing_throw @ stub __report_gsfailure @ cdecl __std_exception_copy(ptr ptr) ucrtbase.__std_exception_copy
Signed-off-by: Martin Storsjö martin@martin.st --- .../api-ms-win-crt-private-l1-1-0.spec | 2 +- dlls/ucrtbase/ucrtbase.spec | 2 +- dlls/vcruntime140/vcruntime140.spec | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/dlls/api-ms-win-crt-private-l1-1-0/api-ms-win-crt-private-l1-1-0.spec b/dlls/api-ms-win-crt-private-l1-1-0/api-ms-win-crt-private-l1-1-0.spec index 3366a435966..4fa47035afe 100644 --- a/dlls/api-ms-win-crt-private-l1-1-0/api-ms-win-crt-private-l1-1-0.spec +++ b/dlls/api-ms-win-crt-private-l1-1-0/api-ms-win-crt-private-l1-1-0.spec @@ -40,7 +40,7 @@ @ stub __dcrt_initial_narrow_environment @ cdecl __intrinsic_abnormal_termination() ucrtbase.__intrinsic_abnormal_termination @ cdecl -arch=i386,x86_64,arm,arm64 -norelay __intrinsic_setjmp(ptr) ucrtbase.__intrinsic_setjmp -@ cdecl -arch=x86_64,arm64 -norelay __intrinsic_setjmpex(ptr ptr) ucrtbase.__intrinsic_setjmpex +@ cdecl -arch=x86_64,arm,arm64 -norelay __intrinsic_setjmpex(ptr ptr) ucrtbase.__intrinsic_setjmpex @ stdcall -arch=arm __jump_unwind(ptr ptr) ucrtbase.__jump_unwind @ cdecl __processing_throw() ucrtbase.__processing_throw @ stub __report_gsfailure diff --git a/dlls/ucrtbase/ucrtbase.spec b/dlls/ucrtbase/ucrtbase.spec index 831bff9eee1..1838f31d515 100644 --- a/dlls/ucrtbase/ucrtbase.spec +++ b/dlls/ucrtbase/ucrtbase.spec @@ -94,7 +94,7 @@ @ cdecl __initialize_lconv_for_unsigned_char() __lconv_init @ cdecl __intrinsic_abnormal_termination() @ cdecl -arch=i386,x86_64,arm,arm64 -norelay __intrinsic_setjmp(ptr) MSVCRT__setjmp -@ cdecl -arch=x86_64,arm64 -norelay __intrinsic_setjmpex(ptr ptr) __wine_setjmpex +@ cdecl -arch=x86_64,arm,arm64 -norelay __intrinsic_setjmpex(ptr ptr) __wine_setjmpex @ cdecl __isascii(long) @ cdecl __iscsym(long) @ cdecl __iscsymf(long) diff --git a/dlls/vcruntime140/vcruntime140.spec b/dlls/vcruntime140/vcruntime140.spec index 7bbb37afcec..074578efe99 100644 --- a/dlls/vcruntime140/vcruntime140.spec +++ b/dlls/vcruntime140/vcruntime140.spec @@ -33,7 +33,7 @@ @ cdecl __current_exception() ucrtbase.__current_exception @ cdecl __current_exception_context() ucrtbase.__current_exception_context @ cdecl -arch=i386,x86_64,arm,arm64 -norelay __intrinsic_setjmp(ptr) ucrtbase.__intrinsic_setjmp -@ cdecl -arch=x86_64,arm64 -norelay __intrinsic_setjmpex(ptr ptr) ucrtbase.__intrinsic_setjmpex +@ cdecl -arch=x86_64,arm,arm64 -norelay __intrinsic_setjmpex(ptr ptr) ucrtbase.__intrinsic_setjmpex @ stdcall -arch=arm __jump_unwind(ptr ptr) ucrtbase.__jump_unwind @ cdecl __processing_throw() ucrtbase.__processing_throw @ stub __report_gsfailure
This matches what was done for x86_64 in 882980c17a9a033fa8e49a4c116af9583698d218.
Signed-off-by: Martin Storsjö martin@martin.st --- dlls/msvcrt/except_arm.c | 1 - dlls/msvcrt/except_arm64.c | 1 - 2 files changed, 2 deletions(-)
diff --git a/dlls/msvcrt/except_arm.c b/dlls/msvcrt/except_arm.c index 8e5c552b552..fecbfe65929 100644 --- a/dlls/msvcrt/except_arm.c +++ b/dlls/msvcrt/except_arm.c @@ -109,7 +109,6 @@ unsigned int CDECL __CxxQueryExceptionSize(void) * _setjmp (MSVCRT.@) */ __ASM_GLOBAL_FUNC(MSVCRT__setjmp, - "mov r1, #0\n\t" /* frame */ "b " __ASM_NAME("__wine_setjmpex"));
/******************************************************************* diff --git a/dlls/msvcrt/except_arm64.c b/dlls/msvcrt/except_arm64.c index 242ef8bdb09..574e77485f9 100644 --- a/dlls/msvcrt/except_arm64.c +++ b/dlls/msvcrt/except_arm64.c @@ -110,7 +110,6 @@ unsigned int CDECL __CxxQueryExceptionSize(void) * _setjmp (MSVCRT.@) */ __ASM_GLOBAL_FUNC(MSVCRT__setjmp, - "mov x1, #0\n\t" /* frame */ "b " __ASM_NAME("__wine_setjmpex"));
/*******************************************************************