[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