aboutsummaryrefslogtreecommitdiff
path: root/lib/std/debug/Dwarf.zig
diff options
context:
space:
mode:
authormlugg <mlugg@mlugg.co.uk>2025-09-17 18:38:11 +0100
committermlugg <mlugg@mlugg.co.uk>2025-09-30 13:44:54 +0100
commita18fd41064493e742eacebc88e2afeadd54ff6f0 (patch)
tree1081fbd6d3c64cf1f583ae3188ab05e0320f03d9 /lib/std/debug/Dwarf.zig
parentb578cca022f4c9ce94439e2ee795639b3a23c8f5 (diff)
downloadzig-a18fd41064493e742eacebc88e2afeadd54ff6f0.tar.gz
zig-a18fd41064493e742eacebc88e2afeadd54ff6f0.zip
std: rework/remove ucontext_t
Our usage of `ucontext_t` in the standard library was kind of problematic. We unnecessarily mimiced libc-specific structures, and our `getcontext` implementation was overkill for our use case of stack tracing. This commit introduces a new namespace, `std.debug.cpu_context`, which contains "context" types for various architectures (currently x86, x86_64, ARM, and AARCH64) containing the general-purpose CPU registers; the ones needed in practice for stack unwinding. Each implementation has a function `current` which populates the structure using inline assembly. The structure is user-overrideable, though that should only be necessary if the standard library does not have an implementation for the *architecture*: that is to say, none of this is OS-dependent. Of course, in POSIX signal handlers, we get a `ucontext_t` from the kernel. The function `std.debug.cpu_context.fromPosixSignalContext` converts this to a `std.debug.cpu_context.Native` with a big ol' target switch. This functionality is not exposed from `std.c` or `std.posix`, and neither are `ucontext_t`, `mcontext_t`, or `getcontext`. The rationale is that these types and functions do not conform to a specific ABI, and in fact tend to get updated over time based on CPU features and extensions; in addition, different libcs use different structures which are "partially compatible" with the kernel structure. Overall, it's a mess, but all we need is the kernel context, so we can just define a kernel-compatible structure as long as we don't claim C compatibility by putting it in `std.c` or `std.posix`. This change resulted in a few nice `std.debug` simplifications, but nothing too noteworthy. However, the main benefit of this change is that DWARF unwinding---sometimes necessary for collecting stack traces reliably---now requires far less target-specific integration. Also fix a bug I noticed in `PageAllocator` (I found this due to a bug in my distro's QEMU distribution; thanks, broken QEMU patch!) and I think a couple of minor bugs in `std.debug`. Resolves: #23801 Resolves: #23802
Diffstat (limited to 'lib/std/debug/Dwarf.zig')
-rw-r--r--lib/std/debug/Dwarf.zig57
1 files changed, 55 insertions, 2 deletions
diff --git a/lib/std/debug/Dwarf.zig b/lib/std/debug/Dwarf.zig
index 9a70746b0a..655a65b709 100644
--- a/lib/std/debug/Dwarf.zig
+++ b/lib/std/debug/Dwarf.zig
@@ -27,7 +27,6 @@ const Reader = std.Io.Reader;
const Dwarf = @This();
pub const expression = @import("Dwarf/expression.zig");
-pub const abi = @import("Dwarf/abi.zig");
pub const call_frame = @import("Dwarf/call_frame.zig");
pub const Unwind = @import("Dwarf/Unwind.zig");
@@ -1415,7 +1414,7 @@ pub fn readUnitHeader(r: *Reader, endian: Endian) ScanError!UnitHeader {
}
/// Returns the DWARF register number for an x86_64 register number found in compact unwind info
-pub fn compactUnwindToDwarfRegNumber(unwind_reg_number: u3) !u8 {
+pub fn compactUnwindToDwarfRegNumber(unwind_reg_number: u3) !u16 {
return switch (unwind_reg_number) {
1 => 3, // RBX
2 => 12, // R12
@@ -1427,6 +1426,60 @@ pub fn compactUnwindToDwarfRegNumber(unwind_reg_number: u3) !u8 {
};
}
+/// Returns `null` for CPU architectures without an instruction pointer register.
+pub fn ipRegNum(arch: std.Target.Cpu.Arch) ?u16 {
+ return switch (arch) {
+ .x86 => 8,
+ .x86_64 => 16,
+ .arm, .armeb, .thumb, .thumbeb => 15,
+ .aarch64, .aarch64_be => 32,
+ else => null,
+ };
+}
+
+pub fn fpRegNum(arch: std.Target.Cpu.Arch) u16 {
+ return switch (arch) {
+ .x86 => 5,
+ .x86_64 => 6,
+ .arm, .armeb, .thumb, .thumbeb => 11,
+ .aarch64, .aarch64_be => 29,
+ else => unreachable,
+ };
+}
+
+pub fn spRegNum(arch: std.Target.Cpu.Arch) u16 {
+ return switch (arch) {
+ .x86 => 4,
+ .x86_64 => 7,
+ .arm, .armeb, .thumb, .thumbeb => 13,
+ .aarch64, .aarch64_be => 31,
+ else => unreachable,
+ };
+}
+
+/// Tells whether unwinding for this target is supported by the Dwarf standard.
+///
+/// See also `std.debug.SelfInfo.supports_unwinding` which tells whether the Zig
+/// standard library has a working implementation of unwinding for this target.
+pub fn supportsUnwinding(target: *const std.Target) bool {
+ return switch (target.cpu.arch) {
+ .amdgcn,
+ .nvptx,
+ .nvptx64,
+ .spirv32,
+ .spirv64,
+ => false,
+
+ // Enabling this causes relocation errors such as:
+ // error: invalid relocation type R_RISCV_SUB32 at offset 0x20
+ .riscv64, .riscv64be, .riscv32, .riscv32be => false,
+
+ // Conservative guess. Feel free to update this logic with any targets
+ // that are known to not support Dwarf unwinding.
+ else => true,
+ };
+}
+
/// This function is to make it handy to comment out the return and make it
/// into a crash when working on this file.
pub fn bad() error{InvalidDebugInfo} {