| author | |
| committer | |
| log | ed7073c6300d6ae3ec4b370c1e13c2bbf11bc08c |
| tree | 2c6dcf4349e4f710a3d71cec3b47b8b31636726f |
| parent | fb88cfdf6aa3fabba700d8340f025e4a3e0d3fb2 |
6 files changed, 420 insertions(+), 523 deletions(-)
CMakeLists.txt+1-1| ... | @@ -613,7 +613,6 @@ set(ZIG_STAGE2_SOURCES | ... | @@ -613,7 +613,6 @@ set(ZIG_STAGE2_SOURCES |
| 613 | src/link/MachO/Atom.zig | 613 | src/link/MachO/Atom.zig |
| 614 | src/link/MachO/CodeSignature.zig | 614 | src/link/MachO/CodeSignature.zig |
| 615 | src/link/MachO/DebugSymbols.zig | 615 | src/link/MachO/DebugSymbols.zig |
| 616 | src/link/MachO/DwarfInfo.zig | ||
| 617 | src/link/MachO/Dylib.zig | 616 | src/link/MachO/Dylib.zig |
| 618 | src/link/MachO/InternalObject.zig | 617 | src/link/MachO/InternalObject.zig |
| 619 | src/link/MachO/Object.zig | 618 | src/link/MachO/Object.zig |
| ... | @@ -625,6 +624,7 @@ set(ZIG_STAGE2_SOURCES | ... | @@ -625,6 +624,7 @@ set(ZIG_STAGE2_SOURCES |
| 625 | src/link/MachO/dyld_info/Rebase.zig | 624 | src/link/MachO/dyld_info/Rebase.zig |
| 626 | src/link/MachO/dyld_info/Trie.zig | 625 | src/link/MachO/dyld_info/Trie.zig |
| 627 | src/link/MachO/dyld_info/bind.zig | 626 | src/link/MachO/dyld_info/bind.zig |
| 627 | src/link/MachO/dwarf.zig | ||
| 628 | src/link/MachO/eh_frame.zig | 628 | src/link/MachO/eh_frame.zig |
| 629 | src/link/MachO/fat.zig | 629 | src/link/MachO/fat.zig |
| 630 | src/link/MachO/file.zig | 630 | 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 | ... | @@ -535,6 +535,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node |
| 535 | 535 | ||
| 536 | try self.addUndefinedGlobals(); | 536 | try self.addUndefinedGlobals(); |
| 537 | try self.resolveSymbols(); | 537 | try self.resolveSymbols(); |
| 538 | try self.parseDebugInfo(); | ||
| 538 | try self.resolveSyntheticSymbols(); | 539 | try self.resolveSyntheticSymbols(); |
| 539 | 540 | ||
| 540 | try self.convertTentativeDefinitions(); | 541 | try self.convertTentativeDefinitions(); |
| ... | @@ -1409,6 +1410,12 @@ fn markLive(self: *MachO) void { | ... | @@ -1409,6 +1410,12 @@ fn markLive(self: *MachO) void { |
| 1409 | } | 1410 | } |
| 1410 | } | 1411 | } |
| 1411 | 1412 | ||
| 1413 | pub fn parseDebugInfo(self: *MachO) !void { | ||
| 1414 | for (self.objects.items) |index| { | ||
| 1415 | try self.getFile(index).?.object.parseDebugInfo(self); | ||
| 1416 | } | ||
| 1417 | } | ||
| 1418 | |||
| 1412 | fn resolveSyntheticSymbols(self: *MachO) !void { | 1419 | fn resolveSyntheticSymbols(self: *MachO) !void { |
| 1413 | const internal = self.getInternalObject() orelse return; | 1420 | const internal = self.getInternalObject() orelse return; |
| 1414 | 1421 | ||
| ... | @@ -4840,7 +4847,6 @@ const Cache = std.Build.Cache; | ... | @@ -4840,7 +4847,6 @@ const Cache = std.Build.Cache; |
| 4840 | const CodeSignature = @import("MachO/CodeSignature.zig"); | 4847 | const CodeSignature = @import("MachO/CodeSignature.zig"); |
| 4841 | const Compilation = @import("../Compilation.zig"); | 4848 | const Compilation = @import("../Compilation.zig"); |
| 4842 | pub const DebugSymbols = @import("MachO/DebugSymbols.zig"); | 4849 | pub const DebugSymbols = @import("MachO/DebugSymbols.zig"); |
| 4843 | const DwarfInfo = @import("MachO/DwarfInfo.zig"); | ||
| 4844 | const Dylib = @import("MachO/Dylib.zig"); | 4850 | const Dylib = @import("MachO/Dylib.zig"); |
| 4845 | const ExportTrieSection = synthetic.ExportTrieSection; | 4851 | const ExportTrieSection = synthetic.ExportTrieSection; |
| 4846 | const File = @import("MachO/file.zig").File; | 4852 | const 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 | ||
| 2 | abbrev_tables: std.AutoArrayHashMapUnmanaged(u64, AbbrevTable) = .{}, | ||
| 3 | /// List of compile units as they appear in the .debug_info bytestream | ||
| 4 | compile_units: std.ArrayListUnmanaged(CompileUnit) = .{}, | ||
| 5 | /// Debug info string table | ||
| 6 | strtab: std.ArrayListUnmanaged(u8) = .{}, | ||
| 7 | /// Debug info data | ||
| 8 | di_data: std.ArrayListUnmanaged(u8) = .{}, | ||
| 9 | |||
| 10 | pub 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 | |||
| 17 | pub 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 | |||
| 27 | fn 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 | |||
| 34 | fn 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 | |||
| 39 | fn 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 | |||
| 91 | fn 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 | |||
| 125 | fn 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 | |||
| 173 | fn 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 | |||
| 258 | fn 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 | |||
| 265 | pub 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 | |||
| 277 | pub 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 | |||
| 290 | pub const Attr = struct { | ||
| 291 | at: At, | ||
| 292 | form: Form, | ||
| 293 | }; | ||
| 294 | |||
| 295 | pub const At = u64; | ||
| 296 | pub const Code = u64; | ||
| 297 | pub const Form = u64; | ||
| 298 | pub const Tag = u64; | ||
| 299 | |||
| 300 | pub const CompileUnitHeader = struct { | ||
| 301 | format: Format, | ||
| 302 | length: u64, | ||
| 303 | version: u16, | ||
| 304 | debug_abbrev_offset: u64, | ||
| 305 | address_size: u8, | ||
| 306 | }; | ||
| 307 | |||
| 308 | pub 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 | |||
| 354 | pub 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 | |||
| 376 | pub 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 | |||
| 469 | pub const Format = enum { | ||
| 470 | dwarf32, | ||
| 471 | dwarf64, | ||
| 472 | }; | ||
| 473 | |||
| 474 | const DebugInfo = struct { | ||
| 475 | debug_info: []const u8, | ||
| 476 | debug_abbrev: []const u8, | ||
| 477 | debug_str: []const u8, | ||
| 478 | }; | ||
| 479 | |||
| 480 | const assert = std.debug.assert; | ||
| 481 | const dwarf = std.dwarf; | ||
| 482 | const leb = std.leb; | ||
| 483 | const log = std.log.scoped(.link); | ||
| 484 | const mem = std.mem; | ||
| 485 | const std = @import("std"); | ||
| 486 | const trace = @import("../../tracy.zig").trace; | ||
| 487 | |||
| 488 | const Allocator = mem.Allocator; | ||
| 489 | const DwarfInfo = @This(); | ||
| 490 | const MachO = @import("../MachO.zig"); | ||
src/link/MachO/Object.zig+126-31| ... | @@ -13,7 +13,7 @@ symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, | ... | @@ -13,7 +13,7 @@ symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, |
| 13 | atoms: std.ArrayListUnmanaged(Atom.Index) = .{}, | 13 | atoms: std.ArrayListUnmanaged(Atom.Index) = .{}, |
| 14 | 14 | ||
| 15 | platform: ?MachO.Platform = null, | 15 | platform: ?MachO.Platform = null, |
| 16 | dwarf_info: ?DwarfInfo = null, | 16 | compile_unit: ?CompileUnit = null, |
| 17 | stab_files: std.ArrayListUnmanaged(StabFile) = .{}, | 17 | stab_files: std.ArrayListUnmanaged(StabFile) = .{}, |
| 18 | 18 | ||
| 19 | eh_frame_sect_index: ?u8 = null, | 19 | eh_frame_sect_index: ?u8 = null, |
| ... | @@ -31,12 +31,6 @@ dynamic_relocs: MachO.DynamicRelocs = .{}, | ... | @@ -31,12 +31,6 @@ dynamic_relocs: MachO.DynamicRelocs = .{}, |
| 31 | output_symtab_ctx: MachO.SymtabCtx = .{}, | 31 | output_symtab_ctx: MachO.SymtabCtx = .{}, |
| 32 | output_ar_state: Archive.ArState = .{}, | 32 | output_ar_state: Archive.ArState = .{}, |
| 33 | 33 | ||
| 34 | const InArchive = struct { | ||
| 35 | path: []const u8, | ||
| 36 | offset: u64, | ||
| 37 | size: u32, | ||
| 38 | }; | ||
| 39 | |||
| 40 | pub fn isObject(path: []const u8) !bool { | 34 | pub fn isObject(path: []const u8) !bool { |
| 41 | const file = try std.fs.cwd().openFile(path, .{}); | 35 | const file = try std.fs.cwd().openFile(path, .{}); |
| 42 | defer file.close(); | 36 | defer file.close(); |
| ... | @@ -60,7 +54,6 @@ pub fn deinit(self: *Object, allocator: Allocator) void { | ... | @@ -60,7 +54,6 @@ pub fn deinit(self: *Object, allocator: Allocator) void { |
| 60 | self.fdes.deinit(allocator); | 54 | self.fdes.deinit(allocator); |
| 61 | self.eh_frame_data.deinit(allocator); | 55 | self.eh_frame_data.deinit(allocator); |
| 62 | self.unwind_records.deinit(allocator); | 56 | self.unwind_records.deinit(allocator); |
| 63 | if (self.dwarf_info) |*dw| dw.deinit(allocator); | ||
| 64 | for (self.stab_files.items) |*sf| { | 57 | for (self.stab_files.items) |*sf| { |
| 65 | sf.stabs.deinit(allocator); | 58 | sf.stabs.deinit(allocator); |
| 66 | } | 59 | } |
| ... | @@ -251,8 +244,6 @@ pub fn parse(self: *Object, macho_file: *MachO) !void { | ... | @@ -251,8 +244,6 @@ pub fn parse(self: *Object, macho_file: *MachO) !void { |
| 251 | // } | 244 | // } |
| 252 | } | 245 | } |
| 253 | 246 | ||
| 254 | try self.initDwarfInfo(macho_file); | ||
| 255 | |||
| 256 | for (self.atoms.items) |atom_index| { | 247 | for (self.atoms.items) |atom_index| { |
| 257 | const atom = macho_file.getAtom(atom_index).?; | 248 | const atom = macho_file.getAtom(atom_index).?; |
| 258 | const isec = atom.getInputSection(macho_file); | 249 | const isec = atom.getInputSection(macho_file); |
| ... | @@ -1214,7 +1205,7 @@ fn parseUnwindRecords(self: *Object, macho_file: *MachO) !void { | ... | @@ -1214,7 +1205,7 @@ fn parseUnwindRecords(self: *Object, macho_file: *MachO) !void { |
| 1214 | /// and record that so that we can emit symbol stabs. | 1205 | /// and record that so that we can emit symbol stabs. |
| 1215 | /// TODO in the future, we want parse debug info and debug line sections so that | 1206 | /// TODO in the future, we want parse debug info and debug line sections so that |
| 1216 | /// we can provide nice error locations to the user. | 1207 | /// we can provide nice error locations to the user. |
| 1217 | fn initDwarfInfo(self: *Object, macho_file: *MachO) !void { | 1208 | pub fn parseDebugInfo(self: *Object, macho_file: *MachO) !void { |
| 1218 | const tracy = trace(@src()); | 1209 | const tracy = trace(@src()); |
| 1219 | defer tracy.end(); | 1210 | defer tracy.end(); |
| 1220 | 1211 | ||
| ... | @@ -1240,17 +1231,107 @@ fn initDwarfInfo(self: *Object, macho_file: *MachO) !void { | ... | @@ -1240,17 +1231,107 @@ fn initDwarfInfo(self: *Object, macho_file: *MachO) !void { |
| 1240 | const debug_str = if (debug_str_index) |index| try self.getSectionData(@intCast(index), macho_file) else &[0]u8{}; | 1231 | const debug_str = if (debug_str_index) |index| try self.getSectionData(@intCast(index), macho_file) else &[0]u8{}; |
| 1241 | defer gpa.free(debug_str); | 1232 | defer gpa.free(debug_str); |
| 1242 | 1233 | ||
| 1243 | var dwarf_info = DwarfInfo{}; | 1234 | self.compile_unit = self.findCompileUnit(.{ |
| 1244 | errdefer dwarf_info.deinit(gpa); | 1235 | .gpa = gpa, |
| 1245 | dwarf_info.init(gpa, .{ | ||
| 1246 | .debug_info = debug_info, | 1236 | .debug_info = debug_info, |
| 1247 | .debug_abbrev = debug_abbrev, | 1237 | .debug_abbrev = debug_abbrev, |
| 1248 | .debug_str = debug_str, | 1238 | .debug_str = debug_str, |
| 1249 | }) catch { | 1239 | }) catch null; // TODO figure out what errors are fatal, and when we silently fail |
| 1250 | try macho_file.reportParseError2(self.index, "invalid __DWARF info found", .{}); | 1240 | } |
| 1251 | return error.MalformedObject; | 1241 | |
| 1242 | fn 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.?), | ||
| 1252 | }; | 1334 | }; |
| 1253 | self.dwarf_info = dwarf_info; | ||
| 1254 | } | 1335 | } |
| 1255 | 1336 | ||
| 1256 | pub fn resolveSymbols(self: *Object, macho_file: *MachO) void { | 1337 | pub fn resolveSymbols(self: *Object, macho_file: *MachO) void { |
| ... | @@ -1591,10 +1672,9 @@ pub fn calcSymtabSize(self: *Object, macho_file: *MachO) !void { | ... | @@ -1591,10 +1672,9 @@ pub fn calcSymtabSize(self: *Object, macho_file: *MachO) !void { |
| 1591 | } | 1672 | } |
| 1592 | 1673 | ||
| 1593 | pub fn calcStabsSize(self: *Object, macho_file: *MachO) error{Overflow}!void { | 1674 | pub fn calcStabsSize(self: *Object, macho_file: *MachO) error{Overflow}!void { |
| 1594 | if (self.dwarf_info) |dw| { | 1675 | if (self.compile_unit) |cu| { |
| 1595 | const cu = dw.compile_units.items[0]; | 1676 | const comp_dir = cu.getCompDir(self); |
| 1596 | const comp_dir = try cu.getCompileDir(dw) orelse return; | 1677 | const tu_name = cu.getTuName(self); |
| 1597 | const tu_name = try cu.getSourceFile(dw) orelse return; | ||
| 1598 | 1678 | ||
| 1599 | self.output_symtab_ctx.nstabs += 4; // N_SO, N_SO, N_OSO, N_SO | 1679 | self.output_symtab_ctx.nstabs += 4; // N_SO, N_SO, N_OSO, N_SO |
| 1600 | self.output_symtab_ctx.strsize += @as(u32, @intCast(comp_dir.len + 1)); // comp_dir | 1680 | 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 | ... | @@ -1709,10 +1789,9 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O |
| 1709 | 1789 | ||
| 1710 | var index = self.output_symtab_ctx.istab; | 1790 | var index = self.output_symtab_ctx.istab; |
| 1711 | 1791 | ||
| 1712 | if (self.dwarf_info) |dw| { | 1792 | if (self.compile_unit) |cu| { |
| 1713 | const cu = dw.compile_units.items[0]; | 1793 | const comp_dir = cu.getCompDir(self); |
| 1714 | const comp_dir = try cu.getCompileDir(dw) orelse return; | 1794 | const tu_name = cu.getTuName(self); |
| 1715 | const tu_name = try cu.getSourceFile(dw) orelse return; | ||
| 1716 | 1795 | ||
| 1717 | // Open scope | 1796 | // Open scope |
| 1718 | // N_SO comp_dir | 1797 | // N_SO comp_dir |
| ... | @@ -1958,10 +2037,7 @@ pub fn hasEhFrameRecords(self: Object) bool { | ... | @@ -1958,10 +2037,7 @@ pub fn hasEhFrameRecords(self: Object) bool { |
| 1958 | } | 2037 | } |
| 1959 | 2038 | ||
| 1960 | pub fn hasDebugInfo(self: Object) bool { | 2039 | pub fn hasDebugInfo(self: Object) bool { |
| 1961 | if (self.dwarf_info) |dw| { | 2040 | return self.compile_unit != null or self.hasSymbolStabs(); |
| 1962 | return dw.compile_units.items.len > 0; | ||
| 1963 | } | ||
| 1964 | return self.hasSymbolStabs(); | ||
| 1965 | } | 2041 | } |
| 1966 | 2042 | ||
| 1967 | fn hasSymbolStabs(self: Object) bool { | 2043 | fn hasSymbolStabs(self: Object) bool { |
| ... | @@ -2194,6 +2270,25 @@ const StabFile = struct { | ... | @@ -2194,6 +2270,25 @@ const StabFile = struct { |
| 2194 | }; | 2270 | }; |
| 2195 | }; | 2271 | }; |
| 2196 | 2272 | ||
| 2273 | const 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 | |||
| 2286 | const InArchive = struct { | ||
| 2287 | path: []const u8, | ||
| 2288 | offset: u64, | ||
| 2289 | size: u32, | ||
| 2290 | }; | ||
| 2291 | |||
| 2197 | const x86_64 = struct { | 2292 | const x86_64 = struct { |
| 2198 | fn parseRelocs( | 2293 | fn parseRelocs( |
| 2199 | self: *const Object, | 2294 | self: *const Object, |
| ... | @@ -2548,6 +2643,7 @@ const aarch64 = struct { | ... | @@ -2548,6 +2643,7 @@ const aarch64 = struct { |
| 2548 | }; | 2643 | }; |
| 2549 | 2644 | ||
| 2550 | const assert = std.debug.assert; | 2645 | const assert = std.debug.assert; |
| 2646 | const dwarf = @import("dwarf.zig"); | ||
| 2551 | const eh_frame = @import("eh_frame.zig"); | 2647 | const eh_frame = @import("eh_frame.zig"); |
| 2552 | const log = std.log.scoped(.link); | 2648 | const log = std.log.scoped(.link); |
| 2553 | const macho = std.macho; | 2649 | const macho = std.macho; |
| ... | @@ -2560,7 +2656,6 @@ const Allocator = mem.Allocator; | ... | @@ -2560,7 +2656,6 @@ const Allocator = mem.Allocator; |
| 2560 | const Archive = @import("Archive.zig"); | 2656 | const Archive = @import("Archive.zig"); |
| 2561 | const Atom = @import("Atom.zig"); | 2657 | const Atom = @import("Atom.zig"); |
| 2562 | const Cie = eh_frame.Cie; | 2658 | const Cie = eh_frame.Cie; |
| 2563 | const DwarfInfo = @import("DwarfInfo.zig"); | ||
| 2564 | const Fde = eh_frame.Fde; | 2659 | const Fde = eh_frame.Fde; |
| 2565 | const File = @import("file.zig").File; | 2660 | const File = @import("file.zig").File; |
| 2566 | const LoadCommandIterator = macho.LoadCommandIterator; | 2661 | const LoadCommandIterator = macho.LoadCommandIterator; |
src/link/MachO/dwarf.zig created+285| ... | @@ -0,0 +1,285 @@ | ||
| 1 | pub 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 | |||
| 183 | pub 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 | |||
| 238 | const AbbrevDecl = struct { | ||
| 239 | code: Code, | ||
| 240 | pos: usize, | ||
| 241 | len: usize, | ||
| 242 | tag: Tag, | ||
| 243 | has_children: bool, | ||
| 244 | }; | ||
| 245 | |||
| 246 | const AbbrevAttr = struct { | ||
| 247 | at: At, | ||
| 248 | form: Form, | ||
| 249 | pos: usize, | ||
| 250 | len: usize, | ||
| 251 | }; | ||
| 252 | |||
| 253 | const CompileUnitHeader = struct { | ||
| 254 | format: DwarfFormat, | ||
| 255 | length: u64, | ||
| 256 | version: u16, | ||
| 257 | debug_abbrev_offset: u64, | ||
| 258 | address_size: u8, | ||
| 259 | }; | ||
| 260 | |||
| 261 | const Die = struct { | ||
| 262 | pos: usize, | ||
| 263 | len: usize, | ||
| 264 | }; | ||
| 265 | |||
| 266 | const DwarfFormat = enum { | ||
| 267 | dwarf32, | ||
| 268 | dwarf64, | ||
| 269 | }; | ||
| 270 | |||
| 271 | const dwarf = std.dwarf; | ||
| 272 | const leb = std.leb; | ||
| 273 | const log = std.log.scoped(.link); | ||
| 274 | const math = std.math; | ||
| 275 | const mem = std.mem; | ||
| 276 | const std = @import("std"); | ||
| 277 | |||
| 278 | const At = u64; | ||
| 279 | const Code = u64; | ||
| 280 | const Form = u64; | ||
| 281 | const Tag = u64; | ||
| 282 | |||
| 283 | pub const AT = dwarf.AT; | ||
| 284 | pub const FORM = dwarf.FORM; | ||
| 285 | pub 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 | ... | @@ -46,6 +46,7 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]c |
| 46 | 46 | ||
| 47 | try macho_file.addUndefinedGlobals(); | 47 | try macho_file.addUndefinedGlobals(); |
| 48 | try macho_file.resolveSymbols(); | 48 | try macho_file.resolveSymbols(); |
| 49 | try macho_file.parseDebugInfo(); | ||
| 49 | try macho_file.dedupLiterals(); | 50 | try macho_file.dedupLiterals(); |
| 50 | markExports(macho_file); | 51 | markExports(macho_file); |
| 51 | claimUnresolved(macho_file); | 52 | claimUnresolved(macho_file); |