authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-04-08 16:31:04+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-04-13 10:56:03+02:00
log421102ba724370d04f14016006141a51c0f3e296
tree0db8c0dbf687586c3f8ad725324a04e3b12bcc21
parentdc7cff8ebc5f00c784647c0bd1b40dec70a5008f

zld: move parsing debug info into object


2 files changed, 205 insertions(+), 166 deletions(-)

src/link/MachO/Object.zig+135
......@@ -2,6 +2,7 @@ const Object = @This();
22
33const std = @import("std");
44const assert = std.debug.assert;
5const dwarf = std.dwarf;
56const fs = std.fs;
67const io = std.io;
78const log = std.log.scoped(.object);
......@@ -43,6 +44,10 @@ dwarf_debug_ranges_index: ?u16 = null,
4344symtab: std.ArrayListUnmanaged(Symbol) = .{},
4445strtab: std.ArrayListUnmanaged(u8) = .{},
4546
47stabs: std.ArrayListUnmanaged(Stab) = .{},
48tu_path: ?[]const u8 = null,
49tu_mtime: ?u64 = null,
50
4651data_in_code_entries: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{},
4752
4853pub const Section = struct {
......@@ -62,6 +67,82 @@ pub const Section = struct {
6267 }
6368};
6469
70const Stab = struct {
71 tag: Tag,
72 symbol: u32,
73 size: ?u64 = null,
74
75 const Tag = enum {
76 function,
77 global,
78 static,
79 };
80};
81
82const DebugInfo = struct {
83 inner: dwarf.DwarfInfo,
84 debug_info: []u8,
85 debug_abbrev: []u8,
86 debug_str: []u8,
87 debug_line: []u8,
88 debug_ranges: []u8,
89
90 pub fn parseFromObject(allocator: *Allocator, object: *const Object) !?DebugInfo {
91 var debug_info = blk: {
92 const index = object.dwarf_debug_info_index orelse return null;
93 break :blk try object.readSection(allocator, index);
94 };
95 var debug_abbrev = blk: {
96 const index = object.dwarf_debug_abbrev_index orelse return null;
97 break :blk try object.readSection(allocator, index);
98 };
99 var debug_str = blk: {
100 const index = object.dwarf_debug_str_index orelse return null;
101 break :blk try object.readSection(allocator, index);
102 };
103 var debug_line = blk: {
104 const index = object.dwarf_debug_line_index orelse return null;
105 break :blk try object.readSection(allocator, index);
106 };
107 var debug_ranges = blk: {
108 if (object.dwarf_debug_ranges_index) |ind| {
109 break :blk try object.readSection(allocator, ind);
110 }
111 break :blk try allocator.alloc(u8, 0);
112 };
113
114 var inner: dwarf.DwarfInfo = .{
115 .endian = .Little,
116 .debug_info = debug_info,
117 .debug_abbrev = debug_abbrev,
118 .debug_str = debug_str,
119 .debug_line = debug_line,
120 .debug_ranges = debug_ranges,
121 };
122 try dwarf.openDwarfDebugInfo(&inner, allocator);
123
124 return DebugInfo{
125 .inner = inner,
126 .debug_info = debug_info,
127 .debug_abbrev = debug_abbrev,
128 .debug_str = debug_str,
129 .debug_line = debug_line,
130 .debug_ranges = debug_ranges,
131 };
132 }
133
134 pub fn deinit(self: *DebugInfo, allocator: *Allocator) void {
135 allocator.free(self.debug_info);
136 allocator.free(self.debug_abbrev);
137 allocator.free(self.debug_str);
138 allocator.free(self.debug_line);
139 allocator.free(self.debug_ranges);
140 self.inner.abbrev_table_list.deinit();
141 self.inner.compile_unit_list.deinit();
142 self.inner.func_list.deinit();
143 }
144};
145
65146pub fn init(allocator: *Allocator) Object {
66147 return .{
67148 .allocator = allocator,
......@@ -81,11 +162,16 @@ pub fn deinit(self: *Object) void {
81162
82163 self.symtab.deinit(self.allocator);
83164 self.strtab.deinit(self.allocator);
165 self.stabs.deinit(self.allocator);
84166 self.data_in_code_entries.deinit(self.allocator);
85167
86168 if (self.name) |n| {
87169 self.allocator.free(n);
88170 }
171
172 if (self.tu_path) |tu_path| {
173 self.allocator.free(tu_path);
174 }
89175}
90176
91177pub fn closeFile(self: Object) void {
......@@ -124,6 +210,7 @@ pub fn parse(self: *Object) !void {
124210 try self.parseSections();
125211 if (self.symtab_cmd_index != null) try self.parseSymtab();
126212 if (self.data_in_code_cmd_index != null) try self.readDataInCode();
213 try self.parseDebugInfo();
127214}
128215
129216pub fn readLoadCommands(self: *Object, reader: anytype) !void {
......@@ -271,6 +358,54 @@ pub fn parseSymtab(self: *Object) !void {
271358 try self.strtab.appendSlice(self.allocator, strtab);
272359}
273360
361pub fn parseDebugInfo(self: *Object) !void {
362 var debug_info = blk: {
363 var di = try DebugInfo.parseFromObject(self.allocator, self);
364 break :blk di orelse return;
365 };
366 defer debug_info.deinit(self.allocator);
367
368 log.warn("parsing debug info in '{s}'", .{self.name.?});
369
370 // We assume there is only one CU.
371 const compile_unit = debug_info.inner.findCompileUnit(0x0) catch |err| switch (err) {
372 error.MissingDebugInfo => {
373 // TODO audit cases with missing debug info and audit our dwarf.zig module.
374 log.warn("invalid or missing debug info in {s}; skipping", .{self.name.?});
375 return;
376 },
377 else => |e| return e,
378 };
379 const name = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT_name);
380 const comp_dir = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT_comp_dir);
381
382 self.tu_path = try std.fs.path.join(self.allocator, &[_][]const u8{ comp_dir, name });
383 self.tu_mtime = mtime: {
384 var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
385 const stat = try self.file.?.stat();
386 break :mtime @intCast(u64, @divFloor(stat.mtime, 1_000_000_000));
387 };
388
389 for (self.symtab.items) |sym, index| {
390 const sym_name = self.getString(sym.inner.n_strx);
391 const size = blk: for (debug_info.inner.func_list.items) |func| {
392 if (func.pc_range) |range| {
393 if (sym.inner.n_value >= range.start and sym.inner.n_value < range.end) {
394 break :blk range.end - range.start;
395 }
396 }
397 } else null;
398
399 // TODO How do we work out static, global, local?
400 const tag: Stab.Tag = if (size == null) .global else .function;
401 try self.stabs.append(self.allocator, .{
402 .tag = tag,
403 .size = size,
404 .symbol = @intCast(u32, index),
405 });
406 }
407}
408
274409pub fn getString(self: *const Object, str_off: u32) []const u8 {
275410 assert(str_off < self.strtab.items.len);
276411 return mem.spanZ(@ptrCast([*:0]const u8, self.strtab.items.ptr + str_off));
src/link/MachO/Zld.zig+70-166
......@@ -2,7 +2,6 @@ const Zld = @This();
22
33const std = @import("std");
44const assert = std.debug.assert;
5const dwarf = std.dwarf;
65const leb = std.leb;
76const mem = std.mem;
87const meta = std.meta;
......@@ -110,70 +109,6 @@ pub const SectionMapping = struct {
110109 offset: u32,
111110};
112111
113const DebugInfo = struct {
114 inner: dwarf.DwarfInfo,
115 debug_info: []u8,
116 debug_abbrev: []u8,
117 debug_str: []u8,
118 debug_line: []u8,
119 debug_ranges: []u8,
120
121 pub fn parseFromObject(allocator: *Allocator, object: Object) !?DebugInfo {
122 var debug_info = blk: {
123 const index = object.dwarf_debug_info_index orelse return null;
124 break :blk try object.readSection(allocator, index);
125 };
126 var debug_abbrev = blk: {
127 const index = object.dwarf_debug_abbrev_index orelse return null;
128 break :blk try object.readSection(allocator, index);
129 };
130 var debug_str = blk: {
131 const index = object.dwarf_debug_str_index orelse return null;
132 break :blk try object.readSection(allocator, index);
133 };
134 var debug_line = blk: {
135 const index = object.dwarf_debug_line_index orelse return null;
136 break :blk try object.readSection(allocator, index);
137 };
138 var debug_ranges = blk: {
139 if (object.dwarf_debug_ranges_index) |ind| {
140 break :blk try object.readSection(allocator, ind);
141 }
142 break :blk try allocator.alloc(u8, 0);
143 };
144
145 var inner: dwarf.DwarfInfo = .{
146 .endian = .Little,
147 .debug_info = debug_info,
148 .debug_abbrev = debug_abbrev,
149 .debug_str = debug_str,
150 .debug_line = debug_line,
151 .debug_ranges = debug_ranges,
152 };
153 try dwarf.openDwarfDebugInfo(&inner, allocator);
154
155 return DebugInfo{
156 .inner = inner,
157 .debug_info = debug_info,
158 .debug_abbrev = debug_abbrev,
159 .debug_str = debug_str,
160 .debug_line = debug_line,
161 .debug_ranges = debug_ranges,
162 };
163 }
164
165 pub fn deinit(self: *DebugInfo, allocator: *Allocator) void {
166 allocator.free(self.debug_info);
167 allocator.free(self.debug_abbrev);
168 allocator.free(self.debug_str);
169 allocator.free(self.debug_line);
170 allocator.free(self.debug_ranges);
171 self.inner.abbrev_table_list.deinit();
172 self.inner.compile_unit_list.deinit();
173 self.inner.func_list.deinit();
174 }
175};
176
177112/// Default path to dyld
178113const DEFAULT_DYLD_PATH: [*:0]const u8 = "/usr/lib/dyld";
179114
......@@ -2510,108 +2445,77 @@ fn writeDebugInfo(self: *Zld) !void {
25102445 var stabs = std.ArrayList(macho.nlist_64).init(self.allocator);
25112446 defer stabs.deinit();
25122447
2513 for (self.objects.items) |object, object_id| {
2514 var debug_info = blk: {
2515 var di = try DebugInfo.parseFromObject(self.allocator, object);
2516 break :blk di orelse continue;
2517 };
2518 defer debug_info.deinit(self.allocator);
2519
2520 // We assume there is only one CU.
2521 const compile_unit = debug_info.inner.findCompileUnit(0x0) catch |err| switch (err) {
2522 error.MissingDebugInfo => {
2523 // TODO audit cases with missing debug info and audit our dwarf.zig module.
2524 log.warn("invalid or missing debug info in {s}; skipping", .{object.name});
2525 continue;
2526 },
2527 else => |e| return e,
2528 };
2529 const name = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT_name);
2530 const comp_dir = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT_comp_dir);
2531
2532 {
2533 const tu_path = try std.fs.path.join(self.allocator, &[_][]const u8{ comp_dir, name });
2534 defer self.allocator.free(tu_path);
2535 const dirname = std.fs.path.dirname(tu_path) orelse "./";
2536 // Current dir
2537 try stabs.append(.{
2538 .n_strx = try self.makeString(tu_path[0 .. dirname.len + 1]),
2539 .n_type = macho.N_SO,
2540 .n_sect = 0,
2541 .n_desc = 0,
2542 .n_value = 0,
2543 });
2544 // Artifact name
2545 try stabs.append(.{
2546 .n_strx = try self.makeString(tu_path[dirname.len + 1 ..]),
2547 .n_type = macho.N_SO,
2548 .n_sect = 0,
2549 .n_desc = 0,
2550 .n_value = 0,
2551 });
2552 // Path to object file with debug info
2553 var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
2554 const stat = try object.file.?.stat();
2555 const mtime = @intCast(u64, @divFloor(stat.mtime, 1_000_000_000));
2556 try stabs.append(.{
2557 .n_strx = try self.makeString(object.name.?),
2558 .n_type = macho.N_OSO,
2559 .n_sect = 0,
2560 .n_desc = 1,
2561 .n_value = mtime,
2562 });
2563 }
2564 log.warn("analyzing debug info in '{s}'", .{object.name});
2448 for (self.objects.items) |object| {
2449 const tu_path = object.tu_path orelse continue;
2450 const tu_mtime = object.tu_mtime orelse continue;
2451 const dirname = std.fs.path.dirname(tu_path) orelse "./";
2452 // Current dir
2453 try stabs.append(.{
2454 .n_strx = try self.makeString(tu_path[0 .. dirname.len + 1]),
2455 .n_type = macho.N_SO,
2456 .n_sect = 0,
2457 .n_desc = 0,
2458 .n_value = 0,
2459 });
2460 // Artifact name
2461 try stabs.append(.{
2462 .n_strx = try self.makeString(tu_path[dirname.len + 1 ..]),
2463 .n_type = macho.N_SO,
2464 .n_sect = 0,
2465 .n_desc = 0,
2466 .n_value = 0,
2467 });
2468 // Path to object file with debug info
2469 try stabs.append(.{
2470 .n_strx = try self.makeString(object.name.?),
2471 .n_type = macho.N_OSO,
2472 .n_sect = 0,
2473 .n_desc = 1,
2474 .n_value = tu_mtime,
2475 });
25652476
2566 for (object.symtab.items) |sym| {
2567 const sym_name = object.getString(sym.inner.n_strx);
2568 const maybe_size = blk: for (debug_info.inner.func_list.items) |func| {
2569 if (func.pc_range) |range| {
2570 // TODO source address needs to be preserved
2571 if (sym.inner.n_value >= range.start and sym.inner.n_value < range.end) {
2572 break :blk range.end - range.start;
2573 }
2574 }
2575 } else null;
2576
2577 if (maybe_size) |size| {
2578 try stabs.append(.{
2579 .n_strx = 0,
2580 .n_type = macho.N_BNSYM,
2581 .n_sect = sym.inner.n_sect,
2582 .n_desc = 0,
2583 .n_value = sym.inner.n_value,
2584 });
2585 try stabs.append(.{
2586 .n_strx = sym.inner.n_strx,
2587 .n_type = macho.N_FUN,
2588 .n_sect = sym.inner.n_sect,
2589 .n_desc = 0,
2590 .n_value = sym.inner.n_value,
2591 });
2592 try stabs.append(.{
2593 .n_strx = 0,
2594 .n_type = macho.N_FUN,
2595 .n_sect = 0,
2596 .n_desc = 0,
2597 .n_value = size,
2598 });
2599 try stabs.append(.{
2600 .n_strx = 0,
2601 .n_type = macho.N_ENSYM,
2602 .n_sect = sym.inner.n_sect,
2603 .n_desc = 0,
2604 .n_value = size,
2605 });
2606 } else {
2607 // TODO need a way to differentiate symbols: global, static, local, etc.
2608 try stabs.append(.{
2609 .n_strx = sym.inner.n_strx,
2610 .n_type = macho.N_STSYM,
2611 .n_sect = sym.inner.n_sect,
2612 .n_desc = 0,
2613 .n_value = sym.inner.n_value,
2614 });
2477 for (object.stabs.items) |stab| {
2478 const sym = object.symtab.items[stab.symbol];
2479 switch (stab.tag) {
2480 .function => {
2481 try stabs.append(.{
2482 .n_strx = 0,
2483 .n_type = macho.N_BNSYM,
2484 .n_sect = sym.inner.n_sect,
2485 .n_desc = 0,
2486 .n_value = sym.inner.n_value,
2487 });
2488 try stabs.append(.{
2489 .n_strx = sym.inner.n_strx,
2490 .n_type = macho.N_FUN,
2491 .n_sect = sym.inner.n_sect,
2492 .n_desc = 0,
2493 .n_value = sym.inner.n_value,
2494 });
2495 try stabs.append(.{
2496 .n_strx = 0,
2497 .n_type = macho.N_FUN,
2498 .n_sect = 0,
2499 .n_desc = 0,
2500 .n_value = stab.size.?,
2501 });
2502 try stabs.append(.{
2503 .n_strx = 0,
2504 .n_type = macho.N_ENSYM,
2505 .n_sect = sym.inner.n_sect,
2506 .n_desc = 0,
2507 .n_value = stab.size.?,
2508 });
2509 },
2510 else => {
2511 try stabs.append(.{
2512 .n_strx = sym.inner.n_strx,
2513 .n_type = macho.N_STSYM,
2514 .n_sect = sym.inner.n_sect,
2515 .n_desc = 0,
2516 .n_value = sym.inner.n_value,
2517 });
2518 },
26152519 }
26162520 }
26172521