Skip to content

std.debug: greatly expand target support for segfault handling/unwinding, and remove public ucontext_t completely - #25516

Merged
alexrp merged 6 commits into
ziglang:masterfrom
alexrp:std-debug
Oct 10, 2025
Merged

std.debug: greatly expand target support for segfault handling/unwinding, and remove public ucontext_t completely#25516
alexrp merged 6 commits into
ziglang:masterfrom
alexrp:std-debug

Conversation

@alexrp

@alexrpalexrp commented Oct 9, 2025

Copy link
Copy Markdown
Member

Codeberg: https://codeberg.org/ziglang/zig/pulls/3

#25227 laid the groundwork for me to finally do some porting and other improvements in std.debug that I'd been meaning to do for ages. Thanks @mlugg!

Greatly expanded target support for segfault handling/unwinding

This PR combined with

results in us having greatly expanded target coverage for segfault handling and stack traces in general. There's still #25422 which I plan to tackle after this, but on basically every other target that sees real use with Zig (and probably even a few that don't), we now have working stack traces on crashes and when using DebugAllocator.

I made a couple of decisions for this based on the fact that we don't expose the signal_ucontext_t type outside of std.debug.cpu_context:

  • Adding all the floating point and vector state to every ucontext_t and mcontext_t variant was way, way too much work, especially when we don't even use the stuff. So I deleted all that and kept only the bare minimum needed to reach into general-purpose registers.
  • There is no particularly compelling reason to stick to the naming and struct nesting used in the system headers. So we can actually unify the access patterns for almost all of these variants by taking some liberties here; as a result, fromPosixSignalContext() is now much nicer to read and extend.

Removal of ucontext_t and related types/functions

This type is useful for two things:

  • Doing non-local control flow with ucontext.h functions.
  • Inspecting machine state in a signal handler.

The first use case is not one we support; we no longer expose bindings to those functions in the standard library. They're also deprecated in POSIX and, as a result, not available in musl.

The second use case is valid, but is very poorly served by the standard library. As evidenced by my changes to std.debug.cpu_context.signal_context_t, users will be better served rolling their own ucontext_t and especially mcontext_t types which fit their specific situation. Further, these types tend to evolve frequently as architectures evolve, and the standard library has not done a good job keeping up, or even providing them for all supported targets.

@alexrpalexrp added breaking Implementing this issue could cause existing code to no longer compile or have different behavior. release notes This PR should be mentioned in the release notes. labels Oct 9, 2025
@alexrp
alexrp requested a review from mluggOctober 9, 2025 08:18
@alexrpalexrp changed the title Std debugstd.debug: greatly expand target support for segfault handling/unwinding, and remove public ucontext_t completelyOct 9, 2025

@mluggmlugg left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great!

@linusglinusg left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also LGTM. I really like the addition of more reference links :)

},

.illumos => &.{
.x86,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Illumos and Solaris haven't supported x86 for a while now. You can run old binaries but it's discouraged to create new ones.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know Solaris hasn't, but illumos seems to still support userspace 32-bit applications? http://www.tribblix.org/32bit.html

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It only matters how you define can_unwind:

  1. Does this arch-os-abi combo support unwinding?
  2. Does the Zig compiler emit code for this combo?

If 2 doesn't matter then you may as well add x86 to Solaris as well. It seems like it doesn't since sparc64-solaris isn't there.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sparc64-solaris is just a to-do along with sparc64-linux and others; I'll do a later pull request that adds full SPARC unwinding to all of std.debug (unless @mlugg beats me to it?).

In general, I'm going off test/llvm_targets.zig for what targets are relevant here. That file in turn boils down to which targets are still supported by the developers/vendor of the OS. If a target is missing here, it's probably because it's missing in test/llvm_targets.zig, while a @compileError represents work to be done.

That said, I'm not sure I entirely understand point 2; the Zig compiler should in theory be able to emit code for x86-solaris, sparc-solaris, sparc64-solaris, etc. It's just that if a target is effectively dead and no one cares about it, we're not going to bother maintaining code paths for it.

I made a couple of decisions for this based on the fact that we don't expose the
signal_ucontext_t type outside of the file:
* Adding all the floating point and vector state to every ucontext_t and
mcontext_t variant was way, way too much work, especially when we don't even
use the stuff. So I deleted all that and kept only the bare minimum needed to
reach into general-purpose registers.
* There is no particularly compelling reason to stick to the naming and struct
nesting used in the system headers. So we can actually unify the access
patterns for almost all of these variants by taking some liberties here; as a
result, fromPosixSignalContext() is now much nicer to read and extend.
This type is useful for two things:
* Doing non-local control flow with ucontext.h functions.
* Inspecting machine state in a signal handler.
The first use case is not one we support; we no longer expose bindings to those
functions in the standard library. They're also deprecated in POSIX and, as a
result, not available in musl.
The second use case is valid, but is very poorly served by the standard library.
As evidenced by my changes to std.debug.cpu_context.signal_context_t, users will
be better served rolling their own ucontext_t and especially mcontext_t types
which fit their specific situation. Further, these types tend to evolve
frequently as architectures evolve, and the standard library has not done a good
job keeping up, or even providing them for all supported targets.
@alexrp

Copy link
Copy Markdown
MemberAuthor

Added some missing align(8) and align(16) to a few of the mcontext_ts since we're omitting the later field declarations that would normally cause them to gain that higher type alignment. Something to keep in mind for future work in this file.

@alexrp
alexrp enabled auto-merge October 10, 2025 02:46
@alexrp
alexrp merged commit f010a31 into ziglang:masterOct 10, 2025
9 checks passed
@alexrp
alexrp deleted the std-debug branch October 10, 2025 12:32
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breakingImplementing this issue could cause existing code to no longer compile or have different behavior.release notesThis PR should be mentioned in the release notes.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@alexrp@mlugg@The-King-of-Toasters@linusg