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:22:42-04:00
loga22ca5d4169406576776436a9bec5c500bc3f2d4
treef418a72a9eb7bc3c3c2f1e759069da467802fd16
parent652902ac7f424a719abf89a6532e207d03368728

Coff: more progress on imports

- Support __imp_ prefixed symbols - Support data imports - Support globals symbols pointing directly to IAT entries - Don't create duplicate IAT entries if multiple globals reference the same import name / ordinal - Rework storage of Symbol.value - Add support for `is_dll_import` - Supply host libc libs to the linker - Add std.meta.BareUnion (from multi_array_list)

4 files changed, 453 insertions(+), 253 deletions(-)

lib/std/meta.zig+9
......@@ -499,6 +499,15 @@ test DeclEnum {
499499 try expectEqualEnum(enum {}, DeclEnum(D));
500500}
501501
502pub fn BareUnion(comptime T: type) type {
503 const u = switch (@typeInfo(T)) {
504 .@"union" => |u| u,
505 else => @compileError("expected union type, found '" ++ @typeName(T) ++ "'"),
506 };
507
508 return @Union(u.layout, null, u.field_names, u.field_types[0..], u.field_attrs[0..]);
509}
510
502511pub fn Tag(comptime T: type) type {
503512 return switch (@typeInfo(T)) {
504513 .@"enum" => |info| info.tag_type,
lib/std/multi_array_list.zig+1-1
......@@ -44,7 +44,7 @@ pub fn MultiArrayList(comptime T: type) type {
4444 const Elem = switch (@typeInfo(T)) {
4545 .@"struct" => T,
4646 .@"union" => |u| struct {
47 pub const Bare = @Union(u.layout, null, u.field_names, u.field_types[0..], u.field_attrs[0..]);
47 pub const Bare = std.meta.BareUnion(T);
4848 pub const Tag =
4949 u.tag_type orelse @compileError("MultiArrayList does not support untagged unions");
5050 tags: Tag,
src/link.zig+59-1
......@@ -1473,7 +1473,8 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void {
14731473
14741474 const target = &comp.root_mod.resolved_target.result;
14751475 const flags = target_util.libcFullLinkFlags(target);
1476 const crt_dir = comp.libc_installation.?.crt_dir.?;
1476 const libc_installation = comp.libc_installation.?;
1477 const crt_dir = libc_installation.crt_dir.?;
14771478 const sep = std.fs.path.sep_str;
14781479 for (flags) |flag| {
14791480 assert(mem.startsWith(u8, flag, "-l"));
......@@ -1525,6 +1526,63 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void {
15251526 },
15261527 }
15271528 }
1529
1530 if (target.os.tag == .windows) {
1531 const inputs: []const struct {
1532 dir: enum { crt, msvc_lib, kernel32_lib },
1533 name: []const u8,
1534 } = if (target.abi.isGnu()) switch (comp.config.link_mode) {
1535 .dynamic => &.{
1536 .{ .dir = .crt, .name = "dllcrt2.obj" },
1537 .{ .dir = .crt, .name = "libmingw32.lib" },
1538 },
1539 .static => &.{
1540 .{ .dir = .crt, .name = "crt2.obj" },
1541 .{ .dir = .crt, .name = "libmingw32.lib" },
1542 },
1543 } else switch (comp.config.link_mode) {
1544 .dynamic => &.{
1545 .{ .dir = .msvc_lib, .name = "msvcrt.lib" },
1546 .{ .dir = .msvc_lib, .name = "vcruntime.lib" },
1547 .{ .dir = .msvc_lib, .name = "legacy_stdio_definitions.lib" },
1548 .{ .dir = .crt, .name = "ucrt.lib" },
1549 .{ .dir = .kernel32_lib, .name = "kernel32.lib" },
1550 .{ .dir = .kernel32_lib, .name = "ntdll.lib" },
1551 },
1552 .static => &.{
1553 .{ .dir = .msvc_lib, .name = "libcmt.lib" },
1554 .{ .dir = .msvc_lib, .name = "libvcruntime.lib" },
1555 .{ .dir = .msvc_lib, .name = "legacy_stdio_definitions.lib" },
1556 .{ .dir = .crt, .name = "libucrt.lib" },
1557 .{ .dir = .kernel32_lib, .name = "kernel32.lib" },
1558 .{ .dir = .kernel32_lib, .name = "ntdll.lib" },
1559 },
1560 };
1561
1562 for (inputs) |lib| {
1563 const path = Path.initCwd(
1564 std.fmt.allocPrint(comp.arena, "{s}" ++ sep ++ "{s}", .{
1565 switch (lib.dir) {
1566 .crt => crt_dir,
1567 .msvc_lib => libc_installation.msvc_lib_dir.?,
1568 .kernel32_lib => libc_installation.kernel32_lib_dir.?,
1569 },
1570 lib.name,
1571 }) catch return diags.setAllocFailure(),
1572 );
1573 if (std.mem.endsWith(u8, lib.name, "lib")) {
1574 base.openLoadArchive(path, null) catch |err| switch (err) {
1575 error.LinkFailure => return, // error reported via diags
1576 else => |e| diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(e)}),
1577 };
1578 } else {
1579 base.openLoadObject(path) catch |err| switch (err) {
1580 error.LinkFailure => return, // error reported via diags
1581 else => |e| diags.addParseError(path, "failed to parse object: {s}", .{@errorName(e)}),
1582 };
1583 }
1584 }
1585 }
15281586 },
15291587 .load_object => |path| {
15301588 const prog_node = comp.link_prog_node.start("Parse Object", 0);
src/link/Coff.zig+384-251
......@@ -84,6 +84,8 @@ pub const default_size_of_heap_commit: u32 = 0x1000;
8484pub const archive_signature = "!<arch>\n";
8585pub const archive_end_of_header = "`\n";
8686
87pub const imp_prefix = "__imp_";
88
8789/// This is the start of a Portable Executable (PE) file.
8890/// It starts with a MS-DOS header followed by a MS-DOS stub program.
8991/// This data does not change so we include it as follows in all binaries.
......@@ -203,7 +205,7 @@ pub const Node = union(enum) {
203205 pseudo_section: PseudoSectionMapIndex,
204206 object_section: ObjectSectionMapIndex,
205207 input_section: InputSection.Index,
206 global: GlobalMapIndex,
208 import_thunk: GlobalMapIndex, // TODO: Rename to import_thunk
207209 nav: NavMapIndex,
208210 uav: UavMapIndex,
209211 lazy_code: LazyMapRef.Index(.code),
......@@ -255,10 +257,6 @@ pub const Node = union(enum) {
255257 return coff.globals.keys()[gmi.unwrap().?];
256258 }
257259
258 pub fn globalNameMutable(gmi: GlobalMapIndex, coff: *Coff) *GlobalName {
259 return &coff.globals.keys()[gmi.unwrap().?];
260 }
261
262260 pub fn symbol(gmi: GlobalMapIndex, coff: *const Coff) Symbol.Index {
263261 return coff.globals.values()[gmi.unwrap().?];
264262 }
......@@ -707,6 +705,12 @@ pub const ExportTable = struct {
707705pub const ImportTable = struct {
708706 ni: MappedFile.Node.Index,
709707 entries: std.array_hash_map.Auto(void, Entry),
708 iat_symbol_indices: std.AutoArrayHashMapUnmanaged(struct {
709 iti: ImportTable.Index,
710 name: String.Optional,
711 // If name == .none this is the ordinal, otherwise the hint
712 ordinal_hint: u16,
713 }, u32),
710714
711715 pub const Entry = struct {
712716 import_lookup_table_ni: MappedFile.Node.Index,
......@@ -815,17 +819,20 @@ pub const Section = struct {
815819
816820pub const GlobalName = struct { name: String, lib_name: String.Optional };
817821
822pub const DllStorageClass = enum(u2) {
823 default,
824 dllimport,
825 dllexport,
826};
827
818828pub const Symbol = struct {
819829 ni: MappedFile.Node.Index,
820830 rva: u32,
821 value: union(enum) {
822 /// For .ni == .input_section, this is the offset of this symbol within the input section
823 input_offset: u32,
824 /// For .ni == none and .gmi != .none, this is a weak alias
825 /// that should replace this symbol, or .null if none exists
826 alias_si: Symbol.Index,
827 /// Otherwise, this is the symbol size if known
828 size: u32,
831 value: std.meta.BareUnion(Symbol.Value),
832 flags: packed struct(u16) {
833 value_tag: ValueTag,
834 dll_storage_class: DllStorageClass,
835 _: u12 = 0,
829836 },
830837 /// Relocations contained within this symbol
831838 loc_relocs: Reloc.Index,
......@@ -836,6 +843,55 @@ pub const Symbol = struct {
836843 sti: SymbolTable.Index,
837844 gmi: Node.GlobalMapIndex,
838845
846 const ValueTag = enum(u2) {
847 node_offset,
848 alias_si,
849 size,
850 };
851
852 pub const Value = union(ValueTag) {
853 /// The offset of the symbol within it's node
854 node_offset: u32,
855 /// For undefined globals, this is a weak alias
856 /// that can replace this symbol, or .null if none exists
857 alias_si: Symbol.Index,
858 /// The symbol size, or 0 if unknown
859 size: u32,
860 };
861
862 pub fn setValue(sym: *Symbol, value: Symbol.Value) void {
863 sym.flags.value_tag = std.meta.activeTag(value);
864 sym.value = switch (sym.flags.value_tag) {
865 inline else => |t| @unionInit(
866 @FieldType(Symbol, "value"),
867 @tagName(t),
868 @field(value, @tagName(t)),
869 ),
870 };
871 }
872
873 pub fn nodeOffset(sym: *const Symbol, coff: *Coff) u32 {
874 return switch (sym.flags.value_tag) {
875 .node_offset => offset: {
876 assert(switch (coff.getNode(sym.ni)) {
877 // Separate nodes are not created for these entries per-symbol
878 .input_section, .import_address_table => true,
879 else => false,
880 });
881 break :offset sym.value.node_offset;
882 },
883 else => 0,
884 };
885 }
886
887 pub fn weakAlias(sym: *const Symbol) Symbol.Index {
888 return if (sym.flags.value_tag == .alias_si) sym.value.alias_si else .null;
889 }
890
891 pub fn size(sym: *const Symbol) u32 {
892 return if (sym.flags.value_tag == .size) sym.value.size else 0;
893 }
894
839895 pub const SectionNumber = enum(i16) {
840896 UNDEFINED = 0,
841897 ABSOLUTE = -1,
......@@ -847,10 +903,7 @@ pub const Symbol = struct {
847903 }
848904
849905 fn hasIndex(sn: SectionNumber) bool {
850 return switch (sn) {
851 .UNDEFINED, .ABSOLUTE, .DEBUG => false,
852 else => true,
853 };
906 return @intFromEnum(sn) > 0;
854907 }
855908
856909 pub fn symbol(sn: SectionNumber, coff: *const Coff) Symbol.Index {
......@@ -902,11 +955,7 @@ pub const Symbol = struct {
902955
903956 pub fn flushMoved(si: Symbol.Index, coff: *Coff) void {
904957 const sym = si.get(coff);
905 sym.rva = coff.computeNodeRva(sym.ni);
906 if (sym.gmi != .none and coff.getNode(sym.ni) == .input_section) {
907 // Symbols in input sections share a ni with their section
908 sym.rva += sym.value.input_offset;
909 }
958 sym.rva = coff.computeNodeRva(sym.ni) + sym.nodeOffset(coff);
910959 si.applyLocationRelocs(coff);
911960 si.applyTargetRelocs(coff);
912961 }
......@@ -967,7 +1016,7 @@ pub const Symbol = struct {
9671016 };
9681017
9691018 comptime {
970 if (!std.debug.runtime_safety) std.debug.assert(@sizeOf(Symbol) == 36);
1019 if (!std.debug.runtime_safety) std.debug.assert(@sizeOf(Symbol) == 32);
9711020 }
9721021};
9731022
......@@ -1379,6 +1428,7 @@ fn create(
13791428 .import_table = .{
13801429 .ni = .none,
13811430 .entries = .empty,
1431 .iat_symbol_indices = .empty,
13821432 },
13831433 .export_table = .{
13841434 .ni = .none,
......@@ -1460,6 +1510,7 @@ pub fn deinit(coff: *Coff) void {
14601510 coff.lib_string_table.deinit(gpa);
14611511 coff.long_names_table.entries.deinit(gpa);
14621512 coff.import_table.entries.deinit(gpa);
1513 coff.import_table.iat_symbol_indices.deinit(gpa);
14631514 coff.export_table.entries.deinit(gpa);
14641515 coff.symbol_table.strings.deinit(gpa);
14651516 coff.symbol_table.pending.deinit(gpa);
......@@ -1840,16 +1891,7 @@ fn initHeaders(
18401891 }
18411892
18421893 try coff.symbols.ensureTotalCapacity(gpa, Symbol.Index.known_count);
1843 coff.symbols.addOneAssumeCapacity().* = .{
1844 .ni = .none,
1845 .rva = 0,
1846 .value = .{ .size = 0 },
1847 .loc_relocs = .none,
1848 .target_relocs = .none,
1849 .section_number = .UNDEFINED,
1850 .sti = .none,
1851 .gmi = .none,
1852 };
1894 assert(coff.addSymbolAssumeCapacity() == .null);
18531895 assert(try coff.addSection(.@".data", .{
18541896 .CNT_INITIALIZED_DATA = true,
18551897 .MEM_READ = true,
......@@ -2057,7 +2099,7 @@ fn computeNodeRva(coff: *Coff, ni: MappedFile.Node.Index) u32 {
20572099 ),
20582100 inline .pseudo_section,
20592101 .object_section,
2060 .global,
2102 .import_thunk,
20612103 .nav,
20622104 .uav,
20632105 .lazy_code,
......@@ -2070,10 +2112,7 @@ fn computeNodeRva(coff: *Coff, ni: MappedFile.Node.Index) u32 {
20702112 return @intCast(parent_rva + offset);
20712113}
20722114fn computeSymbolSectionOffset(coff: *Coff, sym: *const Symbol) u32 {
2073 var section_offset: u32 = if (sym.gmi != .none and coff.getNode(sym.ni) == .input_section)
2074 sym.value.input_offset
2075 else
2076 0;
2115 var section_offset: u32 = sym.nodeOffset(coff);
20772116 var parent_ni = sym.ni;
20782117 while (true) {
20792118 const offset, _ = parent_ni.location(&coff.mf).resolve(&coff.mf);
......@@ -2273,6 +2312,10 @@ fn addSymbolAssumeCapacity(coff: *Coff) Symbol.Index {
22732312 .ni = .none,
22742313 .rva = 0,
22752314 .value = .{ .size = 0 },
2315 .flags = .{
2316 .value_tag = .size,
2317 .dll_storage_class = .default,
2318 },
22762319 .loc_relocs = .none,
22772320 .target_relocs = .none,
22782321 .section_number = .UNDEFINED,
......@@ -2357,8 +2400,9 @@ fn getOrPutStringAssumeCapacity(coff: *Coff, string: []const u8) String {
23572400}
23582401
23592402const GlobalOptions = struct {
2360 name: []const u8, // TODO: Union with String
2403 name: []const u8,
23612404 lib_name: ?[]const u8 = null,
2405 dll_storage_class: DllStorageClass = .default,
23622406};
23632407
23642408fn getOrPutGlobalSymbol(
......@@ -2373,7 +2417,10 @@ fn getOrPutGlobalSymbol(
23732417 });
23742418 if (!sym_gop.found_existing) {
23752419 const si = coff.addSymbolAssumeCapacity();
2376 si.get(coff).gmi = .wrap(@intCast(sym_gop.index));
2420 const sym = si.get(coff);
2421 sym.setValue(.{ .alias_si = .null });
2422 sym.gmi = .wrap(@intCast(sym_gop.index));
2423 sym.flags.dll_storage_class = opts.dll_storage_class;
23772424 sym_gop.value_ptr.* = si;
23782425 coff.synth_prog_node.increaseEstimatedTotalItems(1);
23792426
......@@ -2446,6 +2493,7 @@ pub fn navSymbol(coff: *Coff, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Symbo
24462493 if (nav.getExtern(ip)) |@"extern"| return coff.globalSymbol(.{
24472494 .name = @"extern".name.toSlice(ip),
24482495 .lib_name = @"extern".lib_name.toSlice(ip),
2496 .dll_storage_class = if (@"extern".is_dll_import) .dllimport else .default,
24492497 });
24502498 const nmi = try coff.navMapIndex(zcu, nav_index);
24512499 return nmi.symbol(coff);
......@@ -2774,7 +2822,7 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void
27742822 };
27752823
27762824 coff.targetStore(&entry.value, switch (sym.section_number) {
2777 .UNDEFINED => sym.value.size,
2825 .UNDEFINED => sym.size(),
27782826 .ABSOLUTE,
27792827 .DEBUG,
27802828 => unreachable,
......@@ -2784,7 +2832,7 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void
27842832 },
27852833 });
27862834
2787 log.debug("updateSymbolTableEntry({d}) = {d}", .{ si, sym.sti });
2835 log.debug("flushSymbolTableEntry({d}) = {d}", .{ si, sym.sti });
27882836}
27892837
27902838fn flushInputMember(coff: *Coff, iami: InputArchive.Member.Index) !void {
......@@ -3838,7 +3886,10 @@ fn loadObject(
38383886 .name = symbol.name.toSlice(coff),
38393887 .lib_name = null,
38403888 });
3841 if (!global_gop.found_existing) {
3889
3890 // TODO: What if the same symbol defined twice in this obj?
3891 // TODO: Would need to mark this global as pending, or notice it later when .ni != none
3892 if (!global_gop.found_existing or global_gop.value_ptr.get(coff).ni == .none) {
38423893 symbol.si = global_gop.value_ptr.*;
38433894 break :comdat .include;
38443895 }
......@@ -4003,9 +4054,7 @@ fn loadObject(
40034054 if (!global_gop.found_existing or symbol.si.get(coff).ni == .none) {
40044055 const sym = symbol.si.get(coff);
40054056 if (tag == .external) {
4006 // TOOD: Is it valid to encounter multiple external definitions with different sizes?
4007 assert(sym.value == .size);
4008 sym.value = .{ .size = @max(sym.value.size, value) };
4057 sym.setValue(.{ .size = @max(sym.size(), value) });
40094058 } else {
40104059 const alias = pending_symbols.getPtr(value) orelse
40114060 return diags.failParse(
......@@ -4022,7 +4071,7 @@ fn loadObject(
40224071 if (alias.si == .null) {
40234072 alias.weak_external_psi = .wrap(@intCast(psi));
40244073 } else {
4025 sym.value = .{ .alias_si = alias.si };
4074 sym.setValue(.{ .alias_si = alias.si });
40264075 }
40274076 }
40284077 }
......@@ -4063,20 +4112,21 @@ fn loadObject(
40634112
40644113 if (section.si != symbol.si) {
40654114 const sym = symbol.si.get(coff);
4115 assert(sym.ni == .none);
40664116 sym.ni = section.si.get(coff).ni;
4067 sym.value = switch (symbol.value) {
4117 sym.setValue(switch (symbol.value) {
40684118 .section => |v| .{ .size = v },
4069 .static => |v| .{ .input_offset = v },
4119 .static => |v| .{ .node_offset = v },
40704120 .external => |v| switch (symbol.section_number) {
40714121 .UNDEFINED, .ABSOLUTE, .DEBUG => unreachable,
4072 else => .{ .input_offset = v },
4122 else => .{ .node_offset = v },
40734123 },
40744124 .weak_external => unreachable,
4075 };
4125 });
40764126 sym.section_number = symbol.section_number;
40774127 }
40784128
4079 defer log.debug("addInputSymbol({s}, 0x{x}, {t}=0x{x}) = {d}@{d}", .{
4129 log.debug("addInputSymbol({s}, 0x{x}, {t}=0x{x}) = {d}@{d}", .{
40804130 symbol.name.toSlice(coff),
40814131 index,
40824132 symbol.value,
......@@ -4141,20 +4191,21 @@ fn loadObject(
41414191 pending_symbols.sortUnstable(SortContext{ .v = pending_symbols.values() });
41424192
41434193 try coff.input_symbols.ensureUnusedCapacity(gpa, num_included_symbols + num_included_sections);
4144 var prev_sn: Symbol.SectionNumber = .UNDEFINED;
4145 var include_section = true;
4194 var prev_sn: Symbol.SectionNumber = .DEBUG;
4195 var include_section = false;
41464196 for (pending_symbols.values()) |symbol| {
41474197 // The symbol may have not been included, or it's an undefined external
41484198 if (symbol.si == .null or symbol.si.get(coff).ni == .none) continue;
41494199
41504200 if (prev_sn != symbol.section_number) {
41514201 prev_sn = symbol.section_number;
4152
4153 const section = &sections[symbol.section_number.toIndex()];
4154 include_section = section.comdat_result == .include;
4155 if (include_section) {
4156 const isi = coff.getNode(section.si.get(coff).ni).input_section;
4157 isi.inputSection(coff).first_li = @enumFromInt(coff.input_symbols.items.len);
4202 if (symbol.section_number.hasIndex()) {
4203 const section = &sections[symbol.section_number.toIndex()];
4204 include_section = section.comdat_result == .include;
4205 if (include_section) {
4206 const isi = coff.getNode(section.si.get(coff).ni).input_section;
4207 isi.inputSection(coff).first_li = @enumFromInt(coff.input_symbols.items.len);
4208 }
41584209 }
41594210 }
41604211
......@@ -4329,7 +4380,7 @@ fn loadArchive(coff: *Coff, path: std.Build.Cache.Path, fr: *Io.File.Reader) !vo
43294380 var pos = fr.logicalPos();
43304381 const size = try fr.getSize();
43314382 while (pos < size) : (pos = fr.logicalPos()) {
4332 if ((pos & 1) != 0) r.toss(1);
4383 if ((pos & 1) != 0) try r.discardAll(1);
43334384 const header = try r.takeStruct(std.coff.ArchiveMemberHeader, target_endian);
43344385 const res = try parseArchiveMemberHeader(diags, path, &header, opt_longnames);
43354386
......@@ -4354,7 +4405,7 @@ fn loadArchive(coff: *Coff, path: std.Build.Cache.Path, fr: *Io.File.Reader) !vo
43544405
43554406 const num_members = try r.takeInt(u32, target_endian);
43564407 pos = fr.logicalPos();
4357 if (pos + num_members * 4 > member_end)
4408 if (pos + num_members * @sizeOf(u32) > member_end)
43584409 return diags.failParse(path, "invalid member count 0x{x} in second linker member", .{num_members});
43594410
43604411 try members.ensureTotalCapacity(gpa, num_members);
......@@ -4366,7 +4417,7 @@ fn loadArchive(coff: *Coff, path: std.Build.Cache.Path, fr: *Io.File.Reader) !vo
43664417
43674418 const num_symbols = try r.takeInt(u32, target_endian);
43684419 pos = fr.logicalPos();
4369 if (pos + num_symbols * 2 > member_end)
4420 if (pos + num_symbols * @sizeOf(u16) > member_end)
43704421 return diags.failParse(path, "invalid symbol count 0x{x} in second linker member", .{num_symbols});
43714422
43724423 try symbol_member_indices.ensureTotalCapacity(gpa, num_symbols);
......@@ -4532,7 +4583,9 @@ fn loadArchive(coff: *Coff, path: std.Build.Cache.Path, fr: *Io.File.Reader) !vo
45324583 };
45334584 } else {
45344585 member.content.object.size = res.size;
4535 if (machine != expected_machine) {
4586 // TODO: If .UNKNOWN assert later that it contains no non-undef symbols?
4587 // Microsoft's CRT contains members that set .UNKNOWN but do have symbols
4588 if (machine != expected_machine and machine != .UNKNOWN) {
45364589 return diags.failParse(path, "machine mismatch in member header '{s}': expected {t}, found {t}", .{
45374590 res.name,
45384591 expected_machine,
......@@ -4935,9 +4988,10 @@ fn reportUndefs(coff: *Coff, tid: Zcu.PerThread.Id) !void {
49354988 });
49364989 }
49374990 },
4938 .global => |gmi| err.addNote("referenced by '{s}' in module '{s}'", .{
4991 .import_thunk => |gmi| err.addNote("referenced by import thunk for '{s}'", .{
49394992 gmi.globalName(coff).name.toSlice(coff),
4940 comp.zcu.?.root_mod.fully_qualified_name,
4993 // TODO: This won't always have a ZCU
4994 //comp.zcu.?.root_mod.fully_qualified_name,
49414995 }),
49424996 inline .nav,
49434997 .uav,
......@@ -5099,7 +5153,7 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
50995153 if (sym.ni != .none)
51005154 coff.getNode(sym.ni)
51015155 else
5102 .{ .global = pending_si.key.get(coff).gmi },
5156 .{ .import_thunk = pending_si.key.get(coff).gmi },
51035157 );
51045158 defer sub_prog_node.end();
51055159 coff.flushSymbolTableEntry(
......@@ -5229,7 +5283,7 @@ fn idleProgNode(
52295283 coff.getNode(isi.symbol(coff).node(coff).parent(&coff.mf)).object_section.name(coff).toSlice(coff),
52305284 }) catch &name;
52315285 },
5232 .global => |gmi| gmi.globalName(coff).name.toSlice(coff),
5286 .import_thunk => |gmi| gmi.globalName(coff).name.toSlice(coff),
52335287 .nav => |nmi| {
52345288 const ip = &coff.base.comp.zcu.?.intern_pool;
52355289 break :name ip.getNav(nmi.navIndex(coff)).fqn.toSlice(ip);
......@@ -5306,10 +5360,45 @@ fn flushUav(
53065360 si.applyLocationRelocs(coff);
53075361}
53085362
5363fn aliasGlobal(coff: *Coff, gmi: Node.GlobalMapIndex, alias_si: Symbol.Index) !void {
5364 const gn = gmi.globalName(coff);
5365 const si = gmi.symbol(coff);
5366 const sym = si.get(coff);
5367 assert(sym.section_number == .UNDEFINED);
5368 assert(sym.loc_relocs == .none);
5369
5370 const alias_sym = alias_si.get(coff);
5371 var ri = sym.target_relocs;
5372 while (ri != .none) {
5373 const reloc = ri.get(coff);
5374 assert(reloc.target == si);
5375 reloc.target = alias_si;
5376 if (reloc.next == .none) {
5377 reloc.next = alias_sym.target_relocs;
5378 if (alias_sym.target_relocs != .none)
5379 alias_sym.target_relocs.get(coff).prev = ri;
5380 }
5381 ri = reloc.next;
5382 }
5383
5384 sym.target_relocs = .none;
5385 sym.gmi = alias_sym.gmi;
5386 coff.globals.values()[gmi.unwrap().?] = alias_si;
5387 alias_si.applyTargetRelocs(coff);
5388
5389 log.debug("aliasGlobal({s}, {?s}) {d}->{d} ({?s})", .{
5390 gn.name.toSlice(coff),
5391 gn.lib_name.toSlice(coff),
5392 si,
5393 alias_si,
5394 if (alias_sym.gmi != .none) alias_sym.gmi.globalName(coff).name.toSlice(coff) else null,
5395 });
5396}
5397
53095398fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
53105399 const comp = coff.base.comp;
53115400 const gpa = comp.gpa;
5312 const gn = gmi.globalNameMutable(coff);
5401 const gn = gmi.globalName(coff);
53135402 const si = gmi.symbol(coff);
53145403 const sym = si.get(coff);
53155404
......@@ -5329,112 +5418,102 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
53295418 return true;
53305419 }
53315420
5421 {
5422 // Resolve unresolved .WEAK_EXTERNAL symbols to their aliases
5423 const alias_si = sym.weakAlias();
5424 if (alias_si != .null) {
5425 try coff.aliasGlobal(gmi, alias_si);
5426 return true;
5427 }
5428 }
5429
53325430 const Import = struct {
53335431 lib_name: String,
5334 ref: union(enum) {
5335 name: struct {
5336 str: []const u8,
5337 hint: ?u16,
5338 },
5339 ordinal: u16,
5432 name: String.Optional,
5433 ordinal_hint: u16,
5434 kind: enum {
5435 iat_ptr,
5436 thunk,
53405437 },
53415438 };
53425439
5343 const opt_import: ?Import = if (gn.lib_name == .none and sym.ni == .none) import: {
5344 switch (sym.value) {
5345 .alias_si => |alias_si| {
5346 assert(sym.section_number == .UNDEFINED);
5347 assert(sym.loc_relocs == .none);
5348
5349 const alias_sym = alias_si.get(coff);
5350 var ri = sym.target_relocs;
5351 while (ri != .none) {
5352 const reloc = ri.get(coff);
5353 assert(reloc.target == si);
5354 reloc.target = alias_si;
5355 if (reloc.next == .none) {
5356 reloc.next = alias_sym.target_relocs;
5357 if (alias_sym.target_relocs != .none)
5358 alias_sym.target_relocs.get(coff).prev = ri;
5359 }
5360 ri = reloc.next;
5361 }
5362
5363 sym.target_relocs = .none;
5364 coff.globals.values()[gmi.unwrap().?] = alias_si;
5365 alias_si.applyTargetRelocs(coff);
5366
5367 log.debug(
5368 "flushGlobal({s}, null) alias {d}->{d}",
5369 .{ gmi.globalName(coff).name.toSlice(coff), si, alias_si },
5370 );
5371 return true;
5372 },
5373 .size => {},
5374 .input_offset => unreachable,
5375 }
5440 const opt_import: ?Import = if (sym.ni == .none) import: {
5441 const global_name = gn.name.toSlice(coff);
5442 const imp_match = std.mem.startsWith(u8, global_name, imp_prefix);
5443
5444 // Globals may have the __imp_ prefix already if they are undef externals from another input.
5445 const search_name, const is_imp = if (imp_match or sym.flags.dll_storage_class != .dllimport)
5446 .{ gn.name, imp_match }
5447 else name: {
5448 try coff.ensureUnusedStringCapacity(imp_prefix.len + global_name.len);
5449 const name = try std.fmt.allocPrint(gpa, imp_prefix ++ "{s}", .{global_name});
5450 defer gpa.free(name);
5451 break :name .{ coff.getOrPutStringAssumeCapacity(name), true };
5452 };
53765453
5377 if (coff.input_archive_symbol_indices.get(gmi.globalName(coff).name)) |index| {
5378 var iter: InputArchive.Member.Symbol.Index = index.first;
5454 if (coff.input_archive_symbol_indices.get(search_name)) |indices_list| {
5455 var iter: InputArchive.Member.Symbol.Index = indices_list.first;
53795456 while (true) {
53805457 const archive_sym = &coff.input_archive_symbols.items[@intFromEnum(iter)];
53815458 const member = &coff.input_archive_members.items[@intFromEnum(archive_sym.iami)];
5382 if (!member.flags.is_loaded) {
5383 switch (member.content) {
5384 .import => |import| switch (import.type) {
5385 .CODE,
5386 .DATA,
5387 => {
5388 defer member.flags.is_loaded = true;
5389 // gn.lib_name = import.lib_name.toOptional();
5390 // try coff.globals.setKey(gpa, gmi.unwrap().?, gn.*);
5391
5392 // Switch this global to an import
5393 switch (import.name_type) {
5394 .NAME,
5395 .NAME_NOPREFIX,
5396 .NAME_UNDECORATE,
5397 => |tag| {
5398 var name: []const u8 = import.symbol_name.toSlice(coff);
5399 if (!(std.mem.eql(u8, name, gn.name.toSlice(coff))))
5400 return comp.link_diags.fail("import '{s}' has mismatched symbol name: '{s}'", .{
5401 import.symbol_name.toSlice(coff),
5402 gn.name.toSlice(coff),
5403 });
5404
5405 name = if (tag == .NAME) name else name: {
5406 name = std.mem.trimStart(u8, name, "?@_");
5407 if (tag == .NAME_UNDECORATE)
5408 name = std.mem.sliceTo(name, '@');
5409 break :name name;
5410 };
5411
5412 break :import .{
5413 .lib_name = import.lib_name,
5414 .ref = .{
5415 .name = .{
5416 .str = name,
5417 .hint = import.import_ordinal_hint,
5418 },
5419 },
5420 };
5421 },
5422 .ORDINAL => break :import .{
5423 .lib_name = import.lib_name,
5424 .ref = .{ .ordinal = import.import_ordinal_hint },
5425 },
5426 else => |t| return comp.link_diags.fail("TODO handle name_type {t}", .{t}),
5427 }
5459 member: switch (member.content) {
5460 .object => if (!member.flags.is_loaded) {
5461 if (gn.lib_name.unwrap()) |lib_name|
5462 if (!std.mem.eql(u8, lib_name.toSlice(coff), member.iai.path(coff).stem()))
5463 break :member;
5464
5465 // Try loading the input member and then retry.
5466 // This could still be a member containing imports
5467 // that use the older non-IMPORT_HEADER method.
5468 coff.pending_input = archive_sym.iami;
5469 return false;
5470 },
5471 .import => |import| {
5472 if (gn.lib_name.unwrap()) |lib_name|
5473 if (import.lib_name != lib_name)
5474 break :member;
5475
5476 const name: String.Optional = name: switch (import.name_type) {
5477 .NAME,
5478 .NAME_NOPREFIX,
5479 .NAME_UNDECORATE,
5480 => |tag| {
5481 const symbol_name: []const u8 = import.symbol_name.toSlice(coff);
5482 const end_match = std.mem.endsWith(u8, global_name, symbol_name);
5483 const len_delta = global_name.len -% symbol_name.len;
5484 if (!end_match or
5485 (!imp_match and len_delta != 0) or
5486 (imp_match and len_delta != imp_prefix.len))
5487 return comp.link_diags.fail(
5488 "global '{s}' has mismatched symbol name in import header: '{s}'",
5489 .{
5490 gn.name.toSlice(coff),
5491 import.symbol_name.toSlice(coff),
5492 },
5493 );
5494
5495 const name = if (tag == .NAME) import.symbol_name else undecorated: {
5496 var imp_name = std.mem.trimStart(u8, symbol_name, "?@_");
5497 if (tag == .NAME_UNDECORATE)
5498 imp_name = std.mem.sliceTo(imp_name, '@');
5499
5500 try coff.ensureUnusedStringCapacity(imp_name.len);
5501 break :undecorated coff.getOrPutStringAssumeCapacity(imp_name);
5502 };
5503
5504 break :name name.toOptional();
54285505 },
5429 .CONST => return comp.link_diags.fail("TODO handle import type CONST", .{}),
5430 else => |t| return comp.link_diags.fail("invalid import type: {d}", .{t}),
5431 },
5432 .object => {
5433 // Try loading the input member and then retry
5434 coff.pending_input = archive_sym.iami;
5435 return false;
5436 },
5437 }
5506 .ORDINAL => break :name .none,
5507 else => |t| return comp.link_diags.fail("TODO handle name_type {t}", .{t}),
5508 };
5509
5510 break :import .{
5511 .lib_name = import.lib_name,
5512 .name = name,
5513 .ordinal_hint = import.import_ordinal_hint,
5514 .kind = if (import.type == .CODE and !is_imp) .thunk else .iat_ptr,
5515 };
5516 },
54385517 }
54395518
54405519 if (archive_sym.next == iter) break;
......@@ -5442,28 +5521,20 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
54425521 }
54435522 }
54445523
5445 break :import null;
5446 } else if (gn.lib_name.unwrap()) |lib_name| .{
5447 .lib_name = lib_name,
5448 .ref = .{
5449 .name = .{
5450 .str = gn.name.toSlice(coff),
5451 .hint = null,
5452 },
5453 },
5524 // Allow importing symbols with no implib entry, if a lib_name was specified.
5525 // This is necessary for certain ntdll symbols, such as LdrRegisterDllNotification,
5526 // which are not in the implib.
5527 break :import if (gn.lib_name.unwrap()) |lib_name| .{
5528 .lib_name = lib_name,
5529 .name = gn.name.toOptional(),
5530 .ordinal_hint = 0,
5531 .kind = .iat_ptr,
5532 } else null;
54545533 } else null;
54555534
54565535 if (opt_import) |import| {
54575536 assert(sym.ni == .none);
54585537 const lib_name = import.lib_name.toSlice(coff);
5459 const name = switch (import.ref) {
5460 .name => |n| n.str,
5461 .ordinal => return comp.link_diags.fail("TODO handle imports via ordinal", .{}),
5462 };
5463
5464 log.debug("flushGlobalImport({s}, {s})", .{ name, lib_name });
5465
5466 // TODO: Handle hint
54675538
54685539 try coff.nodes.ensureUnusedCapacity(gpa, 4);
54695540 try coff.symbols.ensureUnusedCapacity(gpa, 1);
......@@ -5547,76 +5618,138 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
55475618 if (target_endian != native_endian)
55485619 std.mem.byteSwapAllFields([2]std.coff.ImportDirectoryEntry, import_directory_entries);
55495620 }
5550 const import_symbol_index = gop.value_ptr.len;
5551 gop.value_ptr.len = import_symbol_index + 1;
5552 const new_symbol_table_size = addr_size * (import_symbol_index + 2);
5553 const import_hint_name_index = gop.value_ptr.hint_name_len;
5554 gop.value_ptr.hint_name_len = @intCast(
5555 import_hint_name_align.forward(import_hint_name_index + 2 + name.len + 1),
5621
5622 log.debug(
5623 "flushGlobalImport({s}, {?s}, {d}, {s})",
5624 .{ gn.name.toSlice(coff), import.name.toSlice(coff), import.ordinal_hint, lib_name },
55565625 );
5557 try gop.value_ptr.import_lookup_table_ni.resize(&coff.mf, gpa, new_symbol_table_size);
5558 const import_address_table_ni = gop.value_ptr.import_address_table_si.node(coff);
5559 try import_address_table_ni.resize(&coff.mf, gpa, new_symbol_table_size);
5560 try gop.value_ptr.import_hint_name_table_ni.resize(&coff.mf, gpa, gop.value_ptr.hint_name_len);
5561 const import_lookup_slice = gop.value_ptr.import_lookup_table_ni.slice(&coff.mf);
5562 const import_address_slice = import_address_table_ni.slice(&coff.mf);
5563 const import_hint_name_slice = gop.value_ptr.import_hint_name_table_ni.slice(&coff.mf);
5564 @memset(import_hint_name_slice[import_hint_name_index..][0..2], 0);
5565 @memcpy(import_hint_name_slice[import_hint_name_index + 2 ..][0..name.len], name);
5566 @memset(import_hint_name_slice[import_hint_name_index + 2 + name.len ..], 0);
5567 const import_hint_name_rva =
5568 coff.computeNodeRva(gop.value_ptr.import_hint_name_table_ni) + import_hint_name_index;
5569 switch (magic) {
5570 _ => unreachable,
5571 inline .PE32, .@"PE32+" => |ct_magic| {
5572 const Addr = switch (ct_magic) {
5573 _ => comptime unreachable,
5574 .PE32 => u32,
5575 .@"PE32+" => u64,
5576 };
5577 const import_lookup_table: []Addr = @ptrCast(@alignCast(import_lookup_slice));
5578 const import_address_table: []Addr = @ptrCast(@alignCast(import_address_slice));
5579 const import_hint_name_rvas: [2]Addr = .{
5580 std.mem.nativeTo(Addr, @intCast(import_hint_name_rva), target_endian),
5581 std.mem.nativeTo(Addr, 0, target_endian),
5582 };
5583 import_lookup_table[import_symbol_index..][0..2].* = import_hint_name_rvas;
5584 import_address_table[import_symbol_index..][0..2].* = import_hint_name_rvas;
5585 },
5626
5627 const iat_symbol_gop = try coff.import_table.iat_symbol_indices.getOrPut(gpa, .{
5628 .iti = @enumFromInt(gop.index),
5629 .name = import.name,
5630 .ordinal_hint = import.ordinal_hint,
5631 });
5632 if (!iat_symbol_gop.found_existing) {
5633 const import_symbol_index = gop.value_ptr.len;
5634 iat_symbol_gop.value_ptr.* = import_symbol_index;
5635
5636 gop.value_ptr.len = import_symbol_index + 1;
5637 const new_symbol_table_size = addr_size * (import_symbol_index + 2);
5638
5639 const opt_name = import.name.toSlice(coff);
5640 const opt_import_hint_name_index = if (opt_name) |name| blk: {
5641 const import_hint_name_index = gop.value_ptr.hint_name_len;
5642 gop.value_ptr.hint_name_len = @intCast(
5643 import_hint_name_align.forward(import_hint_name_index + 2 + name.len + 1),
5644 );
5645 break :blk import_hint_name_index;
5646 } else null;
5647
5648 try gop.value_ptr.import_lookup_table_ni.resize(&coff.mf, gpa, new_symbol_table_size);
5649 const import_address_table_ni = gop.value_ptr.import_address_table_si.node(coff);
5650 try import_address_table_ni.resize(&coff.mf, gpa, new_symbol_table_size);
5651 try gop.value_ptr.import_hint_name_table_ni.resize(&coff.mf, gpa, gop.value_ptr.hint_name_len);
5652
5653 const import_hint_name_rva = if (opt_import_hint_name_index) |import_hint_name_index| blk: {
5654 const import_hint_name_slice = gop.value_ptr.import_hint_name_table_ni.slice(&coff.mf);
5655 const ordinal_hint: *u16 = @ptrCast(@alignCast(import_hint_name_slice[import_hint_name_index..][0..2]));
5656 ordinal_hint.* = std.mem.nativeTo(u16, import.ordinal_hint, target_endian);
5657 @memcpy(import_hint_name_slice[import_hint_name_index + 2 ..][0..opt_name.?.len], opt_name.?);
5658 @memset(import_hint_name_slice[import_hint_name_index + 2 + opt_name.?.len ..], 0);
5659 break :blk coff.computeNodeRva(gop.value_ptr.import_hint_name_table_ni) + import_hint_name_index;
5660 } else 0;
5661
5662 const import_lookup_slice = gop.value_ptr.import_lookup_table_ni.slice(&coff.mf);
5663 const import_address_slice = import_address_table_ni.slice(&coff.mf);
5664 switch (magic) {
5665 _ => unreachable,
5666 inline .PE32, .@"PE32+" => |ct_magic| {
5667 const Payload = packed union(u31) {
5668 ordinal: packed struct(u31) {
5669 ordinal: u16,
5670 _: u15 = 0,
5671 },
5672 hint_name_rva: u31,
5673 };
5674
5675 const Entry = switch (ct_magic) {
5676 _ => comptime unreachable,
5677 .PE32 => packed struct(u32) {
5678 payload: Payload,
5679 is_ordinal: bool,
5680 },
5681 .@"PE32+" => packed struct(u64) {
5682 payload: Payload,
5683 _: u32 = 0,
5684 is_ordinal: bool,
5685 },
5686 };
5687 const import_lookup_table: []Entry = @ptrCast(@alignCast(import_lookup_slice));
5688 const import_address_table: []Entry = @ptrCast(@alignCast(import_address_slice));
5689 const import_hint_name_rvas: [2]Entry = .{
5690 .{
5691 .payload = if (import.name == .none)
5692 .{ .ordinal = .{ .ordinal = import.ordinal_hint } }
5693 else
5694 .{ .hint_name_rva = @intCast(import_hint_name_rva) },
5695 .is_ordinal = import.name == .none,
5696 },
5697 @bitCast(@as(@typeInfo(Entry).@"struct".backing_integer.?, 0)),
5698 };
5699 if (native_endian != target_endian)
5700 for (import_hint_name_rvas) |*v| std.mem.byteSwapAllFields(Entry, v);
5701
5702 import_lookup_table[import_symbol_index..][0..2].* = import_hint_name_rvas;
5703 import_address_table[import_symbol_index..][0..2].* = import_hint_name_rvas;
5704 },
5705 }
55865706 }
5587 sym.section_number = Symbol.Index.text.get(coff).section_number;
5707
55885708 assert(sym.loc_relocs == .none);
5589 sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
5590 switch (coff.targetLoad(&coff.headerPtr().machine)) {
5591 else => |tag| @panic(@tagName(tag)),
5592 .AMD64 => {
5593 const init = [_]u8{ 0xff, 0x25, 0x00, 0x00, 0x00, 0x00 };
5594 const target = &comp.root_mod.resolved_target.result;
5595 const ni = try coff.mf.addLastChildNode(gpa, Symbol.Index.text.node(coff), .{
5596 .alignment = switch (comp.root_mod.optimize_mode) {
5597 .Debug,
5598 .ReleaseSafe,
5599 .ReleaseFast,
5600 => target_util.defaultFunctionAlignment(target),
5601 .ReleaseSmall => target_util.minFunctionAlignment(target),
5602 }.toStdMem(),
5603 .size = init.len,
5604 });
5605 @memcpy(ni.slice(&coff.mf)[0..init.len], &init);
5606 sym.ni = ni;
5607 sym.value.size = init.len;
5608 try coff.addReloc(
5609 si,
5610 init.len - 4,
5611 gop.value_ptr.import_address_table_si,
5612 .{ .known = @intCast(addr_size * import_symbol_index) },
5613 .{ .AMD64 = .REL32 },
5614 );
5709 const iat_offset: u32 = @intCast(addr_size * iat_symbol_gop.value_ptr.*);
5710 switch (import.kind) {
5711 .iat_ptr => {
5712 const iat_sym = gop.value_ptr.import_address_table_si.get(coff);
5713 sym.section_number = iat_sym.section_number;
5714 sym.ni = iat_sym.ni;
5715 sym.setValue(.{ .node_offset = iat_offset });
5716 si.flushMoved(coff);
5717 },
5718 .thunk => {
5719 sym.section_number = Symbol.Index.text.get(coff).section_number;
5720 sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
5721 switch (coff.targetLoad(&coff.headerPtr().machine)) {
5722 else => |tag| @panic(@tagName(tag)),
5723 .AMD64 => {
5724 const init = [_]u8{ 0xff, 0x25, 0x00, 0x00, 0x00, 0x00 };
5725 const target = &comp.root_mod.resolved_target.result;
5726 const ni = try coff.mf.addLastChildNode(gpa, Symbol.Index.text.node(coff), .{
5727 .alignment = switch (comp.root_mod.optimize_mode) {
5728 .Debug,
5729 .ReleaseSafe,
5730 .ReleaseFast,
5731 => target_util.defaultFunctionAlignment(target),
5732 .ReleaseSmall => target_util.minFunctionAlignment(target),
5733 }.toStdMem(),
5734 .size = init.len,
5735 });
5736 @memcpy(ni.slice(&coff.mf)[0..init.len], &init);
5737 sym.ni = ni;
5738 sym.setValue(.{ .size = init.len });
5739 try coff.addReloc(
5740 si,
5741 init.len - 4,
5742 gop.value_ptr.import_address_table_si,
5743 .{ .known = iat_offset },
5744 .{ .AMD64 = .REL32 },
5745 );
5746 },
5747 }
5748 coff.nodes.appendAssumeCapacity(.{ .import_thunk = gmi });
5749 sym.rva = coff.computeNodeRva(sym.ni);
5750 si.applyLocationRelocs(coff);
56155751 },
56165752 }
5617 coff.nodes.appendAssumeCapacity(.{ .global = gmi });
5618 sym.rva = coff.computeNodeRva(sym.ni);
5619 si.applyLocationRelocs(coff);
56205753 }
56215754
56225755 return true;
......@@ -5845,7 +5978,7 @@ fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void {
58455978 },
58465979 inline .pseudo_section,
58475980 .object_section,
5848 .global,
5981 .import_thunk,
58495982 .nav,
58505983 .uav,
58515984 .lazy_code,
......@@ -5980,7 +6113,7 @@ fn flushResized(coff: *Coff, ni: MappedFile.Node.Index) !void {
59806113
59816114 smi.symbol(coff).get(coff).value.size = @intCast(size);
59826115 },
5983 .global,
6116 .import_thunk,
59846117 .nav,
59856118 .uav,
59866119 .lazy_code,
......@@ -6148,7 +6281,7 @@ fn updateExportsInner(
61486281 const export_sym = export_si.get(coff);
61496282 export_sym.ni = exported_ni;
61506283 export_sym.rva = exported_sym.rva;
6151 export_sym.value.size = exported_sym.value.size;
6284 export_sym.setValue(.{ .size = exported_sym.value.size });
61526285 export_sym.section_number = exported_sym.section_number;
61536286 defer export_si.applyTargetRelocs(coff);
61546287
......@@ -6308,7 +6441,7 @@ pub fn printNode(
63086441 inline .pseudo_section, .object_section => |smi| try w.print("({s})", .{
63096442 smi.name(coff).toSlice(coff),
63106443 }),
6311 .global => |gmi| {
6444 .import_thunk => |gmi| {
63126445 const gn = gmi.globalName(coff);
63136446 try w.writeByte('(');
63146447 if (gn.lib_name.toSlice(coff)) |lib_name| try w.print("{s}.dll, ", .{lib_name});