authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2026-06-05 01:55:34-04:00
committergravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2026-06-23 00:22:40-04:00
loge12274f2bcae4a9aebac788bf57988e4fdf7ba10
tree997b08ebeec1038f013606314a484fdcca886aca
parent01db3d49535d59c70de042c60ed524bca72e31f2

- Skip the !use_llvm shared library tests on faiiling platforms

- Outputting correct linker members

8 files changed, 936 insertions(+), 138 deletions(-)

lib/std/coff.zig+27
......@@ -1963,3 +1963,30 @@ pub const IMAGE = struct {
19631963 };
19641964 };
19651965};
1966
1967pub const ArchiveMemberHeader = extern struct {
1968 /// Left-justified '/' terminated member name
1969 name: [16]u8,
1970 /// Left-justified ASCII decimal: seconds since January 1st, 1970
1971 date: [12]u8,
1972 /// Left-justified ASCII decimal: user id
1973 user_id: [6]u8,
1974 /// Left-justified ASCII decimal: group id
1975 group_id: [6]u8,
1976 /// Left-justified ASCII octal: file mode
1977 file_mode: [8]u8,
1978 /// Left-justified ASCII decimal: size of the member following this header,
1979 /// not including the size of this header.
1980 size: [10]u8,
1981 /// The literal string '`\n'
1982 end_of_header: [2]u8,
1983};
1984
1985pub const FirstLinkerMemberHeader = extern struct {
1986 /// Big-endian symbol count
1987 number_of_symbols: u32,
1988};
1989
1990pub const SecondLinkerMemberHeader = extern struct {
1991 number_of_members: u32,
1992};
lib/std/start.zig+1-1
......@@ -93,7 +93,7 @@ fn DllMainCRTStartup(
9393 fdwReason: std.os.windows.DWORD,
9494 lpReserved: std.os.windows.LPVOID,
9595) callconv(.winapi) std.os.windows.BOOL {
96 if (!builtin.single_threaded and !builtin.link_libc) {
96 if (!builtin.single_threaded) {
9797 _ = @import("os/windows/tls.zig");
9898 }
9999
src/crash_report.zig+28
......@@ -84,6 +84,25 @@ pub const CodegenFunc = if (enabled) struct {
8484 pub fn stop(_: InternPool.Index) void {}
8585};
8686
87pub const LinkerOp = if (enabled) struct {
88 lf: *link.File,
89 tid: Zcu.PerThread.Id,
90 threadlocal var current: ?LinkerOp = null;
91 pub fn start(lf: *link.File, tid: Zcu.PerThread.Id) void {
92 std.debug.assert(current == null);
93 current = .{ .lf = lf, .tid = tid };
94 }
95 pub fn stop(lf: *link.File, tid: Zcu.PerThread.Id) void {
96 std.debug.assert(current.?.lf == lf and current.?.tid == tid);
97 current = null;
98 }
99} else struct {
100 const current: ?noreturn = null;
101 // Dummy implementation
102 pub fn start(_: *link.File, _: Zcu.PerThread.Id) void {}
103 pub fn stop(_: *link.File, _: Zcu.PerThread.Id) void {}
104};
105
87106fn dumpCrashContext() Io.Writer.Error!void {
88107 const S = struct {
89108 /// In the case of recursive panics or segfaults, don't print the context for a second time.
......@@ -111,6 +130,14 @@ fn dumpCrashContext() Io.Writer.Error!void {
111130 try w.print("Generating function '{f}'\n\n", .{func_fqn.fmt(&cg.zcu.intern_pool)});
112131 } else if (AnalyzeBody.current) |anal| {
113132 try dumpCrashContextSema(anal, w, &S.crash_heap);
133 } else if (LinkerOp.current) |linker_op| {
134 try w.writeAll("Linker snapshot:\n\n");
135 if (build_options.enable_link_snapshots) {
136 try linker_op.lf.dump(w, linker_op.tid);
137 try w.writeAll("\n\n");
138 } else {
139 try w.print("(build with -Dlink-snapshot to dump linker state)", .{});
140 }
114141 } else {
115142 try w.writeAll("(no context)\n\n");
116143 }
......@@ -185,6 +212,7 @@ const Zir = std.zig.Zir;
185212
186213const Sema = @import("Sema.zig");
187214const Zcu = @import("Zcu.zig");
215const link = @import("link.zig");
188216const InternPool = @import("InternPool.zig");
189217const dev = @import("dev.zig");
190218const print_zir = @import("print_zir.zig");
src/link.zig+33-1
......@@ -25,6 +25,7 @@ const Package = @import("Package.zig");
2525const dev = @import("dev.zig");
2626const target_util = @import("target.zig");
2727const codegen = @import("codegen.zig");
28const crash_report = @import("crash_report.zig");
2829
2930pub const aarch64 = @import("link/aarch64.zig");
3031pub const LdScript = @import("link/LdScript.zig");
......@@ -790,6 +791,7 @@ pub const File = struct {
790791 assert(base.comp.zcu.?.llvm_object == null);
791792 const nav = pt.zcu.intern_pool.getNav(nav_index);
792793 assert(nav.resolved.?.value != .none);
794
793795 switch (base.tag) {
794796 .lld => unreachable,
795797 .plan9 => unreachable,
......@@ -924,6 +926,9 @@ pub const File = struct {
924926 /// Commit pending changes and write headers. Takes into account final output mode.
925927 /// `arena` has the lifetime of the call to `Compilation.update`.
926928 pub fn flush(base: *File, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) Error!void {
929 crash_report.LinkerOp.start(base, tid);
930 defer crash_report.LinkerOp.stop(base, tid);
931
927932 const comp = base.comp;
928933 const io = comp.io;
929934 if (comp.clang_preprocessor_mode == .yes or comp.clang_preprocessor_mode == .pch) {
......@@ -975,6 +980,10 @@ pub const File = struct {
975980 export_indices: []const Zcu.Export.Index,
976981 ) Error!void {
977982 assert(base.comp.zcu.?.llvm_object == null);
983
984 crash_report.LinkerOp.start(base, pt.tid);
985 defer crash_report.LinkerOp.stop(base, pt.tid);
986
978987 switch (base.tag) {
979988 .lld => unreachable,
980989 .plan9 => unreachable,
......@@ -1006,6 +1015,7 @@ pub const File = struct {
10061015 /// Never called when LLVM is codegenning the ZCU.
10071016 pub fn getNavVAddr(base: *File, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index, reloc_info: RelocInfo) Error!u64 {
10081017 assert(base.comp.zcu.?.llvm_object == null);
1018
10091019 switch (base.tag) {
10101020 .lld => unreachable,
10111021 .c => unreachable,
......@@ -1027,6 +1037,7 @@ pub const File = struct {
10271037 decl_align: InternPool.Alignment,
10281038 ) Error!SymbolId {
10291039 assert(base.comp.zcu.?.llvm_object == null);
1040
10301041 switch (base.tag) {
10311042 .lld => unreachable,
10321043 .c => unreachable,
......@@ -1043,6 +1054,7 @@ pub const File = struct {
10431054 /// Never called when LLVM is codegenning the ZCU.
10441055 pub fn getUavVAddr(base: *File, decl_val: InternPool.Index, reloc_info: RelocInfo) Error!u64 {
10451056 assert(base.comp.zcu.?.llvm_object == null);
1057
10461058 switch (base.tag) {
10471059 .lld => unreachable,
10481060 .c => unreachable,
......@@ -1063,6 +1075,7 @@ pub const File = struct {
10631075 name: InternPool.NullTerminatedString,
10641076 ) void {
10651077 assert(base.comp.zcu.?.llvm_object == null);
1078
10661079 switch (base.tag) {
10671080 .lld => unreachable,
10681081 .plan9 => unreachable,
......@@ -1077,6 +1090,24 @@ pub const File = struct {
10771090 }
10781091 }
10791092
1093 pub fn dump(base: *File, w: *Io.Writer, tid: Zcu.PerThread.Id) !void {
1094 if (!build_options.enable_link_snapshots) unreachable;
1095 switch (base.tag) {
1096 .elf,
1097 .macho,
1098 .c,
1099 .wasm,
1100 .spirv,
1101 .plan9,
1102 .lld,
1103 => {},
1104 inline else => |tag| {
1105 dev.check(tag.devFeature());
1106 return @as(*tag.Type(), @fieldParentPtr("base", base)).dump(w, tid);
1107 },
1108 }
1109 }
1110
10801111 /// Opens a path as an object file and parses it into the linker.
10811112 fn openLoadObject(base: *File, path: Path) anyerror!void {
10821113 if (base.tag == .lld) return;
......@@ -1178,8 +1209,9 @@ pub const File = struct {
11781209 pub fn loadInput(base: *File, input: Input) anyerror!void {
11791210 if (base.tag == .lld) return;
11801211 assert(!base.post_prelink);
1212
11811213 switch (base.tag) {
1182 inline .elf, .elf2, .wasm, .spirv => |tag| {
1214 inline .coff2, .elf, .elf2, .wasm, .spirv => |tag| {
11831215 dev.check(tag.devFeature());
11841216 return @as(*tag.Type(), @fieldParentPtr("base", base)).loadInput(input);
11851217 },
src/link/Coff.zig+826-124
......@@ -23,6 +23,11 @@ const implib = @import("../libs/mingw/implib.zig");
2323base: link.File,
2424mf: MappedFile,
2525nodes: std.MultiArrayList(Node),
26members: std.ArrayList(Member),
27pending_members: std.AutoArrayHashMapUnmanaged(Member.Index, void),
28lib_string_table: std.ArrayList(String),
29lib_string_len: u64,
30long_names_table: LongNamesTable,
2631import_table: ImportTable,
2732export_table: ExportTable,
2833strings: std.HashMapUnmanaged(
......@@ -137,18 +142,27 @@ pub const msdos_stub: [120]u8 = .{
137142pub const Node = union(enum) {
138143 file,
139144 header,
145 /// Images and archives only.
140146 signature,
147 /// Archives only.
148 archive_member_header: Member.Index,
149 archive_member: Member.Index,
150
141151 coff_header,
152 /// Image only
142153 optional_header,
154 /// Image only
143155 data_directories,
144156 section_table,
145157 image_section: Symbol.Index,
146158
159 /// Only images contain imports
147160 import_directory_table,
148161 import_lookup_table: ImportTable.Index,
149162 import_address_table: ImportTable.Index,
150163 import_hint_name_table: ImportTable.Index,
151164
165 /// Only images contain exports
152166 export_directory_table,
153167 export_address_table,
154168 export_name_pointer_table,
......@@ -163,6 +177,9 @@ pub const Node = union(enum) {
163177 lazy_code: LazyMapRef.Index(.code),
164178 lazy_const_data: LazyMapRef.Index(.const_data),
165179
180 /// Takes the place of a known node index when that node is not present in the output
181 placeholder,
182
166183 pub const PseudoSectionMapIndex = enum(u32) {
167184 _,
168185
......@@ -262,6 +279,14 @@ pub const Node = union(enum) {
262279 file,
263280 header,
264281 signature,
282 first_linker_member_header,
283 first_linker_member,
284 second_linker_member_header,
285 second_linker_member,
286 longnames_member_header,
287 longnames_member,
288 zcu_member_header,
289 zcu_member,
265290 coff_header,
266291 optional_header,
267292 data_directories,
......@@ -279,8 +304,141 @@ pub const Node = union(enum) {
279304 }
280305};
281306
307pub const Member = struct {
308 kind: Kind,
309 header_ni: MappedFile.Node.Index,
310 content_ni: MappedFile.Node.Index,
311 // Maps symbols contained in this member to their index in the first linker member's symbol table
312 // TODO: This could contain information about the name string if we need
313 symbol_offsets: std.AutoArrayHashMapUnmanaged(Symbol.Index, u33),
314
315 pub const Kind = enum {
316 first_linker,
317 second_linker,
318 longnames,
319 coff,
320 import,
321 };
322
323 pub const Index = enum(u16) {
324 first,
325 second,
326 longnames,
327 _,
328
329 const known_count = @typeInfo(Index).@"enum".fields.len;
330
331 pub fn get(member_index: Member.Index, coff: *Coff) *Member {
332 return &coff.members.items[@intFromEnum(member_index)];
333 }
334 };
335
336 pub fn headerPtr(member: *Member, coff: *Coff) *std.coff.ArchiveMemberHeader {
337 return @ptrCast(@alignCast(member.header_ni.slice(&coff.mf)));
338 }
339
340 pub fn initHeader(member: *Member, coff: *Coff, name: []const u8, timestamp: u32) !void {
341 const header = member.headerPtr(coff);
342 try storeHeaderName(coff, &header.name, name);
343 storeHeaderDecimalStr(&header.date, timestamp);
344
345 // Matching the Microsoft behaviour of emitting blanks for these fields
346 header.user_id = @splat(' ');
347 header.group_id = @splat(' ');
348
349 // file_mode is actually octal, but we only ever write 0 to it
350 storeHeaderDecimalStr(&header.file_mode, 0);
351 if (!member.content_ni.hasResized(&coff.mf))
352 storeHeaderDecimalStr(
353 &header.size,
354 member.content_ni.location(&coff.mf).resolve(&coff.mf)[1],
355 );
356
357 @memcpy(&header.end_of_header, "`\n");
358 }
359
360 /// Sets `name` as the name field of this member's header, either directly (if it's short enough),
361 /// or by creating an entry in the longnames member and storing a reference to that entry.
362 pub fn storeHeaderName(coff: *Coff, field: *[16]u8, name: []const u8) !void {
363 if (name.len < field.len) {
364 @memcpy(field[0..name.len], name);
365 field[name.len] = '/';
366 const padding = field.len - name.len - 1;
367 if (padding > 0) @memset(field[field.len - padding ..], ' ');
368 } else {
369 const gpa = coff.base.comp.gpa;
370 const entries_ctx = LongNamesTable.Adapter{ .coff = coff };
371 const gop = try coff.long_names_table.entries.getOrPutAdapted(
372 gpa,
373 name,
374 entries_ctx,
375 );
376
377 if (!gop.found_existing) {
378 errdefer _ = coff.export_table.entries.pop();
379
380 _, const old_size = Node.known.longnames_member.location(&coff.mf).resolve(&coff.mf);
381 const new_size = old_size + name.len + 1;
382 assert(new_size < comptime try std.math.powi(u64, 10, field.len - 1));
383
384 try Node.known.longnames_member.resize(&coff.mf, gpa, new_size);
385 const name_table_slice = Node.known.longnames_member.slice(&coff.mf);
386 const name_slice = name_table_slice[old_size..][0 .. name.len + 1];
387 @memcpy(name_slice[0..name.len], name);
388 name_slice[name.len] = 0;
389
390 gop.value_ptr.* = .{
391 .index = old_size,
392 .len = name.len,
393 };
394 }
395
396 field[0] = '/';
397 storeHeaderDecimalStr(field[1..], gop.value_ptr.index);
398 }
399 }
400
401 pub fn storeHeaderDecimalStr(field_ptr: anytype, value: u64) void {
402 const array_info = @typeInfo(@typeInfo(@TypeOf(field_ptr)).pointer.child).array;
403 assert(array_info.child == u8);
404 assert(value < comptime try std.math.powi(u64, 10, array_info.len));
405 _ = std.fmt.printInt(field_ptr, value, 10, .lower, .{
406 .width = array_info.len,
407 .alignment = .left,
408 .fill = ' ',
409 });
410 }
411};
412
413pub const LongNamesTable = struct {
414 ni: MappedFile.Node.Index = .none,
415 entries: std.AutoArrayHashMapUnmanaged(void, Entry),
416
417 pub const Entry = struct {
418 index: u64,
419 len: u64,
420 };
421
422 const Adapter = struct {
423 coff: *Coff,
424
425 pub fn eql(adapter: Adapter, lhs_key: []const u8, _: void, rhs_index: usize) bool {
426 assert(adapter.coff.isArchive()); // TODO: move to helper that uses this
427 const longnames_slice = Node.known.longnames_member.slice(&adapter.coff.mf);
428 const rhs = adapter.coff.long_names_table.entries.values()[rhs_index];
429 return std.mem.eql(u8, longnames_slice[rhs.index..][0..rhs.len], lhs_key);
430 }
431
432 pub fn hash(_: Adapter, key: []const u8) u32 {
433 assert(std.mem.indexOfScalar(u8, key, 0) == null);
434 return std.array_hash_map.hashString(key);
435 }
436 };
437};
438
282439pub const ExportTable = struct {
283440 ni: MappedFile.Node.Index,
441 export_directory_table_ni: MappedFile.Node.Index,
284442 export_address_table_si: Symbol.Index,
285443 name_pointer_table_ni: MappedFile.Node.Index,
286444 ordinal_table_ni: MappedFile.Node.Index,
......@@ -535,6 +693,13 @@ pub const Reloc = extern struct {
535693 const loc_slice = loc_sym.ni.slice(&coff.mf)[@intCast(reloc.offset)..];
536694 const target_rva = target_sym.rva +% @as(u64, @bitCast(reloc.addend));
537695 const target_endian = coff.targetEndian();
696
697 // TODO: Is this right?
698 const base = if (coff.isImage())
699 coff.optionalHeaderField(.image_base)
700 else
701 0; // should be offset within section - take target_rva - section_rva (but section is 0!)
702
538703 switch (coff.targetLoad(&coff.headerPtr().machine)) {
539704 else => |machine| @panic(@tagName(machine)),
540705 .AMD64 => switch (reloc.type.AMD64) {
......@@ -543,13 +708,13 @@ pub const Reloc = extern struct {
543708 .ADDR64 => std.mem.writeInt(
544709 u64,
545710 loc_slice[0..8],
546 coff.optionalHeaderField(.image_base) + target_rva,
711 base + target_rva,
547712 target_endian,
548713 ),
549714 .ADDR32 => std.mem.writeInt(
550715 u32,
551716 loc_slice[0..4],
552 @intCast(coff.optionalHeaderField(.image_base) + target_rva),
717 @intCast(base + target_rva),
553718 target_endian,
554719 ),
555720 .ADDR32NB => std.mem.writeInt(
......@@ -607,7 +772,7 @@ pub const Reloc = extern struct {
607772 .DIR16 => std.mem.writeInt(
608773 u16,
609774 loc_slice[0..2],
610 @intCast(coff.optionalHeaderField(.image_base) + target_rva),
775 @intCast(base + target_rva),
611776 target_endian,
612777 ),
613778 .REL16 => std.mem.writeInt(
......@@ -619,7 +784,7 @@ pub const Reloc = extern struct {
619784 .DIR32 => std.mem.writeInt(
620785 u32,
621786 loc_slice[0..4],
622 @intCast(coff.optionalHeaderField(.image_base) + target_rva),
787 @intCast(base + target_rva),
623788 target_endian,
624789 ),
625790 .DIR32NB => std.mem.writeInt(
......@@ -691,14 +856,6 @@ fn create(
691856 assert(target.ofmt == .coff);
692857 if (target.cpu.arch.endian() != comptime targetEndian(undefined))
693858 return error.UnsupportedCOFFArchitecture;
694 const is_image = switch (comp.config.output_mode) {
695 .Exe => true,
696 .Lib => switch (comp.config.link_mode) {
697 .static => false,
698 .dynamic => true,
699 },
700 .Obj => false,
701 };
702859 const machine = target.toCoffMachine();
703860 const timestamp: u32 = 0;
704861 const major_subsystem_version = options.major_subsystem_version orelse 6;
......@@ -743,12 +900,20 @@ fn create(
743900 },
744901 .mf = try .init(file, comp.gpa, io),
745902 .nodes = .empty,
903 .members = .empty,
904 .pending_members = .empty,
905 .lib_string_table = .empty,
906 .lib_string_len = 0,
907 .long_names_table = .{
908 .entries = .empty,
909 },
746910 .import_table = .{
747911 .ni = .none,
748912 .entries = .empty,
749913 },
750914 .export_table = .{
751915 .ni = .none,
916 .export_directory_table_ni = .none,
752917 .export_address_table_si = .null,
753918 .name_pointer_table_ni = .none,
754919 .ordinal_table_ni = .none,
......@@ -785,7 +950,6 @@ fn create(
785950 }
786951
787952 try coff.initHeaders(
788 is_image,
789953 machine,
790954 timestamp,
791955 major_subsystem_version,
......@@ -801,6 +965,7 @@ pub fn deinit(coff: *Coff) void {
801965 const gpa = coff.base.comp.gpa;
802966 coff.mf.deinit(gpa);
803967 coff.nodes.deinit(gpa);
968 coff.long_names_table.entries.deinit(gpa);
804969 coff.import_table.entries.deinit(gpa);
805970 coff.export_table.entries.deinit(gpa);
806971 coff.strings.deinit(gpa);
......@@ -818,9 +983,32 @@ pub fn deinit(coff: *Coff) void {
818983 coff.* = undefined;
819984}
820985
986fn isImage(coff: *const Coff) bool {
987 const comp = coff.base.comp;
988 return switch (comp.config.output_mode) {
989 .Exe => true,
990 .Lib => switch (comp.config.link_mode) {
991 .static => false,
992 .dynamic => true,
993 },
994 .Obj => false,
995 };
996}
997
998fn isArchive(coff: *const Coff) bool {
999 const comp = coff.base.comp;
1000 return switch (comp.config.output_mode) {
1001 .Exe => false,
1002 .Lib => switch (comp.config.link_mode) {
1003 .static => true,
1004 .dynamic => false,
1005 },
1006 .Obj => false,
1007 };
1008}
1009
8211010fn initHeaders(
8221011 coff: *Coff,
823 is_image: bool,
8241012 machine: std.coff.IMAGE.FILE.MACHINE,
8251013 timestamp: u32,
8261014 major_subsystem_version: u16,
......@@ -833,6 +1021,8 @@ fn initHeaders(
8331021 const gpa = comp.gpa;
8341022 const target_endian = coff.targetEndian();
8351023 const file_align: std.mem.Alignment = comptime .fromByteUnits(default_file_alignment);
1024 const is_image = coff.isImage();
1025 const is_archive = coff.isArchive();
8361026
8371027 const optional_header_size: u16 = if (is_image) switch (magic) {
8381028 _ => unreachable,
......@@ -843,33 +1033,106 @@ fn initHeaders(
8431033 else
8441034 0;
8451035
846 const expected_nodes_len = Node.known_count + 12 +
847 @as(usize, @intFromBool(comp.config.any_non_single_threaded)) * 2;
1036 var expected_nodes_len: usize = Node.known_count;
1037 if (comp.zcu != null) {
1038 expected_nodes_len += 3;
1039 if (is_image) expected_nodes_len += 9;
1040 expected_nodes_len += @as(usize, @intFromBool(comp.config.any_non_single_threaded)) * 2;
1041 }
1042 defer assert(coff.nodes.len == expected_nodes_len);
1043
8481044 try coff.nodes.ensureTotalCapacity(gpa, expected_nodes_len);
8491045 coff.nodes.appendAssumeCapacity(.file);
8501046
8511047 const header_ni = Node.known.header;
852 assert(header_ni == try coff.mf.addOnlyChildNode(gpa, .root, .{
1048 assert(header_ni == try coff.mf.addOnlyChildNode(gpa, Node.known.file, .{
8531049 .alignment = coff.mf.flags.block_size,
8541050 .fixed = true,
8551051 }));
8561052 coff.nodes.appendAssumeCapacity(.header);
8571053
1054 const pe_signature = "PE\x00\x00";
1055 const archive_signature = "!<arch>\n";
1056
8581057 const signature_ni = Node.known.signature;
859 assert(signature_ni == try coff.mf.addOnlyChildNode(gpa, header_ni, .{
860 .size = (if (is_image) msdos_stub.len else 0) + "PE\x00\x00".len,
1058 assert(signature_ni == try coff.mf.addLastChildNode(gpa, if (is_image) header_ni else Node.known.file, .{
1059 .size = if (is_image)
1060 msdos_stub.len + pe_signature.len
1061 else if (is_archive)
1062 archive_signature.len
1063 else
1064 0,
8611065 .alignment = .@"4",
8621066 .fixed = true,
8631067 }));
8641068 coff.nodes.appendAssumeCapacity(.signature);
865 {
866 const signature_slice = signature_ni.slice(&coff.mf);
867 if (is_image) @memcpy(signature_slice[0..msdos_stub.len], &msdos_stub);
868 @memcpy(signature_slice[signature_slice.len - 4 ..], "PE\x00\x00");
1069
1070 const signature_slice = signature_ni.slice(&coff.mf);
1071 if (is_image) {
1072 @memcpy(signature_slice[0..msdos_stub.len], &msdos_stub);
1073 @memcpy(signature_slice[signature_slice.len - pe_signature.len ..], pe_signature);
1074 } else if (is_archive) {
1075 @memcpy(signature_slice, archive_signature);
8691076 }
8701077
1078 const opt_zcu_coff_parent_ni = if (is_archive) parent: {
1079 const initial_member_count = Member.Index.known_count + @intFromBool(comp.zcu != null);
1080 try coff.members.ensureTotalCapacity(gpa, initial_member_count);
1081
1082 assert(Member.Index.first == try coff.addMemberAssumeCapacity(.first_linker, @sizeOf(u32)));
1083 coff.targetStore(coff.firstLinkerMemberNumSymbolsPtr(), 0);
1084
1085 assert(Member.Index.second == try coff.addMemberAssumeCapacity(.second_linker, 2 * @sizeOf(u32)));
1086 coff.targetStore(coff.secondLinkerMemberNumMembersPtr(), 0);
1087 coff.targetStore(coff.secondLinkerMemberNumSymbolsPtr(), 0);
1088
1089 assert(Member.Index.longnames == try coff.addMemberAssumeCapacity(.longnames, 0));
1090
1091 const first_linker_member = Member.Index.first.get(coff);
1092 const second_linker_member = Member.Index.second.get(coff);
1093 const longnames_member = Member.Index.longnames.get(coff);
1094
1095 try first_linker_member.initHeader(coff, "", timestamp);
1096 try second_linker_member.initHeader(coff, "", timestamp);
1097 try longnames_member.initHeader(coff, "/", timestamp);
1098
1099 if (comp.zcu) |zcu| {
1100 const zcu_mi = try coff.addMemberAssumeCapacity(.coff, @sizeOf(std.coff.Header));
1101 const zcu_member = zcu_mi.get(coff);
1102 try zcu_member.initHeader(coff, zcu.main_mod.fully_qualified_name, timestamp);
1103
1104 break :parent zcu_member.content_ni;
1105 }
1106
1107 assert(Node.known.zcu_member_header == try coff.mf.addLastChildNode(gpa, Node.known.file, .{}));
1108 assert(Node.known.zcu_member == try coff.mf.addLastChildNode(gpa, Node.known.file, .{}));
1109 coff.nodes.appendAssumeCapacity(.placeholder);
1110 coff.nodes.appendAssumeCapacity(.placeholder);
1111
1112 break :parent null;
1113 } else parent: {
1114 // TODO: Not ideal to have this many placeholder nodes - use two distinct `Node.known` types?
1115 while (true) {
1116 const placeholder_ni = try coff.mf.addLastChildNode(gpa, Node.known.file, .{});
1117 coff.nodes.appendAssumeCapacity(.placeholder);
1118 if (placeholder_ni == Node.known.zcu_member) break;
1119 }
1120
1121 break :parent if (comp.zcu != null) Node.known.header else null;
1122 };
1123
1124 const zcu_coff_parent_ni = opt_zcu_coff_parent_ni orelse {
1125 // If we're not generating any code, no more known nodes are used
1126 while (coff.nodes.len < Node.known_count) {
1127 _ = try coff.mf.addLastChildNode(gpa, Node.known.file, .{});
1128 coff.nodes.appendAssumeCapacity(.placeholder);
1129 }
1130
1131 return;
1132 };
1133
8711134 const coff_header_ni = Node.known.coff_header;
872 assert(coff_header_ni == try coff.mf.addLastChildNode(gpa, header_ni, .{
1135 assert(coff_header_ni == try coff.mf.addLastChildNode(gpa, zcu_coff_parent_ni, .{
8731136 .size = @sizeOf(std.coff.Header),
8741137 .alignment = .@"4",
8751138 .fixed = true,
......@@ -897,7 +1160,7 @@ fn initHeaders(
8971160 }
8981161
8991162 const optional_header_ni = Node.known.optional_header;
900 assert(optional_header_ni == try coff.mf.addLastChildNode(gpa, header_ni, .{
1163 assert(optional_header_ni == try coff.mf.addLastChildNode(gpa, zcu_coff_parent_ni, .{
9011164 .size = optional_header_size,
9021165 .alignment = .@"4",
9031166 .fixed = true,
......@@ -1009,13 +1272,13 @@ fn initHeaders(
10091272 }
10101273
10111274 const data_directories_ni = Node.known.data_directories;
1012 assert(data_directories_ni == try coff.mf.addLastChildNode(gpa, header_ni, .{
1275 assert(data_directories_ni == try coff.mf.addLastChildNode(gpa, zcu_coff_parent_ni, .{
10131276 .size = data_directories_size,
10141277 .alignment = .@"4",
10151278 .fixed = true,
10161279 }));
10171280 coff.nodes.appendAssumeCapacity(.data_directories);
1018 {
1281 if (is_image) {
10191282 const data_directories = coff.dataDirectorySlice();
10201283 @memset(data_directories, .{ .virtual_address = 0, .size = 0 });
10211284 if (target_endian != native_endian) std.mem.byteSwapAllFields(
......@@ -1025,7 +1288,7 @@ fn initHeaders(
10251288 }
10261289
10271290 const section_table_ni = Node.known.section_table;
1028 assert(section_table_ni == try coff.mf.addLastChildNode(gpa, header_ni, .{
1291 assert(section_table_ni == try coff.mf.addLastChildNode(gpa, zcu_coff_parent_ni, .{
10291292 .alignment = .@"4",
10301293 .fixed = true,
10311294 }));
......@@ -1057,43 +1320,45 @@ fn initHeaders(
10571320 .MEM_READ = true,
10581321 }) == .text);
10591322
1060 coff.import_table.ni = try coff.mf.addLastChildNode(
1061 gpa,
1062 (try coff.objectSectionMapIndex(
1063 .@".idata",
1064 coff.mf.flags.block_size,
1065 .{ .read = true },
1066 )).symbol(coff).node(coff),
1067 .{ .alignment = .@"4", .moved = true },
1068 );
1069 coff.nodes.appendAssumeCapacity(.import_directory_table);
1070
10711323 if (is_image) {
1072 const edata_section_ni = (try coff.pseudoSectionMapIndex(
1324 coff.import_table.ni = try coff.mf.addLastChildNode(
1325 gpa,
1326 (try coff.objectSectionMapIndex(
1327 .@".idata",
1328 coff.mf.flags.block_size,
1329 .{ .read = true },
1330 )).symbol(coff).node(coff),
1331 .{ .alignment = .@"4", .moved = true },
1332 );
1333 coff.nodes.appendAssumeCapacity(.import_directory_table);
1334
1335 coff.export_table.ni = (try coff.pseudoSectionMapIndex(
10731336 .@".edata",
10741337 .of(std.coff.ExportDirectoryTable),
10751338 .{ .read = true },
10761339 )).symbol(coff).node(coff);
10771340
1078 coff.export_table.ni = try coff.mf.addLastChildNode(
1341 coff.export_table.export_directory_table_ni = try coff.mf.addLastChildNode(
10791342 gpa,
1080 edata_section_ni,
1343 coff.export_table.ni,
10811344 .{
10821345 .size = @sizeOf(std.coff.ExportDirectoryTable) + file_name.len + 1,
1083 .alignment = .of(std.coff.ExportDirectoryTable),
1084 .fixed = true,
10851346 .moved = true,
1347 .fixed = true,
10861348 },
10871349 );
1350 coff.nodes.appendAssumeCapacity(.export_directory_table);
10881351
10891352 const name_index = @sizeOf(std.coff.ExportDirectoryTable);
1090 @memcpy(coff.export_table.ni.slice(&coff.mf)[name_index..][0..file_name.len], file_name[0..file_name.len]);
1091 @memset(coff.export_table.ni.slice(&coff.mf)[name_index + file_name.len ..], 0);
1353 const table_slice = coff.export_table.export_directory_table_ni.slice(&coff.mf);
1354 @memcpy(table_slice[name_index..][0..file_name.len], file_name[0..file_name.len]);
1355 @memset(table_slice[name_index + file_name.len ..], 0);
10921356
1093 const export_address_table_ni = try coff.mf.addLastChildNode(gpa, edata_section_ni, .{
1094 .alignment = .of(u32),
1357 const export_address_table_ni = try coff.mf.addLastChildNode(gpa, coff.export_table.ni, .{
1358 .alignment = .of(std.coff.ExportAddressTableEntry),
10951359 .moved = true,
10961360 });
1361 coff.nodes.appendAssumeCapacity(.export_address_table);
10971362
10981363 try coff.symbol_table.ensureUnusedCapacity(gpa, 1);
10991364 coff.export_table.export_address_table_si = coff.addSymbolAssumeCapacity();
......@@ -1103,25 +1368,24 @@ fn initHeaders(
11031368 assert(export_address_table_sym.loc_relocs == .none);
11041369 export_address_table_sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
11051370 export_address_table_sym.section_number =
1106 coff.getNode(edata_section_ni).pseudo_section.symbol(coff).get(coff).section_number;
1371 coff.getNode(coff.export_table.ni).pseudo_section.symbol(coff).get(coff).section_number;
11071372
1108 coff.export_table.name_pointer_table_ni = try coff.mf.addLastChildNode(gpa, edata_section_ni, .{
1109 .alignment = .of(u32),
1373 coff.export_table.name_pointer_table_ni = try coff.mf.addLastChildNode(gpa, coff.export_table.ni, .{
1374 .alignment = .of(std.coff.ExportNamePointerTableEntry),
11101375 .moved = true,
11111376 });
1112 coff.export_table.ordinal_table_ni = try coff.mf.addLastChildNode(gpa, edata_section_ni, .{
1113 .alignment = .of(u16),
1377 coff.nodes.appendAssumeCapacity(.export_name_pointer_table);
1378
1379 coff.export_table.ordinal_table_ni = try coff.mf.addLastChildNode(gpa, coff.export_table.ni, .{
1380 .alignment = .of(std.coff.ExportOrdinalTableEntry),
11141381 .moved = true,
11151382 });
1116 coff.export_table.name_table_ni = try coff.mf.addLastChildNode(gpa, edata_section_ni, .{
1383 coff.nodes.appendAssumeCapacity(.export_ordinal_table);
1384
1385 coff.export_table.name_table_ni = try coff.mf.addLastChildNode(gpa, coff.export_table.ni, .{
11171386 .alignment = .of(u8),
11181387 .moved = true,
11191388 });
1120
1121 coff.nodes.appendAssumeCapacity(.export_directory_table);
1122 coff.nodes.appendAssumeCapacity(.export_address_table);
1123 coff.nodes.appendAssumeCapacity(.export_name_pointer_table);
1124 coff.nodes.appendAssumeCapacity(.export_ordinal_table);
11251389 coff.nodes.appendAssumeCapacity(.export_name_table);
11261390
11271391 const export_directory_table = coff.exportDirectoryTable();
......@@ -1145,11 +1409,9 @@ fn initHeaders(
11451409 // While tls variables allocated at runtime are writable, the template itself is not
11461410 if (comp.config.any_non_single_threaded) _ = try coff.objectSectionMapIndex(
11471411 .@".tls$",
1148 coff.mf.flags.block_size,
1412 if (is_image) coff.mf.flags.block_size else .@"1",
11491413 .{ .read = true },
11501414 );
1151
1152 assert(coff.nodes.len == expected_nodes_len);
11531415}
11541416
11551417pub fn startProgress(coff: *Coff, prog_node: std.Progress.Node) void {
......@@ -1181,11 +1443,14 @@ fn computeNodeRva(coff: *Coff, ni: MappedFile.Node.Index) u32 {
11811443 .file,
11821444 .header,
11831445 .signature,
1446 .archive_member_header,
1447 .archive_member,
11841448 .coff_header,
11851449 .optional_header,
11861450 .data_directories,
11871451 .section_table,
11881452 .export_name_table,
1453 .placeholder,
11891454 => unreachable,
11901455 .image_section => |si| si,
11911456 .import_directory_table => break :parent_rva coff.targetLoad(
......@@ -1272,9 +1537,55 @@ fn targetStore(coff: *const Coff, ptr: anytype, val: @typeInfo(@TypeOf(ptr)).poi
12721537}
12731538
12741539pub fn headerPtr(coff: *Coff) *std.coff.Header {
1540 assert(coff.base.comp.zcu != null);
12751541 return @ptrCast(@alignCast(Node.known.coff_header.slice(&coff.mf)));
12761542}
12771543
1544pub fn firstLinkerMemberNumSymbolsPtr(coff: *Coff) *u32 {
1545 assert(coff.isArchive());
1546 return @ptrCast(@alignCast(Node.known.first_linker_member.slice(&coff.mf)));
1547}
1548
1549pub fn firstLinkerMemberOffsetsSlice(coff: *Coff) []u32 {
1550 const len = std.mem.toNative(u32, coff.firstLinkerMemberNumSymbolsPtr().*, .big);
1551 return @ptrCast(@alignCast(Node.known.first_linker_member.slice(&coff.mf)[@sizeOf(u32)..][0 .. len * @sizeOf(u32)]));
1552}
1553
1554pub fn secondLinkerMemberNumMembersPtr(coff: *Coff) *u32 {
1555 assert(coff.isArchive());
1556 return @ptrCast(@alignCast(Node.known.second_linker_member.slice(&coff.mf)));
1557}
1558
1559pub fn secondLinkerMemberOffsetsSlice(coff: *Coff) []u32 {
1560 const num_members = coff.targetLoad(coff.secondLinkerMemberNumMembersPtr());
1561 return @ptrCast(@alignCast(
1562 Node.known.second_linker_member.slice(&coff.mf)[@sizeOf(u32)..][0 .. num_members * @sizeOf(u32)],
1563 ));
1564}
1565
1566pub fn secondLinkerMemberNumSymbolsPtr(coff: *Coff) *u32 {
1567 const num_members = coff.targetLoad(coff.secondLinkerMemberNumMembersPtr());
1568 return @ptrCast(@alignCast(
1569 Node.known.second_linker_member.slice(&coff.mf)[(1 + num_members) * @sizeOf(u32) ..],
1570 ));
1571}
1572
1573pub fn secondLinkerMemberIndicesSlice(coff: *Coff) []u16 {
1574 const num_members = coff.targetLoad(coff.secondLinkerMemberNumMembersPtr());
1575 const num_symbols = coff.targetLoad(coff.secondLinkerMemberNumSymbolsPtr());
1576 return @ptrCast(@alignCast(
1577 Node.known.second_linker_member.slice(&coff.mf)[(2 + num_members) * @sizeOf(u32) ..][0 .. num_symbols * @sizeOf(u16)],
1578 ));
1579}
1580
1581pub fn secondLinkerMemberStringsSlice(coff: *Coff) []u8 {
1582 const num_members = coff.targetLoad(coff.secondLinkerMemberNumMembersPtr());
1583 const num_symbols = coff.targetLoad(coff.secondLinkerMemberNumSymbolsPtr());
1584 return @ptrCast(@alignCast(
1585 Node.known.second_linker_member.slice(&coff.mf)[(2 + num_members) * @sizeOf(u32) + num_symbols * @sizeOf(u16) ..],
1586 ));
1587}
1588
12781589pub fn optionalHeaderStandardPtr(coff: *Coff) *std.coff.OptionalHeader {
12791590 return @ptrCast(@alignCast(
12801591 Node.known.optional_header.slice(&coff.mf)[0..@sizeOf(std.coff.OptionalHeader)],
......@@ -1286,6 +1597,7 @@ pub const OptionalHeaderPtr = union(std.coff.OptionalHeader.Magic) {
12861597 @"PE32+": *std.coff.OptionalHeader.@"PE32+",
12871598};
12881599pub fn optionalHeaderPtr(coff: *Coff) OptionalHeaderPtr {
1600 assert(coff.isImage());
12891601 const slice = Node.known.optional_header.slice(&coff.mf);
12901602 return switch (coff.targetLoad(&coff.optionalHeaderStandardPtr().magic)) {
12911603 _ => unreachable,
......@@ -1300,6 +1612,7 @@ pub fn optionalHeaderField(
13001612 coff: *Coff,
13011613 comptime field: std.meta.FieldEnum(std.coff.OptionalHeader.@"PE32+"),
13021614) @FieldType(std.coff.OptionalHeader.@"PE32+", @tagName(field)) {
1615 assert(coff.isImage());
13031616 return switch (coff.optionalHeaderPtr()) {
13041617 inline else => |optional_header| coff.targetLoad(&@field(optional_header, @tagName(field))),
13051618 };
......@@ -1308,6 +1621,7 @@ pub fn optionalHeaderField(
13081621pub fn dataDirectorySlice(
13091622 coff: *Coff,
13101623) *[std.coff.IMAGE.DIRECTORY_ENTRY.len]std.coff.ImageDataDirectory {
1624 assert(coff.isImage());
13111625 return @ptrCast(@alignCast(Node.known.data_directories.slice(&coff.mf)));
13121626}
13131627pub fn dataDirectoryPtr(
......@@ -1322,6 +1636,7 @@ pub fn sectionTableSlice(coff: *Coff) []std.coff.SectionHeader {
13221636}
13231637
13241638pub fn importDirectoryTableSlice(coff: *Coff) []std.coff.ImportDirectoryEntry {
1639 assert(coff.isImage());
13251640 return @ptrCast(@alignCast(coff.import_table.ni.slice(&coff.mf)));
13261641}
13271642pub fn importDirectoryEntryPtr(
......@@ -1332,10 +1647,13 @@ pub fn importDirectoryEntryPtr(
13321647}
13331648
13341649pub fn exportDirectoryTable(coff: *Coff) *std.coff.ExportDirectoryTable {
1335 return @ptrCast(@alignCast(coff.export_table.ni.slice(&coff.mf)));
1650 return @ptrCast(@alignCast(coff.export_table.export_directory_table_ni.slice(&coff.mf)));
13361651}
13371652
13381653pub fn exportNamePointerTableSlice(coff: *Coff) []std.coff.ExportNamePointerTableEntry {
1654 const debug = coff.export_table.name_pointer_table_ni.slice(&coff.mf);
1655 _ = debug;
1656
13391657 return @ptrCast(@alignCast(coff.export_table.name_pointer_table_ni.slice(&coff.mf)));
13401658}
13411659
......@@ -1388,6 +1706,14 @@ fn getOrPutStringAssumeCapacity(coff: *Coff, string: []const u8) String {
13881706}
13891707
13901708pub fn globalSymbol(coff: *Coff, name: []const u8, lib_name: ?[]const u8) !Symbol.Index {
1709 return (try getOrPutGlobalSymbol(coff, name, lib_name)).value_ptr.*;
1710}
1711
1712fn getOrPutGlobalSymbol(
1713 coff: *Coff,
1714 name: []const u8,
1715 lib_name: ?[]const u8,
1716) !std.AutoArrayHashMapUnmanaged(GlobalName, Symbol.Index).GetOrPutResult {
13911717 const gpa = coff.base.comp.gpa;
13921718 try coff.symbol_table.ensureUnusedCapacity(gpa, 1);
13931719 const sym_gop = try coff.globals.getOrPut(gpa, .{
......@@ -1398,7 +1724,7 @@ pub fn globalSymbol(coff: *Coff, name: []const u8, lib_name: ?[]const u8) !Symbo
13981724 sym_gop.value_ptr.* = coff.addSymbolAssumeCapacity();
13991725 coff.synth_prog_node.increaseEstimatedTotalItems(1);
14001726 }
1401 return sym_gop.value_ptr.*;
1727 return sym_gop;
14021728}
14031729
14041730fn navSection(
......@@ -1500,10 +1826,187 @@ pub fn getVAddr(coff: *Coff, reloc_info: link.File.RelocInfo, target_si: Symbol.
15001826 .I386 => .{ .I386 = .DIR32 },
15011827 },
15021828 );
1503 return coff.optionalHeaderField(.image_base) + target_si.get(coff).rva;
1829
1830 var vaddr: u64 = target_si.get(coff).rva;
1831 if (coff.isImage()) vaddr += coff.optionalHeaderField(.image_base);
1832 return vaddr;
1833}
1834
1835/// Caller guarantees there is capacity for one member and two nodes
1836fn addMemberAssumeCapacity(coff: *Coff, kind: Member.Kind, size: usize) !Member.Index {
1837 const comp = coff.base.comp;
1838 const gpa = comp.gpa;
1839
1840 // TODO: These two nodes could to be inside a movable node? Only if coff or import
1841
1842 const header_ni = try coff.mf.addLastChildNode(gpa, Node.known.file, .{
1843 .size = @sizeOf(std.coff.ArchiveMemberHeader),
1844 .alignment = .@"2",
1845 .fixed = true,
1846 .moved = true,
1847 });
1848
1849 const content_ni = try coff.mf.addLastChildNode(gpa, Node.known.file, .{
1850 // The actual alignment required by the spec is 2, but to allow aligned access to
1851 // the various COFF data structures in-place during linking we overalign
1852 .alignment = switch (kind) {
1853 .coff => .@"4",
1854 else => .@"2",
1855 },
1856 .size = size,
1857 .resized = size > 0,
1858 .fixed = true,
1859 });
1860
1861 const mi: Member.Index = @enumFromInt(coff.members.items.len);
1862 coff.members.appendAssumeCapacity(.{
1863 .kind = kind,
1864 .header_ni = header_ni,
1865 .content_ni = content_ni,
1866 .symbol_offsets = .empty,
1867 });
1868
1869 coff.nodes.appendAssumeCapacity(.{ .archive_member_header = mi });
1870 coff.nodes.appendAssumeCapacity(.{ .archive_member = mi });
1871
1872 switch (kind) {
1873 .first_linker, .second_linker, .longnames => {},
1874 else => {
1875 const new_num_members = coff.members.items.len - Member.Index.known_count;
1876 coff.targetStore(
1877 coff.secondLinkerMemberNumMembersPtr(),
1878 @intCast(new_num_members),
1879 );
1880
1881 const old_size = Node.known.second_linker_member.location(&coff.mf).resolve(&coff.mf)[1];
1882 const old_header_size = new_num_members * @sizeOf(u32);
1883 const trailing_size = old_size - old_header_size;
1884 try Node.known.second_linker_member.resize(&coff.mf, gpa, old_size + @sizeOf(u32));
1885
1886 const slice = Node.known.second_linker_member.slice(&coff.mf);
1887 @memmove(
1888 slice[old_header_size + @sizeOf(u32) ..][0..trailing_size],
1889 slice[old_header_size..][0..trailing_size],
1890 );
1891
1892 // Offset will be written by flushMoved on header_ni
1893 },
1894 }
1895
1896 switch (kind) {
1897 .first_linker,
1898 .longnames,
1899 .import,
1900 => {},
1901 .second_linker,
1902 .coff,
1903 => {
1904 try coff.pending_members.ensureTotalCapacity(
1905 gpa,
1906 coff.pending_members.capacity() + 1,
1907 );
1908 },
1909 }
1910
1911 return mi;
1912}
1913
1914fn appendMemberSymbolString(
1915 coff: *Coff,
1916 strings_ni: MappedFile.Node.Index,
1917 new_size: u64,
1918 name: []const u8,
1919 offset: u64,
1920) !void {
1921 try strings_ni.resize(&coff.mf, coff.base.comp.gpa, new_size);
1922 const name_slice = strings_ni.slice(&coff.mf)[offset..][0 .. name.len + 1];
1923 @memcpy(name_slice[0..name.len], name);
1924 name_slice[name.len] = 0;
1925}
1926
1927fn addMemberSymbol(
1928 coff: *Coff,
1929 name: String,
1930 mi: Member.Index,
1931 si: Symbol.Index,
1932) !void {
1933 const gpa = coff.base.comp.gpa;
1934 const member = mi.get(coff);
1935 assert(member.kind == .coff);
1936
1937 const gop = try member.symbol_offsets.getOrPut(gpa, si);
1938 if (gop.found_existing) return;
1939
1940 // TODO: Detect duplicate names (ie. a name used by a symbol in another member, not the zcu since those already go through globals)
1941
1942 const symbol_index = blk: {
1943 const num_symbols_ptr = coff.firstLinkerMemberNumSymbolsPtr();
1944 const num_symbols = std.mem.toNative(u32, num_symbols_ptr.*, .big);
1945 num_symbols_ptr.* = std.mem.nativeTo(u32, num_symbols + 1, .big);
1946 break :blk num_symbols;
1947 };
1948
1949 gop.value_ptr.* = symbol_index;
1950 const name_slice = name.toSlice(coff);
1951
1952 // Linker member fields are not modeled as nodes because MappedFile
1953 // can't guarantee that they will be tightly packed after resizing
1954
1955 const new_string_table_size = coff.lib_string_len + name_slice.len + 1;
1956 defer coff.lib_string_len = new_string_table_size;
1957
1958 {
1959 const old_header_size = @sizeOf(u32) + symbol_index * @sizeOf(u32);
1960 const new_header_size = old_header_size + @sizeOf(u32);
1961 try Node.known.first_linker_member.resize(&coff.mf, gpa, new_header_size + new_string_table_size);
1962
1963 const slice = Node.known.first_linker_member.slice(&coff.mf);
1964 @memmove(slice[new_header_size..][0..coff.lib_string_len], slice[old_header_size..][0..coff.lib_string_len]);
1965 @memcpy(slice[new_header_size + coff.lib_string_len ..][0 .. name_slice.len + 1], name_slice[0 .. name_slice.len + 1]);
1966
1967 // New offset entry is written in flushMember
1968 }
1969
1970 {
1971 const num_members = coff.targetLoad(coff.secondLinkerMemberNumMembersPtr());
1972 const old_header_size = 2 * @sizeOf(u32) + num_members * @sizeOf(u32) + symbol_index * @sizeOf(u16);
1973 const new_header_size = old_header_size + @sizeOf(u16);
1974 try Node.known.second_linker_member.resize(&coff.mf, gpa, new_header_size + new_string_table_size);
1975
1976 const needs_sort = if (coff.lib_string_table.items.len > 0)
1977 std.mem.lessThan(
1978 u8,
1979 name_slice,
1980 coff.lib_string_table.items[coff.lib_string_table.items.len - 1].toSlice(coff),
1981 )
1982 else
1983 false;
1984
1985 try coff.lib_string_table.append(gpa, name);
1986
1987 const slice = Node.known.second_linker_member.slice(&coff.mf);
1988 const num_symbols_ptr: *u32 = @ptrCast(@alignCast(slice[@sizeOf(u32) + num_members * @sizeOf(u32) ..]));
1989 coff.targetStore(num_symbols_ptr, symbol_index + 1);
1990
1991 if (needs_sort) {
1992 // The entire string table is rebuilt in flushMember after sorting
1993 coff.pending_members.putAssumeCapacity(Member.Index.second, {});
1994 } else {
1995 @memmove(slice[new_header_size..][0..coff.lib_string_len], slice[old_header_size..][0..coff.lib_string_len]);
1996 @memcpy(slice[new_header_size + coff.lib_string_len ..][0 .. name_slice.len + 1], name_slice[0 .. name_slice.len + 1]);
1997 }
1998
1999 // Indices in this table are 1-based
2000 const index_ptr: *u16 = @ptrCast(@alignCast(slice[old_header_size..]));
2001 coff.targetStore(index_ptr, @intCast(@intFromEnum(mi) - Member.Index.known_count + 1));
2002 }
2003
2004 coff.pending_members.putAssumeCapacity(mi, {});
15042005}
15052006
15062007fn addSection(coff: *Coff, name: []const u8, flags: std.coff.SectionHeader.Flags) !Symbol.Index {
2008 assert(coff.base.comp.zcu != null);
2009
15072010 const gpa = coff.base.comp.gpa;
15082011 try coff.nodes.ensureUnusedCapacity(gpa, 1);
15092012 try coff.image_section_table.ensureUnusedCapacity(gpa, 1);
......@@ -1518,21 +2021,34 @@ fn addSection(coff: *Coff, name: []const u8, flags: std.coff.SectionHeader.Flags
15182021 gpa,
15192022 @sizeOf(std.coff.SectionHeader) * section_table_len,
15202023 );
1521 const ni = try coff.mf.addLastChildNode(gpa, .root, .{
1522 .alignment = coff.mf.flags.block_size,
2024
2025 const parent_ni, const alignment = if (coff.isArchive())
2026 .{ Node.known.zcu_member, .@"1" }
2027 else
2028 .{ Node.known.file, coff.mf.flags.block_size };
2029
2030 const ni = try coff.mf.addLastChildNode(gpa, parent_ni, .{
2031 .alignment = alignment,
15232032 .moved = true,
15242033 .bubbles_moved = false,
15252034 });
2035
15262036 const si = coff.addSymbolAssumeCapacity();
15272037 coff.image_section_table.appendAssumeCapacity(si);
15282038 coff.nodes.appendAssumeCapacity(.{ .image_section = si });
15292039 const section_table = coff.sectionTableSlice();
1530 const virtual_size = coff.optionalHeaderField(.section_alignment);
1531 const rva: u32 = switch (section_index) {
1532 0 => @intCast(Node.known.header.location(&coff.mf).resolve(&coff.mf)[1]),
1533 else => coff.image_section_table.items[section_index - 1].get(coff).rva +
1534 coff.targetLoad(&section_table[section_index - 1].virtual_size),
1535 };
2040
2041 const virtual_size, const rva = if (coff.isImage()) block: {
2042 const virtual_size = coff.optionalHeaderField(.section_alignment);
2043 const rva: u32 = switch (section_index) {
2044 0 => @intCast(Node.known.header.location(&coff.mf).resolve(&coff.mf)[1]),
2045 else => coff.image_section_table.items[section_index - 1].get(coff).rva +
2046 coff.targetLoad(&section_table[section_index - 1].virtual_size),
2047 };
2048
2049 break :block .{ virtual_size, rva };
2050 } else .{ 0, 0 };
2051
15362052 {
15372053 const sym = si.get(coff);
15382054 sym.ni = ni;
......@@ -1556,12 +2072,16 @@ fn addSection(coff: *Coff, name: []const u8, flags: std.coff.SectionHeader.Flags
15562072 @memset(section.name[name.len..], 0);
15572073 if (coff.targetEndian() != native_endian)
15582074 std.mem.byteSwapAllFields(std.coff.SectionHeader, section);
1559 switch (coff.optionalHeaderPtr()) {
1560 inline else => |optional_header| coff.targetStore(
1561 &optional_header.size_of_image,
1562 @intCast(rva + virtual_size),
1563 ),
2075
2076 if (coff.isImage()) {
2077 switch (coff.optionalHeaderPtr()) {
2078 inline else => |optional_header| coff.targetStore(
2079 &optional_header.size_of_image,
2080 @intCast(rva + virtual_size),
2081 ),
2082 }
15642083 }
2084
15652085 return si;
15662086}
15672087
......@@ -1686,6 +2206,16 @@ pub fn addReloc(
16862206 target.target_relocs = ri;
16872207}
16882208
2209pub fn loadInput(coff: *Coff, input: link.Input) void {
2210 _ = coff;
2211 switch (input) {
2212 .dso_exact => unreachable,
2213 inline else => |i, tag| {
2214 log.debug("loadInput({s}: {f})", .{ @tagName(tag), i.path.fmtEscapeString() });
2215 },
2216 }
2217}
2218
16892219pub fn prelink(coff: *Coff, prog_node: std.Progress.Node) link.Error!void {
16902220 _ = coff;
16912221 _ = prog_node;
......@@ -1976,6 +2506,11 @@ pub fn flush(
19762506 _ = prog_node;
19772507 while (try coff.idle(tid)) {}
19782508
2509 // TODO: Second linker member symbol tables are built here
2510 if (isArchive(coff)) {
2511 //Member.Index.second.get(coff).content_ni;
2512 }
2513
19792514 const comp = coff.base.comp;
19802515
19812516 // Implib generation should instead be done via building a MappedFile progressively
......@@ -1985,8 +2520,8 @@ pub fn flush(
19852520
19862521 // hack for stage2_x86_64 + coff
19872522 if (comp.compiler_rt_dyn_lib) |crt_file| {
1988 const gpa = comp.gpa;
19892523 const io = comp.io;
2524 const gpa = comp.gpa;
19902525
19912526 const compiler_rt_sub_path = try std.fs.path.join(gpa, &.{
19922527 std.fs.path.dirname(coff.base.emit.sub_path) orelse "",
......@@ -2002,6 +2537,14 @@ pub fn flush(
20022537 .{},
20032538 ) catch |err| return comp.link_diags.fail("copy '{s}' failed: {t}", .{ compiler_rt_sub_path, err });
20042539 }
2540
2541 coff.mf.flush() catch |err| switch (err) {
2542 error.Canceled => |e| return e,
2543 else => |e| return comp.link_diags.fail("flush write failed: {t}", .{e}),
2544 };
2545
2546 coff.dumpStderr(tid) catch |err|
2547 return comp.link_diags.fail("dumping link snapshot failed: {t}", .{err});
20052548}
20062549
20072550pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
......@@ -2080,7 +2623,13 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
20802623 break :task;
20812624 } else coff.mf.update_prog_node.completeOne();
20822625 }
2626 while (coff.pending_members.pop()) |pending_mi| {
2627 // TODO: Prog node
2628 try coff.flushMember(pending_mi.key);
2629 break :task;
2630 }
20832631 if (coff.export_table.pending_sort) {
2632 // TODO: Prog node
20842633 coff.export_table.pending_sort = false;
20852634 coff.flushExportsSort();
20862635 break :task;
......@@ -2090,6 +2639,7 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
20902639 if (coff.globals.count() > coff.global_pending_index) return true;
20912640 for (&coff.lazy.values) |lazy| if (lazy.map.count() > lazy.pending_index) return true;
20922641 if (coff.mf.updates.items.len > 0) return true;
2642 if (coff.pending_members.count() > 0) return true;
20932643 if (coff.export_table.pending_sort) return true;
20942644 return false;
20952645}
......@@ -2183,6 +2733,10 @@ fn flushGlobal(coff: *Coff, pt: Zcu.PerThread, gmi: Node.GlobalMapIndex) !void {
21832733 const gpa = zcu.gpa;
21842734 const gn = gmi.globalName(coff);
21852735
2736 // TODO: We still need to emit a reloc for the __imp_Name symbol?
2737
2738 if (!coff.isImage()) return;
2739
21862740 if (gn.lib_name.toSlice(coff)) |lib_name| {
21872741 const name = gn.name.toSlice(coff);
21882742 try coff.nodes.ensureUnusedCapacity(gpa, 4);
......@@ -2394,19 +2948,44 @@ fn flushLazy(coff: *Coff, pt: Zcu.PerThread, lmr: Node.LazyMapRef) !void {
23942948}
23952949
23962950fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void {
2951 log.debug("flushMoved({s})", .{@tagName(coff.getNode(ni))});
23972952 switch (coff.getNode(ni)) {
23982953 .file,
23992954 .header,
24002955 .signature,
2956 => unreachable,
24012957 .coff_header,
24022958 .optional_header,
24032959 .data_directories,
24042960 .section_table,
2405 => unreachable,
2406 .image_section => |si| return coff.targetStore(
2407 &si.get(coff).section_number.header(coff).pointer_to_raw_data,
2408 @intCast(ni.fileLocation(&coff.mf, false).offset),
2409 ),
2961 .placeholder,
2962 => if (!coff.isArchive()) unreachable,
2963 .archive_member_header => |mi| {
2964 const member = mi.get(coff);
2965 switch (member.kind) {
2966 .first_linker, .second_linker, .longnames => {},
2967 else => coff.targetStore(
2968 &coff.secondLinkerMemberOffsetsSlice()[@intFromEnum(mi) - Member.Index.known_count],
2969 @intCast(ni.fileLocation(&coff.mf, false).offset),
2970 ),
2971 }
2972
2973 if (member.kind == .coff)
2974 try coff.pending_members.put(coff.base.comp.gpa, mi, {});
2975 },
2976 .archive_member,
2977 => {},
2978 .image_section => |si| {
2979 const file_offset = if (isArchive(coff))
2980 si.get(coff).ni.location(&coff.mf).resolve(&coff.mf)[0]
2981 else
2982 ni.fileLocation(&coff.mf, false).offset;
2983
2984 return coff.targetStore(
2985 &si.get(coff).section_number.header(coff).pointer_to_raw_data,
2986 @intCast(file_offset),
2987 );
2988 },
24102989 .import_directory_table => coff.targetStore(
24112990 &coff.dataDirectoryPtr(.IMPORT).virtual_address,
24122991 coff.computeNodeRva(ni),
......@@ -2523,32 +3102,69 @@ fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void {
25233102
25243103fn flushResized(coff: *Coff, ni: MappedFile.Node.Index) !void {
25253104 _, const size = ni.location(&coff.mf).resolve(&coff.mf);
3105 log.debug("flushResized({s}, 0x{x})", .{ @tagName(coff.getNode(ni)), size });
3106
25263107 switch (coff.getNode(ni)) {
2527 .file => {},
3108 .file => {
3109 if (coff.isArchive() and coff.members.items.len > 0) {
3110 const last_member = coff.members.items[coff.members.items.len - 1];
3111 assert(Node.known.file.reverseChildren(&coff.mf).ni == last_member.content_ni);
3112 try coff.flushResized(last_member.content_ni);
3113 }
3114 },
25283115 .header => {
2529 switch (coff.optionalHeaderPtr()) {
2530 inline else => |optional_header| coff.targetStore(
2531 &optional_header.size_of_headers,
2532 @intCast(size),
2533 ),
3116 if (coff.isImage()) {
3117 switch (coff.optionalHeaderPtr()) {
3118 inline else => |optional_header| coff.targetStore(
3119 &optional_header.size_of_headers,
3120 @intCast(size),
3121 ),
3122 }
3123
3124 if (size > coff.image_section_table.items[0].get(coff).rva) try coff.virtualSlide(
3125 0,
3126 std.mem.alignForward(
3127 u32,
3128 @intCast(size * 4),
3129 coff.optionalHeaderField(.section_alignment),
3130 ),
3131 );
25343132 }
2535 if (size > coff.image_section_table.items[0].get(coff).rva) try coff.virtualSlide(
2536 0,
2537 std.mem.alignForward(
2538 u32,
2539 @intCast(size * 4),
2540 coff.optionalHeaderField(.section_alignment),
2541 ),
2542 );
25433133 },
2544 .signature, .coff_header, .optional_header, .data_directories => unreachable,
3134 .signature,
3135 .archive_member_header,
3136 => unreachable,
3137 .archive_member => |mi| {
3138 const content_ni = mi.get(coff).content_ni;
3139 const next_ni = content_ni.next(&coff.mf);
3140 const offset, _ = content_ni.location(&coff.mf).resolve(&coff.mf);
3141 const next_offset = switch (next_ni) {
3142 .none => offset: {
3143 assert(content_ni.parent(&coff.mf) == Node.known.file);
3144 // This must take into account the final file size. If there are trailing
3145 // bytes, they will be expected to contain another valid member header
3146 break :offset coff.mf.memory_map.memory.len;
3147 },
3148 else => offset: {
3149 assert(coff.getNode(next_ni) == .archive_member_header);
3150 break :offset next_ni.location(&coff.mf).resolve(&coff.mf)[0];
3151 },
3152 };
3153
3154 // Not inserting IMAGE_ARCHIVE_PAD `\n` byte here, because we are expanding to full size
3155 Member.storeHeaderDecimalStr(&mi.get(coff).headerPtr(coff).size, next_offset - offset);
3156 },
3157 .coff_header,
3158 .optional_header,
3159 .data_directories,
3160 => unreachable,
25453161 .section_table => {},
25463162 .image_section => |si| {
25473163 const sym = si.get(coff);
25483164 const section_index = sym.section_number.toIndex();
25493165 const section = &coff.sectionTableSlice()[section_index];
25503166 coff.targetStore(&section.size_of_raw_data, @intCast(size));
2551 if (size > coff.targetLoad(&section.virtual_size)) {
3167 if (coff.isImage() and size > coff.targetLoad(&section.virtual_size)) {
25523168 const virtual_size = std.mem.alignForward(
25533169 u32,
25543170 @intCast(size * 4),
......@@ -2562,16 +3178,87 @@ fn flushResized(coff: *Coff, ni: MappedFile.Node.Index) !void {
25623178 &coff.dataDirectoryPtr(.IMPORT).size,
25633179 @intCast(size),
25643180 ),
2565 .import_lookup_table, .import_address_table, .import_hint_name_table => {},
2566 .export_directory_table => coff.targetStore(
2567 &coff.dataDirectoryPtr(.EXPORT).size,
2568 @intCast(size),
2569 ),
2570 .export_address_table, .export_name_pointer_table, .export_ordinal_table, .export_name_table => {},
3181 .import_lookup_table,
3182 .import_address_table,
3183 .import_hint_name_table,
3184 => {},
3185 .export_directory_table => unreachable,
3186 .export_address_table,
3187 .export_name_pointer_table,
3188 .export_ordinal_table,
3189 .export_name_table,
3190 => {},
25713191 inline .pseudo_section,
25723192 .object_section,
2573 => |smi| smi.symbol(coff).get(coff).size = @intCast(size),
2574 .global, .nav, .uav, .lazy_code, .lazy_const_data => {},
3193 => |smi, tag| {
3194 if (tag == .pseudo_section and smi.name(coff) == .@".edata") {
3195 coff.targetStore(
3196 &coff.dataDirectoryPtr(.EXPORT).size,
3197 @intCast(size),
3198 );
3199 }
3200
3201 smi.symbol(coff).get(coff).size = @intCast(size);
3202 },
3203 .global,
3204 .nav,
3205 .uav,
3206 .lazy_code,
3207 .lazy_const_data,
3208 => {},
3209 .placeholder => unreachable,
3210 }
3211}
3212
3213fn flushMember(coff: *Coff, mi: Member.Index) !void {
3214 const member = mi.get(coff);
3215 switch (member.kind) {
3216 .first_linker,
3217 .longnames,
3218 .import,
3219 => unreachable,
3220 .second_linker => {
3221 const Context = struct {
3222 coff: *Coff,
3223 indices: []u16,
3224 strings: []String,
3225
3226 pub fn lessThan(ctx: @This(), lhs: usize, rhs: usize) bool {
3227 return std.mem.lessThan(
3228 u8,
3229 ctx.strings[lhs].toSlice(ctx.coff),
3230 ctx.strings[rhs].toSlice(ctx.coff),
3231 );
3232 }
3233
3234 pub fn swap(ctx: @This(), lhs: usize, rhs: usize) void {
3235 std.mem.swap(u16, &ctx.indices[lhs], &ctx.indices[rhs]);
3236 std.mem.swap(String, &ctx.strings[lhs], &ctx.strings[rhs]);
3237 }
3238 };
3239
3240 std.sort.pdqContext(0, coff.lib_string_table.items.len, Context{
3241 .coff = coff,
3242 .indices = coff.secondLinkerMemberIndicesSlice(),
3243 .strings = coff.lib_string_table.items,
3244 });
3245
3246 var offset: u64 = 0;
3247
3248 var string_table = coff.secondLinkerMemberStringsSlice();
3249 for (coff.lib_string_table.items) |string| {
3250 const str = string.toSlice(coff);
3251 @memcpy(string_table[offset..][0..str.len], str);
3252 string_table[offset + str.len] = 0;
3253 offset += str.len + 1;
3254 }
3255 },
3256 .coff => {
3257 const file_offset: u32 = @intCast(member.header_ni.fileLocation(&coff.mf, false).offset);
3258 const first_linker_offsets = coff.firstLinkerMemberOffsetsSlice();
3259 for (member.symbol_offsets.values()) |offset_index|
3260 first_linker_offsets[offset_index] = std.mem.nativeTo(u32, file_offset, .big);
3261 },
25753262 }
25763263}
25773264
......@@ -2666,12 +3353,14 @@ fn updateExportsInner(
26663353 ))),
26673354 };
26683355 while (try coff.idle(pt.tid)) {}
3356
26693357 const exported_ni = exported_si.node(coff);
26703358 const exported_sym = exported_si.get(coff);
26713359 for (export_indices) |export_index| {
26723360 const @"export" = export_index.ptr(zcu);
26733361 const name = @"export".opts.name.toSlice(ip);
2674 const export_si = try coff.globalSymbol(name, null);
3362 const symbol_gop = try coff.getOrPutGlobalSymbol(name, null);
3363 const export_si = symbol_gop.value_ptr.*;
26753364 const export_sym = export_si.get(coff);
26763365 export_sym.ni = exported_ni;
26773366 export_sym.rva = exported_sym.rva;
......@@ -2687,6 +3376,13 @@ fn updateExportsInner(
26873376 std.mem.byteSwapAllFields(std.coff.ImageDataDirectory, tls_directory);
26883377 }
26893378
3379 if (coff.isArchive())
3380 try coff.addMemberSymbol(
3381 symbol_gop.key_ptr.*.name,
3382 coff.getNode(Node.known.zcu_member).archive_member,
3383 export_si,
3384 );
3385
26903386 if (coff.export_table.ni == .none) continue;
26913387
26923388 const entries_ctx = ExportTable.Adapter{ .coff = coff };
......@@ -2703,7 +3399,7 @@ fn updateExportsInner(
27033399 if (export_count > std.math.maxInt(@FieldType(std.coff.ExportDirectoryTable, "number_of_entries")))
27043400 return coff.base.comp.link_diags.fail("exceeded maximum number of exports", .{});
27053401
2706 const name_index = coff.export_table.name_table_ni.fileLocation(&coff.mf, true).size;
3402 const name_index: u64 = coff.export_table.name_table_ni.location(&coff.mf).resolve(&coff.mf)[1];
27073403 const new_name_table_size = name_index + name.len + 1;
27083404 if (new_name_table_size > std.math.maxInt(@FieldType(ExportTable.Entry, "name_index")))
27093405 return coff.base.comp.link_diags.fail("exports name table limit reached", .{});
......@@ -2714,19 +3410,23 @@ fn updateExportsInner(
27143410 @memcpy(name_table_slice[name_index..][0 .. name.len + 1], name[0 .. name.len + 1]);
27153411
27163412 // If the new name sorts after the current tail of the sorted list, we don't need to re-sort
2717 const ordinal_table_slice = coff.exportOrdinalTableSlice();
2718 if (ordinal_table_slice.len > 0 and !coff.export_table.pending_sort) {
2719 const tail_index: ExportTable.Ordinal =
2720 @enumFromInt(ordinal_table_slice[ordinal_table_slice.len - 1].unbiased_ordinal);
2721 const tail_entry = tail_index.get(coff);
2722 const tail_name = name_table_slice[tail_entry.name_index..][0..tail_entry.name_len];
2723 coff.export_table.pending_sort = std.mem.lessThan(u8, name, tail_name);
3413 {
3414 const ordinal_table_slice = coff.exportOrdinalTableSlice();
3415 if (ordinal_table_slice.len > 0 and !coff.export_table.pending_sort) {
3416 const tail_index: ExportTable.Ordinal =
3417 @enumFromInt(ordinal_table_slice[ordinal_table_slice.len - 1].unbiased_ordinal);
3418 const tail_entry = tail_index.get(coff);
3419 const tail_name = name_table_slice[tail_entry.name_index..][0..tail_entry.name_len];
3420 coff.export_table.pending_sort = std.mem.lessThan(u8, name, tail_name);
3421 }
27243422 }
27253423
27263424 const edt = coff.exportDirectoryTable();
27273425 coff.targetStore(&edt.number_of_names, @intCast(export_count));
27283426 edt.number_of_entries = edt.number_of_names;
27293427
3428 // TODO: If we had an estimate of the total number of exports this could be a lot more efficient
3429
27303430 try coff.export_table.export_address_table_si.node(coff).resize(
27313431 &coff.mf,
27323432 gpa,
......@@ -2783,22 +3483,24 @@ pub fn deleteExport(coff: *Coff, exported: Zcu.Exported, name: InternPool.NullTe
27833483 _ = name;
27843484}
27853485
2786pub fn dump(coff: *Coff, tid: Zcu.PerThread.Id) Io.Cancelable!void {
3486fn dumpStderr(coff: *Coff, tid: Zcu.PerThread.Id) !void {
27873487 const comp = coff.base.comp;
27883488 const io = comp.io;
27893489 var buffer: [512]u8 = undefined;
27903490 const stderr = try io.lockStderr(&buffer, null);
27913491 defer io.unlockStderr();
27923492 const w = &stderr.file_writer.interface;
2793 coff.printNode(tid, w, .root, 0) catch |err| switch (err) {
2794 error.WriteFailed => return stderr.err.?,
2795 };
3493 try coff.dump(w, tid);
3494}
3495
3496pub fn dump(coff: *Coff, w: *Io.Writer, tid: Zcu.PerThread.Id) !void {
3497 try coff.printNode(tid, w, .root, 0);
27963498}
27973499
27983500pub fn printNode(
27993501 coff: *Coff,
28003502 tid: Zcu.PerThread.Id,
2801 w: *std.Io.Writer,
3503 w: *Io.Writer,
28023504 ni: MappedFile.Node.Index,
28033505 indent: usize,
28043506) !void {
......@@ -2830,7 +3532,7 @@ pub fn printNode(
28303532 const ip = &zcu.intern_pool;
28313533 const nav = ip.getNav(nmi.navIndex(coff));
28323534 try w.print("({f}, {f})", .{
2833 Type.fromInterned(nav.typeOf(ip)).fmt(.{ .zcu = zcu, .tid = tid }),
3535 Type.fromInterned(ip.typeOf(nav.resolved.?.value)).fmt(.{ .zcu = zcu, .tid = tid }),
28343536 nav.fqn.fmt(ip),
28353537 });
28363538 },
......@@ -2876,7 +3578,7 @@ pub fn printNode(
28763578 const line_len = 0x10;
28773579 var line_it = std.mem.window(
28783580 u8,
2879 coff.mf.contents[@intCast(file_loc.offset)..][0..@intCast(file_loc.size)],
3581 coff.mf.memory_map.memory[@intCast(file_loc.offset)..][0..@intCast(file_loc.size)],
28803582 line_len,
28813583 line_len,
28823584 );
src/link/Elf2.zig+3-11
......@@ -6711,16 +6711,8 @@ pub fn deleteExport(elf: *Elf, exported: Zcu.Exported, name: InternPool.NullTerm
67116711 _ = name;
67126712}
67136713
6714pub fn dump(elf: *Elf, tid: Zcu.PerThread.Id) Io.Cancelable!void {
6715 const comp = elf.base.comp;
6716 const io = comp.io;
6717 var buffer: [512]u8 = undefined;
6718 const stderr = try io.lockStderr(&buffer, null);
6719 defer io.lockStderr();
6720 const w = &stderr.file_writer.interface;
6721 elf.printNode(tid, w, .root, 0) catch |err| switch (err) {
6722 error.WriteFailed => return stderr.err.?,
6723 };
6714pub fn dump(elf: *Elf, w: *Io.Writer, tid: Zcu.PerThread.Id) !void {
6715 return elf.printNode(tid, w, .root, 0);
67246716}
67256717
67266718pub fn printNode(
......@@ -6770,7 +6762,7 @@ pub fn printNode(
67706762 const ip = &zcu.intern_pool;
67716763 const nav = ip.getNav(nmi.navIndex(elf));
67726764 try w.print("({f}, {f})", .{
6773 Type.fromInterned(nav.typeOf(ip)).fmt(.{ .zcu = zcu, .tid = tid }),
6765 Type.fromInterned(ip.typeOf(nav.resolved.?.value)).fmt(.{ .zcu = zcu, .tid = tid }),
67746766 nav.fqn.fmt(ip),
67756767 });
67766768 },
src/link/MappedFile.zig+7
......@@ -188,6 +188,10 @@ pub const Node = extern struct {
188188 return ni.get(mf).parent;
189189 }
190190
191 pub fn next(ni: Node.Index, mf: *const MappedFile) Node.Index {
192 return ni.get(mf).next;
193 }
194
191195 pub fn ChildIterator(comptime direction: enum { prev, next }) type {
192196 return struct {
193197 mf: *const MappedFile,
......@@ -834,6 +838,7 @@ fn resizeNode(mf: *MappedFile, gpa: std.mem.Allocator, ni: Node.Index, requested
834838 shift = first_floating.flags.alignment.forward(@intCast(
835839 @max(shift, first_floating_size),
836840 ));
841
837842 // Not enough space, try the next node
838843 last_fixed_ni = first_floating_ni;
839844 first_floating_ni = first_floating.next;
......@@ -1135,6 +1140,8 @@ fn ensureTotalCapacityPreciseInner(mf: *MappedFile, new_capacity: usize) (Alloca
11351140 error.OperationUnsupported => {},
11361141 else => |e| return e,
11371142 }
1143
1144 try mf.memory_map.write(io);
11381145 unmap(mf);
11391146 }
11401147
test/standalone/shared_library/build.zig+11-1
......@@ -13,7 +13,14 @@ pub fn build(b: *std.Build) void {
1313 const lib_use_llvm: []const bool = &.{ true, true, false, false };
1414
1515 for (exe_names, lib_names, lib_link_libc, lib_use_llvm) |exe_name, lib_name, dyn_libc, use_llvm| {
16 if (target.result.os.tag == .windows and target.result.abi == .gnu and dyn_libc and !use_llvm)
16 if (!use_llvm and target.result.os.tag == .macos) continue; // TODO: Library not loaded: @rpath/libmathtest-no-llvm.dylib (segment '__CONST_ZIG' vm address out of order)
17 if (!use_llvm and target.result.os.tag == .freebsd) continue; // TODO: Shared object "libmathtest-no-llvm.so.1" not found
18 if (!use_llvm and target.result.os.tag == .netbsd) continue; // TODO: Shared object "libmathtest-no-llvm.so.1" not found
19 if (!use_llvm and target.result.os.tag == .openbsd) continue; // TODO: duplicate symbol definition: atexit
20 if (!use_llvm and target.result.cpu.arch == .loongarch64) continue; // TODO
21 if (!use_llvm and target.result.cpu.arch == .powerpc64le) continue; // TODO
22 if (!use_llvm and target.result.cpu.arch == .s390x) continue; // TODO
23 if (!use_llvm and target.result.os.tag == .windows and target.result.abi == .gnu and dyn_libc)
1724 continue; // TODO: sub-compilation of compiler_rt failed (failed to link with LLD: LibCInstallationNotAvailable)
1825
1926 const lib = b.addLibrary(.{
......@@ -44,6 +51,9 @@ pub fn build(b: *std.Build) void {
4451 });
4552 exe.root_module.linkLibrary(lib);
4653
54 b.getInstallStep().dependOn(&b.addInstallArtifact(lib, .{}).step);
55 b.getInstallStep().dependOn(&b.addInstallArtifact(exe, .{}).step);
56
4757 const run_cmd = b.addRunArtifact(exe);
4858 test_step.dependOn(&run_cmd.step);
4959 }