| author | |
| committer | |
| log | d0dceae736edb43d4c217306a2b0445277f184ce |
| tree | d6bad107975e61e360a50de5436fb297a97435fb |
| parent | 912e7dc54b9b49d96123ffd398e6d40b455997fe |
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 | } |
| 206 | 206 | ||
| 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; | ||
| 208 | 209 | ||
| 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); |
| 262 | 263 | ||
| 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); |
| 302 | 304 | ||
| 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 | }; |
| 761 | 763 | ||
| 762 | fn addPackageTableToCacheHash( | 764 | fn 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; |
| 6 | pub const version: [:0]const u8 = "@ZIG_VERSION@"; | 6 | pub const version: [:0]const u8 = "@ZIG_VERSION@"; |
| 7 | pub const semver = @import("std").SemanticVersion.parse(version) catch unreachable; | 7 | pub const semver = @import("std").SemanticVersion.parse(version) catch unreachable; |
| 8 | pub const enable_logging: bool = @ZIG_ENABLE_LOGGING_BOOL@; | 8 | pub const enable_logging: bool = @ZIG_ENABLE_LOGGING_BOOL@; |
| 9 | pub const enable_link_snapshots: bool = false; | ||
| 9 | pub const enable_tracy = false; | 10 | pub const enable_tracy = false; |
| 10 | pub const is_stage1 = true; | 11 | pub const is_stage1 = true; |
| 11 | pub const skip_non_native = false; | 12 | pub 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, |
| 128 | 128 | ||
| 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 last | 939 | 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 | } |
| 942 | 947 | ||
| 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 | } |
| 4828 | 4841 | ||
| 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; |
| 4832 | 4853 | ||
| 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); |
| 4847 | 4868 | ||
| 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 | |||
| 5213 | fn 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 relocations | 464 | // 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 features | 434 | \\ --verbose-llvm-cpu-features Enable compiler debug output for LLVM CPU features |
| 435 | \\ --debug-log [scope] Enable printing debug/info log messages for scope | 435 | \\ --debug-log [scope] Enable printing debug/info log messages for scope |
| 436 | \\ --debug-compile-errors Crash with helpful diagnostics at the first compile error | 436 | \\ --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 | ; |
| 439 | 440 | ||
| ... | @@ -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; | ||
| 635 | 637 | ||
| 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 | }; |