authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-10-14 13:50:10+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-10-22 12:50:25+02:00
logd0dceae736edb43d4c217306a2b0445277f184ce
treed6bad107975e61e360a50de5436fb297a97435fb
parent912e7dc54b9b49d96123ffd398e6d40b455997fe

macho: dump linker's state as JSON

Each element of the output JSON has the VM address of the generated binary nondecreasing (some elements might occupy the same VM address for example the atom and the relocation might coincide in the address space). The generated JSON can be inspected manually or via a preview tool `zig-snapshots` that I am currently working on and will allow the user to inspect interactively the state of the linker together with the positioning of sections, symbols, atoms and relocations within each snapshot state, and in the future, between snapshots too. This should allow for quicker debugging of the linker which is nontrivial when run in the incremental mode. Note that the state will only be dumped if the compiler is built with `-Dlink-snapshot` flag on, and then the compiler is passed `--debug-link-snapshot` flag upon compiling a source/project.

8 files changed, 322 insertions(+), 17 deletions(-)

build.zig+3
...@@ -205,6 +205,7 @@ pub fn build(b: *Builder) !void {...@@ -205,6 +205,7 @@ pub fn build(b: *Builder) !void {
205 }205 }
206206
207 const enable_logging = b.option(bool, "log", "Whether to enable logging") orelse false;207 const enable_logging = b.option(bool, "log", "Whether to enable logging") orelse false;
208 const enable_link_snapshots = b.option(bool, "link-snapshot", "Whether to enable linker state snapshots") orelse false;
208209
209 const opt_version_string = b.option([]const u8, "version-string", "Override Zig version string. Default is to find out with git.");210 const opt_version_string = b.option([]const u8, "version-string", "Override Zig version string. Default is to find out with git.");
210 const version = if (opt_version_string) |version| version else v: {211 const version = if (opt_version_string) |version| version else v: {
...@@ -261,6 +262,7 @@ pub fn build(b: *Builder) !void {...@@ -261,6 +262,7 @@ pub fn build(b: *Builder) !void {
261 exe_options.addOption(std.SemanticVersion, "semver", semver);262 exe_options.addOption(std.SemanticVersion, "semver", semver);
262263
263 exe_options.addOption(bool, "enable_logging", enable_logging);264 exe_options.addOption(bool, "enable_logging", enable_logging);
265 exe_options.addOption(bool, "enable_link_snapshots", enable_link_snapshots);
264 exe_options.addOption(bool, "enable_tracy", tracy != null);266 exe_options.addOption(bool, "enable_tracy", tracy != null);
265 exe_options.addOption(bool, "is_stage1", is_stage1);267 exe_options.addOption(bool, "is_stage1", is_stage1);
266 exe_options.addOption(bool, "omit_stage2", omit_stage2);268 exe_options.addOption(bool, "omit_stage2", omit_stage2);
...@@ -301,6 +303,7 @@ pub fn build(b: *Builder) !void {...@@ -301,6 +303,7 @@ pub fn build(b: *Builder) !void {
301 test_stage2.addOptions("build_options", test_stage2_options);303 test_stage2.addOptions("build_options", test_stage2_options);
302304
303 test_stage2_options.addOption(bool, "enable_logging", enable_logging);305 test_stage2_options.addOption(bool, "enable_logging", enable_logging);
306 test_stage2_options.addOption(bool, "enable_link_snapshots", enable_link_snapshots);
304 test_stage2_options.addOption(bool, "skip_non_native", skip_non_native);307 test_stage2_options.addOption(bool, "skip_non_native", skip_non_native);
305 test_stage2_options.addOption(bool, "skip_compile_errors", skip_compile_errors);308 test_stage2_options.addOption(bool, "skip_compile_errors", skip_compile_errors);
306 test_stage2_options.addOption(bool, "is_stage1", is_stage1);309 test_stage2_options.addOption(bool, "is_stage1", is_stage1);
src/Compilation.zig+3
...@@ -757,6 +757,8 @@ pub const InitOptions = struct {...@@ -757,6 +757,8 @@ pub const InitOptions = struct {
757 subsystem: ?std.Target.SubSystem = null,757 subsystem: ?std.Target.SubSystem = null,
758 /// WASI-only. Type of WASI execution model ("command" or "reactor").758 /// WASI-only. Type of WASI execution model ("command" or "reactor").
759 wasi_exec_model: ?std.builtin.WasiExecModel = null,759 wasi_exec_model: ?std.builtin.WasiExecModel = null,
760 /// (Zig compiler development) Enable dumping linker's state as JSON.
761 enable_link_snapshots: bool = false,
760};762};
761763
762fn addPackageTableToCacheHash(764fn addPackageTableToCacheHash(
...@@ -1438,6 +1440,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -1438,6 +1440,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
1438 .is_test = options.is_test,1440 .is_test = options.is_test,
1439 .wasi_exec_model = wasi_exec_model,1441 .wasi_exec_model = wasi_exec_model,
1440 .use_stage1 = use_stage1,1442 .use_stage1 = use_stage1,
1443 .enable_link_snapshots = options.enable_link_snapshots,
1441 });1444 });
1442 errdefer bin_file.destroy();1445 errdefer bin_file.destroy();
1443 comp.* = .{1446 comp.* = .{
src/config.zig.in+1
...@@ -6,6 +6,7 @@ pub const llvm_has_arc = false;...@@ -6,6 +6,7 @@ pub const llvm_has_arc = false;
6pub const version: [:0]const u8 = "@ZIG_VERSION@";6pub const version: [:0]const u8 = "@ZIG_VERSION@";
7pub const semver = @import("std").SemanticVersion.parse(version) catch unreachable;7pub const semver = @import("std").SemanticVersion.parse(version) catch unreachable;
8pub const enable_logging: bool = @ZIG_ENABLE_LOGGING_BOOL@;8pub const enable_logging: bool = @ZIG_ENABLE_LOGGING_BOOL@;
9pub const enable_link_snapshots: bool = false;
9pub const enable_tracy = false;10pub const enable_tracy = false;
10pub const is_stage1 = true;11pub const is_stage1 = true;
11pub const skip_non_native = false;12pub const skip_non_native = false;
src/link.zig+3
...@@ -126,6 +126,9 @@ pub const Options = struct {...@@ -126,6 +126,9 @@ pub const Options = struct {
126 /// WASI-only. Type of WASI execution model ("command" or "reactor").126 /// WASI-only. Type of WASI execution model ("command" or "reactor").
127 wasi_exec_model: std.builtin.WasiExecModel = undefined,127 wasi_exec_model: std.builtin.WasiExecModel = undefined,
128128
129 /// (Zig compiler development) Enable dumping of linker's state as JSON.
130 enable_link_snapshots: bool = false,
131
129 pub fn effectiveOutputMode(options: Options) std.builtin.OutputMode {132 pub fn effectiveOutputMode(options: Options) std.builtin.OutputMode {
130 return if (options.use_lld) .Obj else options.output_mode;133 return if (options.use_lld) .Obj else options.output_mode;
131 }134 }
src/link/MachO.zig+294-2
...@@ -938,6 +938,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {...@@ -938,6 +938,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
938 if (self.requires_adhoc_codesig) {938 if (self.requires_adhoc_codesig) {
939 try self.writeCodeSignature(); // code signing always comes last939 try self.writeCodeSignature(); // code signing always comes last
940 }940 }
941
942 if (build_options.enable_link_snapshots) {
943 if (self.base.options.enable_link_snapshots)
944 try self.snapshotState();
945 }
941 }946 }
942947
943 cache: {948 cache: {
...@@ -2424,6 +2429,14 @@ fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {...@@ -2424,6 +2429,14 @@ fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {
2424 continue;2429 continue;
2425 },2430 },
2426 .undef => {2431 .undef => {
2432 const undef = &self.undefs.items[resolv.where_index];
2433 undef.* = .{
2434 .n_strx = 0,
2435 .n_type = macho.N_UNDF,
2436 .n_sect = 0,
2437 .n_desc = 0,
2438 .n_value = 0,
2439 };
2427 _ = self.unresolved.fetchSwapRemove(resolv.where_index);2440 _ = self.unresolved.fetchSwapRemove(resolv.where_index);
2428 },2441 },
2429 }2442 }
...@@ -4826,9 +4839,17 @@ fn writeSymbolTable(self: *MachO) !void {...@@ -4826,9 +4839,17 @@ fn writeSymbolTable(self: *MachO) !void {
4826 }4839 }
4827 }4840 }
48284841
4842 var undefs = std.ArrayList(macho.nlist_64).init(self.base.allocator);
4843 defer undefs.deinit();
4844
4845 for (self.undefs.items) |sym| {
4846 if (sym.n_strx == 0) continue;
4847 try undefs.append(sym);
4848 }
4849
4829 const nlocals = locals.items.len;4850 const nlocals = locals.items.len;
4830 const nexports = self.globals.items.len;4851 const nexports = self.globals.items.len;
4831 const nundefs = self.undefs.items.len;4852 const nundefs = undefs.items.len;
48324853
4833 const locals_off = symtab.symoff;4854 const locals_off = symtab.symoff;
4834 const locals_size = nlocals * @sizeOf(macho.nlist_64);4855 const locals_size = nlocals * @sizeOf(macho.nlist_64);
...@@ -4843,7 +4864,7 @@ fn writeSymbolTable(self: *MachO) !void {...@@ -4843,7 +4864,7 @@ fn writeSymbolTable(self: *MachO) !void {
4843 const undefs_off = exports_off + exports_size;4864 const undefs_off = exports_off + exports_size;
4844 const undefs_size = nundefs * @sizeOf(macho.nlist_64);4865 const undefs_size = nundefs * @sizeOf(macho.nlist_64);
4845 log.debug("writing undefined symbols from 0x{x} to 0x{x}", .{ undefs_off, undefs_size + undefs_off });4866 log.debug("writing undefined symbols from 0x{x} to 0x{x}", .{ undefs_off, undefs_size + undefs_off });
4846 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.undefs.items), undefs_off);4867 try self.base.file.?.pwriteAll(mem.sliceAsBytes(undefs.items), undefs_off);
48474868
4848 symtab.nsyms = @intCast(u32, nlocals + nexports + nundefs);4869 symtab.nsyms = @intCast(u32, nlocals + nexports + nundefs);
4849 seg.inner.filesize += locals_size + exports_size + undefs_size;4870 seg.inner.filesize += locals_size + exports_size + undefs_size;
...@@ -5188,3 +5209,274 @@ pub fn findFirst(comptime T: type, haystack: []T, start: usize, predicate: anyty...@@ -5188,3 +5209,274 @@ pub fn findFirst(comptime T: type, haystack: []T, start: usize, predicate: anyty
5188 }5209 }
5189 return i;5210 return i;
5190}5211}
5212
5213fn snapshotState(self: *MachO) !void {
5214 const emit = self.base.options.emit orelse {
5215 log.debug("no emit directory found; skipping snapshot...", .{});
5216 return;
5217 };
5218
5219 const Snapshot = struct {
5220 const Node = struct {
5221 const Tag = enum {
5222 section_start,
5223 section_end,
5224 atom_start,
5225 atom_end,
5226 relocation,
5227
5228 pub fn jsonStringify(
5229 tag: Tag,
5230 options: std.json.StringifyOptions,
5231 out_stream: anytype,
5232 ) !void {
5233 _ = options;
5234 switch (tag) {
5235 .section_start => try out_stream.writeAll("\"section_start\""),
5236 .section_end => try out_stream.writeAll("\"section_end\""),
5237 .atom_start => try out_stream.writeAll("\"atom_start\""),
5238 .atom_end => try out_stream.writeAll("\"atom_end\""),
5239 .relocation => try out_stream.writeAll("\"relocation\""),
5240 }
5241 }
5242 };
5243 const Payload = struct {
5244 name: []const u8 = "",
5245 aliases: [][]const u8 = &[0][]const u8{},
5246 is_global: bool = false,
5247 target: u64 = 0,
5248 };
5249 address: u64,
5250 tag: Tag,
5251 payload: Payload,
5252 };
5253 timestamp: i128,
5254 nodes: []Node,
5255 };
5256
5257 var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator);
5258 defer arena_allocator.deinit();
5259 const arena = &arena_allocator.allocator;
5260
5261 const out_file = try emit.directory.handle.createFile("snapshots.json", .{
5262 .truncate = self.cold_start,
5263 .read = true,
5264 });
5265 defer out_file.close();
5266
5267 if (out_file.seekFromEnd(-1)) {
5268 try out_file.writer().writeByte(',');
5269 } else |err| switch (err) {
5270 error.Unseekable => try out_file.writer().writeByte('['),
5271 else => |e| return e,
5272 }
5273 var writer = out_file.writer();
5274
5275 var snapshot = Snapshot{
5276 .timestamp = std.time.nanoTimestamp(),
5277 .nodes = undefined,
5278 };
5279 var nodes = std.ArrayList(Snapshot.Node).init(arena);
5280
5281 for (self.section_ordinals.keys()) |key| {
5282 const seg = self.load_commands.items[key.seg].Segment;
5283 const sect = seg.sections.items[key.sect];
5284 const sect_name = try std.fmt.allocPrint(arena, "{s},{s}", .{
5285 commands.segmentName(sect),
5286 commands.sectionName(sect),
5287 });
5288 try nodes.append(.{
5289 .address = sect.addr,
5290 .tag = .section_start,
5291 .payload = .{ .name = sect_name },
5292 });
5293
5294 var atom: *Atom = self.atoms.get(key) orelse {
5295 try nodes.append(.{
5296 .address = sect.addr + sect.size,
5297 .tag = .section_end,
5298 .payload = .{},
5299 });
5300 continue;
5301 };
5302
5303 while (atom.prev) |prev| {
5304 atom = prev;
5305 }
5306
5307 while (true) {
5308 const atom_sym = self.locals.items[atom.local_sym_index];
5309 var node = Snapshot.Node{
5310 .address = atom_sym.n_value,
5311 .tag = .atom_start,
5312 .payload = .{
5313 .name = self.getString(atom_sym.n_strx),
5314 .is_global = self.symbol_resolver.contains(atom_sym.n_strx),
5315 },
5316 };
5317
5318 var aliases = std.ArrayList([]const u8).init(arena);
5319 for (atom.aliases.items) |loc| {
5320 try aliases.append(self.getString(self.locals.items[loc].n_strx));
5321 }
5322 node.payload.aliases = aliases.toOwnedSlice();
5323 try nodes.append(node);
5324
5325 var relocs = std.ArrayList(Snapshot.Node).init(arena);
5326 try relocs.ensureTotalCapacity(atom.relocs.items.len);
5327 for (atom.relocs.items) |rel| {
5328 const arch = self.base.options.target.cpu.arch;
5329 const source_addr = blk: {
5330 const sym = self.locals.items[atom.local_sym_index];
5331 break :blk sym.n_value + rel.offset;
5332 };
5333 const target_addr = blk: {
5334 const is_via_got = got: {
5335 switch (arch) {
5336 .aarch64 => break :got switch (@intToEnum(macho.reloc_type_arm64, rel.@"type")) {
5337 .ARM64_RELOC_GOT_LOAD_PAGE21, .ARM64_RELOC_GOT_LOAD_PAGEOFF12 => true,
5338 else => false,
5339 },
5340 .x86_64 => break :got switch (@intToEnum(macho.reloc_type_x86_64, rel.@"type")) {
5341 .X86_64_RELOC_GOT, .X86_64_RELOC_GOT_LOAD => true,
5342 else => false,
5343 },
5344 else => unreachable,
5345 }
5346 };
5347
5348 if (is_via_got) {
5349 const got_atom = self.got_entries_map.get(rel.target).?;
5350 break :blk self.locals.items[got_atom.local_sym_index].n_value;
5351 }
5352
5353 switch (rel.target) {
5354 .local => |sym_index| {
5355 const sym = self.locals.items[sym_index];
5356 const is_tlv = is_tlv: {
5357 const source_sym = self.locals.items[atom.local_sym_index];
5358 const match = self.section_ordinals.keys()[source_sym.n_sect - 1];
5359 const match_seg = self.load_commands.items[match.seg].Segment;
5360 const match_sect = match_seg.sections.items[match.sect];
5361 break :is_tlv commands.sectionType(match_sect) == macho.S_THREAD_LOCAL_VARIABLES;
5362 };
5363 if (is_tlv) {
5364 const match_seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
5365 const base_address = inner: {
5366 if (self.tlv_data_section_index) |i| {
5367 break :inner match_seg.sections.items[i].addr;
5368 } else if (self.tlv_bss_section_index) |i| {
5369 break :inner match_seg.sections.items[i].addr;
5370 } else unreachable;
5371 };
5372 break :blk sym.n_value - base_address;
5373 }
5374 break :blk sym.n_value;
5375 },
5376 .global => |n_strx| {
5377 const resolv = self.symbol_resolver.get(n_strx).?;
5378 switch (resolv.where) {
5379 .global => break :blk self.globals.items[resolv.where_index].n_value,
5380 .undef => {
5381 break :blk if (self.stubs_map.get(n_strx)) |stub_atom|
5382 self.locals.items[stub_atom.local_sym_index].n_value
5383 else
5384 0;
5385 },
5386 }
5387 },
5388 }
5389 };
5390
5391 relocs.appendAssumeCapacity(.{
5392 .address = source_addr,
5393 .tag = .relocation,
5394 .payload = .{ .target = target_addr },
5395 });
5396 }
5397
5398 if (atom.contained.items.len == 0) {
5399 try nodes.appendSlice(relocs.items);
5400 } else {
5401 // Need to reverse iteration order of relocs since by default for relocatable sources
5402 // they come in reverse. For linking, this doesn't matter in any way, however, for
5403 // arranging the memoryline for displaying it does.
5404 std.mem.reverse(Snapshot.Node, relocs.items);
5405
5406 var next_i: usize = 0;
5407 var last_rel: usize = 0;
5408 while (next_i < atom.contained.items.len) : (next_i += 1) {
5409 const loc = atom.contained.items[next_i];
5410 const cont_sym = self.locals.items[loc.local_sym_index];
5411 const cont_sym_name = self.getString(cont_sym.n_strx);
5412 var contained_node = Snapshot.Node{
5413 .address = cont_sym.n_value,
5414 .tag = .atom_start,
5415 .payload = .{
5416 .name = cont_sym_name,
5417 .is_global = self.symbol_resolver.contains(cont_sym.n_strx),
5418 },
5419 };
5420
5421 // Accumulate aliases
5422 var inner_aliases = std.ArrayList([]const u8).init(arena);
5423 while (true) {
5424 if (next_i + 1 >= atom.contained.items.len) break;
5425 const next_sym = self.locals.items[atom.contained.items[next_i + 1].local_sym_index];
5426 if (next_sym.n_value != cont_sym.n_value) break;
5427 const next_sym_name = self.getString(next_sym.n_strx);
5428 if (self.symbol_resolver.contains(next_sym.n_strx)) {
5429 try inner_aliases.append(contained_node.payload.name);
5430 contained_node.payload.name = next_sym_name;
5431 contained_node.payload.is_global = true;
5432 } else try inner_aliases.append(next_sym_name);
5433 next_i += 1;
5434 }
5435
5436 const cont_size = if (next_i + 1 < atom.contained.items.len)
5437 self.locals.items[atom.contained.items[next_i + 1].local_sym_index].n_value - cont_sym.n_value
5438 else
5439 atom_sym.n_value + atom.size - cont_sym.n_value;
5440
5441 contained_node.payload.aliases = inner_aliases.toOwnedSlice();
5442 try nodes.append(contained_node);
5443
5444 for (relocs.items[last_rel..]) |rel, rel_i| {
5445 if (rel.address >= cont_sym.n_value + cont_size) {
5446 last_rel = rel_i;
5447 break;
5448 }
5449 try nodes.append(rel);
5450 }
5451
5452 try nodes.append(.{
5453 .address = cont_sym.n_value + cont_size,
5454 .tag = .atom_end,
5455 .payload = .{},
5456 });
5457 }
5458 }
5459
5460 try nodes.append(.{
5461 .address = atom_sym.n_value + atom.size,
5462 .tag = .atom_end,
5463 .payload = .{},
5464 });
5465
5466 if (atom.next) |next| {
5467 atom = next;
5468 } else break;
5469 }
5470
5471 try nodes.append(.{
5472 .address = sect.addr + sect.size,
5473 .tag = .section_end,
5474 .payload = .{},
5475 });
5476 }
5477
5478 snapshot.nodes = nodes.toOwnedSlice();
5479
5480 try std.json.stringify(snapshot, .{}, writer);
5481 try writer.writeByte(']');
5482}
src/link/MachO/Atom.zig+1-7
...@@ -345,15 +345,9 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC...@@ -345,15 +345,9 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC
345 const seg = context.object.load_commands.items[context.object.segment_cmd_index.?].Segment;345 const seg = context.object.load_commands.items[context.object.segment_cmd_index.?].Segment;
346 const sect = seg.sections.items[sect_id];346 const sect = seg.sections.items[sect_id];
347 const match = (try context.macho_file.getMatchingSection(sect)) orelse unreachable;347 const match = (try context.macho_file.getMatchingSection(sect)) orelse unreachable;
348 const sym_name = try std.fmt.allocPrint(context.allocator, "{s}_{s}_{s}", .{
349 context.object.name,
350 commands.segmentName(sect),
351 commands.sectionName(sect),
352 });
353 defer context.allocator.free(sym_name);
354 const local_sym_index = @intCast(u32, context.macho_file.locals.items.len);348 const local_sym_index = @intCast(u32, context.macho_file.locals.items.len);
355 try context.macho_file.locals.append(context.allocator, .{349 try context.macho_file.locals.append(context.allocator, .{
356 .n_strx = try context.macho_file.makeString(sym_name),350 .n_strx = 0,
357 .n_type = macho.N_SECT,351 .n_type = macho.N_SECT,
358 .n_sect = @intCast(u8, context.macho_file.section_ordinals.getIndex(match).? + 1),352 .n_sect = @intCast(u8, context.macho_file.section_ordinals.getIndex(match).? + 1),
359 .n_desc = 0,353 .n_desc = 0,
src/link/MachO/Object.zig+8-8
...@@ -174,7 +174,13 @@ pub fn free(self: *Object, allocator: *Allocator, macho_file: *MachO) void {...@@ -174,7 +174,13 @@ pub fn free(self: *Object, allocator: *Allocator, macho_file: *MachO) void {
174 if (atom.local_sym_index != 0) {174 if (atom.local_sym_index != 0) {
175 macho_file.locals_free_list.append(allocator, atom.local_sym_index) catch {};175 macho_file.locals_free_list.append(allocator, atom.local_sym_index) catch {};
176 const local = &macho_file.locals.items[atom.local_sym_index];176 const local = &macho_file.locals.items[atom.local_sym_index];
177 local.n_type = 0;177 local.* = .{
178 .n_strx = 0,
179 .n_type = 0,
180 .n_sect = 0,
181 .n_desc = 0,
182 .n_value = 0,
183 };
178 atom.local_sym_index = 0;184 atom.local_sym_index = 0;
179 }185 }
180 if (atom == last_atom) {186 if (atom == last_atom) {
...@@ -458,15 +464,9 @@ pub fn parseIntoAtoms(self: *Object, allocator: *Allocator, macho_file: *MachO)...@@ -458,15 +464,9 @@ pub fn parseIntoAtoms(self: *Object, allocator: *Allocator, macho_file: *MachO)
458 // a temp one, unless we already did that when working out the relocations464 // a temp one, unless we already did that when working out the relocations
459 // of other atoms.465 // of other atoms.
460 const atom_local_sym_index = self.sections_as_symbols.get(sect_id) orelse blk: {466 const atom_local_sym_index = self.sections_as_symbols.get(sect_id) orelse blk: {
461 const sym_name = try std.fmt.allocPrint(allocator, "{s}_{s}_{s}", .{
462 self.name,
463 segmentName(sect),
464 sectionName(sect),
465 });
466 defer allocator.free(sym_name);
467 const atom_local_sym_index = @intCast(u32, macho_file.locals.items.len);467 const atom_local_sym_index = @intCast(u32, macho_file.locals.items.len);
468 try macho_file.locals.append(allocator, .{468 try macho_file.locals.append(allocator, .{
469 .n_strx = try macho_file.makeString(sym_name),469 .n_strx = 0,
470 .n_type = macho.N_SECT,470 .n_type = macho.N_SECT,
471 .n_sect = @intCast(u8, macho_file.section_ordinals.getIndex(match).? + 1),471 .n_sect = @intCast(u8, macho_file.section_ordinals.getIndex(match).? + 1),
472 .n_desc = 0,472 .n_desc = 0,
src/main.zig+9
...@@ -434,6 +434,7 @@ const usage_build_generic =...@@ -434,6 +434,7 @@ const usage_build_generic =
434 \\ --verbose-llvm-cpu-features Enable compiler debug output for LLVM CPU features434 \\ --verbose-llvm-cpu-features Enable compiler debug output for LLVM CPU features
435 \\ --debug-log [scope] Enable printing debug/info log messages for scope435 \\ --debug-log [scope] Enable printing debug/info log messages for scope
436 \\ --debug-compile-errors Crash with helpful diagnostics at the first compile error436 \\ --debug-compile-errors Crash with helpful diagnostics at the first compile error
437 \\ --debug-link-snapshot Enable dumping of the linker's state in JSON format
437 \\438 \\
438;439;
439440
...@@ -632,6 +633,7 @@ fn buildOutputType(...@@ -632,6 +633,7 @@ fn buildOutputType(
632 var major_subsystem_version: ?u32 = null;633 var major_subsystem_version: ?u32 = null;
633 var minor_subsystem_version: ?u32 = null;634 var minor_subsystem_version: ?u32 = null;
634 var wasi_exec_model: ?std.builtin.WasiExecModel = null;635 var wasi_exec_model: ?std.builtin.WasiExecModel = null;
636 var enable_link_snapshots: bool = false;
635637
636 var system_libs = std.ArrayList([]const u8).init(gpa);638 var system_libs = std.ArrayList([]const u8).init(gpa);
637 defer system_libs.deinit();639 defer system_libs.deinit();
...@@ -929,6 +931,12 @@ fn buildOutputType(...@@ -929,6 +931,12 @@ fn buildOutputType(
929 } else {931 } else {
930 try log_scopes.append(gpa, args[i]);932 try log_scopes.append(gpa, args[i]);
931 }933 }
934 } else if (mem.eql(u8, arg, "--debug-link-snapshot")) {
935 if (!build_options.enable_link_snapshots) {
936 std.log.warn("Zig was compiled without linker snapshots enabled (-Dlink-snapshot). --debug-link-snapshot has no effect.", .{});
937 } else {
938 enable_link_snapshots = true;
939 }
932 } else if (mem.eql(u8, arg, "-fcompiler-rt")) {940 } else if (mem.eql(u8, arg, "-fcompiler-rt")) {
933 want_compiler_rt = true;941 want_compiler_rt = true;
934 } else if (mem.eql(u8, arg, "-fno-compiler-rt")) {942 } else if (mem.eql(u8, arg, "-fno-compiler-rt")) {
...@@ -2139,6 +2147,7 @@ fn buildOutputType(...@@ -2139,6 +2147,7 @@ fn buildOutputType(
2139 .subsystem = subsystem,2147 .subsystem = subsystem,
2140 .wasi_exec_model = wasi_exec_model,2148 .wasi_exec_model = wasi_exec_model,
2141 .debug_compile_errors = debug_compile_errors,2149 .debug_compile_errors = debug_compile_errors,
2150 .enable_link_snapshots = enable_link_snapshots,
2142 }) catch |err| {2151 }) catch |err| {
2143 fatal("unable to create compilation: {s}", .{@errorName(err)});2152 fatal("unable to create compilation: {s}", .{@errorName(err)});
2144 };2153 };