authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-12-03 02:28:22-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-12-03 02:28:22-05:00
log4e09e363cd00b1dd36467ef7958d750ea09f296d
tree6b0b98cf032a6177ec2a8189f5138390d41b9481
parentc013f45ad08c2c6d727bf336767e23d988f5f30b
parent29c7f6810fe00cf69adf81d635d3410402b530e8
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #21720 from kubkon/macho-dwarf-v5

macho: add basic handling of DWARFv5

4 files changed, 522 insertions(+), 459 deletions(-)

CMakeLists.txt+1-1
......@@ -611,6 +611,7 @@ set(ZIG_STAGE2_SOURCES
611611 src/link/MachO/Atom.zig
612612 src/link/MachO/CodeSignature.zig
613613 src/link/MachO/DebugSymbols.zig
614 src/link/MachO/Dwarf.zig
614615 src/link/MachO/Dylib.zig
615616 src/link/MachO/InternalObject.zig
616617 src/link/MachO/Object.zig
......@@ -622,7 +623,6 @@ set(ZIG_STAGE2_SOURCES
622623 src/link/MachO/dyld_info/Rebase.zig
623624 src/link/MachO/dyld_info/Trie.zig
624625 src/link/MachO/dyld_info/bind.zig
625 src/link/MachO/dwarf.zig
626626 src/link/MachO/eh_frame.zig
627627 src/link/MachO/fat.zig
628628 src/link/MachO/file.zig
src/link/MachO/Dwarf.zig created+409
......@@ -0,0 +1,409 @@
1debug_info: []u8 = &[0]u8{},
2debug_abbrev: []u8 = &[0]u8{},
3debug_str: []u8 = &[0]u8{},
4debug_str_offsets: []u8 = &[0]u8{},
5
6pub fn deinit(dwarf: *Dwarf, allocator: Allocator) void {
7 allocator.free(dwarf.debug_info);
8 allocator.free(dwarf.debug_abbrev);
9 allocator.free(dwarf.debug_str);
10 allocator.free(dwarf.debug_str_offsets);
11}
12
13/// Pulls an offset into __debug_str section from a __debug_str_offs section.
14/// This is new in DWARFv5 and requires the producer to specify DW_FORM_strx* (`index` arg)
15/// but also DW_AT_str_offsets_base with DW_FORM_sec_offset (`base` arg) in the opening header
16/// of a "referencing entity" such as DW_TAG_compile_unit.
17fn getOffset(debug_str_offsets: []const u8, base: u64, index: u64, dw_fmt: DwarfFormat) error{Overflow}!u64 {
18 const base_as_usize = math.cast(usize, base) orelse return error.Overflow;
19 const index_as_usize = math.cast(usize, index) orelse return error.Overflow;
20 return switch (dw_fmt) {
21 .dwarf32 => @as(
22 *align(1) const u32,
23 @ptrCast(debug_str_offsets.ptr + base_as_usize + index_as_usize * @sizeOf(u32)),
24 ).*,
25 .dwarf64 => @as(
26 *align(1) const u64,
27 @ptrCast(debug_str_offsets.ptr + base_as_usize + index_as_usize * @sizeOf(u64)),
28 ).*,
29 };
30}
31
32pub const InfoReader = struct {
33 ctx: Dwarf,
34 pos: usize = 0,
35
36 fn bytes(p: InfoReader) []const u8 {
37 return p.ctx.debug_info;
38 }
39
40 pub fn readCompileUnitHeader(p: *InfoReader) !CompileUnitHeader {
41 var length: u64 = try p.readInt(u32);
42 const is_64bit = length == 0xffffffff;
43 if (is_64bit) {
44 length = try p.readInt(u64);
45 }
46 const dw_fmt: DwarfFormat = if (is_64bit) .dwarf64 else .dwarf32;
47 const version = try p.readInt(Version);
48 const rest: struct {
49 debug_abbrev_offset: u64,
50 address_size: u8,
51 unit_type: u8,
52 } = switch (version) {
53 4 => .{
54 .debug_abbrev_offset = try p.readOffset(dw_fmt),
55 .address_size = try p.readByte(),
56 .unit_type = 0,
57 },
58 5 => .{
59 // According to the spec, version 5 introduced .unit_type field in the header, and
60 // it reordered .debug_abbrev_offset with .address_size fields.
61 .unit_type = try p.readByte(),
62 .address_size = try p.readByte(),
63 .debug_abbrev_offset = try p.readOffset(dw_fmt),
64 },
65 else => return error.InvalidVersion,
66 };
67 return .{
68 .format = dw_fmt,
69 .length = length,
70 .version = version,
71 .debug_abbrev_offset = rest.debug_abbrev_offset,
72 .address_size = rest.address_size,
73 .unit_type = rest.unit_type,
74 };
75 }
76
77 pub fn seekToDie(p: *InfoReader, code: Code, cuh: CompileUnitHeader, abbrev_reader: *AbbrevReader) !void {
78 const cuh_length = math.cast(usize, cuh.length) orelse return error.Overflow;
79 const end_pos = p.pos + switch (cuh.format) {
80 .dwarf32 => @as(usize, 4),
81 .dwarf64 => 12,
82 } + cuh_length;
83 while (p.pos < end_pos) {
84 const di_code = try p.readUleb128(u64);
85 if (di_code == 0) return error.UnexpectedEndOfFile;
86 if (di_code == code) return;
87
88 while (try abbrev_reader.readAttr()) |attr| {
89 try p.skip(attr.form, cuh);
90 }
91 }
92 return error.UnexpectedEndOfFile;
93 }
94
95 /// When skipping attributes, we don't really need to be able to handle them all
96 /// since we only ever care about the DW_TAG_compile_unit.
97 pub fn skip(p: *InfoReader, form: Form, cuh: CompileUnitHeader) !void {
98 switch (form) {
99 dw.FORM.sec_offset,
100 dw.FORM.ref_addr,
101 => {
102 _ = try p.readOffset(cuh.format);
103 },
104
105 dw.FORM.addr => {
106 _ = try p.readNBytes(cuh.address_size);
107 },
108
109 dw.FORM.block1,
110 dw.FORM.block2,
111 dw.FORM.block4,
112 dw.FORM.block,
113 => {
114 _ = try p.readBlock(form);
115 },
116
117 dw.FORM.exprloc => {
118 _ = try p.readExprLoc();
119 },
120
121 dw.FORM.flag_present => {},
122
123 dw.FORM.data1,
124 dw.FORM.ref1,
125 dw.FORM.flag,
126 dw.FORM.data2,
127 dw.FORM.ref2,
128 dw.FORM.data4,
129 dw.FORM.ref4,
130 dw.FORM.data8,
131 dw.FORM.ref8,
132 dw.FORM.ref_sig8,
133 dw.FORM.udata,
134 dw.FORM.ref_udata,
135 dw.FORM.sdata,
136 => {
137 _ = try p.readConstant(form);
138 },
139
140 dw.FORM.strp,
141 dw.FORM.string,
142 => {
143 _ = try p.readString(form, cuh);
144 },
145
146 else => if (cuh.version >= 5) switch (form) {
147 dw.FORM.strx,
148 dw.FORM.strx1,
149 dw.FORM.strx2,
150 dw.FORM.strx3,
151 dw.FORM.strx4,
152 => {
153 // We are just iterating over the __debug_info data, so we don't care about an actual
154 // string, therefore we set the `base = 0`.
155 _ = try p.readStringIndexed(form, cuh, 0);
156 },
157
158 dw.FORM.addrx,
159 dw.FORM.addrx1,
160 dw.FORM.addrx2,
161 dw.FORM.addrx3,
162 dw.FORM.addrx4,
163 => {
164 _ = try p.readIndex(form);
165 },
166
167 else => return error.UnhandledForm,
168 } else return error.UnhandledForm,
169 }
170 }
171
172 pub fn readBlock(p: *InfoReader, form: Form) ![]const u8 {
173 const len: u64 = switch (form) {
174 dw.FORM.block1 => try p.readByte(),
175 dw.FORM.block2 => try p.readInt(u16),
176 dw.FORM.block4 => try p.readInt(u32),
177 dw.FORM.block => try p.readUleb128(u64),
178 else => unreachable,
179 };
180 return p.readNBytes(len);
181 }
182
183 pub fn readExprLoc(p: *InfoReader) ![]const u8 {
184 const len: u64 = try p.readUleb128(u64);
185 return p.readNBytes(len);
186 }
187
188 pub fn readConstant(p: *InfoReader, form: Form) !u64 {
189 return switch (form) {
190 dw.FORM.data1, dw.FORM.ref1, dw.FORM.flag => try p.readByte(),
191 dw.FORM.data2, dw.FORM.ref2 => try p.readInt(u16),
192 dw.FORM.data4, dw.FORM.ref4 => try p.readInt(u32),
193 dw.FORM.data8, dw.FORM.ref8, dw.FORM.ref_sig8 => try p.readInt(u64),
194 dw.FORM.udata, dw.FORM.ref_udata => try p.readUleb128(u64),
195 dw.FORM.sdata => @bitCast(try p.readIleb128(i64)),
196 else => return error.UnhandledConstantForm,
197 };
198 }
199
200 pub fn readIndex(p: *InfoReader, form: Form) !u64 {
201 return switch (form) {
202 dw.FORM.strx1, dw.FORM.addrx1 => try p.readByte(),
203 dw.FORM.strx2, dw.FORM.addrx2 => try p.readInt(u16),
204 dw.FORM.strx3, dw.FORM.addrx3 => error.UnhandledForm,
205 dw.FORM.strx4, dw.FORM.addrx4 => try p.readInt(u32),
206 dw.FORM.strx, dw.FORM.addrx => try p.readUleb128(u64),
207 else => return error.UnhandledIndexForm,
208 };
209 }
210
211 pub fn readString(p: *InfoReader, form: Form, cuh: CompileUnitHeader) ![:0]const u8 {
212 switch (form) {
213 dw.FORM.strp => {
214 const off = try p.readOffset(cuh.format);
215 const off_u = math.cast(usize, off) orelse return error.Overflow;
216 return mem.sliceTo(@as([*:0]const u8, @ptrCast(p.ctx.debug_str.ptr + off_u)), 0);
217 },
218 dw.FORM.string => {
219 const start = p.pos;
220 while (p.pos < p.bytes().len) : (p.pos += 1) {
221 if (p.bytes()[p.pos] == 0) break;
222 }
223 if (p.bytes()[p.pos] != 0) return error.UnexpectedEndOfFile;
224 return p.bytes()[start..p.pos :0];
225 },
226 else => unreachable,
227 }
228 }
229
230 pub fn readStringIndexed(p: *InfoReader, form: Form, cuh: CompileUnitHeader, base: u64) ![:0]const u8 {
231 switch (form) {
232 dw.FORM.strx,
233 dw.FORM.strx1,
234 dw.FORM.strx2,
235 dw.FORM.strx3,
236 dw.FORM.strx4,
237 => {
238 const index = try p.readIndex(form);
239 const off = math.cast(
240 usize,
241 try getOffset(p.ctx.debug_str_offsets, base, index, cuh.format),
242 ) orelse return error.Overflow;
243 return mem.sliceTo(@as([*:0]const u8, @ptrCast(p.ctx.debug_str.ptr + off)), 0);
244 },
245 else => unreachable,
246 }
247 }
248
249 pub fn readByte(p: *InfoReader) !u8 {
250 if (p.pos + 1 > p.bytes().len) return error.UnexpectedEndOfFile;
251 defer p.pos += 1;
252 return p.bytes()[p.pos];
253 }
254
255 pub fn readNBytes(p: *InfoReader, num: u64) ![]const u8 {
256 const num_usize = math.cast(usize, num) orelse return error.Overflow;
257 if (p.pos + num_usize > p.bytes().len) return error.UnexpectedEndOfFile;
258 defer p.pos += num_usize;
259 return p.bytes()[p.pos..][0..num_usize];
260 }
261
262 pub fn readInt(p: *InfoReader, comptime Int: type) !Int {
263 if (p.pos + @sizeOf(Int) > p.bytes().len) return error.UnexpectedEndOfFile;
264 defer p.pos += @sizeOf(Int);
265 return mem.readInt(Int, p.bytes()[p.pos..][0..@sizeOf(Int)], .little);
266 }
267
268 pub fn readOffset(p: *InfoReader, dw_fmt: DwarfFormat) !u64 {
269 return switch (dw_fmt) {
270 .dwarf32 => try p.readInt(u32),
271 .dwarf64 => try p.readInt(u64),
272 };
273 }
274
275 pub fn readUleb128(p: *InfoReader, comptime Type: type) !Type {
276 var stream = std.io.fixedBufferStream(p.bytes()[p.pos..]);
277 var creader = std.io.countingReader(stream.reader());
278 const value: Type = try leb.readUleb128(Type, creader.reader());
279 p.pos += math.cast(usize, creader.bytes_read) orelse return error.Overflow;
280 return value;
281 }
282
283 pub fn readIleb128(p: *InfoReader, comptime Type: type) !Type {
284 var stream = std.io.fixedBufferStream(p.bytes()[p.pos..]);
285 var creader = std.io.countingReader(stream.reader());
286 const value: Type = try leb.readIleb128(Type, creader.reader());
287 p.pos += math.cast(usize, creader.bytes_read) orelse return error.Overflow;
288 return value;
289 }
290
291 pub fn seekTo(p: *InfoReader, off: u64) !void {
292 p.pos = math.cast(usize, off) orelse return error.Overflow;
293 }
294};
295
296pub const AbbrevReader = struct {
297 ctx: Dwarf,
298 pos: usize = 0,
299
300 fn bytes(p: AbbrevReader) []const u8 {
301 return p.ctx.debug_abbrev;
302 }
303
304 pub fn hasMore(p: AbbrevReader) bool {
305 return p.pos < p.bytes().len;
306 }
307
308 pub fn readDecl(p: *AbbrevReader) !?AbbrevDecl {
309 const pos = p.pos;
310 const code = try p.readUleb128(Code);
311 if (code == 0) return null;
312
313 const tag = try p.readUleb128(Tag);
314 const has_children = (try p.readByte()) > 0;
315 return .{
316 .code = code,
317 .pos = pos,
318 .len = p.pos - pos,
319 .tag = tag,
320 .has_children = has_children,
321 };
322 }
323
324 pub fn readAttr(p: *AbbrevReader) !?AbbrevAttr {
325 const pos = p.pos;
326 const at = try p.readUleb128(At);
327 const form = try p.readUleb128(Form);
328 return if (at == 0 and form == 0) null else .{
329 .at = at,
330 .form = form,
331 .pos = pos,
332 .len = p.pos - pos,
333 };
334 }
335
336 pub fn readByte(p: *AbbrevReader) !u8 {
337 if (p.pos + 1 > p.bytes().len) return error.Eof;
338 defer p.pos += 1;
339 return p.bytes()[p.pos];
340 }
341
342 pub fn readUleb128(p: *AbbrevReader, comptime Type: type) !Type {
343 var stream = std.io.fixedBufferStream(p.bytes()[p.pos..]);
344 var creader = std.io.countingReader(stream.reader());
345 const value: Type = try leb.readUleb128(Type, creader.reader());
346 p.pos += math.cast(usize, creader.bytes_read) orelse return error.Overflow;
347 return value;
348 }
349
350 pub fn seekTo(p: *AbbrevReader, off: u64) !void {
351 p.pos = math.cast(usize, off) orelse return error.Overflow;
352 }
353};
354
355const AbbrevDecl = struct {
356 code: Code,
357 pos: usize,
358 len: usize,
359 tag: Tag,
360 has_children: bool,
361};
362
363const AbbrevAttr = struct {
364 at: At,
365 form: Form,
366 pos: usize,
367 len: usize,
368};
369
370const CompileUnitHeader = struct {
371 format: DwarfFormat,
372 length: u64,
373 version: Version,
374 debug_abbrev_offset: u64,
375 address_size: u8,
376 unit_type: u8,
377};
378
379const Die = struct {
380 pos: usize,
381 len: usize,
382};
383
384const DwarfFormat = enum {
385 dwarf32,
386 dwarf64,
387};
388
389const dw = std.dwarf;
390const leb = std.leb;
391const log = std.log.scoped(.link);
392const math = std.math;
393const mem = std.mem;
394const std = @import("std");
395const Allocator = mem.Allocator;
396const Dwarf = @This();
397const File = @import("file.zig").File;
398const MachO = @import("../MachO.zig");
399const Object = @import("Object.zig");
400
401pub const At = u64;
402pub const Code = u64;
403pub const Form = u64;
404pub const Tag = u64;
405pub const Version = u16;
406
407pub const AT = dw.AT;
408pub const FORM = dw.FORM;
409pub const TAG = dw.TAG;
src/link/MachO/Object.zig+112-172
......@@ -443,11 +443,8 @@ fn initCstringLiterals(self: *Object, allocator: Allocator, file: File.Handle, m
443443 for (slice.items(.header), 0..) |sect, n_sect| {
444444 if (!isCstringLiteral(sect)) continue;
445445
446 const sect_size = math.cast(usize, sect.size) orelse return error.Overflow;
447 const data = try allocator.alloc(u8, sect_size);
446 const data = try self.readSectionData(allocator, file, @intCast(n_sect));
448447 defer allocator.free(data);
449 const amt = try file.preadAll(data, sect.offset + self.offset);
450 if (amt != data.len) return error.InputOutput;
451448
452449 var count: u32 = 0;
453450 var start: u32 = 0;
......@@ -646,13 +643,10 @@ pub fn resolveLiterals(self: *Object, lp: *MachO.LiteralPool, macho_file: *MachO
646643 }
647644
648645 const slice = self.sections.slice();
649 for (slice.items(.header), slice.items(.subsections)) |header, subs| {
646 for (slice.items(.header), slice.items(.subsections), 0..) |header, subs, n_sect| {
650647 if (isCstringLiteral(header) or isFixedSizeLiteral(header)) {
651 const sect_size = math.cast(usize, header.size) orelse return error.Overflow;
652 const data = try gpa.alloc(u8, sect_size);
648 const data = try self.readSectionData(gpa, file, @intCast(n_sect));
653649 defer gpa.free(data);
654 const amt = try file.preadAll(data, header.offset + self.offset);
655 if (amt != data.len) return error.InputOutput;
656650
657651 for (subs.items) |sub| {
658652 const atom = self.getAtom(sub.atom).?;
......@@ -686,12 +680,7 @@ pub fn resolveLiterals(self: *Object, lp: *MachO.LiteralPool, macho_file: *MachO
686680 buffer.resize(target_size) catch unreachable;
687681 const gop = try sections_data.getOrPut(target.n_sect);
688682 if (!gop.found_existing) {
689 const target_sect = slice.items(.header)[target.n_sect];
690 const target_sect_size = math.cast(usize, target_sect.size) orelse return error.Overflow;
691 const data = try gpa.alloc(u8, target_sect_size);
692 const amt = try file.preadAll(data, target_sect.offset + self.offset);
693 if (amt != data.len) return error.InputOutput;
694 gop.value_ptr.* = data;
683 gop.value_ptr.* = try self.readSectionData(gpa, file, @intCast(target.n_sect));
695684 }
696685 const data = gop.value_ptr.*;
697686 const target_off = math.cast(usize, target.off) orelse return error.Overflow;
......@@ -1000,7 +989,7 @@ fn initRelocs(self: *Object, file: File.Handle, cpu_arch: std.Target.Cpu.Arch, m
1000989 defer tracy.end();
1001990 const slice = self.sections.slice();
1002991
1003 for (slice.items(.header), slice.items(.relocs)) |sect, *out| {
992 for (slice.items(.header), slice.items(.relocs), 0..) |sect, *out, n_sect| {
1004993 if (sect.nreloc == 0) continue;
1005994 // We skip relocs for __DWARF since even in -r mode, the linker is expected to emit
1006995 // debug symbol stabs in the relocatable. This made me curious why that is. For now,
......@@ -1009,8 +998,8 @@ fn initRelocs(self: *Object, file: File.Handle, cpu_arch: std.Target.Cpu.Arch, m
1009998 !mem.eql(u8, sect.sectName(), "__compact_unwind")) continue;
1010999
10111000 switch (cpu_arch) {
1012 .x86_64 => try x86_64.parseRelocs(self, sect, out, file, macho_file),
1013 .aarch64 => try aarch64.parseRelocs(self, sect, out, file, macho_file),
1001 .x86_64 => try x86_64.parseRelocs(self, @intCast(n_sect), sect, out, file, macho_file),
1002 .aarch64 => try aarch64.parseRelocs(self, @intCast(n_sect), sect, out, file, macho_file),
10141003 else => unreachable,
10151004 }
10161005
......@@ -1146,11 +1135,8 @@ fn initUnwindRecords(self: *Object, allocator: Allocator, sect_id: u8, file: Fil
11461135 };
11471136
11481137 const header = self.sections.items(.header)[sect_id];
1149 const size = math.cast(usize, header.size) orelse return error.Overflow;
1150 const data = try allocator.alloc(u8, size);
1138 const data = try self.readSectionData(allocator, file, sect_id);
11511139 defer allocator.free(data);
1152 const amt = try file.preadAll(data, header.offset + self.offset);
1153 if (amt != data.len) return error.InputOutput;
11541140
11551141 const nrecs = @divExact(data.len, @sizeOf(macho.compact_unwind_entry));
11561142 const recs = @as([*]align(1) const macho.compact_unwind_entry, @ptrCast(data.ptr))[0..nrecs];
......@@ -1359,151 +1345,106 @@ fn parseDebugInfo(self: *Object, macho_file: *MachO) !void {
13591345 defer tracy.end();
13601346
13611347 const gpa = macho_file.base.comp.gpa;
1348 const file = macho_file.getFileHandle(self.file_handle);
13621349
1363 var debug_info_index: ?usize = null;
1364 var debug_abbrev_index: ?usize = null;
1365 var debug_str_index: ?usize = null;
1350 var dwarf: Dwarf = .{};
1351 defer dwarf.deinit(gpa);
13661352
13671353 for (self.sections.items(.header), 0..) |sect, index| {
1354 const n_sect: u8 = @intCast(index);
13681355 if (sect.attrs() & macho.S_ATTR_DEBUG == 0) continue;
1369 if (mem.eql(u8, sect.sectName(), "__debug_info")) debug_info_index = index;
1370 if (mem.eql(u8, sect.sectName(), "__debug_abbrev")) debug_abbrev_index = index;
1371 if (mem.eql(u8, sect.sectName(), "__debug_str")) debug_str_index = index;
1356 if (mem.eql(u8, sect.sectName(), "__debug_info")) {
1357 dwarf.debug_info = try self.readSectionData(gpa, file, n_sect);
1358 }
1359 if (mem.eql(u8, sect.sectName(), "__debug_abbrev")) {
1360 dwarf.debug_abbrev = try self.readSectionData(gpa, file, n_sect);
1361 }
1362 if (mem.eql(u8, sect.sectName(), "__debug_str")) {
1363 dwarf.debug_str = try self.readSectionData(gpa, file, n_sect);
1364 }
1365 // __debug_str_offs[ets] section is a new addition in DWARFv5 and is generally
1366 // required in order to correctly parse strings.
1367 if (mem.eql(u8, sect.sectName(), "__debug_str_offs")) {
1368 dwarf.debug_str_offsets = try self.readSectionData(gpa, file, n_sect);
1369 }
13721370 }
13731371
1374 if (debug_info_index == null or debug_abbrev_index == null) return;
1372 if (dwarf.debug_info.len == 0) return;
13751373
1376 const slice = self.sections.slice();
1377 const file = macho_file.getFileHandle(self.file_handle);
1378 const debug_info = blk: {
1379 const sect = slice.items(.header)[debug_info_index.?];
1380 const size = math.cast(usize, sect.size) orelse return error.Overflow;
1381 const data = try gpa.alloc(u8, size);
1382 const amt = try file.preadAll(data, sect.offset + self.offset);
1383 if (amt != data.len) return error.InputOutput;
1384 break :blk data;
1385 };
1386 defer gpa.free(debug_info);
1387 const debug_abbrev = blk: {
1388 const sect = slice.items(.header)[debug_abbrev_index.?];
1389 const size = math.cast(usize, sect.size) orelse return error.Overflow;
1390 const data = try gpa.alloc(u8, size);
1391 const amt = try file.preadAll(data, sect.offset + self.offset);
1392 if (amt != data.len) return error.InputOutput;
1393 break :blk data;
1394 };
1395 defer gpa.free(debug_abbrev);
1396 const debug_str = if (debug_str_index) |sid| blk: {
1397 const sect = slice.items(.header)[sid];
1398 const size = math.cast(usize, sect.size) orelse return error.Overflow;
1399 const data = try gpa.alloc(u8, size);
1400 const amt = try file.preadAll(data, sect.offset + self.offset);
1401 if (amt != data.len) return error.InputOutput;
1402 break :blk data;
1403 } else &[0]u8{};
1404 defer gpa.free(debug_str);
1405
1406 self.compile_unit = self.findCompileUnit(.{
1407 .gpa = gpa,
1408 .debug_info = debug_info,
1409 .debug_abbrev = debug_abbrev,
1410 .debug_str = debug_str,
1411 }) catch null; // TODO figure out what errors are fatal, and when we silently fail
1412}
1413
1414fn findCompileUnit(self: *Object, args: struct {
1415 gpa: Allocator,
1416 debug_info: []const u8,
1417 debug_abbrev: []const u8,
1418 debug_str: []const u8,
1419}) !CompileUnit {
1420 var cu_wip: struct {
1421 comp_dir: ?[:0]const u8 = null,
1422 tu_name: ?[:0]const u8 = null,
1423 } = .{};
1424
1425 const gpa = args.gpa;
1426 var info_reader = dwarf.InfoReader{ .bytes = args.debug_info, .strtab = args.debug_str };
1427 var abbrev_reader = dwarf.AbbrevReader{ .bytes = args.debug_abbrev };
1374 // TODO return error once we fix emitting DWARF in self-hosted backend.
1375 // https://github.com/ziglang/zig/issues/21719
1376 self.compile_unit = self.findCompileUnit(gpa, dwarf) catch null;
1377}
1378
1379fn findCompileUnit(self: *Object, gpa: Allocator, ctx: Dwarf) !CompileUnit {
1380 var info_reader = Dwarf.InfoReader{ .ctx = ctx };
1381 var abbrev_reader = Dwarf.AbbrevReader{ .ctx = ctx };
14281382
14291383 const cuh = try info_reader.readCompileUnitHeader();
14301384 try abbrev_reader.seekTo(cuh.debug_abbrev_offset);
14311385
1432 const cu_decl = (try abbrev_reader.readDecl()) orelse return error.Eof;
1433 if (cu_decl.tag != dwarf.TAG.compile_unit) return error.UnexpectedTag;
1386 const cu_decl = (try abbrev_reader.readDecl()) orelse return error.UnexpectedEndOfFile;
1387 if (cu_decl.tag != Dwarf.TAG.compile_unit) return error.UnexpectedTag;
14341388
14351389 try info_reader.seekToDie(cu_decl.code, cuh, &abbrev_reader);
14361390
1437 while (try abbrev_reader.readAttr()) |attr| switch (attr.at) {
1438 dwarf.AT.name => {
1439 cu_wip.tu_name = try info_reader.readString(attr.form, cuh);
1440 },
1441 dwarf.AT.comp_dir => {
1442 cu_wip.comp_dir = try info_reader.readString(attr.form, cuh);
1443 },
1444 else => switch (attr.form) {
1445 dwarf.FORM.sec_offset,
1446 dwarf.FORM.ref_addr,
1447 => {
1448 _ = try info_reader.readOffset(cuh.format);
1449 },
1450
1451 dwarf.FORM.addr => {
1452 _ = try info_reader.readNBytes(cuh.address_size);
1453 },
1454
1455 dwarf.FORM.block1,
1456 dwarf.FORM.block2,
1457 dwarf.FORM.block4,
1458 dwarf.FORM.block,
1459 => {
1460 _ = try info_reader.readBlock(attr.form);
1461 },
1462
1463 dwarf.FORM.exprloc => {
1464 _ = try info_reader.readExprLoc();
1465 },
1466
1467 dwarf.FORM.flag_present => {},
1468
1469 dwarf.FORM.data1,
1470 dwarf.FORM.ref1,
1471 dwarf.FORM.flag,
1472 dwarf.FORM.data2,
1473 dwarf.FORM.ref2,
1474 dwarf.FORM.data4,
1475 dwarf.FORM.ref4,
1476 dwarf.FORM.data8,
1477 dwarf.FORM.ref8,
1478 dwarf.FORM.ref_sig8,
1479 dwarf.FORM.udata,
1480 dwarf.FORM.ref_udata,
1481 dwarf.FORM.sdata,
1482 => {
1483 _ = try info_reader.readConstant(attr.form);
1484 },
1485
1486 dwarf.FORM.strp,
1487 dwarf.FORM.string,
1488 => {
1489 _ = try info_reader.readString(attr.form, cuh);
1490 },
1491
1492 else => {
1493 // TODO actual errors?
1494 log.err("unhandled DW_FORM_* value with identifier {x}", .{attr.form});
1495 return error.UnhandledForm;
1496 },
1497 },
1391 const Pos = struct {
1392 pos: usize,
1393 form: Dwarf.Form,
14981394 };
1499
1500 if (cu_wip.comp_dir == null) return error.MissingCompDir;
1501 if (cu_wip.tu_name == null) return error.MissingTuName;
1502
1503 return .{
1504 .comp_dir = try self.addString(gpa, cu_wip.comp_dir.?),
1505 .tu_name = try self.addString(gpa, cu_wip.tu_name.?),
1395 var saved: struct {
1396 tu_name: ?Pos,
1397 comp_dir: ?Pos,
1398 str_offsets_base: ?Pos,
1399 } = .{
1400 .tu_name = null,
1401 .comp_dir = null,
1402 .str_offsets_base = null,
15061403 };
1404 while (try abbrev_reader.readAttr()) |attr| {
1405 const pos: Pos = .{ .pos = info_reader.pos, .form = attr.form };
1406 switch (attr.at) {
1407 Dwarf.AT.name => saved.tu_name = pos,
1408 Dwarf.AT.comp_dir => saved.comp_dir = pos,
1409 Dwarf.AT.str_offsets_base => saved.str_offsets_base = pos,
1410 else => {},
1411 }
1412 try info_reader.skip(attr.form, cuh);
1413 }
1414
1415 if (saved.comp_dir == null) return error.MissingCompileDir;
1416 if (saved.tu_name == null) return error.MissingTuName;
1417
1418 const str_offsets_base: ?u64 = if (saved.str_offsets_base) |str_offsets_base| str_offsets_base: {
1419 try info_reader.seekTo(str_offsets_base.pos);
1420 break :str_offsets_base try info_reader.readOffset(cuh.format);
1421 } else null;
1422
1423 var cu: CompileUnit = .{ .comp_dir = .{}, .tu_name = .{} };
1424 for (&[_]struct { Pos, *MachO.String }{
1425 .{ saved.comp_dir.?, &cu.comp_dir },
1426 .{ saved.tu_name.?, &cu.tu_name },
1427 }) |tuple| {
1428 const pos, const str_offset_ptr = tuple;
1429 try info_reader.seekTo(pos.pos);
1430 str_offset_ptr.* = switch (pos.form) {
1431 Dwarf.FORM.strp,
1432 Dwarf.FORM.string,
1433 => try self.addString(gpa, try info_reader.readString(pos.form, cuh)),
1434 Dwarf.FORM.strx,
1435 Dwarf.FORM.strx1,
1436 Dwarf.FORM.strx2,
1437 Dwarf.FORM.strx3,
1438 Dwarf.FORM.strx4,
1439 => blk: {
1440 const base = str_offsets_base orelse return error.MissingStrOffsetsBase;
1441 break :blk try self.addString(gpa, try info_reader.readStringIndexed(pos.form, cuh, base));
1442 },
1443 else => return error.InvalidForm,
1444 };
1445 }
1446
1447 return cu;
15071448}
15081449
15091450pub fn resolveSymbols(self: *Object, macho_file: *MachO) !void {
......@@ -2561,6 +2502,17 @@ pub fn getUnwindRecord(self: *Object, index: UnwindInfo.Record.Index) *UnwindInf
25612502 return &self.unwind_records.items[index];
25622503}
25632504
2505/// Caller owns the memory.
2506pub fn readSectionData(self: Object, allocator: Allocator, file: File.Handle, n_sect: u8) ![]u8 {
2507 const header = self.sections.items(.header)[n_sect];
2508 const size = math.cast(usize, header.size) orelse return error.Overflow;
2509 const data = try allocator.alloc(u8, size);
2510 const amt = try file.preadAll(data, header.offset + self.offset);
2511 errdefer allocator.free(data);
2512 if (amt != data.len) return error.InputOutput;
2513 return data;
2514}
2515
25642516pub fn format(
25652517 self: *Object,
25662518 comptime unused_fmt_string: []const u8,
......@@ -2848,6 +2800,7 @@ const CompactUnwindCtx = struct {
28482800const x86_64 = struct {
28492801 fn parseRelocs(
28502802 self: *Object,
2803 n_sect: u8,
28512804 sect: macho.section_64,
28522805 out: *std.ArrayListUnmanaged(Relocation),
28532806 handle: File.Handle,
......@@ -2857,19 +2810,12 @@ const x86_64 = struct {
28572810
28582811 const relocs_buffer = try gpa.alloc(u8, sect.nreloc * @sizeOf(macho.relocation_info));
28592812 defer gpa.free(relocs_buffer);
2860 {
2861 const amt = try handle.preadAll(relocs_buffer, sect.reloff + self.offset);
2862 if (amt != relocs_buffer.len) return error.InputOutput;
2863 }
2813 const amt = try handle.preadAll(relocs_buffer, sect.reloff + self.offset);
2814 if (amt != relocs_buffer.len) return error.InputOutput;
28642815 const relocs = @as([*]align(1) const macho.relocation_info, @ptrCast(relocs_buffer.ptr))[0..sect.nreloc];
28652816
2866 const sect_size = math.cast(usize, sect.size) orelse return error.Overflow;
2867 const code = try gpa.alloc(u8, sect_size);
2817 const code = try self.readSectionData(gpa, handle, n_sect);
28682818 defer gpa.free(code);
2869 {
2870 const amt = try handle.preadAll(code, sect.offset + self.offset);
2871 if (amt != code.len) return error.InputOutput;
2872 }
28732819
28742820 try out.ensureTotalCapacityPrecise(gpa, relocs.len);
28752821
......@@ -3021,6 +2967,7 @@ const x86_64 = struct {
30212967const aarch64 = struct {
30222968 fn parseRelocs(
30232969 self: *Object,
2970 n_sect: u8,
30242971 sect: macho.section_64,
30252972 out: *std.ArrayListUnmanaged(Relocation),
30262973 handle: File.Handle,
......@@ -3030,19 +2977,12 @@ const aarch64 = struct {
30302977
30312978 const relocs_buffer = try gpa.alloc(u8, sect.nreloc * @sizeOf(macho.relocation_info));
30322979 defer gpa.free(relocs_buffer);
3033 {
3034 const amt = try handle.preadAll(relocs_buffer, sect.reloff + self.offset);
3035 if (amt != relocs_buffer.len) return error.InputOutput;
3036 }
2980 const amt = try handle.preadAll(relocs_buffer, sect.reloff + self.offset);
2981 if (amt != relocs_buffer.len) return error.InputOutput;
30372982 const relocs = @as([*]align(1) const macho.relocation_info, @ptrCast(relocs_buffer.ptr))[0..sect.nreloc];
30382983
3039 const sect_size = math.cast(usize, sect.size) orelse return error.Overflow;
3040 const code = try gpa.alloc(u8, sect_size);
2984 const code = try self.readSectionData(gpa, handle, n_sect);
30412985 defer gpa.free(code);
3042 {
3043 const amt = try handle.preadAll(code, sect.offset + self.offset);
3044 if (amt != code.len) return error.InputOutput;
3045 }
30462986
30472987 try out.ensureTotalCapacityPrecise(gpa, relocs.len);
30482988
......@@ -3219,7 +3159,6 @@ const aarch64 = struct {
32193159};
32203160
32213161const assert = std.debug.assert;
3222const dwarf = @import("dwarf.zig");
32233162const eh_frame = @import("eh_frame.zig");
32243163const log = std.log.scoped(.link);
32253164const macho = std.macho;
......@@ -3233,6 +3172,7 @@ const Allocator = mem.Allocator;
32333172const Archive = @import("Archive.zig");
32343173const Atom = @import("Atom.zig");
32353174const Cie = eh_frame.Cie;
3175const Dwarf = @import("Dwarf.zig");
32363176const Fde = eh_frame.Fde;
32373177const File = @import("file.zig").File;
32383178const LoadCommandIterator = macho.LoadCommandIterator;
src/link/MachO/dwarf.zig deleted-286
......@@ -1,286 +0,0 @@
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 const off_u = math.cast(usize, off) orelse return error.Overflow;
123 return mem.sliceTo(@as([*:0]const u8, @ptrCast(p.strtab.ptr + off_u)), 0);
124 },
125 dwarf.FORM.string => {
126 const start = p.pos;
127 while (p.pos < p.bytes.len) : (p.pos += 1) {
128 if (p.bytes[p.pos] == 0) break;
129 }
130 if (p.bytes[p.pos] != 0) return error.Eof;
131 return p.bytes[start..p.pos :0];
132 },
133 else => unreachable,
134 }
135 }
136
137 pub fn readByte(p: *InfoReader) !u8 {
138 if (p.pos + 1 > p.bytes.len) return error.Eof;
139 defer p.pos += 1;
140 return p.bytes[p.pos];
141 }
142
143 pub fn readNBytes(p: *InfoReader, num: u64) ![]const u8 {
144 const num_usize = math.cast(usize, num) orelse return error.Overflow;
145 if (p.pos + num_usize > p.bytes.len) return error.Eof;
146 defer p.pos += num_usize;
147 return p.bytes[p.pos..][0..num_usize];
148 }
149
150 pub fn readInt(p: *InfoReader, comptime Int: type) !Int {
151 if (p.pos + @sizeOf(Int) > p.bytes.len) return error.Eof;
152 defer p.pos += @sizeOf(Int);
153 return mem.readInt(Int, p.bytes[p.pos..][0..@sizeOf(Int)], .little);
154 }
155
156 pub fn readOffset(p: *InfoReader, dw_fmt: DwarfFormat) !u64 {
157 return switch (dw_fmt) {
158 .dwarf32 => try p.readInt(u32),
159 .dwarf64 => try p.readInt(u64),
160 };
161 }
162
163 pub fn readUleb128(p: *InfoReader, comptime Type: type) !Type {
164 var stream = std.io.fixedBufferStream(p.bytes[p.pos..]);
165 var creader = std.io.countingReader(stream.reader());
166 const value: Type = try leb.readUleb128(Type, creader.reader());
167 p.pos += math.cast(usize, creader.bytes_read) orelse return error.Overflow;
168 return value;
169 }
170
171 pub fn readIleb128(p: *InfoReader, comptime Type: type) !Type {
172 var stream = std.io.fixedBufferStream(p.bytes[p.pos..]);
173 var creader = std.io.countingReader(stream.reader());
174 const value: Type = try leb.readIleb128(Type, creader.reader());
175 p.pos += math.cast(usize, creader.bytes_read) orelse return error.Overflow;
176 return value;
177 }
178
179 pub fn seekTo(p: *InfoReader, off: u64) !void {
180 p.pos = math.cast(usize, off) orelse return error.Overflow;
181 }
182};
183
184pub const AbbrevReader = struct {
185 bytes: []const u8,
186 pos: usize = 0,
187
188 pub fn hasMore(p: AbbrevReader) bool {
189 return p.pos < p.bytes.len;
190 }
191
192 pub fn readDecl(p: *AbbrevReader) !?AbbrevDecl {
193 const pos = p.pos;
194 const code = try p.readUleb128(Code);
195 if (code == 0) return null;
196
197 const tag = try p.readUleb128(Tag);
198 const has_children = (try p.readByte()) > 0;
199 return .{
200 .code = code,
201 .pos = pos,
202 .len = p.pos - pos,
203 .tag = tag,
204 .has_children = has_children,
205 };
206 }
207
208 pub fn readAttr(p: *AbbrevReader) !?AbbrevAttr {
209 const pos = p.pos;
210 const at = try p.readUleb128(At);
211 const form = try p.readUleb128(Form);
212 return if (at == 0 and form == 0) null else .{
213 .at = at,
214 .form = form,
215 .pos = pos,
216 .len = p.pos - pos,
217 };
218 }
219
220 pub fn readByte(p: *AbbrevReader) !u8 {
221 if (p.pos + 1 > p.bytes.len) return error.Eof;
222 defer p.pos += 1;
223 return p.bytes[p.pos];
224 }
225
226 pub fn readUleb128(p: *AbbrevReader, comptime Type: type) !Type {
227 var stream = std.io.fixedBufferStream(p.bytes[p.pos..]);
228 var creader = std.io.countingReader(stream.reader());
229 const value: Type = try leb.readUleb128(Type, creader.reader());
230 p.pos += math.cast(usize, creader.bytes_read) orelse return error.Overflow;
231 return value;
232 }
233
234 pub fn seekTo(p: *AbbrevReader, off: u64) !void {
235 p.pos = math.cast(usize, off) orelse return error.Overflow;
236 }
237};
238
239const AbbrevDecl = struct {
240 code: Code,
241 pos: usize,
242 len: usize,
243 tag: Tag,
244 has_children: bool,
245};
246
247const AbbrevAttr = struct {
248 at: At,
249 form: Form,
250 pos: usize,
251 len: usize,
252};
253
254const CompileUnitHeader = struct {
255 format: DwarfFormat,
256 length: u64,
257 version: u16,
258 debug_abbrev_offset: u64,
259 address_size: u8,
260};
261
262const Die = struct {
263 pos: usize,
264 len: usize,
265};
266
267const DwarfFormat = enum {
268 dwarf32,
269 dwarf64,
270};
271
272const dwarf = std.dwarf;
273const leb = std.leb;
274const log = std.log.scoped(.link);
275const math = std.math;
276const mem = std.mem;
277const std = @import("std");
278
279const At = u64;
280const Code = u64;
281const Form = u64;
282const Tag = u64;
283
284pub const AT = dwarf.AT;
285pub const FORM = dwarf.FORM;
286pub const TAG = dwarf.TAG;