| ... | @@ -27,7 +27,6 @@ header: ?macho.mach_header_64 = null, | ... | @@ -27,7 +27,6 @@ header: ?macho.mach_header_64 = null, |
| 27 | file: ?fs.File = null, | 27 | file: ?fs.File = null, |
| 28 | file_offset: ?u32 = null, | 28 | file_offset: ?u32 = null, |
| 29 | name: ?[]const u8 = null, | 29 | name: ?[]const u8 = null, |
| 30 | mtime: ?u64 = null, | | |
| 31 | | 30 | |
| 32 | load_commands: std.ArrayListUnmanaged(LoadCommand) = .{}, | 31 | load_commands: std.ArrayListUnmanaged(LoadCommand) = .{}, |
| 33 | | 32 | |
| ... | @@ -51,9 +50,17 @@ symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{}, | ... | @@ -51,9 +50,17 @@ symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{}, |
| 51 | strtab: std.ArrayListUnmanaged(u8) = .{}, | 50 | strtab: std.ArrayListUnmanaged(u8) = .{}, |
| 52 | data_in_code_entries: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{}, | 51 | data_in_code_entries: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{}, |
| 53 | | 52 | |
| | 53 | // Debug info |
| | 54 | debug_info: ?DebugInfo = null, |
| | 55 | tu_name: ?[]const u8 = null, |
| | 56 | tu_comp_dir: ?[]const u8 = null, |
| | 57 | mtime: ?u64 = null, |
| | 58 | |
| 54 | symbols: std.ArrayListUnmanaged(*Symbol) = .{}, | 59 | symbols: std.ArrayListUnmanaged(*Symbol) = .{}, |
| 55 | sections_as_symbols: std.AutoHashMapUnmanaged(u8, *Symbol) = .{}, | 60 | sections_as_symbols: std.AutoHashMapUnmanaged(u8, *Symbol) = .{}, |
| 56 | | 61 | |
| | 62 | text_blocks: std.ArrayListUnmanaged(*TextBlock) = .{}, |
| | 63 | |
| 57 | const DebugInfo = struct { | 64 | const DebugInfo = struct { |
| 58 | inner: dwarf.DwarfInfo, | 65 | inner: dwarf.DwarfInfo, |
| 59 | debug_info: []u8, | 66 | debug_info: []u8, |
| ... | @@ -160,6 +167,19 @@ pub fn deinit(self: *Object) void { | ... | @@ -160,6 +167,19 @@ pub fn deinit(self: *Object) void { |
| 160 | self.strtab.deinit(self.allocator); | 167 | self.strtab.deinit(self.allocator); |
| 161 | self.symbols.deinit(self.allocator); | 168 | self.symbols.deinit(self.allocator); |
| 162 | self.sections_as_symbols.deinit(self.allocator); | 169 | self.sections_as_symbols.deinit(self.allocator); |
| | 170 | self.text_blocks.deinit(self.allocator); |
| | 171 | |
| | 172 | if (self.debug_info) |*db| { |
| | 173 | db.deinit(self.allocator); |
| | 174 | } |
| | 175 | |
| | 176 | if (self.tu_name) |n| { |
| | 177 | self.allocator.free(n); |
| | 178 | } |
| | 179 | |
| | 180 | if (self.tu_comp_dir) |n| { |
| | 181 | self.allocator.free(n); |
| | 182 | } |
| 163 | | 183 | |
| 164 | if (self.name) |n| { | 184 | if (self.name) |n| { |
| 165 | self.allocator.free(n); | 185 | self.allocator.free(n); |
| ... | @@ -203,6 +223,7 @@ pub fn parse(self: *Object) !void { | ... | @@ -203,6 +223,7 @@ pub fn parse(self: *Object) !void { |
| 203 | try self.readLoadCommands(reader); | 223 | try self.readLoadCommands(reader); |
| 204 | try self.parseSymtab(); | 224 | try self.parseSymtab(); |
| 205 | try self.parseDataInCode(); | 225 | try self.parseDataInCode(); |
| | 226 | try self.parseDebugInfo(); |
| 206 | } | 227 | } |
| 207 | | 228 | |
| 208 | pub fn readLoadCommands(self: *Object, reader: anytype) !void { | 229 | pub fn readLoadCommands(self: *Object, reader: anytype) !void { |
| ... | @@ -431,11 +452,27 @@ const TextBlockParser = struct { | ... | @@ -431,11 +452,27 @@ const TextBlockParser = struct { |
| 431 | else | 452 | else |
| 432 | max_align; | 453 | max_align; |
| 433 | | 454 | |
| | 455 | const stab: ?TextBlock.Stab = if (self.object.debug_info) |di| blk: { |
| | 456 | // TODO there has to be a better to handle this. |
| | 457 | for (di.inner.func_list.items) |func| { |
| | 458 | if (func.pc_range) |range| { |
| | 459 | if (senior_nlist.nlist.n_value >= range.start and senior_nlist.nlist.n_value < range.end) { |
| | 460 | break :blk TextBlock.Stab{ |
| | 461 | .function = range.end - range.start, |
| | 462 | }; |
| | 463 | } |
| | 464 | } |
| | 465 | } |
| | 466 | if (self.zld.globals.contains(senior_sym.name)) break :blk .global; |
| | 467 | break :blk .static; |
| | 468 | } else null; |
| | 469 | |
| 434 | const block = try self.allocator.create(TextBlock); | 470 | const block = try self.allocator.create(TextBlock); |
| 435 | errdefer self.allocator.destroy(block); | 471 | errdefer self.allocator.destroy(block); |
| 436 | | 472 | |
| 437 | block.* = TextBlock.init(self.allocator); | 473 | block.* = TextBlock.init(self.allocator); |
| 438 | block.local_sym_index = senior_nlist.index; | 474 | block.local_sym_index = senior_nlist.index; |
| | 475 | block.stab = stab; |
| 439 | block.code = try self.allocator.dupe(u8, code); | 476 | block.code = try self.allocator.dupe(u8, code); |
| 440 | block.size = size; | 477 | block.size = size; |
| 441 | block.alignment = actual_align; | 478 | block.alignment = actual_align; |
| ... | @@ -531,9 +568,11 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void { | ... | @@ -531,9 +568,11 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void { |
| 531 | | 568 | |
| 532 | // Is there any padding between symbols within the section? | 569 | // Is there any padding between symbols within the section? |
| 533 | const is_splittable = self.header.?.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0; | 570 | const is_splittable = self.header.?.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0; |
| | 571 | // TODO is it perhaps worth skip parsing subsections in Debug mode and not worry about |
| | 572 | // duplicates at all? Need some benchmarks! |
| 534 | // const is_splittable = false; | 573 | // const is_splittable = false; |
| 535 | | 574 | |
| 536 | const has_dices: bool = blk: { | 575 | zld.has_dices = blk: { |
| 537 | if (self.text_section_index) |index| { | 576 | if (self.text_section_index) |index| { |
| 538 | if (index != id) break :blk false; | 577 | if (index != id) break :blk false; |
| 539 | if (self.data_in_code_entries.items.len == 0) break :blk false; | 578 | if (self.data_in_code_entries.items.len == 0) break :blk false; |
| ... | @@ -541,7 +580,7 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void { | ... | @@ -541,7 +580,7 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void { |
| 541 | } | 580 | } |
| 542 | break :blk false; | 581 | break :blk false; |
| 543 | }; | 582 | }; |
| 544 | zld.has_dices = has_dices; | 583 | zld.has_stabs = zld.has_stabs or self.debug_info != null; |
| 545 | | 584 | |
| 546 | next: { | 585 | next: { |
| 547 | if (is_splittable) blocks: { | 586 | if (is_splittable) blocks: { |
| ... | @@ -625,6 +664,8 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void { | ... | @@ -625,6 +664,8 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void { |
| 625 | } else { | 664 | } else { |
| 626 | try zld.blocks.putNoClobber(zld.allocator, match, block); | 665 | try zld.blocks.putNoClobber(zld.allocator, match, block); |
| 627 | } | 666 | } |
| | 667 | |
| | 668 | try self.text_blocks.append(self.allocator, block); |
| 628 | } | 669 | } |
| 629 | | 670 | |
| 630 | var parser = TextBlockParser{ | 671 | var parser = TextBlockParser{ |
| ... | @@ -681,6 +722,8 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void { | ... | @@ -681,6 +722,8 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void { |
| 681 | } else { | 722 | } else { |
| 682 | try zld.blocks.putNoClobber(zld.allocator, match, block); | 723 | try zld.blocks.putNoClobber(zld.allocator, match, block); |
| 683 | } | 724 | } |
| | 725 | |
| | 726 | try self.text_blocks.append(self.allocator, block); |
| 684 | } | 727 | } |
| 685 | | 728 | |
| 686 | break :next; | 729 | break :next; |
| ... | @@ -758,9 +801,25 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void { | ... | @@ -758,9 +801,25 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void { |
| 758 | reg.segment_id = match.seg; | 801 | reg.segment_id = match.seg; |
| 759 | reg.section_id = match.sect; | 802 | reg.section_id = match.sect; |
| 760 | | 803 | |
| | 804 | const stab: ?TextBlock.Stab = if (self.debug_info) |di| blk: { |
| | 805 | // TODO there has to be a better to handle this. |
| | 806 | for (di.inner.func_list.items) |func| { |
| | 807 | if (func.pc_range) |range| { |
| | 808 | if (reg.address >= range.start and reg.address < range.end) { |
| | 809 | break :blk TextBlock.Stab{ |
| | 810 | .function = range.end - range.start, |
| | 811 | }; |
| | 812 | } |
| | 813 | } |
| | 814 | } |
| | 815 | if (zld.globals.contains(sym.name)) break :blk .global; |
| | 816 | break :blk .static; |
| | 817 | } else null; |
| | 818 | |
| 761 | contained.appendAssumeCapacity(.{ | 819 | contained.appendAssumeCapacity(.{ |
| 762 | .local_sym_index = reg.local_sym_index, | 820 | .local_sym_index = reg.local_sym_index, |
| 763 | .offset = nlist_with_index.nlist.n_value - sect.addr, | 821 | .offset = nlist_with_index.nlist.n_value - sect.addr, |
| | 822 | .stab = stab, |
| 764 | }); | 823 | }); |
| 765 | } | 824 | } |
| 766 | | 825 | |
| ... | @@ -785,6 +844,8 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void { | ... | @@ -785,6 +844,8 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void { |
| 785 | } else { | 844 | } else { |
| 786 | try zld.blocks.putNoClobber(zld.allocator, match, block); | 845 | try zld.blocks.putNoClobber(zld.allocator, match, block); |
| 787 | } | 846 | } |
| | 847 | |
| | 848 | try self.text_blocks.append(self.allocator, block); |
| 788 | } | 849 | } |
| 789 | } | 850 | } |
| 790 | } | 851 | } |
| ... | @@ -861,13 +922,12 @@ fn parseSymtab(self: *Object) !void { | ... | @@ -861,13 +922,12 @@ fn parseSymtab(self: *Object) !void { |
| 861 | } | 922 | } |
| 862 | | 923 | |
| 863 | pub fn parseDebugInfo(self: *Object) !void { | 924 | pub fn parseDebugInfo(self: *Object) !void { |
| | 925 | log.debug("parsing debug info in '{s}'", .{self.name.?}); |
| | 926 | |
| 864 | var debug_info = blk: { | 927 | var debug_info = blk: { |
| 865 | var di = try DebugInfo.parseFromObject(self.allocator, self); | 928 | var di = try DebugInfo.parseFromObject(self.allocator, self); |
| 866 | break :blk di orelse return; | 929 | break :blk di orelse return; |
| 867 | }; | 930 | }; |
| 868 | defer debug_info.deinit(self.allocator); | | |
| 869 | | | |
| 870 | log.debug("parsing debug info in '{s}'", .{self.name.?}); | | |
| 871 | | 931 | |
| 872 | // We assume there is only one CU. | 932 | // We assume there is only one CU. |
| 873 | const compile_unit = debug_info.inner.findCompileUnit(0x0) catch |err| switch (err) { | 933 | const compile_unit = debug_info.inner.findCompileUnit(0x0) catch |err| switch (err) { |
| ... | @@ -881,6 +941,10 @@ pub fn parseDebugInfo(self: *Object) !void { | ... | @@ -881,6 +941,10 @@ pub fn parseDebugInfo(self: *Object) !void { |
| 881 | const name = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT_name); | 941 | const name = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT_name); |
| 882 | const comp_dir = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT_comp_dir); | 942 | const comp_dir = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT_comp_dir); |
| 883 | | 943 | |
| | 944 | self.debug_info = debug_info; |
| | 945 | self.tu_name = try self.allocator.dupe(u8, name); |
| | 946 | self.tu_comp_dir = try self.allocator.dupe(u8, comp_dir); |
| | 947 | |
| 884 | if (self.mtime == null) { | 948 | if (self.mtime == null) { |
| 885 | self.mtime = mtime: { | 949 | self.mtime = mtime: { |
| 886 | const file = self.file orelse break :mtime 0; | 950 | const file = self.file orelse break :mtime 0; |
| ... | @@ -888,67 +952,6 @@ pub fn parseDebugInfo(self: *Object) !void { | ... | @@ -888,67 +952,6 @@ pub fn parseDebugInfo(self: *Object) !void { |
| 888 | break :mtime @intCast(u64, @divFloor(stat.mtime, 1_000_000_000)); | 952 | break :mtime @intCast(u64, @divFloor(stat.mtime, 1_000_000_000)); |
| 889 | }; | 953 | }; |
| 890 | } | 954 | } |
| 891 | | | |
| 892 | try self.stabs.ensureUnusedCapacity(self.allocator, self.symbols.items.len + 4); | | |
| 893 | | | |
| 894 | // Current dir | | |
| 895 | self.stabs.appendAssumeCapacity(try Symbol.Stab.new(self.allocator, comp_dir, .{ | | |
| 896 | .kind = .so, | | |
| 897 | .file = self, | | |
| 898 | })); | | |
| 899 | | | |
| 900 | // Artifact name | | |
| 901 | self.stabs.appendAssumeCapacity(try Symbol.Stab.new(self.allocator, name, .{ | | |
| 902 | .kind = .so, | | |
| 903 | .file = self, | | |
| 904 | })); | | |
| 905 | | | |
| 906 | // Path to object file with debug info | | |
| 907 | self.stabs.appendAssumeCapacity(try Symbol.Stab.new(self.allocator, self.name.?, .{ | | |
| 908 | .kind = .oso, | | |
| 909 | .file = self, | | |
| 910 | })); | | |
| 911 | | | |
| 912 | for (self.symbols.items) |sym| { | | |
| 913 | if (sym.cast(Symbol.Regular)) |reg| { | | |
| 914 | const size: u64 = blk: for (debug_info.inner.func_list.items) |func| { | | |
| 915 | if (func.pc_range) |range| { | | |
| 916 | if (reg.address >= range.start and reg.address < range.end) { | | |
| 917 | break :blk range.end - range.start; | | |
| 918 | } | | |
| 919 | } | | |
| 920 | } else 0; | | |
| 921 | | | |
| 922 | const stab = try Symbol.Stab.new(self.allocator, sym.name, .{ | | |
| 923 | .kind = kind: { | | |
| 924 | if (size > 0) break :kind .function; | | |
| 925 | switch (reg.linkage) { | | |
| 926 | .translation_unit => break :kind .static, | | |
| 927 | else => break :kind .global, | | |
| 928 | } | | |
| 929 | }, | | |
| 930 | .size = size, | | |
| 931 | .symbol = sym, | | |
| 932 | .file = self, | | |
| 933 | }); | | |
| 934 | self.stabs.appendAssumeCapacity(stab); | | |
| 935 | } else if (sym.cast(Symbol.Tentative)) |_| { | | |
| 936 | const stab = try Symbol.Stab.new(self.allocator, sym.name, .{ | | |
| 937 | .kind = .global, | | |
| 938 | .size = 0, | | |
| 939 | .symbol = sym, | | |
| 940 | .file = self, | | |
| 941 | }); | | |
| 942 | self.stabs.appendAssumeCapacity(stab); | | |
| 943 | } | | |
| 944 | } | | |
| 945 | | | |
| 946 | // Closing delimiter. | | |
| 947 | const delim_stab = try Symbol.Stab.new(self.allocator, "", .{ | | |
| 948 | .kind = .so, | | |
| 949 | .file = self, | | |
| 950 | }); | | |
| 951 | self.stabs.appendAssumeCapacity(delim_stab); | | |
| 952 | } | 955 | } |
| 953 | | 956 | |
| 954 | pub fn parseDataInCode(self: *Object) !void { | 957 | pub fn parseDataInCode(self: *Object) !void { |