As with __zig_elf_auxv, we need a canonical location for __tls_get_addr (or
__tls_get_offset on s390x), and compiler-rt is the obvious place. But that also
means that moving all of std.os.linux.tls there just makes sense. I decided not
to namespace this under linux because it's expected that most of this code will
apply to the BSDs too.
The ABI that std now expects is:
extern fn __zig_elf_static_tls_init(phdrs: [*]elf.ElfN.Phdr, phnum: usize) void;
extern fn __zig_elf_static_tls_fill(area: [*]u8) usize;
extern var __zig_elf_static_tls: extern struct {
size: usize,
alignment: usize,
gdt_entry_number: usize,
};
Users can override all three weak symbols with strong symbols if they want to
provide their own ELF TLS implementation, which may be useful to do when doing
manual dynamic loading from a static executable. Note that __tls_get_addr (or
__tls_get_offset on s390x) must also be provided, per the ELF TLS specification.
Our minimum kernel version was released several years after this kernel bug was
fixed. On top of that, it's a hacky workaround that depends on abicalls to work.
Well past time to get rid of it.
On SPARC, the kernel needs to be able to restore the current register window
from the stack when returning from a syscall. That presents a bit of a problem
in freeAndExit() since we're deallocating the stack! The good news is that,
since we do not care about the contents of the incoming and local registers at
that point, we can just tell the kernel that our stack is an undefined global
buffer.
This didn't manifest in QEMU but does reproduce deterministically on a real
kernel/machine.
This isn't causing any functional problem today, but technically
`mapped[tls_offset..]` runs past the tls part of `mapped` and into
the `Instance` storage, and currently `prepareArea()` memsets its
entire argument to zero. It is only the current layout and
initialization order of `mapped` that prevents this from being a
problem. Being more precise here avoids future footguns if any of
that changes.
Replaced by the lockStderr functions of std.Io. Trying to make
`std.process.stderr_thread_mutex` be a bridge across different Io
implementations didn't work in practice.
- delete std.Thread.Futex
- delete std.Thread.Mutex
- delete std.Thread.Semaphore
- delete std.Thread.Condition
- delete std.Thread.RwLock
- delete std.once
std.Thread.Mutex.Recursive remains... for now. it will be replaced with
a special purpose mechanism used only by panic logic.
std.Io.Threaded exposes mutexLock and mutexUnlock for the advanced case
when you need to call them directly.
This allows stack overflows to print stack traces. The size of the
sigaltstack (and whether it is actually set) can be configured by
setting `std.Options.signal_stack_size`.
The default value for the signal stack size was chosen experimentally by
doubling the value required to get stack traces on stack overflow with
the self-hosted x86_64 backend. While some targets may typically use
more stack space than x86_64-linux, the self-hosted x86_64 backend is
quite wasteful with stack at the moment, making it a fair benchmark.
Executables produced by the LLVM backend should have lower stack usage.
As the comment explains, if a signal were to arrive between a detached
thread's `munmap` and `exit` calls, the signal handler would immediately
trigger SIGSEGV due to the stack being unmapped. To solve this, we need
to block all signals before entering this logic. The musl implementation
which this logic was ported from does this exact thing; that logic was
just lost when porting.
Notably, this would lead to a crash with no stack trace, because the
SIGSEGV handler would itself crash due to the missing stack.
TL;DR: "r" considered harmful.
If LLVM chose registers badly, the inline asm which cleans up a thread
on Linux could, on all architectures other than x86_64, clobber either
`munmap` argument with the other argument *or* with the syscall number.
This would cause munmap to return EINVAL, and we would literally *never*
free the thread memory, which isn't ideal.
As it turns out, this was happening on MIPS, and was the cause of the
failures we've recently been seeing for that target: QEMU genuinely was
running out of memory (or at least, the virtualized address space was
getting too fragmented to map many contiguous pages). I've therefore
re-enabled a test which was disabled due to that flakiness.
This bug was accidentally fixed for x86_64 back in 2022 (see 59e33b447),
which probably helped it to go unnoticed for as long as it did!
Resolves: https://codeberg.org/ziglang/zig/issues/30216
* std.option allows overriding the debug Io instance
* if the default is used, start code initializes environ and argv0
also fix some places that needed recancel(), thanks mlugg!
See #30562
It's better to avoid references to this global variable, but, in the
cases where it's needed, such as in std.debug.print and collecting stack
traces, better to share the same instance.