authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-07-13 18:42:17+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-07-15 18:49:47+02:00
log398672eb30dce08bd3370cde7adeb503c64a4892
tree74dbe042641fcf5245ec9b4722eb22cfc5a5d943
parente17f12dd643e9edd90abb66b183d2a59eddc248c

zld: add temp basic handling of debugging stabs


2 files changed, 214 insertions(+), 71 deletions(-)

src/link/MachO/Object.zig+70-67
...@@ -27,7 +27,6 @@ header: ?macho.mach_header_64 = null,...@@ -27,7 +27,6 @@ header: ?macho.mach_header_64 = null,
27file: ?fs.File = null,27file: ?fs.File = null,
28file_offset: ?u32 = null,28file_offset: ?u32 = null,
29name: ?[]const u8 = null,29name: ?[]const u8 = null,
30mtime: ?u64 = null,
3130
32load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},31load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
3332
...@@ -51,9 +50,17 @@ symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{},...@@ -51,9 +50,17 @@ symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{},
51strtab: std.ArrayListUnmanaged(u8) = .{},50strtab: std.ArrayListUnmanaged(u8) = .{},
52data_in_code_entries: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{},51data_in_code_entries: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{},
5352
53// Debug info
54debug_info: ?DebugInfo = null,
55tu_name: ?[]const u8 = null,
56tu_comp_dir: ?[]const u8 = null,
57mtime: ?u64 = null,
58
54symbols: std.ArrayListUnmanaged(*Symbol) = .{},59symbols: std.ArrayListUnmanaged(*Symbol) = .{},
55sections_as_symbols: std.AutoHashMapUnmanaged(u8, *Symbol) = .{},60sections_as_symbols: std.AutoHashMapUnmanaged(u8, *Symbol) = .{},
5661
62text_blocks: std.ArrayListUnmanaged(*TextBlock) = .{},
63
57const DebugInfo = struct {64const 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 }
163183
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}
207228
208pub fn readLoadCommands(self: *Object, reader: anytype) !void {229pub fn readLoadCommands(self: *Object, reader: anytype) !void {
...@@ -431,11 +452,27 @@ const TextBlockParser = struct {...@@ -431,11 +452,27 @@ const TextBlockParser = struct {
431 else452 else
432 max_align;453 max_align;
433454
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);
436472
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 {
531568
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;
535574
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;
545584
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 }
629670
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 }
685728
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;
760803
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 }
766825
...@@ -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}
862923
863pub fn parseDebugInfo(self: *Object) !void {924pub 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.?});
871931
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);
883943
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}
953956
954pub fn parseDataInCode(self: *Object) !void {957pub fn parseDataInCode(self: *Object) !void {
src/link/MachO/Zld.zig+144-4
...@@ -115,6 +115,7 @@ stub_helper_stubs_start_off: ?u64 = null,...@@ -115,6 +115,7 @@ stub_helper_stubs_start_off: ?u64 = null,
115blocks: std.AutoHashMapUnmanaged(MatchingSection, *TextBlock) = .{},115blocks: std.AutoHashMapUnmanaged(MatchingSection, *TextBlock) = .{},
116116
117has_dices: bool = false,117has_dices: bool = false,
118has_stabs: bool = false,
118119
119pub const Output = struct {120pub const Output = struct {
120 tag: enum { exe, dylib },121 tag: enum { exe, dylib },
...@@ -125,6 +126,7 @@ pub const Output = struct {...@@ -125,6 +126,7 @@ pub const Output = struct {
125pub const TextBlock = struct {126pub const TextBlock = struct {
126 allocator: *Allocator,127 allocator: *Allocator,
127 local_sym_index: u32,128 local_sym_index: u32,
129 stab: ?Stab = null,
128 aliases: std.ArrayList(u32),130 aliases: std.ArrayList(u32),
129 references: std.AutoArrayHashMap(u32, void),131 references: std.AutoArrayHashMap(u32, void),
130 contained: ?[]SymbolAtOffset = null,132 contained: ?[]SymbolAtOffset = null,
...@@ -140,6 +142,76 @@ pub const TextBlock = struct {...@@ -140,6 +142,76 @@ pub const TextBlock = struct {
140 pub const SymbolAtOffset = struct {142 pub const SymbolAtOffset = struct {
141 local_sym_index: u32,143 local_sym_index: u32,
142 offset: u64,144 offset: u64,
145 stab: ?Stab = null,
146 };
147
148 pub const Stab = union(enum) {
149 function: u64,
150 static,
151 global,
152
153 pub fn asNlists(stab: Stab, local_sym_index: u32, zld: *Zld) ![]macho.nlist_64 {
154 var nlists = std.ArrayList(macho.nlist_64).init(zld.allocator);
155 defer nlists.deinit();
156
157 const sym = zld.locals.items[local_sym_index];
158 const reg = sym.payload.regular;
159
160 switch (stab) {
161 .function => |size| {
162 try nlists.ensureUnusedCapacity(4);
163 const section_id = reg.sectionId(zld);
164 nlists.appendAssumeCapacity(.{
165 .n_strx = 0,
166 .n_type = macho.N_BNSYM,
167 .n_sect = section_id,
168 .n_desc = 0,
169 .n_value = reg.address,
170 });
171 nlists.appendAssumeCapacity(.{
172 .n_strx = try zld.strtab.getOrPut(sym.name),
173 .n_type = macho.N_FUN,
174 .n_sect = section_id,
175 .n_desc = 0,
176 .n_value = reg.address,
177 });
178 nlists.appendAssumeCapacity(.{
179 .n_strx = 0,
180 .n_type = macho.N_FUN,
181 .n_sect = 0,
182 .n_desc = 0,
183 .n_value = size,
184 });
185 nlists.appendAssumeCapacity(.{
186 .n_strx = 0,
187 .n_type = macho.N_ENSYM,
188 .n_sect = section_id,
189 .n_desc = 0,
190 .n_value = size,
191 });
192 },
193 .global => {
194 try nlists.append(.{
195 .n_strx = try zld.strtab.getOrPut(sym.name),
196 .n_type = macho.N_GSYM,
197 .n_sect = 0,
198 .n_desc = 0,
199 .n_value = 0,
200 });
201 },
202 .static => {
203 try nlists.append(.{
204 .n_strx = try zld.strtab.getOrPut(sym.name),
205 .n_type = macho.N_STSYM,
206 .n_sect = reg.sectionId(zld),
207 .n_desc = 0,
208 .n_value = reg.address,
209 });
210 },
211 }
212
213 return nlists.toOwnedSlice();
214 }
143 };215 };
144216
145 pub fn init(allocator: *Allocator) TextBlock {217 pub fn init(allocator: *Allocator) TextBlock {
...@@ -178,6 +250,9 @@ pub const TextBlock = struct {...@@ -178,6 +250,9 @@ pub const TextBlock = struct {
178 pub fn print_this(self: *const TextBlock, zld: *Zld) void {250 pub fn print_this(self: *const TextBlock, zld: *Zld) void {
179 log.warn("TextBlock", .{});251 log.warn("TextBlock", .{});
180 log.warn(" {}: {}", .{ self.local_sym_index, zld.locals.items[self.local_sym_index] });252 log.warn(" {}: {}", .{ self.local_sym_index, zld.locals.items[self.local_sym_index] });
253 if (self.stab) |stab| {
254 log.warn(" stab: {}", .{stab});
255 }
181 if (self.aliases.items.len > 0) {256 if (self.aliases.items.len > 0) {
182 log.warn(" aliases:", .{});257 log.warn(" aliases:", .{});
183 for (self.aliases.items) |index| {258 for (self.aliases.items) |index| {
...@@ -193,10 +268,18 @@ pub const TextBlock = struct {...@@ -193,10 +268,18 @@ pub const TextBlock = struct {
193 if (self.contained) |contained| {268 if (self.contained) |contained| {
194 log.warn(" contained symbols:", .{});269 log.warn(" contained symbols:", .{});
195 for (contained) |sym_at_off| {270 for (contained) |sym_at_off| {
196 log.warn(" {}: {}\n", .{271 if (sym_at_off.stab) |stab| {
197 sym_at_off.offset,272 log.warn(" {}: {}, stab: {}\n", .{
198 zld.locals.items[sym_at_off.local_sym_index],273 sym_at_off.offset,
199 });274 zld.locals.items[sym_at_off.local_sym_index],
275 stab,
276 });
277 } else {
278 log.warn(" {}: {}\n", .{
279 sym_at_off.offset,
280 zld.locals.items[sym_at_off.local_sym_index],
281 });
282 }
200 }283 }
201 }284 }
202 log.warn(" code.len = {}", .{self.code.len});285 log.warn(" code.len = {}", .{self.code.len});
...@@ -2487,8 +2570,10 @@ fn writeSymbolTable(self: *Zld) !void {...@@ -2487,8 +2570,10 @@ fn writeSymbolTable(self: *Zld) !void {
2487 for (self.locals.items) |symbol, i| {2570 for (self.locals.items) |symbol, i| {
2488 if (i == 0) continue; // skip null symbol2571 if (i == 0) continue; // skip null symbol
2489 if (symbol.isTemp()) continue; // TODO when merging codepaths, this should go into freelist2572 if (symbol.isTemp()) continue; // TODO when merging codepaths, this should go into freelist
2573
2490 const reg = symbol.payload.regular;2574 const reg = symbol.payload.regular;
2491 const nlist = try symbol.asNlist(self, &self.strtab);2575 const nlist = try symbol.asNlist(self, &self.strtab);
2576
2492 if (reg.linkage == .translation_unit) {2577 if (reg.linkage == .translation_unit) {
2493 try locals.append(nlist);2578 try locals.append(nlist);
2494 } else {2579 } else {
...@@ -2496,6 +2581,61 @@ fn writeSymbolTable(self: *Zld) !void {...@@ -2496,6 +2581,61 @@ fn writeSymbolTable(self: *Zld) !void {
2496 }2581 }
2497 }2582 }
24982583
2584 if (self.has_stabs) {
2585 for (self.objects.items) |object| {
2586 if (object.debug_info == null) continue;
2587
2588 // Open scope
2589 try locals.ensureUnusedCapacity(4);
2590 locals.appendAssumeCapacity(.{
2591 .n_strx = try self.strtab.getOrPut(object.tu_comp_dir.?),
2592 .n_type = macho.N_SO,
2593 .n_sect = 0,
2594 .n_desc = 0,
2595 .n_value = 0,
2596 });
2597 locals.appendAssumeCapacity(.{
2598 .n_strx = try self.strtab.getOrPut(object.tu_name.?),
2599 .n_type = macho.N_SO,
2600 .n_sect = 0,
2601 .n_desc = 0,
2602 .n_value = 0,
2603 });
2604 locals.appendAssumeCapacity(.{
2605 .n_strx = try self.strtab.getOrPut(object.name.?),
2606 .n_type = macho.N_OSO,
2607 .n_sect = 0,
2608 .n_desc = 1,
2609 .n_value = object.mtime orelse 0,
2610 });
2611
2612 for (object.text_blocks.items) |block| {
2613 if (block.stab) |stab| {
2614 const nlists = try stab.asNlists(block.local_sym_index, self);
2615 defer self.allocator.free(nlists);
2616 try locals.appendSlice(nlists);
2617 } else {
2618 const contained = block.contained orelse continue;
2619 for (contained) |sym_at_off| {
2620 const stab = sym_at_off.stab orelse continue;
2621 const nlists = try stab.asNlists(sym_at_off.local_sym_index, self);
2622 defer self.allocator.free(nlists);
2623 try locals.appendSlice(nlists);
2624 }
2625 }
2626 }
2627
2628 // Close scope
2629 locals.appendAssumeCapacity(.{
2630 .n_strx = 0,
2631 .n_type = macho.N_SO,
2632 .n_sect = 0,
2633 .n_desc = 0,
2634 .n_value = 0,
2635 });
2636 }
2637 }
2638
2499 var undefs = std.ArrayList(macho.nlist_64).init(self.allocator);2639 var undefs = std.ArrayList(macho.nlist_64).init(self.allocator);
2500 defer undefs.deinit();2640 defer undefs.deinit();
2501 var undef_dir = std.StringHashMap(u32).init(self.allocator);2641 var undef_dir = std.StringHashMap(u32).init(self.allocator);