zig

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

blob eb204d15 (42277B) - Raw


      1 const std = @import("std.zig");
      2 const debug = std.debug;
      3 const fs = std.fs;
      4 const io = std.io;
      5 const mem = std.mem;
      6 const math = std.math;
      7 const leb = @import("leb128.zig");
      8 
      9 const ArrayList = std.ArrayList;
     10 
     11 pub const TAG = @import("dwarf/TAG.zig");
     12 pub const AT = @import("dwarf/AT.zig");
     13 pub const OP = @import("dwarf/OP.zig");
     14 
     15 pub const FORM = struct {
     16     pub const addr = 0x01;
     17     pub const block2 = 0x03;
     18     pub const block4 = 0x04;
     19     pub const data2 = 0x05;
     20     pub const data4 = 0x06;
     21     pub const data8 = 0x07;
     22     pub const string = 0x08;
     23     pub const block = 0x09;
     24     pub const block1 = 0x0a;
     25     pub const data1 = 0x0b;
     26     pub const flag = 0x0c;
     27     pub const sdata = 0x0d;
     28     pub const strp = 0x0e;
     29     pub const udata = 0x0f;
     30     pub const ref_addr = 0x10;
     31     pub const ref1 = 0x11;
     32     pub const ref2 = 0x12;
     33     pub const ref4 = 0x13;
     34     pub const ref8 = 0x14;
     35     pub const ref_udata = 0x15;
     36     pub const indirect = 0x16;
     37     pub const sec_offset = 0x17;
     38     pub const exprloc = 0x18;
     39     pub const flag_present = 0x19;
     40     pub const ref_sig8 = 0x20;
     41 
     42     // Extensions for Fission.  See http://gcc.gnu.org/wiki/DebugFission.
     43     pub const GNU_addr_index = 0x1f01;
     44     pub const GNU_str_index = 0x1f02;
     45 
     46     // Extensions for DWZ multifile.
     47     // See http://www.dwarfstd.org/ShowIssue.php?issue=120604.1&type=open .
     48     pub const GNU_ref_alt = 0x1f20;
     49     pub const GNU_strp_alt = 0x1f21;
     50 };
     51 
     52 pub const ATE = struct {
     53     pub const @"void" = 0x0;
     54     pub const address = 0x1;
     55     pub const boolean = 0x2;
     56     pub const complex_float = 0x3;
     57     pub const float = 0x4;
     58     pub const signed = 0x5;
     59     pub const signed_char = 0x6;
     60     pub const unsigned = 0x7;
     61     pub const unsigned_char = 0x8;
     62 
     63     // DWARF 3.
     64     pub const imaginary_float = 0x9;
     65     pub const packed_decimal = 0xa;
     66     pub const numeric_string = 0xb;
     67     pub const edited = 0xc;
     68     pub const signed_fixed = 0xd;
     69     pub const unsigned_fixed = 0xe;
     70     pub const decimal_float = 0xf;
     71 
     72     // DWARF 4.
     73     pub const UTF = 0x10;
     74 
     75     pub const lo_user = 0x80;
     76     pub const hi_user = 0xff;
     77 
     78     // HP extensions.
     79     pub const HP_float80 = 0x80; // Floating-point (80 bit).
     80     pub const HP_complex_float80 = 0x81; // Complex floating-point (80 bit).
     81     pub const HP_float128 = 0x82; // Floating-point (128 bit).
     82     pub const HP_complex_float128 = 0x83; // Complex fp (128 bit).
     83     pub const HP_floathpintel = 0x84; // Floating-point (82 bit IA64).
     84     pub const HP_imaginary_float80 = 0x85;
     85     pub const HP_imaginary_float128 = 0x86;
     86     pub const HP_VAX_float = 0x88; // F or G floating.
     87     pub const HP_VAX_float_d = 0x89; // D floating.
     88     pub const HP_packed_decimal = 0x8a; // Cobol.
     89     pub const HP_zoned_decimal = 0x8b; // Cobol.
     90     pub const HP_edited = 0x8c; // Cobol.
     91     pub const HP_signed_fixed = 0x8d; // Cobol.
     92     pub const HP_unsigned_fixed = 0x8e; // Cobol.
     93     pub const HP_VAX_complex_float = 0x8f; // F or G floating complex.
     94     pub const HP_VAX_complex_float_d = 0x90; // D floating complex.
     95 };
     96 
     97 pub const CFA = struct {
     98     pub const advance_loc = 0x40;
     99     pub const offset = 0x80;
    100     pub const restore = 0xc0;
    101     pub const nop = 0x00;
    102     pub const set_loc = 0x01;
    103     pub const advance_loc1 = 0x02;
    104     pub const advance_loc2 = 0x03;
    105     pub const advance_loc4 = 0x04;
    106     pub const offset_extended = 0x05;
    107     pub const restore_extended = 0x06;
    108     pub const @"undefined" = 0x07;
    109     pub const same_value = 0x08;
    110     pub const register = 0x09;
    111     pub const remember_state = 0x0a;
    112     pub const restore_state = 0x0b;
    113     pub const def_cfa = 0x0c;
    114     pub const def_cfa_register = 0x0d;
    115     pub const def_cfa_offset = 0x0e;
    116 
    117     // DWARF 3.
    118     pub const def_cfa_expression = 0x0f;
    119     pub const expression = 0x10;
    120     pub const offset_extended_sf = 0x11;
    121     pub const def_cfa_sf = 0x12;
    122     pub const def_cfa_offset_sf = 0x13;
    123     pub const val_offset = 0x14;
    124     pub const val_offset_sf = 0x15;
    125     pub const val_expression = 0x16;
    126 
    127     pub const lo_user = 0x1c;
    128     pub const hi_user = 0x3f;
    129 
    130     // SGI/MIPS specific.
    131     pub const MIPS_advance_loc8 = 0x1d;
    132 
    133     // GNU extensions.
    134     pub const GNU_window_save = 0x2d;
    135     pub const GNU_args_size = 0x2e;
    136     pub const GNU_negative_offset_extended = 0x2f;
    137 };
    138 
    139 pub const CHILDREN = struct {
    140     pub const no = 0x00;
    141     pub const yes = 0x01;
    142 };
    143 
    144 pub const LNS = struct {
    145     pub const extended_op = 0x00;
    146     pub const copy = 0x01;
    147     pub const advance_pc = 0x02;
    148     pub const advance_line = 0x03;
    149     pub const set_file = 0x04;
    150     pub const set_column = 0x05;
    151     pub const negate_stmt = 0x06;
    152     pub const set_basic_block = 0x07;
    153     pub const const_add_pc = 0x08;
    154     pub const fixed_advance_pc = 0x09;
    155     pub const set_prologue_end = 0x0a;
    156     pub const set_epilogue_begin = 0x0b;
    157     pub const set_isa = 0x0c;
    158 };
    159 
    160 pub const LNE = struct {
    161     pub const end_sequence = 0x01;
    162     pub const set_address = 0x02;
    163     pub const define_file = 0x03;
    164     pub const set_discriminator = 0x04;
    165     pub const lo_user = 0x80;
    166     pub const hi_user = 0xff;
    167 };
    168 
    169 pub const LANG = struct {
    170     pub const C89 = 0x0001;
    171     pub const C = 0x0002;
    172     pub const Ada83 = 0x0003;
    173     pub const C_plus_plus = 0x0004;
    174     pub const Cobol74 = 0x0005;
    175     pub const Cobol85 = 0x0006;
    176     pub const Fortran77 = 0x0007;
    177     pub const Fortran90 = 0x0008;
    178     pub const Pascal83 = 0x0009;
    179     pub const Modula2 = 0x000a;
    180     pub const Java = 0x000b;
    181     pub const C99 = 0x000c;
    182     pub const Ada95 = 0x000d;
    183     pub const Fortran95 = 0x000e;
    184     pub const PLI = 0x000f;
    185     pub const ObjC = 0x0010;
    186     pub const ObjC_plus_plus = 0x0011;
    187     pub const UPC = 0x0012;
    188     pub const D = 0x0013;
    189     pub const Python = 0x0014;
    190     pub const Go = 0x0016;
    191     pub const C_plus_plus_11 = 0x001a;
    192     pub const Rust = 0x001c;
    193     pub const C11 = 0x001d;
    194     pub const C_plus_plus_14 = 0x0021;
    195     pub const Fortran03 = 0x0022;
    196     pub const Fortran08 = 0x0023;
    197     pub const lo_user = 0x8000;
    198     pub const hi_user = 0xffff;
    199     pub const Mips_Assembler = 0x8001;
    200     pub const Upc = 0x8765;
    201     pub const HP_Bliss = 0x8003;
    202     pub const HP_Basic91 = 0x8004;
    203     pub const HP_Pascal91 = 0x8005;
    204     pub const HP_IMacro = 0x8006;
    205     pub const HP_Assembler = 0x8007;
    206 };
    207 
    208 pub const UT = struct {
    209     pub const compile = 0x01;
    210     pub const @"type" = 0x02;
    211     pub const partial = 0x03;
    212     pub const skeleton = 0x04;
    213     pub const split_compile = 0x05;
    214     pub const split_type = 0x06;
    215     pub const lo_user = 0x80;
    216     pub const hi_user = 0xff;
    217 };
    218 
    219 pub const LNCT = struct {
    220     pub const path = 0x1;
    221     pub const directory_index = 0x2;
    222     pub const timestamp = 0x3;
    223     pub const size = 0x4;
    224     pub const MD5 = 0x5;
    225     pub const lo_user = 0x2000;
    226     pub const hi_user = 0x3fff;
    227 };
    228 
    229 const PcRange = struct {
    230     start: u64,
    231     end: u64,
    232 };
    233 
    234 const Func = struct {
    235     pc_range: ?PcRange,
    236     name: ?[]const u8,
    237 };
    238 
    239 const CompileUnit = struct {
    240     version: u16,
    241     is_64: bool,
    242     die: *Die,
    243     pc_range: ?PcRange,
    244 };
    245 
    246 const AbbrevTable = ArrayList(AbbrevTableEntry);
    247 
    248 const AbbrevTableHeader = struct {
    249     // offset from .debug_abbrev
    250     offset: u64,
    251     table: AbbrevTable,
    252 };
    253 
    254 const AbbrevTableEntry = struct {
    255     has_children: bool,
    256     abbrev_code: u64,
    257     tag_id: u64,
    258     attrs: ArrayList(AbbrevAttr),
    259 };
    260 
    261 const AbbrevAttr = struct {
    262     attr_id: u64,
    263     form_id: u64,
    264 };
    265 
    266 const FormValue = union(enum) {
    267     Address: u64,
    268     Block: []u8,
    269     Const: Constant,
    270     ExprLoc: []u8,
    271     Flag: bool,
    272     SecOffset: u64,
    273     Ref: u64,
    274     RefAddr: u64,
    275     String: []const u8,
    276     StrPtr: u64,
    277 };
    278 
    279 const Constant = struct {
    280     payload: u64,
    281     signed: bool,
    282 
    283     fn asUnsignedLe(self: *const Constant) !u64 {
    284         if (self.signed) return error.InvalidDebugInfo;
    285         return self.payload;
    286     }
    287 };
    288 
    289 const Die = struct {
    290     tag_id: u64,
    291     has_children: bool,
    292     attrs: ArrayList(Attr),
    293 
    294     const Attr = struct {
    295         id: u64,
    296         value: FormValue,
    297     };
    298 
    299     fn getAttr(self: *const Die, id: u64) ?*const FormValue {
    300         for (self.attrs.items) |*attr| {
    301             if (attr.id == id) return &attr.value;
    302         }
    303         return null;
    304     }
    305 
    306     fn getAttrAddr(self: *const Die, id: u64) !u64 {
    307         const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
    308         return switch (form_value.*) {
    309             FormValue.Address => |value| value,
    310             else => error.InvalidDebugInfo,
    311         };
    312     }
    313 
    314     fn getAttrSecOffset(self: *const Die, id: u64) !u64 {
    315         const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
    316         return switch (form_value.*) {
    317             FormValue.Const => |value| value.asUnsignedLe(),
    318             FormValue.SecOffset => |value| value,
    319             else => error.InvalidDebugInfo,
    320         };
    321     }
    322 
    323     fn getAttrUnsignedLe(self: *const Die, id: u64) !u64 {
    324         const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
    325         return switch (form_value.*) {
    326             FormValue.Const => |value| value.asUnsignedLe(),
    327             else => error.InvalidDebugInfo,
    328         };
    329     }
    330 
    331     fn getAttrRef(self: *const Die, id: u64) !u64 {
    332         const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
    333         return switch (form_value.*) {
    334             FormValue.Ref => |value| value,
    335             else => error.InvalidDebugInfo,
    336         };
    337     }
    338 
    339     pub fn getAttrString(self: *const Die, di: *DwarfInfo, id: u64) ![]const u8 {
    340         const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
    341         return switch (form_value.*) {
    342             FormValue.String => |value| value,
    343             FormValue.StrPtr => |offset| di.getString(offset),
    344             else => error.InvalidDebugInfo,
    345         };
    346     }
    347 };
    348 
    349 const FileEntry = struct {
    350     file_name: []const u8,
    351     dir_index: usize,
    352     mtime: usize,
    353     len_bytes: usize,
    354 };
    355 
    356 const LineNumberProgram = struct {
    357     address: u64,
    358     file: usize,
    359     line: i64,
    360     column: u64,
    361     is_stmt: bool,
    362     basic_block: bool,
    363     end_sequence: bool,
    364 
    365     default_is_stmt: bool,
    366     target_address: u64,
    367     include_dirs: []const []const u8,
    368     file_entries: *ArrayList(FileEntry),
    369 
    370     prev_valid: bool,
    371     prev_address: u64,
    372     prev_file: usize,
    373     prev_line: i64,
    374     prev_column: u64,
    375     prev_is_stmt: bool,
    376     prev_basic_block: bool,
    377     prev_end_sequence: bool,
    378 
    379     // Reset the state machine following the DWARF specification
    380     pub fn reset(self: *LineNumberProgram) void {
    381         self.address = 0;
    382         self.file = 1;
    383         self.line = 1;
    384         self.column = 0;
    385         self.is_stmt = self.default_is_stmt;
    386         self.basic_block = false;
    387         self.end_sequence = false;
    388         // Invalidate all the remaining fields
    389         self.prev_valid = false;
    390         self.prev_address = 0;
    391         self.prev_file = undefined;
    392         self.prev_line = undefined;
    393         self.prev_column = undefined;
    394         self.prev_is_stmt = undefined;
    395         self.prev_basic_block = undefined;
    396         self.prev_end_sequence = undefined;
    397     }
    398 
    399     pub fn init(is_stmt: bool, include_dirs: []const []const u8, file_entries: *ArrayList(FileEntry), target_address: u64) LineNumberProgram {
    400         return LineNumberProgram{
    401             .address = 0,
    402             .file = 1,
    403             .line = 1,
    404             .column = 0,
    405             .is_stmt = is_stmt,
    406             .basic_block = false,
    407             .end_sequence = false,
    408             .include_dirs = include_dirs,
    409             .file_entries = file_entries,
    410             .default_is_stmt = is_stmt,
    411             .target_address = target_address,
    412             .prev_valid = false,
    413             .prev_address = 0,
    414             .prev_file = undefined,
    415             .prev_line = undefined,
    416             .prev_column = undefined,
    417             .prev_is_stmt = undefined,
    418             .prev_basic_block = undefined,
    419             .prev_end_sequence = undefined,
    420         };
    421     }
    422 
    423     pub fn checkLineMatch(self: *LineNumberProgram) !?debug.LineInfo {
    424         if (self.prev_valid and self.target_address >= self.prev_address and self.target_address < self.address) {
    425             const file_entry = if (self.prev_file == 0) {
    426                 return error.MissingDebugInfo;
    427             } else if (self.prev_file - 1 >= self.file_entries.items.len) {
    428                 return error.InvalidDebugInfo;
    429             } else &self.file_entries.items[self.prev_file - 1];
    430 
    431             const dir_name = if (file_entry.dir_index >= self.include_dirs.len) {
    432                 return error.InvalidDebugInfo;
    433             } else self.include_dirs[file_entry.dir_index];
    434             const file_name = try fs.path.join(self.file_entries.allocator, &[_][]const u8{ dir_name, file_entry.file_name });
    435             errdefer self.file_entries.allocator.free(file_name);
    436             return debug.LineInfo{
    437                 .line = if (self.prev_line >= 0) @intCast(u64, self.prev_line) else 0,
    438                 .column = self.prev_column,
    439                 .file_name = file_name,
    440                 .allocator = self.file_entries.allocator,
    441             };
    442         }
    443 
    444         self.prev_valid = true;
    445         self.prev_address = self.address;
    446         self.prev_file = self.file;
    447         self.prev_line = self.line;
    448         self.prev_column = self.column;
    449         self.prev_is_stmt = self.is_stmt;
    450         self.prev_basic_block = self.basic_block;
    451         self.prev_end_sequence = self.end_sequence;
    452         return null;
    453     }
    454 };
    455 
    456 fn readUnitLength(in_stream: anytype, endian: std.builtin.Endian, is_64: *bool) !u64 {
    457     const first_32_bits = try in_stream.readInt(u32, endian);
    458     is_64.* = (first_32_bits == 0xffffffff);
    459     if (is_64.*) {
    460         return in_stream.readInt(u64, endian);
    461     } else {
    462         if (first_32_bits >= 0xfffffff0) return error.InvalidDebugInfo;
    463         // TODO this cast should not be needed
    464         return @as(u64, first_32_bits);
    465     }
    466 }
    467 
    468 // TODO the nosuspends here are workarounds
    469 fn readAllocBytes(allocator: mem.Allocator, in_stream: anytype, size: usize) ![]u8 {
    470     const buf = try allocator.alloc(u8, size);
    471     errdefer allocator.free(buf);
    472     if ((try nosuspend in_stream.read(buf)) < size) return error.EndOfFile;
    473     return buf;
    474 }
    475 
    476 // TODO the nosuspends here are workarounds
    477 fn readAddress(in_stream: anytype, endian: std.builtin.Endian, is_64: bool) !u64 {
    478     return nosuspend if (is_64)
    479         try in_stream.readInt(u64, endian)
    480     else
    481         @as(u64, try in_stream.readInt(u32, endian));
    482 }
    483 
    484 fn parseFormValueBlockLen(allocator: mem.Allocator, in_stream: anytype, size: usize) !FormValue {
    485     const buf = try readAllocBytes(allocator, in_stream, size);
    486     return FormValue{ .Block = buf };
    487 }
    488 
    489 // TODO the nosuspends here are workarounds
    490 fn parseFormValueBlock(allocator: mem.Allocator, in_stream: anytype, endian: std.builtin.Endian, size: usize) !FormValue {
    491     const block_len = try nosuspend in_stream.readVarInt(usize, endian, size);
    492     return parseFormValueBlockLen(allocator, in_stream, block_len);
    493 }
    494 
    495 fn parseFormValueConstant(allocator: mem.Allocator, in_stream: anytype, signed: bool, endian: std.builtin.Endian, comptime size: i32) !FormValue {
    496     _ = allocator;
    497     // TODO: Please forgive me, I've worked around zig not properly spilling some intermediate values here.
    498     // `nosuspend` should be removed from all the function calls once it is fixed.
    499     return FormValue{
    500         .Const = Constant{
    501             .signed = signed,
    502             .payload = switch (size) {
    503                 1 => try nosuspend in_stream.readInt(u8, endian),
    504                 2 => try nosuspend in_stream.readInt(u16, endian),
    505                 4 => try nosuspend in_stream.readInt(u32, endian),
    506                 8 => try nosuspend in_stream.readInt(u64, endian),
    507                 -1 => blk: {
    508                     if (signed) {
    509                         const x = try nosuspend leb.readILEB128(i64, in_stream);
    510                         break :blk @bitCast(u64, x);
    511                     } else {
    512                         const x = try nosuspend leb.readULEB128(u64, in_stream);
    513                         break :blk x;
    514                     }
    515                 },
    516                 else => @compileError("Invalid size"),
    517             },
    518         },
    519     };
    520 }
    521 
    522 // TODO the nosuspends here are workarounds
    523 fn parseFormValueRef(allocator: mem.Allocator, in_stream: anytype, endian: std.builtin.Endian, size: i32) !FormValue {
    524     _ = allocator;
    525     return FormValue{
    526         .Ref = switch (size) {
    527             1 => try nosuspend in_stream.readInt(u8, endian),
    528             2 => try nosuspend in_stream.readInt(u16, endian),
    529             4 => try nosuspend in_stream.readInt(u32, endian),
    530             8 => try nosuspend in_stream.readInt(u64, endian),
    531             -1 => try nosuspend leb.readULEB128(u64, in_stream),
    532             else => unreachable,
    533         },
    534     };
    535 }
    536 
    537 // TODO the nosuspends here are workarounds
    538 fn parseFormValue(allocator: mem.Allocator, in_stream: anytype, form_id: u64, endian: std.builtin.Endian, is_64: bool) anyerror!FormValue {
    539     return switch (form_id) {
    540         FORM.addr => FormValue{ .Address = try readAddress(in_stream, endian, @sizeOf(usize) == 8) },
    541         FORM.block1 => parseFormValueBlock(allocator, in_stream, endian, 1),
    542         FORM.block2 => parseFormValueBlock(allocator, in_stream, endian, 2),
    543         FORM.block4 => parseFormValueBlock(allocator, in_stream, endian, 4),
    544         FORM.block => {
    545             const block_len = try nosuspend leb.readULEB128(usize, in_stream);
    546             return parseFormValueBlockLen(allocator, in_stream, block_len);
    547         },
    548         FORM.data1 => parseFormValueConstant(allocator, in_stream, false, endian, 1),
    549         FORM.data2 => parseFormValueConstant(allocator, in_stream, false, endian, 2),
    550         FORM.data4 => parseFormValueConstant(allocator, in_stream, false, endian, 4),
    551         FORM.data8 => parseFormValueConstant(allocator, in_stream, false, endian, 8),
    552         FORM.udata, FORM.sdata => {
    553             const signed = form_id == FORM.sdata;
    554             return parseFormValueConstant(allocator, in_stream, signed, endian, -1);
    555         },
    556         FORM.exprloc => {
    557             const size = try nosuspend leb.readULEB128(usize, in_stream);
    558             const buf = try readAllocBytes(allocator, in_stream, size);
    559             return FormValue{ .ExprLoc = buf };
    560         },
    561         FORM.flag => FormValue{ .Flag = (try nosuspend in_stream.readByte()) != 0 },
    562         FORM.flag_present => FormValue{ .Flag = true },
    563         FORM.sec_offset => FormValue{ .SecOffset = try readAddress(in_stream, endian, is_64) },
    564 
    565         FORM.ref1 => parseFormValueRef(allocator, in_stream, endian, 1),
    566         FORM.ref2 => parseFormValueRef(allocator, in_stream, endian, 2),
    567         FORM.ref4 => parseFormValueRef(allocator, in_stream, endian, 4),
    568         FORM.ref8 => parseFormValueRef(allocator, in_stream, endian, 8),
    569         FORM.ref_udata => parseFormValueRef(allocator, in_stream, endian, -1),
    570 
    571         FORM.ref_addr => FormValue{ .RefAddr = try readAddress(in_stream, endian, is_64) },
    572         FORM.ref_sig8 => FormValue{ .Ref = try nosuspend in_stream.readInt(u64, endian) },
    573 
    574         FORM.string => FormValue{ .String = try in_stream.readUntilDelimiterAlloc(allocator, 0, math.maxInt(usize)) },
    575         FORM.strp => FormValue{ .StrPtr = try readAddress(in_stream, endian, is_64) },
    576         FORM.indirect => {
    577             const child_form_id = try nosuspend leb.readULEB128(u64, in_stream);
    578             const F = @TypeOf(async parseFormValue(allocator, in_stream, child_form_id, endian, is_64));
    579             var frame = try allocator.create(F);
    580             defer allocator.destroy(frame);
    581             return await @asyncCall(frame, {}, parseFormValue, .{ allocator, in_stream, child_form_id, endian, is_64 });
    582         },
    583         else => error.InvalidDebugInfo,
    584     };
    585 }
    586 
    587 fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*const AbbrevTableEntry {
    588     for (abbrev_table.items) |*table_entry| {
    589         if (table_entry.abbrev_code == abbrev_code) return table_entry;
    590     }
    591     return null;
    592 }
    593 
    594 pub const DwarfInfo = struct {
    595     endian: std.builtin.Endian,
    596     // No memory is owned by the DwarfInfo
    597     debug_info: []const u8,
    598     debug_abbrev: []const u8,
    599     debug_str: []const u8,
    600     debug_line: []const u8,
    601     debug_ranges: ?[]const u8,
    602     // Filled later by the initializer
    603     abbrev_table_list: ArrayList(AbbrevTableHeader) = undefined,
    604     compile_unit_list: ArrayList(CompileUnit) = undefined,
    605     func_list: ArrayList(Func) = undefined,
    606 
    607     pub fn allocator(self: DwarfInfo) mem.Allocator {
    608         return self.abbrev_table_list.allocator;
    609     }
    610 
    611     pub fn getSymbolName(di: *DwarfInfo, address: u64) ?[]const u8 {
    612         for (di.func_list.items) |*func| {
    613             if (func.pc_range) |range| {
    614                 if (address >= range.start and address < range.end) {
    615                     return func.name;
    616                 }
    617             }
    618         }
    619 
    620         return null;
    621     }
    622 
    623     fn scanAllFunctions(di: *DwarfInfo) !void {
    624         var stream = io.fixedBufferStream(di.debug_info);
    625         const in = &stream.reader();
    626         const seekable = &stream.seekableStream();
    627         var this_unit_offset: u64 = 0;
    628 
    629         while (this_unit_offset < try seekable.getEndPos()) {
    630             try seekable.seekTo(this_unit_offset);
    631 
    632             var is_64: bool = undefined;
    633             const unit_length = try readUnitLength(in, di.endian, &is_64);
    634             if (unit_length == 0) return;
    635             const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4));
    636 
    637             const version = try in.readInt(u16, di.endian);
    638             if (version < 2 or version > 5) return error.InvalidDebugInfo;
    639 
    640             const debug_abbrev_offset = if (is_64) try in.readInt(u64, di.endian) else try in.readInt(u32, di.endian);
    641 
    642             const address_size = try in.readByte();
    643             if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo;
    644 
    645             const compile_unit_pos = try seekable.getPos();
    646             const abbrev_table = try di.getAbbrevTable(debug_abbrev_offset);
    647 
    648             try seekable.seekTo(compile_unit_pos);
    649 
    650             const next_unit_pos = this_unit_offset + next_offset;
    651 
    652             while ((try seekable.getPos()) < next_unit_pos) {
    653                 const die_obj = (try di.parseDie(in, abbrev_table, is_64)) orelse continue;
    654                 defer die_obj.attrs.deinit();
    655 
    656                 const after_die_offset = try seekable.getPos();
    657 
    658                 switch (die_obj.tag_id) {
    659                     TAG.subprogram, TAG.inlined_subroutine, TAG.subroutine, TAG.entry_point => {
    660                         const fn_name = x: {
    661                             var depth: i32 = 3;
    662                             var this_die_obj = die_obj;
    663                             // Prenvent endless loops
    664                             while (depth > 0) : (depth -= 1) {
    665                                 if (this_die_obj.getAttr(AT.name)) |_| {
    666                                     const name = try this_die_obj.getAttrString(di, AT.name);
    667                                     break :x name;
    668                                 } else if (this_die_obj.getAttr(AT.abstract_origin)) |_| {
    669                                     // Follow the DIE it points to and repeat
    670                                     const ref_offset = try this_die_obj.getAttrRef(AT.abstract_origin);
    671                                     if (ref_offset > next_offset) return error.InvalidDebugInfo;
    672                                     try seekable.seekTo(this_unit_offset + ref_offset);
    673                                     this_die_obj = (try di.parseDie(in, abbrev_table, is_64)) orelse return error.InvalidDebugInfo;
    674                                 } else if (this_die_obj.getAttr(AT.specification)) |_| {
    675                                     // Follow the DIE it points to and repeat
    676                                     const ref_offset = try this_die_obj.getAttrRef(AT.specification);
    677                                     if (ref_offset > next_offset) return error.InvalidDebugInfo;
    678                                     try seekable.seekTo(this_unit_offset + ref_offset);
    679                                     this_die_obj = (try di.parseDie(in, abbrev_table, is_64)) orelse return error.InvalidDebugInfo;
    680                                 } else {
    681                                     break :x null;
    682                                 }
    683                             }
    684 
    685                             break :x null;
    686                         };
    687 
    688                         const pc_range = x: {
    689                             if (die_obj.getAttrAddr(AT.low_pc)) |low_pc| {
    690                                 if (die_obj.getAttr(AT.high_pc)) |high_pc_value| {
    691                                     const pc_end = switch (high_pc_value.*) {
    692                                         FormValue.Address => |value| value,
    693                                         FormValue.Const => |value| b: {
    694                                             const offset = try value.asUnsignedLe();
    695                                             break :b (low_pc + offset);
    696                                         },
    697                                         else => return error.InvalidDebugInfo,
    698                                     };
    699                                     break :x PcRange{
    700                                         .start = low_pc,
    701                                         .end = pc_end,
    702                                     };
    703                                 } else {
    704                                     break :x null;
    705                                 }
    706                             } else |err| {
    707                                 if (err != error.MissingDebugInfo) return err;
    708                                 break :x null;
    709                             }
    710                         };
    711 
    712                         try di.func_list.append(Func{
    713                             .name = fn_name,
    714                             .pc_range = pc_range,
    715                         });
    716                     },
    717                     else => {},
    718                 }
    719 
    720                 try seekable.seekTo(after_die_offset);
    721             }
    722 
    723             this_unit_offset += next_offset;
    724         }
    725     }
    726 
    727     fn scanAllCompileUnits(di: *DwarfInfo) !void {
    728         var stream = io.fixedBufferStream(di.debug_info);
    729         const in = &stream.reader();
    730         const seekable = &stream.seekableStream();
    731         var this_unit_offset: u64 = 0;
    732 
    733         while (this_unit_offset < try seekable.getEndPos()) {
    734             try seekable.seekTo(this_unit_offset);
    735 
    736             var is_64: bool = undefined;
    737             const unit_length = try readUnitLength(in, di.endian, &is_64);
    738             if (unit_length == 0) return;
    739             const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4));
    740 
    741             const version = try in.readInt(u16, di.endian);
    742             if (version < 2 or version > 5) return error.InvalidDebugInfo;
    743 
    744             const debug_abbrev_offset = if (is_64) try in.readInt(u64, di.endian) else try in.readInt(u32, di.endian);
    745 
    746             const address_size = try in.readByte();
    747             if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo;
    748 
    749             const compile_unit_pos = try seekable.getPos();
    750             const abbrev_table = try di.getAbbrevTable(debug_abbrev_offset);
    751 
    752             try seekable.seekTo(compile_unit_pos);
    753 
    754             const compile_unit_die = try di.allocator().create(Die);
    755             compile_unit_die.* = (try di.parseDie(in, abbrev_table, is_64)) orelse return error.InvalidDebugInfo;
    756 
    757             if (compile_unit_die.tag_id != TAG.compile_unit) return error.InvalidDebugInfo;
    758 
    759             const pc_range = x: {
    760                 if (compile_unit_die.getAttrAddr(AT.low_pc)) |low_pc| {
    761                     if (compile_unit_die.getAttr(AT.high_pc)) |high_pc_value| {
    762                         const pc_end = switch (high_pc_value.*) {
    763                             FormValue.Address => |value| value,
    764                             FormValue.Const => |value| b: {
    765                                 const offset = try value.asUnsignedLe();
    766                                 break :b (low_pc + offset);
    767                             },
    768                             else => return error.InvalidDebugInfo,
    769                         };
    770                         break :x PcRange{
    771                             .start = low_pc,
    772                             .end = pc_end,
    773                         };
    774                     } else {
    775                         break :x null;
    776                     }
    777                 } else |err| {
    778                     if (err != error.MissingDebugInfo) return err;
    779                     break :x null;
    780                 }
    781             };
    782 
    783             try di.compile_unit_list.append(CompileUnit{
    784                 .version = version,
    785                 .is_64 = is_64,
    786                 .pc_range = pc_range,
    787                 .die = compile_unit_die,
    788             });
    789 
    790             this_unit_offset += next_offset;
    791         }
    792     }
    793 
    794     pub fn findCompileUnit(di: *DwarfInfo, target_address: u64) !*const CompileUnit {
    795         for (di.compile_unit_list.items) |*compile_unit| {
    796             if (compile_unit.pc_range) |range| {
    797                 if (target_address >= range.start and target_address < range.end) return compile_unit;
    798             }
    799             if (di.debug_ranges) |debug_ranges| {
    800                 if (compile_unit.die.getAttrSecOffset(AT.ranges)) |ranges_offset| {
    801                     var stream = io.fixedBufferStream(debug_ranges);
    802                     const in = &stream.reader();
    803                     const seekable = &stream.seekableStream();
    804 
    805                     // All the addresses in the list are relative to the value
    806                     // specified by DW_AT.low_pc or to some other value encoded
    807                     // in the list itself.
    808                     // If no starting value is specified use zero.
    809                     var base_address = compile_unit.die.getAttrAddr(AT.low_pc) catch |err| switch (err) {
    810                         error.MissingDebugInfo => 0,
    811                         else => return err,
    812                     };
    813 
    814                     try seekable.seekTo(ranges_offset);
    815 
    816                     while (true) {
    817                         const begin_addr = try in.readInt(usize, di.endian);
    818                         const end_addr = try in.readInt(usize, di.endian);
    819                         if (begin_addr == 0 and end_addr == 0) {
    820                             break;
    821                         }
    822                         // This entry selects a new value for the base address
    823                         if (begin_addr == math.maxInt(usize)) {
    824                             base_address = end_addr;
    825                             continue;
    826                         }
    827                         if (target_address >= base_address + begin_addr and target_address < base_address + end_addr) {
    828                             return compile_unit;
    829                         }
    830                     }
    831                 } else |err| {
    832                     if (err != error.MissingDebugInfo) return err;
    833                     continue;
    834                 }
    835             }
    836         }
    837         return error.MissingDebugInfo;
    838     }
    839 
    840     /// Gets an already existing AbbrevTable given the abbrev_offset, or if not found,
    841     /// seeks in the stream and parses it.
    842     fn getAbbrevTable(di: *DwarfInfo, abbrev_offset: u64) !*const AbbrevTable {
    843         for (di.abbrev_table_list.items) |*header| {
    844             if (header.offset == abbrev_offset) {
    845                 return &header.table;
    846             }
    847         }
    848         try di.abbrev_table_list.append(AbbrevTableHeader{
    849             .offset = abbrev_offset,
    850             .table = try di.parseAbbrevTable(abbrev_offset),
    851         });
    852         return &di.abbrev_table_list.items[di.abbrev_table_list.items.len - 1].table;
    853     }
    854 
    855     fn parseAbbrevTable(di: *DwarfInfo, offset: u64) !AbbrevTable {
    856         var stream = io.fixedBufferStream(di.debug_abbrev);
    857         const in = &stream.reader();
    858         const seekable = &stream.seekableStream();
    859 
    860         try seekable.seekTo(offset);
    861         var result = AbbrevTable.init(di.allocator());
    862         errdefer result.deinit();
    863         while (true) {
    864             const abbrev_code = try leb.readULEB128(u64, in);
    865             if (abbrev_code == 0) return result;
    866             try result.append(AbbrevTableEntry{
    867                 .abbrev_code = abbrev_code,
    868                 .tag_id = try leb.readULEB128(u64, in),
    869                 .has_children = (try in.readByte()) == CHILDREN.yes,
    870                 .attrs = ArrayList(AbbrevAttr).init(di.allocator()),
    871             });
    872             const attrs = &result.items[result.items.len - 1].attrs;
    873 
    874             while (true) {
    875                 const attr_id = try leb.readULEB128(u64, in);
    876                 const form_id = try leb.readULEB128(u64, in);
    877                 if (attr_id == 0 and form_id == 0) break;
    878                 try attrs.append(AbbrevAttr{
    879                     .attr_id = attr_id,
    880                     .form_id = form_id,
    881                 });
    882             }
    883         }
    884     }
    885 
    886     fn parseDie(di: *DwarfInfo, in_stream: anytype, abbrev_table: *const AbbrevTable, is_64: bool) !?Die {
    887         const abbrev_code = try leb.readULEB128(u64, in_stream);
    888         if (abbrev_code == 0) return null;
    889         const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return error.InvalidDebugInfo;
    890 
    891         var result = Die{
    892             .tag_id = table_entry.tag_id,
    893             .has_children = table_entry.has_children,
    894             .attrs = ArrayList(Die.Attr).init(di.allocator()),
    895         };
    896         try result.attrs.resize(table_entry.attrs.items.len);
    897         for (table_entry.attrs.items) |attr, i| {
    898             result.attrs.items[i] = Die.Attr{
    899                 .id = attr.attr_id,
    900                 .value = try parseFormValue(di.allocator(), in_stream, attr.form_id, di.endian, is_64),
    901             };
    902         }
    903         return result;
    904     }
    905 
    906     pub fn getLineNumberInfo(di: *DwarfInfo, compile_unit: CompileUnit, target_address: u64) !debug.LineInfo {
    907         var stream = io.fixedBufferStream(di.debug_line);
    908         const in = &stream.reader();
    909         const seekable = &stream.seekableStream();
    910 
    911         const compile_unit_cwd = try compile_unit.die.getAttrString(di, AT.comp_dir);
    912         const line_info_offset = try compile_unit.die.getAttrSecOffset(AT.stmt_list);
    913 
    914         try seekable.seekTo(line_info_offset);
    915 
    916         var is_64: bool = undefined;
    917         const unit_length = try readUnitLength(in, di.endian, &is_64);
    918         if (unit_length == 0) {
    919             return error.MissingDebugInfo;
    920         }
    921         const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4));
    922 
    923         const version = try in.readInt(u16, di.endian);
    924         if (version < 2 or version > 4) return error.InvalidDebugInfo;
    925 
    926         const prologue_length = if (is_64) try in.readInt(u64, di.endian) else try in.readInt(u32, di.endian);
    927         const prog_start_offset = (try seekable.getPos()) + prologue_length;
    928 
    929         const minimum_instruction_length = try in.readByte();
    930         if (minimum_instruction_length == 0) return error.InvalidDebugInfo;
    931 
    932         if (version >= 4) {
    933             // maximum_operations_per_instruction
    934             _ = try in.readByte();
    935         }
    936 
    937         const default_is_stmt = (try in.readByte()) != 0;
    938         const line_base = try in.readByteSigned();
    939 
    940         const line_range = try in.readByte();
    941         if (line_range == 0) return error.InvalidDebugInfo;
    942 
    943         const opcode_base = try in.readByte();
    944 
    945         const standard_opcode_lengths = try di.allocator().alloc(u8, opcode_base - 1);
    946         defer di.allocator().free(standard_opcode_lengths);
    947 
    948         {
    949             var i: usize = 0;
    950             while (i < opcode_base - 1) : (i += 1) {
    951                 standard_opcode_lengths[i] = try in.readByte();
    952             }
    953         }
    954 
    955         var include_directories = ArrayList([]const u8).init(di.allocator());
    956         try include_directories.append(compile_unit_cwd);
    957         while (true) {
    958             const dir = try in.readUntilDelimiterAlloc(di.allocator(), 0, math.maxInt(usize));
    959             if (dir.len == 0) break;
    960             try include_directories.append(dir);
    961         }
    962 
    963         var file_entries = ArrayList(FileEntry).init(di.allocator());
    964         var prog = LineNumberProgram.init(default_is_stmt, include_directories.items, &file_entries, target_address);
    965 
    966         while (true) {
    967             const file_name = try in.readUntilDelimiterAlloc(di.allocator(), 0, math.maxInt(usize));
    968             if (file_name.len == 0) break;
    969             const dir_index = try leb.readULEB128(usize, in);
    970             const mtime = try leb.readULEB128(usize, in);
    971             const len_bytes = try leb.readULEB128(usize, in);
    972             try file_entries.append(FileEntry{
    973                 .file_name = file_name,
    974                 .dir_index = dir_index,
    975                 .mtime = mtime,
    976                 .len_bytes = len_bytes,
    977             });
    978         }
    979 
    980         try seekable.seekTo(prog_start_offset);
    981 
    982         const next_unit_pos = line_info_offset + next_offset;
    983 
    984         while ((try seekable.getPos()) < next_unit_pos) {
    985             const opcode = try in.readByte();
    986 
    987             if (opcode == LNS.extended_op) {
    988                 const op_size = try leb.readULEB128(u64, in);
    989                 if (op_size < 1) return error.InvalidDebugInfo;
    990                 var sub_op = try in.readByte();
    991                 switch (sub_op) {
    992                     LNE.end_sequence => {
    993                         prog.end_sequence = true;
    994                         if (try prog.checkLineMatch()) |info| return info;
    995                         prog.reset();
    996                     },
    997                     LNE.set_address => {
    998                         const addr = try in.readInt(usize, di.endian);
    999                         prog.address = addr;
   1000                     },
   1001                     LNE.define_file => {
   1002                         const file_name = try in.readUntilDelimiterAlloc(di.allocator(), 0, math.maxInt(usize));
   1003                         const dir_index = try leb.readULEB128(usize, in);
   1004                         const mtime = try leb.readULEB128(usize, in);
   1005                         const len_bytes = try leb.readULEB128(usize, in);
   1006                         try file_entries.append(FileEntry{
   1007                             .file_name = file_name,
   1008                             .dir_index = dir_index,
   1009                             .mtime = mtime,
   1010                             .len_bytes = len_bytes,
   1011                         });
   1012                     },
   1013                     else => {
   1014                         const fwd_amt = math.cast(isize, op_size - 1) catch return error.InvalidDebugInfo;
   1015                         try seekable.seekBy(fwd_amt);
   1016                     },
   1017                 }
   1018             } else if (opcode >= opcode_base) {
   1019                 // special opcodes
   1020                 const adjusted_opcode = opcode - opcode_base;
   1021                 const inc_addr = minimum_instruction_length * (adjusted_opcode / line_range);
   1022                 const inc_line = @as(i32, line_base) + @as(i32, adjusted_opcode % line_range);
   1023                 prog.line += inc_line;
   1024                 prog.address += inc_addr;
   1025                 if (try prog.checkLineMatch()) |info| return info;
   1026                 prog.basic_block = false;
   1027             } else {
   1028                 switch (opcode) {
   1029                     LNS.copy => {
   1030                         if (try prog.checkLineMatch()) |info| return info;
   1031                         prog.basic_block = false;
   1032                     },
   1033                     LNS.advance_pc => {
   1034                         const arg = try leb.readULEB128(usize, in);
   1035                         prog.address += arg * minimum_instruction_length;
   1036                     },
   1037                     LNS.advance_line => {
   1038                         const arg = try leb.readILEB128(i64, in);
   1039                         prog.line += arg;
   1040                     },
   1041                     LNS.set_file => {
   1042                         const arg = try leb.readULEB128(usize, in);
   1043                         prog.file = arg;
   1044                     },
   1045                     LNS.set_column => {
   1046                         const arg = try leb.readULEB128(u64, in);
   1047                         prog.column = arg;
   1048                     },
   1049                     LNS.negate_stmt => {
   1050                         prog.is_stmt = !prog.is_stmt;
   1051                     },
   1052                     LNS.set_basic_block => {
   1053                         prog.basic_block = true;
   1054                     },
   1055                     LNS.const_add_pc => {
   1056                         const inc_addr = minimum_instruction_length * ((255 - opcode_base) / line_range);
   1057                         prog.address += inc_addr;
   1058                     },
   1059                     LNS.fixed_advance_pc => {
   1060                         const arg = try in.readInt(u16, di.endian);
   1061                         prog.address += arg;
   1062                     },
   1063                     LNS.set_prologue_end => {},
   1064                     else => {
   1065                         if (opcode - 1 >= standard_opcode_lengths.len) return error.InvalidDebugInfo;
   1066                         const len_bytes = standard_opcode_lengths[opcode - 1];
   1067                         try seekable.seekBy(len_bytes);
   1068                     },
   1069                 }
   1070             }
   1071         }
   1072 
   1073         return error.MissingDebugInfo;
   1074     }
   1075 
   1076     fn getString(di: *DwarfInfo, offset: u64) ![]const u8 {
   1077         if (offset > di.debug_str.len)
   1078             return error.InvalidDebugInfo;
   1079         const casted_offset = math.cast(usize, offset) catch
   1080             return error.InvalidDebugInfo;
   1081 
   1082         // Valid strings always have a terminating zero byte
   1083         if (mem.indexOfScalarPos(u8, di.debug_str, casted_offset, 0)) |last| {
   1084             return di.debug_str[casted_offset..last];
   1085         }
   1086 
   1087         return error.InvalidDebugInfo;
   1088     }
   1089 };
   1090 
   1091 /// Initialize DWARF info. The caller has the responsibility to initialize most
   1092 /// the DwarfInfo fields before calling. These fields can be left undefined:
   1093 /// * abbrev_table_list
   1094 /// * compile_unit_list
   1095 pub fn openDwarfDebugInfo(di: *DwarfInfo, allocator: mem.Allocator) !void {
   1096     di.abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator);
   1097     di.compile_unit_list = ArrayList(CompileUnit).init(allocator);
   1098     di.func_list = ArrayList(Func).init(allocator);
   1099     try di.scanAllFunctions();
   1100     try di.scanAllCompileUnits();
   1101 }