authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-05-24 15:34:30+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-05-24 15:34:48+02:00
loged7073c6300d6ae3ec4b370c1e13c2bbf11bc08c
tree2c6dcf4349e4f710a3d71cec3b47b8b31636726f
parentfb88cfdf6aa3fabba700d8340f025e4a3e0d3fb2

link/macho: fix perf bug in DWARF parsing


6 files changed, 420 insertions(+), 523 deletions(-)

CMakeLists.txt+1-1
......@@ -613,7 +613,6 @@ set(ZIG_STAGE2_SOURCES
613613 src/link/MachO/Atom.zig
614614 src/link/MachO/CodeSignature.zig
615615 src/link/MachO/DebugSymbols.zig
616 src/link/MachO/DwarfInfo.zig
617616 src/link/MachO/Dylib.zig
618617 src/link/MachO/InternalObject.zig
619618 src/link/MachO/Object.zig
......@@ -625,6 +624,7 @@ set(ZIG_STAGE2_SOURCES
625624 src/link/MachO/dyld_info/Rebase.zig
626625 src/link/MachO/dyld_info/Trie.zig
627626 src/link/MachO/dyld_info/bind.zig
627 src/link/MachO/dwarf.zig
628628 src/link/MachO/eh_frame.zig
629629 src/link/MachO/fat.zig
630630 src/link/MachO/file.zig
src/link/MachO.zig+7-1
......@@ -535,6 +535,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node
535535
536536 try self.addUndefinedGlobals();
537537 try self.resolveSymbols();
538 try self.parseDebugInfo();
538539 try self.resolveSyntheticSymbols();
539540
540541 try self.convertTentativeDefinitions();
......@@ -1409,6 +1410,12 @@ fn markLive(self: *MachO) void {
14091410 }
14101411}
14111412
1413pub fn parseDebugInfo(self: *MachO) !void {
1414 for (self.objects.items) |index| {
1415 try self.getFile(index).?.object.parseDebugInfo(self);
1416 }
1417}
1418
14121419fn resolveSyntheticSymbols(self: *MachO) !void {
14131420 const internal = self.getInternalObject() orelse return;
14141421
......@@ -4840,7 +4847,6 @@ const Cache = std.Build.Cache;
48404847const CodeSignature = @import("MachO/CodeSignature.zig");
48414848const Compilation = @import("../Compilation.zig");
48424849pub const DebugSymbols = @import("MachO/DebugSymbols.zig");
4843const DwarfInfo = @import("MachO/DwarfInfo.zig");
48444850const Dylib = @import("MachO/Dylib.zig");
48454851const ExportTrieSection = synthetic.ExportTrieSection;
48464852const File = @import("MachO/file.zig").File;
src/link/MachO/DwarfInfo.zig deleted-490
......@@ -1,490 +0,0 @@
1/// Abbreviation table indexed by offset in the .debug_abbrev bytestream
2abbrev_tables: std.AutoArrayHashMapUnmanaged(u64, AbbrevTable) = .{},
3/// List of compile units as they appear in the .debug_info bytestream
4compile_units: std.ArrayListUnmanaged(CompileUnit) = .{},
5/// Debug info string table
6strtab: std.ArrayListUnmanaged(u8) = .{},
7/// Debug info data
8di_data: std.ArrayListUnmanaged(u8) = .{},
9
10pub fn init(dw: *DwarfInfo, allocator: Allocator, di: DebugInfo) !void {
11 try dw.strtab.ensureTotalCapacityPrecise(allocator, di.debug_str.len);
12 dw.strtab.appendSliceAssumeCapacity(di.debug_str);
13 try dw.parseAbbrevTables(allocator, di);
14 try dw.parseCompileUnits(allocator, di);
15}
16
17pub fn deinit(dw: *DwarfInfo, allocator: Allocator) void {
18 dw.abbrev_tables.deinit(allocator);
19 for (dw.compile_units.items) |*cu| {
20 cu.deinit(allocator);
21 }
22 dw.compile_units.deinit(allocator);
23 dw.strtab.deinit(allocator);
24 dw.di_data.deinit(allocator);
25}
26
27fn appendDiData(dw: *DwarfInfo, allocator: Allocator, values: []const u8) error{OutOfMemory}!u32 {
28 const index: u32 = @intCast(dw.di_data.items.len);
29 try dw.di_data.ensureUnusedCapacity(allocator, values.len);
30 dw.di_data.appendSliceAssumeCapacity(values);
31 return index;
32}
33
34fn getString(dw: DwarfInfo, off: usize) [:0]const u8 {
35 assert(off < dw.strtab.items.len);
36 return mem.sliceTo(@as([*:0]const u8, @ptrCast(dw.strtab.items.ptr + off)), 0);
37}
38
39fn parseAbbrevTables(dw: *DwarfInfo, allocator: Allocator, di: DebugInfo) !void {
40 const tracy = trace(@src());
41 defer tracy.end();
42
43 const debug_abbrev = di.debug_abbrev;
44 var stream = std.io.fixedBufferStream(debug_abbrev);
45 var creader = std.io.countingReader(stream.reader());
46 const reader = creader.reader();
47
48 while (true) {
49 if (creader.bytes_read >= debug_abbrev.len) break;
50
51 try dw.abbrev_tables.ensureUnusedCapacity(allocator, 1);
52 const table_gop = dw.abbrev_tables.getOrPutAssumeCapacity(@intCast(creader.bytes_read));
53 assert(!table_gop.found_existing);
54 const table = table_gop.value_ptr;
55 table.* = .{};
56
57 while (true) {
58 const code = try leb.readULEB128(Code, reader);
59 if (code == 0) break;
60
61 try table.decls.ensureUnusedCapacity(allocator, 1);
62 const decl_gop = table.decls.getOrPutAssumeCapacity(code);
63 assert(!decl_gop.found_existing);
64 const decl = decl_gop.value_ptr;
65 decl.* = .{
66 .code = code,
67 .tag = undefined,
68 .children = false,
69 };
70 decl.tag = try leb.readULEB128(Tag, reader);
71 decl.children = (try reader.readByte()) > 0;
72
73 while (true) {
74 const at = try leb.readULEB128(At, reader);
75 const form = try leb.readULEB128(Form, reader);
76 if (at == 0 and form == 0) break;
77
78 try decl.attrs.ensureUnusedCapacity(allocator, 1);
79 const attr_gop = decl.attrs.getOrPutAssumeCapacity(at);
80 assert(!attr_gop.found_existing);
81 const attr = attr_gop.value_ptr;
82 attr.* = .{
83 .at = at,
84 .form = form,
85 };
86 }
87 }
88 }
89}
90
91fn parseCompileUnits(dw: *DwarfInfo, allocator: Allocator, di: DebugInfo) !void {
92 const tracy = trace(@src());
93 defer tracy.end();
94
95 const debug_info = di.debug_info;
96 var stream = std.io.fixedBufferStream(debug_info);
97 var creader = std.io.countingReader(stream.reader());
98 const reader = creader.reader();
99
100 while (true) {
101 if (creader.bytes_read == debug_info.len) break;
102
103 const cu = try dw.compile_units.addOne(allocator);
104 cu.* = .{
105 .header = undefined,
106 .pos = creader.bytes_read,
107 };
108
109 var length: u64 = try reader.readInt(u32, .little);
110 const is_64bit = length == 0xffffffff;
111 if (is_64bit) {
112 length = try reader.readInt(u64, .little);
113 }
114 cu.header.format = if (is_64bit) .dwarf64 else .dwarf32;
115 cu.header.length = length;
116 cu.header.version = try reader.readInt(u16, .little);
117 cu.header.debug_abbrev_offset = try readOffset(cu.header.format, reader);
118 cu.header.address_size = try reader.readInt(u8, .little);
119
120 const table = dw.abbrev_tables.get(cu.header.debug_abbrev_offset).?;
121 try dw.parseDie(allocator, cu, table, di, null, &creader);
122 }
123}
124
125fn parseDie(
126 dw: *DwarfInfo,
127 allocator: Allocator,
128 cu: *CompileUnit,
129 table: AbbrevTable,
130 di: DebugInfo,
131 parent: ?u32,
132 creader: anytype,
133) anyerror!void {
134 const tracy = trace(@src());
135 defer tracy.end();
136
137 while (creader.bytes_read < cu.nextCompileUnitOffset()) {
138 const die = try cu.addDie(allocator);
139 cu.diePtr(die).* = .{ .code = undefined };
140 if (parent) |p| {
141 try cu.diePtr(p).children.append(allocator, die);
142 } else {
143 try cu.children.append(allocator, die);
144 }
145
146 const code = try leb.readULEB128(Code, creader.reader());
147 cu.diePtr(die).code = code;
148
149 if (code == 0) {
150 if (parent == null) continue;
151 return; // Close scope
152 }
153
154 const decl = table.decls.get(code) orelse return error.MalformedDwarf; // TODO better errors
155 const data = di.debug_info;
156 try cu.diePtr(die).values.ensureTotalCapacityPrecise(allocator, decl.attrs.values().len);
157
158 for (decl.attrs.values()) |attr| {
159 const start = std.math.cast(usize, creader.bytes_read) orelse return error.Overflow;
160 try advanceByFormSize(cu, attr.form, creader);
161 const end = std.math.cast(usize, creader.bytes_read) orelse return error.Overflow;
162 const index = try dw.appendDiData(allocator, data[start..end]);
163 cu.diePtr(die).values.appendAssumeCapacity(.{ .index = index, .len = @intCast(end - start) });
164 }
165
166 if (decl.children) {
167 // Open scope
168 try dw.parseDie(allocator, cu, table, di, die, creader);
169 }
170 }
171}
172
173fn advanceByFormSize(cu: *CompileUnit, form: Form, creader: anytype) !void {
174 const tracy = trace(@src());
175 defer tracy.end();
176
177 const reader = creader.reader();
178 switch (form) {
179 dwarf.FORM.strp,
180 dwarf.FORM.sec_offset,
181 dwarf.FORM.ref_addr,
182 => {
183 _ = try readOffset(cu.header.format, reader);
184 },
185
186 dwarf.FORM.addr => try reader.skipBytes(cu.header.address_size, .{}),
187
188 dwarf.FORM.block1,
189 dwarf.FORM.block2,
190 dwarf.FORM.block4,
191 dwarf.FORM.block,
192 => {
193 const len: u64 = switch (form) {
194 dwarf.FORM.block1 => try reader.readInt(u8, .little),
195 dwarf.FORM.block2 => try reader.readInt(u16, .little),
196 dwarf.FORM.block4 => try reader.readInt(u32, .little),
197 dwarf.FORM.block => try leb.readULEB128(u64, reader),
198 else => unreachable,
199 };
200 var i: u64 = 0;
201 while (i < len) : (i += 1) {
202 _ = try reader.readByte();
203 }
204 },
205
206 dwarf.FORM.exprloc => {
207 const len = try leb.readULEB128(u64, reader);
208 var i: u64 = 0;
209 while (i < len) : (i += 1) {
210 _ = try reader.readByte();
211 }
212 },
213 dwarf.FORM.flag_present => {},
214
215 dwarf.FORM.data1,
216 dwarf.FORM.ref1,
217 dwarf.FORM.flag,
218 => try reader.skipBytes(1, .{}),
219
220 dwarf.FORM.data2,
221 dwarf.FORM.ref2,
222 => try reader.skipBytes(2, .{}),
223
224 dwarf.FORM.data4,
225 dwarf.FORM.ref4,
226 => try reader.skipBytes(4, .{}),
227
228 dwarf.FORM.data8,
229 dwarf.FORM.ref8,
230 dwarf.FORM.ref_sig8,
231 => try reader.skipBytes(8, .{}),
232
233 dwarf.FORM.udata,
234 dwarf.FORM.ref_udata,
235 => {
236 _ = try leb.readULEB128(u64, reader);
237 },
238
239 dwarf.FORM.sdata => {
240 _ = try leb.readILEB128(i64, reader);
241 },
242
243 dwarf.FORM.string => {
244 while (true) {
245 const byte = try reader.readByte();
246 if (byte == 0x0) break;
247 }
248 },
249
250 else => {
251 // TODO better errors
252 log.err("unhandled DW_FORM_* value with identifier {x}", .{form});
253 return error.UnhandledDwFormValue;
254 },
255 }
256}
257
258fn readOffset(format: Format, reader: anytype) !u64 {
259 return switch (format) {
260 .dwarf32 => try reader.readInt(u32, .little),
261 .dwarf64 => try reader.readInt(u64, .little),
262 };
263}
264
265pub const AbbrevTable = struct {
266 /// Table of abbreviation declarations indexed by their assigned code value
267 decls: std.AutoArrayHashMapUnmanaged(Code, Decl) = .{},
268
269 pub fn deinit(table: *AbbrevTable, gpa: Allocator) void {
270 for (table.decls.values()) |*decl| {
271 decl.deinit(gpa);
272 }
273 table.decls.deinit(gpa);
274 }
275};
276
277pub const Decl = struct {
278 code: Code,
279 tag: Tag,
280 children: bool,
281
282 /// Table of attributes indexed by their AT value
283 attrs: std.AutoArrayHashMapUnmanaged(At, Attr) = .{},
284
285 pub fn deinit(decl: *Decl, gpa: Allocator) void {
286 decl.attrs.deinit(gpa);
287 }
288};
289
290pub const Attr = struct {
291 at: At,
292 form: Form,
293};
294
295pub const At = u64;
296pub const Code = u64;
297pub const Form = u64;
298pub const Tag = u64;
299
300pub const CompileUnitHeader = struct {
301 format: Format,
302 length: u64,
303 version: u16,
304 debug_abbrev_offset: u64,
305 address_size: u8,
306};
307
308pub const CompileUnit = struct {
309 header: CompileUnitHeader,
310 pos: u64,
311 dies: std.ArrayListUnmanaged(Die) = .{},
312 children: std.ArrayListUnmanaged(Die.Index) = .{},
313
314 pub fn deinit(cu: *CompileUnit, gpa: Allocator) void {
315 for (cu.dies.items) |*die| {
316 die.deinit(gpa);
317 }
318 cu.dies.deinit(gpa);
319 cu.children.deinit(gpa);
320 }
321
322 pub fn addDie(cu: *CompileUnit, gpa: Allocator) !Die.Index {
323 const index = @as(Die.Index, @intCast(cu.dies.items.len));
324 _ = try cu.dies.addOne(gpa);
325 return index;
326 }
327
328 pub fn diePtr(cu: *CompileUnit, index: Die.Index) *Die {
329 return &cu.dies.items[index];
330 }
331
332 pub fn getCompileDir(cu: CompileUnit, ctx: DwarfInfo) error{Overflow}!?[:0]const u8 {
333 assert(cu.dies.items.len > 0);
334 const die = cu.dies.items[0];
335 const res = die.find(dwarf.AT.comp_dir, cu, ctx) orelse return null;
336 return res.getString(cu.header.format, ctx);
337 }
338
339 pub fn getSourceFile(cu: CompileUnit, ctx: DwarfInfo) error{Overflow}!?[:0]const u8 {
340 assert(cu.dies.items.len > 0);
341 const die = cu.dies.items[0];
342 const res = die.find(dwarf.AT.name, cu, ctx) orelse return null;
343 return res.getString(cu.header.format, ctx);
344 }
345
346 pub fn nextCompileUnitOffset(cu: CompileUnit) u64 {
347 return cu.pos + switch (cu.header.format) {
348 .dwarf32 => @as(u64, 4),
349 .dwarf64 => 12,
350 } + cu.header.length;
351 }
352};
353
354pub const Die = struct {
355 code: Code,
356 values: std.ArrayListUnmanaged(struct { index: u32, len: u32 }) = .{},
357 children: std.ArrayListUnmanaged(Die.Index) = .{},
358
359 pub fn deinit(die: *Die, gpa: Allocator) void {
360 die.values.deinit(gpa);
361 die.children.deinit(gpa);
362 }
363
364 pub fn find(die: Die, at: At, cu: CompileUnit, ctx: DwarfInfo) ?DieValue {
365 const table = ctx.abbrev_tables.get(cu.header.debug_abbrev_offset) orelse return null;
366 const decl = table.decls.get(die.code).?;
367 const index = decl.attrs.getIndex(at) orelse return null;
368 const attr = decl.attrs.values()[index];
369 const value = die.values.items[index];
370 return .{ .attr = attr, .bytes = ctx.di_data.items[value.index..][0..value.len] };
371 }
372
373 pub const Index = u32;
374};
375
376pub const DieValue = struct {
377 attr: Attr,
378 bytes: []const u8,
379
380 pub fn getFlag(value: DieValue) ?bool {
381 return switch (value.attr.form) {
382 dwarf.FORM.flag => value.bytes[0] == 1,
383 dwarf.FORM.flag_present => true,
384 else => null,
385 };
386 }
387
388 pub fn getString(value: DieValue, format: Format, ctx: DwarfInfo) error{Overflow}!?[:0]const u8 {
389 switch (value.attr.form) {
390 dwarf.FORM.string => {
391 return mem.sliceTo(@as([*:0]const u8, @ptrCast(value.bytes.ptr)), 0);
392 },
393 dwarf.FORM.strp => {
394 const off = switch (format) {
395 .dwarf64 => mem.readInt(u64, value.bytes[0..8], .little),
396 .dwarf32 => mem.readInt(u32, value.bytes[0..4], .little),
397 };
398 const off_u = std.math.cast(usize, off) orelse return error.Overflow;
399 return ctx.getString(off_u);
400 },
401 else => return null,
402 }
403 }
404
405 pub fn getSecOffset(value: DieValue, format: Format) ?u64 {
406 return switch (value.attr.form) {
407 dwarf.FORM.sec_offset => switch (format) {
408 .dwarf32 => mem.readInt(u32, value.bytes[0..4], .little),
409 .dwarf64 => mem.readInt(u64, value.bytes[0..8], .little),
410 },
411 else => null,
412 };
413 }
414
415 pub fn getConstant(value: DieValue) !?i128 {
416 var stream = std.io.fixedBufferStream(value.bytes);
417 const reader = stream.reader();
418 return switch (value.attr.form) {
419 dwarf.FORM.data1 => value.bytes[0],
420 dwarf.FORM.data2 => mem.readInt(u16, value.bytes[0..2], .little),
421 dwarf.FORM.data4 => mem.readInt(u32, value.bytes[0..4], .little),
422 dwarf.FORM.data8 => mem.readInt(u64, value.bytes[0..8], .little),
423 dwarf.FORM.udata => try leb.readULEB128(u64, reader),
424 dwarf.FORM.sdata => try leb.readILEB128(i64, reader),
425 else => null,
426 };
427 }
428
429 pub fn getReference(value: DieValue, format: Format) !?u64 {
430 var stream = std.io.fixedBufferStream(value.bytes);
431 const reader = stream.reader();
432 return switch (value.attr.form) {
433 dwarf.FORM.ref1 => value.bytes[0],
434 dwarf.FORM.ref2 => mem.readInt(u16, value.bytes[0..2], .little),
435 dwarf.FORM.ref4 => mem.readInt(u32, value.bytes[0..4], .little),
436 dwarf.FORM.ref8 => mem.readInt(u64, value.bytes[0..8], .little),
437 dwarf.FORM.ref_udata => try leb.readULEB128(u64, reader),
438 dwarf.FORM.ref_addr => switch (format) {
439 .dwarf32 => mem.readInt(u32, value.bytes[0..4], .little),
440 .dwarf64 => mem.readInt(u64, value.bytes[0..8], .little),
441 },
442 else => null,
443 };
444 }
445
446 pub fn getAddr(value: DieValue, header: CompileUnitHeader) ?u64 {
447 return switch (value.attr.form) {
448 dwarf.FORM.addr => switch (header.address_size) {
449 1 => value.bytes[0],
450 2 => mem.readInt(u16, value.bytes[0..2], .little),
451 4 => mem.readInt(u32, value.bytes[0..4], .little),
452 8 => mem.readInt(u64, value.bytes[0..8], .little),
453 else => null,
454 },
455 else => null,
456 };
457 }
458
459 pub fn getExprloc(value: DieValue) !?[]const u8 {
460 if (value.attr.form != dwarf.FORM.exprloc) return null;
461 var stream = std.io.fixedBufferStream(value.bytes);
462 var creader = std.io.countingReader(stream.reader());
463 const reader = creader.reader();
464 const expr_len = try leb.readULEB128(u64, reader);
465 return value.bytes[creader.bytes_read..][0..expr_len];
466 }
467};
468
469pub const Format = enum {
470 dwarf32,
471 dwarf64,
472};
473
474const DebugInfo = struct {
475 debug_info: []const u8,
476 debug_abbrev: []const u8,
477 debug_str: []const u8,
478};
479
480const assert = std.debug.assert;
481const dwarf = std.dwarf;
482const leb = std.leb;
483const log = std.log.scoped(.link);
484const mem = std.mem;
485const std = @import("std");
486const trace = @import("../../tracy.zig").trace;
487
488const Allocator = mem.Allocator;
489const DwarfInfo = @This();
490const MachO = @import("../MachO.zig");
src/link/MachO/Object.zig+126-31
......@@ -13,7 +13,7 @@ symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
1313atoms: std.ArrayListUnmanaged(Atom.Index) = .{},
1414
1515platform: ?MachO.Platform = null,
16dwarf_info: ?DwarfInfo = null,
16compile_unit: ?CompileUnit = null,
1717stab_files: std.ArrayListUnmanaged(StabFile) = .{},
1818
1919eh_frame_sect_index: ?u8 = null,
......@@ -31,12 +31,6 @@ dynamic_relocs: MachO.DynamicRelocs = .{},
3131output_symtab_ctx: MachO.SymtabCtx = .{},
3232output_ar_state: Archive.ArState = .{},
3333
34const InArchive = struct {
35 path: []const u8,
36 offset: u64,
37 size: u32,
38};
39
4034pub fn isObject(path: []const u8) !bool {
4135 const file = try std.fs.cwd().openFile(path, .{});
4236 defer file.close();
......@@ -60,7 +54,6 @@ pub fn deinit(self: *Object, allocator: Allocator) void {
6054 self.fdes.deinit(allocator);
6155 self.eh_frame_data.deinit(allocator);
6256 self.unwind_records.deinit(allocator);
63 if (self.dwarf_info) |*dw| dw.deinit(allocator);
6457 for (self.stab_files.items) |*sf| {
6558 sf.stabs.deinit(allocator);
6659 }
......@@ -251,8 +244,6 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {
251244 // }
252245 }
253246
254 try self.initDwarfInfo(macho_file);
255
256247 for (self.atoms.items) |atom_index| {
257248 const atom = macho_file.getAtom(atom_index).?;
258249 const isec = atom.getInputSection(macho_file);
......@@ -1214,7 +1205,7 @@ fn parseUnwindRecords(self: *Object, macho_file: *MachO) !void {
12141205/// and record that so that we can emit symbol stabs.
12151206/// TODO in the future, we want parse debug info and debug line sections so that
12161207/// we can provide nice error locations to the user.
1217fn initDwarfInfo(self: *Object, macho_file: *MachO) !void {
1208pub fn parseDebugInfo(self: *Object, macho_file: *MachO) !void {
12181209 const tracy = trace(@src());
12191210 defer tracy.end();
12201211
......@@ -1240,17 +1231,107 @@ fn initDwarfInfo(self: *Object, macho_file: *MachO) !void {
12401231 const debug_str = if (debug_str_index) |index| try self.getSectionData(@intCast(index), macho_file) else &[0]u8{};
12411232 defer gpa.free(debug_str);
12421233
1243 var dwarf_info = DwarfInfo{};
1244 errdefer dwarf_info.deinit(gpa);
1245 dwarf_info.init(gpa, .{
1234 self.compile_unit = self.findCompileUnit(.{
1235 .gpa = gpa,
12461236 .debug_info = debug_info,
12471237 .debug_abbrev = debug_abbrev,
12481238 .debug_str = debug_str,
1249 }) catch {
1250 try macho_file.reportParseError2(self.index, "invalid __DWARF info found", .{});
1251 return error.MalformedObject;
1239 }) catch null; // TODO figure out what errors are fatal, and when we silently fail
1240}
1241
1242fn findCompileUnit(self: *Object, args: struct {
1243 gpa: Allocator,
1244 debug_info: []const u8,
1245 debug_abbrev: []const u8,
1246 debug_str: []const u8,
1247}) !CompileUnit {
1248 var cu_wip: struct {
1249 comp_dir: ?[:0]const u8 = null,
1250 tu_name: ?[:0]const u8 = null,
1251 } = .{};
1252
1253 const gpa = args.gpa;
1254 var info_reader = dwarf.InfoReader{ .bytes = args.debug_info, .strtab = args.debug_str };
1255 var abbrev_reader = dwarf.AbbrevReader{ .bytes = args.debug_abbrev };
1256
1257 const cuh = try info_reader.readCompileUnitHeader();
1258 try abbrev_reader.seekTo(cuh.debug_abbrev_offset);
1259
1260 const cu_decl = (try abbrev_reader.readDecl()) orelse return error.Eof;
1261 if (cu_decl.tag != dwarf.TAG.compile_unit) return error.UnexpectedTag;
1262
1263 try info_reader.seekToDie(cu_decl.code, cuh, &abbrev_reader);
1264
1265 while (try abbrev_reader.readAttr()) |attr| switch (attr.at) {
1266 dwarf.AT.name => {
1267 cu_wip.tu_name = try info_reader.readString(attr.form, cuh);
1268 },
1269 dwarf.AT.comp_dir => {
1270 cu_wip.comp_dir = try info_reader.readString(attr.form, cuh);
1271 },
1272 else => switch (attr.form) {
1273 dwarf.FORM.sec_offset,
1274 dwarf.FORM.ref_addr,
1275 => {
1276 _ = try info_reader.readOffset(cuh.format);
1277 },
1278
1279 dwarf.FORM.addr => {
1280 _ = try info_reader.readNBytes(cuh.address_size);
1281 },
1282
1283 dwarf.FORM.block1,
1284 dwarf.FORM.block2,
1285 dwarf.FORM.block4,
1286 dwarf.FORM.block,
1287 => {
1288 _ = try info_reader.readBlock(attr.form);
1289 },
1290
1291 dwarf.FORM.exprloc => {
1292 _ = try info_reader.readExprLoc();
1293 },
1294
1295 dwarf.FORM.flag_present => {},
1296
1297 dwarf.FORM.data1,
1298 dwarf.FORM.ref1,
1299 dwarf.FORM.flag,
1300 dwarf.FORM.data2,
1301 dwarf.FORM.ref2,
1302 dwarf.FORM.data4,
1303 dwarf.FORM.ref4,
1304 dwarf.FORM.data8,
1305 dwarf.FORM.ref8,
1306 dwarf.FORM.ref_sig8,
1307 dwarf.FORM.udata,
1308 dwarf.FORM.ref_udata,
1309 dwarf.FORM.sdata,
1310 => {
1311 _ = try info_reader.readConstant(attr.form);
1312 },
1313
1314 dwarf.FORM.strp,
1315 dwarf.FORM.string,
1316 => {
1317 _ = try info_reader.readString(attr.form, cuh);
1318 },
1319
1320 else => {
1321 // TODO actual errors?
1322 log.err("unhandled DW_FORM_* value with identifier {x}", .{attr.form});
1323 return error.UnhandledForm;
1324 },
1325 },
1326 };
1327
1328 if (cu_wip.comp_dir == null) return error.MissingCompDir;
1329 if (cu_wip.tu_name == null) return error.MissingTuName;
1330
1331 return .{
1332 .comp_dir = try self.addString(gpa, cu_wip.comp_dir.?),
1333 .tu_name = try self.addString(gpa, cu_wip.tu_name.?),
12521334 };
1253 self.dwarf_info = dwarf_info;
12541335}
12551336
12561337pub fn resolveSymbols(self: *Object, macho_file: *MachO) void {
......@@ -1591,10 +1672,9 @@ pub fn calcSymtabSize(self: *Object, macho_file: *MachO) !void {
15911672}
15921673
15931674pub fn calcStabsSize(self: *Object, macho_file: *MachO) error{Overflow}!void {
1594 if (self.dwarf_info) |dw| {
1595 const cu = dw.compile_units.items[0];
1596 const comp_dir = try cu.getCompileDir(dw) orelse return;
1597 const tu_name = try cu.getSourceFile(dw) orelse return;
1675 if (self.compile_unit) |cu| {
1676 const comp_dir = cu.getCompDir(self);
1677 const tu_name = cu.getTuName(self);
15981678
15991679 self.output_symtab_ctx.nstabs += 4; // N_SO, N_SO, N_OSO, N_SO
16001680 self.output_symtab_ctx.strsize += @as(u32, @intCast(comp_dir.len + 1)); // comp_dir
......@@ -1709,10 +1789,9 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
17091789
17101790 var index = self.output_symtab_ctx.istab;
17111791
1712 if (self.dwarf_info) |dw| {
1713 const cu = dw.compile_units.items[0];
1714 const comp_dir = try cu.getCompileDir(dw) orelse return;
1715 const tu_name = try cu.getSourceFile(dw) orelse return;
1792 if (self.compile_unit) |cu| {
1793 const comp_dir = cu.getCompDir(self);
1794 const tu_name = cu.getTuName(self);
17161795
17171796 // Open scope
17181797 // N_SO comp_dir
......@@ -1958,10 +2037,7 @@ pub fn hasEhFrameRecords(self: Object) bool {
19582037}
19592038
19602039pub fn hasDebugInfo(self: Object) bool {
1961 if (self.dwarf_info) |dw| {
1962 return dw.compile_units.items.len > 0;
1963 }
1964 return self.hasSymbolStabs();
2040 return self.compile_unit != null or self.hasSymbolStabs();
19652041}
19662042
19672043fn hasSymbolStabs(self: Object) bool {
......@@ -2194,6 +2270,25 @@ const StabFile = struct {
21942270 };
21952271};
21962272
2273const CompileUnit = struct {
2274 comp_dir: u32,
2275 tu_name: u32,
2276
2277 fn getCompDir(cu: CompileUnit, object: *const Object) [:0]const u8 {
2278 return object.getString(cu.comp_dir);
2279 }
2280
2281 fn getTuName(cu: CompileUnit, object: *const Object) [:0]const u8 {
2282 return object.getString(cu.tu_name);
2283 }
2284};
2285
2286const InArchive = struct {
2287 path: []const u8,
2288 offset: u64,
2289 size: u32,
2290};
2291
21972292const x86_64 = struct {
21982293 fn parseRelocs(
21992294 self: *const Object,
......@@ -2548,6 +2643,7 @@ const aarch64 = struct {
25482643};
25492644
25502645const assert = std.debug.assert;
2646const dwarf = @import("dwarf.zig");
25512647const eh_frame = @import("eh_frame.zig");
25522648const log = std.log.scoped(.link);
25532649const macho = std.macho;
......@@ -2560,7 +2656,6 @@ const Allocator = mem.Allocator;
25602656const Archive = @import("Archive.zig");
25612657const Atom = @import("Atom.zig");
25622658const Cie = eh_frame.Cie;
2563const DwarfInfo = @import("DwarfInfo.zig");
25642659const Fde = eh_frame.Fde;
25652660const File = @import("file.zig").File;
25662661const LoadCommandIterator = macho.LoadCommandIterator;
src/link/MachO/dwarf.zig created+285
......@@ -0,0 +1,285 @@
1pub const InfoReader = struct {
2 bytes: []const u8,
3 strtab: []const u8,
4 pos: usize = 0,
5
6 pub fn readCompileUnitHeader(p: *InfoReader) !CompileUnitHeader {
7 var length: u64 = try p.readInt(u32);
8 const is_64bit = length == 0xffffffff;
9 if (is_64bit) {
10 length = try p.readInt(u64);
11 }
12 const dw_fmt: DwarfFormat = if (is_64bit) .dwarf64 else .dwarf32;
13 return .{
14 .format = dw_fmt,
15 .length = length,
16 .version = try p.readInt(u16),
17 .debug_abbrev_offset = try p.readOffset(dw_fmt),
18 .address_size = try p.readByte(),
19 };
20 }
21
22 pub fn seekToDie(p: *InfoReader, code: Code, cuh: CompileUnitHeader, abbrev_reader: *AbbrevReader) !void {
23 const cuh_length = math.cast(usize, cuh.length) orelse return error.Overflow;
24 const end_pos = p.pos + switch (cuh.format) {
25 .dwarf32 => @as(usize, 4),
26 .dwarf64 => 12,
27 } + cuh_length;
28 while (p.pos < end_pos) {
29 const di_code = try p.readULEB128(u64);
30 if (di_code == 0) return error.Eof;
31 if (di_code == code) return;
32
33 while (try abbrev_reader.readAttr()) |attr| switch (attr.at) {
34 dwarf.FORM.sec_offset,
35 dwarf.FORM.ref_addr,
36 => {
37 _ = try p.readOffset(cuh.format);
38 },
39
40 dwarf.FORM.addr => {
41 _ = try p.readNBytes(cuh.address_size);
42 },
43
44 dwarf.FORM.block1,
45 dwarf.FORM.block2,
46 dwarf.FORM.block4,
47 dwarf.FORM.block,
48 => {
49 _ = try p.readBlock(attr.form);
50 },
51
52 dwarf.FORM.exprloc => {
53 _ = try p.readExprLoc();
54 },
55
56 dwarf.FORM.flag_present => {},
57
58 dwarf.FORM.data1,
59 dwarf.FORM.ref1,
60 dwarf.FORM.flag,
61 dwarf.FORM.data2,
62 dwarf.FORM.ref2,
63 dwarf.FORM.data4,
64 dwarf.FORM.ref4,
65 dwarf.FORM.data8,
66 dwarf.FORM.ref8,
67 dwarf.FORM.ref_sig8,
68 dwarf.FORM.udata,
69 dwarf.FORM.ref_udata,
70 dwarf.FORM.sdata,
71 => {
72 _ = try p.readConstant(attr.form);
73 },
74
75 dwarf.FORM.strp,
76 dwarf.FORM.string,
77 => {
78 _ = try p.readString(attr.form, cuh);
79 },
80
81 else => {
82 // TODO better errors
83 log.err("unhandled DW_FORM_* value with identifier {x}", .{attr.form});
84 return error.UnhandledDwFormValue;
85 },
86 };
87 }
88 }
89
90 pub fn readBlock(p: *InfoReader, form: Form) ![]const u8 {
91 const len: u64 = switch (form) {
92 dwarf.FORM.block1 => try p.readByte(),
93 dwarf.FORM.block2 => try p.readInt(u16),
94 dwarf.FORM.block4 => try p.readInt(u32),
95 dwarf.FORM.block => try p.readULEB128(u64),
96 else => unreachable,
97 };
98 return p.readNBytes(len);
99 }
100
101 pub fn readExprLoc(p: *InfoReader) ![]const u8 {
102 const len: u64 = try p.readULEB128(u64);
103 return p.readNBytes(len);
104 }
105
106 pub fn readConstant(p: *InfoReader, form: Form) !u64 {
107 return switch (form) {
108 dwarf.FORM.data1, dwarf.FORM.ref1, dwarf.FORM.flag => try p.readByte(),
109 dwarf.FORM.data2, dwarf.FORM.ref2 => try p.readInt(u16),
110 dwarf.FORM.data4, dwarf.FORM.ref4 => try p.readInt(u32),
111 dwarf.FORM.data8, dwarf.FORM.ref8, dwarf.FORM.ref_sig8 => try p.readInt(u64),
112 dwarf.FORM.udata, dwarf.FORM.ref_udata => try p.readULEB128(u64),
113 dwarf.FORM.sdata => @bitCast(try p.readILEB128(i64)),
114 else => return error.UnhandledConstantForm,
115 };
116 }
117
118 pub fn readString(p: *InfoReader, form: Form, cuh: CompileUnitHeader) ![:0]const u8 {
119 switch (form) {
120 dwarf.FORM.strp => {
121 const off = try p.readOffset(cuh.format);
122 return mem.sliceTo(@as([*:0]const u8, @ptrCast(p.strtab.ptr + off)), 0);
123 },
124 dwarf.FORM.string => {
125 const start = p.pos;
126 while (p.pos < p.bytes.len) : (p.pos += 1) {
127 if (p.bytes[p.pos] == 0) break;
128 }
129 if (p.bytes[p.pos] != 0) return error.Eof;
130 return p.bytes[start..p.pos :0];
131 },
132 else => unreachable,
133 }
134 }
135
136 pub fn readByte(p: *InfoReader) !u8 {
137 if (p.pos + 1 > p.bytes.len) return error.Eof;
138 defer p.pos += 1;
139 return p.bytes[p.pos];
140 }
141
142 pub fn readNBytes(p: *InfoReader, num: u64) ![]const u8 {
143 const num_usize = math.cast(usize, num) orelse return error.Overflow;
144 if (p.pos + num_usize > p.bytes.len) return error.Eof;
145 defer p.pos += num_usize;
146 return p.bytes[p.pos..][0..num_usize];
147 }
148
149 pub fn readInt(p: *InfoReader, comptime Int: type) !Int {
150 if (p.pos + @sizeOf(Int) > p.bytes.len) return error.Eof;
151 defer p.pos += @sizeOf(Int);
152 return mem.readInt(Int, p.bytes[p.pos..][0..@sizeOf(Int)], .little);
153 }
154
155 pub fn readOffset(p: *InfoReader, dw_fmt: DwarfFormat) !u64 {
156 return switch (dw_fmt) {
157 .dwarf32 => try p.readInt(u32),
158 .dwarf64 => try p.readInt(u64),
159 };
160 }
161
162 pub fn readULEB128(p: *InfoReader, comptime Type: type) !Type {
163 var stream = std.io.fixedBufferStream(p.bytes[p.pos..]);
164 var creader = std.io.countingReader(stream.reader());
165 const value: Type = try leb.readULEB128(Type, creader.reader());
166 p.pos += creader.bytes_read;
167 return value;
168 }
169
170 pub fn readILEB128(p: *InfoReader, comptime Type: type) !Type {
171 var stream = std.io.fixedBufferStream(p.bytes[p.pos..]);
172 var creader = std.io.countingReader(stream.reader());
173 const value: Type = try leb.readILEB128(Type, creader.reader());
174 p.pos += creader.bytes_read;
175 return value;
176 }
177
178 pub fn seekTo(p: *InfoReader, off: u64) !void {
179 p.pos = math.cast(usize, off) orelse return error.Overflow;
180 }
181};
182
183pub const AbbrevReader = struct {
184 bytes: []const u8,
185 pos: usize = 0,
186
187 pub fn hasMore(p: AbbrevReader) bool {
188 return p.pos < p.bytes.len;
189 }
190
191 pub fn readDecl(p: *AbbrevReader) !?AbbrevDecl {
192 const pos = p.pos;
193 const code = try p.readULEB128(Code);
194 if (code == 0) return null;
195
196 const tag = try p.readULEB128(Tag);
197 const has_children = (try p.readByte()) > 0;
198 return .{
199 .code = code,
200 .pos = pos,
201 .len = p.pos - pos,
202 .tag = tag,
203 .has_children = has_children,
204 };
205 }
206
207 pub fn readAttr(p: *AbbrevReader) !?AbbrevAttr {
208 const pos = p.pos;
209 const at = try p.readULEB128(At);
210 const form = try p.readULEB128(Form);
211 return if (at == 0 and form == 0) null else .{
212 .at = at,
213 .form = form,
214 .pos = pos,
215 .len = p.pos - pos,
216 };
217 }
218
219 pub fn readByte(p: *AbbrevReader) !u8 {
220 if (p.pos + 1 > p.bytes.len) return error.Eof;
221 defer p.pos += 1;
222 return p.bytes[p.pos];
223 }
224
225 pub fn readULEB128(p: *AbbrevReader, comptime Type: type) !Type {
226 var stream = std.io.fixedBufferStream(p.bytes[p.pos..]);
227 var creader = std.io.countingReader(stream.reader());
228 const value: Type = try leb.readULEB128(Type, creader.reader());
229 p.pos += creader.bytes_read;
230 return value;
231 }
232
233 pub fn seekTo(p: *AbbrevReader, off: u64) !void {
234 p.pos = math.cast(usize, off) orelse return error.Overflow;
235 }
236};
237
238const AbbrevDecl = struct {
239 code: Code,
240 pos: usize,
241 len: usize,
242 tag: Tag,
243 has_children: bool,
244};
245
246const AbbrevAttr = struct {
247 at: At,
248 form: Form,
249 pos: usize,
250 len: usize,
251};
252
253const CompileUnitHeader = struct {
254 format: DwarfFormat,
255 length: u64,
256 version: u16,
257 debug_abbrev_offset: u64,
258 address_size: u8,
259};
260
261const Die = struct {
262 pos: usize,
263 len: usize,
264};
265
266const DwarfFormat = enum {
267 dwarf32,
268 dwarf64,
269};
270
271const dwarf = std.dwarf;
272const leb = std.leb;
273const log = std.log.scoped(.link);
274const math = std.math;
275const mem = std.mem;
276const std = @import("std");
277
278const At = u64;
279const Code = u64;
280const Form = u64;
281const Tag = u64;
282
283pub const AT = dwarf.AT;
284pub const FORM = dwarf.FORM;
285pub const TAG = dwarf.TAG;
src/link/MachO/relocatable.zig+1
......@@ -46,6 +46,7 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]c
4646
4747 try macho_file.addUndefinedGlobals();
4848 try macho_file.resolveSymbols();
49 try macho_file.parseDebugInfo();
4950 try macho_file.dedupLiterals();
5051 markExports(macho_file);
5152 claimUnresolved(macho_file);