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
log8bf109b0b34d1d97a71a447ab4d79a89125a2de2
tree2b6b5aea598fd0be43cb05442fbeea8ab63cae8f
parentf5a2bfd95ee75a5ddd57071567052e3482683925

Coff: weak external symbol resolutions, more .drectve argument support

- Track weak external type for resolution later - Wait until exports are known before resolving weak externals / alternate names - Support /DEFAULTLIB, /INCLUDE, /ALTERNATENAME in .drectve - Resolve /DEFAULTLIB during prelink - Start on support linking images with no zcu

1 files changed, 408 insertions(+), 119 deletions(-)

src/link/Coff.zig+408-119
......@@ -19,6 +19,7 @@ const Value = @import("../Value.zig");
1919const Zcu = @import("../Zcu.zig");
2020const ModuleDefinition = @import("../libs/mingw/def.zig").ModuleDefinition;
2121const implib = @import("../libs/mingw/implib.zig");
22const Path = std.Build.Cache.Path;
2223
2324base: link.File,
2425mf: MappedFile,
......@@ -35,16 +36,19 @@ inputs: std.ArrayHashMapUnmanaged(std.Build.Cache.Path, void, std.Build.Cache.Pa
3536input_archives: std.ArrayList(InputArchive),
3637input_archive_members: std.ArrayList(InputArchive.Member),
3738input_archive_symbols: std.ArrayList(InputArchive.Member.Symbol),
38input_archive_symbol_indices: std.AutoArrayHashMapUnmanaged(String, struct {
39 first: InputArchive.Member.Symbol.Index,
40 last: InputArchive.Member.Symbol.Index,
41}),
39input_archive_symbol_indices: std.AutoArrayHashMapUnmanaged(String, InputArchive.SearchList),
4240pending_input: ?InputArchive.Member.Index,
41pending_default_libs: std.ArrayList(struct {
42 path: []const u8,
43 ioi: InputObject.Index,
44}),
45alternate_names: std.AutoArrayHashMapUnmanaged(String, String),
4346input_objects: std.ArrayList(InputObject),
4447input_symbols: std.ArrayList(Symbol.Index),
4548input_sections: std.ArrayList(Node.InputSection),
4649input_section_pending_index: u32,
4750inputs_complete: bool,
51exports_complete: bool,
4852strings: std.HashMapUnmanaged(
4953 u32,
5054 void,
......@@ -58,6 +62,8 @@ object_section_table: std.array_hash_map.Auto(String, Symbol.Index),
5862symbols: std.ArrayList(Symbol),
5963globals: std.array_hash_map.Auto(GlobalName, Symbol.Index),
6064global_pending_index: u32,
65late_globals: std.ArrayList(Node.GlobalMapIndex),
66late_globals_pending_index: u32,
6167navs: std.array_hash_map.Auto(InternPool.Nav.Index, Symbol.Index),
6268uavs: std.array_hash_map.Auto(InternPool.Index, Symbol.Index),
6369lazy: std.EnumArray(link.File.LazySymbol.Kind, struct {
......@@ -73,6 +79,7 @@ synth_prog_node: std.Progress.Node,
7379symbol_prog_node: std.Progress.Node,
7480member_prog_node: std.Progress.Node,
7581input_prog_node: std.Progress.Node,
82subsystem: ?std.zig.Subsystem,
7683dump_snapshot: bool,
7784
7885pub const default_file_alignment: u16 = 0x200;
......@@ -435,6 +442,11 @@ pub const InputArchive = struct {
435442 };
436443 };
437444 };
445
446 pub const SearchList = struct {
447 first: InputArchive.Member.Symbol.Index,
448 last: InputArchive.Member.Symbol.Index,
449 };
438450};
439451
440452pub const InputObject = struct {
......@@ -825,6 +837,23 @@ pub const Section = struct {
825837
826838pub const GlobalName = struct { name: String, lib_name: String.Optional };
827839
840pub const WeakExternalStrat = enum(u2) {
841 no_library,
842 library,
843 alias,
844 anti_dependency,
845
846 pub fn fromFlag(flag: std.coff.WeakExternalFlag) WeakExternalStrat {
847 return switch (flag) {
848 .SEARCH_NOLIBRARY => .no_library,
849 .SEARCH_LIBRARY => .library,
850 .SEARCH_ALIAS => .alias,
851 .ANTI_DEPENDENCY => .anti_dependency,
852 _ => unreachable,
853 };
854 }
855};
856
828857pub const Symbol = struct {
829858 ni: MappedFile.Node.Index,
830859 rva: u32,
......@@ -833,7 +862,9 @@ pub const Symbol = struct {
833862 value_tag: ValueTag,
834863 type: Symbol.Type,
835864 dll_storage_class: DllStorageClass,
836 _: u10 = 0,
865 // Only defined for .alias_si and .alias_name
866 weak_external_strat: WeakExternalStrat,
867 _: u8 = 0,
837868 },
838869 /// Relocations contained within this symbol
839870 loc_relocs: Reloc.Index,
......@@ -859,15 +890,22 @@ pub const Symbol = struct {
859890 const ValueTag = enum(u2) {
860891 node_offset,
861892 alias_si,
893 alias_name,
862894 size,
863895 };
864896
865897 pub const Value = union(ValueTag) {
866 /// The offset of the symbol within it's node
898 /// The offset of the symbol within its node. Used with symbols that
899 /// don't create their own nodes: .input_section, .import_address_table
867900 node_offset: u32,
868 /// For undefined globals, this is a weak alias
869 /// that can replace this symbol, or .null if none exists
901 /// This is a weak alias that can replace this symbol
902 /// Globals only.
870903 alias_si: Symbol.Index,
904 /// For weak externals that have an alias that is also an undef
905 /// external, this is the name of the alias global that should
906 /// be generated if this symbol is not resolved.
907 /// Globals only.
908 alias_name: String,
871909 /// The symbol size, or 0 if unknown
872910 size: u32,
873911 };
......@@ -897,10 +935,6 @@ pub const Symbol = struct {
897935 };
898936 }
899937
900 pub fn weakAlias(sym: *const Symbol) Symbol.Index {
901 return if (sym.flags.value_tag == .alias_si) sym.value.alias_si else .null;
902 }
903
904938 pub fn size(sym: *const Symbol) u32 {
905939 return if (sym.flags.value_tag == .size) sym.value.size else 0;
906940 }
......@@ -1465,11 +1499,14 @@ fn create(
14651499 .input_archive_symbols = .empty,
14661500 .input_archive_symbol_indices = .empty,
14671501 .pending_input = null,
1502 .pending_default_libs = .empty,
1503 .alternate_names = .empty,
14681504 .input_objects = .empty,
14691505 .input_symbols = .empty,
14701506 .input_sections = .empty,
14711507 .input_section_pending_index = 0,
14721508 .inputs_complete = false,
1509 .exports_complete = false,
14731510 .strings = .empty,
14741511 .string_bytes = .empty,
14751512 .section_table = .empty,
......@@ -1478,6 +1515,8 @@ fn create(
14781515 .symbols = .empty,
14791516 .globals = .empty,
14801517 .global_pending_index = 0,
1518 .late_globals = .empty,
1519 .late_globals_pending_index = 0,
14811520 .navs = .empty,
14821521 .uavs = .empty,
14831522 .lazy = .initFill(.{
......@@ -1491,6 +1530,7 @@ fn create(
14911530 .symbol_prog_node = .none,
14921531 .member_prog_node = .none,
14931532 .input_prog_node = .none,
1533 .subsystem = options.subsystem,
14941534 .dump_snapshot = options.enable_link_snapshots,
14951535 };
14961536 errdefer coff.deinit();
......@@ -1533,6 +1573,9 @@ pub fn deinit(coff: *Coff) void {
15331573 coff.input_archive_members.deinit(gpa);
15341574 coff.input_archive_symbols.deinit(gpa);
15351575 coff.input_archive_symbol_indices.deinit(gpa);
1576 for (coff.pending_default_libs.items) |l| gpa.free(l.path);
1577 coff.pending_default_libs.deinit(gpa);
1578 coff.alternate_names.deinit(gpa);
15361579 coff.input_objects.deinit(gpa);
15371580 coff.input_symbols.deinit(gpa);
15381581 coff.input_sections.deinit(gpa);
......@@ -1543,6 +1586,7 @@ pub fn deinit(coff: *Coff) void {
15431586 coff.object_section_table.deinit(gpa);
15441587 coff.symbols.deinit(gpa);
15451588 coff.globals.deinit(gpa);
1589 coff.late_globals.deinit(gpa);
15461590 coff.navs.deinit(gpa);
15471591 coff.uavs.deinit(gpa);
15481592 for (&coff.lazy.values) |*lazy| lazy.map.deinit(gpa);
......@@ -1579,8 +1623,12 @@ fn isObj(coff: *const Coff) bool {
15791623 return coff.base.comp.config.output_mode == .Obj;
15801624}
15811625
1582fn zcuSectionParent(coff: *Coff) MappedFile.Node.Index {
1583 assert(coff.base.comp.zcu != null);
1626fn hasCoffHeader(coff: *const Coff) bool {
1627 return coff.base.comp.zcu != null or !coff.isArchive();
1628}
1629
1630fn sectionParent(coff: *Coff) MappedFile.Node.Index {
1631 assert(coff.hasCoffHeader());
15841632 return if (coff.isArchive()) Node.known.zcu_member else Node.known.file;
15851633}
15861634
......@@ -1611,7 +1659,7 @@ fn initHeaders(
16111659 0;
16121660
16131661 var expected_nodes_len: usize = Node.known_count;
1614 if (comp.zcu != null) {
1662 if (coff.hasCoffHeader()) {
16151663 // Sections
16161664 expected_nodes_len += 3;
16171665
......@@ -1663,7 +1711,7 @@ fn initHeaders(
16631711 @memcpy(signature_slice, archive_signature);
16641712 }
16651713
1666 const opt_zcu_coff_parent_ni = if (is_archive) parent: {
1714 const opt_coff_parent_ni = if (is_archive) parent: {
16671715 const initial_member_count = Member.Index.known_count + @intFromBool(comp.zcu != null);
16681716 try coff.members.ensureTotalCapacity(gpa, initial_member_count);
16691717
......@@ -1709,10 +1757,10 @@ fn initHeaders(
17091757 if (placeholder_ni == Node.known.zcu_member) break;
17101758 }
17111759
1712 break :parent if (comp.zcu != null) Node.known.header else null;
1760 break :parent Node.known.header;
17131761 };
17141762
1715 const zcu_coff_parent_ni = opt_zcu_coff_parent_ni orelse {
1763 const coff_parent_ni = opt_coff_parent_ni orelse {
17161764 // If we're not generating any code, no more known nodes are used
17171765 while (coff.nodes.len < Node.known_count) {
17181766 _ = try coff.mf.addNodeAfter(gpa, Node.known.header, .{});
......@@ -1723,7 +1771,7 @@ fn initHeaders(
17231771 };
17241772
17251773 const coff_header_ni = Node.known.coff_header;
1726 assert(coff_header_ni == try coff.mf.addLastChildNode(gpa, zcu_coff_parent_ni, .{
1774 assert(coff_header_ni == try coff.mf.addLastChildNode(gpa, coff_parent_ni, .{
17271775 .size = @sizeOf(std.coff.Header),
17281776 .alignment = .@"4",
17291777 .fixed = true,
......@@ -1751,7 +1799,7 @@ fn initHeaders(
17511799 }
17521800
17531801 const optional_header_ni = Node.known.optional_header;
1754 assert(optional_header_ni == try coff.mf.addLastChildNode(gpa, zcu_coff_parent_ni, .{
1802 assert(optional_header_ni == try coff.mf.addLastChildNode(gpa, coff_parent_ni, .{
17551803 .size = optional_header_size,
17561804 .alignment = .@"4",
17571805 .fixed = true,
......@@ -1863,7 +1911,7 @@ fn initHeaders(
18631911 }
18641912
18651913 const data_directories_ni = Node.known.data_directories;
1866 assert(data_directories_ni == try coff.mf.addLastChildNode(gpa, zcu_coff_parent_ni, .{
1914 assert(data_directories_ni == try coff.mf.addLastChildNode(gpa, coff_parent_ni, .{
18671915 .size = data_directories_size,
18681916 .alignment = .@"4",
18691917 .fixed = true,
......@@ -1879,7 +1927,7 @@ fn initHeaders(
18791927 }
18801928
18811929 const section_table_ni = Node.known.section_table;
1882 assert(section_table_ni == try coff.mf.addLastChildNode(gpa, zcu_coff_parent_ni, .{
1930 assert(section_table_ni == try coff.mf.addLastChildNode(gpa, coff_parent_ni, .{
18831931 .alignment = .@"4",
18841932 .fixed = true,
18851933 }));
......@@ -1889,14 +1937,14 @@ fn initHeaders(
18891937
18901938 if (!is_image) {
18911939 // TODO: These two nodes could be inside one movable node?
1892 coff.symbol_table.ni = try coff.mf.addLastChildNode(gpa, zcu_coff_parent_ni, .{
1940 coff.symbol_table.ni = try coff.mf.addLastChildNode(gpa, coff_parent_ni, .{
18931941 .alignment = .@"2",
18941942 .fixed = true,
18951943 .moved = true,
18961944 });
18971945 coff.nodes.appendAssumeCapacity(.symbol_table);
18981946
1899 coff.symbol_table.strings_ni = try coff.mf.addLastChildNode(gpa, zcu_coff_parent_ni, .{
1947 coff.symbol_table.strings_ni = try coff.mf.addLastChildNode(gpa, coff_parent_ni, .{
19001948 .size = @sizeOf(u32),
19011949 .fixed = true,
19021950 .resized = true,
......@@ -2030,16 +2078,19 @@ pub fn initBuiltins(coff: *Coff) !void {
20302078 const comp = coff.base.comp;
20312079 const gpa = comp.gpa;
20322080 const target = &comp.root_mod.resolved_target.result;
2081 if (coff.isImage()) {
2082 try coff.symbols.ensureUnusedCapacity(gpa, 1);
2083 try coff.globals.ensureUnusedCapacity(gpa, 1);
2084
2085 const si = try coff.globalSymbol(.{ .name = "__ImageBase", .type = .data });
2086 const sym = si.get(coff);
2087 sym.ni = Node.known.header;
2088 }
2089
20332090 if (coff.isImage() and target.isMinGW() and comp.config.link_libc) {
2034 try coff.symbols.ensureUnusedCapacity(gpa, 5);
2091 try coff.symbols.ensureUnusedCapacity(gpa, 6);
20352092 try coff.globals.ensureUnusedCapacity(gpa, 2);
2036 try coff.nodes.ensureUnusedCapacity(gpa, 3);
2037
2038 {
2039 const si = try coff.globalSymbol(.{ .name = "__ImageBase", .type = .data });
2040 const sym = si.get(coff);
2041 sym.ni = Node.known.header;
2042 }
2093 try coff.nodes.ensureUnusedCapacity(gpa, 6);
20432094
20442095 const lists: []const struct { global: []const u8, start: String, end: String } = &.{
20452096 .{ .global = "__CTOR_LIST__", .start = .@".ctors", .end = .@".ctors$ZZZ" },
......@@ -2083,7 +2134,10 @@ pub fn startProgress(coff: *Coff, prog_node: std.Progress.Node) void {
20832134 prog_node.increaseEstimatedTotalItems(3);
20842135 coff.const_prog_node = prog_node.start("Constants", coff.pending_uavs.count());
20852136 coff.synth_prog_node = prog_node.start("Synthetics", count: {
2086 var count = coff.globals.count() - coff.global_pending_index;
2137 var count =
2138 coff.globals.count() - coff.global_pending_index +
2139 coff.late_globals.items.len - coff.late_globals_pending_index;
2140
20872141 for (&coff.lazy.values) |*lazy| count += lazy.map.count() - lazy.pending_index;
20882142 break :count count;
20892143 });
......@@ -2246,7 +2300,7 @@ fn targetStore(coff: *const Coff, ptr: anytype, val: @typeInfo(@TypeOf(ptr)).poi
22462300}
22472301
22482302pub fn headerPtr(coff: *Coff) *std.coff.Header {
2249 assert(coff.base.comp.zcu != null);
2303 assert(coff.hasCoffHeader());
22502304 return @ptrCast(@alignCast(Node.known.coff_header.slice(&coff.mf)));
22512305}
22522306
......@@ -2405,6 +2459,7 @@ fn addSymbolAssumeCapacity(coff: *Coff) Symbol.Index {
24052459 .value_tag = .size,
24062460 .type = .unknown,
24072461 .dll_storage_class = .default,
2462 .weak_external_strat = undefined,
24082463 },
24092464 .loc_relocs = .none,
24102465 .target_relocs = .none,
......@@ -2509,7 +2564,6 @@ fn getOrPutGlobalSymbol(
25092564 if (!sym_gop.found_existing) {
25102565 const si = coff.addSymbolAssumeCapacity();
25112566 const sym = si.get(coff);
2512 sym.setValue(.{ .alias_si = .null });
25132567 sym.gmi = .wrap(@intCast(sym_gop.index));
25142568 sym.flags.type = opts.type;
25152569 sym.flags.dll_storage_class = opts.dll_storage_class;
......@@ -2982,7 +3036,7 @@ fn flushInputSection(coff: *Coff, isi: Node.InputSection.Index) !void {
29823036}
29833037
29843038fn addSection(coff: *Coff, name: String, flags: std.coff.SectionHeader.Flags) !Symbol.Index {
2985 assert(coff.base.comp.zcu != null);
3039 assert(coff.hasCoffHeader());
29863040
29873041 const gpa = coff.base.comp.gpa;
29883042 try coff.nodes.ensureUnusedCapacity(gpa, 1);
......@@ -3000,7 +3054,7 @@ fn addSection(coff: *Coff, name: String, flags: std.coff.SectionHeader.Flags) !S
30003054 @sizeOf(std.coff.SectionHeader) * section_table_len,
30013055 );
30023056
3003 const ni = try coff.mf.addLastChildNode(gpa, coff.zcuSectionParent(), .{
3057 const ni = try coff.mf.addLastChildNode(gpa, coff.sectionParent(), .{
30043058 .alignment = coff.mf.flags.block_size,
30053059 .moved = true,
30063060 .bubbles_moved = false,
......@@ -3185,7 +3239,7 @@ fn objectSectionMapIndex(
31853239
31863240 const object_section_gop = try coff.object_section_table.getOrPut(gpa, name);
31873241 const osmi: Node.ObjectSectionMapIndex = @enumFromInt(object_section_gop.index);
3188 const sym = if (!object_section_gop.found_existing) sn: {
3242 const sym = if (!object_section_gop.found_existing) sym: {
31893243 try coff.ensureUnusedStringCapacity(name_slice.len);
31903244 const parent_name = coff.getOrPutStringAssumeCapacity(coff.objectSectionParentName(name_slice));
31913245 const parent = (try coff.pseudoSectionMapIndex(parent_name, alignment, effective_attributes)).symbol(coff);
......@@ -3222,7 +3276,7 @@ fn objectSectionMapIndex(
32223276 assert(sym.loc_relocs == .none);
32233277 sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
32243278 coff.nodes.appendAssumeCapacity(.{ .object_section = osmi });
3225 break :sn sym;
3279 break :sym sym;
32263280 } else object_section_gop.value_ptr.get(coff);
32273281
32283282 const parent_ni = sym.ni.parent(&coff.mf);
......@@ -3334,7 +3388,7 @@ pub fn addReloc(
33343388 const new_size = new_num_relocations * std.coff.Relocation.sizeOf();
33353389 if (section.relocation_table_ni == .none) {
33363390 try coff.nodes.ensureUnusedCapacity(gpa, 1);
3337 section.relocation_table_ni = try coff.mf.addLastChildNode(gpa, coff.zcuSectionParent(), .{
3391 section.relocation_table_ni = try coff.mf.addLastChildNode(gpa, coff.sectionParent(), .{
33383392 .size = new_size,
33393393 .alignment = .@"2",
33403394 .moved = true,
......@@ -3637,13 +3691,6 @@ fn loadObject(
36373691 section_i,
36383692 section_name_slice,
36393693 });
3640
3641 if (section.header.flags.LNK_REMOVE or
3642 section.header.flags.MEM_DISCARDABLE)
3643 {
3644 // TODO: Convert .debug$* sections into PDB
3645 continue;
3646 }
36473694 }
36483695
36493696 break :sections sections;
......@@ -3684,14 +3731,16 @@ fn loadObject(
36843731 section: u32,
36853732 // Offset within the section
36863733 static: u32,
3687 // If section is defined, the symbol size. Otherwise offset within the section.
3734 // If section is undefined, the symbol size. Otherwise offset within the section.
36883735 external: u32,
3689 // The index of the target symbol of this alias
3736 // The index of the target symbol of this weak external
36903737 weak_external: u32,
3738 // Trails .weak_external
3739 weak_external_aux: WeakExternalStrat,
36913740 },
36923741 section_number: Symbol.SectionNumber,
36933742 si: Symbol.Index,
3694 // The index of the weak_external that targest this symbol
3743 // If a weak external targets this symbol, the index of the weak external
36953744 weak_external_psi: PendingSymbolIndex,
36963745 };
36973746
......@@ -3741,11 +3790,12 @@ fn loadObject(
37413790
37423791 const psi: PendingSymbolIndex = .wrap(@intCast(pending_symbols.count()));
37433792 const section_number: Symbol.SectionNumber = @enumFromInt(@intFromEnum(symbol.section_number));
3744 const opt_value: ?@FieldType(PendingSymbol, "value") = pending_symbol: switch (symbol.storage_class) {
3793
3794 const values: []const @FieldType(PendingSymbol, "value") = pending_symbols: switch (symbol.storage_class) {
37453795 .STATIC, .LABEL => |storage_class| switch (section_number) {
37463796 // TODO: Do we need to do anything with @feat.00?
37473797 // https://llvm.org/doxygen/namespacellvm_1_1COFF.html#aeffa16735e18df727a173beaf748c392
3748 .UNDEFINED, .DEBUG, .ABSOLUTE => null,
3798 .UNDEFINED, .DEBUG, .ABSOLUTE => &.{},
37493799 else => |sn| {
37503800 const section = &sections[sn.toIndex()];
37513801
......@@ -3803,10 +3853,10 @@ fn loadObject(
38033853 section.psi = psi;
38043854 }
38053855
3806 break :pending_symbol if (is_section)
3856 break :pending_symbols &.{if (is_section)
38073857 .{ .section = section.header.size_of_raw_data }
38083858 else
3809 .{ .static = symbol.value };
3859 .{ .static = symbol.value }};
38103860 },
38113861 },
38123862 .WEAK_EXTERNAL => switch (symbol.section_number) {
......@@ -3830,16 +3880,12 @@ fn loadObject(
38303880 .{ weak_external.tag_index, symbol_i },
38313881 );
38323882
3833 break :pending_symbol switch (weak_external.flag) {
3834 .SEARCH_NOLIBRARY,
3835 .SEARCH_LIBRARY,
3836 => return diags.failParse(
3837 path,
3838 "TODO handle weak external characteristic 0x{x} for symbol 0x{x}",
3839 .{ weak_external.flag, symbol_i },
3840 ),
3841 .SEARCH_ALIAS => .{ .weak_external = weak_external.tag_index },
3842 else => return diags.failParse(
3883 break :pending_symbols switch (weak_external.flag) {
3884 else => |flag| &.{
3885 .{ .weak_external = weak_external.tag_index },
3886 .{ .weak_external_aux = WeakExternalStrat.fromFlag(flag) },
3887 },
3888 _ => return diags.failParse(
38433889 path,
38443890 "encountered unknown weak external characteristic 0x{x} for symbol 0x{x}",
38453891 .{ weak_external.flag, symbol_i },
......@@ -3853,7 +3899,7 @@ fn loadObject(
38533899 ),
38543900 },
38553901 .EXTERNAL => switch (section_number) {
3856 .UNDEFINED => .{ .external = symbol.value },
3902 .UNDEFINED => &.{.{ .external = symbol.value }},
38573903 .ABSOLUTE => return diags.failParse(
38583904 path,
38593905 "TODO unhandled external absolute symbol 0x{x}: '{s}'",
......@@ -3864,7 +3910,7 @@ fn loadObject(
38643910 "unexpected external symbol 0x{x} in DEBUG section: '{s}'",
38653911 .{ symbol_i, name },
38663912 ),
3867 else => .{ .external = symbol.value },
3913 else => &.{.{ .external = symbol.value }},
38683914 },
38693915 .FILE => {
38703916 if (!std.mem.eql(u8, name, ".file"))
......@@ -3878,7 +3924,7 @@ fn loadObject(
38783924 @memcpy(std.mem.asBytes(&file)[0..symbol_size], aux_symbols[0..symbol_size]);
38793925
38803926 input.source_name = (try coff.getOrPutString(file.getFileName())).toOptional();
3881 break :pending_symbol null;
3927 break :pending_symbols &.{};
38823928 },
38833929 else => |storage_class| return diags.failParse(
38843930 path,
......@@ -3887,10 +3933,13 @@ fn loadObject(
38873933 ),
38883934 };
38893935
3890 if (opt_value) |value| {
3936 for (values, 0..) |value, i| {
38913937 switch (value) {
38923938 .section => {},
3893 .static, .external, .weak_external => {
3939 .static,
3940 .external,
3941 .weak_external,
3942 => {
38943943 num_global_symbols += 1;
38953944 if (section_number.hasIndex()) {
38963945 const section = &sections[section_number.toIndex()];
......@@ -3899,10 +3948,11 @@ fn loadObject(
38993948 section.comdat_psi = psi;
39003949 }
39013950 },
3951 .weak_external_aux => {},
39023952 }
39033953
39043954 const symbol_name = coff.getOrPutStringAssumeCapacity(name);
3905 pending_symbols.putAssumeCapacity(symbol_i, .{
3955 pending_symbols.putAssumeCapacity(symbol_i + @as(u32, @intCast(i)), .{
39063956 .name = symbol_name,
39073957 .value = value,
39083958 .section_number = section_number,
......@@ -3917,6 +3967,7 @@ fn loadObject(
39173967 if (section.header.flags.LNK_INFO) {
39183968 if (std.mem.eql(u8, &section.header.name, ".drectve")) {
39193969 try fr.seekTo(fl.offset + section.header.pointer_to_raw_data);
3970 // TODO: Don't really want an additional buffer here, but want to limit to size_of_raw_data
39203971 var buf: [128]u8 = undefined;
39213972 var section_r = r.limited(.limited(section.header.size_of_raw_data), &buf);
39223973 while (section_r.interface.takeDelimiter(' ') catch |err| switch (err) {
......@@ -3926,9 +3977,60 @@ fn loadObject(
39263977 // Microsoft tools emit 3 space characters into this section even with /Zl
39273978 if (arg.len == 0) continue;
39283979
3929 if (std.mem.cutPrefix(u8, arg, "-exclude-symbols:")) |rest| {
3930 // TODO: When implementing mingw auto-exports, use this to not export this symbol
3931 _ = rest;
3980 if (std.ascii.startsWithIgnoreCase(arg, "-exclude-symbols:")) {
3981 // TODO: When implementing mingw auto-exports (if at all?), use this to not export this symbol
3982 } else if (std.ascii.startsWithIgnoreCase(arg, "/include:")) {
3983 _ = try coff.globalSymbol(.{ .name = arg["/include:".len..] });
3984 } else if (std.ascii.startsWithIgnoreCase(arg, "/alternatename:")) {
3985 var split = std.mem.splitScalar(u8, arg["/alternatename:".len..], '=');
3986 const orig = split.first();
3987 const alt = split.next() orelse
3988 return diags.failParse(path, "malformed .drectve argument: '{s}'", .{arg});
3989
3990 try coff.ensureManyUnusedStringCapacity(2, orig.len + alt.len + 2);
3991 const orig_str = coff.getOrPutStringAssumeCapacity(orig);
3992 const alt_str = coff.getOrPutStringAssumeCapacity(alt);
3993 const gop = try coff.alternate_names.getOrPut(gpa, orig_str);
3994 if (!gop.found_existing) {
3995 log.debug("alternateName({s}={s})", .{ orig, alt });
3996 gop.value_ptr.* = alt_str;
3997 } else if (gop.value_ptr.* != alt_str)
3998 return diags.failParse(
3999 path,
4000 "conflicting /alternatename .drectve arguments: first seen as {s}={s}, now seen as {s}={s}",
4001 .{ orig, gop.value_ptr.toSlice(coff), orig, alt },
4002 );
4003 } else if (std.ascii.startsWithIgnoreCase(arg, "/guardsym:")) {
4004 // TODO: https://learn.microsoft.com/en-us/windows/win32/secbp/pe-metadata
4005 } else if (std.ascii.startsWithIgnoreCase(arg, "/merge:")) {
4006 var split = std.mem.splitScalar(u8, arg["/merge:".len..], '=');
4007 const from = split.first();
4008 const to = split.next() orelse
4009 return diags.failParse(path, "malformed .drectve argument: '{s}'", .{arg});
4010
4011 // TODO: Override the parent selection for generated sections below
4012 _ = from;
4013 _ = to;
4014 } else if (std.ascii.startsWithIgnoreCase(arg, "/disallowlib:")) {
4015 const lib_name = arg["/disallowlib:".len..];
4016 // TODO: Track these and issue error in prelink if any match
4017 _ = lib_name;
4018 } else if (std.ascii.startsWithIgnoreCase(arg, "/defaultlib:")) {
4019 const lib_path = arg["/defaultlib:".len..];
4020 const trim = std.mem.trim(u8, lib_path, "\"");
4021 if (lib_path.len == trim.len or lib_path.len - 2 == trim.len) {
4022 if (!comp.config.link_libc or comp.libc_installation == null)
4023 return diags.failParse(path, "encountered /DEFAULTLIB .drectve argument when libc was not available: {s}", .{arg});
4024
4025 (try coff.pending_default_libs.addOne(gpa)).* = .{
4026 .path = try gpa.dupe(u8, lib_path),
4027 .ioi = ioi,
4028 };
4029 } else return diags.failParse(
4030 path,
4031 "malformed /DEFAULTLIB .drectve argument: `{s}`",
4032 .{arg},
4033 );
39324034 } else return diags.failParse(path, "unsupported argument in .drectve section: `{s}`", .{arg});
39334035 }
39344036 }
......@@ -3940,6 +4042,7 @@ fn loadObject(
39404042 if (section.header.flags.LNK_REMOVE or
39414043 section.header.flags.MEM_DISCARDABLE)
39424044 {
4045 // TODO: Convert .debug$* sections into PDB
39434046 section.comdat_result = .skip;
39444047 continue;
39454048 }
......@@ -3973,6 +4076,7 @@ fn loadObject(
39734076 const symbol = &pending_symbols.values()[psi];
39744077 const si = existing: switch (symbol.value) {
39754078 .weak_external => unreachable,
4079 .weak_external_aux => unreachable,
39764080 .static => break :comdat .include,
39774081 .section => {
39784082 assert(section.comdat_psi == .none);
......@@ -4145,14 +4249,21 @@ fn loadObject(
41454249 }
41464250
41474251 for (pending_symbols.values(), pending_symbols.keys(), 0..) |*symbol, index, i| {
4148 defer log.debug("addInputSymbol({s}, 0x{x}, {t}=0x{x}, {d}) = {d}@{d}", .{
4252 switch (symbol.value) {
4253 .weak_external_aux => continue,
4254 else => {},
4255 }
4256
4257 defer log.debug("addInputSymbol({s}, 0x{x}, {t}=0x{x}, {d}) = n{d} {d}@{d}", .{
41494258 symbol.name.toSlice(coff),
41504259 index,
41514260 symbol.value,
4152 symbol.section_number,
41534261 switch (symbol.value) {
4262 .weak_external_aux => unreachable,
41544263 inline else => |v| v,
41554264 },
4265 symbol.section_number,
4266 symbol.si.get(coff).ni,
41564267 symbol.si,
41574268 symbol.si.get(coff).section_number,
41584269 });
......@@ -4161,34 +4272,50 @@ fn loadObject(
41614272 .UNDEFINED => switch (symbol.value) {
41624273 .section,
41634274 .static,
4275 .weak_external_aux,
41644276 => unreachable,
4165 .external,
4166 .weak_external,
4167 => |value, tag| {
4277 .external => {
4278 if (symbol.weak_external_psi.unwrap()) |weak_external_i| {
4279 // If the alias itself is an undef external, we need to wait until flushing the weak
4280 // external global before creating a global for the alias, as another input
4281 // could still provide the weak external.
4282 const weak_sym = pending_symbols.values()[weak_external_i].si.get(coff);
4283 weak_sym.setValue(.{ .alias_name = symbol.name });
4284 weak_sym.flags.weak_external_strat = pending_symbols.values()[weak_external_i + 1].value.weak_external_aux;
4285 }
4286
4287 // Deferred until referenced by a reloc in this object.
4288 // vcruntime.lib defines symbols like this (ie. memcpy_$fo$) that are not referenced
4289 continue;
4290 },
4291 .weak_external => |alias_index| {
41684292 const global_gop = try coff.getOrPutGlobalSymbol(.{ .name = symbol.name.toSlice(coff) });
41694293 symbol.si = global_gop.value_ptr.*;
41704294 if (!global_gop.found_existing or symbol.si.get(coff).ni == .none) {
41714295 const sym = symbol.si.get(coff);
4172 if (tag == .external) {
4173 sym.setValue(.{ .size = @max(sym.size(), value) });
4296 const alias = pending_symbols.getPtr(alias_index) orelse
4297 return diags.failParse(
4298 path,
4299 "weak external 0x{x} {s}{f} targets unknown symbol index 0x{x}",
4300 .{
4301 index,
4302 symbol.name.toSlice(coff),
4303 fmtMemberNameString(member_name),
4304 alias_index,
4305 },
4306 );
4307
4308 if (alias.si == .null and alias_index > index) {
4309 // Resolve this once we see alias
4310 alias.weak_external_psi = .wrap(@intCast(i));
41744311 } else {
4175 const alias = pending_symbols.getPtr(value) orelse
4176 return diags.failParse(
4177 path,
4178 "weak external 0x{x} {s}{f} targets unknown symbol index 0x{x}",
4179 .{
4180 index,
4181 symbol.name.toSlice(coff),
4182 fmtMemberNameString(member_name),
4183 value,
4184 },
4185 );
4186
4187 if (alias.si == .null) {
4188 alias.weak_external_psi = .wrap(@intCast(i));
4189 } else {
4190 sym.setValue(.{ .alias_si = alias.si });
4191 }
4312 sym.setValue(if (alias.si == .null) .{
4313 // See .external branch above
4314 .alias_name = alias.name,
4315 } else .{
4316 .alias_si = alias.si,
4317 });
4318 sym.flags.weak_external_strat = pending_symbols.values()[i + 1].value.weak_external_aux;
41924319 }
41934320 }
41944321
......@@ -4217,13 +4344,17 @@ fn loadObject(
42174344 if (global_gop.found_existing and sym.ni != .none)
42184345 return coff.failMultipleDefinitions(path, member_name, symbol.name, index, global_gop.value_ptr.*, .none);
42194346 },
4220 .weak_external => unreachable,
4347 .weak_external,
4348 .weak_external_aux,
4349 => unreachable,
42214350 }
42224351 }
42234352
42244353 if (symbol.weak_external_psi.unwrap()) |weak_external_i| {
42254354 assert(symbol.si != .null);
4226 pending_symbols.values()[weak_external_i].si.get(coff).setValue(.{ .alias_si = symbol.si });
4355 const weak_sym = pending_symbols.values()[weak_external_i].si.get(coff);
4356 weak_sym.setValue(.{ .alias_si = symbol.si });
4357 weak_sym.flags.weak_external_strat = pending_symbols.values()[weak_external_i + 1].value.weak_external_aux;
42274358 }
42284359
42294360 if (section.si != symbol.si) {
......@@ -4237,7 +4368,9 @@ fn loadObject(
42374368 .UNDEFINED, .ABSOLUTE, .DEBUG => unreachable,
42384369 else => .{ .node_offset = v },
42394370 },
4240 .weak_external => unreachable,
4371 .weak_external,
4372 .weak_external_aux,
4373 => unreachable,
42414374 });
42424375 sym.section_number = section.si.get(coff).section_number;
42434376 }
......@@ -4260,14 +4393,34 @@ fn loadObject(
42604393 if (target_endian != native_endian)
42614394 std.mem.byteSwapAllFields(std.coff.Relocation, &reloc);
42624395
4263 // TODO: This error should show member name for lib
4264 const symbol = pending_symbols.get(reloc.symbol_table_index) orelse
4396 const symbol = pending_symbols.getPtr(reloc.symbol_table_index) orelse
42654397 return diags.failParse(
42664398 path,
4267 "relocation 0x{x} in section '{s}'{f} targets invalid symbol index 0x{x}",
4268 .{ reloc_i, section.name.toSlice(coff), fmtMemberNameString(member_name), reloc.symbol_table_index },
4399 "relocation 0x{x} in section '{s}' of {f}{f} targets invalid symbol index 0x{x}",
4400 .{
4401 reloc_i,
4402 section.name.toSlice(coff),
4403 path.fmtEscapeString(),
4404 fmtMemberNameString(member_name),
4405 reloc.symbol_table_index,
4406 },
42694407 );
42704408
4409 if (symbol.si == .null) {
4410 assert(symbol.section_number == .UNDEFINED);
4411 switch (symbol.value) {
4412 .external => |size| {
4413 const global_gop = try coff.getOrPutGlobalSymbol(.{ .name = symbol.name.toSlice(coff) });
4414 symbol.si = global_gop.value_ptr.*;
4415 if (!global_gop.found_existing or symbol.si.get(coff).ni == .none) {
4416 const sym = symbol.si.get(coff);
4417 sym.setValue(.{ .size = @max(sym.size(), size) });
4418 }
4419 },
4420 else => unreachable,
4421 }
4422 }
4423
42714424 assert(symbol.si != .null);
42724425 try coff.addReloc(
42734426 section.si,
......@@ -4299,7 +4452,7 @@ fn loadObject(
42994452 var prev_sn: Symbol.SectionNumber = .DEBUG;
43004453 var include_section = false;
43014454 for (pending_symbols.values()) |symbol| {
4302 // The symbol may have not been included, or it's an undefined external
4455 // The symbol may have not been included, or it's an undefined external / aux
43034456 if (symbol.si == .null or symbol.si.get(coff).ni == .none) continue;
43044457
43054458 if (prev_sn != symbol.section_number) {
......@@ -4731,6 +4884,71 @@ pub fn prelink(coff: *Coff, prog_node: std.Progress.Node) link.Error!void {
47314884 _ = prog_node;
47324885 log.debug("prelink()", .{});
47334886
4887 if (coff.pending_default_libs.items.len > 0) {
4888 // Libs provided by /DEFAULTLIB arguments in objects are searched after all other inputs
4889 const base = coff.base;
4890 const comp = base.comp;
4891 const gpa = comp.gpa;
4892 const arena = comp.arena;
4893 const target = &comp.root_mod.resolved_target.result;
4894
4895 defer {
4896 for (coff.pending_default_libs.items) |l| gpa.free(l.path);
4897 coff.pending_default_libs.clearAndFree(gpa);
4898 }
4899
4900 assert(comp.config.link_libc);
4901 const libc_installation = comp.libc_installation.?;
4902 const all_paths: [3]?[]const u8 = .{
4903 libc_installation.crt_dir,
4904 libc_installation.msvc_lib_dir,
4905 libc_installation.kernel32_lib_dir,
4906 };
4907 const search_paths = all_paths[0..if (target.abi == .msvc or target.abi == .itanium) 3 else 1];
4908 lib: for (coff.pending_default_libs.items) |lib| {
4909 if (!std.mem.eql(u8, std.fs.path.extension(lib.path), ".lib"))
4910 return comp.link_diags.failParse(
4911 lib.ioi.path(coff),
4912 "/DEFAULTLIB library '{s}' had unexpected extension",
4913 .{lib.path},
4914 );
4915
4916 log.debug("loadDefaultLib({s}, {f})", .{ lib.path, lib.ioi.path(coff) });
4917 for (search_paths) |opt_path| if (opt_path) |search_path| {
4918 const lib_path = try Path.initCwd(search_path).join(arena, lib.path);
4919 const archive = link.openObject(comp.io, lib_path, false, false) catch |err| switch (err) {
4920 error.FileNotFound => {
4921 arena.free(lib_path.sub_path);
4922 continue;
4923 },
4924 else => |e| return comp.link_diags.failParse(
4925 lib.ioi.path(coff),
4926 "error opening /DEFAULTLIB library '{s}': {t}",
4927 .{ lib.path, e },
4928 ),
4929 };
4930 errdefer archive.file.close(comp.io);
4931
4932 coff.loadInput(.{ .archive = archive }) catch |err| switch (err) {
4933 error.LinkFailure => return,
4934 else => |e| return comp.link_diags.failParse(
4935 lib.ioi.path(coff),
4936 "error loading /DEFAULTLIB library '{s}': {t}",
4937 .{ lib.path, e },
4938 ),
4939 };
4940
4941 break :lib;
4942 };
4943
4944 return comp.link_diags.failParse(
4945 lib.ioi.path(coff),
4946 "/DEFAULTLIB library '{s}' was not found",
4947 .{lib.path},
4948 );
4949 }
4950 }
4951
47344952 coff.inputs_complete = true;
47354953}
47364954
......@@ -5143,6 +5361,11 @@ pub fn flush(
51435361) !void {
51445362 _ = arena;
51455363 _ = prog_node;
5364
5365 // TODO: When https://github.com/ziglang/zig/issues/23617 is in,
5366 // this should be set after updateExports instead
5367 coff.exports_complete = true;
5368
51465369 while (try coff.idle(tid)) {}
51475370
51485371 if (coff.isImage())
......@@ -5221,6 +5444,22 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
52215444 }) coff.global_pending_index += 1;
52225445 break :task;
52235446 }
5447 if (coff.exports_complete and coff.late_globals_pending_index < coff.late_globals.items.len) {
5448 const gmi: Node.GlobalMapIndex = coff.late_globals.items[coff.late_globals_pending_index];
5449 const sub_prog_node = coff.synth_prog_node.start(
5450 gmi.globalName(coff).name.toSlice(coff),
5451 0,
5452 );
5453 defer sub_prog_node.end();
5454 if (coff.flushGlobal(gmi) catch |err| switch (err) {
5455 error.OutOfMemory => |e| return e,
5456 else => |e| return comp.link_diags.fail(
5457 "linker failed to lower constant: {t}",
5458 .{e},
5459 ),
5460 }) coff.late_globals_pending_index += 1;
5461 break :task;
5462 }
52245463 var lazy_it = coff.lazy.iterator();
52255464 while (lazy_it.next()) |lazy| if (lazy.value.pending_index < lazy.value.map.count()) {
52265465 const pt: Zcu.PerThread = .{ .zcu = comp.zcu.?, .tid = tid };
......@@ -5357,6 +5596,7 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
53575596 if (coff.pending_uavs.count() > 0) return true;
53585597 if (coff.pending_input != null) return true;
53595598 if (coff.inputs_complete and coff.globals.count() > coff.global_pending_index) return true;
5599 if (coff.exports_complete and coff.late_globals.items.len > coff.late_globals_pending_index) return true;
53605600 for (&coff.lazy.values) |lazy| if (lazy.map.count() > lazy.pending_index) return true;
53615601 if (coff.symbol_table.pending.count() > 0) return true;
53625602 if (coff.input_sections.items.len > coff.input_section_pending_index) return true;
......@@ -5504,10 +5744,11 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
55045744 const gn = gmi.globalName(coff);
55055745 const si = gmi.symbol(coff);
55065746 const sym = si.get(coff);
5747 const is_late = gmi.unwrap().? < coff.global_pending_index;
55075748
55085749 log.debug(
5509 "flushGlobal({s}, {?s}) = {d} ({d})",
5510 .{ gn.name.toSlice(coff), gn.lib_name.toSlice(coff), si, sym.ni },
5750 "flushGlobal({s}, {?s}, {}) = {d} ({d})",
5751 .{ gn.name.toSlice(coff), gn.lib_name.toSlice(coff), is_late, si, sym.ni },
55115752 );
55125753
55135754 if (!coff.isImage()) {
......@@ -5521,16 +5762,6 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
55215762 return true;
55225763 }
55235764
5524 // TODO: Only do this if actually referenced? Might have to do on-demand?
5525 {
5526 // Resolve unresolved .WEAK_EXTERNAL symbols to their aliases
5527 const alias_si = sym.weakAlias();
5528 if (alias_si != .null) {
5529 try coff.aliasGlobal(gmi, alias_si);
5530 return true;
5531 }
5532 }
5533
55345765 const Import = struct {
55355766 lib_name: String,
55365767 name: String.Optional,
......@@ -5555,9 +5786,41 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
55555786 break :name .{ coff.getOrPutStringAssumeCapacity(name), true };
55565787 };
55575788
5558 // TODO: Try to search for __imp_ even when !is_imp, we want to not use thunks if we can
5789 const opt_alt_search_name = coff.alternate_names.get(search_name);
5790 const search_libs = if (is_late) switch (sym.flags.value_tag) {
5791 .alias_si, .alias_name => switch (sym.flags.weak_external_strat) {
5792 .no_library => false,
5793 .library,
5794 .alias,
5795 => true,
5796 .anti_dependency => return comp.link_diags.fail(
5797 // TODO: Figure out what the purpose of this is
5798 "TODO support anti_dependency weak external: {s}",
5799 .{gn.name.toSlice(coff)},
5800 ),
5801 },
5802 else => true,
5803 } else search_libs: {
5804 if (switch (sym.flags.value_tag) {
5805 .alias_si, .alias_name => true,
5806 else => opt_alt_search_name != null,
5807 }) {
5808 // We need to wait until all exports are known before resolving these
5809 coff.synth_prog_node.increaseEstimatedTotalItems(1);
5810 (try coff.late_globals.addOne(gpa)).* = gmi;
5811 return true;
5812 }
5813
5814 break :search_libs true;
5815 };
5816
5817 const opt_indices_lists: []const ?InputArchive.SearchList = if (search_libs) &.{
5818 coff.input_archive_symbol_indices.get(search_name),
5819 if (opt_alt_search_name) |alt| coff.input_archive_symbol_indices.get(alt) else null,
5820 } else &.{};
55595821
5560 if (coff.input_archive_symbol_indices.get(search_name)) |indices_list| {
5822 for (opt_indices_lists) |opt_indices_list| {
5823 const indices_list = opt_indices_list orelse continue;
55615824 var iter: InputArchive.Member.Symbol.Index = indices_list.first;
55625825 while (true) {
55635826 const archive_sym = &coff.input_archive_symbols.items[@intFromEnum(iter)];
......@@ -5631,6 +5894,32 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
56315894 }
56325895 }
56335896
5897 switch (sym.flags.value_tag) {
5898 .alias_si => {
5899 assert(is_late);
5900 try coff.aliasGlobal(gmi, sym.value.alias_si);
5901 return true;
5902 },
5903 .alias_name => {
5904 assert(is_late);
5905 // Convert an unresolved weak external that itself refers to an undef external
5906 // into a (possibly new) global, so it can be resolved separately.
5907 const alias_gop = try coff.getOrPutGlobalSymbol(.{ .name = sym.value.alias_name.toSlice(coff) });
5908 try coff.aliasGlobal(gmi, alias_gop.value_ptr.*);
5909 return true;
5910 },
5911 else => {},
5912 }
5913
5914 // If there was an object that had the alternate name, we've attempted to load it
5915 if (opt_alt_search_name) |alt_search_name| {
5916 assert(is_late);
5917 if (coff.globals.get(.{ .name = alt_search_name, .lib_name = .none })) |alias_si| {
5918 try coff.aliasGlobal(gmi, alias_si);
5919 return true;
5920 }
5921 }
5922
56345923 // Allow importing symbols with no implib entry, if a lib_name was specified.
56355924 // This is necessary for certain ntdll symbols, such as LdrRegisterDllNotification,
56365925 // which are not in the implib.