authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-12-29 22:43:07+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-12-31 10:19:04+01:00
log2875a7335aa5363303019dae8b837036ed547d53
tree117efb4c2d8ae614b701b8a07a716db97a7e4a43
parentd4725cb40bf378959f254ba447a47f5e2f5726fa

macho: add Elf dwarf sections


3 files changed, 1035 insertions(+), 25 deletions(-)

src/link/MachO.zig+301-9
......@@ -11,9 +11,10 @@ const codegen = @import("../codegen.zig");
1111const aarch64 = @import("../codegen/aarch64.zig");
1212const math = std.math;
1313const mem = std.mem;
14const DW = std.dwarf;
15const leb = std.leb;
1416
1517const trace = @import("../tracy.zig").trace;
16const Type = @import("../type.zig").Type;
1718const build_options = @import("build_options");
1819const Module = @import("../Module.zig");
1920const Compilation = @import("../Compilation.zig");
......@@ -108,9 +109,6 @@ dyld_stub_binder_index: ?u16 = null,
108109/// Table of symbol names aka the string table.
109110string_table: std.ArrayListUnmanaged(u8) = .{},
110111
111/// Table of debug symbol names aka the debug string table.
112debug_string_table: std.ArrayListUnmanaged(u8) = .{},
113
114112/// Table of trampolines to the actual symbols in __text section.
115113offset_table: std.ArrayListUnmanaged(u64) = .{},
116114
......@@ -207,12 +205,25 @@ pub const TextBlock = struct {
207205 prev: ?*TextBlock,
208206 next: ?*TextBlock,
209207
208 /// Previous/next linked list pointers. This value is `next ^ prev`.
209 /// This is the linked list node for this Decl's corresponding .debug_info tag.
210 dbg_info_prev: ?*TextBlock,
211 dbg_info_next: ?*TextBlock,
212 /// Offset into .debug_info pointing to the tag for this Decl.
213 dbg_info_off: u32,
214 /// Size of the .debug_info tag for this Decl, not including padding.
215 dbg_info_len: u32,
216
210217 pub const empty = TextBlock{
211218 .local_sym_index = 0,
212219 .offset_table_index = undefined,
213220 .size = 0,
214221 .prev = null,
215222 .next = null,
223 .dbg_info_prev = null,
224 .dbg_info_next = null,
225 .dbg_info_off = undefined,
226 .dbg_info_len = undefined,
216227 };
217228
218229 /// Returns how much room there is to grow in virtual address space.
......@@ -248,7 +259,23 @@ pub const Export = struct {
248259};
249260
250261pub const SrcFn = struct {
251 pub const empty = SrcFn{};
262 /// Offset from the beginning of the Debug Line Program header that contains this function.
263 off: u32,
264 /// Size of the line number program component belonging to this function, not
265 /// including padding.
266 len: u32,
267
268 /// Points to the previous and next neighbors, based on the offset from .debug_line.
269 /// This can be used to find, for example, the capacity of this `SrcFn`.
270 prev: ?*SrcFn,
271 next: ?*SrcFn,
272
273 pub const empty: SrcFn = .{
274 .off = 0,
275 .len = 0,
276 .prev = null,
277 .next = null,
278 };
252279};
253280
254281pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*MachO {
......@@ -361,7 +388,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
361388
362389 if (self.d_sym) |*ds| {
363390 // Flush debug symbols bundle.
364 try ds.flush(self.base.allocator);
391 try ds.flushModule(self.base.allocator, self.base.options);
365392 }
366393
367394 if (target.cpu.arch == .aarch64) {
......@@ -983,7 +1010,6 @@ pub fn deinit(self: *MachO) void {
9831010 self.text_block_free_list.deinit(self.base.allocator);
9841011 self.offset_table.deinit(self.base.allocator);
9851012 self.offset_table_free_list.deinit(self.base.allocator);
986 self.debug_string_table.deinit(self.base.allocator);
9871013 self.string_table.deinit(self.base.allocator);
9881014 self.undef_symbols.deinit(self.base.allocator);
9891015 self.global_symbols.deinit(self.base.allocator);
......@@ -1091,8 +1117,126 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
10911117 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
10921118 defer code_buffer.deinit();
10931119
1120 var dbg_line_buffer = std.ArrayList(u8).init(self.base.allocator);
1121 defer dbg_line_buffer.deinit();
1122
1123 var dbg_info_buffer = std.ArrayList(u8).init(self.base.allocator);
1124 defer dbg_info_buffer.deinit();
1125
1126 var dbg_info_type_relocs: File.DbgInfoTypeRelocsTable = .{};
1127 defer {
1128 var it = dbg_info_type_relocs.iterator();
1129 while (it.next()) |entry| {
1130 entry.value.relocs.deinit(self.base.allocator);
1131 }
1132 dbg_info_type_relocs.deinit(self.base.allocator);
1133 }
1134
10941135 const typed_value = decl.typed_value.most_recent.typed_value;
1095 const res = try codegen.generateSymbol(&self.base, decl.src(), typed_value, &code_buffer, .none);
1136 const is_fn: bool = switch (typed_value.ty.zigTypeTag()) {
1137 .Fn => true,
1138 else => false,
1139 };
1140 if (is_fn) {
1141 const zir_dumps = if (std.builtin.is_test) &[0][]const u8{} else build_options.zir_dumps;
1142 if (zir_dumps.len != 0) {
1143 for (zir_dumps) |fn_name| {
1144 if (mem.eql(u8, mem.spanZ(decl.name), fn_name)) {
1145 std.debug.print("\n{}\n", .{decl.name});
1146 typed_value.val.cast(Value.Payload.Function).?.func.dump(module.*);
1147 }
1148 }
1149 }
1150
1151 // For functions we need to add a prologue to the debug line program.
1152 try dbg_line_buffer.ensureCapacity(26);
1153
1154 const line_off: u28 = blk: {
1155 if (decl.scope.cast(Module.Scope.Container)) |container_scope| {
1156 const tree = container_scope.file_scope.contents.tree;
1157 const file_ast_decls = tree.root_node.decls();
1158 // TODO Look into improving the performance here by adding a token-index-to-line
1159 // lookup table. Currently this involves scanning over the source code for newlines.
1160 const fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?;
1161 const block = fn_proto.getBodyNode().?.castTag(.Block).?;
1162 const line_delta = std.zig.lineDelta(tree.source, 0, tree.token_locs[block.lbrace].start);
1163 break :blk @intCast(u28, line_delta);
1164 } else if (decl.scope.cast(Module.Scope.ZIRModule)) |zir_module| {
1165 const byte_off = zir_module.contents.module.decls[decl.src_index].inst.src;
1166 const line_delta = std.zig.lineDelta(zir_module.source.bytes, 0, byte_off);
1167 break :blk @intCast(u28, line_delta);
1168 } else {
1169 unreachable;
1170 }
1171 };
1172
1173 dbg_line_buffer.appendSliceAssumeCapacity(&[_]u8{
1174 DW.LNS_extended_op,
1175 @sizeOf(u64) + 1,
1176 DW.LNE_set_address,
1177 });
1178 // This is the "relocatable" vaddr, corresponding to `code_buffer` index `0`.
1179 assert(DebugSymbols.dbg_line_vaddr_reloc_index == dbg_line_buffer.items.len);
1180 dbg_line_buffer.items.len += @sizeOf(u64);
1181
1182 dbg_line_buffer.appendAssumeCapacity(DW.LNS_advance_line);
1183 // This is the "relocatable" relative line offset from the previous function's end curly
1184 // to this function's begin curly.
1185 assert(DebugSymbols.getRelocDbgLineOff() == dbg_line_buffer.items.len);
1186 // Here we use a ULEB128-fixed-4 to make sure this field can be overwritten later.
1187 leb.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), line_off);
1188
1189 dbg_line_buffer.appendAssumeCapacity(DW.LNS_set_file);
1190 assert(DebugSymbols.getRelocDbgFileIndex() == dbg_line_buffer.items.len);
1191 // Once we support more than one source file, this will have the ability to be more
1192 // than one possible value.
1193 const file_index = 1;
1194 leb.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), file_index);
1195
1196 // Emit a line for the begin curly with prologue_end=false. The codegen will
1197 // do the work of setting prologue_end=true and epilogue_begin=true.
1198 dbg_line_buffer.appendAssumeCapacity(DW.LNS_copy);
1199
1200 // .debug_info subprogram
1201 const decl_name_with_null = decl.name[0 .. mem.lenZ(decl.name) + 1];
1202 try dbg_info_buffer.ensureCapacity(dbg_info_buffer.items.len + 25 + decl_name_with_null.len);
1203
1204 const fn_ret_type = typed_value.ty.fnReturnType();
1205 const fn_ret_has_bits = fn_ret_type.hasCodeGenBits();
1206 if (fn_ret_has_bits) {
1207 dbg_info_buffer.appendAssumeCapacity(DebugSymbols.abbrev_subprogram);
1208 } else {
1209 dbg_info_buffer.appendAssumeCapacity(DebugSymbols.abbrev_subprogram_retvoid);
1210 }
1211 // These get overwritten after generating the machine code. These values are
1212 // "relocations" and have to be in this fixed place so that functions can be
1213 // moved in virtual address space.
1214 assert(DebugSymbols.dbg_info_low_pc_reloc_index == dbg_info_buffer.items.len);
1215 dbg_info_buffer.items.len += @sizeOf(u64); // DW.AT_low_pc, DW.FORM_addr
1216 assert(DebugSymbols.getRelocDbgInfoSubprogramHighPC() == dbg_info_buffer.items.len);
1217 dbg_info_buffer.items.len += 4; // DW.AT_high_pc, DW.FORM_data4
1218 if (fn_ret_has_bits) {
1219 const gop = try dbg_info_type_relocs.getOrPut(self.base.allocator, fn_ret_type);
1220 if (!gop.found_existing) {
1221 gop.entry.value = .{
1222 .off = undefined,
1223 .relocs = .{},
1224 };
1225 }
1226 try gop.entry.value.relocs.append(self.base.allocator, @intCast(u32, dbg_info_buffer.items.len));
1227 dbg_info_buffer.items.len += 4; // DW.AT_type, DW.FORM_ref4
1228 }
1229 dbg_info_buffer.appendSliceAssumeCapacity(decl_name_with_null); // DW.AT_name, DW.FORM_string
1230 } else {
1231 // TODO implement .debug_info for global variables
1232 }
1233 const res = try codegen.generateSymbol(&self.base, decl.src(), typed_value, &code_buffer, .{
1234 .dwarf = .{
1235 .dbg_line = &dbg_line_buffer,
1236 .dbg_info = &dbg_info_buffer,
1237 .dbg_info_type_relocs = &dbg_info_type_relocs,
1238 },
1239 });
10961240
10971241 const code = switch (res) {
10981242 .externally_managed => |x| x,
......@@ -1178,12 +1322,160 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
11781322 const file_offset = text_section.offset + section_offset;
11791323 try self.base.file.?.pwriteAll(code, file_offset);
11801324
1325 const text_block = &decl.link.macho;
1326 // If the Decl is a function, we need to update the __debug_line program.
1327 if (is_fn) {
1328 // Perform the relocations based on vaddr.
1329 {
1330 const ptr = dbg_line_buffer.items[DebugSymbols.dbg_line_vaddr_reloc_index..][0..8];
1331 mem.writeIntLittle(u64, ptr, symbol.n_value);
1332 }
1333 {
1334 const ptr = dbg_info_buffer.items[DebugSymbols.dbg_info_low_pc_reloc_index..][0..8];
1335 mem.writeIntLittle(u64, ptr, symbol.n_value);
1336 }
1337 {
1338 const ptr = dbg_info_buffer.items[DebugSymbols.getRelocDbgInfoSubprogramHighPC()..][0..4];
1339 mem.writeIntLittle(u32, ptr, @intCast(u32, text_block.size));
1340 }
1341
1342 try dbg_line_buffer.appendSlice(&[_]u8{ DW.LNS_extended_op, 1, DW.LNE_end_sequence });
1343
1344 // Now we have the full contents and may allocate a region to store it.
1345
1346 // This logic is nearly identical to the logic below in `updateDeclDebugInfo` for
1347 // `TextBlock` and the .debug_info. If you are editing this logic, you
1348 // probably need to edit that logic too.
1349
1350 const dwarf_segment = &self.d_sym.?.load_commands.items[self.d_sym.?.dwarf_segment_cmd_index.?].Segment;
1351 const debug_line_sect = &dwarf_segment.sections.items[self.d_sym.?.debug_line_section_index.?];
1352 const src_fn = &decl.fn_link.macho;
1353 src_fn.len = @intCast(u32, dbg_line_buffer.items.len);
1354 if (self.d_sym.?.dbg_line_fn_last) |last| {
1355 if (src_fn.next) |next| {
1356 // Update existing function - non-last item.
1357 if (src_fn.off + src_fn.len + DebugSymbols.min_nop_size > next.off) {
1358 // It grew too big, so we move it to a new location.
1359 if (src_fn.prev) |prev| {
1360 _ = self.d_sym.?.dbg_line_fn_free_list.put(self.base.allocator, prev, {}) catch {};
1361 prev.next = src_fn.next;
1362 }
1363 next.prev = src_fn.prev;
1364 src_fn.next = null;
1365 // Populate where it used to be with NOPs.
1366 const file_pos = debug_line_sect.offset + src_fn.off;
1367 try self.d_sym.?.pwriteDbgLineNops(0, &[0]u8{}, src_fn.len, file_pos);
1368 // TODO Look at the free list before appending at the end.
1369 src_fn.prev = last;
1370 last.next = src_fn;
1371 self.d_sym.?.dbg_line_fn_last = src_fn;
1372
1373 src_fn.off = last.off + (last.len * alloc_num / alloc_den);
1374 }
1375 } else if (src_fn.prev == null) {
1376 // Append new function.
1377 // TODO Look at the free list before appending at the end.
1378 src_fn.prev = last;
1379 last.next = src_fn;
1380 self.d_sym.?.dbg_line_fn_last = src_fn;
1381
1382 src_fn.off = last.off + (last.len * alloc_num / alloc_den);
1383 }
1384 } else {
1385 // This is the first function of the Line Number Program.
1386 self.d_sym.?.dbg_line_fn_first = src_fn;
1387 self.d_sym.?.dbg_line_fn_last = src_fn;
1388
1389 src_fn.off = self.d_sym.?.dbgLineNeededHeaderBytes(module) * alloc_num / alloc_den;
1390 }
1391
1392 const last_src_fn = self.d_sym.?.dbg_line_fn_last.?;
1393 const needed_size = last_src_fn.off + last_src_fn.len;
1394 if (needed_size != debug_line_sect.size) {
1395 if (needed_size > dwarf_segment.allocatedSize(debug_line_sect.offset)) {
1396 const new_offset = dwarf_segment.findFreeSpace(needed_size, 1, null);
1397 const existing_size = last_src_fn.off;
1398
1399 assert(dwarf_segment.inner.fileoff + dwarf_segment.inner.filesize >= new_offset + needed_size);
1400
1401 log.debug("moving __zdebug_line section: {} bytes from 0x{x} to 0x{x}", .{
1402 existing_size,
1403 debug_line_sect.offset,
1404 new_offset,
1405 });
1406
1407 const amt = try self.d_sym.?.file.copyRangeAll(debug_line_sect.offset, self.d_sym.?.file, new_offset, existing_size);
1408 if (amt != existing_size) return error.InputOutput;
1409 debug_line_sect.offset = @intCast(u32, new_offset);
1410 debug_line_sect.addr = dwarf_segment.inner.vmaddr + new_offset - dwarf_segment.inner.fileoff;
1411 }
1412 debug_line_sect.size = needed_size;
1413 self.d_sym.?.load_commands_dirty = true; // TODO look into making only the one section dirty
1414 self.d_sym.?.debug_line_header_dirty = true;
1415 }
1416 const prev_padding_size: u32 = if (src_fn.prev) |prev| src_fn.off - (prev.off + prev.len) else 0;
1417 const next_padding_size: u32 = if (src_fn.next) |next| next.off - (src_fn.off + src_fn.len) else 0;
1418
1419 // We only have support for one compilation unit so far, so the offsets are directly
1420 // from the .debug_line section.
1421 const file_pos = debug_line_sect.offset + src_fn.off;
1422 try self.d_sym.?.pwriteDbgLineNops(prev_padding_size, dbg_line_buffer.items, next_padding_size, file_pos);
1423
1424 // .debug_info - End the TAG_subprogram children.
1425 try dbg_info_buffer.append(0);
1426 }
1427
1428 // Now we emit the .debug_info types of the Decl. These will count towards the size of
1429 // the buffer, so we have to do it before computing the offset, and we can't perform the actual
1430 // relocations yet.
1431 var it = dbg_info_type_relocs.iterator();
1432 while (it.next()) |entry| {
1433 entry.value.off = @intCast(u32, dbg_info_buffer.items.len);
1434 try self.d_sym.?.addDbgInfoType(entry.key, &dbg_info_buffer, self.base.options.target);
1435 }
1436
1437 try self.d_sym.?.updateDeclDebugInfoAllocation(self.base.allocator, text_block, @intCast(u32, dbg_info_buffer.items.len));
1438
1439 // Now that we have the offset assigned we can finally perform type relocations.
1440 it = dbg_info_type_relocs.iterator();
1441 while (it.next()) |entry| {
1442 for (entry.value.relocs.items) |off| {
1443 mem.writeIntLittle(
1444 u32,
1445 dbg_info_buffer.items[off..][0..4],
1446 text_block.dbg_info_off + entry.value.off,
1447 );
1448 }
1449 }
1450
1451 try self.d_sym.?.writeDeclDebugInfo(text_block, dbg_info_buffer.items);
1452
11811453 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
11821454 const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
11831455 try self.updateDeclExports(module, decl, decl_exports);
11841456}
11851457
1186pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {}
1458pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {
1459 const tracy = trace(@src());
1460 defer tracy.end();
1461
1462 const container_scope = decl.scope.cast(Module.Scope.Container).?;
1463 const tree = container_scope.file_scope.contents.tree;
1464 const file_ast_decls = tree.root_node.decls();
1465 // TODO Look into improving the performance here by adding a token-index-to-line
1466 // lookup table. Currently this involves scanning over the source code for newlines.
1467 const fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?;
1468 const block = fn_proto.getBodyNode().?.castTag(.Block).?;
1469 const line_delta = std.zig.lineDelta(tree.source, 0, tree.token_locs[block.lbrace].start);
1470 const casted_line_off = @intCast(u28, line_delta);
1471
1472 const dwarf_segment = &self.d_sym.?.load_commands.items[self.d_sym.?.dwarf_segment_cmd_index.?].Segment;
1473 const shdr = &dwarf_segment.sections.items[self.d_sym.?.debug_line_section_index.?];
1474 const file_pos = shdr.offset + decl.fn_link.macho.off + DebugSymbols.getRelocDbgLineOff();
1475 var data: [4]u8 = undefined;
1476 leb.writeUnsignedFixed(4, &data, casted_line_off);
1477 try self.d_sym.?.file.pwriteAll(&data, file_pos);
1478}
11871479
11881480pub fn updateDeclExports(
11891481 self: *MachO,
src/link/MachO/DebugSymbols.zig+729-15
......@@ -11,7 +11,12 @@ const leb = std.leb;
1111const Allocator = mem.Allocator;
1212
1313const trace = @import("../../tracy.zig").trace;
14const Module = @import("../../Module.zig");
15const Type = @import("../../type.zig").Type;
16const link = @import("../../link.zig");
1417const MachO = @import("../MachO.zig");
18const SrcFn = MachO.SrcFn;
19const TextBlock = MachO.TextBlock;
1520const satMul = MachO.satMul;
1621const alloc_num = MachO.alloc_num;
1722const alloc_den = MachO.alloc_den;
......@@ -58,6 +63,21 @@ debug_line_section_index: ?u16 = null,
5863
5964debug_abbrev_table_offset: ?u64 = null,
6065
66/// A list of `SrcFn` whose Line Number Programs have surplus capacity.
67/// This is the same concept as `text_block_free_list`; see those doc comments.
68dbg_line_fn_free_list: std.AutoHashMapUnmanaged(*SrcFn, void) = .{},
69dbg_line_fn_first: ?*SrcFn = null,
70dbg_line_fn_last: ?*SrcFn = null,
71
72/// A list of `TextBlock` whose corresponding .debug_info tags have surplus capacity.
73/// This is the same concept as `text_block_free_list`; see those doc comments.
74dbg_info_decl_free_list: std.AutoHashMapUnmanaged(*TextBlock, void) = .{},
75dbg_info_decl_first: ?*TextBlock = null,
76dbg_info_decl_last: ?*TextBlock = null,
77
78/// Table of debug symbol names aka the debug string table.
79debug_string_table: std.ArrayListUnmanaged(u8) = .{},
80
6181header_dirty: bool = false,
6282load_commands_dirty: bool = false,
6383string_table_dirty: bool = false,
......@@ -67,6 +87,13 @@ debug_aranges_section_dirty: bool = false,
6787debug_info_header_dirty: bool = false,
6888debug_line_header_dirty: bool = false,
6989
90pub const abbrev_compile_unit = 1;
91pub const abbrev_subprogram = 2;
92pub const abbrev_subprogram_retvoid = 3;
93pub const abbrev_base_type = 4;
94pub const abbrev_pad1 = 5;
95pub const abbrev_parameter = 6;
96
7097/// You must call this function *after* `MachO.populateMissingMetadata()`
7198/// has been called to get a viable debug symbols output.
7299pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void {
......@@ -186,20 +213,14 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void
186213 if (self.debug_str_section_index == null) {
187214 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
188215 self.debug_str_section_index = @intCast(u16, dwarf_segment.sections.items.len);
189 assert(self.base.debug_string_table.items.len == 0);
190
191 const file_size_hint = 200;
192 const p_align = 1;
193 const off = dwarf_segment.findFreeSpace(file_size_hint, p_align, null);
194
195 log.debug("found dSym __debug_strtab free space 0x{x} to 0x{x}", .{ off, off + file_size_hint });
216 assert(self.debug_string_table.items.len == 0);
196217
197218 try dwarf_segment.addSection(allocator, .{
198219 .sectname = makeStaticString("__debug_str"),
199220 .segname = makeStaticString("__DWARF"),
200 .addr = dwarf_segment.inner.vmaddr + off,
201 .size = @intCast(u32, self.base.debug_string_table.items.len),
202 .offset = @intCast(u32, off),
221 .addr = dwarf_segment.inner.vmaddr,
222 .size = @intCast(u32, self.debug_string_table.items.len),
223 .offset = @intCast(u32, dwarf_segment.inner.fileoff),
203224 .@"align" = 1,
204225 .reloff = 0,
205226 .nreloc = 0,
......@@ -225,7 +246,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void
225246 try dwarf_segment.addSection(allocator, .{
226247 .sectname = makeStaticString("__debug_info"),
227248 .segname = makeStaticString("__DWARF"),
228 .addr = dwarf_segment.inner.vmaddr + off,
249 .addr = dwarf_segment.inner.vmaddr + off - dwarf_segment.inner.fileoff,
229250 .size = file_size_hint,
230251 .offset = @intCast(u32, off),
231252 .@"align" = p_align,
......@@ -253,7 +274,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void
253274 try dwarf_segment.addSection(allocator, .{
254275 .sectname = makeStaticString("__debug_abbrev"),
255276 .segname = makeStaticString("__DWARF"),
256 .addr = dwarf_segment.inner.vmaddr + off,
277 .addr = dwarf_segment.inner.vmaddr + off - dwarf_segment.inner.fileoff,
257278 .size = file_size_hint,
258279 .offset = @intCast(u32, off),
259280 .@"align" = p_align,
......@@ -281,7 +302,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void
281302 try dwarf_segment.addSection(allocator, .{
282303 .sectname = makeStaticString("__debug_aranges"),
283304 .segname = makeStaticString("__DWARF"),
284 .addr = dwarf_segment.inner.vmaddr + off,
305 .addr = dwarf_segment.inner.vmaddr + off - dwarf_segment.inner.fileoff,
285306 .size = file_size_hint,
286307 .offset = @intCast(u32, off),
287308 .@"align" = p_align,
......@@ -309,7 +330,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void
309330 try dwarf_segment.addSection(allocator, .{
310331 .sectname = makeStaticString("__debug_line"),
311332 .segname = makeStaticString("__DWARF"),
312 .addr = dwarf_segment.inner.vmaddr + off,
333 .addr = dwarf_segment.inner.vmaddr + off - dwarf_segment.inner.fileoff,
313334 .size = file_size_hint,
314335 .offset = @intCast(u32, off),
315336 .@"align" = p_align,
......@@ -326,17 +347,346 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void
326347 }
327348}
328349
329pub fn flush(self: *DebugSymbols, allocator: *Allocator) !void {
350pub fn flushModule(self: *DebugSymbols, allocator: *Allocator, options: link.Options) !void {
351 // TODO This linker code currently assumes there is only 1 compilation unit and it corresponds to the
352 // Zig source code.
353 const module = options.module orelse return error.LinkingWithoutZigSourceUnimplemented;
354 const init_len_size: usize = 12;
355
356 if (self.debug_abbrev_section_dirty) {
357 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
358 const debug_abbrev_sect = &dwarf_segment.sections.items[self.debug_abbrev_section_index.?];
359
360 // These are LEB encoded but since the values are all less than 127
361 // we can simply append these bytes.
362 const abbrev_buf = [_]u8{
363 abbrev_compile_unit, DW.TAG_compile_unit, DW.CHILDREN_yes, // header
364 DW.AT_stmt_list, DW.FORM_sec_offset, DW.AT_low_pc,
365 DW.FORM_addr, DW.AT_high_pc, DW.FORM_addr,
366 DW.AT_name, DW.FORM_strp, DW.AT_comp_dir,
367 DW.FORM_strp, DW.AT_producer, DW.FORM_strp,
368 DW.AT_language, DW.FORM_data2, 0,
369 0, // table sentinel
370 abbrev_subprogram,
371 DW.TAG_subprogram,
372 DW.CHILDREN_yes, // header
373 DW.AT_low_pc,
374 DW.FORM_addr,
375 DW.AT_high_pc,
376 DW.FORM_data4,
377 DW.AT_type,
378 DW.FORM_ref4,
379 DW.AT_name,
380 DW.FORM_string,
381 0, 0, // table sentinel
382 abbrev_subprogram_retvoid,
383 DW.TAG_subprogram, DW.CHILDREN_yes, // header
384 DW.AT_low_pc, DW.FORM_addr,
385 DW.AT_high_pc, DW.FORM_data4,
386 DW.AT_name, DW.FORM_string,
387 0,
388 0, // table sentinel
389 abbrev_base_type,
390 DW.TAG_base_type,
391 DW.CHILDREN_no, // header
392 DW.AT_encoding,
393 DW.FORM_data1,
394 DW.AT_byte_size,
395 DW.FORM_data1,
396 DW.AT_name,
397 DW.FORM_string, 0, 0, // table sentinel
398 abbrev_pad1, DW.TAG_unspecified_type, DW.CHILDREN_no, // header
399 0, 0, // table sentinel
400 abbrev_parameter,
401 DW.TAG_formal_parameter, DW.CHILDREN_no, // header
402 DW.AT_location, DW.FORM_exprloc,
403 DW.AT_type, DW.FORM_ref4,
404 DW.AT_name, DW.FORM_string,
405 0,
406 0, // table sentinel
407 0,
408 0,
409 0, // section sentinel
410 };
411
412 const needed_size = abbrev_buf.len;
413 const allocated_size = dwarf_segment.allocatedSize(debug_abbrev_sect.offset);
414 if (needed_size > allocated_size) {
415 debug_abbrev_sect.size = 0; // free the space
416 debug_abbrev_sect.offset = @intCast(u32, dwarf_segment.findFreeSpace(needed_size, 1, null));
417 }
418 debug_abbrev_sect.size = needed_size;
419 log.debug("__debug_abbrev start=0x{x} end=0x{x}", .{
420 debug_abbrev_sect.offset,
421 debug_abbrev_sect.offset + needed_size,
422 });
423
424 const abbrev_offset = 0;
425 self.debug_abbrev_table_offset = abbrev_offset;
426 try self.file.pwriteAll(&abbrev_buf, debug_abbrev_sect.offset + abbrev_offset);
427 self.load_commands_dirty = true;
428 self.debug_abbrev_section_dirty = false;
429 }
430
431 if (self.debug_info_header_dirty) debug_info: {
432 // If this value is null it means there is an error in the module;
433 // leave debug_info_header_dirty=true.
434 const first_dbg_info_decl = self.dbg_info_decl_first orelse break :debug_info;
435 const last_dbg_info_decl = self.dbg_info_decl_last.?;
436 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
437 const debug_info_sect = &dwarf_segment.sections.items[self.debug_info_section_index.?];
438
439 var di_buf = std.ArrayList(u8).init(allocator);
440 defer di_buf.deinit();
441
442 // We have a function to compute the upper bound size, because it's needed
443 // for determining where to put the offset of the first `LinkBlock`.
444 try di_buf.ensureCapacity(self.dbgInfoNeededHeaderBytes());
445
446 // initial length - length of the .debug_info contribution for this compilation unit,
447 // not including the initial length itself.
448 // We have to come back and write it later after we know the size.
449 const after_init_len = di_buf.items.len + init_len_size;
450 // +1 for the final 0 that ends the compilation unit children.
451 const dbg_info_end = last_dbg_info_decl.dbg_info_off + last_dbg_info_decl.dbg_info_len + 1;
452 const init_len = dbg_info_end - after_init_len;
453 di_buf.appendNTimesAssumeCapacity(0xff, 4);
454 mem.writeIntLittle(u64, di_buf.addManyAsArrayAssumeCapacity(8), init_len);
455 mem.writeIntLittle(u16, di_buf.addManyAsArrayAssumeCapacity(2), 4); // DWARF version
456 const abbrev_offset = self.debug_abbrev_table_offset.?;
457 mem.writeIntLittle(u64, di_buf.addManyAsArrayAssumeCapacity(8), abbrev_offset);
458 di_buf.appendAssumeCapacity(8); // address size
459 // Write the form for the compile unit, which must match the abbrev table above.
460 const name_strp = try self.makeDebugString(allocator, module.root_pkg.root_src_path);
461 const comp_dir_strp = try self.makeDebugString(allocator, module.root_pkg.root_src_directory.path orelse ".");
462 const producer_strp = try self.makeDebugString(allocator, link.producer_string);
463 // Currently only one compilation unit is supported, so the address range is simply
464 // identical to the main program header virtual address and memory size.
465 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
466 const text_section = text_segment.sections.items[self.text_section_index.?];
467 const low_pc = text_section.addr;
468 const high_pc = text_section.addr + text_section.size;
469
470 di_buf.appendAssumeCapacity(abbrev_compile_unit);
471 mem.writeIntLittle(u64, di_buf.addManyAsArrayAssumeCapacity(8), 0); // DW.AT_stmt_list, DW.FORM_sec_offset
472 mem.writeIntLittle(u64, di_buf.addManyAsArrayAssumeCapacity(8), low_pc);
473 mem.writeIntLittle(u64, di_buf.addManyAsArrayAssumeCapacity(8), high_pc);
474 mem.writeIntLittle(u64, di_buf.addManyAsArrayAssumeCapacity(8), name_strp);
475 mem.writeIntLittle(u64, di_buf.addManyAsArrayAssumeCapacity(8), comp_dir_strp);
476 mem.writeIntLittle(u64, di_buf.addManyAsArrayAssumeCapacity(8), producer_strp);
477 // We are still waiting on dwarf-std.org to assign DW_LANG_Zig a number:
478 // http://dwarfstd.org/ShowIssue.php?issue=171115.1
479 // Until then we say it is C99.
480 mem.writeIntLittle(u16, di_buf.addManyAsArrayAssumeCapacity(2), DW.LANG_C99);
481
482 if (di_buf.items.len > first_dbg_info_decl.dbg_info_off) {
483 // Move the first N decls to the end to make more padding for the header.
484 @panic("TODO: handle __zdebug_info header exceeding its padding");
485 }
486 const jmp_amt = first_dbg_info_decl.dbg_info_off - di_buf.items.len;
487 try self.pwriteDbgInfoNops(0, di_buf.items, jmp_amt, false, debug_info_sect.offset);
488 self.debug_info_header_dirty = false;
489 }
490
491 if (self.debug_aranges_section_dirty) {
492 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
493 const debug_aranges_sect = &dwarf_segment.sections.items[self.debug_aranges_section_index.?];
494 const debug_info_sect = dwarf_segment.sections.items[self.debug_info_section_index.?];
495
496 var di_buf = std.ArrayList(u8).init(allocator);
497 defer di_buf.deinit();
498
499 // Enough for all the data without resizing. When support for more compilation units
500 // is added, the size of this section will become more variable.
501 try di_buf.ensureCapacity(100);
502
503 // initial length - length of the .debug_aranges contribution for this compilation unit,
504 // not including the initial length itself.
505 // We have to come back and write it later after we know the size.
506 const init_len_index = di_buf.items.len;
507 di_buf.items.len += init_len_size;
508 const after_init_len = di_buf.items.len;
509 mem.writeIntLittle(u16, di_buf.addManyAsArrayAssumeCapacity(2), 2); // version
510 // When more than one compilation unit is supported, this will be the offset to it.
511 // For now it is always at offset 0 in .debug_info.
512 mem.writeIntLittle(u64, di_buf.addManyAsArrayAssumeCapacity(8), debug_info_sect.addr); // __debug_info offset
513 di_buf.appendAssumeCapacity(@sizeOf(u64)); // address_size
514 di_buf.appendAssumeCapacity(0); // segment_selector_size
515
516 const end_header_offset = di_buf.items.len;
517 const begin_entries_offset = mem.alignForward(end_header_offset, @sizeOf(u64) * 2);
518 di_buf.appendNTimesAssumeCapacity(0, begin_entries_offset - end_header_offset);
519
520 // Currently only one compilation unit is supported, so the address range is simply
521 // identical to the main program header virtual address and memory size.
522 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
523 const text_section = text_segment.sections.items[self.text_section_index.?];
524 mem.writeIntLittle(u64, di_buf.addManyAsArrayAssumeCapacity(8), text_section.addr);
525 mem.writeIntLittle(u64, di_buf.addManyAsArrayAssumeCapacity(8), text_section.size);
526
527 // Sentinel.
528 mem.writeIntLittle(u64, di_buf.addManyAsArrayAssumeCapacity(8), 0);
529 mem.writeIntLittle(u64, di_buf.addManyAsArrayAssumeCapacity(8), 0);
530
531 // Go back and populate the initial length.
532 const init_len = di_buf.items.len - after_init_len;
533 // initial length - length of the .debug_aranges contribution for this compilation unit,
534 // not including the initial length itself.
535 di_buf.items[init_len_index..][0..4].* = [_]u8{ 0xff, 0xff, 0xff, 0xff };
536 mem.writeIntLittle(u64, di_buf.items[init_len_index + 4 ..][0..8], init_len);
537
538 const needed_size = di_buf.items.len;
539 const allocated_size = dwarf_segment.allocatedSize(debug_aranges_sect.offset);
540 if (needed_size > allocated_size) {
541 debug_aranges_sect.size = 0; // free the space
542 const offset = dwarf_segment.findFreeSpace(needed_size, 16, null);
543 debug_aranges_sect.offset = @intCast(u32, offset);
544 debug_aranges_sect.addr = dwarf_segment.inner.vmaddr + offset - dwarf_segment.inner.fileoff;
545 }
546 debug_aranges_sect.size = needed_size;
547 log.debug("__debug_aranges start=0x{x} end=0x{x}", .{
548 debug_aranges_sect.offset,
549 debug_aranges_sect.offset + needed_size,
550 });
551
552 try self.file.pwriteAll(di_buf.items, debug_aranges_sect.offset);
553 self.load_commands_dirty = true;
554 self.debug_aranges_section_dirty = false;
555 }
556 if (self.debug_line_header_dirty) debug_line: {
557 if (self.dbg_line_fn_first == null) {
558 break :debug_line; // Error in module; leave debug_line_header_dirty=true.
559 }
560 const dbg_line_prg_off = self.getDebugLineProgramOff();
561 const dbg_line_prg_end = self.getDebugLineProgramEnd();
562 assert(dbg_line_prg_end != 0);
563
564 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
565 const debug_line_sect = &dwarf_segment.sections.items[self.debug_line_section_index.?];
566
567 var di_buf = std.ArrayList(u8).init(allocator);
568 defer di_buf.deinit();
569
570 // The size of this header is variable, depending on the number of directories,
571 // files, and padding. We have a function to compute the upper bound size, however,
572 // because it's needed for determining where to put the offset of the first `SrcFn`.
573 try di_buf.ensureCapacity(self.dbgLineNeededHeaderBytes(module));
574
575 // initial length - length of the .debug_line contribution for this compilation unit,
576 // not including the initial length itself.
577 const after_init_len = di_buf.items.len + init_len_size;
578 const init_len = dbg_line_prg_end - after_init_len;
579 di_buf.appendNTimesAssumeCapacity(0xff, 4);
580 mem.writeIntLittle(u64, di_buf.addManyAsArrayAssumeCapacity(8), init_len);
581 mem.writeIntLittle(u16, di_buf.addManyAsArrayAssumeCapacity(2), 4); // version
582
583 // Empirically, debug info consumers do not respect this field, or otherwise
584 // consider it to be an error when it does not point exactly to the end of the header.
585 // Therefore we rely on the NOP jump at the beginning of the Line Number Program for
586 // padding rather than this field.
587 const before_header_len = di_buf.items.len;
588 di_buf.items.len += @sizeOf(u64); // We will come back and write this.
589 const after_header_len = di_buf.items.len;
590
591 const opcode_base = DW.LNS_set_isa + 1;
592 di_buf.appendSliceAssumeCapacity(&[_]u8{
593 1, // minimum_instruction_length
594 1, // maximum_operations_per_instruction
595 1, // default_is_stmt
596 1, // line_base (signed)
597 1, // line_range
598 opcode_base,
599
600 // Standard opcode lengths. The number of items here is based on `opcode_base`.
601 // The value is the number of LEB128 operands the instruction takes.
602 0, // `DW.LNS_copy`
603 1, // `DW.LNS_advance_pc`
604 1, // `DW.LNS_advance_line`
605 1, // `DW.LNS_set_file`
606 1, // `DW.LNS_set_column`
607 0, // `DW.LNS_negate_stmt`
608 0, // `DW.LNS_set_basic_block`
609 0, // `DW.LNS_const_add_pc`
610 1, // `DW.LNS_fixed_advance_pc`
611 0, // `DW.LNS_set_prologue_end`
612 0, // `DW.LNS_set_epilogue_begin`
613 1, // `DW.LNS_set_isa`
614 0, // include_directories (none except the compilation unit cwd)
615 });
616 // file_names[0]
617 di_buf.appendSliceAssumeCapacity(module.root_pkg.root_src_path); // relative path name
618 di_buf.appendSliceAssumeCapacity(&[_]u8{
619 0, // null byte for the relative path name
620 0, // directory_index
621 0, // mtime (TODO supply this)
622 0, // file size bytes (TODO supply this)
623 0, // file_names sentinel
624 });
625
626 const header_len = di_buf.items.len - after_header_len;
627 mem.writeIntLittle(u64, di_buf.items[before_header_len..][0..8], header_len);
628
629 // We use NOPs because consumers empirically do not respect the header length field.
630 if (di_buf.items.len > dbg_line_prg_off) {
631 // Move the first N files to the end to make more padding for the header.
632 @panic("TODO: handle __debug_line header exceeding its padding");
633 }
634 const jmp_amt = dbg_line_prg_off - di_buf.items.len;
635 try self.pwriteDbgLineNops(0, di_buf.items, jmp_amt, debug_line_sect.offset);
636 self.debug_line_header_dirty = false;
637 }
638 {
639 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
640 const debug_strtab_sect = &dwarf_segment.sections.items[self.debug_str_section_index.?];
641 if (self.debug_string_table_dirty or self.debug_string_table.items.len != debug_strtab_sect.size) {
642 const allocated_size = dwarf_segment.allocatedSize(debug_strtab_sect.offset);
643 const needed_size = self.debug_string_table.items.len;
644
645 if (needed_size > allocated_size) {
646 debug_strtab_sect.size = 0; // free the space
647 const new_offset = dwarf_segment.findFreeSpace(needed_size, 1, null);
648 debug_strtab_sect.addr = dwarf_segment.inner.vmaddr + new_offset - dwarf_segment.inner.fileoff;
649 debug_strtab_sect.offset = @intCast(u32, new_offset);
650 }
651 debug_strtab_sect.size = @intCast(u32, needed_size);
652
653 log.debug("__debug_strtab start=0x{x} end=0x{x}", .{
654 debug_strtab_sect.offset,
655 debug_strtab_sect.offset + needed_size,
656 });
657
658 try self.file.pwriteAll(self.debug_string_table.items, debug_strtab_sect.offset);
659 self.load_commands_dirty = true;
660 self.debug_string_table_dirty = false;
661 }
662 }
663
330664 try self.writeStringTable();
665
666 {
667 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
668 var file_size: u64 = 0;
669 for (dwarf_segment.sections.items) |sect| {
670 file_size += sect.size;
671 }
672 dwarf_segment.inner.filesize = file_size;
673 }
674
331675 try self.writeLoadCommands(allocator);
332676 try self.writeHeader();
333677
334678 assert(!self.header_dirty);
335679 assert(!self.load_commands_dirty);
336680 assert(!self.string_table_dirty);
681 assert(!self.debug_abbrev_section_dirty);
682 assert(!self.debug_aranges_section_dirty);
683 assert(!self.debug_string_table_dirty);
337684}
338685
339686pub fn deinit(self: *DebugSymbols, allocator: *Allocator) void {
687 self.dbg_info_decl_free_list.deinit(allocator);
688 self.dbg_line_fn_free_list.deinit(allocator);
689 self.debug_string_table.deinit(allocator);
340690 for (self.load_commands.items) |*lc| {
341691 lc.deinit(allocator);
342692 }
......@@ -535,3 +885,367 @@ pub fn writeStringTable(self: *DebugSymbols) !void {
535885 self.load_commands_dirty = true;
536886 self.string_table_dirty = false;
537887}
888
889/// Asserts the type has codegen bits.
890pub fn addDbgInfoType(
891 self: *DebugSymbols,
892 ty: Type,
893 dbg_info_buffer: *std.ArrayList(u8),
894 target: std.Target,
895) !void {
896 switch (ty.zigTypeTag()) {
897 .Void => unreachable,
898 .NoReturn => unreachable,
899 .Bool => {
900 try dbg_info_buffer.appendSlice(&[_]u8{
901 abbrev_base_type,
902 DW.ATE_boolean, // DW.AT_encoding , DW.FORM_data1
903 1, // DW.AT_byte_size, DW.FORM_data1
904 'b',
905 'o',
906 'o',
907 'l',
908 0, // DW.AT_name, DW.FORM_string
909 });
910 },
911 .Int => {
912 const info = ty.intInfo(target);
913 try dbg_info_buffer.ensureCapacity(dbg_info_buffer.items.len + 12);
914 dbg_info_buffer.appendAssumeCapacity(abbrev_base_type);
915 // DW.AT_encoding, DW.FORM_data1
916 dbg_info_buffer.appendAssumeCapacity(switch (info.signedness) {
917 .signed => DW.ATE_signed,
918 .unsigned => DW.ATE_unsigned,
919 });
920 // DW.AT_byte_size, DW.FORM_data1
921 dbg_info_buffer.appendAssumeCapacity(@intCast(u8, ty.abiSize(target)));
922 // DW.AT_name, DW.FORM_string
923 try dbg_info_buffer.writer().print("{}\x00", .{ty});
924 },
925 else => {
926 std.log.scoped(.compiler).err("TODO implement .debug_info for type '{}'", .{ty});
927 try dbg_info_buffer.append(abbrev_pad1);
928 },
929 }
930}
931
932pub fn updateDeclDebugInfoAllocation(
933 self: *DebugSymbols,
934 allocator: *Allocator,
935 text_block: *TextBlock,
936 len: u32,
937) !void {
938 const tracy = trace(@src());
939 defer tracy.end();
940
941 // This logic is nearly identical to the logic above in `updateDecl` for
942 // `SrcFn` and the line number programs. If you are editing this logic, you
943 // probably need to edit that logic too.
944
945 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
946 const debug_info_sect = &dwarf_segment.sections.items[self.debug_info_section_index.?];
947 text_block.dbg_info_len = len;
948 if (self.dbg_info_decl_last) |last| {
949 if (text_block.dbg_info_next) |next| {
950 // Update existing Decl - non-last item.
951 if (text_block.dbg_info_off + text_block.dbg_info_len + min_nop_size > next.dbg_info_off) {
952 // It grew too big, so we move it to a new location.
953 if (text_block.dbg_info_prev) |prev| {
954 _ = self.dbg_info_decl_free_list.put(allocator, prev, {}) catch {};
955 prev.dbg_info_next = text_block.dbg_info_next;
956 }
957 next.dbg_info_prev = text_block.dbg_info_prev;
958 text_block.dbg_info_next = null;
959 // Populate where it used to be with NOPs.
960 const file_pos = debug_info_sect.offset + text_block.dbg_info_off;
961 try self.pwriteDbgInfoNops(0, &[0]u8{}, text_block.dbg_info_len, false, file_pos);
962 // TODO Look at the free list before appending at the end.
963 text_block.dbg_info_prev = last;
964 last.dbg_info_next = text_block;
965 self.dbg_info_decl_last = text_block;
966
967 text_block.dbg_info_off = last.dbg_info_off + (last.dbg_info_len * alloc_num / alloc_den);
968 }
969 } else if (text_block.dbg_info_prev == null) {
970 // Append new Decl.
971 // TODO Look at the free list before appending at the end.
972 text_block.dbg_info_prev = last;
973 last.dbg_info_next = text_block;
974 self.dbg_info_decl_last = text_block;
975
976 text_block.dbg_info_off = last.dbg_info_off + (last.dbg_info_len * alloc_num / alloc_den);
977 }
978 } else {
979 // This is the first Decl of the .debug_info
980 self.dbg_info_decl_first = text_block;
981 self.dbg_info_decl_last = text_block;
982
983 text_block.dbg_info_off = self.dbgInfoNeededHeaderBytes() * alloc_num / alloc_den;
984 }
985}
986
987pub fn writeDeclDebugInfo(self: *DebugSymbols, text_block: *TextBlock, dbg_info_buf: []const u8) !void {
988 const tracy = trace(@src());
989 defer tracy.end();
990
991 // This logic is nearly identical to the logic above in `updateDecl` for
992 // `SrcFn` and the line number programs. If you are editing this logic, you
993 // probably need to edit that logic too.
994
995 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
996 const debug_info_sect = &dwarf_segment.sections.items[self.debug_info_section_index.?];
997
998 const last_decl = self.dbg_info_decl_last.?;
999 // +1 for a trailing zero to end the children of the decl tag.
1000 const needed_size = last_decl.dbg_info_off + last_decl.dbg_info_len + 1;
1001 if (needed_size != debug_info_sect.size) {
1002 if (needed_size > dwarf_segment.allocatedSize(debug_info_sect.offset)) {
1003 const new_offset = dwarf_segment.findFreeSpace(needed_size, 1, null);
1004 const existing_size = last_decl.dbg_info_off;
1005
1006 // TODO
1007 assert(dwarf_segment.inner.fileoff + dwarf_segment.inner.filesize >= new_offset + needed_size);
1008
1009 log.debug("moving _debug_info section: {} bytes from 0x{x} to 0x{x}", .{
1010 existing_size,
1011 debug_info_sect.offset,
1012 new_offset,
1013 });
1014
1015 const amt = try self.file.copyRangeAll(debug_info_sect.offset, self.file, new_offset, existing_size);
1016 if (amt != existing_size) return error.InputOutput;
1017 debug_info_sect.offset = @intCast(u32, new_offset);
1018 debug_info_sect.addr = dwarf_segment.inner.vmaddr + new_offset - dwarf_segment.inner.fileoff;
1019 }
1020 debug_info_sect.size = needed_size;
1021 self.load_commands_dirty = true; // TODO look into making only the one section dirty
1022 self.debug_info_header_dirty = true;
1023 }
1024 const prev_padding_size: u32 = if (text_block.dbg_info_prev) |prev|
1025 text_block.dbg_info_off - (prev.dbg_info_off + prev.dbg_info_len)
1026 else
1027 0;
1028 const next_padding_size: u32 = if (text_block.dbg_info_next) |next|
1029 next.dbg_info_off - (text_block.dbg_info_off + text_block.dbg_info_len)
1030 else
1031 0;
1032
1033 // To end the children of the decl tag.
1034 const trailing_zero = text_block.dbg_info_next == null;
1035
1036 // We only have support for one compilation unit so far, so the offsets are directly
1037 // from the .debug_info section.
1038 const file_pos = debug_info_sect.offset + text_block.dbg_info_off;
1039 try self.pwriteDbgInfoNops(prev_padding_size, dbg_info_buf, next_padding_size, trailing_zero, file_pos);
1040}
1041
1042fn getDebugLineProgramOff(self: DebugSymbols) u32 {
1043 return self.dbg_line_fn_first.?.off;
1044}
1045
1046fn getDebugLineProgramEnd(self: DebugSymbols) u32 {
1047 return self.dbg_line_fn_last.?.off + self.dbg_line_fn_last.?.len;
1048}
1049
1050/// TODO Improve this to use a table.
1051fn makeDebugString(self: *DebugSymbols, allocator: *Allocator, bytes: []const u8) !u32 {
1052 try self.debug_string_table.ensureCapacity(allocator, self.debug_string_table.items.len + bytes.len + 1);
1053 const result = self.debug_string_table.items.len;
1054 self.debug_string_table.appendSliceAssumeCapacity(bytes);
1055 self.debug_string_table.appendAssumeCapacity(0);
1056 return @intCast(u32, result);
1057}
1058
1059/// The reloc offset for the virtual address of a function in its Line Number Program.
1060/// Size is a virtual address integer.
1061pub const dbg_line_vaddr_reloc_index = 3;
1062/// The reloc offset for the virtual address of a function in its .debug_info TAG_subprogram.
1063/// Size is a virtual address integer.
1064pub const dbg_info_low_pc_reloc_index = 1;
1065
1066/// The reloc offset for the line offset of a function from the previous function's line.
1067/// It's a fixed-size 4-byte ULEB128.
1068pub fn getRelocDbgLineOff() usize {
1069 return dbg_line_vaddr_reloc_index + @sizeOf(u64) + 1;
1070}
1071
1072pub fn getRelocDbgFileIndex() usize {
1073 return getRelocDbgLineOff() + 5;
1074}
1075
1076pub fn getRelocDbgInfoSubprogramHighPC() u32 {
1077 return dbg_info_low_pc_reloc_index + @sizeOf(u64);
1078}
1079
1080pub fn dbgLineNeededHeaderBytes(self: DebugSymbols, module: *Module) u32 {
1081 const directory_entry_format_count = 1;
1082 const file_name_entry_format_count = 1;
1083 const directory_count = 1;
1084 const file_name_count = 1;
1085 const root_src_dir_path_len = if (module.root_pkg.root_src_directory.path) |p| p.len else 1; // "."
1086 return @intCast(u32, 53 + directory_entry_format_count * 2 + file_name_entry_format_count * 2 +
1087 directory_count * 8 + file_name_count * 8 +
1088 // These are encoded as DW.FORM_string rather than DW.FORM_strp as we would like
1089 // because of a workaround for readelf and gdb failing to understand DWARFv5 correctly.
1090 root_src_dir_path_len +
1091 module.root_pkg.root_src_path.len);
1092}
1093
1094fn dbgInfoNeededHeaderBytes(self: DebugSymbols) u32 {
1095 return 120;
1096}
1097
1098pub const min_nop_size = 2;
1099
1100/// Writes to the file a buffer, prefixed and suffixed by the specified number of
1101/// bytes of NOPs. Asserts each padding size is at least `min_nop_size` and total padding bytes
1102/// are less than 126,976 bytes (if this limit is ever reached, this function can be
1103/// improved to make more than one pwritev call, or the limit can be raised by a fixed
1104/// amount by increasing the length of `vecs`).
1105pub fn pwriteDbgLineNops(
1106 self: *DebugSymbols,
1107 prev_padding_size: usize,
1108 buf: []const u8,
1109 next_padding_size: usize,
1110 offset: u64,
1111) !void {
1112 const tracy = trace(@src());
1113 defer tracy.end();
1114
1115 const page_of_nops = [1]u8{DW.LNS_negate_stmt} ** 4096;
1116 const three_byte_nop = [3]u8{ DW.LNS_advance_pc, 0b1000_0000, 0 };
1117 var vecs: [32]std.os.iovec_const = undefined;
1118 var vec_index: usize = 0;
1119 {
1120 var padding_left = prev_padding_size;
1121 if (padding_left % 2 != 0) {
1122 vecs[vec_index] = .{
1123 .iov_base = &three_byte_nop,
1124 .iov_len = three_byte_nop.len,
1125 };
1126 vec_index += 1;
1127 padding_left -= three_byte_nop.len;
1128 }
1129 while (padding_left > page_of_nops.len) {
1130 vecs[vec_index] = .{
1131 .iov_base = &page_of_nops,
1132 .iov_len = page_of_nops.len,
1133 };
1134 vec_index += 1;
1135 padding_left -= page_of_nops.len;
1136 }
1137 if (padding_left > 0) {
1138 vecs[vec_index] = .{
1139 .iov_base = &page_of_nops,
1140 .iov_len = padding_left,
1141 };
1142 vec_index += 1;
1143 }
1144 }
1145
1146 vecs[vec_index] = .{
1147 .iov_base = buf.ptr,
1148 .iov_len = buf.len,
1149 };
1150 vec_index += 1;
1151
1152 {
1153 var padding_left = next_padding_size;
1154 if (padding_left % 2 != 0) {
1155 vecs[vec_index] = .{
1156 .iov_base = &three_byte_nop,
1157 .iov_len = three_byte_nop.len,
1158 };
1159 vec_index += 1;
1160 padding_left -= three_byte_nop.len;
1161 }
1162 while (padding_left > page_of_nops.len) {
1163 vecs[vec_index] = .{
1164 .iov_base = &page_of_nops,
1165 .iov_len = page_of_nops.len,
1166 };
1167 vec_index += 1;
1168 padding_left -= page_of_nops.len;
1169 }
1170 if (padding_left > 0) {
1171 vecs[vec_index] = .{
1172 .iov_base = &page_of_nops,
1173 .iov_len = padding_left,
1174 };
1175 vec_index += 1;
1176 }
1177 }
1178 try self.file.pwritevAll(vecs[0..vec_index], offset - prev_padding_size);
1179}
1180
1181/// Writes to the file a buffer, prefixed and suffixed by the specified number of
1182/// bytes of padding.
1183pub fn pwriteDbgInfoNops(
1184 self: *DebugSymbols,
1185 prev_padding_size: usize,
1186 buf: []const u8,
1187 next_padding_size: usize,
1188 trailing_zero: bool,
1189 offset: u64,
1190) !void {
1191 const tracy = trace(@src());
1192 defer tracy.end();
1193
1194 const page_of_nops = [1]u8{abbrev_pad1} ** 4096;
1195 var vecs: [32]std.os.iovec_const = undefined;
1196 var vec_index: usize = 0;
1197 {
1198 var padding_left = prev_padding_size;
1199 while (padding_left > page_of_nops.len) {
1200 vecs[vec_index] = .{
1201 .iov_base = &page_of_nops,
1202 .iov_len = page_of_nops.len,
1203 };
1204 vec_index += 1;
1205 padding_left -= page_of_nops.len;
1206 }
1207 if (padding_left > 0) {
1208 vecs[vec_index] = .{
1209 .iov_base = &page_of_nops,
1210 .iov_len = padding_left,
1211 };
1212 vec_index += 1;
1213 }
1214 }
1215
1216 vecs[vec_index] = .{
1217 .iov_base = buf.ptr,
1218 .iov_len = buf.len,
1219 };
1220 vec_index += 1;
1221
1222 {
1223 var padding_left = next_padding_size;
1224 while (padding_left > page_of_nops.len) {
1225 vecs[vec_index] = .{
1226 .iov_base = &page_of_nops,
1227 .iov_len = page_of_nops.len,
1228 };
1229 vec_index += 1;
1230 padding_left -= page_of_nops.len;
1231 }
1232 if (padding_left > 0) {
1233 vecs[vec_index] = .{
1234 .iov_base = &page_of_nops,
1235 .iov_len = padding_left,
1236 };
1237 vec_index += 1;
1238 }
1239 }
1240
1241 if (trailing_zero) {
1242 var zbuf = [1]u8{0};
1243 vecs[vec_index] = .{
1244 .iov_base = &zbuf,
1245 .iov_len = zbuf.len,
1246 };
1247 vec_index += 1;
1248 }
1249
1250 try self.file.pwritevAll(vecs[0..vec_index], offset - prev_padding_size);
1251}
src/link/MachO/commands.zig+5-1
......@@ -5,6 +5,7 @@ const mem = std.mem;
55const meta = std.meta;
66const macho = std.macho;
77const testing = std.testing;
8const assert = std.debug.assert;
89
910const Allocator = std.mem.Allocator;
1011const MachO = @import("../MachO.zig");
......@@ -202,9 +203,12 @@ pub const SegmentCommand = struct {
202203
203204 pub fn allocatedSize(self: SegmentCommand, start: u64) u64 {
204205 assert(start > 0);
206 if (start == self.inner.fileoff)
207 return 0;
205208 var min_pos: u64 = std.math.maxInt(u64);
206209 for (self.sections.items) |section| {
207 if (section.offset > start and section.offset < min_pos) min_pos = section.offset;
210 if (section.offset <= start) continue;
211 if (section.offset < min_pos) min_pos = section.offset;
208212 }
209213 return min_pos - start;
210214 }