zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

blob 117d2dfd (63252B) - Raw


      1 // SPDX-License-Identifier: MIT
      2 // Copyright (c) 2015-2021 Zig Contributors
      3 // This file is part of [zig](https://ziglang.org/), which is MIT licensed.
      4 // The MIT license requires this copyright notice to be included in all copies
      5 // and substantial portions of the software.
      6 const std = @import("std.zig");
      7 const builtin = std.builtin;
      8 const math = std.math;
      9 const mem = std.mem;
     10 const io = std.io;
     11 const os = std.os;
     12 const fs = std.fs;
     13 const process = std.process;
     14 const elf = std.elf;
     15 const DW = std.dwarf;
     16 const macho = std.macho;
     17 const coff = std.coff;
     18 const pdb = std.pdb;
     19 const ArrayList = std.ArrayList;
     20 const root = @import("root");
     21 const maxInt = std.math.maxInt;
     22 const File = std.fs.File;
     23 const windows = std.os.windows;
     24 const native_arch = std.Target.current.cpu.arch;
     25 const native_os = std.Target.current.os.tag;
     26 const native_endian = native_arch.endian();
     27 
     28 pub const runtime_safety = switch (builtin.mode) {
     29     .Debug, .ReleaseSafe => true,
     30     .ReleaseFast, .ReleaseSmall => false,
     31 };
     32 
     33 pub const LineInfo = struct {
     34     line: u64,
     35     column: u64,
     36     file_name: []const u8,
     37     allocator: ?*mem.Allocator,
     38 
     39     fn deinit(self: LineInfo) void {
     40         const allocator = self.allocator orelse return;
     41         allocator.free(self.file_name);
     42     }
     43 };
     44 
     45 pub const SymbolInfo = struct {
     46     symbol_name: []const u8 = "???",
     47     compile_unit_name: []const u8 = "???",
     48     line_info: ?LineInfo = null,
     49 
     50     fn deinit(self: @This()) void {
     51         if (self.line_info) |li| {
     52             li.deinit();
     53         }
     54     }
     55 };
     56 const PdbOrDwarf = union(enum) {
     57     pdb: pdb.Pdb,
     58     dwarf: DW.DwarfInfo,
     59 };
     60 
     61 var stderr_mutex = std.Thread.Mutex{};
     62 
     63 /// Deprecated. Use `std.log` functions for logging or `std.debug.print` for
     64 /// "printf debugging".
     65 pub const warn = print;
     66 
     67 /// Print to stderr, unbuffered, and silently returning on failure. Intended
     68 /// for use in "printf debugging." Use `std.log` functions for proper logging.
     69 pub fn print(comptime fmt: []const u8, args: anytype) void {
     70     const held = stderr_mutex.acquire();
     71     defer held.release();
     72     const stderr = io.getStdErr().writer();
     73     nosuspend stderr.print(fmt, args) catch return;
     74 }
     75 
     76 pub fn getStderrMutex() *std.Thread.Mutex {
     77     return &stderr_mutex;
     78 }
     79 
     80 /// TODO multithreaded awareness
     81 var self_debug_info: ?DebugInfo = null;
     82 
     83 pub fn getSelfDebugInfo() !*DebugInfo {
     84     if (self_debug_info) |*info| {
     85         return info;
     86     } else {
     87         self_debug_info = try openSelfDebugInfo(getDebugInfoAllocator());
     88         return &self_debug_info.?;
     89     }
     90 }
     91 
     92 pub fn detectTTYConfig() TTY.Config {
     93     var bytes: [128]u8 = undefined;
     94     const allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator;
     95     if (process.getEnvVarOwned(allocator, "ZIG_DEBUG_COLOR")) |_| {
     96         return .escape_codes;
     97     } else |_| {
     98         const stderr_file = io.getStdErr();
     99         if (stderr_file.supportsAnsiEscapeCodes()) {
    100             return .escape_codes;
    101         } else if (native_os == .windows and stderr_file.isTty()) {
    102             return .windows_api;
    103         } else {
    104             return .no_color;
    105         }
    106     }
    107 }
    108 
    109 /// Tries to print the current stack trace to stderr, unbuffered, and ignores any error returned.
    110 /// TODO multithreaded awareness
    111 pub fn dumpCurrentStackTrace(start_addr: ?usize) void {
    112     nosuspend {
    113         const stderr = io.getStdErr().writer();
    114         if (builtin.strip_debug_info) {
    115             stderr.print("Unable to dump stack trace: debug info stripped\n", .{}) catch return;
    116             return;
    117         }
    118         const debug_info = getSelfDebugInfo() catch |err| {
    119             stderr.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}) catch return;
    120             return;
    121         };
    122         writeCurrentStackTrace(stderr, debug_info, detectTTYConfig(), start_addr) catch |err| {
    123             stderr.print("Unable to dump stack trace: {s}\n", .{@errorName(err)}) catch return;
    124             return;
    125         };
    126     }
    127 }
    128 
    129 /// Tries to print the stack trace starting from the supplied base pointer to stderr,
    130 /// unbuffered, and ignores any error returned.
    131 /// TODO multithreaded awareness
    132 pub fn dumpStackTraceFromBase(bp: usize, ip: usize) void {
    133     nosuspend {
    134         const stderr = io.getStdErr().writer();
    135         if (builtin.strip_debug_info) {
    136             stderr.print("Unable to dump stack trace: debug info stripped\n", .{}) catch return;
    137             return;
    138         }
    139         const debug_info = getSelfDebugInfo() catch |err| {
    140             stderr.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}) catch return;
    141             return;
    142         };
    143         const tty_config = detectTTYConfig();
    144         printSourceAtAddress(debug_info, stderr, ip, tty_config) catch return;
    145         var it = StackIterator.init(null, bp);
    146         while (it.next()) |return_address| {
    147             printSourceAtAddress(debug_info, stderr, return_address - 1, tty_config) catch return;
    148         }
    149     }
    150 }
    151 
    152 /// Returns a slice with the same pointer as addresses, with a potentially smaller len.
    153 /// On Windows, when first_address is not null, we ask for at least 32 stack frames,
    154 /// and then try to find the first address. If addresses.len is more than 32, we
    155 /// capture that many stack frames exactly, and then look for the first address,
    156 /// chopping off the irrelevant frames and shifting so that the returned addresses pointer
    157 /// equals the passed in addresses pointer.
    158 pub fn captureStackTrace(first_address: ?usize, stack_trace: *builtin.StackTrace) void {
    159     if (native_os == .windows) {
    160         const addrs = stack_trace.instruction_addresses;
    161         const u32_addrs_len = @intCast(u32, addrs.len);
    162         const first_addr = first_address orelse {
    163             stack_trace.index = windows.ntdll.RtlCaptureStackBackTrace(
    164                 0,
    165                 u32_addrs_len,
    166                 @ptrCast(**c_void, addrs.ptr),
    167                 null,
    168             );
    169             return;
    170         };
    171         var addr_buf_stack: [32]usize = undefined;
    172         const addr_buf = if (addr_buf_stack.len > addrs.len) addr_buf_stack[0..] else addrs;
    173         const n = windows.ntdll.RtlCaptureStackBackTrace(0, u32_addrs_len, @ptrCast(**c_void, addr_buf.ptr), null);
    174         const first_index = for (addr_buf[0..n]) |addr, i| {
    175             if (addr == first_addr) {
    176                 break i;
    177             }
    178         } else {
    179             stack_trace.index = 0;
    180             return;
    181         };
    182         const slice = addr_buf[first_index..n];
    183         // We use a for loop here because slice and addrs may alias.
    184         for (slice) |addr, i| {
    185             addrs[i] = addr;
    186         }
    187         stack_trace.index = slice.len;
    188     } else {
    189         var it = StackIterator.init(first_address, null);
    190         for (stack_trace.instruction_addresses) |*addr, i| {
    191             addr.* = it.next() orelse {
    192                 stack_trace.index = i;
    193                 return;
    194             };
    195         }
    196         stack_trace.index = stack_trace.instruction_addresses.len;
    197     }
    198 }
    199 
    200 /// Tries to print a stack trace to stderr, unbuffered, and ignores any error returned.
    201 /// TODO multithreaded awareness
    202 pub fn dumpStackTrace(stack_trace: builtin.StackTrace) void {
    203     nosuspend {
    204         const stderr = io.getStdErr().writer();
    205         if (builtin.strip_debug_info) {
    206             stderr.print("Unable to dump stack trace: debug info stripped\n", .{}) catch return;
    207             return;
    208         }
    209         const debug_info = getSelfDebugInfo() catch |err| {
    210             stderr.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}) catch return;
    211             return;
    212         };
    213         writeStackTrace(stack_trace, stderr, getDebugInfoAllocator(), debug_info, detectTTYConfig()) catch |err| {
    214             stderr.print("Unable to dump stack trace: {s}\n", .{@errorName(err)}) catch return;
    215             return;
    216         };
    217     }
    218 }
    219 
    220 /// This function invokes undefined behavior when `ok` is `false`.
    221 /// In Debug and ReleaseSafe modes, calls to this function are always
    222 /// generated, and the `unreachable` statement triggers a panic.
    223 /// In ReleaseFast and ReleaseSmall modes, calls to this function are
    224 /// optimized away, and in fact the optimizer is able to use the assertion
    225 /// in its heuristics.
    226 /// Inside a test block, it is best to use the `std.testing` module rather
    227 /// than this function, because this function may not detect a test failure
    228 /// in ReleaseFast and ReleaseSmall mode. Outside of a test block, this assert
    229 /// function is the correct function to use.
    230 pub fn assert(ok: bool) void {
    231     if (!ok) unreachable; // assertion failure
    232 }
    233 
    234 pub fn panic(comptime format: []const u8, args: anytype) noreturn {
    235     @setCold(true);
    236     // TODO: remove conditional once wasi / LLVM defines __builtin_return_address
    237     const first_trace_addr = if (native_os == .wasi) null else @returnAddress();
    238     panicExtra(null, first_trace_addr, format, args);
    239 }
    240 
    241 /// Non-zero whenever the program triggered a panic.
    242 /// The counter is incremented/decremented atomically.
    243 var panicking: u8 = 0;
    244 
    245 // Locked to avoid interleaving panic messages from multiple threads.
    246 var panic_mutex = std.Thread.Mutex{};
    247 
    248 /// Counts how many times the panic handler is invoked by this thread.
    249 /// This is used to catch and handle panics triggered by the panic handler.
    250 threadlocal var panic_stage: usize = 0;
    251 
    252 pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, comptime format: []const u8, args: anytype) noreturn {
    253     @setCold(true);
    254 
    255     if (enable_segfault_handler) {
    256         // If a segfault happens while panicking, we want it to actually segfault, not trigger
    257         // the handler.
    258         resetSegfaultHandler();
    259     }
    260 
    261     nosuspend switch (panic_stage) {
    262         0 => {
    263             panic_stage = 1;
    264 
    265             _ = @atomicRmw(u8, &panicking, .Add, 1, .SeqCst);
    266 
    267             // Make sure to release the mutex when done
    268             {
    269                 const held = panic_mutex.acquire();
    270                 defer held.release();
    271 
    272                 const stderr = io.getStdErr().writer();
    273                 if (builtin.single_threaded) {
    274                     stderr.print("panic: ", .{}) catch os.abort();
    275                 } else {
    276                     const current_thread_id = std.Thread.getCurrentThreadId();
    277                     stderr.print("thread {d} panic: ", .{current_thread_id}) catch os.abort();
    278                 }
    279                 stderr.print(format ++ "\n", args) catch os.abort();
    280                 if (trace) |t| {
    281                     dumpStackTrace(t.*);
    282                 }
    283                 dumpCurrentStackTrace(first_trace_addr);
    284             }
    285 
    286             if (@atomicRmw(u8, &panicking, .Sub, 1, .SeqCst) != 1) {
    287                 // Another thread is panicking, wait for the last one to finish
    288                 // and call abort()
    289 
    290                 // Sleep forever without hammering the CPU
    291                 var event: std.Thread.StaticResetEvent = .{};
    292                 event.wait();
    293                 unreachable;
    294             }
    295         },
    296         1 => {
    297             panic_stage = 2;
    298 
    299             // A panic happened while trying to print a previous panic message,
    300             // we're still holding the mutex but that's fine as we're going to
    301             // call abort()
    302             const stderr = io.getStdErr().writer();
    303             stderr.print("Panicked during a panic. Aborting.\n", .{}) catch os.abort();
    304         },
    305         else => {
    306             // Panicked while printing "Panicked during a panic."
    307         },
    308     };
    309 
    310     os.abort();
    311 }
    312 
    313 const RED = "\x1b[31;1m";
    314 const GREEN = "\x1b[32;1m";
    315 const CYAN = "\x1b[36;1m";
    316 const WHITE = "\x1b[37;1m";
    317 const BOLD = "\x1b[1m";
    318 const DIM = "\x1b[2m";
    319 const RESET = "\x1b[0m";
    320 
    321 pub fn writeStackTrace(
    322     stack_trace: builtin.StackTrace,
    323     out_stream: anytype,
    324     allocator: *mem.Allocator,
    325     debug_info: *DebugInfo,
    326     tty_config: TTY.Config,
    327 ) !void {
    328     if (builtin.strip_debug_info) return error.MissingDebugInfo;
    329     var frame_index: usize = 0;
    330     var frames_left: usize = std.math.min(stack_trace.index, stack_trace.instruction_addresses.len);
    331 
    332     while (frames_left != 0) : ({
    333         frames_left -= 1;
    334         frame_index = (frame_index + 1) % stack_trace.instruction_addresses.len;
    335     }) {
    336         const return_address = stack_trace.instruction_addresses[frame_index];
    337         try printSourceAtAddress(debug_info, out_stream, return_address - 1, tty_config);
    338     }
    339 }
    340 
    341 pub const StackIterator = struct {
    342     // Skip every frame before this address is found.
    343     first_address: ?usize,
    344     // Last known value of the frame pointer register.
    345     fp: usize,
    346 
    347     pub fn init(first_address: ?usize, fp: ?usize) StackIterator {
    348         if (native_arch == .sparcv9) {
    349             // Flush all the register windows on stack.
    350             asm volatile (
    351                 \\ flushw
    352                 ::: "memory");
    353         }
    354 
    355         return StackIterator{
    356             .first_address = first_address,
    357             .fp = fp orelse @frameAddress(),
    358         };
    359     }
    360 
    361     // Offset of the saved BP wrt the frame pointer.
    362     const fp_offset = if (native_arch.isRISCV())
    363         // On RISC-V the frame pointer points to the top of the saved register
    364         // area, on pretty much every other architecture it points to the stack
    365         // slot where the previous frame pointer is saved.
    366         2 * @sizeOf(usize)
    367     else if (native_arch.isSPARC())
    368         // On SPARC the previous frame pointer is stored at 14 slots past %fp+BIAS.
    369         14 * @sizeOf(usize)
    370     else
    371         0;
    372 
    373     const fp_bias = if (native_arch.isSPARC())
    374         // On SPARC frame pointers are biased by a constant.
    375         2047
    376     else
    377         0;
    378 
    379     // Positive offset of the saved PC wrt the frame pointer.
    380     const pc_offset = if (native_arch == .powerpc64le)
    381         2 * @sizeOf(usize)
    382     else
    383         @sizeOf(usize);
    384 
    385     pub fn next(self: *StackIterator) ?usize {
    386         var address = self.next_internal() orelse return null;
    387 
    388         if (self.first_address) |first_address| {
    389             while (address != first_address) {
    390                 address = self.next_internal() orelse return null;
    391             }
    392             self.first_address = null;
    393         }
    394 
    395         return address;
    396     }
    397 
    398     fn next_internal(self: *StackIterator) ?usize {
    399         const fp = if (comptime native_arch.isSPARC())
    400             // On SPARC the offset is positive. (!)
    401             math.add(usize, self.fp, fp_offset) catch return null
    402         else
    403             math.sub(usize, self.fp, fp_offset) catch return null;
    404 
    405         // Sanity check.
    406         if (fp == 0 or !mem.isAligned(fp, @alignOf(usize)))
    407             return null;
    408 
    409         const new_fp = math.add(usize, @intToPtr(*const usize, fp).*, fp_bias) catch return null;
    410 
    411         // Sanity check: the stack grows down thus all the parent frames must be
    412         // be at addresses that are greater (or equal) than the previous one.
    413         // A zero frame pointer often signals this is the last frame, that case
    414         // is gracefully handled by the next call to next_internal.
    415         if (new_fp != 0 and new_fp < self.fp)
    416             return null;
    417 
    418         const new_pc = @intToPtr(
    419             *const usize,
    420             math.add(usize, fp, pc_offset) catch return null,
    421         ).*;
    422 
    423         self.fp = new_fp;
    424 
    425         return new_pc;
    426     }
    427 };
    428 
    429 pub fn writeCurrentStackTrace(
    430     out_stream: anytype,
    431     debug_info: *DebugInfo,
    432     tty_config: TTY.Config,
    433     start_addr: ?usize,
    434 ) !void {
    435     if (native_os == .windows) {
    436         return writeCurrentStackTraceWindows(out_stream, debug_info, tty_config, start_addr);
    437     }
    438     var it = StackIterator.init(start_addr, null);
    439     while (it.next()) |return_address| {
    440         try printSourceAtAddress(debug_info, out_stream, return_address - 1, tty_config);
    441     }
    442 }
    443 
    444 pub fn writeCurrentStackTraceWindows(
    445     out_stream: anytype,
    446     debug_info: *DebugInfo,
    447     tty_config: TTY.Config,
    448     start_addr: ?usize,
    449 ) !void {
    450     var addr_buf: [1024]usize = undefined;
    451     const n = windows.ntdll.RtlCaptureStackBackTrace(0, addr_buf.len, @ptrCast(**c_void, &addr_buf), null);
    452     const addrs = addr_buf[0..n];
    453     var start_i: usize = if (start_addr) |saddr| blk: {
    454         for (addrs) |addr, i| {
    455             if (addr == saddr) break :blk i;
    456         }
    457         return;
    458     } else 0;
    459     for (addrs[start_i..]) |addr| {
    460         try printSourceAtAddress(debug_info, out_stream, addr - 1, tty_config);
    461     }
    462 }
    463 
    464 pub const TTY = struct {
    465     pub const Color = enum {
    466         Red,
    467         Green,
    468         Cyan,
    469         White,
    470         Dim,
    471         Bold,
    472         Reset,
    473     };
    474 
    475     pub const Config = enum {
    476         no_color,
    477         escape_codes,
    478         // TODO give this a payload of file handle
    479         windows_api,
    480 
    481         pub fn setColor(conf: Config, out_stream: anytype, color: Color) void {
    482             nosuspend switch (conf) {
    483                 .no_color => return,
    484                 .escape_codes => switch (color) {
    485                     .Red => out_stream.writeAll(RED) catch return,
    486                     .Green => out_stream.writeAll(GREEN) catch return,
    487                     .Cyan => out_stream.writeAll(CYAN) catch return,
    488                     .White => out_stream.writeAll(WHITE) catch return,
    489                     .Dim => out_stream.writeAll(DIM) catch return,
    490                     .Bold => out_stream.writeAll(BOLD) catch return,
    491                     .Reset => out_stream.writeAll(RESET) catch return,
    492                 },
    493                 .windows_api => if (native_os == .windows) {
    494                     const stderr_file = io.getStdErr();
    495                     const S = struct {
    496                         var attrs: windows.WORD = undefined;
    497                         var init_attrs = false;
    498                     };
    499                     if (!S.init_attrs) {
    500                         S.init_attrs = true;
    501                         var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
    502                         // TODO handle error
    503                         _ = windows.kernel32.GetConsoleScreenBufferInfo(stderr_file.handle, &info);
    504                         S.attrs = info.wAttributes;
    505                     }
    506 
    507                     // TODO handle errors
    508                     switch (color) {
    509                         .Red => {
    510                             _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_RED | windows.FOREGROUND_INTENSITY) catch {};
    511                         },
    512                         .Green => {
    513                             _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_GREEN | windows.FOREGROUND_INTENSITY) catch {};
    514                         },
    515                         .Cyan => {
    516                             _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY) catch {};
    517                         },
    518                         .White, .Bold => {
    519                             _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_RED | windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY) catch {};
    520                         },
    521                         .Dim => {
    522                             _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_INTENSITY) catch {};
    523                         },
    524                         .Reset => {
    525                             _ = windows.SetConsoleTextAttribute(stderr_file.handle, S.attrs) catch {};
    526                         },
    527                     }
    528                 } else {
    529                     unreachable;
    530                 },
    531             };
    532         }
    533     };
    534 };
    535 
    536 fn machoSearchSymbols(symbols: []const MachoSymbol, address: usize) ?*const MachoSymbol {
    537     var min: usize = 0;
    538     var max: usize = symbols.len - 1; // Exclude sentinel.
    539     while (min < max) {
    540         const mid = min + (max - min) / 2;
    541         const curr = &symbols[mid];
    542         const next = &symbols[mid + 1];
    543         if (address >= next.address()) {
    544             min = mid + 1;
    545         } else if (address < curr.address()) {
    546             max = mid;
    547         } else {
    548             return curr;
    549         }
    550     }
    551     return null;
    552 }
    553 
    554 /// TODO resources https://github.com/ziglang/zig/issues/4353
    555 pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: anytype, address: usize, tty_config: TTY.Config) !void {
    556     const module = debug_info.getModuleForAddress(address) catch |err| switch (err) {
    557         error.MissingDebugInfo, error.InvalidDebugInfo => {
    558             return printLineInfo(
    559                 out_stream,
    560                 null,
    561                 address,
    562                 "???",
    563                 "???",
    564                 tty_config,
    565                 printLineFromFileAnyOs,
    566             );
    567         },
    568         else => return err,
    569     };
    570 
    571     const symbol_info = try module.getSymbolAtAddress(address);
    572     defer symbol_info.deinit();
    573 
    574     return printLineInfo(
    575         out_stream,
    576         symbol_info.line_info,
    577         address,
    578         symbol_info.symbol_name,
    579         symbol_info.compile_unit_name,
    580         tty_config,
    581         printLineFromFileAnyOs,
    582     );
    583 }
    584 
    585 fn printLineInfo(
    586     out_stream: anytype,
    587     line_info: ?LineInfo,
    588     address: usize,
    589     symbol_name: []const u8,
    590     compile_unit_name: []const u8,
    591     tty_config: TTY.Config,
    592     comptime printLineFromFile: anytype,
    593 ) !void {
    594     nosuspend {
    595         tty_config.setColor(out_stream, .Bold);
    596 
    597         if (line_info) |*li| {
    598             try out_stream.print("{s}:{d}:{d}", .{ li.file_name, li.line, li.column });
    599         } else {
    600             try out_stream.writeAll("???:?:?");
    601         }
    602 
    603         tty_config.setColor(out_stream, .Reset);
    604         try out_stream.writeAll(": ");
    605         tty_config.setColor(out_stream, .Dim);
    606         try out_stream.print("0x{x} in {s} ({s})", .{ address, symbol_name, compile_unit_name });
    607         tty_config.setColor(out_stream, .Reset);
    608         try out_stream.writeAll("\n");
    609 
    610         // Show the matching source code line if possible
    611         if (line_info) |li| {
    612             if (printLineFromFile(out_stream, li)) {
    613                 if (li.column > 0) {
    614                     // The caret already takes one char
    615                     const space_needed = @intCast(usize, li.column - 1);
    616 
    617                     try out_stream.writeByteNTimes(' ', space_needed);
    618                     tty_config.setColor(out_stream, .Green);
    619                     try out_stream.writeAll("^");
    620                     tty_config.setColor(out_stream, .Reset);
    621                 }
    622                 try out_stream.writeAll("\n");
    623             } else |err| switch (err) {
    624                 error.EndOfFile, error.FileNotFound => {},
    625                 error.BadPathName => {},
    626                 error.AccessDenied => {},
    627                 else => return err,
    628             }
    629         }
    630     }
    631 }
    632 
    633 // TODO use this
    634 pub const OpenSelfDebugInfoError = error{
    635     MissingDebugInfo,
    636     OutOfMemory,
    637     UnsupportedOperatingSystem,
    638 };
    639 
    640 /// TODO resources https://github.com/ziglang/zig/issues/4353
    641 pub fn openSelfDebugInfo(allocator: *mem.Allocator) anyerror!DebugInfo {
    642     nosuspend {
    643         if (builtin.strip_debug_info)
    644             return error.MissingDebugInfo;
    645         if (@hasDecl(root, "os") and @hasDecl(root.os, "debug") and @hasDecl(root.os.debug, "openSelfDebugInfo")) {
    646             return root.os.debug.openSelfDebugInfo(allocator);
    647         }
    648         switch (native_os) {
    649             .linux,
    650             .freebsd,
    651             .netbsd,
    652             .dragonfly,
    653             .openbsd,
    654             .macos,
    655             .windows,
    656             => return DebugInfo.init(allocator),
    657             else => return error.UnsupportedDebugInfo,
    658         }
    659     }
    660 }
    661 
    662 /// This takes ownership of coff_file: users of this function should not close
    663 /// it themselves, even on error.
    664 /// TODO resources https://github.com/ziglang/zig/issues/4353
    665 /// TODO it's weird to take ownership even on error, rework this code.
    666 fn readCoffDebugInfo(allocator: *mem.Allocator, coff_file: File) !ModuleDebugInfo {
    667     nosuspend {
    668         errdefer coff_file.close();
    669 
    670         const coff_obj = try allocator.create(coff.Coff);
    671         coff_obj.* = coff.Coff.init(allocator, coff_file);
    672 
    673         var di = ModuleDebugInfo{
    674             .base_address = undefined,
    675             .coff = coff_obj,
    676             .debug_data = undefined,
    677         };
    678 
    679         try di.coff.loadHeader();
    680         try di.coff.loadSections();
    681         if (di.coff.getSection(".debug_info")) |sec| {
    682             // This coff file has embedded DWARF debug info
    683             // TODO: free the section data slices
    684             const debug_info_data = di.coff.getSectionData(".debug_info", allocator) catch null;
    685             const debug_abbrev_data = di.coff.getSectionData(".debug_abbrev", allocator) catch null;
    686             const debug_str_data = di.coff.getSectionData(".debug_str", allocator) catch null;
    687             const debug_line_data = di.coff.getSectionData(".debug_line", allocator) catch null;
    688             const debug_ranges_data = di.coff.getSectionData(".debug_ranges", allocator) catch null;
    689 
    690             var dwarf = DW.DwarfInfo{
    691                 .endian = native_endian,
    692                 .debug_info = debug_info_data orelse return error.MissingDebugInfo,
    693                 .debug_abbrev = debug_abbrev_data orelse return error.MissingDebugInfo,
    694                 .debug_str = debug_str_data orelse return error.MissingDebugInfo,
    695                 .debug_line = debug_line_data orelse return error.MissingDebugInfo,
    696                 .debug_ranges = debug_ranges_data,
    697             };
    698             try DW.openDwarfDebugInfo(&dwarf, allocator);
    699             di.debug_data = PdbOrDwarf{ .dwarf = dwarf };
    700             return di;
    701         }
    702 
    703         var path_buf: [windows.MAX_PATH]u8 = undefined;
    704         const len = try di.coff.getPdbPath(path_buf[0..]);
    705         const raw_path = path_buf[0..len];
    706 
    707         const path = try fs.path.resolve(allocator, &[_][]const u8{raw_path});
    708         defer allocator.free(path);
    709 
    710         di.debug_data = PdbOrDwarf{ .pdb = undefined };
    711         di.debug_data.pdb = try pdb.Pdb.init(allocator, path);
    712         try di.debug_data.pdb.parseInfoStream();
    713         try di.debug_data.pdb.parseDbiStream();
    714 
    715         if (!mem.eql(u8, &di.coff.guid, &di.debug_data.pdb.guid) or di.coff.age != di.debug_data.pdb.age)
    716             return error.InvalidDebugInfo;
    717 
    718         return di;
    719     }
    720 }
    721 
    722 fn chopSlice(ptr: []const u8, offset: u64, size: u64) ![]const u8 {
    723     const start = try math.cast(usize, offset);
    724     const end = start + try math.cast(usize, size);
    725     return ptr[start..end];
    726 }
    727 
    728 /// This takes ownership of elf_file: users of this function should not close
    729 /// it themselves, even on error.
    730 /// TODO resources https://github.com/ziglang/zig/issues/4353
    731 /// TODO it's weird to take ownership even on error, rework this code.
    732 pub fn readElfDebugInfo(allocator: *mem.Allocator, elf_file: File) !ModuleDebugInfo {
    733     nosuspend {
    734         const mapped_mem = try mapWholeFile(elf_file);
    735         const hdr = @ptrCast(*const elf.Ehdr, &mapped_mem[0]);
    736         if (!mem.eql(u8, hdr.e_ident[0..4], "\x7fELF")) return error.InvalidElfMagic;
    737         if (hdr.e_ident[elf.EI_VERSION] != 1) return error.InvalidElfVersion;
    738 
    739         const endian: builtin.Endian = switch (hdr.e_ident[elf.EI_DATA]) {
    740             elf.ELFDATA2LSB => .Little,
    741             elf.ELFDATA2MSB => .Big,
    742             else => return error.InvalidElfEndian,
    743         };
    744         assert(endian == native_endian); // this is our own debug info
    745 
    746         const shoff = hdr.e_shoff;
    747         const str_section_off = shoff + @as(u64, hdr.e_shentsize) * @as(u64, hdr.e_shstrndx);
    748         const str_shdr = @ptrCast(
    749             *const elf.Shdr,
    750             @alignCast(@alignOf(elf.Shdr), &mapped_mem[try math.cast(usize, str_section_off)]),
    751         );
    752         const header_strings = mapped_mem[str_shdr.sh_offset .. str_shdr.sh_offset + str_shdr.sh_size];
    753         const shdrs = @ptrCast(
    754             [*]const elf.Shdr,
    755             @alignCast(@alignOf(elf.Shdr), &mapped_mem[shoff]),
    756         )[0..hdr.e_shnum];
    757 
    758         var opt_debug_info: ?[]const u8 = null;
    759         var opt_debug_abbrev: ?[]const u8 = null;
    760         var opt_debug_str: ?[]const u8 = null;
    761         var opt_debug_line: ?[]const u8 = null;
    762         var opt_debug_ranges: ?[]const u8 = null;
    763 
    764         for (shdrs) |*shdr| {
    765             if (shdr.sh_type == elf.SHT_NULL) continue;
    766 
    767             const name = std.mem.span(std.meta.assumeSentinel(header_strings[shdr.sh_name..].ptr, 0));
    768             if (mem.eql(u8, name, ".debug_info")) {
    769                 opt_debug_info = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
    770             } else if (mem.eql(u8, name, ".debug_abbrev")) {
    771                 opt_debug_abbrev = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
    772             } else if (mem.eql(u8, name, ".debug_str")) {
    773                 opt_debug_str = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
    774             } else if (mem.eql(u8, name, ".debug_line")) {
    775                 opt_debug_line = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
    776             } else if (mem.eql(u8, name, ".debug_ranges")) {
    777                 opt_debug_ranges = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
    778             }
    779         }
    780 
    781         var di = DW.DwarfInfo{
    782             .endian = endian,
    783             .debug_info = opt_debug_info orelse return error.MissingDebugInfo,
    784             .debug_abbrev = opt_debug_abbrev orelse return error.MissingDebugInfo,
    785             .debug_str = opt_debug_str orelse return error.MissingDebugInfo,
    786             .debug_line = opt_debug_line orelse return error.MissingDebugInfo,
    787             .debug_ranges = opt_debug_ranges,
    788         };
    789 
    790         try DW.openDwarfDebugInfo(&di, allocator);
    791 
    792         return ModuleDebugInfo{
    793             .base_address = undefined,
    794             .dwarf = di,
    795             .mapped_memory = mapped_mem,
    796         };
    797     }
    798 }
    799 
    800 /// TODO resources https://github.com/ziglang/zig/issues/4353
    801 /// This takes ownership of macho_file: users of this function should not close
    802 /// it themselves, even on error.
    803 /// TODO it's weird to take ownership even on error, rework this code.
    804 fn readMachODebugInfo(allocator: *mem.Allocator, macho_file: File) !ModuleDebugInfo {
    805     const mapped_mem = try mapWholeFile(macho_file);
    806 
    807     const hdr = @ptrCast(
    808         *const macho.mach_header_64,
    809         @alignCast(@alignOf(macho.mach_header_64), mapped_mem.ptr),
    810     );
    811     if (hdr.magic != macho.MH_MAGIC_64)
    812         return error.InvalidDebugInfo;
    813 
    814     const hdr_base = @ptrCast([*]const u8, hdr);
    815     var ptr = hdr_base + @sizeOf(macho.mach_header_64);
    816     var ncmd: u32 = hdr.ncmds;
    817     const symtab = while (ncmd != 0) : (ncmd -= 1) {
    818         const lc = @ptrCast(*const std.macho.load_command, ptr);
    819         switch (lc.cmd) {
    820             std.macho.LC_SYMTAB => break @ptrCast(*const std.macho.symtab_command, ptr),
    821             else => {},
    822         }
    823         ptr = @alignCast(@alignOf(std.macho.load_command), ptr + lc.cmdsize);
    824     } else {
    825         return error.MissingDebugInfo;
    826     };
    827     const syms = @ptrCast([*]const macho.nlist_64, @alignCast(@alignOf(macho.nlist_64), hdr_base + symtab.symoff))[0..symtab.nsyms];
    828     const strings = @ptrCast([*]const u8, hdr_base + symtab.stroff)[0 .. symtab.strsize - 1 :0];
    829 
    830     const symbols_buf = try allocator.alloc(MachoSymbol, syms.len);
    831 
    832     var ofile: ?*const macho.nlist_64 = null;
    833     var reloc: u64 = 0;
    834     var symbol_index: usize = 0;
    835     var last_len: u64 = 0;
    836     for (syms) |*sym| {
    837         if (sym.n_type & std.macho.N_STAB != 0) {
    838             switch (sym.n_type) {
    839                 std.macho.N_OSO => {
    840                     ofile = sym;
    841                     reloc = 0;
    842                 },
    843                 std.macho.N_FUN => {
    844                     if (sym.n_sect == 0) {
    845                         last_len = sym.n_value;
    846                     } else {
    847                         symbols_buf[symbol_index] = MachoSymbol{
    848                             .nlist = sym,
    849                             .ofile = ofile,
    850                             .reloc = reloc,
    851                         };
    852                         symbol_index += 1;
    853                     }
    854                 },
    855                 std.macho.N_BNSYM => {
    856                     if (reloc == 0) {
    857                         reloc = sym.n_value;
    858                     }
    859                 },
    860                 else => continue,
    861             }
    862         }
    863     }
    864     const sentinel = try allocator.create(macho.nlist_64);
    865     sentinel.* = macho.nlist_64{
    866         .n_strx = 0,
    867         .n_type = 36,
    868         .n_sect = 0,
    869         .n_desc = 0,
    870         .n_value = symbols_buf[symbol_index - 1].nlist.n_value + last_len,
    871     };
    872 
    873     const symbols = allocator.shrink(symbols_buf, symbol_index);
    874 
    875     // Even though lld emits symbols in ascending order, this debug code
    876     // should work for programs linked in any valid way.
    877     // This sort is so that we can binary search later.
    878     std.sort.sort(MachoSymbol, symbols, {}, MachoSymbol.addressLessThan);
    879 
    880     return ModuleDebugInfo{
    881         .base_address = undefined,
    882         .mapped_memory = mapped_mem,
    883         .ofiles = ModuleDebugInfo.OFileTable.init(allocator),
    884         .symbols = symbols,
    885         .strings = strings,
    886     };
    887 }
    888 
    889 fn printLineFromFileAnyOs(out_stream: anytype, line_info: LineInfo) !void {
    890     // Need this to always block even in async I/O mode, because this could potentially
    891     // be called from e.g. the event loop code crashing.
    892     var f = try fs.cwd().openFile(line_info.file_name, .{ .intended_io_mode = .blocking });
    893     defer f.close();
    894     // TODO fstat and make sure that the file has the correct size
    895 
    896     var buf: [mem.page_size]u8 = undefined;
    897     var line: usize = 1;
    898     var column: usize = 1;
    899     while (true) {
    900         const amt_read = try f.read(buf[0..]);
    901         const slice = buf[0..amt_read];
    902 
    903         for (slice) |byte| {
    904             if (line == line_info.line) {
    905                 try out_stream.writeByte(byte);
    906                 if (byte == '\n') {
    907                     return;
    908                 }
    909             }
    910             if (byte == '\n') {
    911                 line += 1;
    912                 column = 1;
    913             } else {
    914                 column += 1;
    915             }
    916         }
    917 
    918         if (amt_read < buf.len) return error.EndOfFile;
    919     }
    920 }
    921 
    922 const MachoSymbol = struct {
    923     nlist: *const macho.nlist_64,
    924     ofile: ?*const macho.nlist_64,
    925     reloc: u64,
    926 
    927     /// Returns the address from the macho file
    928     fn address(self: MachoSymbol) u64 {
    929         return self.nlist.n_value;
    930     }
    931 
    932     fn addressLessThan(context: void, lhs: MachoSymbol, rhs: MachoSymbol) bool {
    933         return lhs.address() < rhs.address();
    934     }
    935 };
    936 
    937 /// `file` is expected to have been opened with .intended_io_mode == .blocking.
    938 /// Takes ownership of file, even on error.
    939 /// TODO it's weird to take ownership even on error, rework this code.
    940 fn mapWholeFile(file: File) ![]align(mem.page_size) const u8 {
    941     nosuspend {
    942         defer file.close();
    943 
    944         const file_len = try math.cast(usize, try file.getEndPos());
    945         const mapped_mem = try os.mmap(
    946             null,
    947             file_len,
    948             os.PROT_READ,
    949             os.MAP_SHARED,
    950             file.handle,
    951             0,
    952         );
    953         errdefer os.munmap(mapped_mem);
    954 
    955         return mapped_mem;
    956     }
    957 }
    958 
    959 pub const DebugInfo = struct {
    960     allocator: *mem.Allocator,
    961     address_map: std.AutoHashMap(usize, *ModuleDebugInfo),
    962 
    963     pub fn init(allocator: *mem.Allocator) DebugInfo {
    964         return DebugInfo{
    965             .allocator = allocator,
    966             .address_map = std.AutoHashMap(usize, *ModuleDebugInfo).init(allocator),
    967         };
    968     }
    969 
    970     pub fn deinit(self: *DebugInfo) void {
    971         // TODO: resources https://github.com/ziglang/zig/issues/4353
    972         self.address_map.deinit();
    973     }
    974 
    975     pub fn getModuleForAddress(self: *DebugInfo, address: usize) !*ModuleDebugInfo {
    976         if (comptime std.Target.current.isDarwin()) {
    977             return self.lookupModuleDyld(address);
    978         } else if (native_os == .windows) {
    979             return self.lookupModuleWin32(address);
    980         } else if (native_os == .haiku) {
    981             return self.lookupModuleHaiku(address);
    982         } else {
    983             return self.lookupModuleDl(address);
    984         }
    985     }
    986 
    987     fn lookupModuleDyld(self: *DebugInfo, address: usize) !*ModuleDebugInfo {
    988         const image_count = std.c._dyld_image_count();
    989 
    990         var i: u32 = 0;
    991         while (i < image_count) : (i += 1) {
    992             const base_address = std.c._dyld_get_image_vmaddr_slide(i);
    993 
    994             if (address < base_address) continue;
    995 
    996             const header = std.c._dyld_get_image_header(i) orelse continue;
    997             // The array of load commands is right after the header
    998             var cmd_ptr = @intToPtr([*]u8, @ptrToInt(header) + @sizeOf(macho.mach_header_64));
    999 
   1000             var cmds = header.ncmds;
   1001             while (cmds != 0) : (cmds -= 1) {
   1002                 const lc = @ptrCast(
   1003                     *macho.load_command,
   1004                     @alignCast(@alignOf(macho.load_command), cmd_ptr),
   1005                 );
   1006                 cmd_ptr += lc.cmdsize;
   1007                 if (lc.cmd != macho.LC_SEGMENT_64) continue;
   1008 
   1009                 const segment_cmd = @ptrCast(
   1010                     *const std.macho.segment_command_64,
   1011                     @alignCast(@alignOf(std.macho.segment_command_64), lc),
   1012                 );
   1013 
   1014                 const rebased_address = address - base_address;
   1015                 const seg_start = segment_cmd.vmaddr;
   1016                 const seg_end = seg_start + segment_cmd.vmsize;
   1017 
   1018                 if (rebased_address >= seg_start and rebased_address < seg_end) {
   1019                     if (self.address_map.get(base_address)) |obj_di| {
   1020                         return obj_di;
   1021                     }
   1022 
   1023                     const obj_di = try self.allocator.create(ModuleDebugInfo);
   1024                     errdefer self.allocator.destroy(obj_di);
   1025 
   1026                     const macho_path = mem.spanZ(std.c._dyld_get_image_name(i));
   1027                     const macho_file = fs.cwd().openFile(macho_path, .{ .intended_io_mode = .blocking }) catch |err| switch (err) {
   1028                         error.FileNotFound => return error.MissingDebugInfo,
   1029                         else => return err,
   1030                     };
   1031                     obj_di.* = try readMachODebugInfo(self.allocator, macho_file);
   1032                     obj_di.base_address = base_address;
   1033 
   1034                     try self.address_map.putNoClobber(base_address, obj_di);
   1035 
   1036                     return obj_di;
   1037                 }
   1038             }
   1039         }
   1040 
   1041         return error.MissingDebugInfo;
   1042     }
   1043 
   1044     fn lookupModuleWin32(self: *DebugInfo, address: usize) !*ModuleDebugInfo {
   1045         const process_handle = windows.kernel32.GetCurrentProcess();
   1046 
   1047         // Find how many modules are actually loaded
   1048         var dummy: windows.HMODULE = undefined;
   1049         var bytes_needed: windows.DWORD = undefined;
   1050         if (windows.kernel32.K32EnumProcessModules(
   1051             process_handle,
   1052             @ptrCast([*]windows.HMODULE, &dummy),
   1053             0,
   1054             &bytes_needed,
   1055         ) == 0)
   1056             return error.MissingDebugInfo;
   1057 
   1058         const needed_modules = bytes_needed / @sizeOf(windows.HMODULE);
   1059 
   1060         // Fetch the complete module list
   1061         var modules = try self.allocator.alloc(windows.HMODULE, needed_modules);
   1062         defer self.allocator.free(modules);
   1063         if (windows.kernel32.K32EnumProcessModules(
   1064             process_handle,
   1065             modules.ptr,
   1066             try math.cast(windows.DWORD, modules.len * @sizeOf(windows.HMODULE)),
   1067             &bytes_needed,
   1068         ) == 0)
   1069             return error.MissingDebugInfo;
   1070 
   1071         // There's an unavoidable TOCTOU problem here, the module list may have
   1072         // changed between the two EnumProcessModules call.
   1073         // Pick the smallest amount of elements to avoid processing garbage.
   1074         const needed_modules_after = bytes_needed / @sizeOf(windows.HMODULE);
   1075         const loaded_modules = math.min(needed_modules, needed_modules_after);
   1076 
   1077         for (modules[0..loaded_modules]) |module| {
   1078             var info: windows.MODULEINFO = undefined;
   1079             if (windows.kernel32.K32GetModuleInformation(
   1080                 process_handle,
   1081                 module,
   1082                 &info,
   1083                 @sizeOf(@TypeOf(info)),
   1084             ) == 0)
   1085                 return error.MissingDebugInfo;
   1086 
   1087             const seg_start = @ptrToInt(info.lpBaseOfDll);
   1088             const seg_end = seg_start + info.SizeOfImage;
   1089 
   1090             if (address >= seg_start and address < seg_end) {
   1091                 if (self.address_map.get(seg_start)) |obj_di| {
   1092                     return obj_di;
   1093                 }
   1094 
   1095                 var name_buffer: [windows.PATH_MAX_WIDE + 4:0]u16 = undefined;
   1096                 // openFileAbsoluteW requires the prefix to be present
   1097                 mem.copy(u16, name_buffer[0..4], &[_]u16{ '\\', '?', '?', '\\' });
   1098                 const len = windows.kernel32.K32GetModuleFileNameExW(
   1099                     process_handle,
   1100                     module,
   1101                     @ptrCast(windows.LPWSTR, &name_buffer[4]),
   1102                     windows.PATH_MAX_WIDE,
   1103                 );
   1104                 assert(len > 0);
   1105 
   1106                 const obj_di = try self.allocator.create(ModuleDebugInfo);
   1107                 errdefer self.allocator.destroy(obj_di);
   1108 
   1109                 const coff_file = fs.openFileAbsoluteW(name_buffer[0 .. len + 4 :0], .{}) catch |err| switch (err) {
   1110                     error.FileNotFound => return error.MissingDebugInfo,
   1111                     else => return err,
   1112                 };
   1113                 obj_di.* = try readCoffDebugInfo(self.allocator, coff_file);
   1114                 obj_di.base_address = seg_start;
   1115 
   1116                 try self.address_map.putNoClobber(seg_start, obj_di);
   1117 
   1118                 return obj_di;
   1119             }
   1120         }
   1121 
   1122         return error.MissingDebugInfo;
   1123     }
   1124 
   1125     fn lookupModuleDl(self: *DebugInfo, address: usize) !*ModuleDebugInfo {
   1126         var ctx: struct {
   1127             // Input
   1128             address: usize,
   1129             // Output
   1130             base_address: usize = undefined,
   1131             name: []const u8 = undefined,
   1132         } = .{ .address = address };
   1133         const CtxTy = @TypeOf(ctx);
   1134 
   1135         if (os.dl_iterate_phdr(&ctx, anyerror, struct {
   1136             fn callback(info: *os.dl_phdr_info, size: usize, context: *CtxTy) !void {
   1137                 // The base address is too high
   1138                 if (context.address < info.dlpi_addr)
   1139                     return;
   1140 
   1141                 const phdrs = info.dlpi_phdr[0..info.dlpi_phnum];
   1142                 for (phdrs) |*phdr| {
   1143                     if (phdr.p_type != elf.PT_LOAD) continue;
   1144 
   1145                     const seg_start = info.dlpi_addr + phdr.p_vaddr;
   1146                     const seg_end = seg_start + phdr.p_memsz;
   1147 
   1148                     if (context.address >= seg_start and context.address < seg_end) {
   1149                         // Android libc uses NULL instead of an empty string to mark the
   1150                         // main program
   1151                         context.name = mem.spanZ(info.dlpi_name) orelse "";
   1152                         context.base_address = info.dlpi_addr;
   1153                         // Stop the iteration
   1154                         return error.Found;
   1155                     }
   1156                 }
   1157             }
   1158         }.callback)) {
   1159             return error.MissingDebugInfo;
   1160         } else |err| switch (err) {
   1161             error.Found => {},
   1162             else => return error.MissingDebugInfo,
   1163         }
   1164 
   1165         if (self.address_map.get(ctx.base_address)) |obj_di| {
   1166             return obj_di;
   1167         }
   1168 
   1169         const obj_di = try self.allocator.create(ModuleDebugInfo);
   1170         errdefer self.allocator.destroy(obj_di);
   1171 
   1172         // TODO https://github.com/ziglang/zig/issues/5525
   1173         const copy = if (ctx.name.len > 0)
   1174             fs.cwd().openFile(ctx.name, .{ .intended_io_mode = .blocking })
   1175         else
   1176             fs.openSelfExe(.{ .intended_io_mode = .blocking });
   1177 
   1178         const elf_file = copy catch |err| switch (err) {
   1179             error.FileNotFound => return error.MissingDebugInfo,
   1180             else => return err,
   1181         };
   1182 
   1183         obj_di.* = try readElfDebugInfo(self.allocator, elf_file);
   1184         obj_di.base_address = ctx.base_address;
   1185 
   1186         try self.address_map.putNoClobber(ctx.base_address, obj_di);
   1187 
   1188         return obj_di;
   1189     }
   1190 
   1191     fn lookupModuleHaiku(self: *DebugInfo, address: usize) !*ModuleDebugInfo {
   1192         @panic("TODO implement lookup module for Haiku");
   1193     }
   1194 };
   1195 
   1196 pub const ModuleDebugInfo = switch (native_os) {
   1197     .macos, .ios, .watchos, .tvos => struct {
   1198         base_address: usize,
   1199         mapped_memory: []const u8,
   1200         symbols: []const MachoSymbol,
   1201         strings: [:0]const u8,
   1202         ofiles: OFileTable,
   1203 
   1204         const OFileTable = std.StringHashMap(DW.DwarfInfo);
   1205 
   1206         pub fn allocator(self: @This()) *mem.Allocator {
   1207             return self.ofiles.allocator;
   1208         }
   1209 
   1210         fn loadOFile(self: *@This(), o_file_path: []const u8) !DW.DwarfInfo {
   1211             const o_file = try fs.cwd().openFile(o_file_path, .{ .intended_io_mode = .blocking });
   1212             const mapped_mem = try mapWholeFile(o_file);
   1213 
   1214             const hdr = @ptrCast(
   1215                 *const macho.mach_header_64,
   1216                 @alignCast(@alignOf(macho.mach_header_64), mapped_mem.ptr),
   1217             );
   1218             if (hdr.magic != std.macho.MH_MAGIC_64)
   1219                 return error.InvalidDebugInfo;
   1220 
   1221             const hdr_base = @ptrCast([*]const u8, hdr);
   1222             var ptr = hdr_base + @sizeOf(macho.mach_header_64);
   1223             var ncmd: u32 = hdr.ncmds;
   1224             const segcmd = while (ncmd != 0) : (ncmd -= 1) {
   1225                 const lc = @ptrCast(*const std.macho.load_command, ptr);
   1226                 switch (lc.cmd) {
   1227                     std.macho.LC_SEGMENT_64 => {
   1228                         break @ptrCast(
   1229                             *const std.macho.segment_command_64,
   1230                             @alignCast(@alignOf(std.macho.segment_command_64), ptr),
   1231                         );
   1232                     },
   1233                     else => {},
   1234                 }
   1235                 ptr = @alignCast(@alignOf(std.macho.load_command), ptr + lc.cmdsize);
   1236             } else {
   1237                 return error.MissingDebugInfo;
   1238             };
   1239 
   1240             var opt_debug_line: ?*const macho.section_64 = null;
   1241             var opt_debug_info: ?*const macho.section_64 = null;
   1242             var opt_debug_abbrev: ?*const macho.section_64 = null;
   1243             var opt_debug_str: ?*const macho.section_64 = null;
   1244             var opt_debug_ranges: ?*const macho.section_64 = null;
   1245 
   1246             const sections = @ptrCast(
   1247                 [*]const macho.section_64,
   1248                 @alignCast(@alignOf(macho.section_64), ptr + @sizeOf(std.macho.segment_command_64)),
   1249             )[0..segcmd.nsects];
   1250             for (sections) |*sect| {
   1251                 // The section name may not exceed 16 chars and a trailing null may
   1252                 // not be present
   1253                 const name = if (mem.indexOfScalar(u8, sect.sectname[0..], 0)) |last|
   1254                     sect.sectname[0..last]
   1255                 else
   1256                     sect.sectname[0..];
   1257 
   1258                 if (mem.eql(u8, name, "__debug_line")) {
   1259                     opt_debug_line = sect;
   1260                 } else if (mem.eql(u8, name, "__debug_info")) {
   1261                     opt_debug_info = sect;
   1262                 } else if (mem.eql(u8, name, "__debug_abbrev")) {
   1263                     opt_debug_abbrev = sect;
   1264                 } else if (mem.eql(u8, name, "__debug_str")) {
   1265                     opt_debug_str = sect;
   1266                 } else if (mem.eql(u8, name, "__debug_ranges")) {
   1267                     opt_debug_ranges = sect;
   1268                 }
   1269             }
   1270 
   1271             const debug_line = opt_debug_line orelse
   1272                 return error.MissingDebugInfo;
   1273             const debug_info = opt_debug_info orelse
   1274                 return error.MissingDebugInfo;
   1275             const debug_str = opt_debug_str orelse
   1276                 return error.MissingDebugInfo;
   1277             const debug_abbrev = opt_debug_abbrev orelse
   1278                 return error.MissingDebugInfo;
   1279 
   1280             var di = DW.DwarfInfo{
   1281                 .endian = .Little,
   1282                 .debug_info = try chopSlice(mapped_mem, debug_info.offset, debug_info.size),
   1283                 .debug_abbrev = try chopSlice(mapped_mem, debug_abbrev.offset, debug_abbrev.size),
   1284                 .debug_str = try chopSlice(mapped_mem, debug_str.offset, debug_str.size),
   1285                 .debug_line = try chopSlice(mapped_mem, debug_line.offset, debug_line.size),
   1286                 .debug_ranges = if (opt_debug_ranges) |debug_ranges|
   1287                     try chopSlice(mapped_mem, debug_ranges.offset, debug_ranges.size)
   1288                 else
   1289                     null,
   1290             };
   1291 
   1292             try DW.openDwarfDebugInfo(&di, self.allocator());
   1293 
   1294             // Add the debug info to the cache
   1295             try self.ofiles.putNoClobber(o_file_path, di);
   1296 
   1297             return di;
   1298         }
   1299 
   1300         pub fn getSymbolAtAddress(self: *@This(), address: usize) !SymbolInfo {
   1301             nosuspend {
   1302                 // Translate the VA into an address into this object
   1303                 const relocated_address = address - self.base_address;
   1304                 assert(relocated_address >= 0x100000000);
   1305 
   1306                 // Find the .o file where this symbol is defined
   1307                 const symbol = machoSearchSymbols(self.symbols, relocated_address) orelse
   1308                     return SymbolInfo{};
   1309 
   1310                 // Take the symbol name from the N_FUN STAB entry, we're going to
   1311                 // use it if we fail to find the DWARF infos
   1312                 const stab_symbol = mem.spanZ(self.strings[symbol.nlist.n_strx..]);
   1313 
   1314                 if (symbol.ofile == null)
   1315                     return SymbolInfo{ .symbol_name = stab_symbol };
   1316 
   1317                 const o_file_path = mem.spanZ(self.strings[symbol.ofile.?.n_strx..]);
   1318 
   1319                 // Check if its debug infos are already in the cache
   1320                 var o_file_di = self.ofiles.get(o_file_path) orelse
   1321                     (self.loadOFile(o_file_path) catch |err| switch (err) {
   1322                     error.FileNotFound,
   1323                     error.MissingDebugInfo,
   1324                     error.InvalidDebugInfo,
   1325                     => {
   1326                         return SymbolInfo{ .symbol_name = stab_symbol };
   1327                     },
   1328                     else => return err,
   1329                 });
   1330 
   1331                 // Translate again the address, this time into an address inside the
   1332                 // .o file
   1333                 const relocated_address_o = relocated_address - symbol.reloc;
   1334 
   1335                 if (o_file_di.findCompileUnit(relocated_address_o)) |compile_unit| {
   1336                     return SymbolInfo{
   1337                         .symbol_name = o_file_di.getSymbolName(relocated_address_o) orelse "???",
   1338                         .compile_unit_name = compile_unit.die.getAttrString(&o_file_di, DW.AT_name) catch |err| switch (err) {
   1339                             error.MissingDebugInfo, error.InvalidDebugInfo => "???",
   1340                             else => return err,
   1341                         },
   1342                         .line_info = o_file_di.getLineNumberInfo(compile_unit.*, relocated_address_o) catch |err| switch (err) {
   1343                             error.MissingDebugInfo, error.InvalidDebugInfo => null,
   1344                             else => return err,
   1345                         },
   1346                     };
   1347                 } else |err| switch (err) {
   1348                     error.MissingDebugInfo, error.InvalidDebugInfo => {
   1349                         return SymbolInfo{ .symbol_name = stab_symbol };
   1350                     },
   1351                     else => return err,
   1352                 }
   1353 
   1354                 unreachable;
   1355             }
   1356         }
   1357     },
   1358     .uefi, .windows => struct {
   1359         base_address: usize,
   1360         debug_data: PdbOrDwarf,
   1361         coff: *coff.Coff,
   1362 
   1363         pub fn allocator(self: @This()) *mem.Allocator {
   1364             return self.coff.allocator;
   1365         }
   1366 
   1367         pub fn getSymbolAtAddress(self: *@This(), address: usize) !SymbolInfo {
   1368             // Translate the VA into an address into this object
   1369             const relocated_address = address - self.base_address;
   1370 
   1371             switch (self.debug_data) {
   1372                 .dwarf => |*dwarf| {
   1373                     const dwarf_address = relocated_address + self.coff.pe_header.image_base;
   1374                     return getSymbolFromDwarf(dwarf_address, dwarf);
   1375                 },
   1376                 .pdb => {
   1377                     // fallthrough to pdb handling
   1378                 },
   1379             }
   1380 
   1381             var coff_section: *coff.Section = undefined;
   1382             const mod_index = for (self.debug_data.pdb.sect_contribs) |sect_contrib| {
   1383                 if (sect_contrib.Section > self.coff.sections.items.len) continue;
   1384                 // Remember that SectionContribEntry.Section is 1-based.
   1385                 coff_section = &self.coff.sections.items[sect_contrib.Section - 1];
   1386 
   1387                 const vaddr_start = coff_section.header.virtual_address + sect_contrib.Offset;
   1388                 const vaddr_end = vaddr_start + sect_contrib.Size;
   1389                 if (relocated_address >= vaddr_start and relocated_address < vaddr_end) {
   1390                     break sect_contrib.ModuleIndex;
   1391                 }
   1392             } else {
   1393                 // we have no information to add to the address
   1394                 return SymbolInfo{};
   1395             };
   1396 
   1397             const module = (try self.debug_data.pdb.getModule(mod_index)) orelse
   1398                 return error.InvalidDebugInfo;
   1399             const obj_basename = fs.path.basename(module.obj_file_name);
   1400 
   1401             const symbol_name = self.debug_data.pdb.getSymbolName(
   1402                 module,
   1403                 relocated_address - coff_section.header.virtual_address,
   1404             ) orelse "???";
   1405             const opt_line_info = try self.debug_data.pdb.getLineNumberInfo(
   1406                 module,
   1407                 relocated_address - coff_section.header.virtual_address,
   1408             );
   1409 
   1410             return SymbolInfo{
   1411                 .symbol_name = symbol_name,
   1412                 .compile_unit_name = obj_basename,
   1413                 .line_info = opt_line_info,
   1414             };
   1415         }
   1416     },
   1417     .linux, .netbsd, .freebsd, .dragonfly, .openbsd, .haiku => struct {
   1418         base_address: usize,
   1419         dwarf: DW.DwarfInfo,
   1420         mapped_memory: []const u8,
   1421 
   1422         pub fn getSymbolAtAddress(self: *@This(), address: usize) !SymbolInfo {
   1423             // Translate the VA into an address into this object
   1424             const relocated_address = address - self.base_address;
   1425             return getSymbolFromDwarf(relocated_address, &self.dwarf);
   1426         }
   1427     },
   1428     else => DW.DwarfInfo,
   1429 };
   1430 
   1431 fn getSymbolFromDwarf(address: u64, di: *DW.DwarfInfo) !SymbolInfo {
   1432     if (nosuspend di.findCompileUnit(address)) |compile_unit| {
   1433         return SymbolInfo{
   1434             .symbol_name = nosuspend di.getSymbolName(address) orelse "???",
   1435             .compile_unit_name = compile_unit.die.getAttrString(di, DW.AT_name) catch |err| switch (err) {
   1436                 error.MissingDebugInfo, error.InvalidDebugInfo => "???",
   1437                 else => return err,
   1438             },
   1439             .line_info = nosuspend di.getLineNumberInfo(compile_unit.*, address) catch |err| switch (err) {
   1440                 error.MissingDebugInfo, error.InvalidDebugInfo => null,
   1441                 else => return err,
   1442             },
   1443         };
   1444     } else |err| switch (err) {
   1445         error.MissingDebugInfo, error.InvalidDebugInfo => {
   1446             return SymbolInfo{};
   1447         },
   1448         else => return err,
   1449     }
   1450 }
   1451 
   1452 /// TODO multithreaded awareness
   1453 var debug_info_allocator: ?*mem.Allocator = null;
   1454 var debug_info_arena_allocator: std.heap.ArenaAllocator = undefined;
   1455 fn getDebugInfoAllocator() *mem.Allocator {
   1456     if (debug_info_allocator) |a| return a;
   1457 
   1458     debug_info_arena_allocator = std.heap.ArenaAllocator.init(std.heap.page_allocator);
   1459     debug_info_allocator = &debug_info_arena_allocator.allocator;
   1460     return &debug_info_arena_allocator.allocator;
   1461 }
   1462 
   1463 /// Whether or not the current target can print useful debug information when a segfault occurs.
   1464 pub const have_segfault_handling_support = switch (native_os) {
   1465     .linux, .netbsd => true,
   1466     .windows => true,
   1467     .freebsd, .openbsd => @hasDecl(os, "ucontext_t"),
   1468     else => false,
   1469 };
   1470 pub const enable_segfault_handler: bool = if (@hasDecl(root, "enable_segfault_handler"))
   1471     root.enable_segfault_handler
   1472 else
   1473     runtime_safety and have_segfault_handling_support;
   1474 
   1475 pub fn maybeEnableSegfaultHandler() void {
   1476     if (enable_segfault_handler) {
   1477         std.debug.attachSegfaultHandler();
   1478     }
   1479 }
   1480 
   1481 var windows_segfault_handle: ?windows.HANDLE = null;
   1482 
   1483 /// Attaches a global SIGSEGV handler which calls @panic("segmentation fault");
   1484 pub fn attachSegfaultHandler() void {
   1485     if (!have_segfault_handling_support) {
   1486         @compileError("segfault handler not supported for this target");
   1487     }
   1488     if (native_os == .windows) {
   1489         windows_segfault_handle = windows.kernel32.AddVectoredExceptionHandler(0, handleSegfaultWindows);
   1490         return;
   1491     }
   1492     var act = os.Sigaction{
   1493         .handler = .{ .sigaction = handleSegfaultLinux },
   1494         .mask = os.empty_sigset,
   1495         .flags = (os.SA_SIGINFO | os.SA_RESTART | os.SA_RESETHAND),
   1496     };
   1497 
   1498     os.sigaction(os.SIGSEGV, &act, null);
   1499     os.sigaction(os.SIGILL, &act, null);
   1500     os.sigaction(os.SIGBUS, &act, null);
   1501 }
   1502 
   1503 fn resetSegfaultHandler() void {
   1504     if (native_os == .windows) {
   1505         if (windows_segfault_handle) |handle| {
   1506             assert(windows.kernel32.RemoveVectoredExceptionHandler(handle) != 0);
   1507             windows_segfault_handle = null;
   1508         }
   1509         return;
   1510     }
   1511     var act = os.Sigaction{
   1512         .handler = .{ .sigaction = os.SIG_DFL },
   1513         .mask = os.empty_sigset,
   1514         .flags = 0,
   1515     };
   1516     os.sigaction(os.SIGSEGV, &act, null);
   1517     os.sigaction(os.SIGILL, &act, null);
   1518     os.sigaction(os.SIGBUS, &act, null);
   1519 }
   1520 
   1521 fn handleSegfaultLinux(sig: i32, info: *const os.siginfo_t, ctx_ptr: ?*const c_void) callconv(.C) noreturn {
   1522     // Reset to the default handler so that if a segfault happens in this handler it will crash
   1523     // the process. Also when this handler returns, the original instruction will be repeated
   1524     // and the resulting segfault will crash the process rather than continually dump stack traces.
   1525     resetSegfaultHandler();
   1526 
   1527     const addr = switch (native_os) {
   1528         .linux => @ptrToInt(info.fields.sigfault.addr),
   1529         .freebsd => @ptrToInt(info.addr),
   1530         .netbsd => @ptrToInt(info.info.reason.fault.addr),
   1531         .openbsd => @ptrToInt(info.data.fault.addr),
   1532         else => unreachable,
   1533     };
   1534 
   1535     // Don't use std.debug.print() as stderr_mutex may still be locked.
   1536     nosuspend {
   1537         const stderr = io.getStdErr().writer();
   1538         _ = switch (sig) {
   1539             os.SIGSEGV => stderr.print("Segmentation fault at address 0x{x}\n", .{addr}),
   1540             os.SIGILL => stderr.print("Illegal instruction at address 0x{x}\n", .{addr}),
   1541             os.SIGBUS => stderr.print("Bus error at address 0x{x}\n", .{addr}),
   1542             else => unreachable,
   1543         } catch os.abort();
   1544     }
   1545 
   1546     switch (native_arch) {
   1547         .i386 => {
   1548             const ctx = @ptrCast(*const os.ucontext_t, @alignCast(@alignOf(os.ucontext_t), ctx_ptr));
   1549             const ip = @intCast(usize, ctx.mcontext.gregs[os.REG_EIP]);
   1550             const bp = @intCast(usize, ctx.mcontext.gregs[os.REG_EBP]);
   1551             dumpStackTraceFromBase(bp, ip);
   1552         },
   1553         .x86_64 => {
   1554             const ctx = @ptrCast(*const os.ucontext_t, @alignCast(@alignOf(os.ucontext_t), ctx_ptr));
   1555             const ip = switch (native_os) {
   1556                 .linux, .netbsd => @intCast(usize, ctx.mcontext.gregs[os.REG_RIP]),
   1557                 .freebsd => @intCast(usize, ctx.mcontext.rip),
   1558                 .openbsd => @intCast(usize, ctx.sc_rip),
   1559                 else => unreachable,
   1560             };
   1561             const bp = switch (native_os) {
   1562                 .linux, .netbsd => @intCast(usize, ctx.mcontext.gregs[os.REG_RBP]),
   1563                 .openbsd => @intCast(usize, ctx.sc_rbp),
   1564                 .freebsd => @intCast(usize, ctx.mcontext.rbp),
   1565                 else => unreachable,
   1566             };
   1567             dumpStackTraceFromBase(bp, ip);
   1568         },
   1569         .arm => {
   1570             const ctx = @ptrCast(*const os.ucontext_t, @alignCast(@alignOf(os.ucontext_t), ctx_ptr));
   1571             const ip = @intCast(usize, ctx.mcontext.arm_pc);
   1572             const bp = @intCast(usize, ctx.mcontext.arm_fp);
   1573             dumpStackTraceFromBase(bp, ip);
   1574         },
   1575         .aarch64 => {
   1576             const ctx = @ptrCast(*const os.ucontext_t, @alignCast(@alignOf(os.ucontext_t), ctx_ptr));
   1577             const ip = @intCast(usize, ctx.mcontext.pc);
   1578             // x29 is the ABI-designated frame pointer
   1579             const bp = @intCast(usize, ctx.mcontext.regs[29]);
   1580             dumpStackTraceFromBase(bp, ip);
   1581         },
   1582         else => {},
   1583     }
   1584 
   1585     // We cannot allow the signal handler to return because when it runs the original instruction
   1586     // again, the memory may be mapped and undefined behavior would occur rather than repeating
   1587     // the segfault. So we simply abort here.
   1588     os.abort();
   1589 }
   1590 
   1591 fn handleSegfaultWindows(info: *windows.EXCEPTION_POINTERS) callconv(windows.WINAPI) c_long {
   1592     switch (info.ExceptionRecord.ExceptionCode) {
   1593         windows.EXCEPTION_DATATYPE_MISALIGNMENT => handleSegfaultWindowsExtra(info, 0, "Unaligned Memory Access"),
   1594         windows.EXCEPTION_ACCESS_VIOLATION => handleSegfaultWindowsExtra(info, 1, null),
   1595         windows.EXCEPTION_ILLEGAL_INSTRUCTION => handleSegfaultWindowsExtra(info, 2, null),
   1596         windows.EXCEPTION_STACK_OVERFLOW => handleSegfaultWindowsExtra(info, 0, "Stack Overflow"),
   1597         else => return windows.EXCEPTION_CONTINUE_SEARCH,
   1598     }
   1599 }
   1600 
   1601 // zig won't let me use an anon enum here https://github.com/ziglang/zig/issues/3707
   1602 fn handleSegfaultWindowsExtra(info: *windows.EXCEPTION_POINTERS, comptime msg: u8, comptime format: ?[]const u8) noreturn {
   1603     const exception_address = @ptrToInt(info.ExceptionRecord.ExceptionAddress);
   1604     if (@hasDecl(windows, "CONTEXT")) {
   1605         const regs = info.ContextRecord.getRegs();
   1606         // Don't use std.debug.print() as stderr_mutex may still be locked.
   1607         nosuspend {
   1608             const stderr = io.getStdErr().writer();
   1609             _ = switch (msg) {
   1610                 0 => stderr.print("{s}\n", .{format.?}),
   1611                 1 => stderr.print("Segmentation fault at address 0x{x}\n", .{info.ExceptionRecord.ExceptionInformation[1]}),
   1612                 2 => stderr.print("Illegal instruction at address 0x{x}\n", .{regs.ip}),
   1613                 else => unreachable,
   1614             } catch os.abort();
   1615         }
   1616 
   1617         dumpStackTraceFromBase(regs.bp, regs.ip);
   1618         os.abort();
   1619     } else {
   1620         switch (msg) {
   1621             0 => panicExtra(null, exception_address, format.?, .{}),
   1622             1 => panicExtra(null, exception_address, "Segmentation fault at address 0x{x}", .{info.ExceptionRecord.ExceptionInformation[1]}),
   1623             2 => panicExtra(null, exception_address, "Illegal Instruction", .{}),
   1624             else => unreachable,
   1625         }
   1626     }
   1627 }
   1628 
   1629 pub fn dumpStackPointerAddr(prefix: []const u8) void {
   1630     const sp = asm (""
   1631         : [argc] "={rsp}" (-> usize)
   1632     );
   1633     std.debug.warn("{} sp = 0x{x}\n", .{ prefix, sp });
   1634 }