authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2026-06-05 01:55:35-04:00
committergravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2026-06-23 00:26:55-04:00
loga98a4f465797a7205f9b1cefedfcb4957b65d593
tree29c122cfe37573c168ed0937bf6c6eecc27b4c42
parentdef87a6efb03197d2387dbbe91f02d1676427034

Coff: special symbols

- Add a step after exports are known that looks for specific required symbols (entry points, _tls_used) and set up relocs as they may move after this point - Remove the special case handling of entry point symbols - Change the linker crash reporter to require --dump-link-snapshot to dump the snapshot

4 files changed, 180 insertions(+), 96 deletions(-)

src/crash_report.zig+6-3
...@@ -133,10 +133,13 @@ fn dumpCrashContext() Io.Writer.Error!void {...@@ -133,10 +133,13 @@ fn dumpCrashContext() Io.Writer.Error!void {
133 } else if (LinkerOp.current) |linker_op| {133 } else if (LinkerOp.current) |linker_op| {
134 try w.writeAll("Linker snapshot:\n\n");134 try w.writeAll("Linker snapshot:\n\n");
135 if (build_options.enable_link_snapshots) {135 if (build_options.enable_link_snapshots) {
136 try linker_op.lf.dump(w, linker_op.tid);136 switch (try linker_op.lf.dump(w, linker_op.tid)) {
137 try w.writeAll("\n\n");137 .unsupported => try w.writeAll("(backend does not support link snapshots))"),
138 .disabled => try w.writeAll("(run with --debug-link-snapshot to dump linker state)"),
139 .enabled => try w.writeAll("\n\n"),
140 }
138 } else {141 } else {
139 try w.print("(build with -Dlink-snapshot to dump linker state)", .{});142 try w.writeAll("(build with -Dlink-snapshot to dump linker state)");
140 }143 }
141 } else {144 } else {
142 try w.writeAll("(no context)\n\n");145 try w.writeAll("(no context)\n\n");
src/link.zig+8-2
...@@ -1090,7 +1090,13 @@ pub const File = struct {...@@ -1090,7 +1090,13 @@ pub const File = struct {
1090 }1090 }
1091 }1091 }
10921092
1093 pub fn dump(base: *File, w: *Io.Writer, tid: Zcu.PerThread.Id) !void {1093 pub const DumpResult = enum {
1094 unsupported,
1095 disabled,
1096 enabled,
1097 };
1098
1099 pub fn dump(base: *File, w: *Io.Writer, tid: Zcu.PerThread.Id) !DumpResult {
1094 if (!build_options.enable_link_snapshots) unreachable;1100 if (!build_options.enable_link_snapshots) unreachable;
1095 switch (base.tag) {1101 switch (base.tag) {
1096 .elf,1102 .elf,
...@@ -1100,7 +1106,7 @@ pub const File = struct {...@@ -1100,7 +1106,7 @@ pub const File = struct {
1100 .spirv,1106 .spirv,
1101 .plan9,1107 .plan9,
1102 .lld,1108 .lld,
1103 => {},1109 => return .unsupported,
1104 inline else => |tag| {1110 inline else => |tag| {
1105 dev.check(tag.devFeature());1111 dev.check(tag.devFeature());
1106 return @as(*tag.Type(), @fieldParentPtr("base", base)).dump(w, tid);1112 return @as(*tag.Type(), @fieldParentPtr("base", base)).dump(w, tid);
src/link/Coff.zig+158-89
...@@ -49,6 +49,7 @@ input_sections: std.ArrayList(Node.InputSection),...@@ -49,6 +49,7 @@ input_sections: std.ArrayList(Node.InputSection),
49input_section_pending_index: u32,49input_section_pending_index: u32,
50inputs_complete: bool,50inputs_complete: bool,
51exports_complete: bool,51exports_complete: bool,
52special_symbols_complete: bool,
52strings: std.HashMapUnmanaged(53strings: std.HashMapUnmanaged(
53 u32,54 u32,
54 void,55 void,
...@@ -74,7 +75,6 @@ pending_uavs: std.array_hash_map.Auto(Node.UavMapIndex, struct {...@@ -74,7 +75,6 @@ pending_uavs: std.array_hash_map.Auto(Node.UavMapIndex, struct {
74 alignment: InternPool.Alignment,75 alignment: InternPool.Alignment,
75}),76}),
76relocs: std.ArrayList(Reloc),77relocs: std.ArrayList(Reloc),
77entry: Node.GlobalMapIndex,
78const_prog_node: std.Progress.Node,78const_prog_node: std.Progress.Node,
79synth_prog_node: std.Progress.Node,79synth_prog_node: std.Progress.Node,
80symbol_prog_node: std.Progress.Node,80symbol_prog_node: std.Progress.Node,
...@@ -896,8 +896,7 @@ pub const Symbol = struct {...@@ -896,8 +896,7 @@ pub const Symbol = struct {
896 dll_storage_class: DllStorageClass,896 dll_storage_class: DllStorageClass,
897 // Only defined for .alias_si and .alias_name897 // Only defined for .alias_si and .alias_name
898 weak_external_strat: WeakExternalStrat,898 weak_external_strat: WeakExternalStrat,
899 is_entry: bool,899 _: u8 = 0,
900 _: u7 = 0,
901 },900 },
902 /// Relocations contained within this symbol901 /// Relocations contained within this symbol
903 loc_relocs: Reloc.Index,902 loc_relocs: Reloc.Index,
...@@ -1017,6 +1016,11 @@ pub const Symbol = struct {...@@ -1017,6 +1016,11 @@ pub const Symbol = struct {
1017 return &coff.symbols.items[@intFromEnum(si)];1016 return &coff.symbols.items[@intFromEnum(si)];
1018 }1017 }
10191018
1019 pub fn unwrap(si: Symbol.Index) ?Symbol.Index {
1020 if (si == .null) return null;
1021 return si;
1022 }
1023
1020 pub fn node(si: Symbol.Index, coff: *Coff) MappedFile.Node.Index {1024 pub fn node(si: Symbol.Index, coff: *Coff) MappedFile.Node.Index {
1021 const ni = si.get(coff).ni;1025 const ni = si.get(coff).ni;
1022 assert(ni != .none);1026 assert(ni != .none);
...@@ -1074,12 +1078,6 @@ pub const Symbol = struct {...@@ -1074,12 +1078,6 @@ pub const Symbol = struct {
1074 pub fn applyTargetRelocs(si: Symbol.Index, coff: *Coff, end: Reloc.Index) void {1078 pub fn applyTargetRelocs(si: Symbol.Index, coff: *Coff, end: Reloc.Index) void {
1075 const sym = si.get(coff);1079 const sym = si.get(coff);
10761080
1077 // TODO: Would this be better modeled using an actual reloc? Would need a si for the header
1078 if (sym.flags.is_entry) {
1079 log.debug("updateEntryRVA({d}, 0x{x})", .{ si, sym.rva });
1080 coff.optionalHeaderStandardPtr().address_of_entry_point = sym.rva;
1081 }
1082
1083 var ri = sym.target_relocs;1081 var ri = sym.target_relocs;
1084 while (ri != end) {1082 while (ri != end) {
1085 const reloc = ri.get(coff);1083 const reloc = ri.get(coff);
...@@ -1549,6 +1547,7 @@ fn create(...@@ -1549,6 +1547,7 @@ fn create(
1549 .input_section_pending_index = 0,1547 .input_section_pending_index = 0,
1550 .inputs_complete = false,1548 .inputs_complete = false,
1551 .exports_complete = false,1549 .exports_complete = false,
1550 .special_symbols_complete = false,
1552 .strings = .empty,1551 .strings = .empty,
1553 .string_bytes = .empty,1552 .string_bytes = .empty,
1554 .section_table = .empty,1553 .section_table = .empty,
...@@ -1567,7 +1566,6 @@ fn create(...@@ -1567,7 +1566,6 @@ fn create(
1567 }),1566 }),
1568 .pending_uavs = .empty,1567 .pending_uavs = .empty,
1569 .relocs = .empty,1568 .relocs = .empty,
1570 .entry = .none,
1571 .const_prog_node = .none,1569 .const_prog_node = .none,
1572 .synth_prog_node = .none,1570 .synth_prog_node = .none,
1573 .symbol_prog_node = .none,1571 .symbol_prog_node = .none,
...@@ -2525,7 +2523,6 @@ fn addSymbolAssumeCapacity(coff: *Coff) Symbol.Index {...@@ -2525,7 +2523,6 @@ fn addSymbolAssumeCapacity(coff: *Coff) Symbol.Index {
2525 .type = .unknown,2523 .type = .unknown,
2526 .dll_storage_class = .default,2524 .dll_storage_class = .default,
2527 .weak_external_strat = undefined,2525 .weak_external_strat = undefined,
2528 .is_entry = false,
2529 },2526 },
2530 .loc_relocs = .none,2527 .loc_relocs = .none,
2531 .target_relocs = .none,2528 .target_relocs = .none,
...@@ -2642,6 +2639,14 @@ fn getOrPutGlobalSymbol(...@@ -2642,6 +2639,14 @@ fn getOrPutGlobalSymbol(
2642 return sym_gop;2639 return sym_gop;
2643}2640}
26442641
2642fn getDefinedGlobal(coff: *Coff, name: []const u8) Symbol.Index {
2643 if (coff.globals.get(.{
2644 .name = coff.getString(name).unwrap() orelse return .null,
2645 .lib_name = .none,
2646 })) |si| if (si.get(coff).ni != .none) return si;
2647 return .null;
2648}
2649
2645pub fn globalSymbol(coff: *Coff, opts: GlobalOptions) !Symbol.Index {2650pub fn globalSymbol(coff: *Coff, opts: GlobalOptions) !Symbol.Index {
2646 const gop = try coff.getOrPutGlobalSymbol(opts);2651 const gop = try coff.getOrPutGlobalSymbol(opts);
2647 if (gop.found_existing) {2652 if (gop.found_existing) {
...@@ -3423,8 +3428,6 @@ pub fn addReloc(...@@ -3423,8 +3428,6 @@ pub fn addReloc(
3423) !void {3428) !void {
3424 const gpa = coff.base.comp.gpa;3429 const gpa = coff.base.comp.gpa;
3425 const target = target_si.get(coff);3430 const target = target_si.get(coff);
3426 // TODO: Could duplicate the uninit flag on Symbol.flags?
3427 assert(!coff.targetLoad(loc_si.get(coff).section_number.header(coff).flags).CNT_UNINITIALIZED_DATA);
34283431
3429 const ri: Reloc.Index = @enumFromInt(coff.relocs.items.len);3432 const ri: Reloc.Index = @enumFromInt(coff.relocs.items.len);
3430 log.debug("addReloc({d}@{d}+0x{x} -> {d}@{d}+0x{x}{s}) = {d}", .{3433 log.debug("addReloc({d}@{d}+0x{x} -> {d}@{d}+0x{x}{s}) = {d}", .{
...@@ -4386,11 +4389,10 @@ fn loadObject(...@@ -4386,11 +4389,10 @@ fn loadObject(
4386 // Resolve this once we see alias4389 // Resolve this once we see alias
4387 alias.weak_external_psi = .wrap(@intCast(i));4390 alias.weak_external_psi = .wrap(@intCast(i));
4388 } else {4391 } else {
4389 sym.setValue(if (alias.si == .null) .{4392 sym.setValue(if (alias.si.unwrap()) |alias_si| .{
4390 // See .external branch above4393 .alias_si = alias_si,
4391 .alias_name = alias.name,
4392 } else .{4394 } else .{
4393 .alias_si = alias.si,4395 .alias_name = alias.name,
4394 });4396 });
4395 sym.flags.weak_external_strat = pending_symbols.values()[i + 1].value.weak_external_aux;4397 sym.flags.weak_external_strat = pending_symbols.values()[i + 1].value.weak_external_aux;
4396 }4398 }
...@@ -5027,35 +5029,9 @@ pub fn prelink(coff: *Coff, prog_node: std.Progress.Node) link.Error!void {...@@ -5027,35 +5029,9 @@ pub fn prelink(coff: *Coff, prog_node: std.Progress.Node) link.Error!void {
5027 }5029 }
5028 }5030 }
50295031
5030 if (coff.isImage() and comp.config.link_libc) {
5031 const entries: []const struct { ?[]const u8, []const u8 } = if (coff.isExe())
5032 if (comp.zcu == null) switch (coff.optionalHeaderField(.subsystem)) {
5033 .WINDOWS_CUI => &.{
5034 .{ "main", "mainCRTStartup" },
5035 .{ "wmain", "wmainCRTStartup" },
5036 },
5037 .WINDOWS_GUI => &.{
5038 .{ "WinMain", "WinMainCRTStartup" },
5039 .{ "wWinMain", "wWinMainCRTStartup" },
5040 },
5041 else => unreachable,
5042 } else &.{}
5043 else
5044 &.{.{ null, "_DllMainCRTStartup" }};
5045
5046 for (entries) |entry| {
5047 if (entry[0]) |required_name| {
5048 const str = coff.getString(required_name).unwrap() orelse continue;
5049 const si = coff.globals.get(.{ .name = str, .lib_name = .none }) orelse continue;
5050 if (si.get(coff).ni == .none) continue;
5051 }
5052
5053 const si = try coff.globalSymbol(.{ .name = entry[1], .type = .code });
5054 coff.updateEntry(si.get(coff).gmi);
5055 }
5056 }
5057
5058 coff.inputs_complete = true;5032 coff.inputs_complete = true;
5033 if (comp.zcu == null)
5034 coff.exports_complete = true;
5059}5035}
50605036
5061pub fn updateNav(coff: *Coff, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void {5037pub fn updateNav(coff: *Coff, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void {
...@@ -5347,17 +5323,6 @@ fn reportUndefs(coff: *Coff, tid: Zcu.PerThread.Id) !void {...@@ -5347,17 +5323,6 @@ fn reportUndefs(coff: *Coff, tid: Zcu.PerThread.Id) !void {
5347 const gpa = comp.gpa;5323 const gpa = comp.gpa;
5348 const max_notes = 4;5324 const max_notes = 4;
53495325
5350 if (coff.isImage()) {
5351 if (coff.entry == .none)
5352 comp.link_diags.addError("no entry point defined", .{})
5353 else if (coff.entry.symbol(coff).get(coff).ni == .none) {
5354 comp.link_diags.addError(
5355 "no definition for entry point '{s}' found",
5356 .{coff.entry.globalName(coff).name.toSlice(coff)},
5357 );
5358 }
5359 }
5360
5361 var undef_indices: std.ArrayListUnmanaged(u32) = .empty;5326 var undef_indices: std.ArrayListUnmanaged(u32) = .empty;
5362 for (coff.relocs.items, 0..) |reloc, reloc_i| {5327 for (coff.relocs.items, 0..) |reloc, reloc_i| {
5363 const target_sym = reloc.target.get(coff);5328 const target_sym = reloc.target.get(coff);
...@@ -5406,12 +5371,20 @@ fn reportUndefs(coff: *Coff, tid: Zcu.PerThread.Id) !void {...@@ -5406,12 +5371,20 @@ fn reportUndefs(coff: *Coff, tid: Zcu.PerThread.Id) !void {
5406 for (undef_indices.items[start_i .. i + 1]) |reference_i| {5371 for (undef_indices.items[start_i .. i + 1]) |reference_i| {
5407 if (err.note_slot == num_full_notes) break;5372 if (err.note_slot == num_full_notes) break;
54085373
5409 const loc_si = coff.relocs.items[reference_i].loc;5374 const reloc = &coff.relocs.items[reference_i];
5375 const loc_si = reloc.loc;
5410 if (loc_si == prev_loc_si) continue;5376 if (loc_si == prev_loc_si) continue;
5411 defer prev_loc_si = loc_si;5377 defer prev_loc_si = loc_si;
54125378
5413 const loc_sym = loc_si.get(coff);5379 const loc_sym = loc_si.get(coff);
5414 switch (coff.getNode(loc_sym.ni)) {5380 switch (coff.getNode(loc_sym.ni)) {
5381 .data_directories => {
5382 const dir_align = std.mem.Alignment.of(std.coff.ImageDataDirectory);
5383 const dir: std.coff.IMAGE.DIRECTORY_ENTRY =
5384 @enumFromInt(dir_align.backward(reloc.offset) / @sizeOf(std.coff.IMAGE.DIRECTORY_ENTRY));
5385 err.addNote("referenced by data directory entry: {t}", .{dir});
5386 },
5387 .optional_header => err.addNote("referenced by optional header field", .{}),
5415 .input_section => |isi| {5388 .input_section => |isi| {
5416 const other_ioi = isi.input(coff);5389 const other_ioi = isi.input(coff);
5417 if (loc_sym.gmi == .none) {5390 if (loc_sym.gmi == .none) {
...@@ -5577,6 +5550,17 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {...@@ -5577,6 +5550,17 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
5577 }) coff.late_globals_pending_index += 1;5550 }) coff.late_globals_pending_index += 1;
5578 break :task;5551 break :task;
5579 }5552 }
5553 if (coff.exports_complete and !coff.special_symbols_complete) {
5554 coff.special_symbols_complete = true;
5555 coff.flushSpecialSymbols() catch |err| switch (err) {
5556 error.OutOfMemory => |e| return e,
5557 else => |e| return comp.link_diags.fail(
5558 "linker failed to flush special symbols: {t}",
5559 .{e},
5560 ),
5561 };
5562 break :task;
5563 }
5580 var lazy_it = coff.lazy.iterator();5564 var lazy_it = coff.lazy.iterator();
5581 while (lazy_it.next()) |lazy| if (lazy.value.pending_index < lazy.value.map.count()) {5565 while (lazy_it.next()) |lazy| if (lazy.value.pending_index < lazy.value.map.count()) {
5582 const pt: Zcu.PerThread = .{ .zcu = comp.zcu.?, .tid = tid };5566 const pt: Zcu.PerThread = .{ .zcu = comp.zcu.?, .tid = tid };
...@@ -5713,7 +5697,9 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {...@@ -5713,7 +5697,9 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
5713 if (coff.pending_uavs.count() > 0) return true;5697 if (coff.pending_uavs.count() > 0) return true;
5714 if (coff.pending_input != null) return true;5698 if (coff.pending_input != null) return true;
5715 if (coff.inputs_complete and coff.globals.count() > coff.global_pending_index) return true;5699 if (coff.inputs_complete and coff.globals.count() > coff.global_pending_index) return true;
5700 assert(!coff.exports_complete or coff.inputs_complete);
5716 if (coff.exports_complete and coff.late_globals.items.len > coff.late_globals_pending_index) return true;5701 if (coff.exports_complete and coff.late_globals.items.len > coff.late_globals_pending_index) return true;
5702 if (coff.exports_complete and !coff.special_symbols_complete) return true;
5717 for (&coff.lazy.values) |lazy| if (lazy.map.count() > lazy.pending_index) return true;5703 for (&coff.lazy.values) |lazy| if (lazy.map.count() > lazy.pending_index) return true;
5718 if (coff.symbol_table.pending.count() > 0) return true;5704 if (coff.symbol_table.pending.count() > 0) return true;
5719 if (coff.input_sections.items.len > coff.input_section_pending_index) return true;5705 if (coff.input_sections.items.len > coff.input_section_pending_index) return true;
...@@ -6258,6 +6244,105 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {...@@ -6258,6 +6244,105 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
6258 return true;6244 return true;
6259}6245}
62606246
6247fn flushSpecialSymbols(coff: *Coff) !void {
6248 const comp = coff.base.comp;
6249 const gpa = comp.gpa;
6250 const machine = coff.targetLoad(&coff.headerPtr().machine);
6251
6252 if (coff.isImage()) {
6253 // TODO: Use explicitly specified entry if set, add err if not found
6254 const entries: []const struct { ?[]const u8, []const u8 } = if (coff.isExe())
6255 if (comp.config.link_libc) switch (coff.optionalHeaderField(.subsystem)) {
6256 .WINDOWS_CUI => &.{
6257 .{ "main", "mainCRTStartup" },
6258 .{ "wmain", "wmainCRTStartup" },
6259 },
6260 .WINDOWS_GUI => &.{
6261 .{ "WinMain", "WinMainCRTStartup" },
6262 .{ "wWinMain", "wWinMainCRTStartup" },
6263 },
6264 else => unreachable,
6265 } else &.{
6266 .{ "wWinMainCRTStartup", "wWinMainCRTStartup" },
6267 }
6268 else
6269 &.{.{ null, "_DllMainCRTStartup" }};
6270
6271 const entry_si = for (entries) |entry| {
6272 if (entry[0]) |required_name|
6273 if (coff.getDefinedGlobal(required_name) == .null) continue;
6274
6275 break try coff.globalSymbol(.{ .name = entry[1], .type = .code });
6276 } else .null;
6277
6278 if (entry_si != .null) {
6279 log.debug(
6280 "entry({s}, {d})",
6281 .{ entry_si.get(coff).gmi.globalName(coff).name.toSlice(coff), entry_si },
6282 );
6283
6284 try coff.symbols.ensureTotalCapacity(gpa, 1);
6285 const optional_hdr_si = coff.addSymbolAssumeCapacity();
6286 const optional_hdr_sym = optional_hdr_si.get(coff);
6287 optional_hdr_sym.ni = Node.known.optional_header;
6288 assert(optional_hdr_sym.loc_relocs == .none);
6289 optional_hdr_sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
6290
6291 const optional_hdr = coff.optionalHeaderStandardPtr();
6292 optional_hdr.address_of_entry_point = std.mem.nativeTo(
6293 u32,
6294 entry_si.get(coff).rva,
6295 coff.targetEndian(),
6296 );
6297
6298 try coff.addReloc(
6299 optional_hdr_si,
6300 @intFromPtr(&optional_hdr.address_of_entry_point) - @intFromPtr(optional_hdr),
6301 entry_si,
6302 .{ .known = 0 },
6303 switch (machine) {
6304 else => |tag| @panic(@tagName(tag)),
6305 .AMD64 => .{ .AMD64 = .ADDR32NB },
6306 .I386 => .{ .I386 = .DIR32NB },
6307 },
6308 );
6309 }
6310 }
6311
6312 if (coff.getDefinedGlobal("_tls_used").unwrap()) |tls_used_si| {
6313 const tls_directory = coff.dataDirectoryPtr(.TLS);
6314 tls_directory.* = .{
6315 .virtual_address = tls_used_si.get(coff).rva,
6316 .size = switch (coff.targetLoad(&coff.optionalHeaderStandardPtr().magic)) {
6317 _ => unreachable,
6318 .PE32 => 24,
6319 .@"PE32+" => 40,
6320 },
6321 };
6322 if (coff.targetEndian() != native_endian)
6323 std.mem.byteSwapAllFields(std.coff.ImageDataDirectory, tls_directory);
6324
6325 try coff.symbols.ensureTotalCapacity(gpa, 1);
6326 const data_dir_si = coff.addSymbolAssumeCapacity();
6327 const data_dir_sym = data_dir_si.get(coff);
6328 data_dir_sym.ni = Node.known.data_directories;
6329 assert(data_dir_sym.loc_relocs == .none);
6330 data_dir_sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
6331
6332 try coff.addReloc(
6333 data_dir_si,
6334 @intFromPtr(&tls_directory.virtual_address) - @intFromPtr(coff.dataDirectorySlice().ptr),
6335 tls_used_si,
6336 .{ .known = 0 },
6337 switch (machine) {
6338 else => |tag| @panic(@tagName(tag)),
6339 .AMD64 => .{ .AMD64 = .ADDR32NB },
6340 .I386 => .{ .I386 = .DIR32NB },
6341 },
6342 );
6343 }
6344}
6345
6261fn flushLazy(coff: *Coff, pt: Zcu.PerThread, lmr: Node.LazyMapRef) !void {6346fn flushLazy(coff: *Coff, pt: Zcu.PerThread, lmr: Node.LazyMapRef) !void {
6262 const zcu = pt.zcu;6347 const zcu = pt.zcu;
6263 const gpa = zcu.gpa;6348 const gpa = zcu.gpa;
...@@ -6312,7 +6397,7 @@ fn flushLazy(coff: *Coff, pt: Zcu.PerThread, lmr: Node.LazyMapRef) !void {...@@ -6312,7 +6397,7 @@ fn flushLazy(coff: *Coff, pt: Zcu.PerThread, lmr: Node.LazyMapRef) !void {
6312}6397}
63136398
6314fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void {6399fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void {
6315 log.debug("flushMoved({s})", .{@tagName(coff.getNode(ni))});6400 log.debug("flushMoved({s}, n{d})", .{ @tagName(coff.getNode(ni)), ni });
6316 switch (coff.getNode(ni)) {6401 switch (coff.getNode(ni)) {
6317 .file,6402 .file,
6318 .header,6403 .header,
...@@ -6500,7 +6585,7 @@ fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void {...@@ -6500,7 +6585,7 @@ fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void {
65006585
6501fn flushResized(coff: *Coff, ni: MappedFile.Node.Index) !void {6586fn flushResized(coff: *Coff, ni: MappedFile.Node.Index) !void {
6502 const offset, const size = ni.location(&coff.mf).resolve(&coff.mf);6587 const offset, const size = ni.location(&coff.mf).resolve(&coff.mf);
6503 log.debug("flushResized({s}, 0x{x})", .{ @tagName(coff.getNode(ni)), size });6588 log.debug("flushResized({s}, n{d}, 0x{x})", .{ @tagName(coff.getNode(ni)), ni, size });
65046589
6505 switch (coff.getNode(ni)) {6590 switch (coff.getNode(ni)) {
6506 .file => {6591 .file => {
...@@ -6779,6 +6864,7 @@ fn updateExportsInner(...@@ -6779,6 +6864,7 @@ fn updateExportsInner(
6779 };6864 };
6780 while (try coff.idle(pt.tid)) {}6865 while (try coff.idle(pt.tid)) {}
67816866
6867 const machine = coff.targetLoad(&coff.headerPtr().machine);
6782 const exported_ni = exported_si.node(coff);6868 const exported_ni = exported_si.node(coff);
6783 const exported_sym = exported_si.get(coff);6869 const exported_sym = exported_si.get(coff);
6784 for (export_indices) |export_index| {6870 for (export_indices) |export_index| {
...@@ -6795,18 +6881,7 @@ fn updateExportsInner(...@@ -6795,18 +6881,7 @@ fn updateExportsInner(
6795 export_sym.section_number = exported_sym.section_number;6881 export_sym.section_number = exported_sym.section_number;
6796 defer export_si.applyTargetRelocs(coff, .none);6882 defer export_si.applyTargetRelocs(coff, .none);
67976883
6798 if (coff.isImage()) {6884 if (!coff.isImage()) continue;
6799 if (@"export".opts.name.eqlSlice("_tls_used", ip)) {
6800 const tls_directory = coff.dataDirectoryPtr(.TLS);
6801 tls_directory.* = .{ .virtual_address = exported_sym.rva, .size = exported_sym.value.size };
6802 if (coff.targetEndian() != native_endian)
6803 std.mem.byteSwapAllFields(std.coff.ImageDataDirectory, tls_directory);
6804 } else if ((coff.isExe() and @"export".opts.name.eqlSlice("wWinMainCRTStartup", ip)) or
6805 (!coff.isExe() and @"export".opts.name.eqlSlice("_DllMainCRTStartup", ip)))
6806 {
6807 coff.updateEntry(export_sym.gmi);
6808 }
6809 } else continue;
68106885
6811 const entries_ctx = ExportTable.Adapter{ .coff = coff };6886 const entries_ctx = ExportTable.Adapter{ .coff = coff };
6812 const gop = try coff.export_table.entries.getOrPutAdapted(6887 const gop = try coff.export_table.entries.getOrPutAdapted(
...@@ -6888,7 +6963,11 @@ fn updateExportsInner(...@@ -6888,7 +6963,11 @@ fn updateExportsInner(
6888 @intCast(@sizeOf(std.coff.ExportAddressTableEntry) * gop.index),6963 @intCast(@sizeOf(std.coff.ExportAddressTableEntry) * gop.index),
6889 export_si,6964 export_si,
6890 .{ .known = 0 },6965 .{ .known = 0 },
6891 .{ .AMD64 = .ADDR32NB },6966 switch (machine) {
6967 else => |tag| @panic(@tagName(tag)),
6968 .AMD64 => .{ .AMD64 = .ADDR32NB },
6969 .I386 => .{ .I386 = .DIR32NB },
6970 },
6892 );6971 );
6893 } else {6972 } else {
6894 gop.value_ptr.si = export_si;6973 gop.value_ptr.si = export_si;
...@@ -6898,20 +6977,6 @@ fn updateExportsInner(...@@ -6898,20 +6977,6 @@ fn updateExportsInner(
6898 }6977 }
6899}6978}
69006979
6901/// Caller ensures that `applyTargetRelocs` will be called on `si` eventually
6902fn updateEntry(coff: *Coff, gmi: Node.GlobalMapIndex) void {
6903 const si = gmi.symbol(coff);
6904 log.debug("updateEntry({s}, {d})", .{ gmi.globalName(coff).name.toSlice(coff), si });
6905
6906 if (coff.entry != .none)
6907 coff.entry.symbol(coff).get(coff).flags.is_entry = false;
6908
6909 // TODO: Should we detect the subsystem like link.exe does (if not explicitly set) based on entry name?
6910
6911 coff.entry = gmi;
6912 si.get(coff).flags.is_entry = true;
6913}
6914
6915pub fn deleteExport(coff: *Coff, exported: Zcu.Exported, name: InternPool.NullTerminatedString) void {6980pub fn deleteExport(coff: *Coff, exported: Zcu.Exported, name: InternPool.NullTerminatedString) void {
6916 _ = coff;6981 _ = coff;
6917 _ = exported;6982 _ = exported;
...@@ -6928,11 +6993,15 @@ fn dumpStderr(coff: *Coff, tid: Zcu.PerThread.Id) !void {...@@ -6928,11 +6993,15 @@ fn dumpStderr(coff: *Coff, tid: Zcu.PerThread.Id) !void {
6928 const stderr = try io.lockStderr(&buffer, null);6993 const stderr = try io.lockStderr(&buffer, null);
6929 defer io.unlockStderr();6994 defer io.unlockStderr();
6930 const w = &stderr.file_writer.interface;6995 const w = &stderr.file_writer.interface;
6931 try coff.dump(w, tid);6996 _ = try coff.dump(w, tid);
6932}6997}
69336998
6934pub fn dump(coff: *Coff, w: *Io.Writer, tid: Zcu.PerThread.Id) !void {6999pub fn dump(coff: *Coff, w: *Io.Writer, tid: Zcu.PerThread.Id) !link.File.DumpResult {
6935 try coff.printNode(tid, w, .root, 0);7000 if (coff.dump_snapshot) {
7001 try coff.printNode(tid, w, .root, 0);
7002 return .enabled;
7003 }
7004 return .disabled;
6936}7005}
69377006
6938pub fn printNode(7007pub fn printNode(
src/link/Elf2.zig+8-2
...@@ -161,6 +161,7 @@ textrel_count: u32,...@@ -161,6 +161,7 @@ textrel_count: u32,
161const_prog_node: std.Progress.Node,161const_prog_node: std.Progress.Node,
162synth_prog_node: std.Progress.Node,162synth_prog_node: std.Progress.Node,
163input_prog_node: std.Progress.Node,163input_prog_node: std.Progress.Node,
164dump_snapshot: bool,
164165
165const Error = link.Error || error{MappedFileIo};166const Error = link.Error || error{MappedFileIo};
166167
...@@ -2624,6 +2625,7 @@ fn create(...@@ -2624,6 +2625,7 @@ fn create(
2624 .synth_prog_node = .none,2625 .synth_prog_node = .none,
2625 .input_prog_node = .none,2626 .input_prog_node = .none,
2626 .textrel_count = 0,2627 .textrel_count = 0,
2628 .dump_snapshot = options.enable_link_snapshots,
2627 };2629 };
2628 errdefer elf.deinit();2630 errdefer elf.deinit();
26292631
...@@ -6711,8 +6713,12 @@ pub fn deleteExport(elf: *Elf, exported: Zcu.Exported, name: InternPool.NullTerm...@@ -6711,8 +6713,12 @@ pub fn deleteExport(elf: *Elf, exported: Zcu.Exported, name: InternPool.NullTerm
6711 _ = name;6713 _ = name;
6712}6714}
67136715
6714pub fn dump(elf: *Elf, w: *Io.Writer, tid: Zcu.PerThread.Id) !void {6716pub fn dump(elf: *Elf, w: *Io.Writer, tid: Zcu.PerThread.Id) !link.File.DumpResult {
6715 return elf.printNode(tid, w, .root, 0);6717 if (elf.dump_snapshot) {
6718 try elf.printNode(tid, w, .root, 0);
6719 return .enabled;
6720 }
6721 return .disabled;
6716}6722}
67176723
6718pub fn printNode(6724pub fn printNode(