Merge pull request #22035 from alexrp/unwind-fixes

Better unwind table support + unwind protection in `_start()` and `clone()`
This commit is contained in:
Alex Rønne Petersen 2024-12-13 03:09:24 +01:00 committed by GitHub
commit 130f7c2ed8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
45 changed files with 361 additions and 138 deletions

View File

@ -24,7 +24,8 @@ __clone:
// parent // parent
ret ret
// child // child
1: ldp x1,x0,[sp],#16 1: mov fp, 0
ldp x1,x0,[sp],#16
blr x1 blr x1
mov x8,#93 // SYS_exit mov x8,#93 // SYS_exit
svc #0 svc #0

View File

@ -19,7 +19,8 @@ __clone:
ldmfd sp!,{r4,r5,r6,r7} ldmfd sp!,{r4,r5,r6,r7}
bx lr bx lr
1: mov r0,r6 1: mov fp,#0
mov r0,r6
bl 3f bl 3f
2: mov r7,#1 2: mov r7,#1
svc 0 svc 0

View File

@ -30,9 +30,15 @@ __clone:
mov 8(%ebp),%ebp mov 8(%ebp),%ebp
int $128 int $128
test %eax,%eax test %eax,%eax
jnz 1f jz 1f
add $16,%esp
pop %edi
pop %esi
pop %ebx
pop %ebp
ret
mov %ebp,%eax 1: mov %ebp,%eax
xor %ebp,%ebp xor %ebp,%ebp
call *%eax call *%eax
mov %eax,%ebx mov %eax,%ebx
@ -40,10 +46,3 @@ __clone:
inc %eax inc %eax
int $128 int $128
hlt hlt
1: add $16,%esp
pop %edi
pop %esi
pop %ebx
pop %ebp
ret

View File

@ -22,6 +22,7 @@ __clone:
beqz $a0, 1f # whether child process beqz $a0, 1f # whether child process
jirl $zero, $ra, 0 # parent process return jirl $zero, $ra, 0 # parent process return
1: 1:
move $fp, $zero
ld.d $t8, $sp, 0 # function pointer ld.d $t8, $sp, 0 # function pointer
ld.d $a0, $sp, 8 # argument pointer ld.d $a0, $sp, 8 # argument pointer
jirl $ra, $t8, 0 # call the user's function jirl $ra, $t8, 0 # call the user's function

View File

@ -18,7 +18,8 @@ __clone:
beq 1f beq 1f
movem.l (%sp)+,%d2-%d5 movem.l (%sp)+,%d2-%d5
rts rts
1: move.l %a1,-(%sp) 1: suba.l %%fp,%%fp
move.l %a1,-(%sp)
jsr (%a0) jsr (%a0)
move.l #1,%d0 move.l #1,%d0
trap #0 trap #0

View File

@ -22,7 +22,8 @@ __clone:
rtsd r15, 8 rtsd r15, 8
nop nop
1: lwi r3, r1, 0 1: add r19, r0, r0
lwi r3, r1, 0
lwi r5, r1, 4 lwi r5, r1, 4
brald r15, r3 brald r15, r3
nop nop

View File

@ -27,7 +27,8 @@ __clone:
addu $sp, $sp, 16 addu $sp, $sp, 16
jr $ra jr $ra
nop nop
1: lw $25, 0($sp) 1: move $fp, $0
lw $25, 0($sp)
lw $4, 4($sp) lw $4, 4($sp)
jalr $25 jalr $25
nop nop

View File

@ -25,7 +25,8 @@ __clone:
nop nop
jr $ra jr $ra
nop nop
1: ld $25, 0($sp) # function pointer 1: move $fp, $0
ld $25, 0($sp) # function pointer
ld $4, 8($sp) # argument pointer ld $4, 8($sp) # argument pointer
jalr $25 # call the user's function jalr $25 # call the user's function
nop nop

View File

@ -25,7 +25,8 @@ __clone:
nop nop
jr $ra jr $ra
nop nop
1: lw $25, 0($sp) # function pointer 1: move $fp, $0
lw $25, 0($sp) # function pointer
lw $4, 4($sp) # argument pointer lw $4, 4($sp) # argument pointer
jalr $25 # call the user's function jalr $25 # call the user's function
nop nop

View File

@ -23,7 +23,8 @@ __clone:
l.jr r9 l.jr r9
l.nop l.nop
1: l.lwz r11, 0(r1) 1: l.ori r2, r0, 0
l.lwz r11, 0(r1)
l.jalr r11 l.jalr r11
l.lwz r3, 4(r1) l.lwz r3, 4(r1)

View File

@ -48,9 +48,16 @@ neg 3, 3 #negate the result (errno)
# compare sc result with 0 # compare sc result with 0
cmpwi cr7, 3, 0 cmpwi cr7, 3, 0
# if not 0, jump to end # if not 0, restore stack and return
bne cr7, 2f beq cr7, 2f
lwz 30, 0(1)
lwz 31, 4(1)
addi 1, 1, 16
blr
2:
#else: we're the child #else: we're the child
#call funcptr: move arg (d) into r3 #call funcptr: move arg (d) into r3
mr 3, 31 mr 3, 31
@ -61,13 +68,3 @@ bctrl
# mov SYS_exit into r0 (the exit param is already in r3) # mov SYS_exit into r0 (the exit param is already in r3)
li 0, 1 li 0, 1
sc sc
2:
# restore stack
lwz 30, 0(1)
lwz 31, 4(1)
addi 1, 1, 16
blr

View File

@ -15,12 +15,12 @@ __clone:
mov %rcx,(%rsi) mov %rcx,(%rsi)
syscall syscall
test %eax,%eax test %eax,%eax
jnz 1f jz 1f
xor %ebp,%ebp ret
1: xor %ebp,%ebp
pop %rdi pop %rdi
call *%r9 call *%r9
mov %eax,%edi mov %eax,%edi
movl $0x4000003c,%eax /* SYS_exit */ movl $0x4000003c,%eax /* SYS_exit */
syscall syscall
hlt hlt
1: ret

View File

@ -16,8 +16,9 @@ __clone:
mov %rcx,(%rsi) mov %rcx,(%rsi)
syscall syscall
test %eax,%eax test %eax,%eax
jnz 1f jz 1f
xor %ebp,%ebp ret
1: xor %ebp,%ebp
pop %rdi pop %rdi
call *%r9 call *%r9
mov %eax,%edi mov %eax,%edi
@ -25,4 +26,3 @@ __clone:
mov $60,%al mov $60,%al
syscall syscall
hlt hlt
1: ret

View File

