authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2026-06-05 01:55:35-04:00
committergravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2026-06-23 00:22:42-04:00
loga22ca5d4169406576776436a9bec5c500bc3f2d4
treef418a72a9eb7bc3c3c2f1e759069da467802fd16
parent652902ac7f424a719abf89a6532e207d03368728

Coff: more progress on imports

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

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

lib/std/meta.zig+9
...@@ -499,6 +499,15 @@ test DeclEnum {...@@ -499,6 +499,15 @@ test DeclEnum {
499 try expectEqualEnum(enum {}, DeclEnum(D));499 try expectEqualEnum(enum {}, DeclEnum(D));
500}500}
501501
502pub fn BareUnion(comptime T: type) type {
503 const u = switch (@typeInfo(T)) {
504 .@"union" => |u| u,
505 else => @compileError("expected union type, found '" ++ @typeName(T) ++ "'"),
506 };
507
508 return @Union(u.layout, null, u.field_names, u.field_types[0..], u.field_attrs[0..]);
509}
510
502pub fn Tag(comptime T: type) type {511pub fn Tag(comptime T: type) type {
503 return switch (@typeInfo(T)) {512 return switch (@typeInfo(T)) {
504 .@"enum" => |info| info.tag_type,513 .@"enum" => |info| info.tag_type,
lib/std/multi_array_list.zig+1-1
...@@ -44,7 +44,7 @@ pub fn MultiArrayList(comptime T: type) type {...@@ -44,7 +44,7 @@ pub fn MultiArrayList(comptime T: type) type {
44 const Elem = switch (@typeInfo(T)) {44 const Elem = switch (@typeInfo(T)) {
45 .@"struct" => T,45 .@"struct" => T,
46 .@"union" => |u| struct {46 .@"union" => |u| struct {
47 pub const Bare = @Union(u.layout, null, u.field_names, u.field_types[0..], u.field_attrs[0..]);47 pub const Bare = std.meta.BareUnion(T);
48 pub const Tag =48 pub const Tag =
49 u.tag_type orelse @compileError("MultiArrayList does not support untagged unions");49 u.tag_type orelse @compileError("MultiArrayList does not support untagged unions");
50 tags: Tag,50 tags: Tag,
src/link.zig+59-1
...@@ -1473,7 +1473,8 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void {...@@ -1473,7 +1473,8 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void {
14731473
1474 const target = &comp.root_mod.resolved_target.result;1474 const target = &comp.root_mod.resolved_target.result;
1475 const flags = target_util.libcFullLinkFlags(target);1475 const flags = target_util.libcFullLinkFlags(target);
1476 const crt_dir = comp.libc_installation.?.crt_dir.?;1476 const libc_installation = comp.libc_installation.?;
1477 const crt_dir = libc_installation.crt_dir.?;
1477 const sep = std.fs.path.sep_str;1478 const sep = std.fs.path.sep_str;
1478 for (flags) |flag| {1479 for (flags) |flag| {
1479 assert(mem.startsWith(u8, flag, "-l"));1480 assert(mem.startsWith(u8, flag, "-l"));
...@@ -1525,6 +1526,63 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void {...@@ -1525,6 +1526,63 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void {
1525 },1526 },
1526 }1527 }
1527 }1528 }
1529
1530 if (target.os.tag == .windows) {
1531 const inputs: []const struct {
1532 dir: enum { crt, msvc_lib, kernel32_lib },
1533 name: []const u8,
1534 } = if (target.abi.isGnu()) switch (comp.config.link_mode) {
1535 .dynamic => &.{
1536 .{ .dir = .crt, .name = "dllcrt2.obj" },
1537 .{ .dir = .crt, .name = "libmingw32.lib" },
1538 },
1539 .static => &.{
1540 .{ .dir = .crt, .name = "crt2.obj" },
1541 .{ .dir = .crt, .name = "libmingw32.lib" },
1542 },
1543 } else switch (comp.config.link_mode) {
1544 .dynamic => &.{
1545 .{ .dir = .msvc_lib, .name = "msvcrt.lib" },
1546 .{ .dir = .msvc_lib, .name = "vcruntime.lib" },
1547 .{ .dir = .msvc_lib, .name = "legacy_stdio_definitions.lib" },
1548 .{ .dir = .crt, .name = "ucrt.lib" },
1549 .{ .dir = .kernel32_lib, .name = "kernel32.lib" },
1550 .{ .dir = .kernel32_lib, .name = "ntdll.lib" },
1551 },
1552 .static => &.{
1553 .{ .dir = .msvc_lib, .name = "libcmt.lib" },
1554 .{ .dir = .msvc_lib, .name = "libvcruntime.lib" },
1555 .{ .dir = .msvc_lib, .name = "legacy_stdio_definitions.lib" },
1556 .{ .dir = .crt, .name = "libucrt.lib" },
1557 .{ .dir = .kernel32_lib, .name = "kernel32.lib" },
1558 .{ .dir = .kernel32_lib, .name = "ntdll.lib" },
1559 },
1560 };
1561
1562 for (inputs) |lib| {
1563 const path = Path.initCwd(
1564 std.fmt.allocPrint(comp.arena, "{s}" ++ sep ++ "{s}", .{
1565 switch (lib.dir) {
1566 .crt => crt_dir,
1567 .msvc_lib => libc_installation.msvc_lib_dir.?,
1568 .kernel32_lib => libc_installation.kernel32_lib_dir.?,
1569 },
1570 lib.name,
1571 }) catch return diags.setAllocFailure(),
1572 );
1573 if (std.mem.endsWith(u8, lib.name, "lib")) {
1574 base.openLoadArchive(path, null) catch |err| switch (err) {
1575 error.LinkFailure => return, // error reported via diags
1576 else => |e| diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(e)}),
1577 };
1578 } else {
1579 base.openLoadObject(path) catch |err| switch (err) {
1580 error.LinkFailure => return, // error reported via diags
1581 else => |e| diags.addParseError(path, "failed to parse object: {s}", .{@errorName(e)}),
1582 };
1583 }
1584 }
1585 }
1528 },1586 },
1529 .load_object => |path| {1587 .load_object => |path| {
1530 const prog_node = comp.link_prog_node.start("Parse Object", 0);1588 const prog_node = comp.link_prog_node.start("Parse Object", 0);
src/link/Coff.zig+384-251
...@@ -84,6 +84,8 @@ pub const default_size_of_heap_commit: u32 = 0x1000;...@@ -84,6 +84,8 @@ pub const default_size_of_heap_commit: u32 = 0x1000;
84pub const archive_signature = "!<arch>\n";84pub const archive_signature = "!<arch>\n";
85pub const archive_end_of_header = "`\n";85pub const archive_end_of_header = "`\n";
8686
87pub const imp_prefix = "__imp_";
88
87/// This is the start of a Portable Executable (PE) file.89/// This is the start of a Portable Executable (PE) file.
88/// It starts with a MS-DOS header followed by a MS-DOS stub program.90/// It starts with a MS-DOS header followed by a MS-DOS stub program.
89/// This data does not change so we include it as follows in all binaries.91/// This data does not change so we include it as follows in all binaries.
...@@ -203,7 +205,7 @@ pub const Node = union(enum) {...@@ -203,7 +205,7 @@ pub const Node = union(enum) {
203 pseudo_section: PseudoSectionMapIndex,205 pseudo_section: PseudoSectionMapIndex,
204 object_section: ObjectSectionMapIndex,206 object_section: ObjectSectionMapIndex,
205 input_section: InputSection.Index,207 input_section: InputSection.Index,
206 global: GlobalMapIndex,208 import_thunk: GlobalMapIndex, // TODO: Rename to import_thunk
207 nav: NavMapIndex,209 nav: NavMapIndex,
208 uav: UavMapIndex,210 uav: UavMapIndex,
209 lazy_code: LazyMapRef.Index(.code),211 lazy_code: LazyMapRef.Index(.code),
...@@ -255,10 +257,6 @@ pub const Node = union(enum) {...@@ -255,10 +257,6 @@ pub const Node = union(enum) {
255 return coff.globals.keys()[gmi.unwrap().?];257 return coff.globals.keys()[gmi.unwrap().?];
256 }258 }
257259
258 pub fn globalNameMutable(gmi: GlobalMapIndex, coff: *Coff) *GlobalName {
259 return &coff.globals.keys()[gmi.unwrap().?];
260 }
261
262 pub fn symbol(gmi: GlobalMapIndex, coff: *const Coff) Symbol.Index {260 pub fn symbol(gmi: GlobalMapIndex, coff: *const Coff) Symbol.Index {
263 return coff.globals.values()[gmi.unwrap().?];261 return coff.globals.values()[gmi.unwrap().?];
264 }262 }
...@@ -707,6 +705,12 @@ pub const ExportTable = struct {...@@ -707,6 +705,12 @@ pub const ExportTable = struct {
707pub const ImportTable = struct {705pub const ImportTable = struct {
708 ni: MappedFile.Node.Index,706 ni: MappedFile.Node.Index,
709 entries: std.array_hash_map.Auto(void, Entry),707 entries: std.array_hash_map.Auto(void, Entry),
708 iat_symbol_indices: std.AutoArrayHashMapUnmanaged(struct {
709 iti: ImportTable.Index,
710 name: String.Optional,
711 // If name == .none this is the ordinal, otherwise the hint
712 ordinal_hint: u16,
713 }, u32),
710714
711 pub const Entry = struct {715 pub const Entry = struct {
712 import_lookup_table_ni: MappedFile.Node.Index,716 import_lookup_table_ni: MappedFile.Node.Index,
...@@ -815,17 +819,20 @@ pub const Section = struct {...@@ -815,17 +819,20 @@ pub const Section = struct {
815819
816pub const GlobalName = struct { name: String, lib_name: String.Optional };820pub const GlobalName = struct { name: String, lib_name: String.Optional };
817821
822pub const DllStorageClass = enum(u2) {
823 default,
824 dllimport,
825 dllexport,
826};
827
818pub const Symbol = struct {828pub const Symbol = struct {
819 ni: MappedFile.Node.Index,829 ni: MappedFile.Node.Index,
820 rva: u32,830 rva: u32,
821 value: union(enum) {831 value: std.meta.BareUnion(Symbol.Value),
822 /// For .ni == .input_section, this is the offset of this symbol within the input section832 flags: packed struct(u16) {
823 input_offset: u32,833 value_tag: ValueTag,
824 /// For .ni == none and .gmi != .none, this is a weak alias834 dll_storage_class: DllStorageClass,
825 /// that should replace this symbol, or .null if none exists835 _: u12 = 0,
826 alias_si: Symbol.Index,
827 /// Otherwise, this is the symbol size if known
828 size: u32,
829 },836 },
830 /// Relocations contained within this symbol837 /// Relocations contained within this symbol
831 loc_relocs: Reloc.Index,838 loc_relocs: Reloc.Index,
...@@ -836,6 +843,55 @@ pub const Symbol = struct {...@@ -836,6 +843,55 @@ pub const Symbol = struct {
836 sti: SymbolTable.Index,843 sti: SymbolTable.Index,
837 gmi: Node.GlobalMapIndex,844 gmi: Node.GlobalMapIndex,
838845
846 const ValueTag = enum(u2) {
847 node_offset,
848 alias_si,
849 size,
850 };
851
852 pub const Value = union(ValueTag) {
853 /// The offset of the symbol within it's node
854 node_offset: u32,
855 /// For undefined globals, this is a weak alias
856 /// that can replace this symbol, or .null if none exists
857 alias_si: Symbol.Index,
858 /// The symbol size, or 0 if unknown
859 size: u32,
860 };
861
862 pub fn setValue(sym: *Symbol, value: Symbol.Value) void {
863 sym.flags.value_tag = std.meta.activeTag(value);
864 sym.value = switch (sym.flags.value_tag) {
865 inline else => |t| @unionInit(
866 @FieldType(Symbol, "value"),
867 @tagName(t),
868 @field(value, @tagName(t)),
869 ),
870 };
871 }
872
873 pub fn nodeOffset(sym: *const Symbol, coff: *Coff) u32 {
874 return switch (sym.flags.value_tag) {
875 .node_offset => offset: {
876 assert(switch (coff.getNode(sym.ni)) {
877 // Separate nodes are not created for these entries per-symbol
878 .input_section, .import_address_table => true,
879 else => false,
880 });
881 break :offset sym.value.node_offset;
882 },
883 else => 0,
884 };
885 }
886
887 pub fn weakAlias(sym: *const Symbol) Symbol.Index {
888 return if (sym.flags.value_tag == .alias_si) sym.value.alias_si else .null;
889 }
890
891 pub fn size(sym: *const Symbol) u32 {
892 return if (sym.flags.value_tag == .size) sym.value.size else 0;
893 }
894
839 pub const SectionNumber = enum(i16) {895 pub const SectionNumber = enum(i16) {
840 UNDEFINED = 0,896 UNDEFINED = 0,
841 ABSOLUTE = -1,897 ABSOLUTE = -1,
...@@ -847,10 +903,7 @@ pub const Symbol = struct {...@@ -847,10 +903,7 @@ pub const Symbol = struct {
847 }903 }
848904
849 fn hasIndex(sn: SectionNumber) bool {905 fn hasIndex(sn: SectionNumber) bool {
850 return switch (sn) {906 return @intFromEnum(sn) > 0;
851 .UNDEFINED, .ABSOLUTE, .DEBUG => false,
852 else => true,
853 };
854 }907 }
855908
856 pub fn symbol(sn: SectionNumber, coff: *const Coff) Symbol.Index {909 pub fn symbol(sn: SectionNumber, coff: *const Coff) Symbol.Index {
...@@ -902,11 +955,7 @@ pub const Symbol = struct {...@@ -902,11 +955,7 @@ pub const Symbol = struct {
902955
903 pub fn flushMoved(si: Symbol.Index, coff: *Coff) void {956 pub fn flushMoved(si: Symbol.Index, coff: *Coff) void {
904 const sym = si.get(coff);957 const sym = si.get(coff);
905 sym.rva = coff.computeNodeRva(sym.ni);958 sym.rva = coff.computeNodeRva(sym.ni) + sym.nodeOffset(coff);
906 if (sym.gmi != .none and coff.getNode(sym.ni) == .input_section) {
907 // Symbols in input sections share a ni with their section
908 sym.rva += sym.value.input_offset;
909 }
910 si.applyLocationRelocs(coff);959 si.applyLocationRelocs(coff);
911 si.applyTargetRelocs(coff);960 si.applyTargetRelocs(coff);
912 }961 }
...@@ -967,7 +1016,7 @@ pub const Symbol = struct {...@@ -967,7 +1016,7 @@ pub const Symbol = struct {
967 };1016 };
9681017
969 comptime {1018 comptime {
970 if (!std.debug.runtime_safety) std.debug.assert(@sizeOf(Symbol) == 36);1019 if (!std.debug.runtime_safety) std.debug.assert(@sizeOf(Symbol) == 32);
971 }1020 }
972};1021};
9731022
...@@ -1379,6 +1428,7 @@ fn create(...@@ -1379,6 +1428,7 @@ fn create(
1379 .import_table = .{1428 .import_table = .{
1380 .ni = .none,1429 .ni = .none,
1381 .entries = .empty,1430 .entries = .empty,
1431 .iat_symbol_indices = .empty,
1382 },1432 },
1383 .export_table = .{1433 .export_table = .{
1384 .ni = .none,1434 .ni = .none,
...@@ -1460,6 +1510,7 @@ pub fn deinit(coff: *Coff) void {...@@ -1460,6 +1510,7 @@ pub fn deinit(coff: *Coff) void {
1460 coff.lib_string_table.deinit(gpa);1510 coff.lib_string_table.deinit(gpa);
1461 coff.long_names_table.entries.deinit(gpa);1511 coff.long_names_table.entries.deinit(gpa);
1462 coff.import_table.entries.deinit(gpa);1512 coff.import_table.entries.deinit(gpa);
1513 coff.import_table.iat_symbol_indices.deinit(gpa);
1463 coff.export_table.entries.deinit(gpa);1514 coff.export_table.entries.deinit(gpa);
1464 coff.symbol_table.strings.deinit(gpa);1515 coff.symbol_table.strings.deinit(gpa);
1465 coff.symbol_table.pending.deinit(gpa);1516 coff.symbol_table.pending.deinit(gpa);
...@@ -1840,16 +1891,7 @@ fn initHeaders(...@@ -1840,16 +1891,7 @@ fn initHeaders(
1840 }1891 }
18411892
1842 try coff.symbols.ensureTotalCapacity(gpa, Symbol.Index.known_count);1893 try coff.symbols.ensureTotalCapacity(gpa, Symbol.Index.known_count);
1843 coff.symbols.addOneAssumeCapacity().* = .{1894 assert(coff.addSymbolAssumeCapacity() == .null);
1844 .ni = .none,
1845 .rva = 0,
1846 .value = .{ .size = 0 },
1847 .loc_relocs = .none,
1848 .target_relocs = .none,
1849 .section_number = .UNDEFINED,
1850 .sti = .none,
1851 .gmi = .none,
1852 };
1853 assert(try coff.addSection(.@".data", .{1895 assert(try coff.addSection(.@".data", .{
1854 .CNT_INITIALIZED_DATA = true,1896 .CNT_INITIALIZED_DATA = true,
1855 .MEM_READ = true,1897 .MEM_READ = true,
...@@ -2057,7 +2099,7 @@ fn computeNodeRva(coff: *Coff, ni: MappedFile.Node.Index) u32 {...@@ -2057,7 +2099,7 @@ fn computeNodeRva(coff: *Coff, ni: MappedFile.Node.Index) u32 {
2057 ),2099 ),
2058 inline .pseudo_section,2100 inline .pseudo_section,
2059 .object_section,2101 .object_section,
2060 .global,2102 .import_thunk,
2061 .nav,2103 .nav,
2062 .uav,2104 .uav,
2063 .lazy_code,2105 .lazy_code,
...@@ -2070,10 +2112,7 @@ fn computeNodeRva(coff: *Coff, ni: MappedFile.Node.Index) u32 {...@@ -2070,10 +2112,7 @@ fn computeNodeRva(coff: *Coff, ni: MappedFile.Node.Index) u32 {
2070 return @intCast(parent_rva + offset);2112 return @intCast(parent_rva + offset);
2071}2113}
2072fn computeSymbolSectionOffset(coff: *Coff, sym: *const Symbol) u32 {2114fn computeSymbolSectionOffset(coff: *Coff, sym: *const Symbol) u32 {
2073 var section_offset: u32 = if (sym.gmi != .none and coff.getNode(sym.ni) == .input_section)2115 var section_offset: u32 = sym.nodeOffset(coff);
2074 sym.value.input_offset
2075 else
2076 0;
2077 var parent_ni = sym.ni;2116 var parent_ni = sym.ni;
2078 while (true) {2117 while (true) {
2079 const offset, _ = parent_ni.location(&coff.mf).resolve(&coff.mf);2118 const offset, _ = parent_ni.location(&coff.mf).resolve(&coff.mf);
...@@ -2273,6 +2312,10 @@ fn addSymbolAssumeCapacity(coff: *Coff) Symbol.Index {...@@ -2273,6 +2312,10 @@ fn addSymbolAssumeCapacity(coff: *Coff) Symbol.Index {
2273 .ni = .none,2312 .ni = .none,
2274 .rva = 0,2313 .rva = 0,
2275 .value = .{ .size = 0 },2314 .value = .{ .size = 0 },
2315 .flags = .{
2316 .value_tag = .size,
2317 .dll_storage_class = .default,
2318 },
2276 .loc_relocs = .none,2319 .loc_relocs = .none,
2277 .target_relocs = .none,2320 .target_relocs = .none,
2278 .section_number = .UNDEFINED,2321 .section_number = .UNDEFINED,
...@@ -2357,8 +2400,9 @@ fn getOrPutStringAssumeCapacity(coff: *Coff, string: []const u8) String {...@@ -2357,8 +2400,9 @@ fn getOrPutStringAssumeCapacity(coff: *Coff, string: []const u8) String {
2357}2400}
23582401
2359const GlobalOptions = struct {2402const GlobalOptions = struct {
2360 name: []const u8, // TODO: Union with String2403 name: []const u8,
2361 lib_name: ?[]const u8 = null,2404 lib_name: ?[]const u8 = null,
2405 dll_storage_class: DllStorageClass = .default,
2362};2406};
23632407
2364fn getOrPutGlobalSymbol(2408fn getOrPutGlobalSymbol(
...@@ -2373,7 +2417,10 @@ fn getOrPutGlobalSymbol(...@@ -2373,7 +2417,10 @@ fn getOrPutGlobalSymbol(
2373 });2417 });
2374 if (!sym_gop.found_existing) {2418 if (!sym_gop.found_existing) {
2375 const si = coff.addSymbolAssumeCapacity();2419 const si = coff.addSymbolAssumeCapacity();
2376 si.get(coff).gmi = .wrap(@intCast(sym_gop.index));2420 const sym = si.get(coff);
2421 sym.setValue(.{ .alias_si = .null });
2422 sym.gmi = .wrap(@intCast(sym_gop.index));
2423 sym.flags.dll_storage_class = opts.dll_storage_class;
2377 sym_gop.value_ptr.* = si;2424 sym_gop.value_ptr.* = si;
2378 coff.synth_prog_node.increaseEstimatedTotalItems(1);2425 coff.synth_prog_node.increaseEstimatedTotalItems(1);
23792426
...@@ -2446,6 +2493,7 @@ pub fn navSymbol(coff: *Coff, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Symbo...@@ -2446,6 +2493,7 @@ pub fn navSymbol(coff: *Coff, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Symbo
2446 if (nav.getExtern(ip)) |@"extern"| return coff.globalSymbol(.{2493 if (nav.getExtern(ip)) |@"extern"| return coff.globalSymbol(.{
2447 .name = @"extern".name.toSlice(ip),2494 .name = @"extern".name.toSlice(ip),
2448 .lib_name = @"extern".lib_name.toSlice(ip),2495 .lib_name = @"extern".lib_name.toSlice(ip),
2496 .dll_storage_class = if (@"extern".is_dll_import) .dllimport else .default,
2449 });2497 });
2450 const nmi = try coff.navMapIndex(zcu, nav_index);2498 const nmi = try coff.navMapIndex(zcu, nav_index);
2451 return nmi.symbol(coff);2499 return nmi.symbol(coff);
...@@ -2774,7 +2822,7 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void...@@ -2774,7 +2822,7 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void
2774 };2822 };
27752823
2776 coff.targetStore(&entry.value, switch (sym.section_number) {2824 coff.targetStore(&entry.value, switch (sym.section_number) {
2777 .UNDEFINED => sym.value.size,2825 .UNDEFINED => sym.size(),
2778 .ABSOLUTE,2826 .ABSOLUTE,
2779 .DEBUG,2827 .DEBUG,
2780 => unreachable,2828 => unreachable,
...@@ -2784,7 +2832,7 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void...@@ -2784,7 +2832,7 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void
2784 },2832 },
2785 });2833 });
27862834
2787 log.debug("updateSymbolTableEntry({d}) = {d}", .{ si, sym.sti });2835 log.debug("flushSymbolTableEntry({d}) = {d}", .{ si, sym.sti });
2788}2836}
27892837
2790fn flushInputMember(coff: *Coff, iami: InputArchive.Member.Index) !void {2838fn flushInputMember(coff: *Coff, iami: InputArchive.Member.Index) !void {
...@@ -3838,7 +3886,10 @@ fn loadObject(...@@ -3838,7 +3886,10 @@ fn loadObject(
3838 .name = symbol.name.toSlice(coff),3886 .name = symbol.name.toSlice(coff),
3839 .lib_name = null,3887 .lib_name = null,
3840 });3888 });
3841 if (!global_gop.found_existing) {3889
3890 // TODO: What if the same symbol defined twice in this obj?
3891 // TODO: Would need to mark this global as pending, or notice it later when .ni != none
3892 if (!global_gop.found_existing or global_gop.value_ptr.get(coff).ni == .none) {
3842 symbol.si = global_gop.value_ptr.*;3893 symbol.si = global_gop.value_ptr.*;
3843 break :comdat .include;3894 break :comdat .include;
3844 }3895 }
...@@ -4003,9 +4054,7 @@ fn loadObject(...@@ -4003,9 +4054,7 @@ fn loadObject(
4003 if (!global_gop.found_existing or symbol.si.get(coff).ni == .none) {4054 if (!global_gop.found_existing or symbol.si.get(coff).ni == .none) {
4004 const sym = symbol.si.get(coff);4055 const sym = symbol.si.get(coff);
4005 if (tag == .external) {4056 if (tag == .external) {
4006 // TOOD: Is it valid to encounter multiple external definitions with different sizes?4057 sym.setValue(.{ .size = @max(sym.size(), value) });
4007 assert(sym.value == .size);
4008 sym.value = .{ .size = @max(sym.value.size, value) };
4009 } else {4058 } else {
4010 const alias = pending_symbols.getPtr(value) orelse4059 const alias = pending_symbols.getPtr(value) orelse
4011 return diags.failParse(4060 return diags.failParse(
...@@ -4022,7 +4071,7 @@ fn loadObject(...@@ -4022,7 +4071,7 @@ fn loadObject(
4022 if (alias.si == .null) {4071 if (alias.si == .null) {
4023 alias.weak_external_psi = .wrap(@intCast(psi));4072 alias.weak_external_psi = .wrap(@intCast(psi));
4024 } else {4073 } else {
4025 sym.value = .{ .alias_si = alias.si };4074 sym.setValue(.{ .alias_si = alias.si });
4026 }4075 }
4027 }4076 }
4028 }4077 }
...@@ -4063,20 +4112,21 @@ fn loadObject(...@@ -4063,20 +4112,21 @@ fn loadObject(
40634112
4064 if (section.si != symbol.si) {4113 if (section.si != symbol.si) {
4065 const sym = symbol.si.get(coff);4114 const sym = symbol.si.get(coff);
4115 assert(sym.ni == .none);
4066 sym.ni = section.si.get(coff).ni;4116 sym.ni = section.si.get(coff).ni;
4067 sym.value = switch (symbol.value) {4117 sym.setValue(switch (symbol.value) {
4068 .section => |v| .{ .size = v },4118 .section => |v| .{ .size = v },
4069 .static => |v| .{ .input_offset = v },4119 .static => |v| .{ .node_offset = v },
4070 .external => |v| switch (symbol.section_number) {4120 .external => |v| switch (symbol.section_number) {
4071 .UNDEFINED, .ABSOLUTE, .DEBUG => unreachable,4121 .UNDEFINED, .ABSOLUTE, .DEBUG => unreachable,
4072 else => .{ .input_offset = v },4122 else => .{ .node_offset = v },
4073 },4123 },
4074 .weak_external => unreachable,4124 .weak_external => unreachable,
4075 };4125 });
4076 sym.section_number = symbol.section_number;4126 sym.section_number = symbol.section_number;
4077 }4127 }
40784128
4079 defer log.debug("addInputSymbol({s}, 0x{x}, {t}=0x{x}) = {d}@{d}", .{4129 log.debug("addInputSymbol({s}, 0x{x}, {t}=0x{x}) = {d}@{d}", .{
4080 symbol.name.toSlice(coff),4130 symbol.name.toSlice(coff),
4081 index,4131 index,
4082 symbol.value,4132 symbol.value,
...@@ -4141,20 +4191,21 @@ fn loadObject(...@@ -4141,20 +4191,21 @@ fn loadObject(
4141 pending_symbols.sortUnstable(SortContext{ .v = pending_symbols.values() });4191 pending_symbols.sortUnstable(SortContext{ .v = pending_symbols.values() });
41424192
4143 try coff.input_symbols.ensureUnusedCapacity(gpa, num_included_symbols + num_included_sections);4193 try coff.input_symbols.ensureUnusedCapacity(gpa, num_included_symbols + num_included_sections);
4144 var prev_sn: Symbol.SectionNumber = .UNDEFINED;4194 var prev_sn: Symbol.SectionNumber = .DEBUG;
4145 var include_section = true;4195 var include_section = false;
4146 for (pending_symbols.values()) |symbol| {4196 for (pending_symbols.values()) |symbol| {
4147 // The symbol may have not been included, or it's an undefined external4197 // The symbol may have not been included, or it's an undefined external
4148 if (symbol.si == .null or symbol.si.get(coff).ni == .none) continue;4198 if (symbol.si == .null or symbol.si.get(coff).ni == .none) continue;
41494199
4150 if (prev_sn != symbol.section_number) {4200 if (prev_sn != symbol.section_number) {
4151 prev_sn = symbol.section_number;4201 prev_sn = symbol.section_number;
41524202 if (symbol.section_number.hasIndex()) {
4153 const section = &sections[symbol.section_number.toIndex()];4203 const section = &sections[symbol.section_number.toIndex()];
4154 include_section = section.comdat_result == .include;4204 include_section = section.comdat_result == .include;
4155 if (include_section) {4205 if (include_section) {
4156 const isi = coff.getNode(section.si.get(coff).ni).input_section;4206 const isi = coff.getNode(section.si.get(coff).ni).input_section;
4157 isi.inputSection(coff).first_li = @enumFromInt(coff.input_symbols.items.len);4207 isi.inputSection(coff).first_li = @enumFromInt(coff.input_symbols.items.len);
4208 }
4158 }4209 }
4159 }4210 }
41604211
...@@ -4329,7 +4380,7 @@ fn loadArchive(coff: *Coff, path: std.Build.Cache.Path, fr: *Io.File.Reader) !vo...@@ -4329,7 +4380,7 @@ fn loadArchive(coff: *Coff, path: std.Build.Cache.Path, fr: *Io.File.Reader) !vo
4329 var pos = fr.logicalPos();4380 var pos = fr.logicalPos();
4330 const size = try fr.getSize();4381 const size = try fr.getSize();
4331 while (pos < size) : (pos = fr.logicalPos()) {4382 while (pos < size) : (pos = fr.logicalPos()) {
4332 if ((pos & 1) != 0) r.toss(1);4383 if ((pos & 1) != 0) try r.discardAll(1);
4333 const header = try r.takeStruct(std.coff.ArchiveMemberHeader, target_endian);4384 const header = try r.takeStruct(std.coff.ArchiveMemberHeader, target_endian);
4334 const res = try parseArchiveMemberHeader(diags, path, &header, opt_longnames);4385 const res = try parseArchiveMemberHeader(diags, path, &header, opt_longnames);
43354386
...@@ -4354,7 +4405,7 @@ fn loadArchive(coff: *Coff, path: std.Build.Cache.Path, fr: *Io.File.Reader) !vo...@@ -4354,7 +4405,7 @@ fn loadArchive(coff: *Coff, path: std.Build.Cache.Path, fr: *Io.File.Reader) !vo
43544405
4355 const num_members = try r.takeInt(u32, target_endian);4406 const num_members = try r.takeInt(u32, target_endian);
4356 pos = fr.logicalPos();4407 pos = fr.logicalPos();
4357 if (pos + num_members * 4 > member_end)4408 if (pos + num_members * @sizeOf(u32) > member_end)
4358 return diags.failParse(path, "invalid member count 0x{x} in second linker member", .{num_members});4409 return diags.failParse(path, "invalid member count 0x{x} in second linker member", .{num_members});
43594410
4360 try members.ensureTotalCapacity(gpa, num_members);4411 try members.ensureTotalCapacity(gpa, num_members);
...@@ -4366,7 +4417,7 @@ fn loadArchive(coff: *Coff, path: std.Build.Cache.Path, fr: *Io.File.Reader) !vo...@@ -4366,7 +4417,7 @@ fn loadArchive(coff: *Coff, path: std.Build.Cache.Path, fr: *Io.File.Reader) !vo
43664417
4367 const num_symbols = try r.takeInt(u32, target_endian);4418 const num_symbols = try r.takeInt(u32, target_endian);
4368 pos = fr.logicalPos();4419 pos = fr.logicalPos();
4369 if (pos + num_symbols * 2 > member_end)4420 if (pos + num_symbols * @sizeOf(u16) > member_end)
4370 return diags.failParse(path, "invalid symbol count 0x{x} in second linker member", .{num_symbols});4421 return diags.failParse(path, "invalid symbol count 0x{x} in second linker member", .{num_symbols});
43714422
4372 try symbol_member_indices.ensureTotalCapacity(gpa, num_symbols);4423 try symbol_member_indices.ensureTotalCapacity(gpa, num_symbols);
...@@ -4532,7 +4583,9 @@ fn loadArchive(coff: *Coff, path: std.Build.Cache.Path, fr: *Io.File.Reader) !vo...@@ -4532,7 +4583,9 @@ fn loadArchive(coff: *Coff, path: std.Build.Cache.Path, fr: *Io.File.Reader) !vo
4532 };4583 };
4533 } else {4584 } else {
4534 member.content.object.size = res.size;4585 member.content.object.size = res.size;
4535 if (machine != expected_machine) {4586 // TODO: If .UNKNOWN assert later that it contains no non-undef symbols?
4587 // Microsoft's CRT contains members that set .UNKNOWN but do have symbols
4588 if (machine != expected_machine and machine != .UNKNOWN) {
4536 return diags.failParse(path, "machine mismatch in member header '{s}': expected {t}, found {t}", .{4589 return diags.failParse(path, "machine mismatch in member header '{s}': expected {t}, found {t}", .{
4537 res.name,4590 res.name,
4538 expected_machine,4591 expected_machine,
...@@ -4935,9 +4988,10 @@ fn reportUndefs(coff: *Coff, tid: Zcu.PerThread.Id) !void {...@@ -4935,9 +4988,10 @@ fn reportUndefs(coff: *Coff, tid: Zcu.PerThread.Id) !void {
4935 });4988 });
4936 }4989 }
4937 },4990 },
4938 .global => |gmi| err.addNote("referenced by '{s}' in module '{s}'", .{4991 .import_thunk => |gmi| err.addNote("referenced by import thunk for '{s}'", .{
4939 gmi.globalName(coff).name.toSlice(coff),4992 gmi.globalName(coff).name.toSlice(coff),
4940 comp.zcu.?.root_mod.fully_qualified_name,4993 // TODO: This won't always have a ZCU
4994 //comp.zcu.?.root_mod.fully_qualified_name,
4941 }),4995 }),
4942 inline .nav,4996 inline .nav,
4943 .uav,4997 .uav,
...@@ -5099,7 +5153,7 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {...@@ -5099,7 +5153,7 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
5099 if (sym.ni != .none)5153 if (sym.ni != .none)
5100 coff.getNode(sym.ni)5154 coff.getNode(sym.ni)
5101 else5155 else
5102 .{ .global = pending_si.key.get(coff).gmi },5156 .{ .import_thunk = pending_si.key.get(coff).gmi },
5103 );5157 );
5104 defer sub_prog_node.end();5158 defer sub_prog_node.end();
5105 coff.flushSymbolTableEntry(5159 coff.flushSymbolTableEntry(
...@@ -5229,7 +5283,7 @@ fn idleProgNode(...@@ -5229,7 +5283,7 @@ fn idleProgNode(
5229 coff.getNode(isi.symbol(coff).node(coff).parent(&coff.mf)).object_section.name(coff).toSlice(coff),5283 coff.getNode(isi.symbol(coff).node(coff).parent(&coff.mf)).object_section.name(coff).toSlice(coff),
5230 }) catch &name;5284 }) catch &name;
5231 },5285 },
5232 .global => |gmi| gmi.globalName(coff).name.toSlice(coff),5286 .import_thunk => |gmi| gmi.globalName(coff).name.toSlice(coff),
5233 .nav => |nmi| {5287 .nav => |nmi| {
5234 const ip = &coff.base.comp.zcu.?.intern_pool;5288 const ip = &coff.base.comp.zcu.?.intern_pool;
5235 break :name ip.getNav(nmi.navIndex(coff)).fqn.toSlice(ip);5289 break :name ip.getNav(nmi.navIndex(coff)).fqn.toSlice(ip);
...@@ -5306,10 +5360,45 @@ fn flushUav(...@@ -5306,10 +5360,45 @@ fn flushUav(
5306 si.applyLocationRelocs(coff);5360 si.applyLocationRelocs(coff);
5307}5361}
53085362
5363fn aliasGlobal(coff: *Coff, gmi: Node.GlobalMapIndex, alias_si: Symbol.Index) !void {
5364 const gn = gmi.globalName(coff);
5365 const si = gmi.symbol(coff);
5366 const sym = si.get(coff);
5367 assert(sym.section_number == .UNDEFINED);
5368 assert(sym.loc_relocs == .none);
5369
5370 const alias_sym = alias_si.get(coff);
5371 var ri = sym.target_relocs;
5372 while (ri != .none) {
5373 const reloc = ri.get(coff);
5374 assert(reloc.target == si);
5375 reloc.target = alias_si;
5376 if (reloc.next == .none) {
5377 reloc.next = alias_sym.target_relocs;
5378 if (alias_sym.target_relocs != .none)
5379 alias_sym.target_relocs.get(coff).prev = ri;
5380 }
5381 ri = reloc.next;
5382 }
5383
5384 sym.target_relocs = .none;
5385 sym.gmi = alias_sym.gmi;
5386 coff.globals.values()[gmi.unwrap().?] = alias_si;
5387 alias_si.applyTargetRelocs(coff);
5388
5389 log.debug("aliasGlobal({s}, {?s}) {d}->{d} ({?s})", .{
5390 gn.name.toSlice(coff),
5391 gn.lib_name.toSlice(coff),
5392 si,
5393 alias_si,
5394 if (alias_sym.gmi != .none) alias_sym.gmi.globalName(coff).name.toSlice(coff) else null,
5395 });
5396}
5397
5309fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {5398fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
5310 const comp = coff.base.comp;5399 const comp = coff.base.comp;
5311 const gpa = comp.gpa;5400 const gpa = comp.gpa;
5312 const gn = gmi.globalNameMutable(coff);5401 const gn = gmi.globalName(coff);
5313 const si = gmi.symbol(coff);5402 const si = gmi.symbol(coff);
5314 const sym = si.get(coff);5403 const sym = si.get(coff);
53155404
...@@ -5329,112 +5418,102 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {...@@ -5329,112 +5418,102 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
5329 return true;5418 return true;
5330 }5419 }
53315420
5421 {
5422 // Resolve unresolved .WEAK_EXTERNAL symbols to their aliases
5423 const alias_si = sym.weakAlias();
5424 if (alias_si != .null) {
5425 try coff.aliasGlobal(gmi, alias_si);
5426 return true;
5427 }
5428 }
5429
5332 const Import = struct {5430 const Import = struct {
5333 lib_name: String,5431 lib_name: String,
5334 ref: union(enum) {5432 name: String.Optional,
5335 name: struct {5433 ordinal_hint: u16,
5336 str: []const u8,5434 kind: enum {
5337 hint: ?u16,5435 iat_ptr,
5338 },5436 thunk,
5339 ordinal: u16,
5340 },5437 },
5341 };5438 };
53425439
5343 const opt_import: ?Import = if (gn.lib_name == .none and sym.ni == .none) import: {5440 const opt_import: ?Import = if (sym.ni == .none) import: {
5344 switch (sym.value) {5441 const global_name = gn.name.toSlice(coff);
5345 .alias_si => |alias_si| {5442 const imp_match = std.mem.startsWith(u8, global_name, imp_prefix);
5346 assert(sym.section_number == .UNDEFINED);5443
5347 assert(sym.loc_relocs == .none);5444 // Globals may have the __imp_ prefix already if they are undef externals from another input.
53485445 const search_name, const is_imp = if (imp_match or sym.flags.dll_storage_class != .dllimport)
5349 const alias_sym = alias_si.get(coff);5446 .{ gn.name, imp_match }
5350 var ri = sym.target_relocs;5447 else name: {
5351 while (ri != .none) {5448 try coff.ensureUnusedStringCapacity(imp_prefix.len + global_name.len);
5352 const reloc = ri.get(coff);5449 const name = try std.fmt.allocPrint(gpa, imp_prefix ++ "{s}", .{global_name});
5353 assert(reloc.target == si);5450 defer gpa.free(name);
5354 reloc.target = alias_si;5451 break :name .{ coff.getOrPutStringAssumeCapacity(name), true };
5355 if (reloc.next == .none) {5452 };
5356 reloc.next = alias_sym.target_relocs;
5357 if (alias_sym.target_relocs != .none)
5358 alias_sym.target_relocs.get(coff).prev = ri;
5359 }
5360 ri = reloc.next;
5361 }
5362
5363 sym.target_relocs = .none;
5364 coff.globals.values()[gmi.unwrap().?] = alias_si;
5365 alias_si.applyTargetRelocs(coff);
5366
5367 log.debug(
5368 "flushGlobal({s}, null) alias {d}->{d}",
5369 .{ gmi.globalName(coff).name.toSlice(coff), si, alias_si },
5370 );
5371 return true;
5372 },
5373 .size => {},
5374 .input_offset => unreachable,
5375 }
53765453
5377 if (coff.input_archive_symbol_indices.get(gmi.globalName(coff).name)) |index| {5454 if (coff.input_archive_symbol_indices.get(search_name)) |indices_list| {
5378 var iter: InputArchive.Member.Symbol.Index = index.first;5455 var iter: InputArchive.Member.Symbol.Index = indices_list.first;
5379 while (true) {5456 while (true) {
5380 const archive_sym = &coff.input_archive_symbols.items[@intFromEnum(iter)];5457 const archive_sym = &coff.input_archive_symbols.items[@intFromEnum(iter)];
5381 const member = &coff.input_archive_members.items[@intFromEnum(archive_sym.iami)];5458 const member = &coff.input_archive_members.items[@intFromEnum(archive_sym.iami)];
5382 if (!member.flags.is_loaded) {5459 member: switch (member.content) {
5383 switch (member.content) {5460 .object => if (!member.flags.is_loaded) {
5384 .import => |import| switch (import.type) {5461 if (gn.lib_name.unwrap()) |lib_name|
5385 .CODE,5462 if (!std.mem.eql(u8, lib_name.toSlice(coff), member.iai.path(coff).stem()))
5386 .DATA,5463 break :member;
5387 => {5464
5388 defer member.flags.is_loaded = true;5465 // Try loading the input member and then retry.
5389 // gn.lib_name = import.lib_name.toOptional();5466 // This could still be a member containing imports
5390 // try coff.globals.setKey(gpa, gmi.unwrap().?, gn.*);5467 // that use the older non-IMPORT_HEADER method.
53915468 coff.pending_input = archive_sym.iami;
5392 // Switch this global to an import5469 return false;
5393 switch (import.name_type) {5470 },
5394 .NAME,5471 .import => |import| {
5395 .NAME_NOPREFIX,5472 if (gn.lib_name.unwrap()) |lib_name|
5396 .NAME_UNDECORATE,5473 if (import.lib_name != lib_name)
5397 => |tag| {5474 break :member;
5398 var name: []const u8 = import.symbol_name.toSlice(coff);5475
5399 if (!(std.mem.eql(u8, name, gn.name.toSlice(coff))))5476 const name: String.Optional = name: switch (import.name_type) {
5400 return comp.link_diags.fail("import '{s}' has mismatched symbol name: '{s}'", .{5477 .NAME,
5401 import.symbol_name.toSlice(coff),5478 .NAME_NOPREFIX,
5402 gn.name.toSlice(coff),5479 .NAME_UNDECORATE,
5403 });5480 => |tag| {
54045481 const symbol_name: []const u8 = import.symbol_name.toSlice(coff);
5405 name = if (tag == .NAME) name else name: {5482 const end_match = std.mem.endsWith(u8, global_name, symbol_name);
5406 name = std.mem.trimStart(u8, name, "?@_");5483 const len_delta = global_name.len -% symbol_name.len;
5407 if (tag == .NAME_UNDECORATE)5484 if (!end_match or
5408 name = std.mem.sliceTo(name, '@');5485 (!imp_match and len_delta != 0) or
5409 break :name name;5486 (imp_match and len_delta != imp_prefix.len))
5410 };5487 return comp.link_diags.fail(
54115488 "global '{s}' has mismatched symbol name in import header: '{s}'",
5412 break :import .{5489 .{
5413 .lib_name = import.lib_name,5490 gn.name.toSlice(coff),
5414 .ref = .{5491 import.symbol_name.toSlice(coff),
5415 .name = .{5492 },
5416 .str = name,5493 );
5417 .hint = import.import_ordinal_hint,5494
5418 },5495 const name = if (tag == .NAME) import.symbol_name else undecorated: {
5419 },5496 var imp_name = std.mem.trimStart(u8, symbol_name, "?@_");
5420 };5497 if (tag == .NAME_UNDECORATE)
5421 },5498 imp_name = std.mem.sliceTo(imp_name, '@');
5422 .ORDINAL => break :import .{5499
5423 .lib_name = import.lib_name,5500 try coff.ensureUnusedStringCapacity(imp_name.len);
5424 .ref = .{ .ordinal = import.import_ordinal_hint },5501 break :undecorated coff.getOrPutStringAssumeCapacity(imp_name);
5425 },5502 };
5426 else => |t| return comp.link_diags.fail("TODO handle name_type {t}", .{t}),5503
5427 }5504 break :name name.toOptional();
5428 },5505 },
5429 .CONST => return comp.link_diags.fail("TODO handle import type CONST", .{}),5506 .ORDINAL => break :name .none,
5430 else => |t| return comp.link_diags.fail("invalid import type: {d}", .{t}),5507 else => |t| return comp.link_diags.fail("TODO handle name_type {t}", .{t}),
5431 },5508 };
5432 .object => {5509
5433 // Try loading the input member and then retry5510 break :import .{
5434 coff.pending_input = archive_sym.iami;5511 .lib_name = import.lib_name,
5435 return false;5512 .name = name,
5436 },5513 .ordinal_hint = import.import_ordinal_hint,
5437 }5514 .kind = if (import.type == .CODE and !is_imp) .thunk else .iat_ptr,
5515 };
5516 },
5438 }5517 }
54395518
5440 if (archive_sym.next == iter) break;5519 if (archive_sym.next == iter) break;
...@@ -5442,28 +5521,20 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {...@@ -5442,28 +5521,20 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
5442 }5521 }
5443 }5522 }
54445523
5445 break :import null;5524 // Allow importing symbols with no implib entry, if a lib_name was specified.
5446 } else if (gn.lib_name.unwrap()) |lib_name| .{5525 // This is necessary for certain ntdll symbols, such as LdrRegisterDllNotification,
5447 .lib_name = lib_name,5526 // which are not in the implib.
5448 .ref = .{5527 break :import if (gn.lib_name.unwrap()) |lib_name| .{
5449 .name = .{5528 .lib_name = lib_name,
5450 .str = gn.name.toSlice(coff),5529 .name = gn.name.toOptional(),
5451 .hint = null,5530 .ordinal_hint = 0,
5452 },5531 .kind = .iat_ptr,
5453 },5532 } else null;
5454 } else null;5533 } else null;
54555534
5456 if (opt_import) |import| {5535 if (opt_import) |import| {
5457 assert(sym.ni == .none);5536 assert(sym.ni == .none);
5458 const lib_name = import.lib_name.toSlice(coff);5537 const lib_name = import.lib_name.toSlice(coff);
5459 const name = switch (import.ref) {
5460 .name => |n| n.str,
5461 .ordinal => return comp.link_diags.fail("TODO handle imports via ordinal", .{}),
5462 };
5463
5464 log.debug("flushGlobalImport({s}, {s})", .{ name, lib_name });
5465
5466 // TODO: Handle hint
54675538
5468 try coff.nodes.ensureUnusedCapacity(gpa, 4);5539 try coff.nodes.ensureUnusedCapacity(gpa, 4);
5469 try coff.symbols.ensureUnusedCapacity(gpa, 1);5540 try coff.symbols.ensureUnusedCapacity(gpa, 1);
...@@ -5547,76 +5618,138 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {...@@ -5547,76 +5618,138 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
5547 if (target_endian != native_endian)5618 if (target_endian != native_endian)
5548 std.mem.byteSwapAllFields([2]std.coff.ImportDirectoryEntry, import_directory_entries);5619 std.mem.byteSwapAllFields([2]std.coff.ImportDirectoryEntry, import_directory_entries);
5549 }5620 }
5550 const import_symbol_index = gop.value_ptr.len;5621
5551 gop.value_ptr.len = import_symbol_index + 1;5622 log.debug(
5552 const new_symbol_table_size = addr_size * (import_symbol_index + 2);5623 "flushGlobalImport({s}, {?s}, {d}, {s})",
5553 const import_hint_name_index = gop.value_ptr.hint_name_len;5624 .{ gn.name.toSlice(coff), import.name.toSlice(coff), import.ordinal_hint, lib_name },
5554 gop.value_ptr.hint_name_len = @intCast(
5555 import_hint_name_align.forward(import_hint_name_index + 2 + name.len + 1),
5556 );5625 );
5557 try gop.value_ptr.import_lookup_table_ni.resize(&coff.mf, gpa, new_symbol_table_size);5626
5558 const import_address_table_ni = gop.value_ptr.import_address_table_si.node(coff);5627 const iat_symbol_gop = try coff.import_table.iat_symbol_indices.getOrPut(gpa, .{
5559 try import_address_table_ni.resize(&coff.mf, gpa, new_symbol_table_size);5628 .iti = @enumFromInt(gop.index),
5560 try gop.value_ptr.import_hint_name_table_ni.resize(&coff.mf, gpa, gop.value_ptr.hint_name_len);5629 .name = import.name,
5561 const import_lookup_slice = gop.value_ptr.import_lookup_table_ni.slice(&coff.mf);5630 .ordinal_hint = import.ordinal_hint,
5562 const import_address_slice = import_address_table_ni.slice(&coff.mf);5631 });
5563 const import_hint_name_slice = gop.value_ptr.import_hint_name_table_ni.slice(&coff.mf);5632 if (!iat_symbol_gop.found_existing) {
5564 @memset(import_hint_name_slice[import_hint_name_index..][0..2], 0);5633 const import_symbol_index = gop.value_ptr.len;
5565 @memcpy(import_hint_name_slice[import_hint_name_index + 2 ..][0..name.len], name);5634 iat_symbol_gop.value_ptr.* = import_symbol_index;
5566 @memset(import_hint_name_slice[import_hint_name_index + 2 + name.len ..], 0);5635
5567 const import_hint_name_rva =5636 gop.value_ptr.len = import_symbol_index + 1;
5568 coff.computeNodeRva(gop.value_ptr.import_hint_name_table_ni) + import_hint_name_index;5637 const new_symbol_table_size = addr_size * (import_symbol_index + 2);
5569 switch (magic) {5638
5570 _ => unreachable,5639 const opt_name = import.name.toSlice(coff);
5571 inline .PE32, .@"PE32+" => |ct_magic| {5640 const opt_import_hint_name_index = if (opt_name) |name| blk: {
5572 const Addr = switch (ct_magic) {5641 const import_hint_name_index = gop.value_ptr.hint_name_len;
5573 _ => comptime unreachable,5642 gop.value_ptr.hint_name_len = @intCast(
5574 .PE32 => u32,5643 import_hint_name_align.forward(import_hint_name_index + 2 + name.len + 1),
5575 .@"PE32+" => u64,5644 );
5576 };5645 break :blk import_hint_name_index;
5577 const import_lookup_table: []Addr = @ptrCast(@alignCast(import_lookup_slice));5646 } else null;
5578 const import_address_table: []Addr = @ptrCast(@alignCast(import_address_slice));5647
5579 const import_hint_name_rvas: [2]Addr = .{5648 try gop.value_ptr.import_lookup_table_ni.resize(&coff.mf, gpa, new_symbol_table_size);
5580 std.mem.nativeTo(Addr, @intCast(import_hint_name_rva), target_endian),5649 const import_address_table_ni = gop.value_ptr.import_address_table_si.node(coff);
5581 std.mem.nativeTo(Addr, 0, target_endian),5650 try import_address_table_ni.resize(&coff.mf, gpa, new_symbol_table_size);
5582 };5651 try gop.value_ptr.import_hint_name_table_ni.resize(&coff.mf, gpa, gop.value_ptr.hint_name_len);
5583 import_lookup_table[import_symbol_index..][0..2].* = import_hint_name_rvas;5652
5584 import_address_table[import_symbol_index..][0..2].* = import_hint_name_rvas;5653 const import_hint_name_rva = if (opt_import_hint_name_index) |import_hint_name_index| blk: {
5585 },5654 const import_hint_name_slice = gop.value_ptr.import_hint_name_table_ni.slice(&coff.mf);
5655 const ordinal_hint: *u16 = @ptrCast(@alignCast(import_hint_name_slice[import_hint_name_index..][0..2]));
5656 ordinal_hint.* = std.mem.nativeTo(u16, import.ordinal_hint, target_endian);
5657 @memcpy(import_hint_name_slice[import_hint_name_index + 2 ..][0..opt_name.?.len], opt_name.?);
5658 @memset(import_hint_name_slice[import_hint_name_index + 2 + opt_name.?.len ..], 0);
5659 break :blk coff.computeNodeRva(gop.value_ptr.import_hint_name_table_ni) + import_hint_name_index;
5660 } else 0;
5661
5662 const import_lookup_slice = gop.value_ptr.import_lookup_table_ni.slice(&coff.mf);
5663 const import_address_slice = import_address_table_ni.slice(&coff.mf);
5664 switch (magic) {
5665 _ => unreachable,
5666 inline .PE32, .@"PE32+" => |ct_magic| {
5667 const Payload = packed union(u31) {
5668 ordinal: packed struct(u31) {
5669 ordinal: u16,
5670 _: u15 = 0,
5671 },
5672 hint_name_rva: u31,
5673 };
5674
5675 const Entry = switch (ct_magic) {
5676 _ => comptime unreachable,
5677 .PE32 => packed struct(u32) {
5678 payload: Payload,
5679 is_ordinal: bool,
5680 },
5681 .@"PE32+" => packed struct(u64) {
5682 payload: Payload,
5683 _: u32 = 0,
5684 is_ordinal: bool,
5685 },
5686 };
5687 const import_lookup_table: []Entry = @ptrCast(@alignCast(import_lookup_slice));
5688 const import_address_table: []Entry = @ptrCast(@alignCast(import_address_slice));
5689 const import_hint_name_rvas: [2]Entry = .{
5690 .{
5691 .payload = if (import.name == .none)
5692 .{ .ordinal = .{ .ordinal = import.ordinal_hint } }
5693 else
5694 .{ .hint_name_rva = @intCast(import_hint_name_rva) },
5695 .is_ordinal = import.name == .none,
5696 },
5697 @bitCast(@as(@typeInfo(Entry).@"struct".backing_integer.?, 0)),
5698 };
5699 if (native_endian != target_endian)
5700 for (import_hint_name_rvas) |*v| std.mem.byteSwapAllFields(Entry, v);
5701
5702 import_lookup_table[import_symbol_index..][0..2].* = import_hint_name_rvas;
5703 import_address_table[import_symbol_index..][0..2].* = import_hint_name_rvas;
5704 },
5705 }
5586 }5706 }
5587 sym.section_number = Symbol.Index.text.get(coff).section_number;5707
5588 assert(sym.loc_relocs == .none);5708 assert(sym.loc_relocs == .none);
5589 sym.loc_relocs = @enumFromInt(coff.relocs.items.len);5709 const iat_offset: u32 = @intCast(addr_size * iat_symbol_gop.value_ptr.*);
5590 switch (coff.targetLoad(&coff.headerPtr().machine)) {5710 switch (import.kind) {
5591 else => |tag| @panic(@tagName(tag)),5711 .iat_ptr => {
5592 .AMD64 => {5712 const iat_sym = gop.value_ptr.import_address_table_si.get(coff);
5593 const init = [_]u8{ 0xff, 0x25, 0x00, 0x00, 0x00, 0x00 };5713 sym.section_number = iat_sym.section_number;
5594 const target = &comp.root_mod.resolved_target.result;5714 sym.ni = iat_sym.ni;
5595 const ni = try coff.mf.addLastChildNode(gpa, Symbol.Index.text.node(coff), .{5715 sym.setValue(.{ .node_offset = iat_offset });
5596 .alignment = switch (comp.root_mod.optimize_mode) {5716 si.flushMoved(coff);
5597 .Debug,5717 },
5598 .ReleaseSafe,5718 .thunk => {
5599 .ReleaseFast,5719 sym.section_number = Symbol.Index.text.get(coff).section_number;
5600 => target_util.defaultFunctionAlignment(target),5720 sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
5601 .ReleaseSmall => target_util.minFunctionAlignment(target),5721 switch (coff.targetLoad(&coff.headerPtr().machine)) {
5602 }.toStdMem(),5722 else => |tag| @panic(@tagName(tag)),
5603 .size = init.len,5723 .AMD64 => {
5604 });5724 const init = [_]u8{ 0xff, 0x25, 0x00, 0x00, 0x00, 0x00 };
5605 @memcpy(ni.slice(&coff.mf)[0..init.len], &init);5725 const target = &comp.root_mod.resolved_target.result;
5606 sym.ni = ni;5726 const ni = try coff.mf.addLastChildNode(gpa, Symbol.Index.text.node(coff), .{
5607 sym.value.size = init.len;5727 .alignment = switch (comp.root_mod.optimize_mode) {
5608 try coff.addReloc(5728 .Debug,
5609 si,5729 .ReleaseSafe,
5610 init.len - 4,5730 .ReleaseFast,
5611 gop.value_ptr.import_address_table_si,5731 => target_util.defaultFunctionAlignment(target),
5612 .{ .known = @intCast(addr_size * import_symbol_index) },5732 .ReleaseSmall => target_util.minFunctionAlignment(target),
5613 .{ .AMD64 = .REL32 },5733 }.toStdMem(),
5614 );5734 .size = init.len,
5735 });
5736 @memcpy(ni.slice(&coff.mf)[0..init.len], &init);
5737 sym.ni = ni;
5738 sym.setValue(.{ .size = init.len });
5739 try coff.addReloc(
5740 si,
5741 init.len - 4,
5742 gop.value_ptr.import_address_table_si,
5743 .{ .known = iat_offset },
5744 .{ .AMD64 = .REL32 },
5745 );
5746 },
5747 }
5748 coff.nodes.appendAssumeCapacity(.{ .import_thunk = gmi });
5749 sym.rva = coff.computeNodeRva(sym.ni);
5750 si.applyLocationRelocs(coff);
5615 },5751 },
5616 }5752 }
5617 coff.nodes.appendAssumeCapacity(.{ .global = gmi });
5618 sym.rva = coff.computeNodeRva(sym.ni);
5619 si.applyLocationRelocs(coff);
5620 }5753 }
56215754
5622 return true;5755 return true;
...@@ -5845,7 +5978,7 @@ fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void {...@@ -5845,7 +5978,7 @@ fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void {
5845 },5978 },
5846 inline .pseudo_section,5979 inline .pseudo_section,
5847 .object_section,5980 .object_section,
5848 .global,5981 .import_thunk,
5849 .nav,5982 .nav,
5850 .uav,5983 .uav,
5851 .lazy_code,5984 .lazy_code,
...@@ -5980,7 +6113,7 @@ fn flushResized(coff: *Coff, ni: MappedFile.Node.Index) !void {...@@ -5980,7 +6113,7 @@ fn flushResized(coff: *Coff, ni: MappedFile.Node.Index) !void {
59806113
5981 smi.symbol(coff).get(coff).value.size = @intCast(size);6114 smi.symbol(coff).get(coff).value.size = @intCast(size);
5982 },6115 },
5983 .global,6116 .import_thunk,
5984 .nav,6117 .nav,
5985 .uav,6118 .uav,
5986 .lazy_code,6119 .lazy_code,
...@@ -6148,7 +6281,7 @@ fn updateExportsInner(...@@ -6148,7 +6281,7 @@ fn updateExportsInner(
6148 const export_sym = export_si.get(coff);6281 const export_sym = export_si.get(coff);
6149 export_sym.ni = exported_ni;6282 export_sym.ni = exported_ni;
6150 export_sym.rva = exported_sym.rva;6283 export_sym.rva = exported_sym.rva;
6151 export_sym.value.size = exported_sym.value.size;6284 export_sym.setValue(.{ .size = exported_sym.value.size });
6152 export_sym.section_number = exported_sym.section_number;6285 export_sym.section_number = exported_sym.section_number;
6153 defer export_si.applyTargetRelocs(coff);6286 defer export_si.applyTargetRelocs(coff);
61546287
...@@ -6308,7 +6441,7 @@ pub fn printNode(...@@ -6308,7 +6441,7 @@ pub fn printNode(
6308 inline .pseudo_section, .object_section => |smi| try w.print("({s})", .{6441 inline .pseudo_section, .object_section => |smi| try w.print("({s})", .{
6309 smi.name(coff).toSlice(coff),6442 smi.name(coff).toSlice(coff),
6310 }),6443 }),
6311 .global => |gmi| {6444 .import_thunk => |gmi| {
6312 const gn = gmi.globalName(coff);6445 const gn = gmi.globalName(coff);
6313 try w.writeByte('(');6446 try w.writeByte('(');
6314 if (gn.lib_name.toSlice(coff)) |lib_name| try w.print("{s}.dll, ", .{lib_name});6447 if (gn.lib_name.toSlice(coff)) |lib_name| try w.print("{s}.dll, ", .{lib_name});