1const std = @import("std.zig"); 2const debug = std.debug; 3const fs = std.fs; 4const io = std.io; 5const mem = std.mem; 6const math = std.math; 7const leb = @import("leb128.zig"); 8 9const ArrayList = std.ArrayList; 10 11pub const TAG = @import("dwarf/TAG.zig"); 12pub const AT = @import("dwarf/AT.zig"); 13pub const OP = @import("dwarf/OP.zig"); 14 15pub 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 52pub 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 97pub 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 139pub const CHILDREN = struct { 140 pub const no = 0x00; 141 pub const yes = 0x01; 142}; 143 144pub 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 160pub 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 169pub 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 208pub 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 219pub 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 229const PcRange = struct { 230 start: u64, 231 end: u64, 232}; 233 234const Func = struct { 235 pc_range: ?PcRange, 236 name: ?[]const u8, 237}; 238 239const CompileUnit = struct { 240 version: u16, 241 is_64: bool, 242 die: *Die, 243 pc_range: ?PcRange, 244}; 245 246const AbbrevTable = ArrayList(AbbrevTableEntry); 247 248const AbbrevTableHeader = struct { 249 // offset from .debug_abbrev 250 offset: u64, 251 table: AbbrevTable, 252}; 253 254const AbbrevTableEntry = struct { 255 has_children: bool, 256 abbrev_code: u64, 257 tag_id: u64, 258 attrs: ArrayList(AbbrevAttr), 259}; 260 261const AbbrevAttr = struct { 262 attr_id: u64, 263 form_id: u64, 264}; 265 266const 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 279const 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 289const 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 349const FileEntry = struct { 350 file_name: []const u8, 351 dir_index: usize, 352 mtime: usize, 353 len_bytes: usize, 354}; 355 356const 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 456fn 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 469fn 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 477fn 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 484fn 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 490fn 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 495fn 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 523fn 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 538fn 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 587fn 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 594pub 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 1095pub 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} 1102