@ -706,7 +706,7 @@ pub const ExecutableOptions = struct {
single_threaded: ?bool = null, single_threaded: ?bool = null,
pic: ?bool = null, pic: ?bool = null,
strip: ?bool = null, strip: ?bool = null,
unwind_tables: ?bool = null, unwind_tables: ?std.builtin.UnwindTables = null,
omit_frame_pointer: ?bool = null, omit_frame_pointer: ?bool = null,
sanitize_thread: ?bool = null, sanitize_thread: ?bool = null,
error_tracing: ?bool = null, error_tracing: ?bool = null,
@ -762,7 +762,7 @@ pub const ObjectOptions = struct {
single_threaded: ?bool = null, single_threaded: ?bool = null,
pic: ?bool = null, pic: ?bool = null,
strip: ?bool = null, strip: ?bool = null,
unwind_tables: ?bool = null, unwind_tables: ?std.builtin.UnwindTables = null,
omit_frame_pointer: ?bool = null, omit_frame_pointer: ?bool = null,
sanitize_thread: ?bool = null, sanitize_thread: ?bool = null,
error_tracing: ?bool = null, error_tracing: ?bool = null,
@ -810,7 +810,7 @@ pub const SharedLibraryOptions = struct {
single_threaded: ?bool = null, single_threaded: ?bool = null,
pic: ?bool = null, pic: ?bool = null,
strip: ?bool = null, strip: ?bool = null,
unwind_tables: ?bool = null, unwind_tables: ?std.builtin.UnwindTables = null,
omit_frame_pointer: ?bool = null, omit_frame_pointer: ?bool = null,
sanitize_thread: ?bool = null, sanitize_thread: ?bool = null,
error_tracing: ?bool = null, error_tracing: ?bool = null,
@ -867,7 +867,7 @@ pub const StaticLibraryOptions = struct {
single_threaded: ?bool = null, single_threaded: ?bool = null,
pic: ?bool = null, pic: ?bool = null,
strip: ?bool = null, strip: ?bool = null,
unwind_tables: ?bool = null, unwind_tables: ?std.builtin.UnwindTables = null,
omit_frame_pointer: ?bool = null, omit_frame_pointer: ?bool = null,
sanitize_thread: ?bool = null, sanitize_thread: ?bool = null,
error_tracing: ?bool = null, error_tracing: ?bool = null,
@ -919,7 +919,7 @@ pub const TestOptions = struct {
single_threaded: ?bool = null, single_threaded: ?bool = null,
pic: ?bool = null, pic: ?bool = null,
strip: ?bool = null, strip: ?bool = null,
unwind_tables: ?bool = null, unwind_tables: ?std.builtin.UnwindTables = null,
omit_frame_pointer: ?bool = null, omit_frame_pointer: ?bool = null,
sanitize_thread: ?bool = null, sanitize_thread: ?bool = null,
error_tracing: ?bool = null, error_tracing: ?bool = null,

View File

@ -22,7 +22,7 @@ frameworks: std.StringArrayHashMapUnmanaged(LinkFrameworkOptions),
link_objects: std.ArrayListUnmanaged(LinkObject), link_objects: std.ArrayListUnmanaged(LinkObject),
strip: ?bool, strip: ?bool,
unwind_tables: ?bool, unwind_tables: ?std.builtin.UnwindTables,
single_threaded: ?bool, single_threaded: ?bool,
stack_protector: ?bool, stack_protector: ?bool,
stack_check: ?bool, stack_check: ?bool,
@ -218,7 +218,7 @@ pub const CreateOptions = struct {
link_libcpp: ?bool = null, link_libcpp: ?bool = null,
single_threaded: ?bool = null, single_threaded: ?bool = null,
strip: ?bool = null, strip: ?bool = null,
unwind_tables: ?bool = null, unwind_tables: ?std.builtin.UnwindTables = null,
dwarf_format: ?std.dwarf.Format = null, dwarf_format: ?std.dwarf.Format = null,
code_model: std.builtin.CodeModel = .default, code_model: std.builtin.CodeModel = .default,
stack_protector: ?bool = null, stack_protector: ?bool = null,
@ -675,7 +675,6 @@ pub fn appendZigProcessFlags(
const b = m.owner; const b = m.owner;
try addFlag(zig_args, m.strip, "-fstrip", "-fno-strip"); try addFlag(zig_args, m.strip, "-fstrip", "-fno-strip");
try addFlag(zig_args, m.unwind_tables, "-funwind-tables", "-fno-unwind-tables");
try addFlag(zig_args, m.single_threaded, "-fsingle-threaded", "-fno-single-threaded"); try addFlag(zig_args, m.single_threaded, "-fsingle-threaded", "-fno-single-threaded");
try addFlag(zig_args, m.stack_check, "-fstack-check", "-fno-stack-check"); try addFlag(zig_args, m.stack_check, "-fstack-check", "-fno-stack-check");
try addFlag(zig_args, m.stack_protector, "-fstack-protector", "-fno-stack-protector"); try addFlag(zig_args, m.stack_protector, "-fstack-protector", "-fno-stack-protector");
@ -695,6 +694,14 @@ pub fn appendZigProcessFlags(
}); });
} }
if (m.unwind_tables) |unwind_tables| {
try zig_args.append(switch (unwind_tables) {
.none => "-fno-unwind-tables",
.sync => "-funwind-tables",
.@"async" => "-fasync-unwind-tables",
});
}
try zig_args.ensureUnusedCapacity(1); try zig_args.ensureUnusedCapacity(1);
if (m.optimize) |optimize| switch (optimize) { if (m.optimize) |optimize| switch (optimize) {
.Debug => zig_args.appendAssumeCapacity("-ODebug"), .Debug => zig_args.appendAssumeCapacity("-ODebug"),

View File

@ -804,6 +804,14 @@ pub const LinkMode = enum {
dynamic, dynamic,
}; };
/// This data structure is used by the Zig language code generation and
/// therefore must be kept in sync with the compiler implementation.
pub const UnwindTables = enum {
none,
sync,
@"async",
};
/// This data structure is used by the Zig language code generation and /// This data structure is used by the Zig language code generation and
/// therefore must be kept in sync with the compiler implementation. /// therefore must be kept in sync with the compiler implementation.
pub const WasiExecModel = enum { pub const WasiExecModel = enum {

View File

@ -120,8 +120,13 @@ pub fn clone() callconv(.Naked) usize {
\\ cbz x0,1f \\ cbz x0,1f
\\ // parent \\ // parent
\\ ret \\ ret
\\
\\ // child \\ // child
\\1: ldp x1,x0,[sp],#16 \\1: .cfi_undefined lr
\\ mov fp, 0
\\ mov lr, 0
\\
\\ ldp x1,x0,[sp],#16
\\ blr x1 \\ blr x1
\\ mov x8,#93 // SYS_exit \\ mov x8,#93 // SYS_exit
\\ svc #0 \\ svc #0

View File

@ -120,11 +120,16 @@ pub fn clone() callconv(.Naked) usize {
\\ ldmfd sp!,{r4,r5,r6,r7} \\ ldmfd sp!,{r4,r5,r6,r7}
\\ bx lr \\ bx lr
\\ \\
\\1: mov r0,r6 \\ // https://github.com/llvm/llvm-project/issues/115891
\\1: mov r7, #0
\\ mov r11, #0
\\ mov lr, #0
\\
\\ mov r0,r6
\\ bl 3f \\ bl 3f
\\2: mov r7,#1 // SYS_exit \\ mov r7,#1 // SYS_exit
\\ svc 0 \\ svc 0
\\ b 2b \\
\\3: bx r5 \\3: bx r5
); );
} }

View File

@ -118,6 +118,10 @@ pub fn clone() callconv(.Naked) usize {
\\ p0 = cmp.eq(r0, #0) \\ p0 = cmp.eq(r0, #0)
\\ if (!p0) dealloc_return \\ if (!p0) dealloc_return
\\ \\
\\ .cfi_undefined r31
\\ r30 = #0
\\ r31 = #0
\\
\\ r0 = r10 \\ r0 = r10
\\ callr r11 \\ callr r11
\\ \\

View File

@ -121,6 +121,10 @@ pub fn clone() callconv(.Naked) usize {
\\ beqz $a0, 1f # whether child process \\ beqz $a0, 1f # whether child process
\\ jirl $zero, $ra, 0 # parent process return \\ jirl $zero, $ra, 0 # parent process return
\\1: \\1:
\\ .cfi_undefined 1
\\ move $fp, $zero
\\ move $ra, $zero
\\
\\ ld.d $t8, $sp, 0 # function pointer \\ ld.d $t8, $sp, 0 # function pointer
\\ ld.d $a0, $sp, 8 # argument pointer \\ ld.d $a0, $sp, 8 # argument pointer
\\ jirl $ra, $t8, 0 # call the user's function \\ jirl $ra, $t8, 0 # call the user's function

View File

@ -231,6 +231,10 @@ pub fn clone() callconv(.Naked) usize {
\\ jr $ra \\ jr $ra
\\ nop \\ nop
\\1: \\1:
\\ .cfi_undefined $ra
\\ move $fp, $zero
\\ move $ra, $zero
\\
\\ lw $25, 0($sp) \\ lw $25, 0($sp)
\\ lw $4, 4($sp) \\ lw $4, 4($sp)
\\ jalr $25 \\ jalr $25

View File

@ -210,6 +210,10 @@ pub fn clone() callconv(.Naked) usize {
\\ jr $ra \\ jr $ra
\\ nop \\ nop
\\1: \\1:
\\ .cfi_undefined $ra
\\ move $fp, $zero
\\ move $ra, $zero
\\
\\ ld $25, 0($sp) \\ ld $25, 0($sp)
\\ ld $4, 8($sp) \\ ld $4, 8($sp)
\\ jalr $25 \\ jalr $25

View File

@ -133,14 +133,14 @@ pub fn clone() callconv(.Naked) usize {
// syscall(SYS_clone, flags, stack, ptid, tls, ctid) // syscall(SYS_clone, flags, stack, ptid, tls, ctid)
// 0 3, 4, 5, 6, 7 // 0 3, 4, 5, 6, 7
asm volatile ( asm volatile (
\\ # store non-volatile regs r30, r31 on stack in order to put our \\ # store non-volatile regs r29, r30 on stack in order to put our
\\ # start func and its arg there \\ # start func and its arg there
\\ stwu 30, -16(1) \\ stwu 29, -16(1)
\\ stw 31, 4(1) \\ stw 30, 4(1)
\\ \\
\\ # save r3 (func) into r30, and r6(arg) into r31 \\ # save r3 (func) into r29, and r6(arg) into r30
\\ mr 30, 3 \\ mr 29, 3
\\ mr 31, 6 \\ mr 30, 6
\\ \\
\\ # create initial stack frame for new thread \\ # create initial stack frame for new thread
\\ clrrwi 4, 4, 4 \\ clrrwi 4, 4, 4
@ -167,28 +167,28 @@ pub fn clone() callconv(.Naked) usize {
\\ # compare sc result with 0 \\ # compare sc result with 0
\\ cmpwi cr7, 3, 0 \\ cmpwi cr7, 3, 0
\\ \\
\\ # if not 0, jump to end \\ # if not 0, restore stack and return
\\ bne cr7, 2f \\ beq cr7, 2f
\\ lwz 29, 0(1)
\\ lwz 30, 4(1)
\\ addi 1, 1, 16
\\ blr
\\ \\
\\ #else: we're the child \\ #else: we're the child
\\ 2:
\\ .cfi_undefined lr
\\ li 31, 0
\\ mtlr 0
\\
\\ #call funcptr: move arg (d) into r3 \\ #call funcptr: move arg (d) into r3
\\ mr 3, 31 \\ mr 3, 30
\\ #move r30 (funcptr) into CTR reg \\ #move r29 (funcptr) into CTR reg
\\ mtctr 30 \\ mtctr 29
\\ # call CTR reg \\ # call CTR reg
\\ bctrl \\ bctrl
\\ # mov SYS_exit into r0 (the exit param is already in r3) \\ # mov SYS_exit into r0 (the exit param is already in r3)
\\ li 0, 1 \\ li 0, 1
\\ sc \\ sc
\\
\\ 2:
\\
\\ # restore stack
\\ lwz 30, 0(1)
\\ lwz 31, 4(1)
\\ addi 1, 1, 16
\\
\\ blr
); );
} }

View File

@ -160,7 +160,12 @@ pub fn clone() callconv(.Naked) usize {
\\ cmpwi cr7, 3, 0 \\ cmpwi cr7, 3, 0
\\ bnelr cr7 \\ bnelr cr7
\\ \\
\\ # we're the child. call fn(arg) \\ # we're the child
\\ .cfi_undefined lr
\\ li 31, 0
\\ mtlr 0
\\
\\ # call fn(arg)
\\ ld 3, 16(1) \\ ld 3, 16(1)
\\ ld 12, 8(1) \\ ld 12, 8(1)
\\ mtctr 12 \\ mtctr 12

View File

@ -120,7 +120,11 @@ pub fn clone() callconv(.Naked) usize {
\\ ret \\ ret
\\ \\
\\ # Child \\ # Child
\\1: lw a1, 0(sp) \\1: .cfi_undefined ra
\\ mv fp, zero
\\ mv ra, zero
\\
\\ lw a1, 0(sp)
\\ lw a0, 4(sp) \\ lw a0, 4(sp)
\\ jalr a1 \\ jalr a1
\\ \\

View File

@ -120,7 +120,11 @@ pub fn clone() callconv(.Naked) usize {
\\ ret \\ ret
\\ \\
\\ # Child \\ # Child
\\1: ld a1, 0(sp) \\1: .cfi_undefined ra
\\ mv fp, zero
\\ mv ra, zero
\\
\\ ld a1, 0(sp)
\\ ld a0, 8(sp) \\ ld a0, 8(sp)
\\ jalr a1 \\ jalr a1
\\ \\

View File

@ -133,7 +133,12 @@ pub fn clone() callconv(.Naked) usize {
\\ltgr %%r2, %%r2 \\ltgr %%r2, %%r2
\\bnzr %%r14 \\bnzr %%r14
\\ \\
\\# we're the child. call fn(arg) \\# we're the child
\\.cfi_undefined %%r14
\\lghi %%r11, 0
\\lghi %%r14, 0
\\
\\# call fn(arg)
\\lg %%r1, 8(%%r15) \\lg %%r1, 8(%%r15)
\\lg %%r2, 16(%%r15) \\lg %%r2, 16(%%r15)
\\basr %%r14, %%r1 \\basr %%r14, %%r1

View File

@ -198,29 +198,34 @@ pub fn clone() callconv(.Naked) usize {
\\ mov %%i5, %%o3 \\ mov %%i5, %%o3
\\ ldx [%%fp + 0x8af], %%o4 \\ ldx [%%fp + 0x8af], %%o4
\\ t 0x6d \\ t 0x6d
\\ bcs,pn %%xcc, 2f \\ bcs,pn %%xcc, 1f
\\ nop \\ nop
\\ # The child pid is returned in o0 while o1 tells if this \\ # The child pid is returned in o0 while o1 tells if this
\\ # process is # the child (=1) or the parent (=0). \\ # process is # the child (=1) or the parent (=0).
\\ brnz %%o1, 1f \\ brnz %%o1, 2f
\\ nop \\ nop
\\ # Parent process, return the child pid \\ # Parent process, return the child pid
\\ mov %%o0, %%i0 \\ mov %%o0, %%i0
\\ ret \\ ret
\\ restore \\ restore
\\1: \\1:
\\ # Child process, call func(arg) \\ # The syscall failed
\\ sub %%g0, %%o0, %%i0
\\ ret
\\ restore
\\2:
\\ # Child process
\\ .cfi_undefined %%i7
\\ mov %%g0, %%fp
\\ mov %%g0, %%i7
\\
\\ # call func(arg)
\\ mov %%g0, %%fp \\ mov %%g0, %%fp
\\ call %%g2 \\ call %%g2
\\ mov %%g3, %%o0 \\ mov %%g3, %%o0
\\ # Exit \\ # Exit
\\ mov 1, %%g1 // SYS_exit \\ mov 1, %%g1 // SYS_exit
\\ t 0x6d \\ t 0x6d
\\2:
\\ # The syscall failed
\\ sub %%g0, %%o0, %%i0
\\ ret
\\ restore
); );
} }

View File

@ -148,19 +148,22 @@ pub fn clone() callconv(.Naked) usize {
\\ movl $120,%%eax // SYS_clone \\ movl $120,%%eax // SYS_clone
\\ int $128 \\ int $128
\\ testl %%eax,%%eax \\ testl %%eax,%%eax
\\ jnz 1f \\ jz 1f
\\ popl %%eax
\\ xorl %%ebp,%%ebp
\\ calll *%%eax
\\ movl %%eax,%%ebx
\\ movl $1,%%eax // SYS_exit
\\ int $128
\\1:
\\ popl %%edi \\ popl %%edi
\\ popl %%esi \\ popl %%esi
\\ popl %%ebx \\ popl %%ebx
\\ popl %%ebp \\ popl %%ebp
\\ retl \\ retl
\\
\\1:
\\ .cfi_undefined %%eip
\\ xorl %%ebp,%%ebp
\\
\\ popl %%eax
\\ calll *%%eax
\\ movl %%eax,%%ebx
\\ movl $1,%%eax // SYS_exit
\\ int $128
); );
} }

View File

@ -116,8 +116,10 @@ pub fn clone() callconv(.Naked) usize {
\\ testq %%rax,%%rax \\ testq %%rax,%%rax
\\ jz 1f \\ jz 1f
\\ retq \\ retq
\\
\\1: .cfi_undefined %%rip \\1: .cfi_undefined %%rip
\\ xorl %%ebp,%%ebp \\ xorl %%ebp,%%ebp
\\
\\ popq %%rdi \\ popq %%rdi
\\ callq *%%r9 \\ callq *%%r9
\\ movl %%eax,%%edi \\ movl %%eax,%%edi

View File

@ -230,6 +230,29 @@ fn _start() callconv(.naked) noreturn {
); );
} }
// This is the first userspace frame. Prevent DWARF-based unwinders from unwinding further. We
// prevent FP-based unwinders from unwinding further by zeroing the register further below.
asm volatile (switch (native_arch) {
.arc => ".cfi_undefined blink",
.arm, .armeb, .thumb, .thumbeb => "", // https://github.com/llvm/llvm-project/issues/115891
.aarch64, .aarch64_be => ".cfi_undefined lr",
.csky => ".cfi_undefined lr",
.hexagon => ".cfi_undefined r31",
.loongarch32, .loongarch64 => ".cfi_undefined 1",
.m68k => ".cfi_undefined pc",
.mips, .mipsel, .mips64, .mips64el => ".cfi_undefined $ra",
.powerpc, .powerpcle, .powerpc64, .powerpc64le => ".cfi_undefined lr",
.riscv32, .riscv64 => if (builtin.zig_backend == .stage2_riscv64)
""
else
".cfi_undefined ra",
.s390x => ".cfi_undefined %%r14",
.sparc, .sparc64 => ".cfi_undefined %%i7",
.x86 => ".cfi_undefined %%eip",
.x86_64 => ".cfi_undefined %%rip",
else => @compileError("unsupported arch"),
});
// Move this to the riscv prong below when this is resolved: https://github.com/ziglang/zig/issues/20918 // Move this to the riscv prong below when this is resolved: https://github.com/ziglang/zig/issues/20918
if (builtin.cpu.arch.isRISCV() and builtin.zig_backend != .stage2_riscv64) asm volatile ( if (builtin.cpu.arch.isRISCV() and builtin.zig_backend != .stage2_riscv64) asm volatile (
\\ .weak __global_pointer$ \\ .weak __global_pointer$
@ -247,7 +270,6 @@ fn _start() callconv(.naked) noreturn {
// linker explicitly. // linker explicitly.
asm volatile (switch (native_arch) { asm volatile (switch (native_arch) {
.x86_64 => .x86_64 =>
\\ .cfi_undefined %%rip
\\ xorl %%ebp, %%ebp \\ xorl %%ebp, %%ebp
\\ movq %%rsp, %%rdi \\ movq %%rsp, %%rdi
\\ andq $-16, %%rsp \\ andq $-16, %%rsp
@ -279,8 +301,10 @@ fn _start() callconv(.naked) noreturn {
, ,
.arm, .armeb, .thumb, .thumbeb => .arm, .armeb, .thumb, .thumbeb =>
// Note that this code must work for Thumb-1. // Note that this code must work for Thumb-1.
// r7 = FP (local), r11 = FP (unwind)
\\ movs v1, #0 \\ movs v1, #0
\\ mov fp, v1 \\ mov r7, v1
\\ mov r11, v1
\\ mov lr, v1 \\ mov lr, v1
\\ mov a1, sp \\ mov a1, sp
\\ subs v1, #16 \\ subs v1, #16
@ -290,20 +314,23 @@ fn _start() callconv(.naked) noreturn {
, ,
.csky => .csky =>
// The CSKY ABI assumes that `gb` is set to the address of the GOT in order for // The CSKY ABI assumes that `gb` is set to the address of the GOT in order for
// position-independent code to work. We depend on this in `std.os.linux.start_pie` // position-independent code to work. We depend on this in `std.os.linux.pie` to locate
// to locate `_DYNAMIC` as well. // `_DYNAMIC` as well.
// r8 = FP
\\ grs t0, 1f \\ grs t0, 1f
\\ 1: \\ 1:
\\ lrw gb, 1b@GOTPC \\ lrw gb, 1b@GOTPC
\\ addu gb, t0 \\ addu gb, t0
\\ movi r8, 0
\\ movi lr, 0 \\ movi lr, 0
\\ mov a0, sp \\ mov a0, sp
\\ andi sp, sp, -8 \\ andi sp, sp, -8
\\ jmpi %[posixCallMainAndExit] \\ jmpi %[posixCallMainAndExit]
, ,
.hexagon => .hexagon =>
// r29 = SP, r30 = FP // r29 = SP, r30 = FP, r31 = LR
\\ r30 = #0 \\ r30 = #0
\\ r31 = #0
\\ r0 = r29 \\ r0 = r29
\\ r29 = and(r29, #-16) \\ r29 = and(r29, #-16)
\\ memw(r29 + #-8) = r29 \\ memw(r29 + #-8) = r29
@ -312,12 +339,13 @@ fn _start() callconv(.naked) noreturn {
, ,
.loongarch32, .loongarch64 => .loongarch32, .loongarch64 =>
\\ move $fp, $zero \\ move $fp, $zero
\\ move $ra, $zero
\\ move $a0, $sp \\ move $a0, $sp
\\ bstrins.d $sp, $zero, 3, 0 \\ bstrins.d $sp, $zero, 3, 0
\\ b %[posixCallMainAndExit] \\ b %[posixCallMainAndExit]
, ,
.riscv32, .riscv64 => .riscv32, .riscv64 =>
\\ li s0, 0 \\ li fp, 0
\\ li ra, 0 \\ li ra, 0
\\ mv a0, sp \\ mv a0, sp
\\ andi sp, sp, -16 \\ andi sp, sp, -16
@ -371,28 +399,35 @@ fn _start() callconv(.naked) noreturn {
, ,
.powerpc, .powerpcle => .powerpc, .powerpcle =>
// Set up the initial stack frame, and clear the back chain pointer. // Set up the initial stack frame, and clear the back chain pointer.
// r1 = SP, r31 = FP
\\ mr 3, 1 \\ mr 3, 1
\\ clrrwi 1, 1, 4 \\ clrrwi 1, 1, 4
\\ li 0, 0 \\ li 0, 0
\\ stwu 1, -16(1) \\ stwu 1, -16(1)
\\ stw 0, 0(1) \\ stw 0, 0(1)
\\ li 31, 0
\\ mtlr 0 \\ mtlr 0
\\ b %[posixCallMainAndExit] \\ b %[posixCallMainAndExit]
, ,
.powerpc64, .powerpc64le => .powerpc64, .powerpc64le =>
// Set up the ToC and initial stack frame, and clear the back chain pointer. // Set up the ToC and initial stack frame, and clear the back chain pointer.
// r1 = SP, r2 = ToC, r31 = FP
\\ addis 2, 12, .TOC. - %[_start]@ha \\ addis 2, 12, .TOC. - %[_start]@ha
\\ addi 2, 2, .TOC. - %[_start]@l \\ addi 2, 2, .TOC. - %[_start]@l
\\ mr 3, 1 \\ mr 3, 1
\\ clrrdi 1, 1, 4 \\ clrrdi 1, 1, 4
\\ li 0, 0 \\ li 0, 0
\\ stdu 0, -32(1) \\ stdu 0, -32(1)
\\ li 31, 0
\\ mtlr 0 \\ mtlr 0
\\ b %[posixCallMainAndExit] \\ b %[posixCallMainAndExit]
\\ nop \\ nop
, ,
.s390x => .s390x =>
// Set up the stack frame (register save area and cleared back-chain slot). // Set up the stack frame (register save area and cleared back-chain slot).
// r11 = FP, r14 = LR, r15 = SP
\\ lghi %%r11, 0
\\ lghi %%r14, 0
\\ lgr %%r2, %%r15 \\ lgr %%r2, %%r15
\\ lghi %%r0, -16 \\ lghi %%r0, -16
\\ ngr %%r15, %%r0 \\ ngr %%r15, %%r0
@ -403,7 +438,9 @@ fn _start() callconv(.naked) noreturn {
, ,
.sparc => .sparc =>
// argc is stored after a register window (16 registers * 4 bytes). // argc is stored after a register window (16 registers * 4 bytes).
// i7 = LR
\\ mov %%g0, %%fp \\ mov %%g0, %%fp
\\ mov %%g0, %%i7
\\ add %%sp, 64, %%o0 \\ add %%sp, 64, %%o0
\\ and %%sp, -8, %%sp \\ and %%sp, -8, %%sp
\\ ba,a %[posixCallMainAndExit] \\ ba,a %[posixCallMainAndExit]
@ -411,7 +448,9 @@ fn _start() callconv(.naked) noreturn {
.sparc64 => .sparc64 =>
// argc is stored after a register window (16 registers * 8 bytes) plus the stack bias // argc is stored after a register window (16 registers * 8 bytes) plus the stack bias
// (2047 bytes). // (2047 bytes).
// i7 = LR
\\ mov %%g0, %%fp \\ mov %%g0, %%fp
\\ mov %%g0, %%i7
\\ add %%sp, 2175, %%o0 \\ add %%sp, 2175, %%o0
\\ add %%sp, 2047, %%sp \\ add %%sp, 2047, %%sp
\\ and %%sp, -16, %%sp \\ and %%sp, -16, %%sp

View File

@ -2,6 +2,7 @@ target: std.Target,
zig_backend: std.builtin.CompilerBackend, zig_backend: std.builtin.CompilerBackend,
output_mode: std.builtin.OutputMode, output_mode: std.builtin.OutputMode,
link_mode: std.builtin.LinkMode, link_mode: std.builtin.LinkMode,
unwind_tables: std.builtin.UnwindTables,
is_test: bool, is_test: bool,
single_threaded: bool, single_threaded: bool,
link_libc: bool, link_libc: bool,
@ -40,6 +41,7 @@ pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void {
\\ \\
\\pub const output_mode: std.builtin.OutputMode = .{p_}; \\pub const output_mode: std.builtin.OutputMode = .{p_};
\\pub const link_mode: std.builtin.LinkMode = .{p_}; \\pub const link_mode: std.builtin.LinkMode = .{p_};
\\pub const unwind_tables: std.builtin.UnwindTables = .{p_};
\\pub const is_test = {}; \\pub const is_test = {};
\\pub const single_threaded = {}; \\pub const single_threaded = {};
\\pub const abi: std.Target.Abi = .{p_}; \\pub const abi: std.Target.Abi = .{p_};
@ -53,6 +55,7 @@ pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void {
std.zig.fmtId(@tagName(zig_backend)), std.zig.fmtId(@tagName(zig_backend)),
std.zig.fmtId(@tagName(opts.output_mode)), std.zig.fmtId(@tagName(opts.output_mode)),
std.zig.fmtId(@tagName(opts.link_mode)), std.zig.fmtId(@tagName(opts.link_mode)),
std.zig.fmtId(@tagName(opts.unwind_tables)),
opts.is_test, opts.is_test,
opts.single_threaded, opts.single_threaded,
std.zig.fmtId(@tagName(target.abi)), std.zig.fmtId(@tagName(target.abi)),

View File

@ -1261,12 +1261,15 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
// The "any" values provided by resolved config only account for // The "any" values provided by resolved config only account for
// explicitly-provided settings. We now make them additionally account // explicitly-provided settings. We now make them additionally account
// for default setting resolution. // for default setting resolution.
const any_unwind_tables = options.config.any_unwind_tables or options.root_mod.unwind_tables; const any_unwind_tables = switch (options.config.any_unwind_tables) {
.none => options.root_mod.unwind_tables,
.sync, .@"async" => |uwt| uwt,
};
const any_non_single_threaded = options.config.any_non_single_threaded or !options.root_mod.single_threaded; const any_non_single_threaded = options.config.any_non_single_threaded or !options.root_mod.single_threaded;
const any_sanitize_thread = options.config.any_sanitize_thread or options.root_mod.sanitize_thread; const any_sanitize_thread = options.config.any_sanitize_thread or options.root_mod.sanitize_thread;
const any_fuzz = options.config.any_fuzz or options.root_mod.fuzz; const any_fuzz = options.config.any_fuzz or options.root_mod.fuzz;
const link_eh_frame_hdr = options.link_eh_frame_hdr or any_unwind_tables; const link_eh_frame_hdr = options.link_eh_frame_hdr or any_unwind_tables != .none;
const build_id = options.build_id orelse .none; const build_id = options.build_id orelse .none;
const link_libc = options.config.link_libc; const link_libc = options.config.link_libc;
@ -1354,6 +1357,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
cache.hash.add(options.config.pie); cache.hash.add(options.config.pie);
cache.hash.add(options.config.lto); cache.hash.add(options.config.lto);
cache.hash.add(options.config.link_mode); cache.hash.add(options.config.link_mode);
cache.hash.add(options.config.any_unwind_tables);
cache.hash.add(options.function_sections); cache.hash.add(options.function_sections);
cache.hash.add(options.data_sections); cache.hash.add(options.data_sections);
cache.hash.add(link_libc); cache.hash.add(link_libc);
@ -5553,10 +5557,17 @@ pub fn addCCArgs(
try argv.append("-Werror=date-time"); try argv.append("-Werror=date-time");
} }
if (mod.unwind_tables) { switch (mod.unwind_tables) {
try argv.append("-funwind-tables"); .none => {
} else { try argv.append("-fno-unwind-tables");
try argv.append("-fno-unwind-tables"); try argv.append("-fno-asynchronous-unwind-tables");
},
.sync => {
// Need to override Clang's convoluted default logic.
try argv.append("-fno-asynchronous-unwind-tables");
try argv.append("-funwind-tables");
},
.@"async" => try argv.append("-fasynchronous-unwind-tables"),
} }
}, },
.shared_library, .ll, .bc, .unknown, .static_library, .object, .def, .zig, .res, .manifest => {}, .shared_library, .ll, .bc, .unknown, .static_library, .object, .def, .zig, .res, .manifest => {},
@ -6288,6 +6299,7 @@ pub const CrtFileOptions = struct {
function_sections: ?bool = null, function_sections: ?bool = null,
data_sections: ?bool = null, data_sections: ?bool = null,
omit_frame_pointer: ?bool = null, omit_frame_pointer: ?bool = null,
unwind_tables: ?std.builtin.UnwindTables = null,
pic: ?bool = null, pic: ?bool = null,
no_builtin: ?bool = null, no_builtin: ?bool = null,
}; };
@ -6349,7 +6361,8 @@ pub fn build_crt_file(
// Some libcs (e.g. musl) are opinionated about -fomit-frame-pointer. // Some libcs (e.g. musl) are opinionated about -fomit-frame-pointer.
.omit_frame_pointer = options.omit_frame_pointer orelse comp.root_mod.omit_frame_pointer, .omit_frame_pointer = options.omit_frame_pointer orelse comp.root_mod.omit_frame_pointer,
.valgrind = false, .valgrind = false,
.unwind_tables = false, // Some libcs (e.g. MinGW) are opinionated about -funwind-tables.
.unwind_tables = options.unwind_tables orelse .none,
// Some CRT objects (e.g. musl's rcrt1.o and Scrt1.o) are opinionated about PIC. // Some CRT objects (e.g. musl's rcrt1.o and Scrt1.o) are opinionated about PIC.
.pic = options.pic orelse comp.root_mod.pic, .pic = options.pic orelse comp.root_mod.pic,
.optimize_mode = comp.compilerRtOptMode(), .optimize_mode = comp.compilerRtOptMode(),

View File

@ -12,13 +12,14 @@ link_libunwind: bool,
/// True if and only if the c_source_files field will have nonzero length when /// True if and only if the c_source_files field will have nonzero length when
/// calling Compilation.create. /// calling Compilation.create.
any_c_source_files: bool, any_c_source_files: bool,
/// This is true if any Module has unwind_tables set explicitly to true. Until /// This is not `.none` if any `Module` has `unwind_tables` set explicitly to a
/// Compilation.create is called, it is possible for this to be false while in /// value other than `.none`. Until `Compilation.create()` is called, it is
/// fact all Module instances have unwind_tables=true due to the default /// possible for this to be `.none` while in fact all `Module` instances have
/// being unwind_tables=true. After Compilation.create is called this will /// `unwind_tables != .none` due to the default. After `Compilation.create()` is
/// also take into account the default setting, making this value true if and /// called, this will also take into account the default setting, making this
/// only if any Module has unwind_tables set to true. /// value `.sync` or `.@"async"` if and only if any `Module` has
any_unwind_tables: bool, /// `unwind_tables != .none`.
any_unwind_tables: std.builtin.UnwindTables,
/// This is true if any Module has single_threaded set explicitly to false. Until /// This is true if any Module has single_threaded set explicitly to false. Until
/// Compilation.create is called, it is possible for this to be false while in /// Compilation.create is called, it is possible for this to be false while in
/// fact all Module instances have single_threaded=false due to the default /// fact all Module instances have single_threaded=false due to the default
@ -85,7 +86,7 @@ pub const Options = struct {
any_non_single_threaded: bool = false, any_non_single_threaded: bool = false,
any_sanitize_thread: bool = false, any_sanitize_thread: bool = false,
any_fuzz: bool = false, any_fuzz: bool = false,
any_unwind_tables: bool = false, any_unwind_tables: std.builtin.UnwindTables = .none,
any_dyn_libs: bool = false, any_dyn_libs: bool = false,
any_c_source_files: bool = false, any_c_source_files: bool = false,
any_non_stripped: bool = false, any_non_stripped: bool = false,
@ -356,8 +357,11 @@ pub fn resolve(options: Options) ResolveError!Config {
break :b false; break :b false;
}; };
const any_unwind_tables = options.any_unwind_tables or const any_unwind_tables = b: {
link_libunwind or target_util.needUnwindTables(target); if (options.any_unwind_tables != .none) break :b options.any_unwind_tables;
break :b target_util.needUnwindTables(target, link_libunwind, options.any_sanitize_thread);
};
const link_mode = b: { const link_mode = b: {
const explicitly_exe_or_dyn_lib = switch (options.output_mode) { const explicitly_exe_or_dyn_lib = switch (options.output_mode) {

View File

@ -27,7 +27,7 @@ red_zone: bool,
sanitize_c: bool, sanitize_c: bool,
sanitize_thread: bool, sanitize_thread: bool,
fuzz: bool, fuzz: bool,
unwind_tables: bool, unwind_tables: std.builtin.UnwindTables,
cc_argv: []const []const u8, cc_argv: []const []const u8,
/// (SPIR-V) whether to generate a structured control flow graph or not /// (SPIR-V) whether to generate a structured control flow graph or not
structured_cfg: bool, structured_cfg: bool,
@ -91,7 +91,7 @@ pub const CreateOptions = struct {
/// other number means stack protection with that buffer size. /// other number means stack protection with that buffer size.
stack_protector: ?u32 = null, stack_protector: ?u32 = null,
red_zone: ?bool = null, red_zone: ?bool = null,
unwind_tables: ?bool = null, unwind_tables: ?std.builtin.UnwindTables = null,
sanitize_c: ?bool = null, sanitize_c: ?bool = null,
sanitize_thread: ?bool = null, sanitize_thread: ?bool = null,
fuzz: ?bool = null, fuzz: ?bool = null,
@ -112,7 +112,7 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module {
if (options.inherited.sanitize_thread == true) assert(options.global.any_sanitize_thread); if (options.inherited.sanitize_thread == true) assert(options.global.any_sanitize_thread);
if (options.inherited.fuzz == true) assert(options.global.any_fuzz); if (options.inherited.fuzz == true) assert(options.global.any_fuzz);
if (options.inherited.single_threaded == false) assert(options.global.any_non_single_threaded); if (options.inherited.single_threaded == false) assert(options.global.any_non_single_threaded);
if (options.inherited.unwind_tables == true) assert(options.global.any_unwind_tables); if (options.inherited.unwind_tables) |uwt| if (uwt != .none) assert(options.global.any_unwind_tables != .none);
if (options.inherited.error_tracing == true) assert(options.global.any_error_tracing); if (options.inherited.error_tracing == true) assert(options.global.any_error_tracing);
const resolved_target = options.inherited.resolved_target orelse options.parent.?.resolved_target; const resolved_target = options.inherited.resolved_target orelse options.parent.?.resolved_target;
@ -382,6 +382,7 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module {
.zig_backend = zig_backend, .zig_backend = zig_backend,
.output_mode = options.global.output_mode, .output_mode = options.global.output_mode,
.link_mode = options.global.link_mode, .link_mode = options.global.link_mode,
.unwind_tables = options.global.any_unwind_tables,
.is_test = options.global.is_test, .is_test = options.global.is_test,
.single_threaded = single_threaded, .single_threaded = single_threaded,
.link_libc = options.global.link_libc, .link_libc = options.global.link_libc,

View File

@ -8442,6 +8442,10 @@ fn failSymbol(func: *Func, comptime format: []const u8, args: anytype) InnerErro
} }
fn parseRegName(name: []const u8) ?Register { fn parseRegName(name: []const u8) ?Register {
// The `fp` alias for `s0` is awkward to fit into the current `Register` scheme, so for now we
// special-case it here.
if (std.mem.eql(u8, name, "fp")) return .s0;
return std.meta.stringToEnum(Register, name); return std.meta.stringToEnum(Register, name);
} }

View File

@ -2757,7 +2757,14 @@ flagpd1("fast"),
flagpd1("fastcp"), flagpd1("fastcp"),
flagpd1("fastf"), flagpd1("fastf"),
flagpd1("fasync-exceptions"), flagpd1("fasync-exceptions"),
flagpd1("fasynchronous-unwind-tables"), .{
.name = "fasynchronous-unwind-tables",
.syntax = .flag,
.zig_equivalent = .asynchronous_unwind_tables,
.pd1 = true,
.pd2 = false,
.psl = false,
},
flagpd1("fauto-import"), flagpd1("fauto-import"),
flagpd1("fauto-profile"), flagpd1("fauto-profile"),
flagpd1("fauto-profile-accurate"), flagpd1("fauto-profile-accurate"),
@ -3247,7 +3254,14 @@ flagpd1("fno-assume-sane-operator-new"),
flagpd1("fno-assume-unique-vtables"), flagpd1("fno-assume-unique-vtables"),
flagpd1("fno-assumptions"), flagpd1("fno-assumptions"),
flagpd1("fno-async-exceptions"), flagpd1("fno-async-exceptions"),
flagpd1("fno-asynchronous-unwind-tables"), .{
.name = "fno-asynchronous-unwind-tables",
.syntax = .flag,
.zig_equivalent = .no_asynchronous_unwind_tables,
.pd1 = true,
.pd2 = false,
.psl = false,
},
flagpd1("fno-auto-import"), flagpd1("fno-auto-import"),
flagpd1("fno-auto-profile"), flagpd1("fno-auto-profile"),
flagpd1("fno-auto-profile-accurate"), flagpd1("fno-auto-profile-accurate"),

View File

@ -3141,8 +3141,11 @@ pub const Object = struct {
} }, &o.builder); } }, &o.builder);
} }
try attributes.addFnAttr(.nounwind, &o.builder); try attributes.addFnAttr(.nounwind, &o.builder);
if (owner_mod.unwind_tables) { if (owner_mod.unwind_tables != .none) {
try attributes.addFnAttr(.{ .uwtable = Builder.Attribute.UwTable.default }, &o.builder); try attributes.addFnAttr(
.{ .uwtable = if (owner_mod.unwind_tables == .@"async") .@"async" else .sync },
&o.builder,
);
} }
if (owner_mod.no_builtin) { if (owner_mod.no_builtin) {
// The intent here is for compiler-rt and libc functions to not generate // The intent here is for compiler-rt and libc functions to not generate

View File

@ -397,7 +397,6 @@ pub fn buildLibCXXABI(comp: *Compilation, prog_node: std.Progress.Node) BuildErr
const optimize_mode = comp.compilerRtOptMode(); const optimize_mode = comp.compilerRtOptMode();
const strip = comp.compilerRtStrip(); const strip = comp.compilerRtStrip();
const unwind_tables = true;
const config = Compilation.Config.resolve(.{ const config = Compilation.Config.resolve(.{
.output_mode = output_mode, .output_mode = output_mode,
@ -409,7 +408,6 @@ pub fn buildLibCXXABI(comp: *Compilation, prog_node: std.Progress.Node) BuildErr
.root_optimize_mode = optimize_mode, .root_optimize_mode = optimize_mode,
.root_strip = strip, .root_strip = strip,
.link_libc = true, .link_libc = true,
.any_unwind_tables = unwind_tables,
.lto = comp.config.lto, .lto = comp.config.lto,
.any_sanitize_thread = comp.config.any_sanitize_thread, .any_sanitize_thread = comp.config.any_sanitize_thread,
}) catch |err| { }) catch |err| {
@ -440,7 +438,12 @@ pub fn buildLibCXXABI(comp: *Compilation, prog_node: std.Progress.Node) BuildErr
.valgrind = false, .valgrind = false,
.optimize_mode = optimize_mode, .optimize_mode = optimize_mode,
.structured_cfg = comp.root_mod.structured_cfg, .structured_cfg = comp.root_mod.structured_cfg,
.unwind_tables = unwind_tables, // See the `-fno-exceptions` logic for WASI.
// The old 32-bit x86 variant of SEH doesn't use tables.
.unwind_tables = if (target.os.tag == .wasi or (target.cpu.arch == .x86 and target.os.tag == .windows))
.none
else
.@"async",
.pic = comp.root_mod.pic, .pic = comp.root_mod.pic,
}, },
.global = config, .global = config,

View File

@ -65,7 +65,8 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: std.Progress.Node) BuildErr
.sanitize_c = false, .sanitize_c = false,
.sanitize_thread = false, .sanitize_thread = false,
// necessary so that libunwind can unwind through its own stack frames // necessary so that libunwind can unwind through its own stack frames
.unwind_tables = true, // The old 32-bit x86 variant of SEH doesn't use tables.
.unwind_tables = if (target.cpu.arch == .x86 and target.os.tag == .windows) .none else .@"async",
.pic = if (target_util.supports_fpic(target)) true else null, .pic = if (target_util.supports_fpic(target)) true else null,
.optimize_mode = comp.compilerRtOptMode(), .optimize_mode = comp.compilerRtOptMode(),
}, },

View File

@ -510,6 +510,7 @@ const usage_build_generic =
\\ -ffuzz Enable fuzz testing instrumentation \\ -ffuzz Enable fuzz testing instrumentation
\\ -fno-fuzz Disable fuzz testing instrumentation \\ -fno-fuzz Disable fuzz testing instrumentation
\\ -funwind-tables Always produce unwind table entries for all functions \\ -funwind-tables Always produce unwind table entries for all functions
\\ -fasync-unwind-tables Always produce asynchronous unwind table entries for all functions
\\ -fno-unwind-tables Never produce unwind table entries \\ -fno-unwind-tables Never produce unwind table entries
\\ -ferror-tracing Enable error tracing in ReleaseFast mode \\ -ferror-tracing Enable error tracing in ReleaseFast mode
\\ -fno-error-tracing Disable error tracing in Debug and ReleaseSafe mode \\ -fno-error-tracing Disable error tracing in Debug and ReleaseSafe mode
@ -1385,9 +1386,11 @@ fn buildOutputType(
} else if (mem.eql(u8, arg, "-fno-lto")) { } else if (mem.eql(u8, arg, "-fno-lto")) {
create_module.opts.lto = false; create_module.opts.lto = false;
} else if (mem.eql(u8, arg, "-funwind-tables")) { } else if (mem.eql(u8, arg, "-funwind-tables")) {
mod_opts.unwind_tables = true; mod_opts.unwind_tables = .sync;
} else if (mem.eql(u8, arg, "-fasync-unwind-tables")) {
mod_opts.unwind_tables = .@"async";
} else if (mem.eql(u8, arg, "-fno-unwind-tables")) { } else if (mem.eql(u8, arg, "-fno-unwind-tables")) {
mod_opts.unwind_tables = false; mod_opts.unwind_tables = .none;
} else if (mem.eql(u8, arg, "-fstack-check")) { } else if (mem.eql(u8, arg, "-fstack-check")) {
mod_opts.stack_check = true; mod_opts.stack_check = true;
} else if (mem.eql(u8, arg, "-fno-stack-check")) { } else if (mem.eql(u8, arg, "-fno-stack-check")) {
@ -1973,8 +1976,27 @@ fn buildOutputType(
} }
}, },
.no_stack_protector => mod_opts.stack_protector = 0, .no_stack_protector => mod_opts.stack_protector = 0,
.unwind_tables => mod_opts.unwind_tables = true, // The way these unwind table options are processed in GCC and Clang is crazy
.no_unwind_tables => mod_opts.unwind_tables = false, // convoluted, and we also don't know the target triple here, so this is all
// best-effort.
.unwind_tables => if (mod_opts.unwind_tables) |uwt| switch (uwt) {
.none => {
mod_opts.unwind_tables = .sync;
},
.sync, .@"async" => {},
} else {
mod_opts.unwind_tables = .sync;
},
.no_unwind_tables => mod_opts.unwind_tables = .none,
.asynchronous_unwind_tables => mod_opts.unwind_tables = .@"async",
.no_asynchronous_unwind_tables => if (mod_opts.unwind_tables) |uwt| switch (uwt) {
.none, .sync => {},
.@"async" => {
mod_opts.unwind_tables = .sync;
},
} else {
mod_opts.unwind_tables = .sync;
},
.nostdlib => { .nostdlib => {
create_module.opts.ensure_libc_on_non_freestanding = false; create_module.opts.ensure_libc_on_non_freestanding = false;
create_module.opts.ensure_libcpp_on_non_freestanding = false; create_module.opts.ensure_libcpp_on_non_freestanding = false;
@ -2788,8 +2810,15 @@ fn buildOutputType(
create_module.opts.any_sanitize_thread = true; create_module.opts.any_sanitize_thread = true;
if (mod_opts.fuzz == true) if (mod_opts.fuzz == true)
create_module.opts.any_fuzz = true; create_module.opts.any_fuzz = true;
if (mod_opts.unwind_tables == true) if (mod_opts.unwind_tables) |uwt| switch (uwt) {
create_module.opts.any_unwind_tables = true; .none => {},
.sync => if (create_module.opts.any_unwind_tables == .none) {
create_module.opts.any_unwind_tables = .sync;
},
.@"async" => {
create_module.opts.any_unwind_tables = .@"async";
},
};
if (mod_opts.strip == false) if (mod_opts.strip == false)
create_module.opts.any_non_stripped = true; create_module.opts.any_non_stripped = true;
if (mod_opts.error_tracing == true) if (mod_opts.error_tracing == true)
@ -5713,6 +5742,8 @@ pub const ClangArgIterator = struct {
no_lto, no_lto,
unwind_tables, unwind_tables,
no_unwind_tables, no_unwind_tables,
asynchronous_unwind_tables,
no_asynchronous_unwind_tables,
nostdlib, nostdlib,
nostdlib_cpp, nostdlib_cpp,
shared, shared,
@ -7435,8 +7466,15 @@ fn handleModArg(
create_module.opts.any_sanitize_thread = true; create_module.opts.any_sanitize_thread = true;
if (mod_opts.fuzz == true) if (mod_opts.fuzz == true)
create_module.opts.any_fuzz = true; create_module.opts.any_fuzz = true;
if (mod_opts.unwind_tables == true) if (mod_opts.unwind_tables) |uwt| switch (uwt) {
create_module.opts.any_unwind_tables = true; .none => {},
.sync => if (create_module.opts.any_unwind_tables == .none) {
create_module.opts.any_unwind_tables = .sync;
},
.@"async" => {
create_module.opts.any_unwind_tables = .@"async";
},
};
if (mod_opts.strip == false) if (mod_opts.strip == false)
create_module.opts.any_non_stripped = true; create_module.opts.any_non_stripped = true;
if (mod_opts.error_tracing == true) if (mod_opts.error_tracing == true)

View File

@ -24,6 +24,10 @@ pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progre
var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa); var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa);
defer arena_allocator.deinit(); defer arena_allocator.deinit();
const arena = arena_allocator.allocator(); const arena = arena_allocator.allocator();
const target = comp.getTarget();
// The old 32-bit x86 variant of SEH doesn't use tables.
const unwind_tables: std.builtin.UnwindTables = if (target.cpu.arch != .x86) .@"async" else .none;
switch (crt_file) { switch (crt_file) {
.crt2_o => { .crt2_o => {
@ -41,7 +45,9 @@ pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progre
.owner = undefined, .owner = undefined,
}, },
}; };
return comp.build_crt_file("crt2", .Obj, .@"mingw-w64 crt2.o", prog_node, &files, .{}); return comp.build_crt_file("crt2", .Obj, .@"mingw-w64 crt2.o", prog_node, &files, .{
.unwind_tables = unwind_tables,
});
}, },
.dllcrt2_o => { .dllcrt2_o => {
@ -56,7 +62,9 @@ pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progre
.owner = undefined, .owner = undefined,
}, },
}; };
return comp.build_crt_file("dllcrt2", .Obj, .@"mingw-w64 dllcrt2.o", prog_node, &files, .{}); return comp.build_crt_file("dllcrt2", .Obj, .@"mingw-w64 dllcrt2.o", prog_node, &files, .{
.unwind_tables = unwind_tables,
});
}, },
.mingw32_lib => { .mingw32_lib => {
@ -73,7 +81,6 @@ pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progre
.owner = undefined, .owner = undefined,
}); });
} }
const target = comp.getTarget();
if (target.cpu.arch == .x86 or target.cpu.arch == .x86_64) { if (target.cpu.arch == .x86 or target.cpu.arch == .x86_64) {
for (mingw32_x86_src) |dep| { for (mingw32_x86_src) |dep| {
try c_source_files.append(.{ try c_source_files.append(.{
@ -118,7 +125,9 @@ pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progre
} else { } else {
@panic("unsupported arch"); @panic("unsupported arch");
} }
return comp.build_crt_file("mingw32", .Lib, .@"mingw-w64 mingw32.lib", prog_node, c_source_files.items, .{}); return comp.build_crt_file("mingw32", .Lib, .@"mingw-w64 mingw32.lib", prog_node, c_source_files.items, .{
.unwind_tables = unwind_tables,
});
}, },
} }
} }

View File

@ -4,6 +4,7 @@ const assert = std.debug.assert;
const Type = @import("Type.zig"); const Type = @import("Type.zig");
const AddressSpace = std.builtin.AddressSpace; const AddressSpace = std.builtin.AddressSpace;
const Alignment = @import("InternPool.zig").Alignment; const Alignment = @import("InternPool.zig").Alignment;
const Compilation = @import("Compilation.zig");
const Feature = @import("Zcu.zig").Feature; const Feature = @import("Zcu.zig").Feature;
pub const default_stack_protector_buffer_size = 4; pub const default_stack_protector_buffer_size = 4;
@ -408,8 +409,16 @@ pub fn clangSupportsNoImplicitFloatArg(target: std.Target) bool {
}; };
} }
pub fn needUnwindTables(target: std.Target) bool { pub fn needUnwindTables(target: std.Target, libunwind: bool, libtsan: bool) std.builtin.UnwindTables {
return target.os.tag == .windows or target.isDarwin() or std.debug.Dwarf.abi.supportsUnwinding(target); if (target.os.tag == .windows) {
// The old 32-bit x86 variant of SEH doesn't use tables.
return if (target.cpu.arch != .x86) .@"async" else .none;
}
if (target.os.tag.isDarwin()) return .@"async";
if (libunwind) return .@"async";
if (libtsan) return .@"async";
if (std.debug.Dwarf.abi.supportsUnwinding(target)) return .@"async";
return .none;
} }
pub fn defaultAddressSpace( pub fn defaultAddressSpace(

View File

@ -23,7 +23,7 @@ pub fn build(b: *std.Build) void {
.root_source_file = b.path("unwind.zig"), .root_source_file = b.path("unwind.zig"),
.target = target, .target = target,
.optimize = optimize, .optimize = optimize,
.unwind_tables = if (target.result.isDarwin()) true else null, .unwind_tables = if (target.result.isDarwin()) .@"async" else null,
.omit_frame_pointer = false, .omit_frame_pointer = false,
}); });
@ -46,7 +46,7 @@ pub fn build(b: *std.Build) void {
.root_source_file = b.path("unwind.zig"), .root_source_file = b.path("unwind.zig"),
.target = target, .target = target,
.optimize = optimize, .optimize = optimize,
.unwind_tables = true, .unwind_tables = .@"async",
.omit_frame_pointer = true, .omit_frame_pointer = true,
}); });
@ -85,7 +85,7 @@ pub fn build(b: *std.Build) void {
.root_source_file = b.path("shared_lib_unwind.zig"), .root_source_file = b.path("shared_lib_unwind.zig"),
.target = target, .target = target,
.optimize = optimize, .optimize = optimize,
.unwind_tables = if (target.result.isDarwin()) true else null, .unwind_tables = if (target.result.isDarwin()) .@"async" else null,
.omit_frame_pointer = true, .omit_frame_pointer = true,
}); });

View File

@ -110,6 +110,14 @@ const known_options = [_]KnownOpt{
.name = "fno-unwind-tables", .name = "fno-unwind-tables",
.ident = "no_unwind_tables", .ident = "no_unwind_tables",
}, },
.{
.name = "fasynchronous-unwind-tables",
.ident = "asynchronous_unwind_tables",
},
.{
.name = "fno-asynchronous-unwind-tables",
.ident = "no_asynchronous_unwind_tables",
},
.{ .{
.name = "nolibc", .name = "nolibc",
.ident = "nostdlib", .ident = "nostdlib",