authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-10-16 12:28:13+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-12-02 22:05:21-05:00
log8e815000515182f06fa436668664e4329c407a3e
tree43af30351ba752d3fb19c7dffbf2fd501b3438f4
parent808306f49ac8f7fd57c10a48a0126fefd07ab690

macho: handle DWARFv5 when parsing debug info in objects


2 files changed, 301 insertions(+), 225 deletions(-)

src/link/MachO/Dwarf.zig+207-93
...@@ -1,25 +1,73 @@...@@ -1,25 +1,73 @@
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) u64 {
18 return switch (dw_fmt) {
19 .dwarf32 => @as(*align(1) const u32, @ptrCast(debug_str_offsets.ptr + base + index * @sizeOf(u32))).*,
20 .dwarf64 => @as(*align(1) const u64, @ptrCast(debug_str_offsets.ptr + base + index * @sizeOf(u64))).*,
21 };
22}
23
1pub const InfoReader = struct {24pub const InfoReader = struct {
2 bytes: []const u8,25 ctx: Dwarf,
3 strtab: []const u8,
4 pos: usize = 0,26 pos: usize = 0,
527
6 pub fn readCompileUnitHeader(p: *InfoReader) !CompileUnitHeader {28 fn bytes(p: InfoReader) []const u8 {
29 return p.ctx.debug_info;
30 }
31
32 pub fn readCompileUnitHeader(p: *InfoReader, macho_file: *MachO) !CompileUnitHeader {
33 _ = macho_file;
7 var length: u64 = try p.readInt(u32);34 var length: u64 = try p.readInt(u32);
8 const is_64bit = length == 0xffffffff;35 const is_64bit = length == 0xffffffff;
9 if (is_64bit) {36 if (is_64bit) {
10 length = try p.readInt(u64);37 length = try p.readInt(u64);
11 }38 }
12 const dw_fmt: DwarfFormat = if (is_64bit) .dwarf64 else .dwarf32;39 const dw_fmt: DwarfFormat = if (is_64bit) .dwarf64 else .dwarf32;
40 const version = try p.readInt(Version);
41 const rest: struct {
42 debug_abbrev_offset: u64,
43 address_size: u8,
44 unit_type: u8,
45 } = switch (version) {
46 4 => .{
47 .debug_abbrev_offset = try p.readOffset(dw_fmt),
48 .address_size = try p.readByte(),
49 .unit_type = 0,
50 },
51 5 => .{
52 // According to the spec, version 5 introduced .unit_type field in the header, and
53 // it reordered .debug_abbrev_offset with .address_size fields.
54 .unit_type = try p.readByte(),
55 .address_size = try p.readByte(),
56 .debug_abbrev_offset = try p.readOffset(dw_fmt),
57 },
58 else => return error.InvalidVersion,
59 };
13 return .{60 return .{
14 .format = dw_fmt,61 .format = dw_fmt,
15 .length = length,62 .length = length,
16 .version = try p.readInt(u16),63 .version = version,
17 .debug_abbrev_offset = try p.readOffset(dw_fmt),64 .debug_abbrev_offset = rest.debug_abbrev_offset,
18 .address_size = try p.readByte(),65 .address_size = rest.address_size,
66 .unit_type = rest.unit_type,
19 };67 };
20 }68 }
2169
22 pub fn seekToDie(p: *InfoReader, code: Code, cuh: CompileUnitHeader, abbrev_reader: *AbbrevReader) !void {70 pub fn seekToDie(p: *InfoReader, code: Code, cuh: CompileUnitHeader, abbrev_reader: *AbbrevReader, macho_file: *MachO) !void {
23 const cuh_length = math.cast(usize, cuh.length) orelse return error.Overflow;71 const cuh_length = math.cast(usize, cuh.length) orelse return error.Overflow;
24 const end_pos = p.pos + switch (cuh.format) {72 const end_pos = p.pos + switch (cuh.format) {
25 .dwarf32 => @as(usize, 4),73 .dwarf32 => @as(usize, 4),
...@@ -27,72 +75,100 @@ pub const InfoReader = struct {...@@ -27,72 +75,100 @@ pub const InfoReader = struct {
27 } + cuh_length;75 } + cuh_length;
28 while (p.pos < end_pos) {76 while (p.pos < end_pos) {
29 const di_code = try p.readUleb128(u64);77 const di_code = try p.readUleb128(u64);
30 if (di_code == 0) return error.Eof;78 if (di_code == 0) return error.UnexpectedEndOfFile;
31 if (di_code == code) return;79 if (di_code == code) return;
3280
33 while (try abbrev_reader.readAttr()) |attr| switch (attr.at) {81 while (try abbrev_reader.readAttr()) |attr| {
34 dwarf.FORM.sec_offset,82 try p.skip(attr.form, cuh, macho_file);
35 dwarf.FORM.ref_addr,83 }
36 => {84 }
37 _ = try p.readOffset(cuh.format);85 return error.UnexpectedEndOfFile;
38 },86 }
3987
40 dwarf.FORM.addr => {88 /// When skipping attributes, we don't really need to be able to handle them all
41 _ = try p.readNBytes(cuh.address_size);89 /// since we only ever care about the DW_TAG_compile_unit.
42 },90 pub fn skip(p: *InfoReader, form: Form, cuh: CompileUnitHeader, macho_file: *MachO) !void {
91 _ = macho_file;
92 switch (form) {
93 dw.FORM.sec_offset,
94 dw.FORM.ref_addr,
95 => {
96 _ = try p.readOffset(cuh.format);
97 },
4398
44 dwarf.FORM.block1,99 dw.FORM.addr => {
45 dwarf.FORM.block2,100 _ = try p.readNBytes(cuh.address_size);
46 dwarf.FORM.block4,101 },
47 dwarf.FORM.block,
48 => {
49 _ = try p.readBlock(attr.form);
50 },
51102
52 dwarf.FORM.exprloc => {103 dw.FORM.block1,
53 _ = try p.readExprLoc();104 dw.FORM.block2,
54 },105 dw.FORM.block4,
106 dw.FORM.block,
107 => {
108 _ = try p.readBlock(form);
109 },
110
111 dw.FORM.exprloc => {
112 _ = try p.readExprLoc();
113 },
55114
56 dwarf.FORM.flag_present => {},115 dw.FORM.flag_present => {},
57116
58 dwarf.FORM.data1,117 dw.FORM.data1,
59 dwarf.FORM.ref1,118 dw.FORM.ref1,
60 dwarf.FORM.flag,119 dw.FORM.flag,
61 dwarf.FORM.data2,120 dw.FORM.data2,
62 dwarf.FORM.ref2,121 dw.FORM.ref2,
63 dwarf.FORM.data4,122 dw.FORM.data4,
64 dwarf.FORM.ref4,123 dw.FORM.ref4,
65 dwarf.FORM.data8,124 dw.FORM.data8,
66 dwarf.FORM.ref8,125 dw.FORM.ref8,
67 dwarf.FORM.ref_sig8,126 dw.FORM.ref_sig8,
68 dwarf.FORM.udata,127 dw.FORM.udata,
69 dwarf.FORM.ref_udata,128 dw.FORM.ref_udata,
70 dwarf.FORM.sdata,129 dw.FORM.sdata,
130 => {
131 _ = try p.readConstant(form);
132 },
133
134 dw.FORM.strp,
135 dw.FORM.string,
136 => {
137 _ = try p.readString(form, cuh);
138 },
139
140 else => if (cuh.version >= 5) switch (form) {
141 dw.FORM.strx,
142 dw.FORM.strx1,
143 dw.FORM.strx2,
144 dw.FORM.strx3,
145 dw.FORM.strx4,
71 => {146 => {
72 _ = try p.readConstant(attr.form);147 // We are just iterating over the __debug_info data, so we don't care about an actual
148 // string, therefore we set the `base = 0`.
149 _ = try p.readStringIndexed(form, cuh, 0);
73 },150 },
74151
75 dwarf.FORM.strp,152 dw.FORM.addrx,
76 dwarf.FORM.string,153 dw.FORM.addrx1,
154 dw.FORM.addrx2,
155 dw.FORM.addrx3,
156 dw.FORM.addrx4,
77 => {157 => {
78 _ = try p.readString(attr.form, cuh);158 _ = try p.readIndex(form);
79 },159 },
80160
81 else => {161 else => return error.UnknownForm,
82 // TODO better errors162 } else return error.UnknownForm,
83 log.err("unhandled DW_FORM_* value with identifier {x}", .{attr.form});
84 return error.UnhandledDwFormValue;
85 },
86 };
87 }163 }
88 }164 }
89165
90 pub fn readBlock(p: *InfoReader, form: Form) ![]const u8 {166 pub fn readBlock(p: *InfoReader, form: Form) ![]const u8 {
91 const len: u64 = switch (form) {167 const len: u64 = switch (form) {
92 dwarf.FORM.block1 => try p.readByte(),168 dw.FORM.block1 => try p.readByte(),
93 dwarf.FORM.block2 => try p.readInt(u16),169 dw.FORM.block2 => try p.readInt(u16),
94 dwarf.FORM.block4 => try p.readInt(u32),170 dw.FORM.block4 => try p.readInt(u32),
95 dwarf.FORM.block => try p.readUleb128(u64),171 dw.FORM.block => try p.readUleb128(u64),
96 else => unreachable,172 else => unreachable,
97 };173 };
98 return p.readNBytes(len);174 return p.readNBytes(len);
...@@ -105,52 +181,79 @@ pub const InfoReader = struct {...@@ -105,52 +181,79 @@ pub const InfoReader = struct {
105181
106 pub fn readConstant(p: *InfoReader, form: Form) !u64 {182 pub fn readConstant(p: *InfoReader, form: Form) !u64 {
107 return switch (form) {183 return switch (form) {
108 dwarf.FORM.data1, dwarf.FORM.ref1, dwarf.FORM.flag => try p.readByte(),184 dw.FORM.data1, dw.FORM.ref1, dw.FORM.flag => try p.readByte(),
109 dwarf.FORM.data2, dwarf.FORM.ref2 => try p.readInt(u16),185 dw.FORM.data2, dw.FORM.ref2 => try p.readInt(u16),
110 dwarf.FORM.data4, dwarf.FORM.ref4 => try p.readInt(u32),186 dw.FORM.data4, dw.FORM.ref4 => try p.readInt(u32),
111 dwarf.FORM.data8, dwarf.FORM.ref8, dwarf.FORM.ref_sig8 => try p.readInt(u64),187 dw.FORM.data8, dw.FORM.ref8, dw.FORM.ref_sig8 => try p.readInt(u64),
112 dwarf.FORM.udata, dwarf.FORM.ref_udata => try p.readUleb128(u64),188 dw.FORM.udata, dw.FORM.ref_udata => try p.readUleb128(u64),
113 dwarf.FORM.sdata => @bitCast(try p.readIleb128(i64)),189 dw.FORM.sdata => @bitCast(try p.readIleb128(i64)),
114 else => return error.UnhandledConstantForm,190 else => return error.UnhandledConstantForm,
115 };191 };
116 }192 }
117193
194 pub fn readIndex(p: *InfoReader, form: Form) !u64 {
195 return switch (form) {
196 dw.FORM.strx1, dw.FORM.addrx1 => try p.readByte(),
197 dw.FORM.strx2, dw.FORM.addrx2 => try p.readInt(u16),
198 dw.FORM.strx3, dw.FORM.addrx3 => error.UnhandledDwForm,
199 dw.FORM.strx4, dw.FORM.addrx4 => try p.readInt(u32),
200 dw.FORM.strx, dw.FORM.addrx => try p.readUleb128(u64),
201 else => return error.UnhandledIndexForm,
202 };
203 }
204
118 pub fn readString(p: *InfoReader, form: Form, cuh: CompileUnitHeader) ![:0]const u8 {205 pub fn readString(p: *InfoReader, form: Form, cuh: CompileUnitHeader) ![:0]const u8 {
119 switch (form) {206 switch (form) {
120 dwarf.FORM.strp => {207 dw.FORM.strp => {
121 const off = try p.readOffset(cuh.format);208 const off = try p.readOffset(cuh.format);
122 const off_u = math.cast(usize, off) orelse return error.Overflow;209 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);210 return mem.sliceTo(@as([*:0]const u8, @ptrCast(p.ctx.debug_str.ptr + off_u)), 0);
124 },211 },
125 dwarf.FORM.string => {212 dw.FORM.string => {
126 const start = p.pos;213 const start = p.pos;
127 while (p.pos < p.bytes.len) : (p.pos += 1) {214 while (p.pos < p.bytes().len) : (p.pos += 1) {
128 if (p.bytes[p.pos] == 0) break;215 if (p.bytes()[p.pos] == 0) break;
129 }216 }
130 if (p.bytes[p.pos] != 0) return error.Eof;217 if (p.bytes()[p.pos] != 0) return error.UnexpectedEndOfFile;
131 return p.bytes[start..p.pos :0];218 return p.bytes()[start..p.pos :0];
219 },
220 else => unreachable,
221 }
222 }
223
224 pub fn readStringIndexed(p: *InfoReader, form: Form, cuh: CompileUnitHeader, base: u64) ![:0]const u8 {
225 switch (form) {
226 dw.FORM.strx,
227 dw.FORM.strx1,
228 dw.FORM.strx2,
229 dw.FORM.strx3,
230 dw.FORM.strx4,
231 => {
232 const index = try p.readIndex(form);
233 const off = getOffset(p.ctx.debug_str_offsets, base, index, cuh.format);
234 return mem.sliceTo(@as([*:0]const u8, @ptrCast(p.ctx.debug_str.ptr + off)), 0);
132 },235 },
133 else => unreachable,236 else => unreachable,
134 }237 }
135 }238 }
136239
137 pub fn readByte(p: *InfoReader) !u8 {240 pub fn readByte(p: *InfoReader) !u8 {
138 if (p.pos + 1 > p.bytes.len) return error.Eof;241 if (p.pos + 1 > p.bytes().len) return error.UnexpectedEndOfFile;
139 defer p.pos += 1;242 defer p.pos += 1;
140 return p.bytes[p.pos];243 return p.bytes()[p.pos];
141 }244 }
142245
143 pub fn readNBytes(p: *InfoReader, num: u64) ![]const u8 {246 pub fn readNBytes(p: *InfoReader, num: u64) ![]const u8 {
144 const num_usize = math.cast(usize, num) orelse return error.Overflow;247 const num_usize = math.cast(usize, num) orelse return error.Overflow;
145 if (p.pos + num_usize > p.bytes.len) return error.Eof;248 if (p.pos + num_usize > p.bytes().len) return error.UnexpectedEndOfFile;
146 defer p.pos += num_usize;249 defer p.pos += num_usize;
147 return p.bytes[p.pos..][0..num_usize];250 return p.bytes()[p.pos..][0..num_usize];
148 }251 }
149252
150 pub fn readInt(p: *InfoReader, comptime Int: type) !Int {253 pub fn readInt(p: *InfoReader, comptime Int: type) !Int {
151 if (p.pos + @sizeOf(Int) > p.bytes.len) return error.Eof;254 if (p.pos + @sizeOf(Int) > p.bytes().len) return error.UnexpectedEndOfFile;
152 defer p.pos += @sizeOf(Int);255 defer p.pos += @sizeOf(Int);
153 return mem.readInt(Int, p.bytes[p.pos..][0..@sizeOf(Int)], .little);256 return mem.readInt(Int, p.bytes()[p.pos..][0..@sizeOf(Int)], .little);
154 }257 }
155258
156 pub fn readOffset(p: *InfoReader, dw_fmt: DwarfFormat) !u64 {259 pub fn readOffset(p: *InfoReader, dw_fmt: DwarfFormat) !u64 {
...@@ -161,7 +264,7 @@ pub const InfoReader = struct {...@@ -161,7 +264,7 @@ pub const InfoReader = struct {
161 }264 }
162265
163 pub fn readUleb128(p: *InfoReader, comptime Type: type) !Type {266 pub fn readUleb128(p: *InfoReader, comptime Type: type) !Type {
164 var stream = std.io.fixedBufferStream(p.bytes[p.pos..]);267 var stream = std.io.fixedBufferStream(p.bytes()[p.pos..]);
165 var creader = std.io.countingReader(stream.reader());268 var creader = std.io.countingReader(stream.reader());
166 const value: Type = try leb.readUleb128(Type, creader.reader());269 const value: Type = try leb.readUleb128(Type, creader.reader());
167 p.pos += math.cast(usize, creader.bytes_read) orelse return error.Overflow;270 p.pos += math.cast(usize, creader.bytes_read) orelse return error.Overflow;
...@@ -169,7 +272,7 @@ pub const InfoReader = struct {...@@ -169,7 +272,7 @@ pub const InfoReader = struct {
169 }272 }
170273
171 pub fn readIleb128(p: *InfoReader, comptime Type: type) !Type {274 pub fn readIleb128(p: *InfoReader, comptime Type: type) !Type {
172 var stream = std.io.fixedBufferStream(p.bytes[p.pos..]);275 var stream = std.io.fixedBufferStream(p.bytes()[p.pos..]);
173 var creader = std.io.countingReader(stream.reader());276 var creader = std.io.countingReader(stream.reader());
174 const value: Type = try leb.readIleb128(Type, creader.reader());277 const value: Type = try leb.readIleb128(Type, creader.reader());
175 p.pos += math.cast(usize, creader.bytes_read) orelse return error.Overflow;278 p.pos += math.cast(usize, creader.bytes_read) orelse return error.Overflow;
...@@ -182,11 +285,15 @@ pub const InfoReader = struct {...@@ -182,11 +285,15 @@ pub const InfoReader = struct {
182};285};
183286
184pub const AbbrevReader = struct {287pub const AbbrevReader = struct {
185 bytes: []const u8,288 ctx: Dwarf,
186 pos: usize = 0,289 pos: usize = 0,
187290
291 fn bytes(p: AbbrevReader) []const u8 {
292 return p.ctx.debug_abbrev;
293 }
294
188 pub fn hasMore(p: AbbrevReader) bool {295 pub fn hasMore(p: AbbrevReader) bool {
189 return p.pos < p.bytes.len;296 return p.pos < p.bytes().len;
190 }297 }
191298
192 pub fn readDecl(p: *AbbrevReader) !?AbbrevDecl {299 pub fn readDecl(p: *AbbrevReader) !?AbbrevDecl {
...@@ -218,13 +325,13 @@ pub const AbbrevReader = struct {...@@ -218,13 +325,13 @@ pub const AbbrevReader = struct {
218 }325 }
219326
220 pub fn readByte(p: *AbbrevReader) !u8 {327 pub fn readByte(p: *AbbrevReader) !u8 {
221 if (p.pos + 1 > p.bytes.len) return error.Eof;328 if (p.pos + 1 > p.bytes().len) return error.Eof;
222 defer p.pos += 1;329 defer p.pos += 1;
223 return p.bytes[p.pos];330 return p.bytes()[p.pos];
224 }331 }
225332
226 pub fn readUleb128(p: *AbbrevReader, comptime Type: type) !Type {333 pub fn readUleb128(p: *AbbrevReader, comptime Type: type) !Type {
227 var stream = std.io.fixedBufferStream(p.bytes[p.pos..]);334 var stream = std.io.fixedBufferStream(p.bytes()[p.pos..]);
228 var creader = std.io.countingReader(stream.reader());335 var creader = std.io.countingReader(stream.reader());
229 const value: Type = try leb.readUleb128(Type, creader.reader());336 const value: Type = try leb.readUleb128(Type, creader.reader());
230 p.pos += math.cast(usize, creader.bytes_read) orelse return error.Overflow;337 p.pos += math.cast(usize, creader.bytes_read) orelse return error.Overflow;
...@@ -254,9 +361,10 @@ const AbbrevAttr = struct {...@@ -254,9 +361,10 @@ const AbbrevAttr = struct {
254const CompileUnitHeader = struct {361const CompileUnitHeader = struct {
255 format: DwarfFormat,362 format: DwarfFormat,
256 length: u64,363 length: u64,
257 version: u16,364 version: Version,
258 debug_abbrev_offset: u64,365 debug_abbrev_offset: u64,
259 address_size: u8,366 address_size: u8,
367 unit_type: u8,
260};368};
261369
262const Die = struct {370const Die = struct {
...@@ -269,18 +377,24 @@ const DwarfFormat = enum {...@@ -269,18 +377,24 @@ const DwarfFormat = enum {
269 dwarf64,377 dwarf64,
270};378};
271379
272const dwarf = std.dwarf;380const dw = std.dwarf;
273const leb = std.leb;381const leb = std.leb;
274const log = std.log.scoped(.link);382const log = std.log.scoped(.link);
275const math = std.math;383const math = std.math;
276const mem = std.mem;384const mem = std.mem;
277const std = @import("std");385const std = @import("std");
278386const Allocator = mem.Allocator;
279const At = u64;387const Dwarf = @This();
280const Code = u64;388const File = @import("file.zig").File;
281const Form = u64;389const MachO = @import("../MachO.zig");
282const Tag = u64;390const Object = @import("Object.zig");
283391
284pub const AT = dwarf.AT;392pub const At = u64;
285pub const FORM = dwarf.FORM;393pub const Code = u64;
286pub const TAG = dwarf.TAG;394pub const Form = u64;
395pub const Tag = u64;
396pub const Version = u16;
397
398pub const AT = dw.AT;
399pub const FORM = dw.FORM;
400pub const TAG = dw.TAG;
src/link/MachO/Object.zig+94-132
...@@ -1359,151 +1359,102 @@ fn parseDebugInfo(self: *Object, macho_file: *MachO) !void {...@@ -1359,151 +1359,102 @@ fn parseDebugInfo(self: *Object, macho_file: *MachO) !void {
1359 defer tracy.end();1359 defer tracy.end();
13601360
1361 const gpa = macho_file.base.comp.gpa;1361 const gpa = macho_file.base.comp.gpa;
1362 const file = macho_file.getFileHandle(self.file_handle);
13621363
1363 var debug_info_index: ?usize = null;1364 var dwarf: Dwarf = .{};
1364 var debug_abbrev_index: ?usize = null;1365 defer dwarf.deinit(gpa);
1365 var debug_str_index: ?usize = null;
13661366
1367 for (self.sections.items(.header), 0..) |sect, index| {1367 for (self.sections.items(.header), 0..) |sect, index| {
1368 const n_sect: u8 = @intCast(index);
1368 if (sect.attrs() & macho.S_ATTR_DEBUG == 0) continue;1369 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_info")) {
1370 if (mem.eql(u8, sect.sectName(), "__debug_abbrev")) debug_abbrev_index = index;1371 dwarf.debug_info = try self.readSectionData(gpa, file, n_sect);
1371 if (mem.eql(u8, sect.sectName(), "__debug_str")) debug_str_index = index;1372 }
1373 if (mem.eql(u8, sect.sectName(), "__debug_abbrev")) {
1374 dwarf.debug_abbrev = try self.readSectionData(gpa, file, n_sect);
1375 }
1376 if (mem.eql(u8, sect.sectName(), "__debug_str")) {
1377 dwarf.debug_str = try self.readSectionData(gpa, file, n_sect);
1378 }
1379 if (mem.eql(u8, sect.sectName(), "__debug_str_offs")) {
1380 dwarf.debug_str_offsets = try self.readSectionData(gpa, file, n_sect);
1381 }
1372 }1382 }
13731383
1374 if (debug_info_index == null or debug_abbrev_index == null) return;1384 if (dwarf.debug_info.len == 0) return;
1375
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 };
1428
1429 const cuh = try info_reader.readCompileUnitHeader();
1430 try abbrev_reader.seekTo(cuh.debug_abbrev_offset);
1431
1432 const cu_decl = (try abbrev_reader.readDecl()) orelse return error.Eof;
1433 if (cu_decl.tag != dwarf.TAG.compile_unit) return error.UnexpectedTag;
1434
1435 try info_reader.seekToDie(cu_decl.code, cuh, &abbrev_reader);
1436
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 },
14501385
1451 dwarf.FORM.addr => {1386 self.compile_unit = try self.findCompileUnit(gpa, dwarf, macho_file);
1452 _ = try info_reader.readNBytes(cuh.address_size);1387}
1453 },
14541388
1455 dwarf.FORM.block1,1389fn findCompileUnit(self: *Object, gpa: Allocator, ctx: Dwarf, macho_file: *MachO) !CompileUnit {
1456 dwarf.FORM.block2,1390 var info_reader = Dwarf.InfoReader{ .ctx = ctx };
1457 dwarf.FORM.block4,1391 var abbrev_reader = Dwarf.AbbrevReader{ .ctx = ctx };
1458 dwarf.FORM.block,
1459 => {
1460 _ = try info_reader.readBlock(attr.form);
1461 },
14621392
1463 dwarf.FORM.exprloc => {1393 const cuh = try info_reader.readCompileUnitHeader(macho_file);
1464 _ = try info_reader.readExprLoc();1394 try abbrev_reader.seekTo(cuh.debug_abbrev_offset);
1465 },
14661395
1467 dwarf.FORM.flag_present => {},1396 const cu_decl = (try abbrev_reader.readDecl()) orelse return error.UnexpectedEndOfFile;
14681397 if (cu_decl.tag != Dwarf.TAG.compile_unit) return error.UnexpectedTag;
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 },
14851398
1486 dwarf.FORM.strp,1399 try info_reader.seekToDie(cu_decl.code, cuh, &abbrev_reader, macho_file);
1487 dwarf.FORM.string,
1488 => {
1489 _ = try info_reader.readString(attr.form, cuh);
1490 },
14911400
1492 else => {1401 const Pos = struct {
1493 // TODO actual errors?1402 pos: usize,
1494 log.err("unhandled DW_FORM_* value with identifier {x}", .{attr.form});1403 form: Dwarf.Form,
1495 return error.UnhandledForm;
1496 },
1497 },
1498 };1404 };
14991405 var saved: struct {
1500 if (cu_wip.comp_dir == null) return error.MissingCompDir;1406 tu_name: ?Pos,
1501 if (cu_wip.tu_name == null) return error.MissingTuName;1407 comp_dir: ?Pos,
15021408 str_offsets_base: ?Pos,
1503 return .{1409 } = .{
1504 .comp_dir = try self.addString(gpa, cu_wip.comp_dir.?),1410 .tu_name = null,
1505 .tu_name = try self.addString(gpa, cu_wip.tu_name.?),1411 .comp_dir = null,
1412 .str_offsets_base = null,
1506 };1413 };
1414 while (try abbrev_reader.readAttr()) |attr| {
1415 const pos: Pos = .{ .pos = info_reader.pos, .form = attr.form };
1416 switch (attr.at) {
1417 Dwarf.AT.name => saved.tu_name = pos,
1418 Dwarf.AT.comp_dir => saved.comp_dir = pos,
1419 Dwarf.AT.str_offsets_base => saved.str_offsets_base = pos,
1420 else => {},
1421 }
1422 try info_reader.skip(attr.form, cuh, macho_file);
1423 }
1424
1425 if (saved.comp_dir == null) return error.MissingCompDir;
1426 if (saved.tu_name == null) return error.MissingTuName;
1427
1428 const str_offsets_base: ?u64 = if (saved.str_offsets_base) |str_offsets_base| str_offsets_base: {
1429 try info_reader.seekTo(str_offsets_base.pos);
1430 break :str_offsets_base try info_reader.readOffset(cuh.format);
1431 } else null;
1432
1433 var cu: CompileUnit = .{ .comp_dir = .{}, .tu_name = .{} };
1434 for (&[_]struct { Pos, *MachO.String }{
1435 .{ saved.comp_dir.?, &cu.comp_dir },
1436 .{ saved.tu_name.?, &cu.tu_name },
1437 }) |tuple| {
1438 const pos, const str_offset_ptr = tuple;
1439 try info_reader.seekTo(pos.pos);
1440 str_offset_ptr.* = switch (pos.form) {
1441 Dwarf.FORM.strp,
1442 Dwarf.FORM.string,
1443 => try self.addString(gpa, try info_reader.readString(pos.form, cuh)),
1444 Dwarf.FORM.strx,
1445 Dwarf.FORM.strx1,
1446 Dwarf.FORM.strx2,
1447 Dwarf.FORM.strx3,
1448 Dwarf.FORM.strx4,
1449 => blk: {
1450 const base = str_offsets_base orelse return error.MalformedDwarf;
1451 break :blk try self.addString(gpa, try info_reader.readStringIndexed(pos.form, cuh, base));
1452 },
1453 else => return error.InvalidForm,
1454 };
1455 }
1456
1457 return cu;
1507}1458}
15081459
1509pub fn resolveSymbols(self: *Object, macho_file: *MachO) !void {1460pub fn resolveSymbols(self: *Object, macho_file: *MachO) !void {
...@@ -2561,6 +2512,17 @@ pub fn getUnwindRecord(self: *Object, index: UnwindInfo.Record.Index) *UnwindInf...@@ -2561,6 +2512,17 @@ pub fn getUnwindRecord(self: *Object, index: UnwindInfo.Record.Index) *UnwindInf
2561 return &self.unwind_records.items[index];2512 return &self.unwind_records.items[index];
2562}2513}
25632514
2515/// Caller owns the memory.
2516pub fn readSectionData(self: Object, allocator: Allocator, file: File.Handle, n_sect: u8) ![]u8 {
2517 const header = self.sections.items(.header)[n_sect];
2518 const size = math.cast(usize, header.size) orelse return error.Overflow;
2519 const data = try allocator.alloc(u8, size);
2520 const amt = try file.preadAll(data, header.offset + self.offset);
2521 errdefer allocator.free(data);
2522 if (amt != data.len) return error.InputOutput;
2523 return data;
2524}
2525
2564pub fn format(2526pub fn format(
2565 self: *Object,2527 self: *Object,
2566 comptime unused_fmt_string: []const u8,2528 comptime unused_fmt_string: []const u8,
...@@ -3219,7 +3181,6 @@ const aarch64 = struct {...@@ -3219,7 +3181,6 @@ const aarch64 = struct {
3219};3181};
32203182
3221const assert = std.debug.assert;3183const assert = std.debug.assert;
3222const dwarf = @import("dwarf.zig");
3223const eh_frame = @import("eh_frame.zig");3184const eh_frame = @import("eh_frame.zig");
3224const log = std.log.scoped(.link);3185const log = std.log.scoped(.link);
3225const macho = std.macho;3186const macho = std.macho;
...@@ -3233,6 +3194,7 @@ const Allocator = mem.Allocator;...@@ -3233,6 +3194,7 @@ const Allocator = mem.Allocator;
3233const Archive = @import("Archive.zig");3194const Archive = @import("Archive.zig");
3234const Atom = @import("Atom.zig");3195const Atom = @import("Atom.zig");
3235const Cie = eh_frame.Cie;3196const Cie = eh_frame.Cie;
3197const Dwarf = @import("Dwarf.zig");
3236const Fde = eh_frame.Fde;3198const Fde = eh_frame.Fde;
3237const File = @import("file.zig").File;3199const File = @import("file.zig").File;
3238const LoadCommandIterator = macho.LoadCommandIterator;3200const LoadCommandIterator = macho.LoadCommandIterator;