-
Notifications
You must be signed in to change notification settings - Fork 15.1k
Description
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.