Skip to content

SEH for Windows aarch64: invalid Packed unwind data detection with H=1 #54879

@egelke

Description

@egelke

There is an issue with the the detection of "packed unwind data" when compiling aaarch64 asm with SEH directives on Windows, e.g.:

clang -g  -c -target aarch64-pc-windows-msvc -o obj\main.obj main.asm
link /OUT:bin\program.exe "kernel32.lib" /DEBUG /MACHINE:ARM64 /SUBSYSTEM:CONSOLE /NOLOGO /NODEFAULTLIB /ENTRY:"_start" obj\main.obj

Everything works fine as long as you don't home the integer parameter registers. The below example results in a correctly packed unwind data.

     .global func
func:
    .seh_proc func
    stp x19, x20,[sp,#-0x10]!
    .seh_save_regp_x x19, 0x10
    stp fp, lr, [sp, #-0x10]!
    .seh_save_fplr_x 0x10
    mov fp, sp
    .seh_set_fp
    .seh_endprologue

    //return success
    mov w0, wzr

    .seh_startepilogue
    ldp fp, lr, [sp], #0x10
    .seh_save_fplr_x 0x10
    ldp x19, x20, [sp], #0x10
    .seh_save_regp_x x19, 0x10
    .seh_endepilogue

    ret
    .seh_endfunclet
    .seh_endproc
c:\> dumpbin /UNWINDINFO bin\program.exe
           Begin    Packed  Function Name

  00000010 00001038    Y     func
   Start=40001038  Flag=1  FuncLen=1C  RegF=0  RegI=2  H=0  CR=3  FrameSize=0x20
      [RawPdata=00001038 0162001D]
      +0000 stp  x19,x20,[sp,#-0x10]!; Actual=stp         x19,x20,[sp,#-0x10]!
      +0004 stp  fp,lr,[sp,#-0x10]!  ; Actual=stp         fp,lr,[sp,#-0x10]!
      +0008 mov  fp,sp               ; Actual=mov         fp,sp
   Epilog #1 unwind:  (Offset=10)
      +0010 ldp  fp,lr,[sp],#0x10    ; Actual=ldp         fp,lr,[sp],#0x10
      +0014 ldp  x19,x20,[sp],#0x10  ; Actual=ldp         x19,x20,[sp],#0x10
      +0018 ret                      ; Actual=ret

However, when you home the integer parameter registers, i.e. try to generate packed unwind data with H=1, it fails recognize that it's a packed format and generates an unpacked version instead:

     .global func
func:
    .seh_proc func
    stp x19, x20,[sp,#-0x50]!
    .seh_save_regp_x x19, 0x50
    stp x0, x1, [sp, #0x10]
    .seh_nop
    stp x2, x3, [sp, #0x20]
    .seh_nop
    stp x4, x5, [sp, #0x30]
    .seh_nop
    stp x6, x7, [sp, #0x40]
    .seh_nop
    stp fp, lr, [sp, #-0x10]!
    .seh_save_fplr_x 0x10
    mov fp, sp
    .seh_set_fp
    .seh_endprologue

    //return success
    mov w0, wzr

    .seh_startepilogue
    ldp fp, lr, [sp], #0x10
    .seh_save_fplr_x 0x10
    ldp x19, x20, [sp], #0x50
    .seh_save_regp_x x19, 0x50
    .seh_endepilogue

    ret
    .seh_endfunclet
    .seh_endproc
c:\> dumpbin /UNWINDINFO bin\program.exe
           Begin    Packed  Function Name

  00000010 00001038    N     func
   Start=40001038  Xdata=400032E8
      [RawPdata=00001038 000032E8]
   FuncLen=2C  Vers=0  X=0  E=0  Epilogs=1  CodeWords=3
   Prolog unwind:
      06: 2A...... +0000 stp   x19,x20,[sp,#-0x50]!; Actual=stp         x19,x20,[sp,#-0x50]!
      05: E3...... +0004 nop                      ; Actual=stp         x0,x1,[sp,#0x10]
      04: E3...... +0008 nop                      ; Actual=stp         x2,x3,[sp,#0x20]
      03: E3...... +000C nop                      ; Actual=stp         x4,x5,[sp,#0x30]
      02: E3...... +0010 nop                      ; Actual=stp         x6,x7,[sp,#0x40]
      01: 81...... +0014 stp   fp,lr,[sp,#-0x10]! ; Actual=stp         fp,lr,[sp,#-0x10]!
      00: E1...... +0018 mov   fp,sp              ; Actual=mov         fp,sp
                   +001C (end sequence)
   Epilog #1 unwind:  (Offset=10  Index=8)
      08: 81...... +0020 ldp   fp,lr,[sp],#0x10   ; Actual=ldp         fp,lr,[sp],#0x10
      09: 2A...... +0024 ldp   x19,x20,[sp],#0x50 ; Actual=ldp         x19,x20,[sp],#0x50
      0A: E4...... +0028 end                      ; Actual=ret
                   +0030 (end sequence)
   [RawXdata=1840000B 02000008 E3E381E1 E42AE3E3 E3E42A81]

If you however, repeat the "nop" seh directives with instructions into the unwind code, then you get an incorrect packed unwind data as shows by the following example:

     .global func
func:
    .seh_proc func
    stp x19, x20,[sp,#-0x50]!
    .seh_save_regp_x x19, 0x50
    stp x0, x1, [sp, #0x10]
    .seh_nop
    stp x2, x3, [sp, #0x20]
    .seh_nop
    stp x4, x5, [sp, #0x30]
    .seh_nop
    stp x6, x7, [sp, #0x40]
    .seh_nop
    stp fp, lr, [sp, #-0x10]!
    .seh_save_fplr_x 0x10
    mov fp, sp
    .seh_set_fp
    .seh_endprologue

    //return success
    mov w0, wzr

    .seh_startepilogue
    ldp fp, lr, [sp], #0x10
    .seh_save_fplr_x 0x10
    ldp x6, x7, [sp, 0x40]
    .seh_nop
    ldp x4, x5, [sp, 0x30]
    .seh_nop
    ldp x2, x3, [sp, 0x20]
    .seh_nop
    ldp x0, x1, [sp, 0x10]
    .seh_nop
    ldp x19, x20, [sp], #0x50
    .seh_save_regp_x x19, 0x50
    .seh_endepilogue

    ret
    .seh_endfunclet
    .seh_endproc
  00000010 00001038    Y     func
   Start=40001038  Flag=1  FuncLen=3C  RegF=0  RegI=2  H=1  CR=3  FrameSize=0x60
      [RawPdata=00001038 0372003D]
      +0000 stp  x19,x20,[sp,#-0x50]!; Actual=stp         x19,x20,[sp,#-0x50]!
      +0004 stp  x0,x1,[sp,#0x10]    ; Actual=stp         x0,x1,[sp,#0x10]
      +0008 stp  x2,x3,[sp,#0x20]    ; Actual=stp         x2,x3,[sp,#0x20]
**** Expected opcode A90207E0
      +000C stp  x4,x5,[sp,#0x30]    ; Actual=stp         x4,x5,[sp,#0x30]
**** Expected opcode A90307E0
      +0010 stp  x6,x7,[sp,#0x40]    ; Actual=stp         x6,x7,[sp,#0x40]
**** Expected opcode A90407E0
      +0014 stp  fp,lr,[sp,#-0x10]!  ; Actual=stp         fp,lr,[sp,#-0x10]!
      +0018 mov  fp,sp               ; Actual=mov         fp,sp
   Epilog #1 unwind:  (Offset=30)
      +0030 ldp  fp,lr,[sp],#0x10    ; Actual=ldp         x0,x1,[sp,#0x10]
**** Expected opcode A8C17BFD
      +0034 ldp  x19,x20,[sp],#0x50  ; Actual=ldp         x19,x20,[sp],#0x50
      +0038 ret                      ; Actual=ret

(please ignore the bug in MS dumpbin.exe, the opcode is correct it's just the check that is wrong since it expect x0 and x1 instead of x2 and x3)

Obviously, a NOP should not be repeated in the unwind code. Instead, when clang encounters a .seh_nop directive in the prologue it should not expect any matching instruction in the epilogue in order for it to to generate a packed unwind data.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions