authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-09-05 21:28:18+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-09-30 13:44:51+01:00
log9859440d83e5ef17d353be39f32f2dc0b9ce0e02
treec94b29a9be0a120d6df33509d50e46915f488a82
parentc2ada49354897f89a2e2030ad1e083a8ee9b7c3b
signaturelock-open Commit is signed but in an unrecognized format.

add freestanding support IN THEORY

untested because this branch has errors rn

5 files changed, 93 insertions(+), 90 deletions(-)

lib/std/debug.zig+14-11
...@@ -14,6 +14,8 @@ const builtin = @import("builtin");...@@ -14,6 +14,8 @@ const builtin = @import("builtin");
14const native_arch = builtin.cpu.arch;14const native_arch = builtin.cpu.arch;
15const native_os = builtin.os.tag;15const native_os = builtin.os.tag;
1616
17const root = @import("root");
18
17pub const Dwarf = @import("debug/Dwarf.zig");19pub const Dwarf = @import("debug/Dwarf.zig");
18pub const Pdb = @import("debug/Pdb.zig");20pub const Pdb = @import("debug/Pdb.zig");
19pub const SelfInfo = @import("debug/SelfInfo.zig");21pub const SelfInfo = @import("debug/SelfInfo.zig");
...@@ -942,19 +944,15 @@ fn printLineInfo(...@@ -942,19 +944,15 @@ fn printLineInfo(
942 tty_config.setColor(writer, .reset) catch {};944 tty_config.setColor(writer, .reset) catch {};
943 }945 }
944 try writer.writeAll("\n");946 try writer.writeAll("\n");
945 } else |err| switch (err) {
946 error.WriteFailed => |e| return e,
947 else => {
948 // Ignore everything else. Seeing some lines in the trace without the associated
949 // source line printed is a far better user experience than interleaving the
950 // trace with a load of filesystem error crap. The user can always just open the
951 // source file themselves to see the line.
952 },
953 }947 }
954 }948 }
955 }949 }
956}950}
957fn printLineFromFile(writer: *Writer, source_location: SourceLocation) !void {951fn printLineFromFile(writer: *Writer, source_location: SourceLocation) !void {
952 if (@hasDecl(root, "debug") and @hasDecl(root.debug, "printLineFromFile")) {
953 return root.debug.printLineFromFile(writer, source_location);
954 }
955
958 // Need this to always block even in async I/O mode, because this could potentially956 // Need this to always block even in async I/O mode, because this could potentially
959 // be called from e.g. the event loop code crashing.957 // be called from e.g. the event loop code crashing.
960 var f = try fs.cwd().openFile(source_location.file_name, .{});958 var f = try fs.cwd().openFile(source_location.file_name, .{});
...@@ -1139,11 +1137,16 @@ test printLineFromFile {...@@ -1139,11 +1137,16 @@ test printLineFromFile {
11391137
1140/// TODO multithreaded awareness1138/// TODO multithreaded awareness
1141var debug_info_arena: ?std.heap.ArenaAllocator = null;1139var debug_info_arena: ?std.heap.ArenaAllocator = null;
1140var debug_info_fba: std.heap.FixedBufferAllocator = .init(&debug_info_fba_buf);
1141var debug_info_fba_buf: [1024 * 1024 * 4]u8 = undefined;
1142fn getDebugInfoAllocator() mem.Allocator {1142fn getDebugInfoAllocator() mem.Allocator {
1143 if (debug_info_arena == null) {1143 if (false) {
1144 debug_info_arena = .init(std.heap.page_allocator);1144 if (debug_info_arena == null) {
1145 debug_info_arena = .init(std.heap.page_allocator);
1146 }
1147 return debug_info_arena.?.allocator();
1145 }1148 }
1146 return debug_info_arena.?.allocator();1149 return debug_info_fba.allocator();
1147}1150}
11481151
1149/// Whether or not the current target can print useful debug information when a segfault occurs.1152/// Whether or not the current target can print useful debug information when a segfault occurs.
lib/std/debug/SelfInfo.zig+57-79
...@@ -14,6 +14,8 @@ const Dwarf = std.debug.Dwarf;...@@ -14,6 +14,8 @@ const Dwarf = std.debug.Dwarf;
14const regBytes = Dwarf.abi.regBytes;14const regBytes = Dwarf.abi.regBytes;
15const regValueNative = Dwarf.abi.regValueNative;15const regValueNative = Dwarf.abi.regValueNative;
1616
17const root = @import("root");
18
17const SelfInfo = @This();19const SelfInfo = @This();
1820
19modules: std.AutoArrayHashMapUnmanaged(usize, Module.DebugInfo),21modules: std.AutoArrayHashMapUnmanaged(usize, Module.DebugInfo),
...@@ -33,49 +35,12 @@ pub const Error = error{...@@ -33,49 +35,12 @@ pub const Error = error{
33};35};
3436
35/// Indicates whether the `SelfInfo` implementation has support for this target.37/// Indicates whether the `SelfInfo` implementation has support for this target.
36pub const target_supported: bool = switch (native_os) {38pub const target_supported: bool = Module != void;
37 .linux,
38 .freebsd,
39 .netbsd,
40 .dragonfly,
41 .openbsd,
42 .macos,
43 .solaris,
44 .illumos,
45 .windows,
46 => true,
47 else => false,
48};
4939
50/// Indicates whether unwinding for the host is *implemented* here in the Zig40/// Indicates whether the `SelfInfo` implementation has support for unwinding on this target.
51/// standard library.
52///41///
53/// See also `Dwarf.abi.supportsUnwinding` which tells whether Dwarf supports42/// For whether DWARF unwinding is *theoretically* possible, see `Dwarf.abi.supportsUnwinding`.
54/// unwinding on a target *in theory*.43pub const supports_unwinding: bool = Module.supports_unwinding;
55pub const supports_unwinding: bool = switch (builtin.target.cpu.arch) {
56 .x86 => switch (builtin.target.os.tag) {
57 .linux, .netbsd, .solaris, .illumos => true,
58 else => false,
59 },
60 .x86_64 => switch (builtin.target.os.tag) {
61 .linux, .netbsd, .freebsd, .openbsd, .macos, .ios, .solaris, .illumos => true,
62 else => false,
63 },
64 .arm, .armeb, .thumb, .thumbeb => switch (builtin.target.os.tag) {
65 .linux => true,
66 else => false,
67 },
68 .aarch64, .aarch64_be => switch (builtin.target.os.tag) {
69 .linux, .netbsd, .freebsd, .macos, .ios => true,
70 else => false,
71 },
72 // Unwinding is possible on other targets but this implementation does
73 // not support them...yet!
74 else => false,
75};
76comptime {
77 if (supports_unwinding) assert(Dwarf.abi.supportsUnwinding(&builtin.target));
78}
7944
80pub const init: SelfInfo = .{45pub const init: SelfInfo = .{
81 .modules = .empty,46 .modules = .empty,
...@@ -114,48 +79,61 @@ pub fn getModuleNameForAddress(self: *SelfInfo, gpa: Allocator, address: usize)...@@ -114,48 +79,61 @@ pub fn getModuleNameForAddress(self: *SelfInfo, gpa: Allocator, address: usize)
114 return module.name;79 return module.name;
115}80}
11681
117/// This type contains the target-specific implementation. It must expose the following declarations:82/// `void` indicates that `SelfInfo` is not supported for this target.
118///83///
119/// * `LookupCache: type`, with the following declarations unless `LookupCache == void`:84/// This type contains the target-specific implementation. Logically, a `Module` represents a subset
120/// * `init: LookupCache`85/// of the executable with its own debug information. This typically corresponds to what ELF calls a
121/// * `deinit: fn (*LookupCache, Allocator) void`86/// module, i.e. a shared library or executable image, but could be anything. For instance, it would
122/// * `lookup: fn (*LookupCache, Allocator, address: usize) !Module`87/// be valid to consider the entire application one module, or on the other hand to consider each
123/// * `key: fn (*const Module) usize`88/// object file a module.
124/// * `DebugInfo: type`, with the following declarations:
125/// * `DebugInfo.init: DebugInfo`
126/// * `getSymbolAtAddress: fn (*const Module, Allocator, *DebugInfo, address: usize) !std.debug.Symbol`
127///89///
128/// If unwinding is supported on this target, it must additionally expose the following declarations:90/// This type must must expose the following declarations:
129///91///
130/// * `unwindFrame: fn (*const Module, Allocator, *DebugInfo, *UnwindContext) !usize`92/// ```
131const Module = switch (native_os) {93/// /// Holds state cached by the implementation between calls to `lookup`.
132 else => {}, // Dwarf, // TODO MLUGG: it's this on master but that's definitely broken atm...94/// /// This may be `void`, in which case the inner declarations can be omitted.
133 .linux, .netbsd, .freebsd, .dragonfly, .openbsd, .haiku, .solaris, .illumos => @import("SelfInfo/ElfModule.zig"),95/// pub const LookupCache = struct {
134 .macos, .ios, .watchos, .tvos, .visionos => @import("SelfInfo/DarwinModule.zig"),96/// pub const init: LookupCache;
135 .uefi, .windows => @import("SelfInfo/WindowsModule.zig"),97/// pub fn deinit(lc: *LookupCache, gpa: Allocator) void;
136 .wasi, .emscripten => struct {98/// };
137 const LookupCache = void;99/// /// Holds debug information associated with a particular `Module`.
138 fn lookup(cache: *LookupCache, gpa: Allocator, address: usize) !Module {100/// pub const DebugInfo = struct {
139 _ = cache;101/// pub const init: DebugInfo;
140 _ = gpa;102/// };
141 _ = address;103/// /// Finds the `Module` corresponding to `address`.
142 @panic("TODO implement lookup module for Wasm");104/// pub fn lookup(lc: *LookupCache, gpa: Allocator, address: usize) SelfInfo.Error!Module;
143 }105/// /// Returns a unique identifier for this `Module`, such as a load address.
144 const DebugInfo = struct {106/// pub fn key(mod: *const Module) usize;
145 const init: DebugInfo = .{};107/// /// Locates and loads location information for the symbol corresponding to `address`.
146 };108/// pub fn getSymbolAtAddress(
147 fn getSymbolAtAddress(module: *const Module, gpa: Allocator, di: *DebugInfo, address: usize) !std.debug.Symbol {109/// mod: *const Module,
148 _ = module;110/// gpa: Allocator,
149 _ = gpa;111/// di: *DebugInfo,
150 _ = di;112/// address: usize,
151 _ = address;113/// ) SelfInfo.Error!std.debug.Symbol;
152 unreachable;114/// /// Whether a reliable stack unwinding strategy, such as DWARF unwinding, is available.
153 }115/// pub const supports_unwinding: bool;
154 },116/// /// Only required if `supports_unwinding == true`. Unwinds a single stack frame and returns
117/// /// the next return address (which may be 0 indicating end of stack). This is currently
118/// /// specialized to DWARF unwinding.
119/// pub fn unwindFrame(
120/// mod: *const Module,
121/// gpa: Allocator,
122/// di: *DebugInfo,
123/// ctx: *SelfInfo.UnwindContext,
124/// ) SelfInfo.Error!usize;
125/// ```
126const Module: type = Module: {
127 if (@hasDecl(root, "debug") and @hasDecl(root.debug, "Module")) {
128 break :Module root.debug.Module;
129 }
130 break :Module switch (native_os) {
131 .linux, .netbsd, .freebsd, .dragonfly, .openbsd, .haiku, .solaris, .illumos => @import("SelfInfo/ElfModule.zig"),
132 .macos, .ios, .watchos, .tvos, .visionos => @import("SelfInfo/DarwinModule.zig"),
133 .uefi, .windows => @import("SelfInfo/WindowsModule.zig"),
134 else => void,
135 };
155};136};
156test {
157 _ = Module;
158}
159137
160pub const UnwindContext = struct {138pub const UnwindContext = struct {
161 gpa: Allocator, // MLUGG TODO: make unmanaged (also maybe rename this type, DwarfUnwindContext or smth idk)139 gpa: Allocator, // MLUGG TODO: make unmanaged (also maybe rename this type, DwarfUnwindContext or smth idk)
lib/std/debug/SelfInfo/DarwinModule.zig+1
...@@ -251,6 +251,7 @@ pub fn getSymbolAtAddress(module: *const DarwinModule, gpa: Allocator, di: *Debu...@@ -251,6 +251,7 @@ pub fn getSymbolAtAddress(module: *const DarwinModule, gpa: Allocator, di: *Debu
251 ) catch null,251 ) catch null,
252 };252 };
253}253}
254pub const supports_unwinding: bool = true;
254/// Unwind a frame using MachO compact unwind info (from __unwind_info).255/// Unwind a frame using MachO compact unwind info (from __unwind_info).
255/// If the compact encoding can't encode a way to unwind a frame, it will256/// If the compact encoding can't encode a way to unwind a frame, it will
256/// defer unwinding to DWARF, in which case `.eh_frame` will be used if available.257/// defer unwinding to DWARF, in which case `.eh_frame` will be used if available.
lib/std/debug/SelfInfo/ElfModule.zig+20
...@@ -205,6 +205,26 @@ pub fn unwindFrame(module: *const ElfModule, gpa: Allocator, di: *DebugInfo, con...@@ -205,6 +205,26 @@ pub fn unwindFrame(module: *const ElfModule, gpa: Allocator, di: *DebugInfo, con
205 }205 }
206 return error.MissingDebugInfo;206 return error.MissingDebugInfo;
207}207}
208pub const supports_unwinding: bool = s: {
209 const archs: []const std.Target.Cpu.Arch = switch (builtin.target.os.tag) {
210 .linux => &.{ .x86, .x86_64, .arm, .armeb, .thumb, .thumbeb, .aarch64, .aarch64_be },
211 .netbsd => &.{ .x86, .x86_64, .aarch64, .aarch64_be },
212 .freebsd => &.{ .x86_64, .aarch64, .aarch64_be },
213 .openbsd => &.{.x86_64},
214 .solaris => &.{ .x86, .x86_64 },
215 .illumos => &.{ .x86, .x86_64 },
216 else => unreachable,
217 };
218 for (archs) |a| {
219 if (builtin.target.cpu.arch == a) break :s true;
220 }
221 break :s false;
222};
223comptime {
224 if (supports_unwinding) {
225 std.debug.assert(Dwarf.abi.supportsUnwinding(&builtin.target));
226 }
227}
208228
209const ElfModule = @This();229const ElfModule = @This();
210230
lib/std/debug/SelfInfo/WindowsModule.zig+1
...@@ -246,6 +246,7 @@ pub const DebugInfo = struct {...@@ -246,6 +246,7 @@ pub const DebugInfo = struct {
246 };246 };
247 }247 }
248};248};
249pub const supports_unwinding: bool = false;
249250
250const WindowsModule = @This();251const WindowsModule = @This();
251252