| ... | @@ -7,8 +7,6 @@ const mem = std.mem; | ... | @@ -7,8 +7,6 @@ const mem = std.mem; |
| 7 | const math = std.math; | 7 | const math = std.math; |
| 8 | const leb = @import("leb128.zig"); | 8 | const leb = @import("leb128.zig"); |
| 9 | | 9 | |
| 10 | const ArrayList = std.ArrayList; | | |
| 11 | | | |
| 12 | pub const TAG = @import("dwarf/TAG.zig"); | 10 | pub const TAG = @import("dwarf/TAG.zig"); |
| 13 | pub const AT = @import("dwarf/AT.zig"); | 11 | pub const AT = @import("dwarf/AT.zig"); |
| 14 | pub const OP = @import("dwarf/OP.zig"); | 12 | pub const OP = @import("dwarf/OP.zig"); |
| ... | @@ -157,6 +155,12 @@ const PcRange = struct { | ... | @@ -157,6 +155,12 @@ const PcRange = struct { |
| 157 | const Func = struct { | 155 | const Func = struct { |
| 158 | pc_range: ?PcRange, | 156 | pc_range: ?PcRange, |
| 159 | name: ?[]const u8, | 157 | name: ?[]const u8, |
| | 158 | |
| | 159 | fn deinit(func: *Func, allocator: mem.Allocator) void { |
| | 160 | if (func.name) |name| { |
| | 161 | allocator.free(name); |
| | 162 | } |
| | 163 | } |
| 160 | }; | 164 | }; |
| 161 | | 165 | |
| 162 | const CompileUnit = struct { | 166 | const CompileUnit = struct { |
| ... | @@ -166,19 +170,30 @@ const CompileUnit = struct { | ... | @@ -166,19 +170,30 @@ const CompileUnit = struct { |
| 166 | pc_range: ?PcRange, | 170 | pc_range: ?PcRange, |
| 167 | }; | 171 | }; |
| 168 | | 172 | |
| 169 | const AbbrevTable = ArrayList(AbbrevTableEntry); | 173 | const AbbrevTable = std.ArrayList(AbbrevTableEntry); |
| 170 | | 174 | |
| 171 | const AbbrevTableHeader = struct { | 175 | const AbbrevTableHeader = struct { |
| 172 | // offset from .debug_abbrev | 176 | // offset from .debug_abbrev |
| 173 | offset: u64, | 177 | offset: u64, |
| 174 | table: AbbrevTable, | 178 | table: AbbrevTable, |
| | 179 | |
| | 180 | fn deinit(header: *AbbrevTableHeader) void { |
| | 181 | for (header.table.items) |*entry| { |
| | 182 | entry.deinit(); |
| | 183 | } |
| | 184 | header.table.deinit(); |
| | 185 | } |
| 175 | }; | 186 | }; |
| 176 | | 187 | |
| 177 | const AbbrevTableEntry = struct { | 188 | const AbbrevTableEntry = struct { |
| 178 | has_children: bool, | 189 | has_children: bool, |
| 179 | abbrev_code: u64, | 190 | abbrev_code: u64, |
| 180 | tag_id: u64, | 191 | tag_id: u64, |
| 181 | attrs: ArrayList(AbbrevAttr), | 192 | attrs: std.ArrayList(AbbrevAttr), |
| | 193 | |
| | 194 | fn deinit(entry: *AbbrevTableEntry) void { |
| | 195 | entry.attrs.deinit(); |
| | 196 | } |
| 182 | }; | 197 | }; |
| 183 | | 198 | |
| 184 | const AbbrevAttr = struct { | 199 | const AbbrevAttr = struct { |
| ... | @@ -213,15 +228,22 @@ const Constant = struct { | ... | @@ -213,15 +228,22 @@ const Constant = struct { |
| 213 | }; | 228 | }; |
| 214 | | 229 | |
| 215 | const Die = struct { | 230 | const Die = struct { |
| | 231 | // Arena for Die's Attr's and FormValue's. |
| | 232 | arena: std.heap.ArenaAllocator, |
| 216 | tag_id: u64, | 233 | tag_id: u64, |
| 217 | has_children: bool, | 234 | has_children: bool, |
| 218 | attrs: ArrayList(Attr), | 235 | attrs: std.ArrayListUnmanaged(Attr) = .{}, |
| 219 | | 236 | |
| 220 | const Attr = struct { | 237 | const Attr = struct { |
| 221 | id: u64, | 238 | id: u64, |
| 222 | value: FormValue, | 239 | value: FormValue, |
| 223 | }; | 240 | }; |
| 224 | | 241 | |
| | 242 | fn deinit(self: *Die, allocator: mem.Allocator) void { |
| | 243 | self.arena.deinit(); |
| | 244 | self.attrs.deinit(allocator); |
| | 245 | } |
| | 246 | |
| 225 | fn getAttr(self: *const Die, id: u64) ?*const FormValue { | 247 | fn getAttr(self: *const Die, id: u64) ?*const FormValue { |
| 226 | for (self.attrs.items) |*attr| { | 248 | for (self.attrs.items) |*attr| { |
| 227 | if (attr.id == id) return &attr.value; | 249 | if (attr.id == id) return &attr.value; |
| ... | @@ -292,7 +314,6 @@ const LineNumberProgram = struct { | ... | @@ -292,7 +314,6 @@ const LineNumberProgram = struct { |
| 292 | default_is_stmt: bool, | 314 | default_is_stmt: bool, |
| 293 | target_address: u64, | 315 | target_address: u64, |
| 294 | include_dirs: []const []const u8, | 316 | include_dirs: []const []const u8, |
| 295 | file_entries: *ArrayList(FileEntry), | | |
| 296 | | 317 | |
| 297 | prev_valid: bool, | 318 | prev_valid: bool, |
| 298 | prev_address: u64, | 319 | prev_address: u64, |
| ... | @@ -323,7 +344,7 @@ const LineNumberProgram = struct { | ... | @@ -323,7 +344,7 @@ const LineNumberProgram = struct { |
| 323 | self.prev_end_sequence = undefined; | 344 | self.prev_end_sequence = undefined; |
| 324 | } | 345 | } |
| 325 | | 346 | |
| 326 | pub fn init(is_stmt: bool, include_dirs: []const []const u8, file_entries: *ArrayList(FileEntry), target_address: u64) LineNumberProgram { | 347 | pub fn init(is_stmt: bool, include_dirs: []const []const u8, target_address: u64) LineNumberProgram { |
| 327 | return LineNumberProgram{ | 348 | return LineNumberProgram{ |
| 328 | .address = 0, | 349 | .address = 0, |
| 329 | .file = 1, | 350 | .file = 1, |
| ... | @@ -333,7 +354,6 @@ const LineNumberProgram = struct { | ... | @@ -333,7 +354,6 @@ const LineNumberProgram = struct { |
| 333 | .basic_block = false, | 354 | .basic_block = false, |
| 334 | .end_sequence = false, | 355 | .end_sequence = false, |
| 335 | .include_dirs = include_dirs, | 356 | .include_dirs = include_dirs, |
| 336 | .file_entries = file_entries, | | |
| 337 | .default_is_stmt = is_stmt, | 357 | .default_is_stmt = is_stmt, |
| 338 | .target_address = target_address, | 358 | .target_address = target_address, |
| 339 | .prev_valid = false, | 359 | .prev_valid = false, |
| ... | @@ -347,24 +367,28 @@ const LineNumberProgram = struct { | ... | @@ -347,24 +367,28 @@ const LineNumberProgram = struct { |
| 347 | }; | 367 | }; |
| 348 | } | 368 | } |
| 349 | | 369 | |
| 350 | pub fn checkLineMatch(self: *LineNumberProgram) !?debug.LineInfo { | 370 | pub fn checkLineMatch( |
| | 371 | self: *LineNumberProgram, |
| | 372 | allocator: mem.Allocator, |
| | 373 | file_entries: []const FileEntry, |
| | 374 | ) !?debug.LineInfo { |
| 351 | if (self.prev_valid and self.target_address >= self.prev_address and self.target_address < self.address) { | 375 | if (self.prev_valid and self.target_address >= self.prev_address and self.target_address < self.address) { |
| 352 | const file_entry = if (self.prev_file == 0) { | 376 | const file_entry = if (self.prev_file == 0) { |
| 353 | return error.MissingDebugInfo; | 377 | return error.MissingDebugInfo; |
| 354 | } else if (self.prev_file - 1 >= self.file_entries.items.len) { | 378 | } else if (self.prev_file - 1 >= file_entries.len) { |
| 355 | return error.InvalidDebugInfo; | 379 | return error.InvalidDebugInfo; |
| 356 | } else &self.file_entries.items[self.prev_file - 1]; | 380 | } else &file_entries[self.prev_file - 1]; |
| 357 | | 381 | |
| 358 | const dir_name = if (file_entry.dir_index >= self.include_dirs.len) { | 382 | const dir_name = if (file_entry.dir_index >= self.include_dirs.len) { |
| 359 | return error.InvalidDebugInfo; | 383 | return error.InvalidDebugInfo; |
| 360 | } else self.include_dirs[file_entry.dir_index]; | 384 | } else self.include_dirs[file_entry.dir_index]; |
| 361 | const file_name = try fs.path.join(self.file_entries.allocator, &[_][]const u8{ dir_name, file_entry.file_name }); | 385 | |
| 362 | errdefer self.file_entries.allocator.free(file_name); | 386 | const file_name = try fs.path.join(allocator, &[_][]const u8{ dir_name, file_entry.file_name }); |
| | 387 | |
| 363 | return debug.LineInfo{ | 388 | return debug.LineInfo{ |
| 364 | .line = if (self.prev_line >= 0) @intCast(u64, self.prev_line) else 0, | 389 | .line = if (self.prev_line >= 0) @intCast(u64, self.prev_line) else 0, |
| 365 | .column = self.prev_column, | 390 | .column = self.prev_column, |
| 366 | .file_name = file_name, | 391 | .file_name = file_name, |
| 367 | .allocator = self.file_entries.allocator, | | |
| 368 | }; | 392 | }; |
| 369 | } | 393 | } |
| 370 | | 394 | |
| ... | @@ -419,8 +443,7 @@ fn parseFormValueBlock(allocator: mem.Allocator, in_stream: anytype, endian: std | ... | @@ -419,8 +443,7 @@ fn parseFormValueBlock(allocator: mem.Allocator, in_stream: anytype, endian: std |
| 419 | return parseFormValueBlockLen(allocator, in_stream, block_len); | 443 | return parseFormValueBlockLen(allocator, in_stream, block_len); |
| 420 | } | 444 | } |
| 421 | | 445 | |
| 422 | fn parseFormValueConstant(allocator: mem.Allocator, in_stream: anytype, signed: bool, endian: std.builtin.Endian, comptime size: i32) !FormValue { | 446 | fn parseFormValueConstant(in_stream: anytype, signed: bool, endian: std.builtin.Endian, comptime size: i32) !FormValue { |
| 423 | _ = allocator; | | |
| 424 | // TODO: Please forgive me, I've worked around zig not properly spilling some intermediate values here. | 447 | // TODO: Please forgive me, I've worked around zig not properly spilling some intermediate values here. |
| 425 | // `nosuspend` should be removed from all the function calls once it is fixed. | 448 | // `nosuspend` should be removed from all the function calls once it is fixed. |
| 426 | return FormValue{ | 449 | return FormValue{ |
| ... | @@ -447,8 +470,7 @@ fn parseFormValueConstant(allocator: mem.Allocator, in_stream: anytype, signed: | ... | @@ -447,8 +470,7 @@ fn parseFormValueConstant(allocator: mem.Allocator, in_stream: anytype, signed: |
| 447 | } | 470 | } |
| 448 | | 471 | |
| 449 | // TODO the nosuspends here are workarounds | 472 | // TODO the nosuspends here are workarounds |
| 450 | fn parseFormValueRef(allocator: mem.Allocator, in_stream: anytype, endian: std.builtin.Endian, size: i32) !FormValue { | 473 | fn parseFormValueRef(in_stream: anytype, endian: std.builtin.Endian, size: i32) !FormValue { |
| 451 | _ = allocator; | | |
| 452 | return FormValue{ | 474 | return FormValue{ |
| 453 | .Ref = switch (size) { | 475 | .Ref = switch (size) { |
| 454 | 1 => try nosuspend in_stream.readInt(u8, endian), | 476 | 1 => try nosuspend in_stream.readInt(u8, endian), |
| ... | @@ -472,13 +494,13 @@ fn parseFormValue(allocator: mem.Allocator, in_stream: anytype, form_id: u64, en | ... | @@ -472,13 +494,13 @@ fn parseFormValue(allocator: mem.Allocator, in_stream: anytype, form_id: u64, en |
| 472 | const block_len = try nosuspend leb.readULEB128(usize, in_stream); | 494 | const block_len = try nosuspend leb.readULEB128(usize, in_stream); |
| 473 | return parseFormValueBlockLen(allocator, in_stream, block_len); | 495 | return parseFormValueBlockLen(allocator, in_stream, block_len); |
| 474 | }, | 496 | }, |
| 475 | FORM.data1 => parseFormValueConstant(allocator, in_stream, false, endian, 1), | 497 | FORM.data1 => parseFormValueConstant(in_stream, false, endian, 1), |
| 476 | FORM.data2 => parseFormValueConstant(allocator, in_stream, false, endian, 2), | 498 | FORM.data2 => parseFormValueConstant(in_stream, false, endian, 2), |
| 477 | FORM.data4 => parseFormValueConstant(allocator, in_stream, false, endian, 4), | 499 | FORM.data4 => parseFormValueConstant(in_stream, false, endian, 4), |
| 478 | FORM.data8 => parseFormValueConstant(allocator, in_stream, false, endian, 8), | 500 | FORM.data8 => parseFormValueConstant(in_stream, false, endian, 8), |
| 479 | FORM.udata, FORM.sdata => { | 501 | FORM.udata, FORM.sdata => { |
| 480 | const signed = form_id == FORM.sdata; | 502 | const signed = form_id == FORM.sdata; |
| 481 | return parseFormValueConstant(allocator, in_stream, signed, endian, -1); | 503 | return parseFormValueConstant(in_stream, signed, endian, -1); |
| 482 | }, | 504 | }, |
| 483 | FORM.exprloc => { | 505 | FORM.exprloc => { |
| 484 | const size = try nosuspend leb.readULEB128(usize, in_stream); | 506 | const size = try nosuspend leb.readULEB128(usize, in_stream); |
| ... | @@ -489,11 +511,11 @@ fn parseFormValue(allocator: mem.Allocator, in_stream: anytype, form_id: u64, en | ... | @@ -489,11 +511,11 @@ fn parseFormValue(allocator: mem.Allocator, in_stream: anytype, form_id: u64, en |
| 489 | FORM.flag_present => FormValue{ .Flag = true }, | 511 | FORM.flag_present => FormValue{ .Flag = true }, |
| 490 | FORM.sec_offset => FormValue{ .SecOffset = try readAddress(in_stream, endian, is_64) }, | 512 | FORM.sec_offset => FormValue{ .SecOffset = try readAddress(in_stream, endian, is_64) }, |
| 491 | | 513 | |
| 492 | FORM.ref1 => parseFormValueRef(allocator, in_stream, endian, 1), | 514 | FORM.ref1 => parseFormValueRef(in_stream, endian, 1), |
| 493 | FORM.ref2 => parseFormValueRef(allocator, in_stream, endian, 2), | 515 | FORM.ref2 => parseFormValueRef(in_stream, endian, 2), |
| 494 | FORM.ref4 => parseFormValueRef(allocator, in_stream, endian, 4), | 516 | FORM.ref4 => parseFormValueRef(in_stream, endian, 4), |
| 495 | FORM.ref8 => parseFormValueRef(allocator, in_stream, endian, 8), | 517 | FORM.ref8 => parseFormValueRef(in_stream, endian, 8), |
| 496 | FORM.ref_udata => parseFormValueRef(allocator, in_stream, endian, -1), | 518 | FORM.ref_udata => parseFormValueRef(in_stream, endian, -1), |
| 497 | | 519 | |
| 498 | FORM.ref_addr => FormValue{ .RefAddr = try readAddress(in_stream, endian, is_64) }, | 520 | FORM.ref_addr => FormValue{ .RefAddr = try readAddress(in_stream, endian, is_64) }, |
| 499 | FORM.ref_sig8 => FormValue{ .Ref = try nosuspend in_stream.readInt(u64, endian) }, | 521 | FORM.ref_sig8 => FormValue{ .Ref = try nosuspend in_stream.readInt(u64, endian) }, |
| ... | @@ -536,12 +558,24 @@ pub const DwarfInfo = struct { | ... | @@ -536,12 +558,24 @@ pub const DwarfInfo = struct { |
| 536 | debug_line_str: ?[]const u8, | 558 | debug_line_str: ?[]const u8, |
| 537 | debug_ranges: ?[]const u8, | 559 | debug_ranges: ?[]const u8, |
| 538 | // Filled later by the initializer | 560 | // Filled later by the initializer |
| 539 | abbrev_table_list: ArrayList(AbbrevTableHeader) = undefined, | 561 | abbrev_table_list: std.ArrayListUnmanaged(AbbrevTableHeader) = .{}, |
| 540 | compile_unit_list: ArrayList(CompileUnit) = undefined, | 562 | compile_unit_list: std.ArrayListUnmanaged(CompileUnit) = .{}, |
| 541 | func_list: ArrayList(Func) = undefined, | 563 | func_list: std.ArrayListUnmanaged(Func) = .{}, |
| 542 | | 564 | |
| 543 | pub fn allocator(self: DwarfInfo) mem.Allocator { | 565 | pub fn deinit(di: *DwarfInfo, allocator: mem.Allocator) void { |
| 544 | return self.abbrev_table_list.allocator; | 566 | for (di.abbrev_table_list.items) |*abbrev| { |
| | 567 | abbrev.deinit(); |
| | 568 | } |
| | 569 | di.abbrev_table_list.deinit(allocator); |
| | 570 | for (di.compile_unit_list.items) |*cu| { |
| | 571 | cu.die.deinit(allocator); |
| | 572 | allocator.destroy(cu.die); |
| | 573 | } |
| | 574 | di.compile_unit_list.deinit(allocator); |
| | 575 | for (di.func_list.items) |*func| { |
| | 576 | func.deinit(allocator); |
| | 577 | } |
| | 578 | di.func_list.deinit(allocator); |
| 545 | } | 579 | } |
| 546 | | 580 | |
| 547 | pub fn getSymbolName(di: *DwarfInfo, address: u64) ?[]const u8 { | 581 | pub fn getSymbolName(di: *DwarfInfo, address: u64) ?[]const u8 { |
| ... | @@ -556,12 +590,16 @@ pub const DwarfInfo = struct { | ... | @@ -556,12 +590,16 @@ pub const DwarfInfo = struct { |
| 556 | return null; | 590 | return null; |
| 557 | } | 591 | } |
| 558 | | 592 | |
| 559 | fn scanAllFunctions(di: *DwarfInfo) !void { | 593 | fn scanAllFunctions(di: *DwarfInfo, allocator: mem.Allocator) !void { |
| 560 | var stream = io.fixedBufferStream(di.debug_info); | 594 | var stream = io.fixedBufferStream(di.debug_info); |
| 561 | const in = &stream.reader(); | 595 | const in = &stream.reader(); |
| 562 | const seekable = &stream.seekableStream(); | 596 | const seekable = &stream.seekableStream(); |
| 563 | var this_unit_offset: u64 = 0; | 597 | var this_unit_offset: u64 = 0; |
| 564 | | 598 | |
| | 599 | var tmp_arena = std.heap.ArenaAllocator.init(allocator); |
| | 600 | defer tmp_arena.deinit(); |
| | 601 | const arena = tmp_arena.allocator(); |
| | 602 | |
| 565 | while (this_unit_offset < try seekable.getEndPos()) { | 603 | while (this_unit_offset < try seekable.getEndPos()) { |
| 566 | try seekable.seekTo(this_unit_offset); | 604 | try seekable.seekTo(this_unit_offset); |
| 567 | | 605 | |
| ... | @@ -580,26 +618,30 @@ pub const DwarfInfo = struct { | ... | @@ -580,26 +618,30 @@ pub const DwarfInfo = struct { |
| 580 | const unit_type = try in.readInt(u8, di.endian); | 618 | const unit_type = try in.readInt(u8, di.endian); |
| 581 | if (unit_type != UT.compile) return error.InvalidDebugInfo; | 619 | if (unit_type != UT.compile) return error.InvalidDebugInfo; |
| 582 | address_size = try in.readByte(); | 620 | address_size = try in.readByte(); |
| 583 | debug_abbrev_offset = if (is_64) try in.readInt(u64, di.endian) else try in.readInt(u32, di.endian); | 621 | debug_abbrev_offset = if (is_64) |
| | 622 | try in.readInt(u64, di.endian) |
| | 623 | else |
| | 624 | try in.readInt(u32, di.endian); |
| 584 | }, | 625 | }, |
| 585 | else => { | 626 | else => { |
| 586 | debug_abbrev_offset = if (is_64) try in.readInt(u64, di.endian) else try in.readInt(u32, di.endian); | 627 | debug_abbrev_offset = if (is_64) |
| | 628 | try in.readInt(u64, di.endian) |
| | 629 | else |
| | 630 | try in.readInt(u32, di.endian); |
| 587 | address_size = try in.readByte(); | 631 | address_size = try in.readByte(); |
| 588 | }, | 632 | }, |
| 589 | } | 633 | } |
| 590 | if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo; | 634 | if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo; |
| 591 | | 635 | |
| 592 | const compile_unit_pos = try seekable.getPos(); | 636 | const compile_unit_pos = try seekable.getPos(); |
| 593 | const abbrev_table = try di.getAbbrevTable(debug_abbrev_offset); | 637 | const abbrev_table = try di.getAbbrevTable(allocator, debug_abbrev_offset); |
| 594 | | 638 | |
| 595 | try seekable.seekTo(compile_unit_pos); | 639 | try seekable.seekTo(compile_unit_pos); |
| 596 | | 640 | |
| 597 | const next_unit_pos = this_unit_offset + next_offset; | 641 | const next_unit_pos = this_unit_offset + next_offset; |
| 598 | | 642 | |
| 599 | while ((try seekable.getPos()) < next_unit_pos) { | 643 | while ((try seekable.getPos()) < next_unit_pos) { |
| 600 | const die_obj = (try di.parseDie(in, abbrev_table, is_64)) orelse continue; | 644 | const die_obj = (try di.parseDie(arena, in, abbrev_table, is_64)) orelse continue; |
| 601 | defer die_obj.attrs.deinit(); | | |
| 602 | | | |
| 603 | const after_die_offset = try seekable.getPos(); | 645 | const after_die_offset = try seekable.getPos(); |
| 604 | | 646 | |
| 605 | switch (die_obj.tag_id) { | 647 | switch (die_obj.tag_id) { |
| ... | @@ -607,23 +649,33 @@ pub const DwarfInfo = struct { | ... | @@ -607,23 +649,33 @@ pub const DwarfInfo = struct { |
| 607 | const fn_name = x: { | 649 | const fn_name = x: { |
| 608 | var depth: i32 = 3; | 650 | var depth: i32 = 3; |
| 609 | var this_die_obj = die_obj; | 651 | var this_die_obj = die_obj; |
| 610 | // Prenvent endless loops | 652 | // Prevent endless loops |
| 611 | while (depth > 0) : (depth -= 1) { | 653 | while (depth > 0) : (depth -= 1) { |
| 612 | if (this_die_obj.getAttr(AT.name)) |_| { | 654 | if (this_die_obj.getAttr(AT.name)) |_| { |
| 613 | const name = try this_die_obj.getAttrString(di, AT.name); | 655 | const name = try this_die_obj.getAttrString(di, AT.name); |
| 614 | break :x name; | 656 | break :x try allocator.dupe(u8, name); |
| 615 | } else if (this_die_obj.getAttr(AT.abstract_origin)) |_| { | 657 | } else if (this_die_obj.getAttr(AT.abstract_origin)) |_| { |
| 616 | // Follow the DIE it points to and repeat | 658 | // Follow the DIE it points to and repeat |
| 617 | const ref_offset = try this_die_obj.getAttrRef(AT.abstract_origin); | 659 | const ref_offset = try this_die_obj.getAttrRef(AT.abstract_origin); |
| 618 | if (ref_offset > next_offset) return error.InvalidDebugInfo; | 660 | if (ref_offset > next_offset) return error.InvalidDebugInfo; |
| 619 | try seekable.seekTo(this_unit_offset + ref_offset); | 661 | try seekable.seekTo(this_unit_offset + ref_offset); |
| 620 | this_die_obj = (try di.parseDie(in, abbrev_table, is_64)) orelse return error.InvalidDebugInfo; | 662 | this_die_obj = (try di.parseDie( |
| | 663 | arena, |
| | 664 | in, |
| | 665 | abbrev_table, |
| | 666 | is_64, |
| | 667 | )) orelse return error.InvalidDebugInfo; |
| 621 | } else if (this_die_obj.getAttr(AT.specification)) |_| { | 668 | } else if (this_die_obj.getAttr(AT.specification)) |_| { |
| 622 | // Follow the DIE it points to and repeat | 669 | // Follow the DIE it points to and repeat |
| 623 | const ref_offset = try this_die_obj.getAttrRef(AT.specification); | 670 | const ref_offset = try this_die_obj.getAttrRef(AT.specification); |
| 624 | if (ref_offset > next_offset) return error.InvalidDebugInfo; | 671 | if (ref_offset > next_offset) return error.InvalidDebugInfo; |
| 625 | try seekable.seekTo(this_unit_offset + ref_offset); | 672 | try seekable.seekTo(this_unit_offset + ref_offset); |
| 626 | this_die_obj = (try di.parseDie(in, abbrev_table, is_64)) orelse return error.InvalidDebugInfo; | 673 | this_die_obj = (try di.parseDie( |
| | 674 | arena, |
| | 675 | in, |
| | 676 | abbrev_table, |
| | 677 | is_64, |
| | 678 | )) orelse return error.InvalidDebugInfo; |
| 627 | } else { | 679 | } else { |
| 628 | break :x null; | 680 | break :x null; |
| 629 | } | 681 | } |
| ... | @@ -656,7 +708,7 @@ pub const DwarfInfo = struct { | ... | @@ -656,7 +708,7 @@ pub const DwarfInfo = struct { |
| 656 | } | 708 | } |
| 657 | }; | 709 | }; |
| 658 | | 710 | |
| 659 | try di.func_list.append(Func{ | 711 | try di.func_list.append(allocator, Func{ |
| 660 | .name = fn_name, | 712 | .name = fn_name, |
| 661 | .pc_range = pc_range, | 713 | .pc_range = pc_range, |
| 662 | }); | 714 | }); |
| ... | @@ -671,7 +723,7 @@ pub const DwarfInfo = struct { | ... | @@ -671,7 +723,7 @@ pub const DwarfInfo = struct { |
| 671 | } | 723 | } |
| 672 | } | 724 | } |
| 673 | | 725 | |
| 674 | fn scanAllCompileUnits(di: *DwarfInfo) !void { | 726 | fn scanAllCompileUnits(di: *DwarfInfo, allocator: mem.Allocator) !void { |
| 675 | var stream = io.fixedBufferStream(di.debug_info); | 727 | var stream = io.fixedBufferStream(di.debug_info); |
| 676 | const in = &stream.reader(); | 728 | const in = &stream.reader(); |
| 677 | const seekable = &stream.seekableStream(); | 729 | const seekable = &stream.seekableStream(); |
| ... | @@ -695,22 +747,30 @@ pub const DwarfInfo = struct { | ... | @@ -695,22 +747,30 @@ pub const DwarfInfo = struct { |
| 695 | const unit_type = try in.readInt(u8, di.endian); | 747 | const unit_type = try in.readInt(u8, di.endian); |
| 696 | if (unit_type != UT.compile) return error.InvalidDebugInfo; | 748 | if (unit_type != UT.compile) return error.InvalidDebugInfo; |
| 697 | address_size = try in.readByte(); | 749 | address_size = try in.readByte(); |
| 698 | debug_abbrev_offset = if (is_64) try in.readInt(u64, di.endian) else try in.readInt(u32, di.endian); | 750 | debug_abbrev_offset = if (is_64) |
| | 751 | try in.readInt(u64, di.endian) |
| | 752 | else |
| | 753 | try in.readInt(u32, di.endian); |
| 699 | }, | 754 | }, |
| 700 | else => { | 755 | else => { |
| 701 | debug_abbrev_offset = if (is_64) try in.readInt(u64, di.endian) else try in.readInt(u32, di.endian); | 756 | debug_abbrev_offset = if (is_64) |
| | 757 | try in.readInt(u64, di.endian) |
| | 758 | else |
| | 759 | try in.readInt(u32, di.endian); |
| 702 | address_size = try in.readByte(); | 760 | address_size = try in.readByte(); |
| 703 | }, | 761 | }, |
| 704 | } | 762 | } |
| 705 | if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo; | 763 | if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo; |
| 706 | | 764 | |
| 707 | const compile_unit_pos = try seekable.getPos(); | 765 | const compile_unit_pos = try seekable.getPos(); |
| 708 | const abbrev_table = try di.getAbbrevTable(debug_abbrev_offset); | 766 | const abbrev_table = try di.getAbbrevTable(allocator, debug_abbrev_offset); |
| 709 | | 767 | |
| 710 | try seekable.seekTo(compile_unit_pos); | 768 | try seekable.seekTo(compile_unit_pos); |
| 711 | | 769 | |
| 712 | const compile_unit_die = try di.allocator().create(Die); | 770 | const compile_unit_die = try allocator.create(Die); |
| 713 | compile_unit_die.* = (try di.parseDie(in, abbrev_table, is_64)) orelse return error.InvalidDebugInfo; | 771 | errdefer allocator.destroy(compile_unit_die); |
| | 772 | compile_unit_die.* = (try di.parseDie(allocator, in, abbrev_table, is_64)) orelse |
| | 773 | return error.InvalidDebugInfo; |
| 714 | | 774 | |
| 715 | if (compile_unit_die.tag_id != TAG.compile_unit) return error.InvalidDebugInfo; | 775 | if (compile_unit_die.tag_id != TAG.compile_unit) return error.InvalidDebugInfo; |
| 716 | | 776 | |
| ... | @@ -738,7 +798,7 @@ pub const DwarfInfo = struct { | ... | @@ -738,7 +798,7 @@ pub const DwarfInfo = struct { |
| 738 | } | 798 | } |
| 739 | }; | 799 | }; |
| 740 | | 800 | |
| 741 | try di.compile_unit_list.append(CompileUnit{ | 801 | try di.compile_unit_list.append(allocator, CompileUnit{ |
| 742 | .version = version, | 802 | .version = version, |
| 743 | .is_64 = is_64, | 803 | .is_64 = is_64, |
| 744 | .pc_range = pc_range, | 804 | .pc_range = pc_range, |
| ... | @@ -797,27 +857,33 @@ pub const DwarfInfo = struct { | ... | @@ -797,27 +857,33 @@ pub const DwarfInfo = struct { |
| 797 | | 857 | |
| 798 | /// Gets an already existing AbbrevTable given the abbrev_offset, or if not found, | 858 | /// Gets an already existing AbbrevTable given the abbrev_offset, or if not found, |
| 799 | /// seeks in the stream and parses it. | 859 | /// seeks in the stream and parses it. |
| 800 | fn getAbbrevTable(di: *DwarfInfo, abbrev_offset: u64) !*const AbbrevTable { | 860 | fn getAbbrevTable(di: *DwarfInfo, allocator: mem.Allocator, abbrev_offset: u64) !*const AbbrevTable { |
| 801 | for (di.abbrev_table_list.items) |*header| { | 861 | for (di.abbrev_table_list.items) |*header| { |
| 802 | if (header.offset == abbrev_offset) { | 862 | if (header.offset == abbrev_offset) { |
| 803 | return &header.table; | 863 | return &header.table; |
| 804 | } | 864 | } |
| 805 | } | 865 | } |
| 806 | try di.abbrev_table_list.append(AbbrevTableHeader{ | 866 | try di.abbrev_table_list.append(allocator, AbbrevTableHeader{ |
| 807 | .offset = abbrev_offset, | 867 | .offset = abbrev_offset, |
| 808 | .table = try di.parseAbbrevTable(abbrev_offset), | 868 | .table = try di.parseAbbrevTable(allocator, abbrev_offset), |
| 809 | }); | 869 | }); |
| 810 | return &di.abbrev_table_list.items[di.abbrev_table_list.items.len - 1].table; | 870 | return &di.abbrev_table_list.items[di.abbrev_table_list.items.len - 1].table; |
| 811 | } | 871 | } |
| 812 | | 872 | |
| 813 | fn parseAbbrevTable(di: *DwarfInfo, offset: u64) !AbbrevTable { | 873 | fn parseAbbrevTable(di: *DwarfInfo, allocator: mem.Allocator, offset: u64) !AbbrevTable { |
| 814 | var stream = io.fixedBufferStream(di.debug_abbrev); | 874 | var stream = io.fixedBufferStream(di.debug_abbrev); |
| 815 | const in = &stream.reader(); | 875 | const in = &stream.reader(); |
| 816 | const seekable = &stream.seekableStream(); | 876 | const seekable = &stream.seekableStream(); |
| 817 | | 877 | |
| 818 | try seekable.seekTo(offset); | 878 | try seekable.seekTo(offset); |
| 819 | var result = AbbrevTable.init(di.allocator()); | 879 | var result = AbbrevTable.init(allocator); |
| 820 | errdefer result.deinit(); | 880 | errdefer { |
| | 881 | for (result.items) |*entry| { |
| | 882 | entry.attrs.deinit(); |
| | 883 | } |
| | 884 | result.deinit(); |
| | 885 | } |
| | 886 | |
| 821 | while (true) { | 887 | while (true) { |
| 822 | const abbrev_code = try leb.readULEB128(u64, in); | 888 | const abbrev_code = try leb.readULEB128(u64, in); |
| 823 | if (abbrev_code == 0) return result; | 889 | if (abbrev_code == 0) return result; |
| ... | @@ -825,7 +891,7 @@ pub const DwarfInfo = struct { | ... | @@ -825,7 +891,7 @@ pub const DwarfInfo = struct { |
| 825 | .abbrev_code = abbrev_code, | 891 | .abbrev_code = abbrev_code, |
| 826 | .tag_id = try leb.readULEB128(u64, in), | 892 | .tag_id = try leb.readULEB128(u64, in), |
| 827 | .has_children = (try in.readByte()) == CHILDREN.yes, | 893 | .has_children = (try in.readByte()) == CHILDREN.yes, |
| 828 | .attrs = ArrayList(AbbrevAttr).init(di.allocator()), | 894 | .attrs = std.ArrayList(AbbrevAttr).init(allocator), |
| 829 | }); | 895 | }); |
| 830 | const attrs = &result.items[result.items.len - 1].attrs; | 896 | const attrs = &result.items[result.items.len - 1].attrs; |
| 831 | | 897 | |
| ... | @@ -844,21 +910,34 @@ pub const DwarfInfo = struct { | ... | @@ -844,21 +910,34 @@ pub const DwarfInfo = struct { |
| 844 | } | 910 | } |
| 845 | } | 911 | } |
| 846 | | 912 | |
| 847 | fn parseDie(di: *DwarfInfo, in_stream: anytype, abbrev_table: *const AbbrevTable, is_64: bool) !?Die { | 913 | fn parseDie( |
| | 914 | di: *DwarfInfo, |
| | 915 | allocator: mem.Allocator, |
| | 916 | in_stream: anytype, |
| | 917 | abbrev_table: *const AbbrevTable, |
| | 918 | is_64: bool, |
| | 919 | ) !?Die { |
| 848 | const abbrev_code = try leb.readULEB128(u64, in_stream); | 920 | const abbrev_code = try leb.readULEB128(u64, in_stream); |
| 849 | if (abbrev_code == 0) return null; | 921 | if (abbrev_code == 0) return null; |
| 850 | const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return error.InvalidDebugInfo; | 922 | const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return error.InvalidDebugInfo; |
| 851 | | 923 | |
| 852 | var result = Die{ | 924 | var result = Die{ |
| | 925 | // Lives as long as the Die. |
| | 926 | .arena = std.heap.ArenaAllocator.init(allocator), |
| 853 | .tag_id = table_entry.tag_id, | 927 | .tag_id = table_entry.tag_id, |
| 854 | .has_children = table_entry.has_children, | 928 | .has_children = table_entry.has_children, |
| 855 | .attrs = ArrayList(Die.Attr).init(di.allocator()), | | |
| 856 | }; | 929 | }; |
| 857 | try result.attrs.resize(table_entry.attrs.items.len); | 930 | try result.attrs.resize(allocator, table_entry.attrs.items.len); |
| 858 | for (table_entry.attrs.items) |attr, i| { | 931 | for (table_entry.attrs.items) |attr, i| { |
| 859 | result.attrs.items[i] = Die.Attr{ | 932 | result.attrs.items[i] = Die.Attr{ |
| 860 | .id = attr.attr_id, | 933 | .id = attr.attr_id, |
| 861 | .value = try parseFormValue(di.allocator(), in_stream, attr.form_id, di.endian, is_64), | 934 | .value = try parseFormValue( |
| | 935 | result.arena.allocator(), |
| | 936 | in_stream, |
| | 937 | attr.form_id, |
| | 938 | di.endian, |
| | 939 | is_64, |
| | 940 | ), |
| 862 | }; | 941 | }; |
| 863 | if (attr.form_id == FORM.implicit_const) { | 942 | if (attr.form_id == FORM.implicit_const) { |
| 864 | result.attrs.items[i].value.Const.payload = @bitCast(u64, attr.payload); | 943 | result.attrs.items[i].value.Const.payload = @bitCast(u64, attr.payload); |
| ... | @@ -867,7 +946,12 @@ pub const DwarfInfo = struct { | ... | @@ -867,7 +946,12 @@ pub const DwarfInfo = struct { |
| 867 | return result; | 946 | return result; |
| 868 | } | 947 | } |
| 869 | | 948 | |
| 870 | pub fn getLineNumberInfo(di: *DwarfInfo, compile_unit: CompileUnit, target_address: u64) !debug.LineInfo { | 949 | pub fn getLineNumberInfo( |
| | 950 | di: *DwarfInfo, |
| | 951 | allocator: mem.Allocator, |
| | 952 | compile_unit: CompileUnit, |
| | 953 | target_address: u64, |
| | 954 | ) !debug.LineInfo { |
| 871 | var stream = io.fixedBufferStream(di.debug_line); | 955 | var stream = io.fixedBufferStream(di.debug_line); |
| 872 | const in = &stream.reader(); | 956 | const in = &stream.reader(); |
| 873 | const seekable = &stream.seekableStream(); | 957 | const seekable = &stream.seekableStream(); |
| ... | @@ -906,8 +990,8 @@ pub const DwarfInfo = struct { | ... | @@ -906,8 +990,8 @@ pub const DwarfInfo = struct { |
| 906 | | 990 | |
| 907 | const opcode_base = try in.readByte(); | 991 | const opcode_base = try in.readByte(); |
| 908 | | 992 | |
| 909 | const standard_opcode_lengths = try di.allocator().alloc(u8, opcode_base - 1); | 993 | const standard_opcode_lengths = try allocator.alloc(u8, opcode_base - 1); |
| 910 | defer di.allocator().free(standard_opcode_lengths); | 994 | defer allocator.free(standard_opcode_lengths); |
| 911 | | 995 | |
| 912 | { | 996 | { |
| 913 | var i: usize = 0; | 997 | var i: usize = 0; |
| ... | @@ -916,19 +1000,28 @@ pub const DwarfInfo = struct { | ... | @@ -916,19 +1000,28 @@ pub const DwarfInfo = struct { |
| 916 | } | 1000 | } |
| 917 | } | 1001 | } |
| 918 | | 1002 | |
| 919 | var include_directories = ArrayList([]const u8).init(di.allocator()); | 1003 | var tmp_arena = std.heap.ArenaAllocator.init(allocator); |
| | 1004 | defer tmp_arena.deinit(); |
| | 1005 | const arena = tmp_arena.allocator(); |
| | 1006 | |
| | 1007 | var include_directories = std.ArrayList([]const u8).init(arena); |
| 920 | try include_directories.append(compile_unit_cwd); | 1008 | try include_directories.append(compile_unit_cwd); |
| | 1009 | |
| 921 | while (true) { | 1010 | while (true) { |
| 922 | const dir = try in.readUntilDelimiterAlloc(di.allocator(), 0, math.maxInt(usize)); | 1011 | const dir = try in.readUntilDelimiterAlloc(arena, 0, math.maxInt(usize)); |
| 923 | if (dir.len == 0) break; | 1012 | if (dir.len == 0) break; |
| 924 | try include_directories.append(dir); | 1013 | try include_directories.append(dir); |
| 925 | } | 1014 | } |
| 926 | | 1015 | |
| 927 | var file_entries = ArrayList(FileEntry).init(di.allocator()); | 1016 | var file_entries = std.ArrayList(FileEntry).init(arena); |
| 928 | var prog = LineNumberProgram.init(default_is_stmt, include_directories.items, &file_entries, target_address); | 1017 | var prog = LineNumberProgram.init( |
| | 1018 | default_is_stmt, |
| | 1019 | include_directories.items, |
| | 1020 | target_address, |
| | 1021 | ); |
| 929 | | 1022 | |
| 930 | while (true) { | 1023 | while (true) { |
| 931 | const file_name = try in.readUntilDelimiterAlloc(di.allocator(), 0, math.maxInt(usize)); | 1024 | const file_name = try in.readUntilDelimiterAlloc(arena, 0, math.maxInt(usize)); |
| 932 | if (file_name.len == 0) break; | 1025 | if (file_name.len == 0) break; |
| 933 | const dir_index = try leb.readULEB128(usize, in); | 1026 | const dir_index = try leb.readULEB128(usize, in); |
| 934 | const mtime = try leb.readULEB128(usize, in); | 1027 | const mtime = try leb.readULEB128(usize, in); |
| ... | @@ -955,7 +1048,7 @@ pub const DwarfInfo = struct { | ... | @@ -955,7 +1048,7 @@ pub const DwarfInfo = struct { |
| 955 | switch (sub_op) { | 1048 | switch (sub_op) { |
| 956 | LNE.end_sequence => { | 1049 | LNE.end_sequence => { |
| 957 | prog.end_sequence = true; | 1050 | prog.end_sequence = true; |
| 958 | if (try prog.checkLineMatch()) |info| return info; | 1051 | if (try prog.checkLineMatch(allocator, file_entries.items)) |info| return info; |
| 959 | prog.reset(); | 1052 | prog.reset(); |
| 960 | }, | 1053 | }, |
| 961 | LNE.set_address => { | 1054 | LNE.set_address => { |
| ... | @@ -963,7 +1056,7 @@ pub const DwarfInfo = struct { | ... | @@ -963,7 +1056,7 @@ pub const DwarfInfo = struct { |
| 963 | prog.address = addr; | 1056 | prog.address = addr; |
| 964 | }, | 1057 | }, |
| 965 | LNE.define_file => { | 1058 | LNE.define_file => { |
| 966 | const file_name = try in.readUntilDelimiterAlloc(di.allocator(), 0, math.maxInt(usize)); | 1059 | const file_name = try in.readUntilDelimiterAlloc(arena, 0, math.maxInt(usize)); |
| 967 | const dir_index = try leb.readULEB128(usize, in); | 1060 | const dir_index = try leb.readULEB128(usize, in); |
| 968 | const mtime = try leb.readULEB128(usize, in); | 1061 | const mtime = try leb.readULEB128(usize, in); |
| 969 | const len_bytes = try leb.readULEB128(usize, in); | 1062 | const len_bytes = try leb.readULEB128(usize, in); |
| ... | @@ -986,12 +1079,12 @@ pub const DwarfInfo = struct { | ... | @@ -986,12 +1079,12 @@ pub const DwarfInfo = struct { |
| 986 | const inc_line = @as(i32, line_base) + @as(i32, adjusted_opcode % line_range); | 1079 | const inc_line = @as(i32, line_base) + @as(i32, adjusted_opcode % line_range); |
| 987 | prog.line += inc_line; | 1080 | prog.line += inc_line; |
| 988 | prog.address += inc_addr; | 1081 | prog.address += inc_addr; |
| 989 | if (try prog.checkLineMatch()) |info| return info; | 1082 | if (try prog.checkLineMatch(allocator, file_entries.items)) |info| return info; |
| 990 | prog.basic_block = false; | 1083 | prog.basic_block = false; |
| 991 | } else { | 1084 | } else { |
| 992 | switch (opcode) { | 1085 | switch (opcode) { |
| 993 | LNS.copy => { | 1086 | LNS.copy => { |
| 994 | if (try prog.checkLineMatch()) |info| return info; | 1087 | if (try prog.checkLineMatch(allocator, file_entries.items)) |info| return info; |
| 995 | prog.basic_block = false; | 1088 | prog.basic_block = false; |
| 996 | }, | 1089 | }, |
| 997 | LNS.advance_pc => { | 1090 | LNS.advance_pc => { |
| ... | @@ -1068,13 +1161,8 @@ pub const DwarfInfo = struct { | ... | @@ -1068,13 +1161,8 @@ pub const DwarfInfo = struct { |
| 1068 | }; | 1161 | }; |
| 1069 | | 1162 | |
| 1070 | /// Initialize DWARF info. The caller has the responsibility to initialize most | 1163 | /// Initialize DWARF info. The caller has the responsibility to initialize most |
| 1071 | /// the DwarfInfo fields before calling. These fields can be left undefined: | 1164 | /// the DwarfInfo fields before calling. |
| 1072 | /// * abbrev_table_list | | |
| 1073 | /// * compile_unit_list | | |
| 1074 | pub fn openDwarfDebugInfo(di: *DwarfInfo, allocator: mem.Allocator) !void { | 1165 | pub fn openDwarfDebugInfo(di: *DwarfInfo, allocator: mem.Allocator) !void { |
| 1075 | di.abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator); | 1166 | try di.scanAllFunctions(allocator); |
| 1076 | di.compile_unit_list = ArrayList(CompileUnit).init(allocator); | 1167 | try di.scanAllCompileUnits(allocator); |
| 1077 | di.func_list = ArrayList(Func).init(allocator); | | |
| 1078 | try di.scanAllFunctions(); | | |
| 1079 | try di.scanAllCompileUnits(); | | |
| 1080 | } | 1168 | } |