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");...@@ -19,6 +19,7 @@ const Value = @import("../Value.zig");
19const Zcu = @import("../Zcu.zig");19const Zcu = @import("../Zcu.zig");
20const ModuleDefinition = @import("../libs/mingw/def.zig").ModuleDefinition;20const ModuleDefinition = @import("../libs/mingw/def.zig").ModuleDefinition;
21const implib = @import("../libs/mingw/implib.zig");21const implib = @import("../libs/mingw/implib.zig");
22const Path = std.Build.Cache.Path;
2223
23base: link.File,24base: link.File,
24mf: MappedFile,25mf: MappedFile,
...@@ -35,16 +36,19 @@ inputs: std.ArrayHashMapUnmanaged(std.Build.Cache.Path, void, std.Build.Cache.Pa...@@ -35,16 +36,19 @@ inputs: std.ArrayHashMapUnmanaged(std.Build.Cache.Path, void, std.Build.Cache.Pa
35input_archives: std.ArrayList(InputArchive),36input_archives: std.ArrayList(InputArchive),
36input_archive_members: std.ArrayList(InputArchive.Member),37input_archive_members: std.ArrayList(InputArchive.Member),
37input_archive_symbols: std.ArrayList(InputArchive.Member.Symbol),38input_archive_symbols: std.ArrayList(InputArchive.Member.Symbol),
38input_archive_symbol_indices: std.AutoArrayHashMapUnmanaged(String, struct {39input_archive_symbol_indices: std.AutoArrayHashMapUnmanaged(String, InputArchive.SearchList),
39 first: InputArchive.Member.Symbol.Index,
40 last: InputArchive.Member.Symbol.Index,
41}),
42pending_input: ?InputArchive.Member.Index,40pending_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),
43input_objects: std.ArrayList(InputObject),46input_objects: std.ArrayList(InputObject),
44input_symbols: std.ArrayList(Symbol.Index),47input_symbols: std.ArrayList(Symbol.Index),
45input_sections: std.ArrayList(Node.InputSection),48input_sections: std.ArrayList(Node.InputSection),
46input_section_pending_index: u32,49input_section_pending_index: u32,
47inputs_complete: bool,50inputs_complete: bool,
51exports_complete: bool,
48strings: std.HashMapUnmanaged(52strings: std.HashMapUnmanaged(
49 u32,53 u32,
50 void,54 void,
...@@ -58,6 +62,8 @@ object_section_table: std.array_hash_map.Auto(String, Symbol.Index),...@@ -58,6 +62,8 @@ object_section_table: std.array_hash_map.Auto(String, Symbol.Index),
58symbols: std.ArrayList(Symbol),62symbols: std.ArrayList(Symbol),
59globals: std.array_hash_map.Auto(GlobalName, Symbol.Index),63globals: std.array_hash_map.Auto(GlobalName, Symbol.Index),
60global_pending_index: u32,64global_pending_index: u32,
65late_globals: std.ArrayList(Node.GlobalMapIndex),
66late_globals_pending_index: u32,
61navs: std.array_hash_map.Auto(InternPool.Nav.Index, Symbol.Index),67navs: std.array_hash_map.Auto(InternPool.Nav.Index, Symbol.Index),
62uavs: std.array_hash_map.Auto(InternPool.Index, Symbol.Index),68uavs: std.array_hash_map.Auto(InternPool.Index, Symbol.Index),
63lazy: std.EnumArray(link.File.LazySymbol.Kind, struct {69lazy: std.EnumArray(link.File.LazySymbol.Kind, struct {
...@@ -73,6 +79,7 @@ synth_prog_node: std.Progress.Node,...@@ -73,6 +79,7 @@ synth_prog_node: std.Progress.Node,
73symbol_prog_node: std.Progress.Node,79symbol_prog_node: std.Progress.Node,
74member_prog_node: std.Progress.Node,80member_prog_node: std.Progress.Node,
75input_prog_node: std.Progress.Node,81input_prog_node: std.Progress.Node,
82subsystem: ?std.zig.Subsystem,
76dump_snapshot: bool,83dump_snapshot: bool,
7784
78pub const default_file_alignment: u16 = 0x200;85pub const default_file_alignment: u16 = 0x200;
...@@ -435,6 +442,11 @@ pub const InputArchive = struct {...@@ -435,6 +442,11 @@ pub const InputArchive = struct {
435 };442 };
436 };443 };
437 };444 };
445
446 pub const SearchList = struct {
447 first: InputArchive.Member.Symbol.Index,
448 last: InputArchive.Member.Symbol.Index,
449 };
438};450};
439451
440pub const InputObject = struct {452pub const InputObject = struct {
...@@ -825,6 +837,23 @@ pub const Section = struct {...@@ -825,6 +837,23 @@ pub const Section = struct {
825837
826pub const GlobalName = struct { name: String, lib_name: String.Optional };838pub 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
828pub const Symbol = struct {857pub const Symbol = struct {
829 ni: MappedFile.Node.Index,858 ni: MappedFile.Node.Index,
830 rva: u32,859 rva: u32,
...@@ -833,7 +862,9 @@ pub const Symbol = struct {...@@ -833,7 +862,9 @@ pub const Symbol = struct {
833 value_tag: ValueTag,862 value_tag: ValueTag,
834 type: Symbol.Type,863 type: Symbol.Type,
835 dll_storage_class: DllStorageClass,864 dll_storage_class: DllStorageClass,
836 _: u10 = 0,865 // Only defined for .alias_si and .alias_name
866 weak_external_strat: WeakExternalStrat,
867 _: u8 = 0,
837 },868 },
838 /// Relocations contained within this symbol869 /// Relocations contained within this symbol
839 loc_relocs: Reloc.Index,870 loc_relocs: Reloc.Index,
...@@ -859,15 +890,22 @@ pub const Symbol = struct {...@@ -859,15 +890,22 @@ pub const Symbol = struct {
859 const ValueTag = enum(u2) {890 const ValueTag = enum(u2) {
860 node_offset,891 node_offset,
861 alias_si,892 alias_si,
893 alias_name,
862 size,894 size,
863 };895 };
864896
865 pub const Value = union(ValueTag) {897 pub const Value = union(ValueTag) {
866 /// The offset of the symbol within it's node898 /// The offset of the symbol within its node. Used with symbols that
899 /// don't create their own nodes: .input_section, .import_address_table
867 node_offset: u32,900 node_offset: u32,
868 /// For undefined globals, this is a weak alias901 /// This is a weak alias that can replace this symbol
869 /// that can replace this symbol, or .null if none exists902 /// Globals only.
870 alias_si: Symbol.Index,903 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,
871 /// The symbol size, or 0 if unknown909 /// The symbol size, or 0 if unknown
872 size: u32,910 size: u32,
873 };911 };
...@@ -897,10 +935,6 @@ pub const Symbol = struct {...@@ -897,10 +935,6 @@ pub const Symbol = struct {
897 };935 };
898 }936 }
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
904 pub fn size(sym: *const Symbol) u32 {938 pub fn size(sym: *const Symbol) u32 {
905 return if (sym.flags.value_tag == .size) sym.value.size else 0;939 return if (sym.flags.value_tag == .size) sym.value.size else 0;
906 }940 }
...@@ -1465,11 +1499,14 @@ fn create(...@@ -1465,11 +1499,14 @@ fn create(
1465 .input_archive_symbols = .empty,1499 .input_archive_symbols = .empty,
1466 .input_archive_symbol_indices = .empty,1500 .input_archive_symbol_indices = .empty,
1467 .pending_input = null,1501 .pending_input = null,
1502 .pending_default_libs = .empty,
1503 .alternate_names = .empty,
1468 .input_objects = .empty,1504 .input_objects = .empty,
1469 .input_symbols = .empty,1505 .input_symbols = .empty,
1470 .input_sections = .empty,1506 .input_sections = .empty,
1471 .input_section_pending_index = 0,1507 .input_section_pending_index = 0,
1472 .inputs_complete = false,1508 .inputs_complete = false,
1509 .exports_complete = false,
1473 .strings = .empty,1510 .strings = .empty,
1474 .string_bytes = .empty,1511 .string_bytes = .empty,
1475 .section_table = .empty,1512 .section_table = .empty,
...@@ -1478,6 +1515,8 @@ fn create(...@@ -1478,6 +1515,8 @@ fn create(
1478 .symbols = .empty,1515 .symbols = .empty,
1479 .globals = .empty,1516 .globals = .empty,
1480 .global_pending_index = 0,1517 .global_pending_index = 0,
1518 .late_globals = .empty,
1519 .late_globals_pending_index = 0,
1481 .navs = .empty,1520 .navs = .empty,
1482 .uavs = .empty,1521 .uavs = .empty,
1483 .lazy = .initFill(.{1522 .lazy = .initFill(.{
...@@ -1491,6 +1530,7 @@ fn create(...@@ -1491,6 +1530,7 @@ fn create(
1491 .symbol_prog_node = .none,1530 .symbol_prog_node = .none,
1492 .member_prog_node = .none,1531 .member_prog_node = .none,
1493 .input_prog_node = .none,1532 .input_prog_node = .none,
1533 .subsystem = options.subsystem,
1494 .dump_snapshot = options.enable_link_snapshots,1534 .dump_snapshot = options.enable_link_snapshots,
1495 };1535 };
1496 errdefer coff.deinit();1536 errdefer coff.deinit();
...@@ -1533,6 +1573,9 @@ pub fn deinit(coff: *Coff) void {...@@ -1533,6 +1573,9 @@ pub fn deinit(coff: *Coff) void {
1533 coff.input_archive_members.deinit(gpa);1573 coff.input_archive_members.deinit(gpa);
1534 coff.input_archive_symbols.deinit(gpa);1574 coff.input_archive_symbols.deinit(gpa);
1535 coff.input_archive_symbol_indices.deinit(gpa);1575 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);
1536 coff.input_objects.deinit(gpa);1579 coff.input_objects.deinit(gpa);
1537 coff.input_symbols.deinit(gpa);1580 coff.input_symbols.deinit(gpa);
1538 coff.input_sections.deinit(gpa);1581 coff.input_sections.deinit(gpa);
...@@ -1543,6 +1586,7 @@ pub fn deinit(coff: *Coff) void {...@@ -1543,6 +1586,7 @@ pub fn deinit(coff: *Coff) void {
1543 coff.object_section_table.deinit(gpa);1586 coff.object_section_table.deinit(gpa);
1544 coff.symbols.deinit(gpa);1587 coff.symbols.deinit(gpa);
1545 coff.globals.deinit(gpa);1588 coff.globals.deinit(gpa);
1589 coff.late_globals.deinit(gpa);
1546 coff.navs.deinit(gpa);1590 coff.navs.deinit(gpa);
1547 coff.uavs.deinit(gpa);1591 coff.uavs.deinit(gpa);
1548 for (&coff.lazy.values) |*lazy| lazy.map.deinit(gpa);1592 for (&coff.lazy.values) |*lazy| lazy.map.deinit(gpa);
...@@ -1579,8 +1623,12 @@ fn isObj(coff: *const Coff) bool {...@@ -1579,8 +1623,12 @@ fn isObj(coff: *const Coff) bool {
1579 return coff.base.comp.config.output_mode == .Obj;1623 return coff.base.comp.config.output_mode == .Obj;
1580}1624}
15811625
1582fn zcuSectionParent(coff: *Coff) MappedFile.Node.Index {1626fn hasCoffHeader(coff: *const Coff) bool {
1583 assert(coff.base.comp.zcu != null);1627 return coff.base.comp.zcu != null or !coff.isArchive();
1628}
1629
1630fn sectionParent(coff: *Coff) MappedFile.Node.Index {
1631 assert(coff.hasCoffHeader());
1584 return if (coff.isArchive()) Node.known.zcu_member else Node.known.file;1632 return if (coff.isArchive()) Node.known.zcu_member else Node.known.file;
1585}1633}
15861634
...@@ -1611,7 +1659,7 @@ fn initHeaders(...@@ -1611,7 +1659,7 @@ fn initHeaders(
1611 0;1659 0;
16121660
1613 var expected_nodes_len: usize = Node.known_count;1661 var expected_nodes_len: usize = Node.known_count;
1614 if (comp.zcu != null) {1662 if (coff.hasCoffHeader()) {
1615 // Sections1663 // Sections
1616 expected_nodes_len += 3;1664 expected_nodes_len += 3;
16171665
...@@ -1663,7 +1711,7 @@ fn initHeaders(...@@ -1663,7 +1711,7 @@ fn initHeaders(
1663 @memcpy(signature_slice, archive_signature);1711 @memcpy(signature_slice, archive_signature);
1664 }1712 }
16651713
1666 const opt_zcu_coff_parent_ni = if (is_archive) parent: {1714 const opt_coff_parent_ni = if (is_archive) parent: {
1667 const initial_member_count = Member.Index.known_count + @intFromBool(comp.zcu != null);1715 const initial_member_count = Member.Index.known_count + @intFromBool(comp.zcu != null);
1668 try coff.members.ensureTotalCapacity(gpa, initial_member_count);1716 try coff.members.ensureTotalCapacity(gpa, initial_member_count);
16691717
...@@ -1709,10 +1757,10 @@ fn initHeaders(...@@ -1709,10 +1757,10 @@ fn initHeaders(
1709 if (placeholder_ni == Node.known.zcu_member) break;1757 if (placeholder_ni == Node.known.zcu_member) break;
1710 }1758 }
17111759
1712 break :parent if (comp.zcu != null) Node.known.header else null;1760 break :parent Node.known.header;
1713 };1761 };
17141762
1715 const zcu_coff_parent_ni = opt_zcu_coff_parent_ni orelse {1763 const coff_parent_ni = opt_coff_parent_ni orelse {
1716 // If we're not generating any code, no more known nodes are used1764 // If we're not generating any code, no more known nodes are used
1717 while (coff.nodes.len < Node.known_count) {1765 while (coff.nodes.len < Node.known_count) {
1718 _ = try coff.mf.addNodeAfter(gpa, Node.known.header, .{});1766 _ = try coff.mf.addNodeAfter(gpa, Node.known.header, .{});
...@@ -1723,7 +1771,7 @@ fn initHeaders(...@@ -1723,7 +1771,7 @@ fn initHeaders(
1723 };1771 };
17241772
1725 const coff_header_ni = Node.known.coff_header;1773 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, .{
1727 .size = @sizeOf(std.coff.Header),1775 .size = @sizeOf(std.coff.Header),
1728 .alignment = .@"4",1776 .alignment = .@"4",
1729 .fixed = true,1777 .fixed = true,
...@@ -1751,7 +1799,7 @@ fn initHeaders(...@@ -1751,7 +1799,7 @@ fn initHeaders(
1751 }1799 }
17521800
1753 const optional_header_ni = Node.known.optional_header;1801 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, .{
1755 .size = optional_header_size,1803 .size = optional_header_size,
1756 .alignment = .@"4",1804 .alignment = .@"4",
1757 .fixed = true,1805 .fixed = true,
...@@ -1863,7 +1911,7 @@ fn initHeaders(...@@ -1863,7 +1911,7 @@ fn initHeaders(
1863 }1911 }
18641912
1865 const data_directories_ni = Node.known.data_directories;1913 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, .{
1867 .size = data_directories_size,1915 .size = data_directories_size,
1868 .alignment = .@"4",1916 .alignment = .@"4",
1869 .fixed = true,1917 .fixed = true,
...@@ -1879,7 +1927,7 @@ fn initHeaders(...@@ -1879,7 +1927,7 @@ fn initHeaders(
1879 }1927 }
18801928
1881 const section_table_ni = Node.known.section_table;1929 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, .{
1883 .alignment = .@"4",1931 .alignment = .@"4",
1884 .fixed = true,1932 .fixed = true,
1885 }));1933 }));
...@@ -1889,14 +1937,14 @@ fn initHeaders(...@@ -1889,14 +1937,14 @@ fn initHeaders(
18891937
1890 if (!is_image) {1938 if (!is_image) {
1891 // TODO: These two nodes could be inside one movable node?1939 // 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, .{
1893 .alignment = .@"2",1941 .alignment = .@"2",
1894 .fixed = true,1942 .fixed = true,
1895 .moved = true,1943 .moved = true,
1896 });1944 });
1897 coff.nodes.appendAssumeCapacity(.symbol_table);1945 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, .{
1900 .size = @sizeOf(u32),1948 .size = @sizeOf(u32),
1901 .fixed = true,1949 .fixed = true,
1902 .resized = true,1950 .resized = true,
...@@ -2030,16 +2078,19 @@ pub fn initBuiltins(coff: *Coff) !void {...@@ -2030,16 +2078,19 @@ pub fn initBuiltins(coff: *Coff) !void {
2030 const comp = coff.base.comp;2078 const comp = coff.base.comp;
2031 const gpa = comp.gpa;2079 const gpa = comp.gpa;
2032 const target = &comp.root_mod.resolved_target.result;2080 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
2033 if (coff.isImage() and target.isMinGW() and comp.config.link_libc) {2090 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);
2035 try coff.globals.ensureUnusedCapacity(gpa, 2);2092 try coff.globals.ensureUnusedCapacity(gpa, 2);
2036 try coff.nodes.ensureUnusedCapacity(gpa, 3);2093 try coff.nodes.ensureUnusedCapacity(gpa, 6);
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 }
20432094
2044 const lists: []const struct { global: []const u8, start: String, end: String } = &.{2095 const lists: []const struct { global: []const u8, start: String, end: String } = &.{
2045 .{ .global = "__CTOR_LIST__", .start = .@".ctors", .end = .@".ctors$ZZZ" },2096 .{ .global = "__CTOR_LIST__", .start = .@".ctors", .end = .@".ctors$ZZZ" },
...@@ -2083,7 +2134,10 @@ pub fn startProgress(coff: *Coff, prog_node: std.Progress.Node) void {...@@ -2083,7 +2134,10 @@ pub fn startProgress(coff: *Coff, prog_node: std.Progress.Node) void {
2083 prog_node.increaseEstimatedTotalItems(3);2134 prog_node.increaseEstimatedTotalItems(3);
2084 coff.const_prog_node = prog_node.start("Constants", coff.pending_uavs.count());2135 coff.const_prog_node = prog_node.start("Constants", coff.pending_uavs.count());
2085 coff.synth_prog_node = prog_node.start("Synthetics", count: {2136 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
2087 for (&coff.lazy.values) |*lazy| count += lazy.map.count() - lazy.pending_index;2141 for (&coff.lazy.values) |*lazy| count += lazy.map.count() - lazy.pending_index;
2088 break :count count;2142 break :count count;
2089 });2143 });
...@@ -2246,7 +2300,7 @@ fn targetStore(coff: *const Coff, ptr: anytype, val: @typeInfo(@TypeOf(ptr)).poi...@@ -2246,7 +2300,7 @@ fn targetStore(coff: *const Coff, ptr: anytype, val: @typeInfo(@TypeOf(ptr)).poi
2246}2300}
22472301
2248pub fn headerPtr(coff: *Coff) *std.coff.Header {2302pub fn headerPtr(coff: *Coff) *std.coff.Header {
2249 assert(coff.base.comp.zcu != null);2303 assert(coff.hasCoffHeader());
2250 return @ptrCast(@alignCast(Node.known.coff_header.slice(&coff.mf)));2304 return @ptrCast(@alignCast(Node.known.coff_header.slice(&coff.mf)));
2251}2305}
22522306
...@@ -2405,6 +2459,7 @@ fn addSymbolAssumeCapacity(coff: *Coff) Symbol.Index {...@@ -2405,6 +2459,7 @@ fn addSymbolAssumeCapacity(coff: *Coff) Symbol.Index {
2405 .value_tag = .size,2459 .value_tag = .size,
2406 .type = .unknown,2460 .type = .unknown,
2407 .dll_storage_class = .default,2461 .dll_storage_class = .default,
2462 .weak_external_strat = undefined,
2408 },2463 },
2409 .loc_relocs = .none,2464 .loc_relocs = .none,
2410 .target_relocs = .none,2465 .target_relocs = .none,
...@@ -2509,7 +2564,6 @@ fn getOrPutGlobalSymbol(...@@ -2509,7 +2564,6 @@ fn getOrPutGlobalSymbol(
2509 if (!sym_gop.found_existing) {2564 if (!sym_gop.found_existing) {
2510 const si = coff.addSymbolAssumeCapacity();2565 const si = coff.addSymbolAssumeCapacity();
2511 const sym = si.get(coff);2566 const sym = si.get(coff);
2512 sym.setValue(.{ .alias_si = .null });
2513 sym.gmi = .wrap(@intCast(sym_gop.index));2567 sym.gmi = .wrap(@intCast(sym_gop.index));
2514 sym.flags.type = opts.type;2568 sym.flags.type = opts.type;
2515 sym.flags.dll_storage_class = opts.dll_storage_class;2569 sym.flags.dll_storage_class = opts.dll_storage_class;
...@@ -2982,7 +3036,7 @@ fn flushInputSection(coff: *Coff, isi: Node.InputSection.Index) !void {...@@ -2982,7 +3036,7 @@ fn flushInputSection(coff: *Coff, isi: Node.InputSection.Index) !void {
2982}3036}
29833037
2984fn addSection(coff: *Coff, name: String, flags: std.coff.SectionHeader.Flags) !Symbol.Index {3038fn addSection(coff: *Coff, name: String, flags: std.coff.SectionHeader.Flags) !Symbol.Index {
2985 assert(coff.base.comp.zcu != null);3039 assert(coff.hasCoffHeader());
29863040
2987 const gpa = coff.base.comp.gpa;3041 const gpa = coff.base.comp.gpa;
2988 try coff.nodes.ensureUnusedCapacity(gpa, 1);3042 try coff.nodes.ensureUnusedCapacity(gpa, 1);
...@@ -3000,7 +3054,7 @@ fn addSection(coff: *Coff, name: String, flags: std.coff.SectionHeader.Flags) !S...@@ -3000,7 +3054,7 @@ fn addSection(coff: *Coff, name: String, flags: std.coff.SectionHeader.Flags) !S
3000 @sizeOf(std.coff.SectionHeader) * section_table_len,3054 @sizeOf(std.coff.SectionHeader) * section_table_len,
3001 );3055 );
30023056
3003 const ni = try coff.mf.addLastChildNode(gpa, coff.zcuSectionParent(), .{3057 const ni = try coff.mf.addLastChildNode(gpa, coff.sectionParent(), .{
3004 .alignment = coff.mf.flags.block_size,3058 .alignment = coff.mf.flags.block_size,
3005 .moved = true,3059 .moved = true,
3006 .bubbles_moved = false,3060 .bubbles_moved = false,
...@@ -3185,7 +3239,7 @@ fn objectSectionMapIndex(...@@ -3185,7 +3239,7 @@ fn objectSectionMapIndex(
31853239
3186 const object_section_gop = try coff.object_section_table.getOrPut(gpa, name);3240 const object_section_gop = try coff.object_section_table.getOrPut(gpa, name);
3187 const osmi: Node.ObjectSectionMapIndex = @enumFromInt(object_section_gop.index);3241 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: {
3189 try coff.ensureUnusedStringCapacity(name_slice.len);3243 try coff.ensureUnusedStringCapacity(name_slice.len);
3190 const parent_name = coff.getOrPutStringAssumeCapacity(coff.objectSectionParentName(name_slice));3244 const parent_name = coff.getOrPutStringAssumeCapacity(coff.objectSectionParentName(name_slice));
3191 const parent = (try coff.pseudoSectionMapIndex(parent_name, alignment, effective_attributes)).symbol(coff);3245 const parent = (try coff.pseudoSectionMapIndex(parent_name, alignment, effective_attributes)).symbol(coff);
...@@ -3222,7 +3276,7 @@ fn objectSectionMapIndex(...@@ -3222,7 +3276,7 @@ fn objectSectionMapIndex(
3222 assert(sym.loc_relocs == .none);3276 assert(sym.loc_relocs == .none);
3223 sym.loc_relocs = @enumFromInt(coff.relocs.items.len);3277 sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
3224 coff.nodes.appendAssumeCapacity(.{ .object_section = osmi });3278 coff.nodes.appendAssumeCapacity(.{ .object_section = osmi });
3225 break :sn sym;3279 break :sym sym;
3226 } else object_section_gop.value_ptr.get(coff);3280 } else object_section_gop.value_ptr.get(coff);
32273281
3228 const parent_ni = sym.ni.parent(&coff.mf);3282 const parent_ni = sym.ni.parent(&coff.mf);
...@@ -3334,7 +3388,7 @@ pub fn addReloc(...@@ -3334,7 +3388,7 @@ pub fn addReloc(
3334 const new_size = new_num_relocations * std.coff.Relocation.sizeOf();3388 const new_size = new_num_relocations * std.coff.Relocation.sizeOf();
3335 if (section.relocation_table_ni == .none) {3389 if (section.relocation_table_ni == .none) {
3336 try coff.nodes.ensureUnusedCapacity(gpa, 1);3390 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(), .{
3338 .size = new_size,3392 .size = new_size,
3339 .alignment = .@"2",3393 .alignment = .@"2",
3340 .moved = true,3394 .moved = true,
...@@ -3637,13 +3691,6 @@ fn loadObject(...@@ -3637,13 +3691,6 @@ fn loadObject(
3637 section_i,3691 section_i,
3638 section_name_slice,3692 section_name_slice,
3639 });3693 });
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 }
3647 }3694 }
36483695
3649 break :sections sections;3696 break :sections sections;
...@@ -3684,14 +3731,16 @@ fn loadObject(...@@ -3684,14 +3731,16 @@ fn loadObject(
3684 section: u32,3731 section: u32,
3685 // Offset within the section3732 // Offset within the section
3686 static: u32,3733 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.
3688 external: u32,3735 external: u32,
3689 // The index of the target symbol of this alias3736 // The index of the target symbol of this weak external
3690 weak_external: u32,3737 weak_external: u32,
3738 // Trails .weak_external
3739 weak_external_aux: WeakExternalStrat,
3691 },3740 },
3692 section_number: Symbol.SectionNumber,3741 section_number: Symbol.SectionNumber,
3693 si: Symbol.Index,3742 si: Symbol.Index,
3694 // The index of the weak_external that targest this symbol3743 // If a weak external targets this symbol, the index of the weak external
3695 weak_external_psi: PendingSymbolIndex,3744 weak_external_psi: PendingSymbolIndex,
3696 };3745 };
36973746
...@@ -3741,11 +3790,12 @@ fn loadObject(...@@ -3741,11 +3790,12 @@ fn loadObject(
37413790
3742 const psi: PendingSymbolIndex = .wrap(@intCast(pending_symbols.count()));3791 const psi: PendingSymbolIndex = .wrap(@intCast(pending_symbols.count()));
3743 const section_number: Symbol.SectionNumber = @enumFromInt(@intFromEnum(symbol.section_number));3792 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) {
3745 .STATIC, .LABEL => |storage_class| switch (section_number) {3795 .STATIC, .LABEL => |storage_class| switch (section_number) {
3746 // TODO: Do we need to do anything with @feat.00?3796 // TODO: Do we need to do anything with @feat.00?
3747 // https://llvm.org/doxygen/namespacellvm_1_1COFF.html#aeffa16735e18df727a173beaf748c3923797 // https://llvm.org/doxygen/namespacellvm_1_1COFF.html#aeffa16735e18df727a173beaf748c392
3748 .UNDEFINED, .DEBUG, .ABSOLUTE => null,3798 .UNDEFINED, .DEBUG, .ABSOLUTE => &.{},
3749 else => |sn| {3799 else => |sn| {
3750 const section = &sections[sn.toIndex()];3800 const section = &sections[sn.toIndex()];
37513801
...@@ -3803,10 +3853,10 @@ fn loadObject(...@@ -3803,10 +3853,10 @@ fn loadObject(
3803 section.psi = psi;3853 section.psi = psi;
3804 }3854 }
38053855
3806 break :pending_symbol if (is_section)3856 break :pending_symbols &.{if (is_section)
3807 .{ .section = section.header.size_of_raw_data }3857 .{ .section = section.header.size_of_raw_data }
3808 else3858 else
3809 .{ .static = symbol.value };3859 .{ .static = symbol.value }};
3810 },3860 },
3811 },3861 },
3812 .WEAK_EXTERNAL => switch (symbol.section_number) {3862 .WEAK_EXTERNAL => switch (symbol.section_number) {
...@@ -3830,16 +3880,12 @@ fn loadObject(...@@ -3830,16 +3880,12 @@ fn loadObject(
3830 .{ weak_external.tag_index, symbol_i },3880 .{ weak_external.tag_index, symbol_i },
3831 );3881 );
38323882
3833 break :pending_symbol switch (weak_external.flag) {3883 break :pending_symbols switch (weak_external.flag) {
3834 .SEARCH_NOLIBRARY,3884 else => |flag| &.{
3835 .SEARCH_LIBRARY,3885 .{ .weak_external = weak_external.tag_index },
3836 => return diags.failParse(3886 .{ .weak_external_aux = WeakExternalStrat.fromFlag(flag) },
3837 path,3887 },
3838 "TODO handle weak external characteristic 0x{x} for symbol 0x{x}",3888 _ => return diags.failParse(
3839 .{ weak_external.flag, symbol_i },
3840 ),
3841 .SEARCH_ALIAS => .{ .weak_external = weak_external.tag_index },
3842 else => return diags.failParse(
3843 path,3889 path,
3844 "encountered unknown weak external characteristic 0x{x} for symbol 0x{x}",3890 "encountered unknown weak external characteristic 0x{x} for symbol 0x{x}",
3845 .{ weak_external.flag, symbol_i },3891 .{ weak_external.flag, symbol_i },
...@@ -3853,7 +3899,7 @@ fn loadObject(...@@ -3853,7 +3899,7 @@ fn loadObject(
3853 ),3899 ),
3854 },3900 },
3855 .EXTERNAL => switch (section_number) {3901 .EXTERNAL => switch (section_number) {
3856 .UNDEFINED => .{ .external = symbol.value },3902 .UNDEFINED => &.{.{ .external = symbol.value }},
3857 .ABSOLUTE => return diags.failParse(3903 .ABSOLUTE => return diags.failParse(
3858 path,3904 path,
3859 "TODO unhandled external absolute symbol 0x{x}: '{s}'",3905 "TODO unhandled external absolute symbol 0x{x}: '{s}'",
...@@ -3864,7 +3910,7 @@ fn loadObject(...@@ -3864,7 +3910,7 @@ fn loadObject(
3864 "unexpected external symbol 0x{x} in DEBUG section: '{s}'",3910 "unexpected external symbol 0x{x} in DEBUG section: '{s}'",
3865 .{ symbol_i, name },3911 .{ symbol_i, name },
3866 ),3912 ),
3867 else => .{ .external = symbol.value },3913 else => &.{.{ .external = symbol.value }},
3868 },3914 },
3869 .FILE => {3915 .FILE => {
3870 if (!std.mem.eql(u8, name, ".file"))3916 if (!std.mem.eql(u8, name, ".file"))
...@@ -3878,7 +3924,7 @@ fn loadObject(...@@ -3878,7 +3924,7 @@ fn loadObject(
3878 @memcpy(std.mem.asBytes(&file)[0..symbol_size], aux_symbols[0..symbol_size]);3924 @memcpy(std.mem.asBytes(&file)[0..symbol_size], aux_symbols[0..symbol_size]);
38793925
3880 input.source_name = (try coff.getOrPutString(file.getFileName())).toOptional();3926 input.source_name = (try coff.getOrPutString(file.getFileName())).toOptional();
3881 break :pending_symbol null;3927 break :pending_symbols &.{};
3882 },3928 },
3883 else => |storage_class| return diags.failParse(3929 else => |storage_class| return diags.failParse(
3884 path,3930 path,
...@@ -3887,10 +3933,13 @@ fn loadObject(...@@ -3887,10 +3933,13 @@ fn loadObject(
3887 ),3933 ),
3888 };3934 };
38893935
3890 if (opt_value) |value| {3936 for (values, 0..) |value, i| {
3891 switch (value) {3937 switch (value) {
3892 .section => {},3938 .section => {},
3893 .static, .external, .weak_external => {3939 .static,
3940 .external,
3941 .weak_external,
3942 => {
3894 num_global_symbols += 1;3943 num_global_symbols += 1;
3895 if (section_number.hasIndex()) {3944 if (section_number.hasIndex()) {
3896 const section = &sections[section_number.toIndex()];3945 const section = &sections[section_number.toIndex()];
...@@ -3899,10 +3948,11 @@ fn loadObject(...@@ -3899,10 +3948,11 @@ fn loadObject(
3899 section.comdat_psi = psi;3948 section.comdat_psi = psi;
3900 }3949 }
3901 },3950 },
3951 .weak_external_aux => {},
3902 }3952 }
39033953
3904 const symbol_name = coff.getOrPutStringAssumeCapacity(name);3954 const symbol_name = coff.getOrPutStringAssumeCapacity(name);
3905 pending_symbols.putAssumeCapacity(symbol_i, .{3955 pending_symbols.putAssumeCapacity(symbol_i + @as(u32, @intCast(i)), .{
3906 .name = symbol_name,3956 .name = symbol_name,
3907 .value = value,3957 .value = value,
3908 .section_number = section_number,3958 .section_number = section_number,
...@@ -3917,6 +3967,7 @@ fn loadObject(...@@ -3917,6 +3967,7 @@ fn loadObject(
3917 if (section.header.flags.LNK_INFO) {3967 if (section.header.flags.LNK_INFO) {
3918 if (std.mem.eql(u8, &section.header.name, ".drectve")) {3968 if (std.mem.eql(u8, &section.header.name, ".drectve")) {
3919 try fr.seekTo(fl.offset + section.header.pointer_to_raw_data);3969 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
3920 var buf: [128]u8 = undefined;3971 var buf: [128]u8 = undefined;
3921 var section_r = r.limited(.limited(section.header.size_of_raw_data), &buf);3972 var section_r = r.limited(.limited(section.header.size_of_raw_data), &buf);
3922 while (section_r.interface.takeDelimiter(' ') catch |err| switch (err) {3973 while (section_r.interface.takeDelimiter(' ') catch |err| switch (err) {
...@@ -3926,9 +3977,60 @@ fn loadObject(...@@ -3926,9 +3977,60 @@ fn loadObject(
3926 // Microsoft tools emit 3 space characters into this section even with /Zl3977 // Microsoft tools emit 3 space characters into this section even with /Zl
3927 if (arg.len == 0) continue;3978 if (arg.len == 0) continue;
39283979
3929 if (std.mem.cutPrefix(u8, arg, "-exclude-symbols:")) |rest| {3980 if (std.ascii.startsWithIgnoreCase(arg, "-exclude-symbols:")) {
3930 // TODO: When implementing mingw auto-exports, use this to not export this symbol3981 // TODO: When implementing mingw auto-exports (if at all?), use this to not export this symbol
3931 _ = rest;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 );
3932 } else return diags.failParse(path, "unsupported argument in .drectve section: `{s}`", .{arg});4034 } else return diags.failParse(path, "unsupported argument in .drectve section: `{s}`", .{arg});
3933 }4035 }
3934 }4036 }
...@@ -3940,6 +4042,7 @@ fn loadObject(...@@ -3940,6 +4042,7 @@ fn loadObject(
3940 if (section.header.flags.LNK_REMOVE or4042 if (section.header.flags.LNK_REMOVE or
3941 section.header.flags.MEM_DISCARDABLE)4043 section.header.flags.MEM_DISCARDABLE)
3942 {4044 {
4045 // TODO: Convert .debug$* sections into PDB
3943 section.comdat_result = .skip;4046 section.comdat_result = .skip;
3944 continue;4047 continue;
3945 }4048 }
...@@ -3973,6 +4076,7 @@ fn loadObject(...@@ -3973,6 +4076,7 @@ fn loadObject(
3973 const symbol = &pending_symbols.values()[psi];4076 const symbol = &pending_symbols.values()[psi];
3974 const si = existing: switch (symbol.value) {4077 const si = existing: switch (symbol.value) {
3975 .weak_external => unreachable,4078 .weak_external => unreachable,
4079 .weak_external_aux => unreachable,
3976 .static => break :comdat .include,4080 .static => break :comdat .include,
3977 .section => {4081 .section => {
3978 assert(section.comdat_psi == .none);4082 assert(section.comdat_psi == .none);
...@@ -4145,14 +4249,21 @@ fn loadObject(...@@ -4145,14 +4249,21 @@ fn loadObject(
4145 }4249 }
41464250
4147 for (pending_symbols.values(), pending_symbols.keys(), 0..) |*symbol, index, i| {4251 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}", .{
4149 symbol.name.toSlice(coff),4258 symbol.name.toSlice(coff),
4150 index,4259 index,
4151 symbol.value,4260 symbol.value,
4152 symbol.section_number,
4153 switch (symbol.value) {4261 switch (symbol.value) {
4262 .weak_external_aux => unreachable,
4154 inline else => |v| v,4263 inline else => |v| v,
4155 },4264 },
4265 symbol.section_number,
4266 symbol.si.get(coff).ni,
4156 symbol.si,4267 symbol.si,
4157 symbol.si.get(coff).section_number,4268 symbol.si.get(coff).section_number,
4158 });4269 });
...@@ -4161,34 +4272,50 @@ fn loadObject(...@@ -4161,34 +4272,50 @@ fn loadObject(
4161 .UNDEFINED => switch (symbol.value) {4272 .UNDEFINED => switch (symbol.value) {
4162 .section,4273 .section,
4163 .static,4274 .static,
4275 .weak_external_aux,
4164 => unreachable,4276 => unreachable,
4165 .external,4277 .external => {
4166 .weak_external,4278 if (symbol.weak_external_psi.unwrap()) |weak_external_i| {
4167 => |value, tag| {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| {
4168 const global_gop = try coff.getOrPutGlobalSymbol(.{ .name = symbol.name.toSlice(coff) });4292 const global_gop = try coff.getOrPutGlobalSymbol(.{ .name = symbol.name.toSlice(coff) });
4169 symbol.si = global_gop.value_ptr.*;4293 symbol.si = global_gop.value_ptr.*;
4170 if (!global_gop.found_existing or symbol.si.get(coff).ni == .none) {4294 if (!global_gop.found_existing or symbol.si.get(coff).ni == .none) {
4171 const sym = symbol.si.get(coff);4295 const sym = symbol.si.get(coff);
4172 if (tag == .external) {4296 const alias = pending_symbols.getPtr(alias_index) orelse
4173 sym.setValue(.{ .size = @max(sym.size(), value) });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));
4174 } else {4311 } else {
4175 const alias = pending_symbols.getPtr(value) orelse4312 sym.setValue(if (alias.si == .null) .{
4176 return diags.failParse(4313 // See .external branch above
4177 path,4314 .alias_name = alias.name,
4178 "weak external 0x{x} {s}{f} targets unknown symbol index 0x{x}",4315 } else .{
4179 .{4316 .alias_si = alias.si,
4180 index,4317 });
4181 symbol.name.toSlice(coff),4318 sym.flags.weak_external_strat = pending_symbols.values()[i + 1].value.weak_external_aux;
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 }
4192 }4319 }
4193 }4320 }
41944321
...@@ -4217,13 +4344,17 @@ fn loadObject(...@@ -4217,13 +4344,17 @@ fn loadObject(
4217 if (global_gop.found_existing and sym.ni != .none)4344 if (global_gop.found_existing and sym.ni != .none)
4218 return coff.failMultipleDefinitions(path, member_name, symbol.name, index, global_gop.value_ptr.*, .none);4345 return coff.failMultipleDefinitions(path, member_name, symbol.name, index, global_gop.value_ptr.*, .none);
4219 },4346 },
4220 .weak_external => unreachable,4347 .weak_external,
4348 .weak_external_aux,
4349 => unreachable,
4221 }4350 }
4222 }4351 }
42234352
4224 if (symbol.weak_external_psi.unwrap()) |weak_external_i| {4353 if (symbol.weak_external_psi.unwrap()) |weak_external_i| {
4225 assert(symbol.si != .null);4354 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;
4227 }4358 }
42284359
4229 if (section.si != symbol.si) {4360 if (section.si != symbol.si) {
...@@ -4237,7 +4368,9 @@ fn loadObject(...@@ -4237,7 +4368,9 @@ fn loadObject(
4237 .UNDEFINED, .ABSOLUTE, .DEBUG => unreachable,4368 .UNDEFINED, .ABSOLUTE, .DEBUG => unreachable,
4238 else => .{ .node_offset = v },4369 else => .{ .node_offset = v },
4239 },4370 },
4240 .weak_external => unreachable,4371 .weak_external,
4372 .weak_external_aux,
4373 => unreachable,
4241 });4374 });
4242 sym.section_number = section.si.get(coff).section_number;4375 sym.section_number = section.si.get(coff).section_number;
4243 }4376 }
...@@ -4260,14 +4393,34 @@ fn loadObject(...@@ -4260,14 +4393,34 @@ fn loadObject(
4260 if (target_endian != native_endian)4393 if (target_endian != native_endian)
4261 std.mem.byteSwapAllFields(std.coff.Relocation, &reloc);4394 std.mem.byteSwapAllFields(std.coff.Relocation, &reloc);
42624395
4263 // TODO: This error should show member name for lib4396 const symbol = pending_symbols.getPtr(reloc.symbol_table_index) orelse
4264 const symbol = pending_symbols.get(reloc.symbol_table_index) orelse
4265 return diags.failParse(4397 return diags.failParse(
4266 path,4398 path,
4267 "relocation 0x{x} in section '{s}'{f} targets invalid symbol index 0x{x}",4399 "relocation 0x{x} in section '{s}' of {f}{f} targets invalid symbol index 0x{x}",
4268 .{ reloc_i, section.name.toSlice(coff), fmtMemberNameString(member_name), reloc.symbol_table_index },4400 .{
4401 reloc_i,
4402 section.name.toSlice(coff),
4403 path.fmtEscapeString(),
4404 fmtMemberNameString(member_name),
4405 reloc.symbol_table_index,
4406 },
4269 );4407 );
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
4271 assert(symbol.si != .null);4424 assert(symbol.si != .null);
4272 try coff.addReloc(4425 try coff.addReloc(
4273 section.si,4426 section.si,
...@@ -4299,7 +4452,7 @@ fn loadObject(...@@ -4299,7 +4452,7 @@ fn loadObject(
4299 var prev_sn: Symbol.SectionNumber = .DEBUG;4452 var prev_sn: Symbol.SectionNumber = .DEBUG;
4300 var include_section = false;4453 var include_section = false;
4301 for (pending_symbols.values()) |symbol| {4454 for (pending_symbols.values()) |symbol| {
4302 // The symbol may have not been included, or it's an undefined external4455 // The symbol may have not been included, or it's an undefined external / aux
4303 if (symbol.si == .null or symbol.si.get(coff).ni == .none) continue;4456 if (symbol.si == .null or symbol.si.get(coff).ni == .none) continue;
43044457
4305 if (prev_sn != symbol.section_number) {4458 if (prev_sn != symbol.section_number) {
...@@ -4731,6 +4884,71 @@ pub fn prelink(coff: *Coff, prog_node: std.Progress.Node) link.Error!void {...@@ -4731,6 +4884,71 @@ pub fn prelink(coff: *Coff, prog_node: std.Progress.Node) link.Error!void {
4731 _ = prog_node;4884 _ = prog_node;
4732 log.debug("prelink()", .{});4885 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
4734 coff.inputs_complete = true;4952 coff.inputs_complete = true;
4735}4953}
47364954
...@@ -5143,6 +5361,11 @@ pub fn flush(...@@ -5143,6 +5361,11 @@ pub fn flush(
5143) !void {5361) !void {
5144 _ = arena;5362 _ = arena;
5145 _ = prog_node;5363 _ = 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
5146 while (try coff.idle(tid)) {}5369 while (try coff.idle(tid)) {}
51475370
5148 if (coff.isImage())5371 if (coff.isImage())
...@@ -5221,6 +5444,22 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {...@@ -5221,6 +5444,22 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
5221 }) coff.global_pending_index += 1;5444 }) coff.global_pending_index += 1;
5222 break :task;5445 break :task;
5223 }5446 }
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 }
5224 var lazy_it = coff.lazy.iterator();5463 var lazy_it = coff.lazy.iterator();
5225 while (lazy_it.next()) |lazy| if (lazy.value.pending_index < lazy.value.map.count()) {5464 while (lazy_it.next()) |lazy| if (lazy.value.pending_index < lazy.value.map.count()) {
5226 const pt: Zcu.PerThread = .{ .zcu = comp.zcu.?, .tid = tid };5465 const pt: Zcu.PerThread = .{ .zcu = comp.zcu.?, .tid = tid };
...@@ -5357,6 +5596,7 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {...@@ -5357,6 +5596,7 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
5357 if (coff.pending_uavs.count() > 0) return true;5596 if (coff.pending_uavs.count() > 0) return true;
5358 if (coff.pending_input != null) return true;5597 if (coff.pending_input != null) return true;
5359 if (coff.inputs_complete and coff.globals.count() > coff.global_pending_index) return true;5598 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;
5360 for (&coff.lazy.values) |lazy| if (lazy.map.count() > lazy.pending_index) return true;5600 for (&coff.lazy.values) |lazy| if (lazy.map.count() > lazy.pending_index) return true;
5361 if (coff.symbol_table.pending.count() > 0) return true;5601 if (coff.symbol_table.pending.count() > 0) return true;
5362 if (coff.input_sections.items.len > coff.input_section_pending_index) return true;5602 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 {...@@ -5504,10 +5744,11 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
5504 const gn = gmi.globalName(coff);5744 const gn = gmi.globalName(coff);
5505 const si = gmi.symbol(coff);5745 const si = gmi.symbol(coff);
5506 const sym = si.get(coff);5746 const sym = si.get(coff);
5747 const is_late = gmi.unwrap().? < coff.global_pending_index;
55075748
5508 log.debug(5749 log.debug(
5509 "flushGlobal({s}, {?s}) = {d} ({d})",5750 "flushGlobal({s}, {?s}, {}) = {d} ({d})",
5510 .{ gn.name.toSlice(coff), gn.lib_name.toSlice(coff), si, sym.ni },5751 .{ gn.name.toSlice(coff), gn.lib_name.toSlice(coff), is_late, si, sym.ni },
5511 );5752 );
55125753
5513 if (!coff.isImage()) {5754 if (!coff.isImage()) {
...@@ -5521,16 +5762,6 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {...@@ -5521,16 +5762,6 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
5521 return true;5762 return true;
5522 }5763 }
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
5534 const Import = struct {5765 const Import = struct {
5535 lib_name: String,5766 lib_name: String,
5536 name: String.Optional,5767 name: String.Optional,
...@@ -5555,9 +5786,41 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {...@@ -5555,9 +5786,41 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
5555 break :name .{ coff.getOrPutStringAssumeCapacity(name), true };5786 break :name .{ coff.getOrPutStringAssumeCapacity(name), true };
5556 };5787 };
55575788
5558 // TODO: Try to search for __imp_ even when !is_imp, we want to not use thunks if we can5789 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;
5561 var iter: InputArchive.Member.Symbol.Index = indices_list.first;5824 var iter: InputArchive.Member.Symbol.Index = indices_list.first;
5562 while (true) {5825 while (true) {
5563 const archive_sym = &coff.input_archive_symbols.items[@intFromEnum(iter)];5826 const archive_sym = &coff.input_archive_symbols.items[@intFromEnum(iter)];
...@@ -5631,6 +5894,32 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {...@@ -5631,6 +5894,32 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
5631 }5894 }
5632 }5895 }
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
5634 // Allow importing symbols with no implib entry, if a lib_name was specified.5923 // Allow importing symbols with no implib entry, if a lib_name was specified.
5635 // This is necessary for certain ntdll symbols, such as LdrRegisterDllNotification,5924 // This is necessary for certain ntdll symbols, such as LdrRegisterDllNotification,
5636 // which are not in the implib.5925 // which are not in the implib.