[Bug 46189] New: [Bug 46187] Windows PowerShell Core 6.2 Preview 2 for ARM32 crashes due to ntdll ' set_cpu_context' not restoring Thumb mode during return from exception handling
wine-bugs at winehq.org
wine-bugs at winehq.org
Sat Nov 24 19:51:23 CST 2018
https://bugs.winehq.org/show_bug.cgi?id=46189
Bug ID: 46189
Summary: [Bug 46187] Windows PowerShell Core 6.2 Preview 2 for
ARM32 crashes due to ntdll 'set_cpu_context' not
restoring Thumb mode during return from exception
handling
Product: Wine
Version: 3.21
Hardware: arm
OS: Linux
Status: NEW
Severity: normal
Priority: P2
Component: ntdll
Assignee: wine-bugs at winehq.org
Reporter: focht at gmx.net
Distribution: ---
Hello folks,
the continuation of bug 46187("Windows PowerShell Core 6.2 Preview 2 for ARM32
crashes due to unhandled trap_no 0 (write watch access causes SIGSEGV)")
Even with trap code 0 properly translated it still crashes.
Debugger session (with fixup for bug #46187 applied).
--- snip ---
$ gdb wine
GNU gdb (GDB) 8.2
...
Reading symbols from wine...done.
(gdb) run pwsh.exe
Starting program: /home/focht/projects/wine/mainline-install-arm/bin/wine
pwsh.exe
...
Thread 1 "pwsh.exe" hit Breakpoint 1, virtual_handle_fault (addr=0xf3ce0000,
err=1, on_signal_stack=0) at
/home/focht/projects/wine/mainline-src/dlls/ntdll/virtual.c:2010
2010 NTSTATUS ret = STATUS_ACCESS_VIOLATION;
(gdb) bt
#0 virtual_handle_fault (addr=0xf3ce0000, err=1, on_signal_stack=0) at
/home/focht/projects/wine/mainline-src/dlls/ntdll/virtual.c:2010
#1 0xf7c612ec in raise_segv_exception (rec=0xf73ce9f8, context=0xf73ce858) at
/home/focht/projects/wine/mainline-src/dlls/ntdll/signal_arm.c:574
#2 0xf7c61152 in raise_func_trampoline_thumb () at
/home/focht/projects/wine/mainline-src/dlls/ntdll/signal_arm.c:508
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
(gdb) info locals
ret = 0xc0000005
page = 0xf3ce0000
sigset = {__val = {0x0, 0x0, 0xf7c61150, 0x600f0030, 0xf3ce0000, 0x0, 0x0,
0xf3ce0000, 0xf7ccdd34, 0xa0000, 0xffffffff, 0xf6d4a5e4, 0xf7ddf7a9,
0xf73ce82c, 0xf7c7f6c0, 0x0, 0x1,
0x11000, 0xf3ce0000, 0x43, 0x1, 0xf73ce834, 0xf7c7fd20, 0xffffffff, 0xa5e4,
0x11000, 0xf3ce0000, 0xb0000, 0x1, 0x1, 0xf3ce0000, 0x11}}
vprot = 0x63
...
(gdb) n
2027 set_page_vprot_bits( page, page_size, 0, VPROT_WRITEWATCH
);
(gdb) n
2028 mprotect_range( page, page_size, 0, 0 );
(gdb) n
2031 if (VIRTUAL_GetUnixProt( get_page_vprot( page )) & PROT_WRITE)
(gdb) n
2033 if ((vprot & VPROT_WRITEWATCH) || is_write_watch_range(
page, page_size ))
(gdb) n
2034 ret = STATUS_SUCCESS;
...
--- snip ---
The problem is the way Wine restores the context on ARM32 via
'set_cpu_context':
--- snip ---
Dump of assembler code for function set_cpu_context:
0xf7c605c0 <+0>: ldr r1, [r0, #68] ; 0x44
=> 0xf7c605c4 <+4>: msr CPSR_f, r1
0xf7c605c8 <+8>: ldr r1, [r0, #64] ; 0x40
0xf7c605cc <+12>: ldr lr, [r0, #60] ; 0x3c
0xf7c605d0 <+16>: ldr sp, [r0, #56] ; 0x38
0xf7c605d4 <+20>: push {r1} ; (str r1, [sp, #-4]!)
0xf7c605d8 <+24>: ldmib r0, {r0, r1, r2, r3, r4, r5, r6, r7, r8, r9,
r10, r11, r12}
0xf7c605dc <+28>: pop {pc} ; (ldr pc, [sp], #4)
(gdb) info reg
r0 0xf73ce858 0xf73ce858
r1 0x602f0030 0x602f0030
r2 0x0 0x0
r3 0x0 0x0
r4 0xf74baee0 0xf74baee0
r5 0x90068 0x90068
r6 0x0 0x0
r7 0xf6d4a5e4 0xf6d4a5e4
r8 0xf3ce0000 0xf3ce0000
r9 0xf6e78500 0xf6e78500
r10 0xf6d51268 0xf6d51268
r11 0xf73ce854 0xf73ce854
r12 0xaf 0xaf
sp 0xf73ce840 0xf73ce840
lr 0xf7c61350 0xf7c61350
pc 0xf7c605c4 0xf7c605c4 <set_cpu_context+4>
cpsr 0x600f0010 0x600f0010
Unable to fetch SVE register header: Invalid argument.
--- snip ---
CPSR = 0x600f0010 = Wine ARM32 mode
R1 = old CPSR before fault = 0x602f0030 = app code in Thumb mode
Wine -> CPSR_f = only flag bits set (execution state/control bits can't be set
explicitly in USR mode by design, no USR SPSR_xxx).
--- snip ---
(gdb) disas
Dump of assembler code for function set_cpu_context:
0xf7c605c0 <+0>: ldr r1, [r0, #68] ; 0x44
0xf7c605c4 <+4>: msr CPSR_f, r1
0xf7c605c8 <+8>: ldr r1, [r0, #64] ; 0x40
0xf7c605cc <+12>: ldr lr, [r0, #60] ; 0x3c
0xf7c605d0 <+16>: ldr sp, [r0, #56] ; 0x38
0xf7c605d4 <+20>: push {r1} ; (str r1, [sp, #-4]!)
0xf7c605d8 <+24>: ldmib r0, {r0, r1, r2, r3, r4, r5, r6, r7, r8, r9,
r10, r11, r12}
=> 0xf7c605dc <+28>: pop {pc} ; (ldr pc, [sp], #4)
End of assembler dump.
(gdb) si
0xf6c5160c in ?? ()
(gdb) info reg
r0 0xf3ce0020 0xf3ce0020
r1 0xf73ce944 0xf73ce944
r2 0x11000 0x11000
r3 0xf3d90000 0xf3d90000
r4 0xf74baee0 0xf74baee0
r5 0x90068 0x90068
r6 0x0 0x0
r7 0xf6d4a5e4 0xf6d4a5e4
r8 0xf3ce0000 0xf3ce0000
r9 0xf6e78500 0xf6e78500
r10 0xf6d51268 0xf6d51268
r11 0xf73ceaa0 0xf73ceaa0
r12 0xaf 0xaf
sp 0xf73cea48 0xf73cea48
lr 0xf7ddf7a9 0xf7ddf7a9
pc 0xf6c5160c 0xf6c5160c
cpsr 0x600f0010 0x600f0010
Unable to fetch SVE register header: Invalid argument.
(gdb) set arm fallback-mode thumb
(gdb) x/10i $pc
=> 0xf6c5160c: strd r6, r3, [r0, #-32]
0xf6c51610: ldr r3, [sp, #36] ; 0x24
0xf6c51612: ldr r4, [sp, #44] ; 0x2c
0xf6c51614: str.w r3, [r0, #-24]
0xf6c51618: ldr r3, [sp, #40] ; 0x28
0xf6c5161a: add.w r3, r0, r3, lsl #2
0xf6c5161e: str.w r3, [r0, #-20]
0xf6c51622: strd r5, r6, [r0, #-8]
0xf6c51626: ldr.w r3, [r0, #-20]
0xf6c5162a: add.w r3, r3, r4, lsl #1
(gdb) si
0xf6c51610 in ?? ()
(gdb) info reg
r0 0xf3ce0020 4090363936
r1 0xf73ce944 4147964228
r2 0x11000 69632
r3 0xf3d90000 4091084800
r4 0xf74baee0 4148932320
r5 0x90068 589928
r6 0x0 0
r7 0xf6d4a5e4 4141131236
r8 0xf3ce0000 4090363904
r9 0xf6e78500 4142368000
r10 0xf6d51268 4141159016
r11 0xf73ceaa0 4147964576
r12 0xaf 175
sp 0xf73cea48 0xf73cea48
lr 0xf7ddf7a9 -136448087
pc 0xf6c51610 0xf6c51610
cpsr 0x600f0010 1611595792
Unable to fetch SVE register header: Invalid argument.
(gdb) si
0xf6c51614 in ?? ()
(gdb) si
0xf6c51618 in ?? ()
(gdb) si
0xf6c78248 in ?? ()
(gdb) si
Thread 1 "pwsh.exe" received signal SIGILL, Illegal instruction.
0xf6c78248 in ?? ()
--- snip ---
Wine source:
https://source.winehq.org/git/wine.git/blob/HEAD:/dlls/ntdll/signal_arm.c#l283
--- snip ---
283 void DECLSPEC_HIDDEN set_cpu_context( const CONTEXT *context );
284 __ASM_GLOBAL_FUNC( set_cpu_context,
285 ".arm\n\t"
286 "ldr r1, [r0, #0x44]\n\t" /* context->Cpsr */
287 "msr CPSR_f, r1\n\t"
288 "ldr r1, [r0, #0x40]\n\t" /* context->Pc */
289 "ldr lr, [r0, #0x3c]\n\t" /* context->Lr */
290 "ldr sp, [r0, #0x38]\n\t" /* context->Sp */
291 "push {r1}\n\t"
292 "ldmib r0, {r0-r12}\n\t" /* context->R0..R12 */
293 "pop {pc}" )
--- snip ---
Well, that's an unfortunate combination of things here. The app binaries
contain a mix of 16-bit Thumb and 32-bit Thumb(2) instructions. Thumb-2
instruction set is the default for Windows on ARM per convention.
https://msdn.microsoft.com/en-us/library/dn736986.aspx)
--- quote ---
The instruction set for Windows on ARM is strictly limited to Thumb-2. All code
executed on this platform is expected to start and remain in Thumb mode at all
times. An attempt to switch into the legacy ARM instruction set may succeed,
but if it does, any exceptions or interrupts that occur may lead to an
application fault in user mode, or a bugcheck in kernel mode.
A side-effect of this requirement is that all code pointers must have the low
bit set. This is so that when they are loaded and branched to via BLX or BX,
the processor will remain in Thumb mode and not try to execute the target code
as 32-bit ARM instructions.
--- quote ---
The write-watch fault is caused by an instruction that has 32-bit encoding
(Thumb2), located at 0xf6c5160c -> bits[1:0] of the address = 0b00.
Due to the segfault handling, the mode is switched to ARM (there are also
explicit '.arm' directives in Wine code) -> CPSR 'T' bit gone.
Wine's 'set_cpu_context' uses 'pop {pc}' to return to the faulting instruction
for re-execution after write-watch has been reset. The instruction behaviour is
defined by ARM:
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0588b/Babefbce.html
--- quote ---
== POP, with reglist including the PC
This instruction causes a branch to the address popped off the stack into the
PC. This is usually a return from a subroutine, where the LR was pushed onto
the stack at the start of the subroutine.
In ARMv5T and above:
bits[1:0] must not be 0b10
if bit[0] is 1, execution continues in Thumb state
if bit[0] is 0, execution continues in ARM state.
In ARMv4, bits[1:0] of the address loaded must be 0b00.
== Thumb instructions
A subset of these instructions are available in the Thumb instruction set.
The following restrictions apply to the 16-bit instructions:
For PUSH, reglist can only include the Lo registers and the LR
For POP, reglist can only include the Lo registers and the PC.
The following restrictions apply to the 32-bit instructions:
reglist must not include the SP
For PUSH, reglist must not include the PC
For POP, reglist can include either the LR or the PC, but not both.
== Restrictions on reglist in ARM instructions
ARM PUSH instructions can have SP and PC in the reglist but these instructions
that include SP or PC in the reglist are deprecated in ARMv6T2 and above.
ARM POP instructions cannot have SP but can have PC in the reglist. These
instructions that include both PC and LR in the reglist are deprecated in
ARMv6T2 and above.
--- quote ---
Since I run the whole target under QEMU aarch64 system emulation I double
checked that part too. Qemu behaves correctly, not switching from ARM to Thumb
mode because bits[1:0] of the address = 0b00.
https://github.com/qemu/qemu/blob/master/target/arm/translate.c#L225
--- snip ---
/* Set a CPU register. The source must be a temporary and will be
marked as dead. */
static void store_reg(DisasContext *s, int reg, TCGv_i32 var)
{
if (reg == 15) {
/* In Thumb mode, we must ignore bit 0.
* In ARM mode, for ARMv4 and ARMv5, it is UNPREDICTABLE if bits [1:0]
* are not 0b00, but for ARMv6 and above, we must ignore bits [1:0].
* We choose to ignore [1:0] in ARM mode for all architecture versions.
*/
tcg_gen_andi_i32(var, var, s->thumb ? ~1 : ~3);
s->base.is_jmp = DISAS_JUMP;
}
tcg_gen_mov_i32(cpu_R[reg], var);
tcg_temp_free_i32(var);
}
--- snip ---
Due to Wine's cpu context helper not restoring Thumb mode properly, execution
is incorrectly resumed in ARM mode, leading to crash shortly after.
Any Wine ARM32-specific assembly code/wrappers that pass control directly to
app code, bypassing the compiler's ARM-Thumb interworking, has to make sure
that it restores thumb mode properly.
$ sha1sum PowerShell-6.2.0-preview.2-win-arm32.zip
b77b87906514e802c03c84fcb72ce39f925c3b41
PowerShell-6.2.0-preview.2-win-arm32.zip
$ du -sh PowerShell-6.2.0-preview.2-win-arm32.zip
40M PowerShell-6.2.0-preview.2-win-arm32.zip
Regards
--
Do not reply to this email, post in Bugzilla using the
above URL to reply.
You are receiving this mail because:
You are watching all bug changes.
More information about the wine-bugs
mailing list