| ... | ... | @@ -49,7 +49,7 @@ input_sections: std.ArrayList(Node.InputSection), |
| 49 | 49 | input_section_pending_index: u32, |
| 50 | 50 | inputs_complete: bool, |
| 51 | 51 | exports_complete: bool, |
| 52 | | special_symbols_complete: bool, |
| 52 | pending_special_symbol: SpecialSymbol, |
| 53 | 53 | strings: std.HashMapUnmanaged( |
| 54 | 54 | u32, |
| 55 | 55 | void, |
| ... | ... | @@ -790,6 +790,7 @@ pub const ImportTable = struct { |
| 790 | 790 | }; |
| 791 | 791 | |
| 792 | 792 | pub const String = enum(u32) { |
| 793 | // TODO: Re-order |
| 793 | 794 | @".data" = 0, |
| 794 | 795 | @".idata" = 6, |
| 795 | 796 | @".rdata" = 13, |
| ... | ... | @@ -802,6 +803,7 @@ pub const String = enum(u32) { |
| 802 | 803 | @".dtors$ZZZ" = 64, |
| 803 | 804 | @".bss" = 75, |
| 804 | 805 | @".fptable" = 80, |
| 806 | @".tls" = 89, |
| 805 | 807 | _, |
| 806 | 808 | |
| 807 | 809 | pub const Optional = enum(u32) { |
| ... | ... | @@ -817,6 +819,7 @@ pub const String = enum(u32) { |
| 817 | 819 | @".dtors$ZZZ" = @intFromEnum(String.@".dtors$ZZZ"), |
| 818 | 820 | @".bss" = @intFromEnum(String.@".bss"), |
| 819 | 821 | @".fptable" = @intFromEnum(String.@".fptable"), |
| 822 | @".tls" = @intFromEnum(String.@".tls"), |
| 820 | 823 | none = std.math.maxInt(u32), |
| 821 | 824 | _, |
| 822 | 825 | |
| ... | ... | @@ -892,6 +895,12 @@ pub const WeakExternalStrat = enum(u2) { |
| 892 | 895 | } |
| 893 | 896 | }; |
| 894 | 897 | |
| 898 | const SpecialSymbol = enum { |
| 899 | entry, |
| 900 | tls, |
| 901 | none, |
| 902 | }; |
| 903 | |
| 895 | 904 | pub const Symbol = struct { |
| 896 | 905 | ni: MappedFile.Node.Index, |
| 897 | 906 | rva: u32, |
| ... | ... | @@ -1538,7 +1547,7 @@ fn create( |
| 1538 | 1547 | .input_section_pending_index = 0, |
| 1539 | 1548 | .inputs_complete = false, |
| 1540 | 1549 | .exports_complete = false, |
| 1541 | | .special_symbols_complete = false, |
| 1550 | .pending_special_symbol = .entry, |
| 1542 | 1551 | .strings = .empty, |
| 1543 | 1552 | .string_bytes = .empty, |
| 1544 | 1553 | .section_table = .empty, |
| ... | ... | @@ -1718,7 +1727,7 @@ fn initHeaders( |
| 1718 | 1727 | // TLS section |
| 1719 | 1728 | if (comp.config.any_non_single_threaded) { |
| 1720 | 1729 | if (!is_image) expected_nodes_len += 1; |
| 1721 | | expected_nodes_len += 2; |
| 1730 | expected_nodes_len += 1; |
| 1722 | 1731 | } |
| 1723 | 1732 | } |
| 1724 | 1733 | defer assert(coff.nodes.len == expected_nodes_len); |
| ... | ... | @@ -2130,10 +2139,11 @@ fn initHeaders( |
| 2130 | 2139 | }); |
| 2131 | 2140 | |
| 2132 | 2141 | // While tls variables allocated at runtime are writable, the template itself is not. |
| 2133 | | // In images, this call triggers the creation of a .tls pseudo section in .rdata. |
| 2134 | | // In objects / archives, this section is part of the above .tls$ section. |
| 2135 | | _ = try coff.objectSectionMapIndex( |
| 2136 | | .@".tls$", |
| 2142 | // In images, the template is in a .tls pseudo section in .rdata. |
| 2143 | // In objects / archives, this section is part of the above .tls$ section. The suffix |
| 2144 | // is maintained so merging can occur with other input tls symbols when linked later. |
| 2145 | _ = try coff.pseudoSectionMapIndex( |
| 2146 | if (is_image) .@".tls" else .@".tls$", |
| 2137 | 2147 | coff.mf.flags.block_size, |
| 2138 | 2148 | .{ .read = true, .write = !is_image, .initialized = true }, |
| 2139 | 2149 | ); |
| ... | ... | @@ -5622,15 +5632,15 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool { |
| 5622 | 5632 | }) coff.late_globals_pending_index += 1; |
| 5623 | 5633 | break :task; |
| 5624 | 5634 | } |
| 5625 | | if (coff.exports_complete and !coff.special_symbols_complete) { |
| 5626 | | coff.special_symbols_complete = true; |
| 5627 | | coff.flushSpecialSymbols() catch |err| switch (err) { |
| 5628 | | error.OutOfMemory => |e| return e, |
| 5629 | | else => |e| return comp.link_diags.fail( |
| 5630 | | "linker failed to flush special symbols: {t}", |
| 5631 | | .{e}, |
| 5632 | | ), |
| 5633 | | }; |
| 5635 | if (coff.exports_complete and coff.pending_special_symbol != .none) { |
| 5636 | coff.pending_special_symbol = coff.flushSpecialSymbol(coff.pending_special_symbol) catch |err| |
| 5637 | switch (err) { |
| 5638 | error.OutOfMemory => |e| return e, |
| 5639 | else => |e| return comp.link_diags.fail( |
| 5640 | "linker failed to flush special symbols: {t}", |
| 5641 | .{e}, |
| 5642 | ), |
| 5643 | }; |
| 5634 | 5644 | break :task; |
| 5635 | 5645 | } |
| 5636 | 5646 | var lazy_it = coff.lazy.iterator(); |
| ... | ... | @@ -5773,7 +5783,7 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool { |
| 5773 | 5783 | if (coff.inputs_complete and coff.globals.count() > coff.global_pending_index) return true; |
| 5774 | 5784 | assert(!coff.exports_complete or coff.inputs_complete); |
| 5775 | 5785 | if (coff.exports_complete and coff.late_globals.items.len > coff.late_globals_pending_index) return true; |
| 5776 | | if (coff.exports_complete and !coff.special_symbols_complete) return true; |
| 5786 | if (coff.exports_complete and coff.pending_special_symbol != .none) return true; |
| 5777 | 5787 | for (&coff.lazy.values) |lazy| if (lazy.map.count() > lazy.pending_index) return true; |
| 5778 | 5788 | if (coff.symbol_table.pending.count() > 0) return true; |
| 5779 | 5789 | if (coff.input_sections.items.len > coff.input_section_pending_index) return true; |
| ... | ... | @@ -6318,103 +6328,116 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool { |
| 6318 | 6328 | return true; |
| 6319 | 6329 | } |
| 6320 | 6330 | |
| 6321 | | fn flushSpecialSymbols(coff: *Coff) !void { |
| 6331 | fn flushSpecialSymbol(coff: *Coff, pending: SpecialSymbol) !SpecialSymbol { |
| 6322 | 6332 | const comp = coff.base.comp; |
| 6323 | 6333 | const gpa = comp.gpa; |
| 6324 | 6334 | const machine = coff.targetLoad(&coff.headerPtr().machine); |
| 6325 | 6335 | |
| 6326 | | if (coff.isImage()) { |
| 6327 | | // TODO: Use explicitly specified entry if set, add err if not found |
| 6328 | | const entries: []const struct { ?[]const u8, []const u8 } = if (coff.isExe()) |
| 6329 | | if (comp.config.link_libc) switch (coff.optionalHeaderField(.subsystem)) { |
| 6330 | | .WINDOWS_CUI => &.{ |
| 6331 | | .{ "main", "mainCRTStartup" }, |
| 6332 | | .{ "wmain", "wmainCRTStartup" }, |
| 6333 | | }, |
| 6334 | | .WINDOWS_GUI => &.{ |
| 6335 | | .{ "WinMain", "WinMainCRTStartup" }, |
| 6336 | | .{ "wWinMain", "wWinMainCRTStartup" }, |
| 6337 | | }, |
| 6338 | | else => unreachable, |
| 6339 | | } else &.{ |
| 6340 | | .{ "wWinMainCRTStartup", "wWinMainCRTStartup" }, |
| 6341 | | } |
| 6342 | | else |
| 6343 | | &.{.{ null, "_DllMainCRTStartup" }}; |
| 6336 | if (!coff.isImage()) return .none; |
| 6337 | return next: switch (pending) { |
| 6338 | .entry => { |
| 6339 | // TODO: Use explicitly specified entry if set, add err if not found |
| 6340 | const entries: []const struct { ?[]const u8, []const u8 } = if (coff.isExe()) |
| 6341 | if (comp.config.link_libc) switch (coff.optionalHeaderField(.subsystem)) { |
| 6342 | .WINDOWS_CUI => &.{ |
| 6343 | .{ "main", "mainCRTStartup" }, |
| 6344 | .{ "wmain", "wmainCRTStartup" }, |
| 6345 | }, |
| 6346 | .WINDOWS_GUI => &.{ |
| 6347 | .{ "WinMain", "WinMainCRTStartup" }, |
| 6348 | .{ "wWinMain", "wWinMainCRTStartup" }, |
| 6349 | }, |
| 6350 | else => unreachable, |
| 6351 | } else &.{ |
| 6352 | .{ "wWinMainCRTStartup", "wWinMainCRTStartup" }, |
| 6353 | } |
| 6354 | else |
| 6355 | &.{.{ null, "_DllMainCRTStartup" }}; |
| 6344 | 6356 | |
| 6345 | | const entry_si = for (entries) |entry| { |
| 6346 | | if (entry[0]) |required_name| |
| 6347 | | if (coff.getDefinedGlobal(required_name) == .null) continue; |
| 6357 | const entry_si = for (entries) |entry| { |
| 6358 | if (entry[0]) |required_name| |
| 6359 | if (coff.getDefinedGlobal(required_name) == .null) continue; |
| 6348 | 6360 | |
| 6349 | | break try coff.globalSymbol(.{ .name = entry[1], .type = .code }); |
| 6350 | | } else .null; |
| 6361 | break try coff.globalSymbol(.{ .name = entry[1], .type = .code }); |
| 6362 | } else .null; |
| 6351 | 6363 | |
| 6352 | | if (entry_si != .null) { |
| 6353 | | log.debug( |
| 6354 | | "entry({s}, {d})", |
| 6355 | | .{ entry_si.get(coff).gmi.globalName(coff).name.toSlice(coff), entry_si }, |
| 6356 | | ); |
| 6364 | if (entry_si != .null) { |
| 6365 | log.debug( |
| 6366 | "entry({s}, {d})", |
| 6367 | .{ entry_si.get(coff).gmi.globalName(coff).name.toSlice(coff), entry_si }, |
| 6368 | ); |
| 6357 | 6369 | |
| 6358 | | try coff.symbols.ensureTotalCapacity(gpa, 1); |
| 6359 | | const optional_hdr_si = coff.addSymbolAssumeCapacity(); |
| 6360 | | const optional_hdr_sym = optional_hdr_si.get(coff); |
| 6361 | | optional_hdr_sym.ni = Node.known.optional_header; |
| 6362 | | assert(optional_hdr_sym.loc_relocs == .none); |
| 6363 | | optional_hdr_sym.loc_relocs = @enumFromInt(coff.relocs.items.len); |
| 6364 | | |
| 6365 | | const optional_hdr = coff.optionalHeaderStandardPtr(); |
| 6366 | | optional_hdr.address_of_entry_point = std.mem.nativeTo( |
| 6367 | | u32, |
| 6368 | | entry_si.get(coff).rva, |
| 6369 | | coff.targetEndian(), |
| 6370 | | ); |
| 6370 | try coff.symbols.ensureUnusedCapacity(gpa, 1); |
| 6371 | const optional_hdr_si = coff.addSymbolAssumeCapacity(); |
| 6372 | const optional_hdr_sym = optional_hdr_si.get(coff); |
| 6373 | optional_hdr_sym.ni = Node.known.optional_header; |
| 6374 | assert(optional_hdr_sym.loc_relocs == .none); |
| 6375 | optional_hdr_sym.loc_relocs = @enumFromInt(coff.relocs.items.len); |
| 6371 | 6376 | |
| 6372 | | try coff.addReloc( |
| 6373 | | optional_hdr_si, |
| 6374 | | @intFromPtr(&optional_hdr.address_of_entry_point) - @intFromPtr(optional_hdr), |
| 6375 | | entry_si, |
| 6376 | | .{ .known = 0 }, |
| 6377 | | switch (machine) { |
| 6378 | | else => |tag| @panic(@tagName(tag)), |
| 6379 | | .AMD64 => .{ .AMD64 = .ADDR32NB }, |
| 6380 | | .I386 => .{ .I386 = .DIR32NB }, |
| 6381 | | }, |
| 6382 | | ); |
| 6383 | | } |
| 6384 | | } |
| 6377 | const optional_hdr = coff.optionalHeaderStandardPtr(); |
| 6378 | optional_hdr.address_of_entry_point = std.mem.nativeTo( |
| 6379 | u32, |
| 6380 | entry_si.get(coff).rva, |
| 6381 | coff.targetEndian(), |
| 6382 | ); |
| 6385 | 6383 | |
| 6386 | | if (coff.getDefinedGlobal("_tls_used").unwrap()) |tls_used_si| { |
| 6387 | | const tls_directory = coff.dataDirectoryPtr(.TLS); |
| 6388 | | tls_directory.* = .{ |
| 6389 | | .virtual_address = tls_used_si.get(coff).rva, |
| 6390 | | .size = switch (coff.targetLoad(&coff.optionalHeaderStandardPtr().magic)) { |
| 6391 | | _ => unreachable, |
| 6392 | | .PE32 => 24, |
| 6393 | | .@"PE32+" => 40, |
| 6394 | | }, |
| 6395 | | }; |
| 6396 | | if (coff.targetEndian() != native_endian) |
| 6397 | | std.mem.byteSwapAllFields(std.coff.ImageDataDirectory, tls_directory); |
| 6398 | | |
| 6399 | | try coff.symbols.ensureTotalCapacity(gpa, 1); |
| 6400 | | const data_dir_si = coff.addSymbolAssumeCapacity(); |
| 6401 | | const data_dir_sym = data_dir_si.get(coff); |
| 6402 | | data_dir_sym.ni = Node.known.data_directories; |
| 6403 | | assert(data_dir_sym.loc_relocs == .none); |
| 6404 | | data_dir_sym.loc_relocs = @enumFromInt(coff.relocs.items.len); |
| 6405 | | |
| 6406 | | try coff.addReloc( |
| 6407 | | data_dir_si, |
| 6408 | | @intFromPtr(&tls_directory.virtual_address) - @intFromPtr(coff.dataDirectorySlice().ptr), |
| 6409 | | tls_used_si, |
| 6410 | | .{ .known = 0 }, |
| 6411 | | switch (machine) { |
| 6412 | | else => |tag| @panic(@tagName(tag)), |
| 6413 | | .AMD64 => .{ .AMD64 = .ADDR32NB }, |
| 6414 | | .I386 => .{ .I386 = .DIR32NB }, |
| 6415 | | }, |
| 6416 | | ); |
| 6417 | | } |
| 6384 | try coff.addReloc( |
| 6385 | optional_hdr_si, |
| 6386 | @intFromPtr(&optional_hdr.address_of_entry_point) - @intFromPtr(optional_hdr), |
| 6387 | entry_si, |
| 6388 | .{ .known = 0 }, |
| 6389 | switch (machine) { |
| 6390 | else => |tag| @panic(@tagName(tag)), |
| 6391 | .AMD64 => .{ .AMD64 = .ADDR32NB }, |
| 6392 | .I386 => .{ .I386 = .DIR32NB }, |
| 6393 | }, |
| 6394 | ); |
| 6395 | } |
| 6396 | |
| 6397 | // Referencing the startup functions may trigger loading the object containing them, |
| 6398 | // we need to wait until that is done before looking for further symbols. |
| 6399 | break :next .tls; |
| 6400 | }, |
| 6401 | .tls => { |
| 6402 | if (coff.getDefinedGlobal("_tls_used").unwrap()) |tls_used_si| { |
| 6403 | log.debug("tlsDir({d})", .{tls_used_si}); |
| 6404 | |
| 6405 | const tls_directory = coff.dataDirectoryPtr(.TLS); |
| 6406 | tls_directory.* = .{ |
| 6407 | .virtual_address = tls_used_si.get(coff).rva, |
| 6408 | .size = switch (coff.targetLoad(&coff.optionalHeaderStandardPtr().magic)) { |
| 6409 | _ => unreachable, |
| 6410 | .PE32 => 24, |
| 6411 | .@"PE32+" => 40, |
| 6412 | }, |
| 6413 | }; |
| 6414 | if (coff.targetEndian() != native_endian) |
| 6415 | std.mem.byteSwapAllFields(std.coff.ImageDataDirectory, tls_directory); |
| 6416 | |
| 6417 | try coff.symbols.ensureUnusedCapacity(gpa, 1); |
| 6418 | const data_dir_si = coff.addSymbolAssumeCapacity(); |
| 6419 | const data_dir_sym = data_dir_si.get(coff); |
| 6420 | data_dir_sym.ni = Node.known.data_directories; |
| 6421 | assert(data_dir_sym.loc_relocs == .none); |
| 6422 | data_dir_sym.loc_relocs = @enumFromInt(coff.relocs.items.len); |
| 6423 | |
| 6424 | try coff.addReloc( |
| 6425 | data_dir_si, |
| 6426 | @intFromPtr(&tls_directory.virtual_address) - @intFromPtr(coff.dataDirectorySlice().ptr), |
| 6427 | tls_used_si, |
| 6428 | .{ .known = 0 }, |
| 6429 | switch (machine) { |
| 6430 | else => |tag| @panic(@tagName(tag)), |
| 6431 | .AMD64 => .{ .AMD64 = .ADDR32NB }, |
| 6432 | .I386 => .{ .I386 = .DIR32NB }, |
| 6433 | }, |
| 6434 | ); |
| 6435 | } |
| 6436 | |
| 6437 | break :next .none; |
| 6438 | }, |
| 6439 | .none => unreachable, |
| 6440 | }; |
| 6418 | 6441 | } |
| 6419 | 6442 | |
| 6420 | 6443 | fn flushLazy(coff: *Coff, pt: Zcu.PerThread, lmr: Node.LazyMapRef) !void { |
| ... | ... | @@ -7137,11 +7160,68 @@ fn dumpStderr(coff: *Coff, tid: Zcu.PerThread.Id) !void { |
| 7137 | 7160 | pub fn dump(coff: *Coff, w: *Io.Writer, tid: Zcu.PerThread.Id) !link.File.DumpResult { |
| 7138 | 7161 | if (coff.dump_snapshot) { |
| 7139 | 7162 | try coff.printNode(tid, w, .root, 0); |
| 7163 | try w.writeAll("Section table:\n"); |
| 7164 | for (coff.section_table.keys(), coff.section_table.values()) |name, sec| |
| 7165 | try coff.printSection(w, name, sec.si); |
| 7166 | try w.writeAll("Symbol table:\n"); |
| 7167 | for (1..coff.symbols.items.len) |si| |
| 7168 | try coff.printSymbol(w, @enumFromInt(si)); |
| 7169 | |
| 7140 | 7170 | return .enabled; |
| 7141 | 7171 | } |
| 7142 | 7172 | return .disabled; |
| 7143 | 7173 | } |
| 7144 | 7174 | |
| 7175 | fn printSection(coff: *Coff, w: *Io.Writer, name: String, si: Symbol.Index) !void { |
| 7176 | const sym = si.get(coff); |
| 7177 | try w.print("{d:0>6}@{d:0>2} {x:08} n{d:0>8} | {s}\n", .{ |
| 7178 | si, |
| 7179 | sym.section_number, |
| 7180 | if (sym.flags.value_tag == .size) sym.value.size else 0, |
| 7181 | sym.ni, |
| 7182 | name.toSlice(coff), |
| 7183 | }); |
| 7184 | } |
| 7185 | |
| 7186 | fn printSymbol(coff: *Coff, w: *Io.Writer, si: Symbol.Index) !void { |
| 7187 | const sym = si.get(coff); |
| 7188 | const node = coff.getNode(sym.ni); |
| 7189 | try w.print("{d:0>6}@{d:0>2} {x:08} {s} n{d:0>8}+{x:08}:{t: <26} | {x:08} | {f}\n", .{ |
| 7190 | si, |
| 7191 | sym.section_number, |
| 7192 | if (sym.flags.value_tag == .size) |
| 7193 | @as(u64, sym.value.size) |
| 7194 | else if (sym.ni != .none) |
| 7195 | sym.ni.location(&coff.mf).resolve(&coff.mf)[1] |
| 7196 | else |
| 7197 | 0, |
| 7198 | switch (sym.flags.type) { |
| 7199 | .unknown => "u", |
| 7200 | .code => "c", |
| 7201 | .data => "d", |
| 7202 | }, |
| 7203 | sym.ni, |
| 7204 | if (sym.flags.value_tag == .node_offset) sym.value.node_offset else 0, |
| 7205 | node, |
| 7206 | sym.rva, |
| 7207 | fmtGlobalName(coff, sym.gmi), |
| 7208 | }); |
| 7209 | } |
| 7210 | |
| 7211 | const FmtGlobalName = struct { coff: *Coff, gmi: Node.GlobalMapIndex }; |
| 7212 | |
| 7213 | fn fmtGlobalName(coff: *Coff, gmi: Node.GlobalMapIndex) std.fmt.Alt(FmtGlobalName, globalNameEscape) { |
| 7214 | return .{ .data = .{ .coff = coff, .gmi = gmi } }; |
| 7215 | } |
| 7216 | |
| 7217 | fn globalNameEscape(data: FmtGlobalName, w: *std.Io.Writer) std.Io.Writer.Error!void { |
| 7218 | if (data.gmi == .none) return; |
| 7219 | const gn = data.gmi.globalName(data.coff); |
| 7220 | try w.writeAll(gn.name.toSlice(data.coff)); |
| 7221 | if (gn.lib_name.unwrap()) |lib_name| |
| 7222 | try w.print("({s})", .{lib_name.toSlice(data.coff)}); |
| 7223 | } |
| 7224 | |
| 7145 | 7225 | pub fn printNode( |
| 7146 | 7226 | coff: *Coff, |
| 7147 | 7227 | tid: Zcu.PerThread.Id, |