zig

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

blob 45abfda8 (88038B) - Raw


      1 const std = @import("std.zig");
      2 const math = std.math;
      3 const mem = std.mem;
      4 const io = std.io;
      5 const os = std.os;
      6 const elf = std.elf;
      7 const DW = std.dwarf;
      8 const macho = std.macho;
      9 const coff = std.coff;
     10 const pdb = std.pdb;
     11 const windows = os.windows;
     12 const ArrayList = std.ArrayList;
     13 const builtin = @import("builtin");
     14 const maxInt = std.math.maxInt;
     15 
     16 const leb = @import("debug/leb128.zig");
     17 
     18 pub const FailingAllocator = @import("debug/failing_allocator.zig").FailingAllocator;
     19 pub const failing_allocator = &FailingAllocator.init(global_allocator, 0).allocator;
     20 
     21 pub const runtime_safety = switch (builtin.mode) {
     22     builtin.Mode.Debug, builtin.Mode.ReleaseSafe => true,
     23     builtin.Mode.ReleaseFast, builtin.Mode.ReleaseSmall => false,
     24 };
     25 
     26 const Module = struct {
     27     mod_info: pdb.ModInfo,
     28     module_name: []u8,
     29     obj_file_name: []u8,
     30 
     31     populated: bool,
     32     symbols: []u8,
     33     subsect_info: []u8,
     34     checksum_offset: ?usize,
     35 };
     36 
     37 /// Tries to write to stderr, unbuffered, and ignores any error returned.
     38 /// Does not append a newline.
     39 var stderr_file: os.File = undefined;
     40 var stderr_file_out_stream: os.File.OutStream = undefined;
     41 
     42 var stderr_stream: ?*io.OutStream(os.File.WriteError) = null;
     43 var stderr_mutex = std.Mutex.init();
     44 pub fn warn(comptime fmt: []const u8, args: ...) void {
     45     const held = stderr_mutex.acquire();
     46     defer held.release();
     47     const stderr = getStderrStream() catch return;
     48     stderr.print(fmt, args) catch return;
     49 }
     50 
     51 pub fn getStderrStream() !*io.OutStream(os.File.WriteError) {
     52     if (stderr_stream) |st| {
     53         return st;
     54     } else {
     55         stderr_file = try io.getStdErr();
     56         stderr_file_out_stream = stderr_file.outStream();
     57         const st = &stderr_file_out_stream.stream;
     58         stderr_stream = st;
     59         return st;
     60     }
     61 }
     62 
     63 /// TODO multithreaded awareness
     64 var self_debug_info: ?DebugInfo = null;
     65 
     66 pub fn getSelfDebugInfo() !*DebugInfo {
     67     if (self_debug_info) |*info| {
     68         return info;
     69     } else {
     70         self_debug_info = try openSelfDebugInfo(getDebugInfoAllocator());
     71         return &self_debug_info.?;
     72     }
     73 }
     74 
     75 fn wantTtyColor() bool {
     76     var bytes: [128]u8 = undefined;
     77     const allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator;
     78     return if (std.os.getEnvVarOwned(allocator, "ZIG_DEBUG_COLOR")) |_| true else |_| stderr_file.isTty();
     79 }
     80 
     81 /// Tries to print the current stack trace to stderr, unbuffered, and ignores any error returned.
     82 /// TODO multithreaded awareness
     83 pub fn dumpCurrentStackTrace(start_addr: ?usize) void {
     84     const stderr = getStderrStream() catch return;
     85     const debug_info = getSelfDebugInfo() catch |err| {
     86         stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return;
     87         return;
     88     };
     89     writeCurrentStackTrace(stderr, debug_info, wantTtyColor(), start_addr) catch |err| {
     90         stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return;
     91         return;
     92     };
     93 }
     94 
     95 /// Returns a slice with the same pointer as addresses, with a potentially smaller len.
     96 /// On Windows, when first_address is not null, we ask for at least 32 stack frames,
     97 /// and then try to find the first address. If addresses.len is more than 32, we
     98 /// capture that many stack frames exactly, and then look for the first address,
     99 /// chopping off the irrelevant frames and shifting so that the returned addresses pointer
    100 /// equals the passed in addresses pointer.
    101 pub fn captureStackTrace(first_address: ?usize, stack_trace: *builtin.StackTrace) void {
    102     switch (builtin.os) {
    103         builtin.Os.windows => {
    104             const addrs = stack_trace.instruction_addresses;
    105             const u32_addrs_len = @intCast(u32, addrs.len);
    106             const first_addr = first_address orelse {
    107                 stack_trace.index = windows.RtlCaptureStackBackTrace(
    108                     0,
    109                     u32_addrs_len,
    110                     @ptrCast(**c_void, addrs.ptr),
    111                     null,
    112                 );
    113                 return;
    114             };
    115             var addr_buf_stack: [32]usize = undefined;
    116             const addr_buf = if (addr_buf_stack.len > addrs.len) addr_buf_stack[0..] else addrs;
    117             const n = windows.RtlCaptureStackBackTrace(0, u32_addrs_len, @ptrCast(**c_void, addr_buf.ptr), null);
    118             const first_index = for (addr_buf[0..n]) |addr, i| {
    119                 if (addr == first_addr) {
    120                     break i;
    121                 }
    122             } else {
    123                 stack_trace.index = 0;
    124                 return;
    125             };
    126             const slice = addr_buf[first_index..n];
    127             // We use a for loop here because slice and addrs may alias.
    128             for (slice) |addr, i| {
    129                 addrs[i] = addr;
    130             }
    131             stack_trace.index = slice.len;
    132         },
    133         else => {
    134             var it = StackIterator.init(first_address);
    135             for (stack_trace.instruction_addresses) |*addr, i| {
    136                 addr.* = it.next() orelse {
    137                     stack_trace.index = i;
    138                     return;
    139                 };
    140             }
    141             stack_trace.index = stack_trace.instruction_addresses.len;
    142         },
    143     }
    144 }
    145 
    146 /// Tries to print a stack trace to stderr, unbuffered, and ignores any error returned.
    147 /// TODO multithreaded awareness
    148 pub fn dumpStackTrace(stack_trace: builtin.StackTrace) void {
    149     const stderr = getStderrStream() catch return;
    150     const debug_info = getSelfDebugInfo() catch |err| {
    151         stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return;
    152         return;
    153     };
    154     writeStackTrace(stack_trace, stderr, getDebugInfoAllocator(), debug_info, wantTtyColor()) catch |err| {
    155         stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return;
    156         return;
    157     };
    158 }
    159 
    160 /// This function invokes undefined behavior when `ok` is `false`.
    161 /// In Debug and ReleaseSafe modes, calls to this function are always
    162 /// generated, and the `unreachable` statement triggers a panic.
    163 /// In ReleaseFast and ReleaseSmall modes, calls to this function are
    164 /// optimized away, and in fact the optimizer is able to use the assertion
    165 /// in its heuristics.
    166 /// Inside a test block, it is best to use the `std.testing` module rather
    167 /// than this function, because this function may not detect a test failure
    168 /// in ReleaseFast and ReleaseSafe mode. Outside of a test block, this assert
    169 /// function is the correct function to use.
    170 pub fn assert(ok: bool) void {
    171     if (!ok) unreachable; // assertion failure
    172 }
    173 
    174 pub fn panic(comptime format: []const u8, args: ...) noreturn {
    175     @setCold(true);
    176     const first_trace_addr = @returnAddress();
    177     panicExtra(null, first_trace_addr, format, args);
    178 }
    179 
    180 /// TODO multithreaded awareness
    181 var panicking: u8 = 0; // TODO make this a bool
    182 
    183 pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, comptime format: []const u8, args: ...) noreturn {
    184     @setCold(true);
    185 
    186     if (@atomicRmw(u8, &panicking, builtin.AtomicRmwOp.Xchg, 1, builtin.AtomicOrder.SeqCst) == 1) {
    187         // Panicked during a panic.
    188 
    189         // TODO detect if a different thread caused the panic, because in that case
    190         // we would want to return here instead of calling abort, so that the thread
    191         // which first called panic can finish printing a stack trace.
    192         os.abort();
    193     }
    194     const stderr = getStderrStream() catch os.abort();
    195     stderr.print(format ++ "\n", args) catch os.abort();
    196     if (trace) |t| {
    197         dumpStackTrace(t.*);
    198     }
    199     dumpCurrentStackTrace(first_trace_addr);
    200 
    201     os.abort();
    202 }
    203 
    204 const RED = "\x1b[31;1m";
    205 const GREEN = "\x1b[32;1m";
    206 const CYAN = "\x1b[36;1m";
    207 const WHITE = "\x1b[37;1m";
    208 const DIM = "\x1b[2m";
    209 const RESET = "\x1b[0m";
    210 
    211 pub fn writeStackTrace(
    212     stack_trace: builtin.StackTrace,
    213     out_stream: var,
    214     allocator: *mem.Allocator,
    215     debug_info: *DebugInfo,
    216     tty_color: bool,
    217 ) !void {
    218     var frame_index: usize = 0;
    219     var frames_left: usize = std.math.min(stack_trace.index, stack_trace.instruction_addresses.len);
    220 
    221     while (frames_left != 0) : ({
    222         frames_left -= 1;
    223         frame_index = (frame_index + 1) % stack_trace.instruction_addresses.len;
    224     }) {
    225         const return_address = stack_trace.instruction_addresses[frame_index];
    226         try printSourceAtAddress(debug_info, out_stream, return_address - 1, tty_color);
    227     }
    228 }
    229 
    230 pub const StackIterator = struct {
    231     first_addr: ?usize,
    232     fp: usize,
    233 
    234     pub fn init(first_addr: ?usize) StackIterator {
    235         return StackIterator{
    236             .first_addr = first_addr,
    237             .fp = @frameAddress(),
    238         };
    239     }
    240 
    241     fn next(self: *StackIterator) ?usize {
    242         if (self.fp == 0) return null;
    243         self.fp = @intToPtr(*const usize, self.fp).*;
    244         if (self.fp == 0) return null;
    245 
    246         if (self.first_addr) |addr| {
    247             while (self.fp != 0) : (self.fp = @intToPtr(*const usize, self.fp).*) {
    248                 const return_address = @intToPtr(*const usize, self.fp + @sizeOf(usize)).*;
    249                 if (addr == return_address) {
    250                     self.first_addr = null;
    251                     return return_address;
    252                 }
    253             }
    254         }
    255 
    256         const return_address = @intToPtr(*const usize, self.fp + @sizeOf(usize)).*;
    257         return return_address;
    258     }
    259 };
    260 
    261 pub fn writeCurrentStackTrace(out_stream: var, debug_info: *DebugInfo, tty_color: bool, start_addr: ?usize) !void {
    262     switch (builtin.os) {
    263         builtin.Os.windows => return writeCurrentStackTraceWindows(out_stream, debug_info, tty_color, start_addr),
    264         else => {},
    265     }
    266     var it = StackIterator.init(start_addr);
    267     while (it.next()) |return_address| {
    268         try printSourceAtAddress(debug_info, out_stream, return_address - 1, tty_color);
    269     }
    270 }
    271 
    272 pub fn writeCurrentStackTraceWindows(
    273     out_stream: var,
    274     debug_info: *DebugInfo,
    275     tty_color: bool,
    276     start_addr: ?usize,
    277 ) !void {
    278     var addr_buf: [1024]usize = undefined;
    279     const n = windows.RtlCaptureStackBackTrace(0, addr_buf.len, @ptrCast(**c_void, &addr_buf), null);
    280     const addrs = addr_buf[0..n];
    281     var start_i: usize = if (start_addr) |saddr| blk: {
    282         for (addrs) |addr, i| {
    283             if (addr == saddr) break :blk i;
    284         }
    285         return;
    286     } else 0;
    287     for (addrs[start_i..]) |addr| {
    288         try printSourceAtAddress(debug_info, out_stream, addr, tty_color);
    289     }
    290 }
    291 
    292 pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: var, address: usize, tty_color: bool) !void {
    293     switch (builtin.os) {
    294         builtin.Os.macosx => return printSourceAtAddressMacOs(debug_info, out_stream, address, tty_color),
    295         builtin.Os.linux, builtin.Os.freebsd, builtin.Os.netbsd => return printSourceAtAddressLinux(debug_info, out_stream, address, tty_color),
    296         builtin.Os.windows => return printSourceAtAddressWindows(debug_info, out_stream, address, tty_color),
    297         else => return error.UnsupportedOperatingSystem,
    298     }
    299 }
    300 
    301 fn printSourceAtAddressWindows(di: *DebugInfo, out_stream: var, relocated_address: usize, tty_color: bool) !void {
    302     const allocator = getDebugInfoAllocator();
    303     const base_address = os.getBaseAddress();
    304     const relative_address = relocated_address - base_address;
    305 
    306     var coff_section: *coff.Section = undefined;
    307     const mod_index = for (di.sect_contribs) |sect_contrib| {
    308         if (sect_contrib.Section > di.coff.sections.len) continue;
    309         // Remember that SectionContribEntry.Section is 1-based.
    310         coff_section = &di.coff.sections.toSlice()[sect_contrib.Section - 1];
    311 
    312         const vaddr_start = coff_section.header.virtual_address + sect_contrib.Offset;
    313         const vaddr_end = vaddr_start + sect_contrib.Size;
    314         if (relative_address >= vaddr_start and relative_address < vaddr_end) {
    315             break sect_contrib.ModuleIndex;
    316         }
    317     } else {
    318         // we have no information to add to the address
    319         if (tty_color) {
    320             try out_stream.print("???:?:?: ");
    321             setTtyColor(TtyColor.Dim);
    322             try out_stream.print("0x{x} in ??? (???)", relocated_address);
    323             setTtyColor(TtyColor.Reset);
    324             try out_stream.print("\n\n\n");
    325         } else {
    326             try out_stream.print("???:?:?: 0x{x} in ??? (???)\n\n\n", relocated_address);
    327         }
    328         return;
    329     };
    330 
    331     const mod = &di.modules[mod_index];
    332     try populateModule(di, mod);
    333     const obj_basename = os.path.basename(mod.obj_file_name);
    334 
    335     var symbol_i: usize = 0;
    336     const symbol_name = while (symbol_i != mod.symbols.len) {
    337         const prefix = @ptrCast(*pdb.RecordPrefix, &mod.symbols[symbol_i]);
    338         if (prefix.RecordLen < 2)
    339             return error.InvalidDebugInfo;
    340         switch (prefix.RecordKind) {
    341             pdb.SymbolKind.S_LPROC32 => {
    342                 const proc_sym = @ptrCast(*pdb.ProcSym, &mod.symbols[symbol_i + @sizeOf(pdb.RecordPrefix)]);
    343                 const vaddr_start = coff_section.header.virtual_address + proc_sym.CodeOffset;
    344                 const vaddr_end = vaddr_start + proc_sym.CodeSize;
    345                 if (relative_address >= vaddr_start and relative_address < vaddr_end) {
    346                     break mem.toSliceConst(u8, @ptrCast([*]u8, proc_sym) + @sizeOf(pdb.ProcSym));
    347                 }
    348             },
    349             else => {},
    350         }
    351         symbol_i += prefix.RecordLen + @sizeOf(u16);
    352         if (symbol_i > mod.symbols.len)
    353             return error.InvalidDebugInfo;
    354     } else "???";
    355 
    356     const subsect_info = mod.subsect_info;
    357 
    358     var sect_offset: usize = 0;
    359     var skip_len: usize = undefined;
    360     const opt_line_info = subsections: {
    361         const checksum_offset = mod.checksum_offset orelse break :subsections null;
    362         while (sect_offset != subsect_info.len) : (sect_offset += skip_len) {
    363             const subsect_hdr = @ptrCast(*pdb.DebugSubsectionHeader, &subsect_info[sect_offset]);
    364             skip_len = subsect_hdr.Length;
    365             sect_offset += @sizeOf(pdb.DebugSubsectionHeader);
    366 
    367             switch (subsect_hdr.Kind) {
    368                 pdb.DebugSubsectionKind.Lines => {
    369                     var line_index = sect_offset;
    370 
    371                     const line_hdr = @ptrCast(*pdb.LineFragmentHeader, &subsect_info[line_index]);
    372                     if (line_hdr.RelocSegment == 0) return error.MissingDebugInfo;
    373                     line_index += @sizeOf(pdb.LineFragmentHeader);
    374                     const frag_vaddr_start = coff_section.header.virtual_address + line_hdr.RelocOffset;
    375                     const frag_vaddr_end = frag_vaddr_start + line_hdr.CodeSize;
    376 
    377                     if (relative_address >= frag_vaddr_start and relative_address < frag_vaddr_end) {
    378                         // There is an unknown number of LineBlockFragmentHeaders (and their accompanying line and column records)
    379                         // from now on. We will iterate through them, and eventually find a LineInfo that we're interested in,
    380                         // breaking out to :subsections. If not, we will make sure to not read anything outside of this subsection.
    381                         const subsection_end_index = sect_offset + subsect_hdr.Length;
    382 
    383                         while (line_index < subsection_end_index) {
    384                             const block_hdr = @ptrCast(*pdb.LineBlockFragmentHeader, &subsect_info[line_index]);
    385                             line_index += @sizeOf(pdb.LineBlockFragmentHeader);
    386                             const start_line_index = line_index;
    387 
    388                             const has_column = line_hdr.Flags.LF_HaveColumns;
    389 
    390                             // All line entries are stored inside their line block by ascending start address.
    391                             // Heuristic: we want to find the last line entry that has a vaddr_start <= relative_address.
    392                             // This is done with a simple linear search.
    393                             var line_i: u32 = 0;
    394                             while (line_i < block_hdr.NumLines) : (line_i += 1) {
    395                                 const line_num_entry = @ptrCast(*pdb.LineNumberEntry, &subsect_info[line_index]);
    396                                 line_index += @sizeOf(pdb.LineNumberEntry);
    397 
    398                                 const vaddr_start = frag_vaddr_start + line_num_entry.Offset;
    399                                 if (relative_address <= vaddr_start) {
    400                                     break;
    401                                 }
    402                             }
    403 
    404                             // line_i == 0 would mean that no matching LineNumberEntry was found.
    405                             if (line_i > 0) {
    406                                 const subsect_index = checksum_offset + block_hdr.NameIndex;
    407                                 const chksum_hdr = @ptrCast(*pdb.FileChecksumEntryHeader, &mod.subsect_info[subsect_index]);
    408                                 const strtab_offset = @sizeOf(pdb.PDBStringTableHeader) + chksum_hdr.FileNameOffset;
    409                                 try di.pdb.string_table.seekTo(strtab_offset);
    410                                 const source_file_name = try di.pdb.string_table.readNullTermString(allocator);
    411 
    412                                 const line_entry_idx = line_i - 1;
    413 
    414                                 const column = if (has_column) blk: {
    415                                     const start_col_index = start_line_index + @sizeOf(pdb.LineNumberEntry) * block_hdr.NumLines;
    416                                     const col_index = start_col_index + @sizeOf(pdb.ColumnNumberEntry) * line_entry_idx;
    417                                     const col_num_entry = @ptrCast(*pdb.ColumnNumberEntry, &subsect_info[col_index]);
    418                                     break :blk col_num_entry.StartColumn;
    419                                 } else 0;
    420 
    421                                 const found_line_index = start_line_index + line_entry_idx * @sizeOf(pdb.LineNumberEntry);
    422                                 const line_num_entry = @ptrCast(*pdb.LineNumberEntry, &subsect_info[found_line_index]);
    423                                 const flags = @ptrCast(*pdb.LineNumberEntry.Flags, &line_num_entry.Flags);
    424 
    425                                 break :subsections LineInfo{
    426                                     .allocator = allocator,
    427                                     .file_name = source_file_name,
    428                                     .line = flags.Start,
    429                                     .column = column,
    430                                 };
    431                             }
    432                         }
    433 
    434                         // Checking that we are not reading garbage after the (possibly) multiple block fragments.
    435                         if (line_index != subsection_end_index) {
    436                             return error.InvalidDebugInfo;
    437                         }
    438                     }
    439                 },
    440                 else => {},
    441             }
    442 
    443             if (sect_offset > subsect_info.len)
    444                 return error.InvalidDebugInfo;
    445         } else {
    446             break :subsections null;
    447         }
    448     };
    449 
    450     if (tty_color) {
    451         setTtyColor(TtyColor.White);
    452         if (opt_line_info) |li| {
    453             try out_stream.print("{}:{}:{}", li.file_name, li.line, li.column);
    454         } else {
    455             try out_stream.print("???:?:?");
    456         }
    457         setTtyColor(TtyColor.Reset);
    458         try out_stream.print(": ");
    459         setTtyColor(TtyColor.Dim);
    460         try out_stream.print("0x{x} in {} ({})", relocated_address, symbol_name, obj_basename);
    461         setTtyColor(TtyColor.Reset);
    462 
    463         if (opt_line_info) |line_info| {
    464             try out_stream.print("\n");
    465             if (printLineFromFileAnyOs(out_stream, line_info)) {
    466                 if (line_info.column == 0) {
    467                     try out_stream.write("\n");
    468                 } else {
    469                     {
    470                         var col_i: usize = 1;
    471                         while (col_i < line_info.column) : (col_i += 1) {
    472                             try out_stream.writeByte(' ');
    473                         }
    474                     }
    475                     setTtyColor(TtyColor.Green);
    476                     try out_stream.write("^");
    477                     setTtyColor(TtyColor.Reset);
    478                     try out_stream.write("\n");
    479                 }
    480             } else |err| switch (err) {
    481                 error.EndOfFile => {},
    482                 error.FileNotFound => {
    483                     setTtyColor(TtyColor.Dim);
    484                     try out_stream.write("file not found\n\n");
    485                     setTtyColor(TtyColor.White);
    486                 },
    487                 else => return err,
    488             }
    489         } else {
    490             try out_stream.print("\n\n\n");
    491         }
    492     } else {
    493         if (opt_line_info) |li| {
    494             try out_stream.print("{}:{}:{}: 0x{x} in {} ({})\n\n\n", li.file_name, li.line, li.column, relocated_address, symbol_name, obj_basename);
    495         } else {
    496             try out_stream.print("???:?:?: 0x{x} in {} ({})\n\n\n", relocated_address, symbol_name, obj_basename);
    497         }
    498     }
    499 }
    500 
    501 const TtyColor = enum {
    502     Red,
    503     Green,
    504     Cyan,
    505     White,
    506     Dim,
    507     Bold,
    508     Reset,
    509 };
    510 
    511 /// TODO this is a special case hack right now. clean it up and maybe make it part of std.fmt
    512 fn setTtyColor(tty_color: TtyColor) void {
    513     if (os.supportsAnsiEscapeCodes(stderr_file.handle)) {
    514         switch (tty_color) {
    515             TtyColor.Red => {
    516                 stderr_file.write(RED) catch return;
    517             },
    518             TtyColor.Green => {
    519                 stderr_file.write(GREEN) catch return;
    520             },
    521             TtyColor.Cyan => {
    522                 stderr_file.write(CYAN) catch return;
    523             },
    524             TtyColor.White, TtyColor.Bold => {
    525                 stderr_file.write(WHITE) catch return;
    526             },
    527             TtyColor.Dim => {
    528                 stderr_file.write(DIM) catch return;
    529             },
    530             TtyColor.Reset => {
    531                 stderr_file.write(RESET) catch return;
    532             },
    533         }
    534     } else {
    535         const S = struct {
    536             var attrs: windows.WORD = undefined;
    537             var init_attrs = false;
    538         };
    539         if (!S.init_attrs) {
    540             S.init_attrs = true;
    541             var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
    542             // TODO handle error
    543             _ = windows.GetConsoleScreenBufferInfo(stderr_file.handle, &info);
    544             S.attrs = info.wAttributes;
    545         }
    546 
    547         // TODO handle errors
    548         switch (tty_color) {
    549             TtyColor.Red => {
    550                 _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_RED | windows.FOREGROUND_INTENSITY);
    551             },
    552             TtyColor.Green => {
    553                 _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_GREEN | windows.FOREGROUND_INTENSITY);
    554             },
    555             TtyColor.Cyan => {
    556                 _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY);
    557             },
    558             TtyColor.White, TtyColor.Bold => {
    559                 _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_RED | windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY);
    560             },
    561             TtyColor.Dim => {
    562                 _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_INTENSITY);
    563             },
    564             TtyColor.Reset => {
    565                 _ = windows.SetConsoleTextAttribute(stderr_file.handle, S.attrs);
    566             },
    567         }
    568     }
    569 }
    570 
    571 fn populateModule(di: *DebugInfo, mod: *Module) !void {
    572     if (mod.populated)
    573         return;
    574     const allocator = getDebugInfoAllocator();
    575 
    576     // At most one can be non-zero.
    577     if (mod.mod_info.C11ByteSize != 0 and mod.mod_info.C13ByteSize != 0)
    578         return error.InvalidDebugInfo;
    579 
    580     if (mod.mod_info.C13ByteSize == 0)
    581         return;
    582 
    583     const modi = di.pdb.getStreamById(mod.mod_info.ModuleSymStream) orelse return error.MissingDebugInfo;
    584 
    585     const signature = try modi.stream.readIntLittle(u32);
    586     if (signature != 4)
    587         return error.InvalidDebugInfo;
    588 
    589     mod.symbols = try allocator.alloc(u8, mod.mod_info.SymByteSize - 4);
    590     try modi.stream.readNoEof(mod.symbols);
    591 
    592     mod.subsect_info = try allocator.alloc(u8, mod.mod_info.C13ByteSize);
    593     try modi.stream.readNoEof(mod.subsect_info);
    594 
    595     var sect_offset: usize = 0;
    596     var skip_len: usize = undefined;
    597     while (sect_offset != mod.subsect_info.len) : (sect_offset += skip_len) {
    598         const subsect_hdr = @ptrCast(*pdb.DebugSubsectionHeader, &mod.subsect_info[sect_offset]);
    599         skip_len = subsect_hdr.Length;
    600         sect_offset += @sizeOf(pdb.DebugSubsectionHeader);
    601 
    602         switch (subsect_hdr.Kind) {
    603             pdb.DebugSubsectionKind.FileChecksums => {
    604                 mod.checksum_offset = sect_offset;
    605                 break;
    606             },
    607             else => {},
    608         }
    609 
    610         if (sect_offset > mod.subsect_info.len)
    611             return error.InvalidDebugInfo;
    612     }
    613 
    614     mod.populated = true;
    615 }
    616 
    617 fn machoSearchSymbols(symbols: []const MachoSymbol, address: usize) ?*const MachoSymbol {
    618     var min: usize = 0;
    619     var max: usize = symbols.len - 1; // Exclude sentinel.
    620     while (min < max) {
    621         const mid = min + (max - min) / 2;
    622         const curr = &symbols[mid];
    623         const next = &symbols[mid + 1];
    624         if (address >= next.address()) {
    625             min = mid + 1;
    626         } else if (address < curr.address()) {
    627             max = mid;
    628         } else {
    629             return curr;
    630         }
    631     }
    632     return null;
    633 }
    634 
    635 fn printSourceAtAddressMacOs(di: *DebugInfo, out_stream: var, address: usize, tty_color: bool) !void {
    636     const base_addr = std.os.getBaseAddress();
    637     const adjusted_addr = 0x100000000 + (address - base_addr);
    638 
    639     const symbol = machoSearchSymbols(di.symbols, adjusted_addr) orelse {
    640         if (tty_color) {
    641             try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in ??? (???)" ++ RESET ++ "\n\n\n", address);
    642         } else {
    643             try out_stream.print("???:?:?: 0x{x} in ??? (???)\n\n\n", address);
    644         }
    645         return;
    646     };
    647 
    648     const symbol_name = mem.toSliceConst(u8, di.strings.ptr + symbol.nlist.n_strx);
    649     const compile_unit_name = if (symbol.ofile) |ofile| blk: {
    650         const ofile_path = mem.toSliceConst(u8, di.strings.ptr + ofile.n_strx);
    651         break :blk os.path.basename(ofile_path);
    652     } else "???";
    653     if (getLineNumberInfoMacOs(di, symbol.*, adjusted_addr)) |line_info| {
    654         defer line_info.deinit();
    655         try printLineInfo(
    656             out_stream,
    657             line_info,
    658             address,
    659             symbol_name,
    660             compile_unit_name,
    661             tty_color,
    662             printLineFromFileAnyOs,
    663         );
    664     } else |err| switch (err) {
    665         error.MissingDebugInfo, error.InvalidDebugInfo => {
    666             if (tty_color) {
    667                 try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in {} ({})" ++ RESET ++ "\n\n\n", address, symbol_name, compile_unit_name);
    668             } else {
    669                 try out_stream.print("???:?:?: 0x{x} in {} ({})\n\n\n", address, symbol_name, compile_unit_name);
    670             }
    671         },
    672         else => return err,
    673     }
    674 }
    675 
    676 /// This function works in freestanding mode.
    677 /// fn printLineFromFile(out_stream: var, line_info: LineInfo) !void
    678 pub fn printSourceAtAddressDwarf(
    679     debug_info: *DwarfInfo,
    680     out_stream: var,
    681     address: usize,
    682     tty_color: bool,
    683     comptime printLineFromFile: var,
    684 ) !void {
    685     const compile_unit = findCompileUnit(debug_info, address) catch {
    686         if (tty_color) {
    687             try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in ??? (???)" ++ RESET ++ "\n\n\n", address);
    688         } else {
    689             try out_stream.print("???:?:?: 0x{x} in ??? (???)\n\n\n", address);
    690         }
    691         return;
    692     };
    693     const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name);
    694     if (getLineNumberInfoDwarf(debug_info, compile_unit.*, address)) |line_info| {
    695         defer line_info.deinit();
    696         const symbol_name = getSymbolNameDwarf(debug_info, address) orelse "???";
    697         try printLineInfo(
    698             out_stream,
    699             line_info,
    700             address,
    701             symbol_name,
    702             compile_unit_name,
    703             tty_color,
    704             printLineFromFile,
    705         );
    706     } else |err| switch (err) {
    707         error.MissingDebugInfo, error.InvalidDebugInfo => {
    708             if (tty_color) {
    709                 try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in ??? ({})" ++ RESET ++ "\n\n\n", address, compile_unit_name);
    710             } else {
    711                 try out_stream.print("???:?:?: 0x{x} in ??? ({})\n\n\n", address, compile_unit_name);
    712             }
    713         },
    714         else => return err,
    715     }
    716 }
    717 
    718 pub fn printSourceAtAddressLinux(debug_info: *DebugInfo, out_stream: var, address: usize, tty_color: bool) !void {
    719     return printSourceAtAddressDwarf(debug_info, out_stream, address, tty_color, printLineFromFileAnyOs);
    720 }
    721 
    722 fn printLineInfo(
    723     out_stream: var,
    724     line_info: LineInfo,
    725     address: usize,
    726     symbol_name: []const u8,
    727     compile_unit_name: []const u8,
    728     tty_color: bool,
    729     comptime printLineFromFile: var,
    730 ) !void {
    731     if (tty_color) {
    732         try out_stream.print(
    733             WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++ DIM ++ "0x{x} in {} ({})" ++ RESET ++ "\n",
    734             line_info.file_name,
    735             line_info.line,
    736             line_info.column,
    737             address,
    738             symbol_name,
    739             compile_unit_name,
    740         );
    741         if (printLineFromFile(out_stream, line_info)) {
    742             if (line_info.column == 0) {
    743                 try out_stream.write("\n");
    744             } else {
    745                 {
    746                     var col_i: usize = 1;
    747                     while (col_i < line_info.column) : (col_i += 1) {
    748                         try out_stream.writeByte(' ');
    749                     }
    750                 }
    751                 try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n");
    752             }
    753         } else |err| switch (err) {
    754             error.EndOfFile => {},
    755             else => return err,
    756         }
    757     } else {
    758         try out_stream.print(
    759             "{}:{}:{}: 0x{x} in {} ({})\n",
    760             line_info.file_name,
    761             line_info.line,
    762             line_info.column,
    763             address,
    764             symbol_name,
    765             compile_unit_name,
    766         );
    767     }
    768 }
    769 
    770 // TODO use this
    771 pub const OpenSelfDebugInfoError = error{
    772     MissingDebugInfo,
    773     OutOfMemory,
    774     UnsupportedOperatingSystem,
    775 };
    776 
    777 pub fn openSelfDebugInfo(allocator: *mem.Allocator) !DebugInfo {
    778     switch (builtin.os) {
    779         builtin.Os.linux, builtin.Os.freebsd, builtin.Os.netbsd => return openSelfDebugInfoLinux(allocator),
    780         builtin.Os.macosx, builtin.Os.ios => return openSelfDebugInfoMacOs(allocator),
    781         builtin.Os.windows => return openSelfDebugInfoWindows(allocator),
    782         else => return error.UnsupportedOperatingSystem,
    783     }
    784 }
    785 
    786 fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo {
    787     const self_file = try os.openSelfExe();
    788     defer self_file.close();
    789 
    790     const coff_obj = try allocator.create(coff.Coff);
    791     coff_obj.* = coff.Coff{
    792         .in_file = self_file,
    793         .allocator = allocator,
    794         .coff_header = undefined,
    795         .pe_header = undefined,
    796         .sections = undefined,
    797         .guid = undefined,
    798         .age = undefined,
    799     };
    800 
    801     var di = DebugInfo{
    802         .coff = coff_obj,
    803         .pdb = undefined,
    804         .sect_contribs = undefined,
    805         .modules = undefined,
    806     };
    807 
    808     try di.coff.loadHeader();
    809 
    810     var path_buf: [windows.MAX_PATH]u8 = undefined;
    811     const len = try di.coff.getPdbPath(path_buf[0..]);
    812     const raw_path = path_buf[0..len];
    813 
    814     const path = try os.path.resolve(allocator, [][]const u8{raw_path});
    815 
    816     try di.pdb.openFile(di.coff, path);
    817 
    818     var pdb_stream = di.pdb.getStream(pdb.StreamType.Pdb) orelse return error.InvalidDebugInfo;
    819     const version = try pdb_stream.stream.readIntLittle(u32);
    820     const signature = try pdb_stream.stream.readIntLittle(u32);
    821     const age = try pdb_stream.stream.readIntLittle(u32);
    822     var guid: [16]u8 = undefined;
    823     try pdb_stream.stream.readNoEof(guid[0..]);
    824     if (!mem.eql(u8, di.coff.guid, guid) or di.coff.age != age)
    825         return error.InvalidDebugInfo;
    826     // We validated the executable and pdb match.
    827 
    828     const string_table_index = str_tab_index: {
    829         const name_bytes_len = try pdb_stream.stream.readIntLittle(u32);
    830         const name_bytes = try allocator.alloc(u8, name_bytes_len);
    831         try pdb_stream.stream.readNoEof(name_bytes);
    832 
    833         const HashTableHeader = packed struct {
    834             Size: u32,
    835             Capacity: u32,
    836 
    837             fn maxLoad(cap: u32) u32 {
    838                 return cap * 2 / 3 + 1;
    839             }
    840         };
    841         const hash_tbl_hdr = try pdb_stream.stream.readStruct(HashTableHeader);
    842         if (hash_tbl_hdr.Capacity == 0)
    843             return error.InvalidDebugInfo;
    844 
    845         if (hash_tbl_hdr.Size > HashTableHeader.maxLoad(hash_tbl_hdr.Capacity))
    846             return error.InvalidDebugInfo;
    847 
    848         const present = try readSparseBitVector(&pdb_stream.stream, allocator);
    849         if (present.len != hash_tbl_hdr.Size)
    850             return error.InvalidDebugInfo;
    851         const deleted = try readSparseBitVector(&pdb_stream.stream, allocator);
    852 
    853         const Bucket = struct {
    854             first: u32,
    855             second: u32,
    856         };
    857         const bucket_list = try allocator.alloc(Bucket, present.len);
    858         for (present) |_| {
    859             const name_offset = try pdb_stream.stream.readIntLittle(u32);
    860             const name_index = try pdb_stream.stream.readIntLittle(u32);
    861             const name = mem.toSlice(u8, name_bytes.ptr + name_offset);
    862             if (mem.eql(u8, name, "/names")) {
    863                 break :str_tab_index name_index;
    864             }
    865         }
    866         return error.MissingDebugInfo;
    867     };
    868 
    869     di.pdb.string_table = di.pdb.getStreamById(string_table_index) orelse return error.InvalidDebugInfo;
    870     di.pdb.dbi = di.pdb.getStream(pdb.StreamType.Dbi) orelse return error.MissingDebugInfo;
    871 
    872     const dbi = di.pdb.dbi;
    873 
    874     // Dbi Header
    875     const dbi_stream_header = try dbi.stream.readStruct(pdb.DbiStreamHeader);
    876     const mod_info_size = dbi_stream_header.ModInfoSize;
    877     const section_contrib_size = dbi_stream_header.SectionContributionSize;
    878 
    879     var modules = ArrayList(Module).init(allocator);
    880 
    881     // Module Info Substream
    882     var mod_info_offset: usize = 0;
    883     while (mod_info_offset != mod_info_size) {
    884         const mod_info = try dbi.stream.readStruct(pdb.ModInfo);
    885         var this_record_len: usize = @sizeOf(pdb.ModInfo);
    886 
    887         const module_name = try dbi.readNullTermString(allocator);
    888         this_record_len += module_name.len + 1;
    889 
    890         const obj_file_name = try dbi.readNullTermString(allocator);
    891         this_record_len += obj_file_name.len + 1;
    892 
    893         if (this_record_len % 4 != 0) {
    894             const round_to_next_4 = (this_record_len | 0x3) + 1;
    895             const march_forward_bytes = round_to_next_4 - this_record_len;
    896             try dbi.seekForward(march_forward_bytes);
    897             this_record_len += march_forward_bytes;
    898         }
    899 
    900         try modules.append(Module{
    901             .mod_info = mod_info,
    902             .module_name = module_name,
    903             .obj_file_name = obj_file_name,
    904 
    905             .populated = false,
    906             .symbols = undefined,
    907             .subsect_info = undefined,
    908             .checksum_offset = null,
    909         });
    910 
    911         mod_info_offset += this_record_len;
    912         if (mod_info_offset > mod_info_size)
    913             return error.InvalidDebugInfo;
    914     }
    915 
    916     di.modules = modules.toOwnedSlice();
    917 
    918     // Section Contribution Substream
    919     var sect_contribs = ArrayList(pdb.SectionContribEntry).init(allocator);
    920     var sect_cont_offset: usize = 0;
    921     if (section_contrib_size != 0) {
    922         const ver = @intToEnum(pdb.SectionContrSubstreamVersion, try dbi.stream.readIntLittle(u32));
    923         if (ver != pdb.SectionContrSubstreamVersion.Ver60)
    924             return error.InvalidDebugInfo;
    925         sect_cont_offset += @sizeOf(u32);
    926     }
    927     while (sect_cont_offset != section_contrib_size) {
    928         const entry = try sect_contribs.addOne();
    929         entry.* = try dbi.stream.readStruct(pdb.SectionContribEntry);
    930         sect_cont_offset += @sizeOf(pdb.SectionContribEntry);
    931 
    932         if (sect_cont_offset > section_contrib_size)
    933             return error.InvalidDebugInfo;
    934     }
    935 
    936     di.sect_contribs = sect_contribs.toOwnedSlice();
    937 
    938     return di;
    939 }
    940 
    941 fn readSparseBitVector(stream: var, allocator: *mem.Allocator) ![]usize {
    942     const num_words = try stream.readIntLittle(u32);
    943     var word_i: usize = 0;
    944     var list = ArrayList(usize).init(allocator);
    945     while (word_i != num_words) : (word_i += 1) {
    946         const word = try stream.readIntLittle(u32);
    947         var bit_i: u5 = 0;
    948         while (true) : (bit_i += 1) {
    949             if (word & (u32(1) << bit_i) != 0) {
    950                 try list.append(word_i * 32 + bit_i);
    951             }
    952             if (bit_i == maxInt(u5)) break;
    953         }
    954     }
    955     return list.toOwnedSlice();
    956 }
    957 
    958 fn findDwarfSectionFromElf(elf_file: *elf.Elf, name: []const u8) !?DwarfInfo.Section {
    959     const elf_header = (try elf_file.findSection(name)) orelse return null;
    960     return DwarfInfo.Section{
    961         .offset = elf_header.offset,
    962         .size = elf_header.size,
    963     };
    964 }
    965 
    966 /// Initialize DWARF info. The caller has the responsibility to initialize most
    967 /// the DwarfInfo fields before calling. These fields can be left undefined:
    968 /// * abbrev_table_list
    969 /// * compile_unit_list
    970 pub fn openDwarfDebugInfo(di: *DwarfInfo, allocator: *mem.Allocator) !void {
    971     di.abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator);
    972     di.compile_unit_list = ArrayList(CompileUnit).init(allocator);
    973     di.func_list = ArrayList(Func).init(allocator);
    974     try scanAllFunctions(di);
    975     try scanAllCompileUnits(di);
    976 }
    977 
    978 pub fn openElfDebugInfo(
    979     allocator: *mem.Allocator,
    980     elf_seekable_stream: *DwarfSeekableStream,
    981     elf_in_stream: *DwarfInStream,
    982 ) !DwarfInfo {
    983     var efile: elf.Elf = undefined;
    984     try efile.openStream(allocator, elf_seekable_stream, elf_in_stream);
    985     errdefer efile.close();
    986 
    987     var di = DwarfInfo{
    988         .dwarf_seekable_stream = elf_seekable_stream,
    989         .dwarf_in_stream = elf_in_stream,
    990         .endian = efile.endian,
    991         .debug_info = (try findDwarfSectionFromElf(&efile, ".debug_info")) orelse return error.MissingDebugInfo,
    992         .debug_abbrev = (try findDwarfSectionFromElf(&efile, ".debug_abbrev")) orelse return error.MissingDebugInfo,
    993         .debug_str = (try findDwarfSectionFromElf(&efile, ".debug_str")) orelse return error.MissingDebugInfo,
    994         .debug_line = (try findDwarfSectionFromElf(&efile, ".debug_line")) orelse return error.MissingDebugInfo,
    995         .debug_ranges = (try findDwarfSectionFromElf(&efile, ".debug_ranges")),
    996         .abbrev_table_list = undefined,
    997         .compile_unit_list = undefined,
    998         .func_list = undefined,
    999     };
   1000     try openDwarfDebugInfo(&di, allocator);
   1001     return di;
   1002 }
   1003 
   1004 fn openSelfDebugInfoLinux(allocator: *mem.Allocator) !DwarfInfo {
   1005     const S = struct {
   1006         var self_exe_file: os.File = undefined;
   1007         var self_exe_mmap_seekable: io.SliceSeekableInStream = undefined;
   1008     };
   1009 
   1010     S.self_exe_file = try os.openSelfExe();
   1011     errdefer S.self_exe_file.close();
   1012 
   1013     const self_exe_mmap_len = try S.self_exe_file.getEndPos();
   1014     const self_exe_mmap = os.posix.mmap(
   1015         null,
   1016         self_exe_mmap_len,
   1017         os.posix.PROT_READ,
   1018         os.posix.MAP_SHARED,
   1019         S.self_exe_file.handle,
   1020         0,
   1021     );
   1022     if (self_exe_mmap == os.posix.MAP_FAILED) return error.OutOfMemory;
   1023     errdefer assert(os.posix.munmap(self_exe_mmap, self_exe_mmap_len) == 0);
   1024 
   1025     const file_mmap_slice = @intToPtr([*]const u8, self_exe_mmap)[0..self_exe_mmap_len];
   1026     S.self_exe_mmap_seekable = io.SliceSeekableInStream.init(file_mmap_slice);
   1027 
   1028     return openElfDebugInfo(
   1029         allocator,
   1030         // TODO https://github.com/ziglang/zig/issues/764
   1031         @ptrCast(*DwarfSeekableStream, &S.self_exe_mmap_seekable.seekable_stream),
   1032         // TODO https://github.com/ziglang/zig/issues/764
   1033         @ptrCast(*DwarfInStream, &S.self_exe_mmap_seekable.stream),
   1034     );
   1035 }
   1036 
   1037 fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo {
   1038     const hdr = &std.c._mh_execute_header;
   1039     assert(hdr.magic == std.macho.MH_MAGIC_64);
   1040 
   1041     const hdr_base = @ptrCast([*]u8, hdr);
   1042     var ptr = hdr_base + @sizeOf(macho.mach_header_64);
   1043     var ncmd: u32 = hdr.ncmds;
   1044     const symtab = while (ncmd != 0) : (ncmd -= 1) {
   1045         const lc = @ptrCast(*std.macho.load_command, ptr);
   1046         switch (lc.cmd) {
   1047             std.macho.LC_SYMTAB => break @ptrCast(*std.macho.symtab_command, ptr),
   1048             else => {},
   1049         }
   1050         ptr += lc.cmdsize; // TODO https://github.com/ziglang/zig/issues/1403
   1051     } else {
   1052         return error.MissingDebugInfo;
   1053     };
   1054     const syms = @ptrCast([*]macho.nlist_64, @alignCast(@alignOf(macho.nlist_64), hdr_base + symtab.symoff))[0..symtab.nsyms];
   1055     const strings = @ptrCast([*]u8, hdr_base + symtab.stroff)[0..symtab.strsize];
   1056 
   1057     const symbols_buf = try allocator.alloc(MachoSymbol, syms.len);
   1058 
   1059     var ofile: ?*macho.nlist_64 = null;
   1060     var reloc: u64 = 0;
   1061     var symbol_index: usize = 0;
   1062     var last_len: u64 = 0;
   1063     for (syms) |*sym| {
   1064         if (sym.n_type & std.macho.N_STAB != 0) {
   1065             switch (sym.n_type) {
   1066                 std.macho.N_OSO => {
   1067                     ofile = sym;
   1068                     reloc = 0;
   1069                 },
   1070                 std.macho.N_FUN => {
   1071                     if (sym.n_sect == 0) {
   1072                         last_len = sym.n_value;
   1073                     } else {
   1074                         symbols_buf[symbol_index] = MachoSymbol{
   1075                             .nlist = sym,
   1076                             .ofile = ofile,
   1077                             .reloc = reloc,
   1078                         };
   1079                         symbol_index += 1;
   1080                     }
   1081                 },
   1082                 std.macho.N_BNSYM => {
   1083                     if (reloc == 0) {
   1084                         reloc = sym.n_value;
   1085                     }
   1086                 },
   1087                 else => continue,
   1088             }
   1089         }
   1090     }
   1091     const sentinel = try allocator.create(macho.nlist_64);
   1092     sentinel.* = macho.nlist_64{
   1093         .n_strx = 0,
   1094         .n_type = 36,
   1095         .n_sect = 0,
   1096         .n_desc = 0,
   1097         .n_value = symbols_buf[symbol_index - 1].nlist.n_value + last_len,
   1098     };
   1099 
   1100     const symbols = allocator.shrink(symbols_buf, symbol_index);
   1101 
   1102     // Even though lld emits symbols in ascending order, this debug code
   1103     // should work for programs linked in any valid way.
   1104     // This sort is so that we can binary search later.
   1105     std.sort.sort(MachoSymbol, symbols, MachoSymbol.addressLessThan);
   1106 
   1107     return DebugInfo{
   1108         .ofiles = DebugInfo.OFileTable.init(allocator),
   1109         .symbols = symbols,
   1110         .strings = strings,
   1111     };
   1112 }
   1113 
   1114 fn printLineFromFileAnyOs(out_stream: var, line_info: LineInfo) !void {
   1115     var f = try os.File.openRead(line_info.file_name);
   1116     defer f.close();
   1117     // TODO fstat and make sure that the file has the correct size
   1118 
   1119     var buf: [os.page_size]u8 = undefined;
   1120     var line: usize = 1;
   1121     var column: usize = 1;
   1122     var abs_index: usize = 0;
   1123     while (true) {
   1124         const amt_read = try f.read(buf[0..]);
   1125         const slice = buf[0..amt_read];
   1126 
   1127         for (slice) |byte| {
   1128             if (line == line_info.line) {
   1129                 try out_stream.writeByte(byte);
   1130                 if (byte == '\n') {
   1131                     return;
   1132                 }
   1133             }
   1134             if (byte == '\n') {
   1135                 line += 1;
   1136                 column = 1;
   1137             } else {
   1138                 column += 1;
   1139             }
   1140         }
   1141 
   1142         if (amt_read < buf.len) return error.EndOfFile;
   1143     }
   1144 }
   1145 
   1146 const MachoSymbol = struct {
   1147     nlist: *macho.nlist_64,
   1148     ofile: ?*macho.nlist_64,
   1149     reloc: u64,
   1150 
   1151     /// Returns the address from the macho file
   1152     fn address(self: MachoSymbol) u64 {
   1153         return self.nlist.n_value;
   1154     }
   1155 
   1156     fn addressLessThan(lhs: MachoSymbol, rhs: MachoSymbol) bool {
   1157         return lhs.address() < rhs.address();
   1158     }
   1159 };
   1160 
   1161 const MachOFile = struct {
   1162     bytes: []align(@alignOf(macho.mach_header_64)) const u8,
   1163     sect_debug_info: ?*const macho.section_64,
   1164     sect_debug_line: ?*const macho.section_64,
   1165 };
   1166 
   1167 pub const DwarfSeekableStream = io.SeekableStream(anyerror, anyerror);
   1168 pub const DwarfInStream = io.InStream(anyerror);
   1169 
   1170 pub const DwarfInfo = struct {
   1171     dwarf_seekable_stream: *DwarfSeekableStream,
   1172     dwarf_in_stream: *DwarfInStream,
   1173     endian: builtin.Endian,
   1174     debug_info: Section,
   1175     debug_abbrev: Section,
   1176     debug_str: Section,
   1177     debug_line: Section,
   1178     debug_ranges: ?Section,
   1179     abbrev_table_list: ArrayList(AbbrevTableHeader),
   1180     compile_unit_list: ArrayList(CompileUnit),
   1181     func_list: ArrayList(Func),
   1182 
   1183     pub const Section = struct {
   1184         offset: u64,
   1185         size: u64,
   1186     };
   1187 
   1188     pub fn allocator(self: DwarfInfo) *mem.Allocator {
   1189         return self.abbrev_table_list.allocator;
   1190     }
   1191 
   1192     pub fn readString(self: *DwarfInfo) ![]u8 {
   1193         return readStringRaw(self.allocator(), self.dwarf_in_stream);
   1194     }
   1195 };
   1196 
   1197 pub const DebugInfo = switch (builtin.os) {
   1198     builtin.Os.macosx, builtin.Os.ios => struct {
   1199         symbols: []const MachoSymbol,
   1200         strings: []const u8,
   1201         ofiles: OFileTable,
   1202 
   1203         const OFileTable = std.HashMap(
   1204             *macho.nlist_64,
   1205             MachOFile,
   1206             std.hash_map.getHashPtrAddrFn(*macho.nlist_64),
   1207             std.hash_map.getTrivialEqlFn(*macho.nlist_64),
   1208         );
   1209 
   1210         pub fn allocator(self: DebugInfo) *mem.Allocator {
   1211             return self.ofiles.allocator;
   1212         }
   1213     },
   1214     builtin.Os.uefi, builtin.Os.windows => struct {
   1215         pdb: pdb.Pdb,
   1216         coff: *coff.Coff,
   1217         sect_contribs: []pdb.SectionContribEntry,
   1218         modules: []Module,
   1219     },
   1220     builtin.Os.linux, builtin.Os.freebsd, builtin.Os.netbsd => DwarfInfo,
   1221     else => @compileError("Unsupported OS"),
   1222 };
   1223 
   1224 const PcRange = struct {
   1225     start: u64,
   1226     end: u64,
   1227 };
   1228 
   1229 const CompileUnit = struct {
   1230     version: u16,
   1231     is_64: bool,
   1232     die: *Die,
   1233     pc_range: ?PcRange,
   1234 };
   1235 
   1236 const AbbrevTable = ArrayList(AbbrevTableEntry);
   1237 
   1238 const AbbrevTableHeader = struct {
   1239     // offset from .debug_abbrev
   1240     offset: u64,
   1241     table: AbbrevTable,
   1242 };
   1243 
   1244 const AbbrevTableEntry = struct {
   1245     has_children: bool,
   1246     abbrev_code: u64,
   1247     tag_id: u64,
   1248     attrs: ArrayList(AbbrevAttr),
   1249 };
   1250 
   1251 const AbbrevAttr = struct {
   1252     attr_id: u64,
   1253     form_id: u64,
   1254 };
   1255 
   1256 const FormValue = union(enum) {
   1257     Address: u64,
   1258     Block: []u8,
   1259     Const: Constant,
   1260     ExprLoc: []u8,
   1261     Flag: bool,
   1262     SecOffset: u64,
   1263     Ref: u64,
   1264     RefAddr: u64,
   1265     String: []u8,
   1266     StrPtr: u64,
   1267 };
   1268 
   1269 const Constant = struct {
   1270     payload: u64,
   1271     signed: bool,
   1272 
   1273     fn asUnsignedLe(self: *const Constant) !u64 {
   1274         if (self.signed) return error.InvalidDebugInfo;
   1275         return self.payload;
   1276     }
   1277 };
   1278 
   1279 const Die = struct {
   1280     tag_id: u64,
   1281     has_children: bool,
   1282     attrs: ArrayList(Attr),
   1283 
   1284     const Attr = struct {
   1285         id: u64,
   1286         value: FormValue,
   1287     };
   1288 
   1289     fn getAttr(self: *const Die, id: u64) ?*const FormValue {
   1290         for (self.attrs.toSliceConst()) |*attr| {
   1291             if (attr.id == id) return &attr.value;
   1292         }
   1293         return null;
   1294     }
   1295 
   1296     fn getAttrAddr(self: *const Die, id: u64) !u64 {
   1297         const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
   1298         return switch (form_value.*) {
   1299             FormValue.Address => |value| value,
   1300             else => error.InvalidDebugInfo,
   1301         };
   1302     }
   1303 
   1304     fn getAttrSecOffset(self: *const Die, id: u64) !u64 {
   1305         const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
   1306         return switch (form_value.*) {
   1307             FormValue.Const => |value| value.asUnsignedLe(),
   1308             FormValue.SecOffset => |value| value,
   1309             else => error.InvalidDebugInfo,
   1310         };
   1311     }
   1312 
   1313     fn getAttrUnsignedLe(self: *const Die, id: u64) !u64 {
   1314         const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
   1315         return switch (form_value.*) {
   1316             FormValue.Const => |value| value.asUnsignedLe(),
   1317             else => error.InvalidDebugInfo,
   1318         };
   1319     }
   1320 
   1321     fn getAttrRef(self: *const Die, id: u64) !u64 {
   1322         const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
   1323         return switch (form_value.*) {
   1324             FormValue.Ref => |value| value,
   1325             else => error.InvalidDebugInfo,
   1326         };
   1327     }
   1328 
   1329     fn getAttrString(self: *const Die, di: *DwarfInfo, id: u64) ![]u8 {
   1330         const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
   1331         return switch (form_value.*) {
   1332             FormValue.String => |value| value,
   1333             FormValue.StrPtr => |offset| getString(di, offset),
   1334             else => error.InvalidDebugInfo,
   1335         };
   1336     }
   1337 };
   1338 
   1339 const FileEntry = struct {
   1340     file_name: []const u8,
   1341     dir_index: usize,
   1342     mtime: usize,
   1343     len_bytes: usize,
   1344 };
   1345 
   1346 pub const LineInfo = struct {
   1347     line: u64,
   1348     column: u64,
   1349     file_name: []const u8,
   1350     allocator: ?*mem.Allocator,
   1351 
   1352     fn deinit(self: LineInfo) void {
   1353         const allocator = self.allocator orelse return;
   1354         allocator.free(self.file_name);
   1355     }
   1356 };
   1357 
   1358 const LineNumberProgram = struct {
   1359     address: usize,
   1360     file: usize,
   1361     line: i64,
   1362     column: u64,
   1363     is_stmt: bool,
   1364     basic_block: bool,
   1365     end_sequence: bool,
   1366 
   1367     target_address: usize,
   1368     include_dirs: []const []const u8,
   1369     file_entries: *ArrayList(FileEntry),
   1370 
   1371     prev_address: usize,
   1372     prev_file: usize,
   1373     prev_line: i64,
   1374     prev_column: u64,
   1375     prev_is_stmt: bool,
   1376     prev_basic_block: bool,
   1377     prev_end_sequence: bool,
   1378 
   1379     pub fn init(is_stmt: bool, include_dirs: []const []const u8, file_entries: *ArrayList(FileEntry), target_address: usize) LineNumberProgram {
   1380         return LineNumberProgram{
   1381             .address = 0,
   1382             .file = 1,
   1383             .line = 1,
   1384             .column = 0,
   1385             .is_stmt = is_stmt,
   1386             .basic_block = false,
   1387             .end_sequence = false,
   1388             .include_dirs = include_dirs,
   1389             .file_entries = file_entries,
   1390             .target_address = target_address,
   1391             .prev_address = 0,
   1392             .prev_file = undefined,
   1393             .prev_line = undefined,
   1394             .prev_column = undefined,
   1395             .prev_is_stmt = undefined,
   1396             .prev_basic_block = undefined,
   1397             .prev_end_sequence = undefined,
   1398         };
   1399     }
   1400 
   1401     pub fn checkLineMatch(self: *LineNumberProgram) !?LineInfo {
   1402         if (self.target_address >= self.prev_address and self.target_address < self.address) {
   1403             const file_entry = if (self.prev_file == 0) {
   1404                 return error.MissingDebugInfo;
   1405             } else if (self.prev_file - 1 >= self.file_entries.len) {
   1406                 return error.InvalidDebugInfo;
   1407             } else
   1408                 &self.file_entries.items[self.prev_file - 1];
   1409 
   1410             const dir_name = if (file_entry.dir_index >= self.include_dirs.len) {
   1411                 return error.InvalidDebugInfo;
   1412             } else
   1413                 self.include_dirs[file_entry.dir_index];
   1414             const file_name = try os.path.join(self.file_entries.allocator, [][]const u8{ dir_name, file_entry.file_name });
   1415             errdefer self.file_entries.allocator.free(file_name);
   1416             return LineInfo{
   1417                 .line = if (self.prev_line >= 0) @intCast(u64, self.prev_line) else 0,
   1418                 .column = self.prev_column,
   1419                 .file_name = file_name,
   1420                 .allocator = self.file_entries.allocator,
   1421             };
   1422         }
   1423 
   1424         self.prev_address = self.address;
   1425         self.prev_file = self.file;
   1426         self.prev_line = self.line;
   1427         self.prev_column = self.column;
   1428         self.prev_is_stmt = self.is_stmt;
   1429         self.prev_basic_block = self.basic_block;
   1430         self.prev_end_sequence = self.end_sequence;
   1431         return null;
   1432     }
   1433 };
   1434 
   1435 fn readStringRaw(allocator: *mem.Allocator, in_stream: var) ![]u8 {
   1436     var buf = ArrayList(u8).init(allocator);
   1437     while (true) {
   1438         const byte = try in_stream.readByte();
   1439         if (byte == 0) break;
   1440         try buf.append(byte);
   1441     }
   1442     return buf.toSlice();
   1443 }
   1444 
   1445 fn getString(di: *DwarfInfo, offset: u64) ![]u8 {
   1446     const pos = di.debug_str.offset + offset;
   1447     try di.dwarf_seekable_stream.seekTo(pos);
   1448     return di.readString();
   1449 }
   1450 
   1451 fn readAllocBytes(allocator: *mem.Allocator, in_stream: var, size: usize) ![]u8 {
   1452     const buf = try allocator.alloc(u8, size);
   1453     errdefer allocator.free(buf);
   1454     if ((try in_stream.read(buf)) < size) return error.EndOfFile;
   1455     return buf;
   1456 }
   1457 
   1458 fn parseFormValueBlockLen(allocator: *mem.Allocator, in_stream: var, size: usize) !FormValue {
   1459     const buf = try readAllocBytes(allocator, in_stream, size);
   1460     return FormValue{ .Block = buf };
   1461 }
   1462 
   1463 fn parseFormValueBlock(allocator: *mem.Allocator, in_stream: var, size: usize) !FormValue {
   1464     const block_len = try in_stream.readVarInt(usize, builtin.Endian.Little, size);
   1465     return parseFormValueBlockLen(allocator, in_stream, block_len);
   1466 }
   1467 
   1468 fn parseFormValueConstant(allocator: *mem.Allocator, in_stream: var, signed: bool, comptime size: i32) !FormValue {
   1469     return FormValue{
   1470         .Const = Constant{
   1471             .signed = signed,
   1472             .payload = switch (size) {
   1473                 1 => try in_stream.readIntLittle(u8),
   1474                 2 => try in_stream.readIntLittle(u16),
   1475                 4 => try in_stream.readIntLittle(u32),
   1476                 8 => try in_stream.readIntLittle(u64),
   1477                 -1 => if (signed) @bitCast(u64, try leb.readILEB128(i64, in_stream)) else try leb.readULEB128(u64, in_stream),
   1478                 else => @compileError("Invalid size"),
   1479             },
   1480         },
   1481     };
   1482 }
   1483 
   1484 fn parseFormValueDwarfOffsetSize(in_stream: var, is_64: bool) !u64 {
   1485     return if (is_64) try in_stream.readIntLittle(u64) else u64(try in_stream.readIntLittle(u32));
   1486 }
   1487 
   1488 fn parseFormValueTargetAddrSize(in_stream: var) !u64 {
   1489     return if (@sizeOf(usize) == 4) u64(try in_stream.readIntLittle(u32)) else if (@sizeOf(usize) == 8) try in_stream.readIntLittle(u64) else unreachable;
   1490 }
   1491 
   1492 fn parseFormValueRef(allocator: *mem.Allocator, in_stream: var, size: i32) !FormValue {
   1493     return FormValue{
   1494         .Ref = switch (size) {
   1495             1 => try in_stream.readIntLittle(u8),
   1496             2 => try in_stream.readIntLittle(u16),
   1497             4 => try in_stream.readIntLittle(u32),
   1498             8 => try in_stream.readIntLittle(u64),
   1499             -1 => try leb.readULEB128(u64, in_stream),
   1500             else => unreachable,
   1501         },
   1502     };
   1503 }
   1504 
   1505 fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64: bool) anyerror!FormValue {
   1506     return switch (form_id) {
   1507         DW.FORM_addr => FormValue{ .Address = try parseFormValueTargetAddrSize(in_stream) },
   1508         DW.FORM_block1 => parseFormValueBlock(allocator, in_stream, 1),
   1509         DW.FORM_block2 => parseFormValueBlock(allocator, in_stream, 2),
   1510         DW.FORM_block4 => parseFormValueBlock(allocator, in_stream, 4),
   1511         DW.FORM_block => x: {
   1512             const block_len = try leb.readULEB128(usize, in_stream);
   1513             return parseFormValueBlockLen(allocator, in_stream, block_len);
   1514         },
   1515         DW.FORM_data1 => parseFormValueConstant(allocator, in_stream, false, 1),
   1516         DW.FORM_data2 => parseFormValueConstant(allocator, in_stream, false, 2),
   1517         DW.FORM_data4 => parseFormValueConstant(allocator, in_stream, false, 4),
   1518         DW.FORM_data8 => parseFormValueConstant(allocator, in_stream, false, 8),
   1519         DW.FORM_udata, DW.FORM_sdata => {
   1520             const signed = form_id == DW.FORM_sdata;
   1521             return parseFormValueConstant(allocator, in_stream, signed, -1);
   1522         },
   1523         DW.FORM_exprloc => {
   1524             const size = try leb.readULEB128(usize, in_stream);
   1525             const buf = try readAllocBytes(allocator, in_stream, size);
   1526             return FormValue{ .ExprLoc = buf };
   1527         },
   1528         DW.FORM_flag => FormValue{ .Flag = (try in_stream.readByte()) != 0 },
   1529         DW.FORM_flag_present => FormValue{ .Flag = true },
   1530         DW.FORM_sec_offset => FormValue{ .SecOffset = try parseFormValueDwarfOffsetSize(in_stream, is_64) },
   1531 
   1532         DW.FORM_ref1 => parseFormValueRef(allocator, in_stream, 1),
   1533         DW.FORM_ref2 => parseFormValueRef(allocator, in_stream, 2),
   1534         DW.FORM_ref4 => parseFormValueRef(allocator, in_stream, 4),
   1535         DW.FORM_ref8 => parseFormValueRef(allocator, in_stream, 8),
   1536         DW.FORM_ref_udata => parseFormValueRef(allocator, in_stream, -1),
   1537 
   1538         DW.FORM_ref_addr => FormValue{ .RefAddr = try parseFormValueDwarfOffsetSize(in_stream, is_64) },
   1539         DW.FORM_ref_sig8 => FormValue{ .Ref = try in_stream.readIntLittle(u64) },
   1540 
   1541         DW.FORM_string => FormValue{ .String = try readStringRaw(allocator, in_stream) },
   1542         DW.FORM_strp => FormValue{ .StrPtr = try parseFormValueDwarfOffsetSize(in_stream, is_64) },
   1543         DW.FORM_indirect => {
   1544             const child_form_id = try leb.readULEB128(u64, in_stream);
   1545             return parseFormValue(allocator, in_stream, child_form_id, is_64);
   1546         },
   1547         else => error.InvalidDebugInfo,
   1548     };
   1549 }
   1550 
   1551 fn parseAbbrevTable(di: *DwarfInfo) !AbbrevTable {
   1552     var result = AbbrevTable.init(di.allocator());
   1553     while (true) {
   1554         const abbrev_code = try leb.readULEB128(u64, di.dwarf_in_stream);
   1555         if (abbrev_code == 0) return result;
   1556         try result.append(AbbrevTableEntry{
   1557             .abbrev_code = abbrev_code,
   1558             .tag_id = try leb.readULEB128(u64, di.dwarf_in_stream),
   1559             .has_children = (try di.dwarf_in_stream.readByte()) == DW.CHILDREN_yes,
   1560             .attrs = ArrayList(AbbrevAttr).init(di.allocator()),
   1561         });
   1562         const attrs = &result.items[result.len - 1].attrs;
   1563 
   1564         while (true) {
   1565             const attr_id = try leb.readULEB128(u64, di.dwarf_in_stream);
   1566             const form_id = try leb.readULEB128(u64, di.dwarf_in_stream);
   1567             if (attr_id == 0 and form_id == 0) break;
   1568             try attrs.append(AbbrevAttr{
   1569                 .attr_id = attr_id,
   1570                 .form_id = form_id,
   1571             });
   1572         }
   1573     }
   1574 }
   1575 
   1576 /// Gets an already existing AbbrevTable given the abbrev_offset, or if not found,
   1577 /// seeks in the stream and parses it.
   1578 fn getAbbrevTable(di: *DwarfInfo, abbrev_offset: u64) !*const AbbrevTable {
   1579     for (di.abbrev_table_list.toSlice()) |*header| {
   1580         if (header.offset == abbrev_offset) {
   1581             return &header.table;
   1582         }
   1583     }
   1584     try di.dwarf_seekable_stream.seekTo(di.debug_abbrev.offset + abbrev_offset);
   1585     try di.abbrev_table_list.append(AbbrevTableHeader{
   1586         .offset = abbrev_offset,
   1587         .table = try parseAbbrevTable(di),
   1588     });
   1589     return &di.abbrev_table_list.items[di.abbrev_table_list.len - 1].table;
   1590 }
   1591 
   1592 fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*const AbbrevTableEntry {
   1593     for (abbrev_table.toSliceConst()) |*table_entry| {
   1594         if (table_entry.abbrev_code == abbrev_code) return table_entry;
   1595     }
   1596     return null;
   1597 }
   1598 
   1599 fn parseDie1(di: *DwarfInfo, abbrev_table: *const AbbrevTable, is_64: bool) !?Die {
   1600     const abbrev_code = try leb.readULEB128(u64, di.dwarf_in_stream);
   1601     if (abbrev_code == 0) return null;
   1602     const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return error.InvalidDebugInfo;
   1603 
   1604     var result = Die{
   1605         .tag_id = table_entry.tag_id,
   1606         .has_children = table_entry.has_children,
   1607         .attrs = ArrayList(Die.Attr).init(di.allocator()),
   1608     };
   1609     try result.attrs.resize(table_entry.attrs.len);
   1610     for (table_entry.attrs.toSliceConst()) |attr, i| {
   1611         result.attrs.items[i] = Die.Attr{
   1612             .id = attr.attr_id,
   1613             .value = try parseFormValue(di.allocator(), di.dwarf_in_stream, attr.form_id, is_64),
   1614         };
   1615     }
   1616     return result;
   1617 }
   1618 
   1619 fn parseDie(di: *DwarfInfo, abbrev_table: *const AbbrevTable, is_64: bool) !Die {
   1620     const abbrev_code = try leb.readULEB128(u64, di.dwarf_in_stream);
   1621     const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return error.InvalidDebugInfo;
   1622 
   1623     var result = Die{
   1624         .tag_id = table_entry.tag_id,
   1625         .has_children = table_entry.has_children,
   1626         .attrs = ArrayList(Die.Attr).init(di.allocator()),
   1627     };
   1628     try result.attrs.resize(table_entry.attrs.len);
   1629     for (table_entry.attrs.toSliceConst()) |attr, i| {
   1630         result.attrs.items[i] = Die.Attr{
   1631             .id = attr.attr_id,
   1632             .value = try parseFormValue(di.allocator(), di.dwarf_in_stream, attr.form_id, is_64),
   1633         };
   1634     }
   1635     return result;
   1636 }
   1637 
   1638 fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: usize) !LineInfo {
   1639     const ofile = symbol.ofile orelse return error.MissingDebugInfo;
   1640     const gop = try di.ofiles.getOrPut(ofile);
   1641     const mach_o_file = if (gop.found_existing) &gop.kv.value else blk: {
   1642         errdefer _ = di.ofiles.remove(ofile);
   1643         const ofile_path = mem.toSliceConst(u8, di.strings.ptr + ofile.n_strx);
   1644 
   1645         gop.kv.value = MachOFile{
   1646             .bytes = try std.io.readFileAllocAligned(di.ofiles.allocator, ofile_path, @alignOf(macho.mach_header_64)),
   1647             .sect_debug_info = null,
   1648             .sect_debug_line = null,
   1649         };
   1650         const hdr = @ptrCast(*const macho.mach_header_64, gop.kv.value.bytes.ptr);
   1651         if (hdr.magic != std.macho.MH_MAGIC_64) return error.InvalidDebugInfo;
   1652 
   1653         const hdr_base = @ptrCast([*]const u8, hdr);
   1654         var ptr = hdr_base + @sizeOf(macho.mach_header_64);
   1655         var ncmd: u32 = hdr.ncmds;
   1656         const segcmd = while (ncmd != 0) : (ncmd -= 1) {
   1657             const lc = @ptrCast(*const std.macho.load_command, ptr);
   1658             switch (lc.cmd) {
   1659                 std.macho.LC_SEGMENT_64 => break @ptrCast(*const std.macho.segment_command_64, @alignCast(@alignOf(std.macho.segment_command_64), ptr)),
   1660                 else => {},
   1661             }
   1662             ptr += lc.cmdsize; // TODO https://github.com/ziglang/zig/issues/1403
   1663         } else {
   1664             return error.MissingDebugInfo;
   1665         };
   1666         const sections = @ptrCast([*]const macho.section_64, @alignCast(@alignOf(macho.section_64), ptr + @sizeOf(std.macho.segment_command_64)))[0..segcmd.nsects];
   1667         for (sections) |*sect| {
   1668             if (sect.flags & macho.SECTION_TYPE == macho.S_REGULAR and
   1669                 (sect.flags & macho.SECTION_ATTRIBUTES) & macho.S_ATTR_DEBUG == macho.S_ATTR_DEBUG)
   1670             {
   1671                 const sect_name = mem.toSliceConst(u8, &sect.sectname);
   1672                 if (mem.eql(u8, sect_name, "__debug_line")) {
   1673                     gop.kv.value.sect_debug_line = sect;
   1674                 } else if (mem.eql(u8, sect_name, "__debug_info")) {
   1675                     gop.kv.value.sect_debug_info = sect;
   1676                 }
   1677             }
   1678         }
   1679 
   1680         break :blk &gop.kv.value;
   1681     };
   1682 
   1683     const sect_debug_line = mach_o_file.sect_debug_line orelse return error.MissingDebugInfo;
   1684     var ptr = mach_o_file.bytes.ptr + sect_debug_line.offset;
   1685 
   1686     var is_64: bool = undefined;
   1687     const unit_length = try readInitialLengthMem(&ptr, &is_64);
   1688     if (unit_length == 0) return error.MissingDebugInfo;
   1689 
   1690     const version = readIntMem(&ptr, u16, builtin.Endian.Little);
   1691     // TODO support 3 and 5
   1692     if (version != 2 and version != 4) return error.InvalidDebugInfo;
   1693 
   1694     const prologue_length = if (is_64)
   1695         readIntMem(&ptr, u64, builtin.Endian.Little)
   1696     else
   1697         readIntMem(&ptr, u32, builtin.Endian.Little);
   1698     const prog_start = ptr + prologue_length;
   1699 
   1700     const minimum_instruction_length = readByteMem(&ptr);
   1701     if (minimum_instruction_length == 0) return error.InvalidDebugInfo;
   1702 
   1703     if (version >= 4) {
   1704         // maximum_operations_per_instruction
   1705         ptr += 1;
   1706     }
   1707 
   1708     const default_is_stmt = readByteMem(&ptr) != 0;
   1709     const line_base = readByteSignedMem(&ptr);
   1710 
   1711     const line_range = readByteMem(&ptr);
   1712     if (line_range == 0) return error.InvalidDebugInfo;
   1713 
   1714     const opcode_base = readByteMem(&ptr);
   1715 
   1716     const standard_opcode_lengths = ptr[0 .. opcode_base - 1];
   1717     ptr += opcode_base - 1;
   1718 
   1719     var include_directories = ArrayList([]const u8).init(di.allocator());
   1720     try include_directories.append("");
   1721     while (true) {
   1722         const dir = readStringMem(&ptr);
   1723         if (dir.len == 0) break;
   1724         try include_directories.append(dir);
   1725     }
   1726 
   1727     var file_entries = ArrayList(FileEntry).init(di.allocator());
   1728     var prog = LineNumberProgram.init(default_is_stmt, include_directories.toSliceConst(), &file_entries, target_address);
   1729 
   1730     while (true) {
   1731         const file_name = readStringMem(&ptr);
   1732         if (file_name.len == 0) break;
   1733         const dir_index = try leb.readULEB128Mem(usize, &ptr);
   1734         const mtime = try leb.readULEB128Mem(usize, &ptr);
   1735         const len_bytes = try leb.readULEB128Mem(usize, &ptr);
   1736         try file_entries.append(FileEntry{
   1737             .file_name = file_name,
   1738             .dir_index = dir_index,
   1739             .mtime = mtime,
   1740             .len_bytes = len_bytes,
   1741         });
   1742     }
   1743 
   1744     ptr = prog_start;
   1745     while (true) {
   1746         const opcode = readByteMem(&ptr);
   1747 
   1748         if (opcode == DW.LNS_extended_op) {
   1749             const op_size = try leb.readULEB128Mem(u64, &ptr);
   1750             if (op_size < 1) return error.InvalidDebugInfo;
   1751             var sub_op = readByteMem(&ptr);
   1752             switch (sub_op) {
   1753                 DW.LNE_end_sequence => {
   1754                     prog.end_sequence = true;
   1755                     if (try prog.checkLineMatch()) |info| return info;
   1756                     return error.MissingDebugInfo;
   1757                 },
   1758                 DW.LNE_set_address => {
   1759                     const addr = readIntMem(&ptr, usize, builtin.Endian.Little);
   1760                     prog.address = symbol.reloc + addr;
   1761                 },
   1762                 DW.LNE_define_file => {
   1763                     const file_name = readStringMem(&ptr);
   1764                     const dir_index = try leb.readULEB128Mem(usize, &ptr);
   1765                     const mtime = try leb.readULEB128Mem(usize, &ptr);
   1766                     const len_bytes = try leb.readULEB128Mem(usize, &ptr);
   1767                     try file_entries.append(FileEntry{
   1768                         .file_name = file_name,
   1769                         .dir_index = dir_index,
   1770                         .mtime = mtime,
   1771                         .len_bytes = len_bytes,
   1772                     });
   1773                 },
   1774                 else => {
   1775                     ptr += op_size - 1;
   1776                 },
   1777             }
   1778         } else if (opcode >= opcode_base) {
   1779             // special opcodes
   1780             const adjusted_opcode = opcode - opcode_base;
   1781             const inc_addr = minimum_instruction_length * (adjusted_opcode / line_range);
   1782             const inc_line = i32(line_base) + i32(adjusted_opcode % line_range);
   1783             prog.line += inc_line;
   1784             prog.address += inc_addr;
   1785             if (try prog.checkLineMatch()) |info| return info;
   1786             prog.basic_block = false;
   1787         } else {
   1788             switch (opcode) {
   1789                 DW.LNS_copy => {
   1790                     if (try prog.checkLineMatch()) |info| return info;
   1791                     prog.basic_block = false;
   1792                 },
   1793                 DW.LNS_advance_pc => {
   1794                     const arg = try leb.readULEB128Mem(usize, &ptr);
   1795                     prog.address += arg * minimum_instruction_length;
   1796                 },
   1797                 DW.LNS_advance_line => {
   1798                     const arg = try leb.readILEB128Mem(i64, &ptr);
   1799                     prog.line += arg;
   1800                 },
   1801                 DW.LNS_set_file => {
   1802                     const arg = try leb.readULEB128Mem(usize, &ptr);
   1803                     prog.file = arg;
   1804                 },
   1805                 DW.LNS_set_column => {
   1806                     const arg = try leb.readULEB128Mem(u64, &ptr);
   1807                     prog.column = arg;
   1808                 },
   1809                 DW.LNS_negate_stmt => {
   1810                     prog.is_stmt = !prog.is_stmt;
   1811                 },
   1812                 DW.LNS_set_basic_block => {
   1813                     prog.basic_block = true;
   1814                 },
   1815                 DW.LNS_const_add_pc => {
   1816                     const inc_addr = minimum_instruction_length * ((255 - opcode_base) / line_range);
   1817                     prog.address += inc_addr;
   1818                 },
   1819                 DW.LNS_fixed_advance_pc => {
   1820                     const arg = readIntMem(&ptr, u16, builtin.Endian.Little);
   1821                     prog.address += arg;
   1822                 },
   1823                 DW.LNS_set_prologue_end => {},
   1824                 else => {
   1825                     if (opcode - 1 >= standard_opcode_lengths.len) return error.InvalidDebugInfo;
   1826                     const len_bytes = standard_opcode_lengths[opcode - 1];
   1827                     ptr += len_bytes;
   1828                 },
   1829             }
   1830         }
   1831     }
   1832 
   1833     return error.MissingDebugInfo;
   1834 }
   1835 
   1836 fn getLineNumberInfoDwarf(di: *DwarfInfo, compile_unit: CompileUnit, target_address: usize) !LineInfo {
   1837     const compile_unit_cwd = try compile_unit.die.getAttrString(di, DW.AT_comp_dir);
   1838     const line_info_offset = try compile_unit.die.getAttrSecOffset(DW.AT_stmt_list);
   1839 
   1840     assert(line_info_offset < di.debug_line.size);
   1841 
   1842     try di.dwarf_seekable_stream.seekTo(di.debug_line.offset + line_info_offset);
   1843 
   1844     var is_64: bool = undefined;
   1845     const unit_length = try readInitialLength(@typeOf(di.dwarf_in_stream.readFn).ReturnType.ErrorSet, di.dwarf_in_stream, &is_64);
   1846     if (unit_length == 0) {
   1847         return error.MissingDebugInfo;
   1848     }
   1849     const next_offset = unit_length + (if (is_64) usize(12) else usize(4));
   1850 
   1851     const version = try di.dwarf_in_stream.readInt(u16, di.endian);
   1852     // TODO support 3 and 5
   1853     if (version != 2 and version != 4) return error.InvalidDebugInfo;
   1854 
   1855     const prologue_length = if (is_64) try di.dwarf_in_stream.readInt(u64, di.endian) else try di.dwarf_in_stream.readInt(u32, di.endian);
   1856     const prog_start_offset = (try di.dwarf_seekable_stream.getPos()) + prologue_length;
   1857 
   1858     const minimum_instruction_length = try di.dwarf_in_stream.readByte();
   1859     if (minimum_instruction_length == 0) return error.InvalidDebugInfo;
   1860 
   1861     if (version >= 4) {
   1862         // maximum_operations_per_instruction
   1863         _ = try di.dwarf_in_stream.readByte();
   1864     }
   1865 
   1866     const default_is_stmt = (try di.dwarf_in_stream.readByte()) != 0;
   1867     const line_base = try di.dwarf_in_stream.readByteSigned();
   1868 
   1869     const line_range = try di.dwarf_in_stream.readByte();
   1870     if (line_range == 0) return error.InvalidDebugInfo;
   1871 
   1872     const opcode_base = try di.dwarf_in_stream.readByte();
   1873 
   1874     const standard_opcode_lengths = try di.allocator().alloc(u8, opcode_base - 1);
   1875 
   1876     {
   1877         var i: usize = 0;
   1878         while (i < opcode_base - 1) : (i += 1) {
   1879             standard_opcode_lengths[i] = try di.dwarf_in_stream.readByte();
   1880         }
   1881     }
   1882 
   1883     var include_directories = ArrayList([]u8).init(di.allocator());
   1884     try include_directories.append(compile_unit_cwd);
   1885     while (true) {
   1886         const dir = try di.readString();
   1887         if (dir.len == 0) break;
   1888         try include_directories.append(dir);
   1889     }
   1890 
   1891     var file_entries = ArrayList(FileEntry).init(di.allocator());
   1892     var prog = LineNumberProgram.init(default_is_stmt, include_directories.toSliceConst(), &file_entries, target_address);
   1893 
   1894     while (true) {
   1895         const file_name = try di.readString();
   1896         if (file_name.len == 0) break;
   1897         const dir_index = try leb.readULEB128(usize, di.dwarf_in_stream);
   1898         const mtime = try leb.readULEB128(usize, di.dwarf_in_stream);
   1899         const len_bytes = try leb.readULEB128(usize, di.dwarf_in_stream);
   1900         try file_entries.append(FileEntry{
   1901             .file_name = file_name,
   1902             .dir_index = dir_index,
   1903             .mtime = mtime,
   1904             .len_bytes = len_bytes,
   1905         });
   1906     }
   1907 
   1908     try di.dwarf_seekable_stream.seekTo(prog_start_offset);
   1909 
   1910     while (true) {
   1911         const opcode = try di.dwarf_in_stream.readByte();
   1912 
   1913         if (opcode == DW.LNS_extended_op) {
   1914             const op_size = try leb.readULEB128(u64, di.dwarf_in_stream);
   1915             if (op_size < 1) return error.InvalidDebugInfo;
   1916             var sub_op = try di.dwarf_in_stream.readByte();
   1917             switch (sub_op) {
   1918                 DW.LNE_end_sequence => {
   1919                     prog.end_sequence = true;
   1920                     if (try prog.checkLineMatch()) |info| return info;
   1921                     return error.MissingDebugInfo;
   1922                 },
   1923                 DW.LNE_set_address => {
   1924                     const addr = try di.dwarf_in_stream.readInt(usize, di.endian);
   1925                     prog.address = addr;
   1926                 },
   1927                 DW.LNE_define_file => {
   1928                     const file_name = try di.readString();
   1929                     const dir_index = try leb.readULEB128(usize, di.dwarf_in_stream);
   1930                     const mtime = try leb.readULEB128(usize, di.dwarf_in_stream);
   1931                     const len_bytes = try leb.readULEB128(usize, di.dwarf_in_stream);
   1932                     try file_entries.append(FileEntry{
   1933                         .file_name = file_name,
   1934                         .dir_index = dir_index,
   1935                         .mtime = mtime,
   1936                         .len_bytes = len_bytes,
   1937                     });
   1938                 },
   1939                 else => {
   1940                     const fwd_amt = math.cast(isize, op_size - 1) catch return error.InvalidDebugInfo;
   1941                     try di.dwarf_seekable_stream.seekForward(fwd_amt);
   1942                 },
   1943             }
   1944         } else if (opcode >= opcode_base) {
   1945             // special opcodes
   1946             const adjusted_opcode = opcode - opcode_base;
   1947             const inc_addr = minimum_instruction_length * (adjusted_opcode / line_range);
   1948             const inc_line = i32(line_base) + i32(adjusted_opcode % line_range);
   1949             prog.line += inc_line;
   1950             prog.address += inc_addr;
   1951             if (try prog.checkLineMatch()) |info| return info;
   1952             prog.basic_block = false;
   1953         } else {
   1954             switch (opcode) {
   1955                 DW.LNS_copy => {
   1956                     if (try prog.checkLineMatch()) |info| return info;
   1957                     prog.basic_block = false;
   1958                 },
   1959                 DW.LNS_advance_pc => {
   1960                     const arg = try leb.readULEB128(usize, di.dwarf_in_stream);
   1961                     prog.address += arg * minimum_instruction_length;
   1962                 },
   1963                 DW.LNS_advance_line => {
   1964                     const arg = try leb.readILEB128(i64, di.dwarf_in_stream);
   1965                     prog.line += arg;
   1966                 },
   1967                 DW.LNS_set_file => {
   1968                     const arg = try leb.readULEB128(usize, di.dwarf_in_stream);
   1969                     prog.file = arg;
   1970                 },
   1971                 DW.LNS_set_column => {
   1972                     const arg = try leb.readULEB128(u64, di.dwarf_in_stream);
   1973                     prog.column = arg;
   1974                 },
   1975                 DW.LNS_negate_stmt => {
   1976                     prog.is_stmt = !prog.is_stmt;
   1977                 },
   1978                 DW.LNS_set_basic_block => {
   1979                     prog.basic_block = true;
   1980                 },
   1981                 DW.LNS_const_add_pc => {
   1982                     const inc_addr = minimum_instruction_length * ((255 - opcode_base) / line_range);
   1983                     prog.address += inc_addr;
   1984                 },
   1985                 DW.LNS_fixed_advance_pc => {
   1986                     const arg = try di.dwarf_in_stream.readInt(u16, di.endian);
   1987                     prog.address += arg;
   1988                 },
   1989                 DW.LNS_set_prologue_end => {},
   1990                 else => {
   1991                     if (opcode - 1 >= standard_opcode_lengths.len) return error.InvalidDebugInfo;
   1992                     const len_bytes = standard_opcode_lengths[opcode - 1];
   1993                     try di.dwarf_seekable_stream.seekForward(len_bytes);
   1994                 },
   1995             }
   1996         }
   1997     }
   1998 
   1999     return error.MissingDebugInfo;
   2000 }
   2001 
   2002 const Func = struct {
   2003     pc_range: ?PcRange,
   2004     name: ?[]u8,
   2005 };
   2006 
   2007 fn getSymbolNameDwarf(di: *DwarfInfo, address: u64) ?[]const u8 {
   2008     for (di.func_list.toSliceConst()) |*func| {
   2009         if (func.pc_range) |range| {
   2010             if (address >= range.start and address < range.end) {
   2011                 return func.name;
   2012             }
   2013         }
   2014     }
   2015 
   2016     return null;
   2017 }
   2018 
   2019 fn scanAllFunctions(di: *DwarfInfo) !void {
   2020     const debug_info_end = di.debug_info.offset + di.debug_info.size;
   2021     var this_unit_offset = di.debug_info.offset;
   2022 
   2023     while (this_unit_offset < debug_info_end) {
   2024         try di.dwarf_seekable_stream.seekTo(this_unit_offset);
   2025 
   2026         var is_64: bool = undefined;
   2027         const unit_length = try readInitialLength(@typeOf(di.dwarf_in_stream.readFn).ReturnType.ErrorSet, di.dwarf_in_stream, &is_64);
   2028         if (unit_length == 0) return;
   2029         const next_offset = unit_length + (if (is_64) usize(12) else usize(4));
   2030 
   2031         const version = try di.dwarf_in_stream.readInt(u16, di.endian);
   2032         if (version < 2 or version > 5) return error.InvalidDebugInfo;
   2033 
   2034         const debug_abbrev_offset = if (is_64) try di.dwarf_in_stream.readInt(u64, di.endian) else try di.dwarf_in_stream.readInt(u32, di.endian);
   2035 
   2036         const address_size = try di.dwarf_in_stream.readByte();
   2037         if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo;
   2038 
   2039         const compile_unit_pos = try di.dwarf_seekable_stream.getPos();
   2040         const abbrev_table = try getAbbrevTable(di, debug_abbrev_offset);
   2041 
   2042         try di.dwarf_seekable_stream.seekTo(compile_unit_pos);
   2043 
   2044         const next_unit_pos = this_unit_offset + next_offset;
   2045 
   2046         while ((try di.dwarf_seekable_stream.getPos()) < next_unit_pos) {
   2047             const die_obj = (try parseDie1(di, abbrev_table, is_64)) orelse continue;
   2048             const after_die_offset = try di.dwarf_seekable_stream.getPos();
   2049 
   2050             switch (die_obj.tag_id) {
   2051                 DW.TAG_subprogram, DW.TAG_inlined_subroutine, DW.TAG_subroutine, DW.TAG_entry_point => {
   2052                     const fn_name = x: {
   2053                         var depth: i32 = 3;
   2054                         var this_die_obj = die_obj;
   2055                         // Prenvent endless loops
   2056                         while (depth > 0) : (depth -= 1) {
   2057                             if (this_die_obj.getAttr(DW.AT_name)) |_| {
   2058                                 const name = try this_die_obj.getAttrString(di, DW.AT_name);
   2059                                 break :x name;
   2060                             } else if (this_die_obj.getAttr(DW.AT_abstract_origin)) |ref| {
   2061                                 // Follow the DIE it points to and repeat
   2062                                 const ref_offset = try this_die_obj.getAttrRef(DW.AT_abstract_origin);
   2063                                 if (ref_offset > next_offset) return error.InvalidDebugInfo;
   2064                                 try di.dwarf_seekable_stream.seekTo(this_unit_offset + ref_offset);
   2065                                 this_die_obj = (try parseDie1(di, abbrev_table, is_64)) orelse return error.InvalidDebugInfo;
   2066                             } else if (this_die_obj.getAttr(DW.AT_specification)) |ref| {
   2067                                 // Follow the DIE it points to and repeat
   2068                                 const ref_offset = try this_die_obj.getAttrRef(DW.AT_specification);
   2069                                 if (ref_offset > next_offset) return error.InvalidDebugInfo;
   2070                                 try di.dwarf_seekable_stream.seekTo(this_unit_offset + ref_offset);
   2071                                 this_die_obj = (try parseDie1(di, abbrev_table, is_64)) orelse return error.InvalidDebugInfo;
   2072                             } else {
   2073                                 break :x null;
   2074                             }
   2075                         }
   2076 
   2077                         break :x null;
   2078                     };
   2079 
   2080                     const pc_range = x: {
   2081                         if (die_obj.getAttrAddr(DW.AT_low_pc)) |low_pc| {
   2082                             if (die_obj.getAttr(DW.AT_high_pc)) |high_pc_value| {
   2083                                 const pc_end = switch (high_pc_value.*) {
   2084                                     FormValue.Address => |value| value,
   2085                                     FormValue.Const => |value| b: {
   2086                                         const offset = try value.asUnsignedLe();
   2087                                         break :b (low_pc + offset);
   2088                                     },
   2089                                     else => return error.InvalidDebugInfo,
   2090                                 };
   2091                                 break :x PcRange{
   2092                                     .start = low_pc,
   2093                                     .end = pc_end,
   2094                                 };
   2095                             } else {
   2096                                 break :x null;
   2097                             }
   2098                         } else |err| {
   2099                             if (err != error.MissingDebugInfo) return err;
   2100                             break :x null;
   2101                         }
   2102                     };
   2103 
   2104                     try di.func_list.append(Func{
   2105                         .name = fn_name,
   2106                         .pc_range = pc_range,
   2107                     });
   2108                 },
   2109                 else => {
   2110                     continue;
   2111                 },
   2112             }
   2113 
   2114             try di.dwarf_seekable_stream.seekTo(after_die_offset);
   2115         }
   2116 
   2117         this_unit_offset += next_offset;
   2118     }
   2119 }
   2120 
   2121 fn scanAllCompileUnits(di: *DwarfInfo) !void {
   2122     const debug_info_end = di.debug_info.offset + di.debug_info.size;
   2123     var this_unit_offset = di.debug_info.offset;
   2124 
   2125     while (this_unit_offset < debug_info_end) {
   2126         try di.dwarf_seekable_stream.seekTo(this_unit_offset);
   2127 
   2128         var is_64: bool = undefined;
   2129         const unit_length = try readInitialLength(@typeOf(di.dwarf_in_stream.readFn).ReturnType.ErrorSet, di.dwarf_in_stream, &is_64);
   2130         if (unit_length == 0) return;
   2131         const next_offset = unit_length + (if (is_64) usize(12) else usize(4));
   2132 
   2133         const version = try di.dwarf_in_stream.readInt(u16, di.endian);
   2134         if (version < 2 or version > 5) return error.InvalidDebugInfo;
   2135 
   2136         const debug_abbrev_offset = if (is_64) try di.dwarf_in_stream.readInt(u64, di.endian) else try di.dwarf_in_stream.readInt(u32, di.endian);
   2137 
   2138         const address_size = try di.dwarf_in_stream.readByte();
   2139         if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo;
   2140 
   2141         const compile_unit_pos = try di.dwarf_seekable_stream.getPos();
   2142         const abbrev_table = try getAbbrevTable(di, debug_abbrev_offset);
   2143 
   2144         try di.dwarf_seekable_stream.seekTo(compile_unit_pos);
   2145 
   2146         const compile_unit_die = try di.allocator().create(Die);
   2147         compile_unit_die.* = try parseDie(di, abbrev_table, is_64);
   2148 
   2149         if (compile_unit_die.tag_id != DW.TAG_compile_unit) return error.InvalidDebugInfo;
   2150 
   2151         const pc_range = x: {
   2152             if (compile_unit_die.getAttrAddr(DW.AT_low_pc)) |low_pc| {
   2153                 if (compile_unit_die.getAttr(DW.AT_high_pc)) |high_pc_value| {
   2154                     const pc_end = switch (high_pc_value.*) {
   2155                         FormValue.Address => |value| value,
   2156                         FormValue.Const => |value| b: {
   2157                             const offset = try value.asUnsignedLe();
   2158                             break :b (low_pc + offset);
   2159                         },
   2160                         else => return error.InvalidDebugInfo,
   2161                     };
   2162                     break :x PcRange{
   2163                         .start = low_pc,
   2164                         .end = pc_end,
   2165                     };
   2166                 } else {
   2167                     break :x null;
   2168                 }
   2169             } else |err| {
   2170                 if (err != error.MissingDebugInfo) return err;
   2171                 break :x null;
   2172             }
   2173         };
   2174 
   2175         try di.compile_unit_list.append(CompileUnit{
   2176             .version = version,
   2177             .is_64 = is_64,
   2178             .pc_range = pc_range,
   2179             .die = compile_unit_die,
   2180         });
   2181 
   2182         this_unit_offset += next_offset;
   2183     }
   2184 }
   2185 
   2186 fn findCompileUnit(di: *DwarfInfo, target_address: u64) !*const CompileUnit {
   2187     for (di.compile_unit_list.toSlice()) |*compile_unit| {
   2188         if (compile_unit.pc_range) |range| {
   2189             if (target_address >= range.start and target_address < range.end) return compile_unit;
   2190         }
   2191         if (compile_unit.die.getAttrSecOffset(DW.AT_ranges)) |ranges_offset| {
   2192             var base_address: usize = 0;
   2193             if (di.debug_ranges) |debug_ranges| {
   2194                 try di.dwarf_seekable_stream.seekTo(debug_ranges.offset + ranges_offset);
   2195                 while (true) {
   2196                     const begin_addr = try di.dwarf_in_stream.readIntLittle(usize);
   2197                     const end_addr = try di.dwarf_in_stream.readIntLittle(usize);
   2198                     if (begin_addr == 0 and end_addr == 0) {
   2199                         break;
   2200                     }
   2201                     if (begin_addr == maxInt(usize)) {
   2202                         base_address = begin_addr;
   2203                         continue;
   2204                     }
   2205                     if (target_address >= begin_addr and target_address < end_addr) {
   2206                         return compile_unit;
   2207                     }
   2208                 }
   2209             }
   2210         } else |err| {
   2211             if (err != error.MissingDebugInfo) return err;
   2212             continue;
   2213         }
   2214     }
   2215     return error.MissingDebugInfo;
   2216 }
   2217 
   2218 fn readIntMem(ptr: *[*]const u8, comptime T: type, endian: builtin.Endian) T {
   2219     // TODO https://github.com/ziglang/zig/issues/863
   2220     const result = mem.readIntSlice(T, ptr.*[0..@sizeOf(T)], endian);
   2221     ptr.* += @sizeOf(T);
   2222     return result;
   2223 }
   2224 
   2225 fn readByteMem(ptr: *[*]const u8) u8 {
   2226     const result = ptr.*[0];
   2227     ptr.* += 1;
   2228     return result;
   2229 }
   2230 
   2231 fn readByteSignedMem(ptr: *[*]const u8) i8 {
   2232     return @bitCast(i8, readByteMem(ptr));
   2233 }
   2234 
   2235 fn readInitialLengthMem(ptr: *[*]const u8, is_64: *bool) !u64 {
   2236     // TODO this code can be improved with https://github.com/ziglang/zig/issues/863
   2237     const first_32_bits = mem.readIntSliceLittle(u32, ptr.*[0..4]);
   2238     is_64.* = (first_32_bits == 0xffffffff);
   2239     if (is_64.*) {
   2240         ptr.* += 4;
   2241         const result = mem.readIntSliceLittle(u64, ptr.*[0..8]);
   2242         ptr.* += 8;
   2243         return result;
   2244     } else {
   2245         if (first_32_bits >= 0xfffffff0) return error.InvalidDebugInfo;
   2246         ptr.* += 4;
   2247         return u64(first_32_bits);
   2248     }
   2249 }
   2250 
   2251 fn readStringMem(ptr: *[*]const u8) []const u8 {
   2252     const result = mem.toSliceConst(u8, ptr.*);
   2253     ptr.* += result.len + 1;
   2254     return result;
   2255 }
   2256 
   2257 fn readInitialLength(comptime E: type, in_stream: *io.InStream(E), is_64: *bool) !u64 {
   2258     const first_32_bits = try in_stream.readIntLittle(u32);
   2259     is_64.* = (first_32_bits == 0xffffffff);
   2260     if (is_64.*) {
   2261         return in_stream.readIntLittle(u64);
   2262     } else {
   2263         if (first_32_bits >= 0xfffffff0) return error.InvalidDebugInfo;
   2264         return u64(first_32_bits);
   2265     }
   2266 }
   2267 
   2268 /// This should only be used in temporary test programs.
   2269 pub const global_allocator = &global_fixed_allocator.allocator;
   2270 var global_fixed_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(global_allocator_mem[0..]);
   2271 var global_allocator_mem: [100 * 1024]u8 = undefined;
   2272 
   2273 /// TODO multithreaded awareness
   2274 var debug_info_allocator: ?*mem.Allocator = null;
   2275 var debug_info_direct_allocator: std.heap.DirectAllocator = undefined;
   2276 var debug_info_arena_allocator: std.heap.ArenaAllocator = undefined;
   2277 fn getDebugInfoAllocator() *mem.Allocator {
   2278     if (debug_info_allocator) |a| return a;
   2279 
   2280     debug_info_direct_allocator = std.heap.DirectAllocator.init();
   2281     debug_info_arena_allocator = std.heap.ArenaAllocator.init(&debug_info_direct_allocator.allocator);
   2282     debug_info_allocator = &debug_info_arena_allocator.allocator;
   2283     return &debug_info_arena_allocator.allocator;
   2284 }