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 {...@@ -1963,3 +1963,30 @@ pub const IMAGE = struct {
1963 };1963 };
1964 };1964 };
1965};1965};
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(...@@ -93,7 +93,7 @@ fn DllMainCRTStartup(
93 fdwReason: std.os.windows.DWORD,93 fdwReason: std.os.windows.DWORD,
94 lpReserved: std.os.windows.LPVOID,94 lpReserved: std.os.windows.LPVOID,
95) callconv(.winapi) std.os.windows.BOOL {95) callconv(.winapi) std.os.windows.BOOL {
96 if (!builtin.single_threaded and !builtin.link_libc) {96 if (!builtin.single_threaded) {
97 _ = @import("os/windows/tls.zig");97 _ = @import("os/windows/tls.zig");
98 }98 }
9999
src/crash_report.zig+28
...@@ -84,6 +84,25 @@ pub const CodegenFunc = if (enabled) struct {...@@ -84,6 +84,25 @@ pub const CodegenFunc = if (enabled) struct {
84 pub fn stop(_: InternPool.Index) void {}84 pub fn stop(_: InternPool.Index) void {}
85};85};
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
87fn dumpCrashContext() Io.Writer.Error!void {106fn dumpCrashContext() Io.Writer.Error!void {
88 const S = struct {107 const S = struct {
89 /// In the case of recursive panics or segfaults, don't print the context for a second time.108 /// 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 {...@@ -111,6 +130,14 @@ fn dumpCrashContext() Io.Writer.Error!void {
111 try w.print("Generating function '{f}'\n\n", .{func_fqn.fmt(&cg.zcu.intern_pool)});130 try w.print("Generating function '{f}'\n\n", .{func_fqn.fmt(&cg.zcu.intern_pool)});
112 } else if (AnalyzeBody.current) |anal| {131 } else if (AnalyzeBody.current) |anal| {
113 try dumpCrashContextSema(anal, w, &S.crash_heap);132 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 }
114 } else {141 } else {
115 try w.writeAll("(no context)\n\n");142 try w.writeAll("(no context)\n\n");
116 }143 }
...@@ -185,6 +212,7 @@ const Zir = std.zig.Zir;...@@ -185,6 +212,7 @@ const Zir = std.zig.Zir;
185212
186const Sema = @import("Sema.zig");213const Sema = @import("Sema.zig");
187const Zcu = @import("Zcu.zig");214const Zcu = @import("Zcu.zig");
215const link = @import("link.zig");
188const InternPool = @import("InternPool.zig");216const InternPool = @import("InternPool.zig");
189const dev = @import("dev.zig");217const dev = @import("dev.zig");
190const print_zir = @import("print_zir.zig");218const print_zir = @import("print_zir.zig");
src/link.zig+33-1
...@@ -25,6 +25,7 @@ const Package = @import("Package.zig");...@@ -25,6 +25,7 @@ const Package = @import("Package.zig");
25const dev = @import("dev.zig");25const dev = @import("dev.zig");
26const target_util = @import("target.zig");26const target_util = @import("target.zig");
27const codegen = @import("codegen.zig");27const codegen = @import("codegen.zig");
28const crash_report = @import("crash_report.zig");
2829
29pub const aarch64 = @import("link/aarch64.zig");30pub const aarch64 = @import("link/aarch64.zig");
30pub const LdScript = @import("link/LdScript.zig");31pub const LdScript = @import("link/LdScript.zig");
...@@ -790,6 +791,7 @@ pub const File = struct {...@@ -790,6 +791,7 @@ pub const File = struct {
790 assert(base.comp.zcu.?.llvm_object == null);791 assert(base.comp.zcu.?.llvm_object == null);
791 const nav = pt.zcu.intern_pool.getNav(nav_index);792 const nav = pt.zcu.intern_pool.getNav(nav_index);
792 assert(nav.resolved.?.value != .none);793 assert(nav.resolved.?.value != .none);
794
793 switch (base.tag) {795 switch (base.tag) {
794 .lld => unreachable,796 .lld => unreachable,
795 .plan9 => unreachable,797 .plan9 => unreachable,
...@@ -924,6 +926,9 @@ pub const File = struct {...@@ -924,6 +926,9 @@ pub const File = struct {
924 /// Commit pending changes and write headers. Takes into account final output mode.926 /// Commit pending changes and write headers. Takes into account final output mode.
925 /// `arena` has the lifetime of the call to `Compilation.update`.927 /// `arena` has the lifetime of the call to `Compilation.update`.
926 pub fn flush(base: *File, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) Error!void {928 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
927 const comp = base.comp;932 const comp = base.comp;
928 const io = comp.io;933 const io = comp.io;
929 if (comp.clang_preprocessor_mode == .yes or comp.clang_preprocessor_mode == .pch) {934 if (comp.clang_preprocessor_mode == .yes or comp.clang_preprocessor_mode == .pch) {
...@@ -975,6 +980,10 @@ pub const File = struct {...@@ -975,6 +980,10 @@ pub const File = struct {
975 export_indices: []const Zcu.Export.Index,980 export_indices: []const Zcu.Export.Index,
976 ) Error!void {981 ) Error!void {
977 assert(base.comp.zcu.?.llvm_object == null);982 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
978 switch (base.tag) {987 switch (base.tag) {
979 .lld => unreachable,988 .lld => unreachable,
980 .plan9 => unreachable,989 .plan9 => unreachable,
...@@ -1006,6 +1015,7 @@ pub const File = struct {...@@ -1006,6 +1015,7 @@ pub const File = struct {
1006 /// Never called when LLVM is codegenning the ZCU.1015 /// Never called when LLVM is codegenning the ZCU.
1007 pub fn getNavVAddr(base: *File, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index, reloc_info: RelocInfo) Error!u64 {1016 pub fn getNavVAddr(base: *File, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index, reloc_info: RelocInfo) Error!u64 {
1008 assert(base.comp.zcu.?.llvm_object == null);1017 assert(base.comp.zcu.?.llvm_object == null);
1018
1009 switch (base.tag) {1019 switch (base.tag) {
1010 .lld => unreachable,1020 .lld => unreachable,
1011 .c => unreachable,1021 .c => unreachable,
...@@ -1027,6 +1037,7 @@ pub const File = struct {...@@ -1027,6 +1037,7 @@ pub const File = struct {
1027 decl_align: InternPool.Alignment,1037 decl_align: InternPool.Alignment,
1028 ) Error!SymbolId {1038 ) Error!SymbolId {
1029 assert(base.comp.zcu.?.llvm_object == null);1039 assert(base.comp.zcu.?.llvm_object == null);
1040
1030 switch (base.tag) {1041 switch (base.tag) {
1031 .lld => unreachable,1042 .lld => unreachable,
1032 .c => unreachable,1043 .c => unreachable,
...@@ -1043,6 +1054,7 @@ pub const File = struct {...@@ -1043,6 +1054,7 @@ pub const File = struct {
1043 /// Never called when LLVM is codegenning the ZCU.1054 /// Never called when LLVM is codegenning the ZCU.
1044 pub fn getUavVAddr(base: *File, decl_val: InternPool.Index, reloc_info: RelocInfo) Error!u64 {1055 pub fn getUavVAddr(base: *File, decl_val: InternPool.Index, reloc_info: RelocInfo) Error!u64 {
1045 assert(base.comp.zcu.?.llvm_object == null);1056 assert(base.comp.zcu.?.llvm_object == null);
1057
1046 switch (base.tag) {1058 switch (base.tag) {
1047 .lld => unreachable,1059 .lld => unreachable,
1048 .c => unreachable,1060 .c => unreachable,
...@@ -1063,6 +1075,7 @@ pub const File = struct {...@@ -1063,6 +1075,7 @@ pub const File = struct {
1063 name: InternPool.NullTerminatedString,1075 name: InternPool.NullTerminatedString,
1064 ) void {1076 ) void {
1065 assert(base.comp.zcu.?.llvm_object == null);1077 assert(base.comp.zcu.?.llvm_object == null);
1078
1066 switch (base.tag) {1079 switch (base.tag) {
1067 .lld => unreachable,1080 .lld => unreachable,
1068 .plan9 => unreachable,1081 .plan9 => unreachable,
...@@ -1077,6 +1090,24 @@ pub const File = struct {...@@ -1077,6 +1090,24 @@ pub const File = struct {
1077 }1090 }
1078 }1091 }
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
1080 /// Opens a path as an object file and parses it into the linker.1111 /// Opens a path as an object file and parses it into the linker.
1081 fn openLoadObject(base: *File, path: Path) anyerror!void {1112 fn openLoadObject(base: *File, path: Path) anyerror!void {
1082 if (base.tag == .lld) return;1113 if (base.tag == .lld) return;
...@@ -1178,8 +1209,9 @@ pub const File = struct {...@@ -1178,8 +1209,9 @@ pub const File = struct {
1178 pub fn loadInput(base: *File, input: Input) anyerror!void {1209 pub fn loadInput(base: *File, input: Input) anyerror!void {
1179 if (base.tag == .lld) return;1210 if (base.tag == .lld) return;
1180 assert(!base.post_prelink);1211 assert(!base.post_prelink);
1212
1181 switch (base.tag) {1213 switch (base.tag) {
1182 inline .elf, .elf2, .wasm, .spirv => |tag| {1214 inline .coff2, .elf, .elf2, .wasm, .spirv => |tag| {
1183 dev.check(tag.devFeature());1215 dev.check(tag.devFeature());
1184 return @as(*tag.Type(), @fieldParentPtr("base", base)).loadInput(input);1216 return @as(*tag.Type(), @fieldParentPtr("base", base)).loadInput(input);
1185 },1217 },
src/link/Coff.zig+826-124
...@@ -23,6 +23,11 @@ const implib = @import("../libs/mingw/implib.zig");...@@ -23,6 +23,11 @@ const implib = @import("../libs/mingw/implib.zig");
23base: link.File,23base: link.File,
24mf: MappedFile,24mf: MappedFile,
25nodes: std.MultiArrayList(Node),25nodes: 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,
26import_table: ImportTable,31import_table: ImportTable,
27export_table: ExportTable,32export_table: ExportTable,
28strings: std.HashMapUnmanaged(33strings: std.HashMapUnmanaged(
...@@ -137,18 +142,27 @@ pub const msdos_stub: [120]u8 = .{...@@ -137,18 +142,27 @@ pub const msdos_stub: [120]u8 = .{
137pub const Node = union(enum) {142pub const Node = union(enum) {
138 file,143 file,
139 header,144 header,
145 /// Images and archives only.
140 signature,146 signature,
147 /// Archives only.
148 archive_member_header: Member.Index,
149 archive_member: Member.Index,
150
141 coff_header,151 coff_header,
152 /// Image only
142 optional_header,153 optional_header,
154 /// Image only
143 data_directories,155 data_directories,
144 section_table,156 section_table,
145 image_section: Symbol.Index,157 image_section: Symbol.Index,
146158
159 /// Only images contain imports
147 import_directory_table,160 import_directory_table,
148 import_lookup_table: ImportTable.Index,161 import_lookup_table: ImportTable.Index,
149 import_address_table: ImportTable.Index,162 import_address_table: ImportTable.Index,
150 import_hint_name_table: ImportTable.Index,163 import_hint_name_table: ImportTable.Index,
151164
165 /// Only images contain exports
152 export_directory_table,166 export_directory_table,
153 export_address_table,167 export_address_table,
154 export_name_pointer_table,168 export_name_pointer_table,
...@@ -163,6 +177,9 @@ pub const Node = union(enum) {...@@ -163,6 +177,9 @@ pub const Node = union(enum) {
163 lazy_code: LazyMapRef.Index(.code),177 lazy_code: LazyMapRef.Index(.code),
164 lazy_const_data: LazyMapRef.Index(.const_data),178 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
166 pub const PseudoSectionMapIndex = enum(u32) {183 pub const PseudoSectionMapIndex = enum(u32) {
167 _,184 _,
168185
...@@ -262,6 +279,14 @@ pub const Node = union(enum) {...@@ -262,6 +279,14 @@ pub const Node = union(enum) {
262 file,279 file,
263 header,280 header,
264 signature,281 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,
265 coff_header,290 coff_header,
266 optional_header,291 optional_header,
267 data_directories,292 data_directories,
...@@ -279,8 +304,141 @@ pub const Node = union(enum) {...@@ -279,8 +304,141 @@ pub const Node = union(enum) {
279 }304 }
280};305};
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
282pub const ExportTable = struct {439pub const ExportTable = struct {
283 ni: MappedFile.Node.Index,440 ni: MappedFile.Node.Index,
441 export_directory_table_ni: MappedFile.Node.Index,
284 export_address_table_si: Symbol.Index,442 export_address_table_si: Symbol.Index,
285 name_pointer_table_ni: MappedFile.Node.Index,443 name_pointer_table_ni: MappedFile.Node.Index,
286 ordinal_table_ni: MappedFile.Node.Index,444 ordinal_table_ni: MappedFile.Node.Index,
...@@ -535,6 +693,13 @@ pub const Reloc = extern struct {...@@ -535,6 +693,13 @@ pub const Reloc = extern struct {
535 const loc_slice = loc_sym.ni.slice(&coff.mf)[@intCast(reloc.offset)..];693 const loc_slice = loc_sym.ni.slice(&coff.mf)[@intCast(reloc.offset)..];
536 const target_rva = target_sym.rva +% @as(u64, @bitCast(reloc.addend));694 const target_rva = target_sym.rva +% @as(u64, @bitCast(reloc.addend));
537 const target_endian = coff.targetEndian();695 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
538 switch (coff.targetLoad(&coff.headerPtr().machine)) {703 switch (coff.targetLoad(&coff.headerPtr().machine)) {
539 else => |machine| @panic(@tagName(machine)),704 else => |machine| @panic(@tagName(machine)),
540 .AMD64 => switch (reloc.type.AMD64) {705 .AMD64 => switch (reloc.type.AMD64) {
...@@ -543,13 +708,13 @@ pub const Reloc = extern struct {...@@ -543,13 +708,13 @@ pub const Reloc = extern struct {
543 .ADDR64 => std.mem.writeInt(708 .ADDR64 => std.mem.writeInt(
544 u64,709 u64,
545 loc_slice[0..8],710 loc_slice[0..8],
546 coff.optionalHeaderField(.image_base) + target_rva,711 base + target_rva,
547 target_endian,712 target_endian,
548 ),713 ),
549 .ADDR32 => std.mem.writeInt(714 .ADDR32 => std.mem.writeInt(
550 u32,715 u32,
551 loc_slice[0..4],716 loc_slice[0..4],
552 @intCast(coff.optionalHeaderField(.image_base) + target_rva),717 @intCast(base + target_rva),
553 target_endian,718 target_endian,
554 ),719 ),
555 .ADDR32NB => std.mem.writeInt(720 .ADDR32NB => std.mem.writeInt(
...@@ -607,7 +772,7 @@ pub const Reloc = extern struct {...@@ -607,7 +772,7 @@ pub const Reloc = extern struct {
607 .DIR16 => std.mem.writeInt(772 .DIR16 => std.mem.writeInt(
608 u16,773 u16,
609 loc_slice[0..2],774 loc_slice[0..2],
610 @intCast(coff.optionalHeaderField(.image_base) + target_rva),775 @intCast(base + target_rva),
611 target_endian,776 target_endian,
612 ),777 ),
613 .REL16 => std.mem.writeInt(778 .REL16 => std.mem.writeInt(
...@@ -619,7 +784,7 @@ pub const Reloc = extern struct {...@@ -619,7 +784,7 @@ pub const Reloc = extern struct {
619 .DIR32 => std.mem.writeInt(784 .DIR32 => std.mem.writeInt(
620 u32,785 u32,
621 loc_slice[0..4],786 loc_slice[0..4],
622 @intCast(coff.optionalHeaderField(.image_base) + target_rva),787 @intCast(base + target_rva),
623 target_endian,788 target_endian,
624 ),789 ),
625 .DIR32NB => std.mem.writeInt(790 .DIR32NB => std.mem.writeInt(
...@@ -691,14 +856,6 @@ fn create(...@@ -691,14 +856,6 @@ fn create(
691 assert(target.ofmt == .coff);856 assert(target.ofmt == .coff);
692 if (target.cpu.arch.endian() != comptime targetEndian(undefined))857 if (target.cpu.arch.endian() != comptime targetEndian(undefined))
693 return error.UnsupportedCOFFArchitecture;858 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 };
702 const machine = target.toCoffMachine();859 const machine = target.toCoffMachine();
703 const timestamp: u32 = 0;860 const timestamp: u32 = 0;
704 const major_subsystem_version = options.major_subsystem_version orelse 6;861 const major_subsystem_version = options.major_subsystem_version orelse 6;
...@@ -743,12 +900,20 @@ fn create(...@@ -743,12 +900,20 @@ fn create(
743 },900 },
744 .mf = try .init(file, comp.gpa, io),901 .mf = try .init(file, comp.gpa, io),
745 .nodes = .empty,902 .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 },
746 .import_table = .{910 .import_table = .{
747 .ni = .none,911 .ni = .none,
748 .entries = .empty,912 .entries = .empty,
749 },913 },
750 .export_table = .{914 .export_table = .{
751 .ni = .none,915 .ni = .none,
916 .export_directory_table_ni = .none,
752 .export_address_table_si = .null,917 .export_address_table_si = .null,
753 .name_pointer_table_ni = .none,918 .name_pointer_table_ni = .none,
754 .ordinal_table_ni = .none,919 .ordinal_table_ni = .none,
...@@ -785,7 +950,6 @@ fn create(...@@ -785,7 +950,6 @@ fn create(
785 }950 }
786951
787 try coff.initHeaders(952 try coff.initHeaders(
788 is_image,
789 machine,953 machine,
790 timestamp,954 timestamp,
791 major_subsystem_version,955 major_subsystem_version,
...@@ -801,6 +965,7 @@ pub fn deinit(coff: *Coff) void {...@@ -801,6 +965,7 @@ pub fn deinit(coff: *Coff) void {
801 const gpa = coff.base.comp.gpa;965 const gpa = coff.base.comp.gpa;
802 coff.mf.deinit(gpa);966 coff.mf.deinit(gpa);
803 coff.nodes.deinit(gpa);967 coff.nodes.deinit(gpa);
968 coff.long_names_table.entries.deinit(gpa);
804 coff.import_table.entries.deinit(gpa);969 coff.import_table.entries.deinit(gpa);
805 coff.export_table.entries.deinit(gpa);970 coff.export_table.entries.deinit(gpa);
806 coff.strings.deinit(gpa);971 coff.strings.deinit(gpa);
...@@ -818,9 +983,32 @@ pub fn deinit(coff: *Coff) void {...@@ -818,9 +983,32 @@ pub fn deinit(coff: *Coff) void {
818 coff.* = undefined;983 coff.* = undefined;
819}984}
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
821fn initHeaders(1010fn initHeaders(
822 coff: *Coff,1011 coff: *Coff,
823 is_image: bool,
824 machine: std.coff.IMAGE.FILE.MACHINE,1012 machine: std.coff.IMAGE.FILE.MACHINE,
825 timestamp: u32,1013 timestamp: u32,
826 major_subsystem_version: u16,1014 major_subsystem_version: u16,
...@@ -833,6 +1021,8 @@ fn initHeaders(...@@ -833,6 +1021,8 @@ fn initHeaders(
833 const gpa = comp.gpa;1021 const gpa = comp.gpa;
834 const target_endian = coff.targetEndian();1022 const target_endian = coff.targetEndian();
835 const file_align: std.mem.Alignment = comptime .fromByteUnits(default_file_alignment);1023 const file_align: std.mem.Alignment = comptime .fromByteUnits(default_file_alignment);
1024 const is_image = coff.isImage();
1025 const is_archive = coff.isArchive();
8361026
837 const optional_header_size: u16 = if (is_image) switch (magic) {1027 const optional_header_size: u16 = if (is_image) switch (magic) {
838 _ => unreachable,1028 _ => unreachable,
...@@ -843,33 +1033,106 @@ fn initHeaders(...@@ -843,33 +1033,106 @@ fn initHeaders(
843 else1033 else
844 0;1034 0;
8451035
846 const expected_nodes_len = Node.known_count + 12 +1036 var expected_nodes_len: usize = Node.known_count;
847 @as(usize, @intFromBool(comp.config.any_non_single_threaded)) * 2;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
848 try coff.nodes.ensureTotalCapacity(gpa, expected_nodes_len);1044 try coff.nodes.ensureTotalCapacity(gpa, expected_nodes_len);
849 coff.nodes.appendAssumeCapacity(.file);1045 coff.nodes.appendAssumeCapacity(.file);
8501046
851 const header_ni = Node.known.header;1047 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, .{
853 .alignment = coff.mf.flags.block_size,1049 .alignment = coff.mf.flags.block_size,
854 .fixed = true,1050 .fixed = true,
855 }));1051 }));
856 coff.nodes.appendAssumeCapacity(.header);1052 coff.nodes.appendAssumeCapacity(.header);
8571053
1054 const pe_signature = "PE\x00\x00";
1055 const archive_signature = "!<arch>\n";
1056
858 const signature_ni = Node.known.signature;1057 const signature_ni = Node.known.signature;
859 assert(signature_ni == try coff.mf.addOnlyChildNode(gpa, header_ni, .{1058 assert(signature_ni == try coff.mf.addLastChildNode(gpa, if (is_image) header_ni else Node.known.file, .{
860 .size = (if (is_image) msdos_stub.len else 0) + "PE\x00\x00".len,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,
861 .alignment = .@"4",1065 .alignment = .@"4",
862 .fixed = true,1066 .fixed = true,
863 }));1067 }));
864 coff.nodes.appendAssumeCapacity(.signature);1068 coff.nodes.appendAssumeCapacity(.signature);
865 {1069
866 const signature_slice = signature_ni.slice(&coff.mf);1070 const signature_slice = signature_ni.slice(&coff.mf);
867 if (is_image) @memcpy(signature_slice[0..msdos_stub.len], &msdos_stub);1071 if (is_image) {
868 @memcpy(signature_slice[signature_slice.len - 4 ..], "PE\x00\x00");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);
869 }1076 }
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
871 const coff_header_ni = Node.known.coff_header;1134 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, .{
873 .size = @sizeOf(std.coff.Header),1136 .size = @sizeOf(std.coff.Header),
874 .alignment = .@"4",1137 .alignment = .@"4",
875 .fixed = true,1138 .fixed = true,
...@@ -897,7 +1160,7 @@ fn initHeaders(...@@ -897,7 +1160,7 @@ fn initHeaders(
897 }1160 }
8981161
899 const optional_header_ni = Node.known.optional_header;1162 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, .{
901 .size = optional_header_size,1164 .size = optional_header_size,
902 .alignment = .@"4",1165 .alignment = .@"4",
903 .fixed = true,1166 .fixed = true,
...@@ -1009,13 +1272,13 @@ fn initHeaders(...@@ -1009,13 +1272,13 @@ fn initHeaders(
1009 }1272 }
10101273
1011 const data_directories_ni = Node.known.data_directories;1274 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, .{
1013 .size = data_directories_size,1276 .size = data_directories_size,
1014 .alignment = .@"4",1277 .alignment = .@"4",
1015 .fixed = true,1278 .fixed = true,
1016 }));1279 }));
1017 coff.nodes.appendAssumeCapacity(.data_directories);1280 coff.nodes.appendAssumeCapacity(.data_directories);
1018 {1281 if (is_image) {
1019 const data_directories = coff.dataDirectorySlice();1282 const data_directories = coff.dataDirectorySlice();
1020 @memset(data_directories, .{ .virtual_address = 0, .size = 0 });1283 @memset(data_directories, .{ .virtual_address = 0, .size = 0 });
1021 if (target_endian != native_endian) std.mem.byteSwapAllFields(1284 if (target_endian != native_endian) std.mem.byteSwapAllFields(
...@@ -1025,7 +1288,7 @@ fn initHeaders(...@@ -1025,7 +1288,7 @@ fn initHeaders(
1025 }1288 }
10261289
1027 const section_table_ni = Node.known.section_table;1290 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, .{
1029 .alignment = .@"4",1292 .alignment = .@"4",
1030 .fixed = true,1293 .fixed = true,
1031 }));1294 }));
...@@ -1057,43 +1320,45 @@ fn initHeaders(...@@ -1057,43 +1320,45 @@ fn initHeaders(
1057 .MEM_READ = true,1320 .MEM_READ = true,
1058 }) == .text);1321 }) == .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
1071 if (is_image) {1323 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(
1073 .@".edata",1336 .@".edata",
1074 .of(std.coff.ExportDirectoryTable),1337 .of(std.coff.ExportDirectoryTable),
1075 .{ .read = true },1338 .{ .read = true },
1076 )).symbol(coff).node(coff);1339 )).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(
1079 gpa,1342 gpa,
1080 edata_section_ni,1343 coff.export_table.ni,
1081 .{1344 .{
1082 .size = @sizeOf(std.coff.ExportDirectoryTable) + file_name.len + 1,1345 .size = @sizeOf(std.coff.ExportDirectoryTable) + file_name.len + 1,
1083 .alignment = .of(std.coff.ExportDirectoryTable),
1084 .fixed = true,
1085 .moved = true,1346 .moved = true,
1347 .fixed = true,
1086 },1348 },
1087 );1349 );
1350 coff.nodes.appendAssumeCapacity(.export_directory_table);
10881351
1089 const name_index = @sizeOf(std.coff.ExportDirectoryTable);1352 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]);1353 const table_slice = coff.export_table.export_directory_table_ni.slice(&coff.mf);
1091 @memset(coff.export_table.ni.slice(&coff.mf)[name_index + file_name.len ..], 0);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, .{1357 const export_address_table_ni = try coff.mf.addLastChildNode(gpa, coff.export_table.ni, .{
1094 .alignment = .of(u32),1358 .alignment = .of(std.coff.ExportAddressTableEntry),
1095 .moved = true,1359 .moved = true,
1096 });1360 });
1361 coff.nodes.appendAssumeCapacity(.export_address_table);
10971362
1098 try coff.symbol_table.ensureUnusedCapacity(gpa, 1);1363 try coff.symbol_table.ensureUnusedCapacity(gpa, 1);
1099 coff.export_table.export_address_table_si = coff.addSymbolAssumeCapacity();1364 coff.export_table.export_address_table_si = coff.addSymbolAssumeCapacity();
...@@ -1103,25 +1368,24 @@ fn initHeaders(...@@ -1103,25 +1368,24 @@ fn initHeaders(
1103 assert(export_address_table_sym.loc_relocs == .none);1368 assert(export_address_table_sym.loc_relocs == .none);
1104 export_address_table_sym.loc_relocs = @enumFromInt(coff.relocs.items.len);1369 export_address_table_sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
1105 export_address_table_sym.section_number =1370 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, .{1373 coff.export_table.name_pointer_table_ni = try coff.mf.addLastChildNode(gpa, coff.export_table.ni, .{
1109 .alignment = .of(u32),1374 .alignment = .of(std.coff.ExportNamePointerTableEntry),
1110 .moved = true,1375 .moved = true,
1111 });1376 });
1112 coff.export_table.ordinal_table_ni = try coff.mf.addLastChildNode(gpa, edata_section_ni, .{1377 coff.nodes.appendAssumeCapacity(.export_name_pointer_table);
1113 .alignment = .of(u16),1378
1379 coff.export_table.ordinal_table_ni = try coff.mf.addLastChildNode(gpa, coff.export_table.ni, .{
1380 .alignment = .of(std.coff.ExportOrdinalTableEntry),
1114 .moved = true,1381 .moved = true,
1115 });1382 });
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, .{
1117 .alignment = .of(u8),1386 .alignment = .of(u8),
1118 .moved = true,1387 .moved = true,
1119 });1388 });
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);
1125 coff.nodes.appendAssumeCapacity(.export_name_table);1389 coff.nodes.appendAssumeCapacity(.export_name_table);
11261390
1127 const export_directory_table = coff.exportDirectoryTable();1391 const export_directory_table = coff.exportDirectoryTable();
...@@ -1145,11 +1409,9 @@ fn initHeaders(...@@ -1145,11 +1409,9 @@ fn initHeaders(
1145 // While tls variables allocated at runtime are writable, the template itself is not1409 // While tls variables allocated at runtime are writable, the template itself is not
1146 if (comp.config.any_non_single_threaded) _ = try coff.objectSectionMapIndex(1410 if (comp.config.any_non_single_threaded) _ = try coff.objectSectionMapIndex(
1147 .@".tls$",1411 .@".tls$",
1148 coff.mf.flags.block_size,1412 if (is_image) coff.mf.flags.block_size else .@"1",
1149 .{ .read = true },1413 .{ .read = true },
1150 );1414 );
1151
1152 assert(coff.nodes.len == expected_nodes_len);
1153}1415}
11541416
1155pub fn startProgress(coff: *Coff, prog_node: std.Progress.Node) void {1417pub fn startProgress(coff: *Coff, prog_node: std.Progress.Node) void {
...@@ -1181,11 +1443,14 @@ fn computeNodeRva(coff: *Coff, ni: MappedFile.Node.Index) u32 {...@@ -1181,11 +1443,14 @@ fn computeNodeRva(coff: *Coff, ni: MappedFile.Node.Index) u32 {
1181 .file,1443 .file,
1182 .header,1444 .header,
1183 .signature,1445 .signature,
1446 .archive_member_header,
1447 .archive_member,
1184 .coff_header,1448 .coff_header,
1185 .optional_header,1449 .optional_header,
1186 .data_directories,1450 .data_directories,
1187 .section_table,1451 .section_table,
1188 .export_name_table,1452 .export_name_table,
1453 .placeholder,
1189 => unreachable,1454 => unreachable,
1190 .image_section => |si| si,1455 .image_section => |si| si,
1191 .import_directory_table => break :parent_rva coff.targetLoad(1456 .import_directory_table => break :parent_rva coff.targetLoad(
...@@ -1272,9 +1537,55 @@ fn targetStore(coff: *const Coff, ptr: anytype, val: @typeInfo(@TypeOf(ptr)).poi...@@ -1272,9 +1537,55 @@ fn targetStore(coff: *const Coff, ptr: anytype, val: @typeInfo(@TypeOf(ptr)).poi
1272}1537}
12731538
1274pub fn headerPtr(coff: *Coff) *std.coff.Header {1539pub fn headerPtr(coff: *Coff) *std.coff.Header {
1540 assert(coff.base.comp.zcu != null);
1275 return @ptrCast(@alignCast(Node.known.coff_header.slice(&coff.mf)));1541 return @ptrCast(@alignCast(Node.known.coff_header.slice(&coff.mf)));
1276}1542}
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
1278pub fn optionalHeaderStandardPtr(coff: *Coff) *std.coff.OptionalHeader {1589pub fn optionalHeaderStandardPtr(coff: *Coff) *std.coff.OptionalHeader {
1279 return @ptrCast(@alignCast(1590 return @ptrCast(@alignCast(
1280 Node.known.optional_header.slice(&coff.mf)[0..@sizeOf(std.coff.OptionalHeader)],1591 Node.known.optional_header.slice(&coff.mf)[0..@sizeOf(std.coff.OptionalHeader)],
...@@ -1286,6 +1597,7 @@ pub const OptionalHeaderPtr = union(std.coff.OptionalHeader.Magic) {...@@ -1286,6 +1597,7 @@ pub const OptionalHeaderPtr = union(std.coff.OptionalHeader.Magic) {
1286 @"PE32+": *std.coff.OptionalHeader.@"PE32+",1597 @"PE32+": *std.coff.OptionalHeader.@"PE32+",
1287};1598};
1288pub fn optionalHeaderPtr(coff: *Coff) OptionalHeaderPtr {1599pub fn optionalHeaderPtr(coff: *Coff) OptionalHeaderPtr {
1600 assert(coff.isImage());
1289 const slice = Node.known.optional_header.slice(&coff.mf);1601 const slice = Node.known.optional_header.slice(&coff.mf);
1290 return switch (coff.targetLoad(&coff.optionalHeaderStandardPtr().magic)) {1602 return switch (coff.targetLoad(&coff.optionalHeaderStandardPtr().magic)) {
1291 _ => unreachable,1603 _ => unreachable,
...@@ -1300,6 +1612,7 @@ pub fn optionalHeaderField(...@@ -1300,6 +1612,7 @@ pub fn optionalHeaderField(
1300 coff: *Coff,1612 coff: *Coff,
1301 comptime field: std.meta.FieldEnum(std.coff.OptionalHeader.@"PE32+"),1613 comptime field: std.meta.FieldEnum(std.coff.OptionalHeader.@"PE32+"),
1302) @FieldType(std.coff.OptionalHeader.@"PE32+", @tagName(field)) {1614) @FieldType(std.coff.OptionalHeader.@"PE32+", @tagName(field)) {
1615 assert(coff.isImage());
1303 return switch (coff.optionalHeaderPtr()) {1616 return switch (coff.optionalHeaderPtr()) {
1304 inline else => |optional_header| coff.targetLoad(&@field(optional_header, @tagName(field))),1617 inline else => |optional_header| coff.targetLoad(&@field(optional_header, @tagName(field))),
1305 };1618 };
...@@ -1308,6 +1621,7 @@ pub fn optionalHeaderField(...@@ -1308,6 +1621,7 @@ pub fn optionalHeaderField(
1308pub fn dataDirectorySlice(1621pub fn dataDirectorySlice(
1309 coff: *Coff,1622 coff: *Coff,
1310) *[std.coff.IMAGE.DIRECTORY_ENTRY.len]std.coff.ImageDataDirectory {1623) *[std.coff.IMAGE.DIRECTORY_ENTRY.len]std.coff.ImageDataDirectory {
1624 assert(coff.isImage());
1311 return @ptrCast(@alignCast(Node.known.data_directories.slice(&coff.mf)));1625 return @ptrCast(@alignCast(Node.known.data_directories.slice(&coff.mf)));
1312}1626}
1313pub fn dataDirectoryPtr(1627pub fn dataDirectoryPtr(
...@@ -1322,6 +1636,7 @@ pub fn sectionTableSlice(coff: *Coff) []std.coff.SectionHeader {...@@ -1322,6 +1636,7 @@ pub fn sectionTableSlice(coff: *Coff) []std.coff.SectionHeader {
1322}1636}
13231637
1324pub fn importDirectoryTableSlice(coff: *Coff) []std.coff.ImportDirectoryEntry {1638pub fn importDirectoryTableSlice(coff: *Coff) []std.coff.ImportDirectoryEntry {
1639 assert(coff.isImage());
1325 return @ptrCast(@alignCast(coff.import_table.ni.slice(&coff.mf)));1640 return @ptrCast(@alignCast(coff.import_table.ni.slice(&coff.mf)));
1326}1641}
1327pub fn importDirectoryEntryPtr(1642pub fn importDirectoryEntryPtr(
...@@ -1332,10 +1647,13 @@ pub fn importDirectoryEntryPtr(...@@ -1332,10 +1647,13 @@ pub fn importDirectoryEntryPtr(
1332}1647}
13331648
1334pub fn exportDirectoryTable(coff: *Coff) *std.coff.ExportDirectoryTable {1649pub 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)));
1336}1651}
13371652
1338pub fn exportNamePointerTableSlice(coff: *Coff) []std.coff.ExportNamePointerTableEntry {1653pub fn exportNamePointerTableSlice(coff: *Coff) []std.coff.ExportNamePointerTableEntry {
1654 const debug = coff.export_table.name_pointer_table_ni.slice(&coff.mf);
1655 _ = debug;
1656
1339 return @ptrCast(@alignCast(coff.export_table.name_pointer_table_ni.slice(&coff.mf)));1657 return @ptrCast(@alignCast(coff.export_table.name_pointer_table_ni.slice(&coff.mf)));
1340}1658}
13411659
...@@ -1388,6 +1706,14 @@ fn getOrPutStringAssumeCapacity(coff: *Coff, string: []const u8) String {...@@ -1388,6 +1706,14 @@ fn getOrPutStringAssumeCapacity(coff: *Coff, string: []const u8) String {
1388}1706}
13891707
1390pub fn globalSymbol(coff: *Coff, name: []const u8, lib_name: ?[]const u8) !Symbol.Index {1708pub 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 {
1391 const gpa = coff.base.comp.gpa;1717 const gpa = coff.base.comp.gpa;
1392 try coff.symbol_table.ensureUnusedCapacity(gpa, 1);1718 try coff.symbol_table.ensureUnusedCapacity(gpa, 1);
1393 const sym_gop = try coff.globals.getOrPut(gpa, .{1719 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...@@ -1398,7 +1724,7 @@ pub fn globalSymbol(coff: *Coff, name: []const u8, lib_name: ?[]const u8) !Symbo
1398 sym_gop.value_ptr.* = coff.addSymbolAssumeCapacity();1724 sym_gop.value_ptr.* = coff.addSymbolAssumeCapacity();
1399 coff.synth_prog_node.increaseEstimatedTotalItems(1);1725 coff.synth_prog_node.increaseEstimatedTotalItems(1);
1400 }1726 }
1401 return sym_gop.value_ptr.*;1727 return sym_gop;
1402}1728}
14031729
1404fn navSection(1730fn navSection(
...@@ -1500,10 +1826,187 @@ pub fn getVAddr(coff: *Coff, reloc_info: link.File.RelocInfo, target_si: Symbol....@@ -1500,10 +1826,187 @@ pub fn getVAddr(coff: *Coff, reloc_info: link.File.RelocInfo, target_si: Symbol.
1500 .I386 => .{ .I386 = .DIR32 },1826 .I386 => .{ .I386 = .DIR32 },
1501 },1827 },
1502 );1828 );
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, {});
1504}2005}
15052006
1506fn addSection(coff: *Coff, name: []const u8, flags: std.coff.SectionHeader.Flags) !Symbol.Index {2007fn addSection(coff: *Coff, name: []const u8, flags: std.coff.SectionHeader.Flags) !Symbol.Index {
2008 assert(coff.base.comp.zcu != null);
2009
1507 const gpa = coff.base.comp.gpa;2010 const gpa = coff.base.comp.gpa;
1508 try coff.nodes.ensureUnusedCapacity(gpa, 1);2011 try coff.nodes.ensureUnusedCapacity(gpa, 1);
1509 try coff.image_section_table.ensureUnusedCapacity(gpa, 1);2012 try coff.image_section_table.ensureUnusedCapacity(gpa, 1);
...@@ -1518,21 +2021,34 @@ fn addSection(coff: *Coff, name: []const u8, flags: std.coff.SectionHeader.Flags...@@ -1518,21 +2021,34 @@ fn addSection(coff: *Coff, name: []const u8, flags: std.coff.SectionHeader.Flags
1518 gpa,2021 gpa,
1519 @sizeOf(std.coff.SectionHeader) * section_table_len,2022 @sizeOf(std.coff.SectionHeader) * section_table_len,
1520 );2023 );
1521 const ni = try coff.mf.addLastChildNode(gpa, .root, .{2024
1522 .alignment = coff.mf.flags.block_size,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,
1523 .moved = true,2032 .moved = true,
1524 .bubbles_moved = false,2033 .bubbles_moved = false,
1525 });2034 });
2035
1526 const si = coff.addSymbolAssumeCapacity();2036 const si = coff.addSymbolAssumeCapacity();
1527 coff.image_section_table.appendAssumeCapacity(si);2037 coff.image_section_table.appendAssumeCapacity(si);
1528 coff.nodes.appendAssumeCapacity(.{ .image_section = si });2038 coff.nodes.appendAssumeCapacity(.{ .image_section = si });
1529 const section_table = coff.sectionTableSlice();2039 const section_table = coff.sectionTableSlice();
1530 const virtual_size = coff.optionalHeaderField(.section_alignment);2040
1531 const rva: u32 = switch (section_index) {2041 const virtual_size, const rva = if (coff.isImage()) block: {
1532 0 => @intCast(Node.known.header.location(&coff.mf).resolve(&coff.mf)[1]),2042 const virtual_size = coff.optionalHeaderField(.section_alignment);
1533 else => coff.image_section_table.items[section_index - 1].get(coff).rva +2043 const rva: u32 = switch (section_index) {
1534 coff.targetLoad(&section_table[section_index - 1].virtual_size),2044 0 => @intCast(Node.known.header.location(&coff.mf).resolve(&coff.mf)[1]),
1535 };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
1536 {2052 {
1537 const sym = si.get(coff);2053 const sym = si.get(coff);
1538 sym.ni = ni;2054 sym.ni = ni;
...@@ -1556,12 +2072,16 @@ fn addSection(coff: *Coff, name: []const u8, flags: std.coff.SectionHeader.Flags...@@ -1556,12 +2072,16 @@ fn addSection(coff: *Coff, name: []const u8, flags: std.coff.SectionHeader.Flags
1556 @memset(section.name[name.len..], 0);2072 @memset(section.name[name.len..], 0);
1557 if (coff.targetEndian() != native_endian)2073 if (coff.targetEndian() != native_endian)
1558 std.mem.byteSwapAllFields(std.coff.SectionHeader, section);2074 std.mem.byteSwapAllFields(std.coff.SectionHeader, section);
1559 switch (coff.optionalHeaderPtr()) {2075
1560 inline else => |optional_header| coff.targetStore(2076 if (coff.isImage()) {
1561 &optional_header.size_of_image,2077 switch (coff.optionalHeaderPtr()) {
1562 @intCast(rva + virtual_size),2078 inline else => |optional_header| coff.targetStore(
1563 ),2079 &optional_header.size_of_image,
2080 @intCast(rva + virtual_size),
2081 ),
2082 }
1564 }2083 }
2084
1565 return si;2085 return si;
1566}2086}
15672087
...@@ -1686,6 +2206,16 @@ pub fn addReloc(...@@ -1686,6 +2206,16 @@ pub fn addReloc(
1686 target.target_relocs = ri;2206 target.target_relocs = ri;
1687}2207}
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
1689pub fn prelink(coff: *Coff, prog_node: std.Progress.Node) link.Error!void {2219pub fn prelink(coff: *Coff, prog_node: std.Progress.Node) link.Error!void {
1690 _ = coff;2220 _ = coff;
1691 _ = prog_node;2221 _ = prog_node;
...@@ -1976,6 +2506,11 @@ pub fn flush(...@@ -1976,6 +2506,11 @@ pub fn flush(
1976 _ = prog_node;2506 _ = prog_node;
1977 while (try coff.idle(tid)) {}2507 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
1979 const comp = coff.base.comp;2514 const comp = coff.base.comp;
19802515
1981 // Implib generation should instead be done via building a MappedFile progressively2516 // Implib generation should instead be done via building a MappedFile progressively
...@@ -1985,8 +2520,8 @@ pub fn flush(...@@ -1985,8 +2520,8 @@ pub fn flush(
19852520
1986 // hack for stage2_x86_64 + coff2521 // hack for stage2_x86_64 + coff
1987 if (comp.compiler_rt_dyn_lib) |crt_file| {2522 if (comp.compiler_rt_dyn_lib) |crt_file| {
1988 const gpa = comp.gpa;
1989 const io = comp.io;2523 const io = comp.io;
2524 const gpa = comp.gpa;
19902525
1991 const compiler_rt_sub_path = try std.fs.path.join(gpa, &.{2526 const compiler_rt_sub_path = try std.fs.path.join(gpa, &.{
1992 std.fs.path.dirname(coff.base.emit.sub_path) orelse "",2527 std.fs.path.dirname(coff.base.emit.sub_path) orelse "",
...@@ -2002,6 +2537,14 @@ pub fn flush(...@@ -2002,6 +2537,14 @@ pub fn flush(
2002 .{},2537 .{},
2003 ) catch |err| return comp.link_diags.fail("copy '{s}' failed: {t}", .{ compiler_rt_sub_path, err });2538 ) catch |err| return comp.link_diags.fail("copy '{s}' failed: {t}", .{ compiler_rt_sub_path, err });
2004 }2539 }
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});
2005}2548}
20062549
2007pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {2550pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
...@@ -2080,7 +2623,13 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {...@@ -2080,7 +2623,13 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
2080 break :task;2623 break :task;
2081 } else coff.mf.update_prog_node.completeOne();2624 } else coff.mf.update_prog_node.completeOne();
2082 }2625 }
2626 while (coff.pending_members.pop()) |pending_mi| {
2627 // TODO: Prog node
2628 try coff.flushMember(pending_mi.key);
2629 break :task;
2630 }
2083 if (coff.export_table.pending_sort) {2631 if (coff.export_table.pending_sort) {
2632 // TODO: Prog node
2084 coff.export_table.pending_sort = false;2633 coff.export_table.pending_sort = false;
2085 coff.flushExportsSort();2634 coff.flushExportsSort();
2086 break :task;2635 break :task;
...@@ -2090,6 +2639,7 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {...@@ -2090,6 +2639,7 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
2090 if (coff.globals.count() > coff.global_pending_index) return true;2639 if (coff.globals.count() > coff.global_pending_index) return true;
2091 for (&coff.lazy.values) |lazy| if (lazy.map.count() > lazy.pending_index) return true;2640 for (&coff.lazy.values) |lazy| if (lazy.map.count() > lazy.pending_index) return true;
2092 if (coff.mf.updates.items.len > 0) return true;2641 if (coff.mf.updates.items.len > 0) return true;
2642 if (coff.pending_members.count() > 0) return true;
2093 if (coff.export_table.pending_sort) return true;2643 if (coff.export_table.pending_sort) return true;
2094 return false;2644 return false;
2095}2645}
...@@ -2183,6 +2733,10 @@ fn flushGlobal(coff: *Coff, pt: Zcu.PerThread, gmi: Node.GlobalMapIndex) !void {...@@ -2183,6 +2733,10 @@ fn flushGlobal(coff: *Coff, pt: Zcu.PerThread, gmi: Node.GlobalMapIndex) !void {
2183 const gpa = zcu.gpa;2733 const gpa = zcu.gpa;
2184 const gn = gmi.globalName(coff);2734 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
2186 if (gn.lib_name.toSlice(coff)) |lib_name| {2740 if (gn.lib_name.toSlice(coff)) |lib_name| {
2187 const name = gn.name.toSlice(coff);2741 const name = gn.name.toSlice(coff);
2188 try coff.nodes.ensureUnusedCapacity(gpa, 4);2742 try coff.nodes.ensureUnusedCapacity(gpa, 4);
...@@ -2394,19 +2948,44 @@ fn flushLazy(coff: *Coff, pt: Zcu.PerThread, lmr: Node.LazyMapRef) !void {...@@ -2394,19 +2948,44 @@ fn flushLazy(coff: *Coff, pt: Zcu.PerThread, lmr: Node.LazyMapRef) !void {
2394}2948}
23952949
2396fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void {2950fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void {
2951 log.debug("flushMoved({s})", .{@tagName(coff.getNode(ni))});
2397 switch (coff.getNode(ni)) {2952 switch (coff.getNode(ni)) {
2398 .file,2953 .file,
2399 .header,2954 .header,
2400 .signature,2955 .signature,
2956 => unreachable,
2401 .coff_header,2957 .coff_header,
2402 .optional_header,2958 .optional_header,
2403 .data_directories,2959 .data_directories,
2404 .section_table,2960 .section_table,
2405 => unreachable,2961 .placeholder,
2406 .image_section => |si| return coff.targetStore(2962 => if (!coff.isArchive()) unreachable,
2407 &si.get(coff).section_number.header(coff).pointer_to_raw_data,2963 .archive_member_header => |mi| {
2408 @intCast(ni.fileLocation(&coff.mf, false).offset),2964 const member = mi.get(coff);
2409 ),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 },
2410 .import_directory_table => coff.targetStore(2989 .import_directory_table => coff.targetStore(
2411 &coff.dataDirectoryPtr(.IMPORT).virtual_address,2990 &coff.dataDirectoryPtr(.IMPORT).virtual_address,
2412 coff.computeNodeRva(ni),2991 coff.computeNodeRva(ni),
...@@ -2523,32 +3102,69 @@ fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void {...@@ -2523,32 +3102,69 @@ fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void {
25233102
2524fn flushResized(coff: *Coff, ni: MappedFile.Node.Index) !void {3103fn flushResized(coff: *Coff, ni: MappedFile.Node.Index) !void {
2525 _, const size = ni.location(&coff.mf).resolve(&coff.mf);3104 _, const size = ni.location(&coff.mf).resolve(&coff.mf);
3105 log.debug("flushResized({s}, 0x{x})", .{ @tagName(coff.getNode(ni)), size });
3106
2526 switch (coff.getNode(ni)) {3107 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 },
2528 .header => {3115 .header => {
2529 switch (coff.optionalHeaderPtr()) {3116 if (coff.isImage()) {
2530 inline else => |optional_header| coff.targetStore(3117 switch (coff.optionalHeaderPtr()) {
2531 &optional_header.size_of_headers,3118 inline else => |optional_header| coff.targetStore(
2532 @intCast(size),3119 &optional_header.size_of_headers,
2533 ),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 );
2534 }3132 }
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 );
2543 },3133 },
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,
2545 .section_table => {},3161 .section_table => {},
2546 .image_section => |si| {3162 .image_section => |si| {
2547 const sym = si.get(coff);3163 const sym = si.get(coff);
2548 const section_index = sym.section_number.toIndex();3164 const section_index = sym.section_number.toIndex();
2549 const section = &coff.sectionTableSlice()[section_index];3165 const section = &coff.sectionTableSlice()[section_index];
2550 coff.targetStore(&section.size_of_raw_data, @intCast(size));3166 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)) {
2552 const virtual_size = std.mem.alignForward(3168 const virtual_size = std.mem.alignForward(
2553 u32,3169 u32,
2554 @intCast(size * 4),3170 @intCast(size * 4),
...@@ -2562,16 +3178,87 @@ fn flushResized(coff: *Coff, ni: MappedFile.Node.Index) !void {...@@ -2562,16 +3178,87 @@ fn flushResized(coff: *Coff, ni: MappedFile.Node.Index) !void {
2562 &coff.dataDirectoryPtr(.IMPORT).size,3178 &coff.dataDirectoryPtr(.IMPORT).size,
2563 @intCast(size),3179 @intCast(size),
2564 ),3180 ),
2565 .import_lookup_table, .import_address_table, .import_hint_name_table => {},3181 .import_lookup_table,
2566 .export_directory_table => coff.targetStore(3182 .import_address_table,
2567 &coff.dataDirectoryPtr(.EXPORT).size,3183 .import_hint_name_table,
2568 @intCast(size),3184 => {},
2569 ),3185 .export_directory_table => unreachable,
2570 .export_address_table, .export_name_pointer_table, .export_ordinal_table, .export_name_table => {},3186 .export_address_table,
3187 .export_name_pointer_table,
3188 .export_ordinal_table,
3189 .export_name_table,
3190 => {},
2571 inline .pseudo_section,3191 inline .pseudo_section,
2572 .object_section,3192 .object_section,
2573 => |smi| smi.symbol(coff).get(coff).size = @intCast(size),3193 => |smi, tag| {
2574 .global, .nav, .uav, .lazy_code, .lazy_const_data => {},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 },
2575 }3262 }
2576}3263}
25773264
...@@ -2666,12 +3353,14 @@ fn updateExportsInner(...@@ -2666,12 +3353,14 @@ fn updateExportsInner(
2666 ))),3353 ))),
2667 };3354 };
2668 while (try coff.idle(pt.tid)) {}3355 while (try coff.idle(pt.tid)) {}
3356
2669 const exported_ni = exported_si.node(coff);3357 const exported_ni = exported_si.node(coff);
2670 const exported_sym = exported_si.get(coff);3358 const exported_sym = exported_si.get(coff);
2671 for (export_indices) |export_index| {3359 for (export_indices) |export_index| {
2672 const @"export" = export_index.ptr(zcu);3360 const @"export" = export_index.ptr(zcu);
2673 const name = @"export".opts.name.toSlice(ip);3361 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.*;
2675 const export_sym = export_si.get(coff);3364 const export_sym = export_si.get(coff);
2676 export_sym.ni = exported_ni;3365 export_sym.ni = exported_ni;
2677 export_sym.rva = exported_sym.rva;3366 export_sym.rva = exported_sym.rva;
...@@ -2687,6 +3376,13 @@ fn updateExportsInner(...@@ -2687,6 +3376,13 @@ fn updateExportsInner(
2687 std.mem.byteSwapAllFields(std.coff.ImageDataDirectory, tls_directory);3376 std.mem.byteSwapAllFields(std.coff.ImageDataDirectory, tls_directory);
2688 }3377 }
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
2690 if (coff.export_table.ni == .none) continue;3386 if (coff.export_table.ni == .none) continue;
26913387
2692 const entries_ctx = ExportTable.Adapter{ .coff = coff };3388 const entries_ctx = ExportTable.Adapter{ .coff = coff };
...@@ -2703,7 +3399,7 @@ fn updateExportsInner(...@@ -2703,7 +3399,7 @@ fn updateExportsInner(
2703 if (export_count > std.math.maxInt(@FieldType(std.coff.ExportDirectoryTable, "number_of_entries")))3399 if (export_count > std.math.maxInt(@FieldType(std.coff.ExportDirectoryTable, "number_of_entries")))
2704 return coff.base.comp.link_diags.fail("exceeded maximum number of exports", .{});3400 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];
2707 const new_name_table_size = name_index + name.len + 1;3403 const new_name_table_size = name_index + name.len + 1;
2708 if (new_name_table_size > std.math.maxInt(@FieldType(ExportTable.Entry, "name_index")))3404 if (new_name_table_size > std.math.maxInt(@FieldType(ExportTable.Entry, "name_index")))
2709 return coff.base.comp.link_diags.fail("exports name table limit reached", .{});3405 return coff.base.comp.link_diags.fail("exports name table limit reached", .{});
...@@ -2714,19 +3410,23 @@ fn updateExportsInner(...@@ -2714,19 +3410,23 @@ fn updateExportsInner(
2714 @memcpy(name_table_slice[name_index..][0 .. name.len + 1], name[0 .. name.len + 1]);3410 @memcpy(name_table_slice[name_index..][0 .. name.len + 1], name[0 .. name.len + 1]);
27153411
2716 // If the new name sorts after the current tail of the sorted list, we don't need to re-sort3412 // 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();3413 {
2718 if (ordinal_table_slice.len > 0 and !coff.export_table.pending_sort) {3414 const ordinal_table_slice = coff.exportOrdinalTableSlice();
2719 const tail_index: ExportTable.Ordinal =3415 if (ordinal_table_slice.len > 0 and !coff.export_table.pending_sort) {
2720 @enumFromInt(ordinal_table_slice[ordinal_table_slice.len - 1].unbiased_ordinal);3416 const tail_index: ExportTable.Ordinal =
2721 const tail_entry = tail_index.get(coff);3417 @enumFromInt(ordinal_table_slice[ordinal_table_slice.len - 1].unbiased_ordinal);
2722 const tail_name = name_table_slice[tail_entry.name_index..][0..tail_entry.name_len];3418 const tail_entry = tail_index.get(coff);
2723 coff.export_table.pending_sort = std.mem.lessThan(u8, name, tail_name);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 }
2724 }3422 }
27253423
2726 const edt = coff.exportDirectoryTable();3424 const edt = coff.exportDirectoryTable();
2727 coff.targetStore(&edt.number_of_names, @intCast(export_count));3425 coff.targetStore(&edt.number_of_names, @intCast(export_count));
2728 edt.number_of_entries = edt.number_of_names;3426 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
2730 try coff.export_table.export_address_table_si.node(coff).resize(3430 try coff.export_table.export_address_table_si.node(coff).resize(
2731 &coff.mf,3431 &coff.mf,
2732 gpa,3432 gpa,
...@@ -2783,22 +3483,24 @@ pub fn deleteExport(coff: *Coff, exported: Zcu.Exported, name: InternPool.NullTe...@@ -2783,22 +3483,24 @@ pub fn deleteExport(coff: *Coff, exported: Zcu.Exported, name: InternPool.NullTe
2783 _ = name;3483 _ = name;
2784}3484}
27853485
2786pub fn dump(coff: *Coff, tid: Zcu.PerThread.Id) Io.Cancelable!void {3486fn dumpStderr(coff: *Coff, tid: Zcu.PerThread.Id) !void {
2787 const comp = coff.base.comp;3487 const comp = coff.base.comp;
2788 const io = comp.io;3488 const io = comp.io;
2789 var buffer: [512]u8 = undefined;3489 var buffer: [512]u8 = undefined;
2790 const stderr = try io.lockStderr(&buffer, null);3490 const stderr = try io.lockStderr(&buffer, null);
2791 defer io.unlockStderr();3491 defer io.unlockStderr();
2792 const w = &stderr.file_writer.interface;3492 const w = &stderr.file_writer.interface;
2793 coff.printNode(tid, w, .root, 0) catch |err| switch (err) {3493 try coff.dump(w, tid);
2794 error.WriteFailed => return stderr.err.?,3494}
2795 };3495
3496pub fn dump(coff: *Coff, w: *Io.Writer, tid: Zcu.PerThread.Id) !void {
3497 try coff.printNode(tid, w, .root, 0);
2796}3498}
27973499
2798pub fn printNode(3500pub fn printNode(
2799 coff: *Coff,3501 coff: *Coff,
2800 tid: Zcu.PerThread.Id,3502 tid: Zcu.PerThread.Id,
2801 w: *std.Io.Writer,3503 w: *Io.Writer,
2802 ni: MappedFile.Node.Index,3504 ni: MappedFile.Node.Index,
2803 indent: usize,3505 indent: usize,
2804) !void {3506) !void {
...@@ -2830,7 +3532,7 @@ pub fn printNode(...@@ -2830,7 +3532,7 @@ pub fn printNode(
2830 const ip = &zcu.intern_pool;3532 const ip = &zcu.intern_pool;
2831 const nav = ip.getNav(nmi.navIndex(coff));3533 const nav = ip.getNav(nmi.navIndex(coff));
2832 try w.print("({f}, {f})", .{3534 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 }),
2834 nav.fqn.fmt(ip),3536 nav.fqn.fmt(ip),
2835 });3537 });
2836 },3538 },
...@@ -2876,7 +3578,7 @@ pub fn printNode(...@@ -2876,7 +3578,7 @@ pub fn printNode(
2876 const line_len = 0x10;3578 const line_len = 0x10;
2877 var line_it = std.mem.window(3579 var line_it = std.mem.window(
2878 u8,3580 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)],
2880 line_len,3582 line_len,
2881 line_len,3583 line_len,
2882 );3584 );
src/link/Elf2.zig+3-11
...@@ -6711,16 +6711,8 @@ pub fn deleteExport(elf: *Elf, exported: Zcu.Exported, name: InternPool.NullTerm...@@ -6711,16 +6711,8 @@ pub fn deleteExport(elf: *Elf, exported: Zcu.Exported, name: InternPool.NullTerm
6711 _ = name;6711 _ = name;
6712}6712}
67136713
6714pub fn dump(elf: *Elf, tid: Zcu.PerThread.Id) Io.Cancelable!void {6714pub fn dump(elf: *Elf, w: *Io.Writer, tid: Zcu.PerThread.Id) !void {
6715 const comp = elf.base.comp;6715 return elf.printNode(tid, w, .root, 0);
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 };
6724}6716}
67256717
6726pub fn printNode(6718pub fn printNode(
...@@ -6770,7 +6762,7 @@ pub fn printNode(...@@ -6770,7 +6762,7 @@ pub fn printNode(
6770 const ip = &zcu.intern_pool;6762 const ip = &zcu.intern_pool;
6771 const nav = ip.getNav(nmi.navIndex(elf));6763 const nav = ip.getNav(nmi.navIndex(elf));
6772 try w.print("({f}, {f})", .{6764 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 }),
6774 nav.fqn.fmt(ip),6766 nav.fqn.fmt(ip),
6775 });6767 });
6776 },6768 },
src/link/MappedFile.zig+7
...@@ -188,6 +188,10 @@ pub const Node = extern struct {...@@ -188,6 +188,10 @@ pub const Node = extern struct {
188 return ni.get(mf).parent;188 return ni.get(mf).parent;
189 }189 }
190190
191 pub fn next(ni: Node.Index, mf: *const MappedFile) Node.Index {
192 return ni.get(mf).next;
193 }
194
191 pub fn ChildIterator(comptime direction: enum { prev, next }) type {195 pub fn ChildIterator(comptime direction: enum { prev, next }) type {
192 return struct {196 return struct {
193 mf: *const MappedFile,197 mf: *const MappedFile,
...@@ -834,6 +838,7 @@ fn resizeNode(mf: *MappedFile, gpa: std.mem.Allocator, ni: Node.Index, requested...@@ -834,6 +838,7 @@ fn resizeNode(mf: *MappedFile, gpa: std.mem.Allocator, ni: Node.Index, requested
834 shift = first_floating.flags.alignment.forward(@intCast(838 shift = first_floating.flags.alignment.forward(@intCast(
835 @max(shift, first_floating_size),839 @max(shift, first_floating_size),
836 ));840 ));
841
837 // Not enough space, try the next node842 // Not enough space, try the next node
838 last_fixed_ni = first_floating_ni;843 last_fixed_ni = first_floating_ni;
839 first_floating_ni = first_floating.next;844 first_floating_ni = first_floating.next;
...@@ -1135,6 +1140,8 @@ fn ensureTotalCapacityPreciseInner(mf: *MappedFile, new_capacity: usize) (Alloca...@@ -1135,6 +1140,8 @@ fn ensureTotalCapacityPreciseInner(mf: *MappedFile, new_capacity: usize) (Alloca
1135 error.OperationUnsupported => {},1140 error.OperationUnsupported => {},
1136 else => |e| return e,1141 else => |e| return e,
1137 }1142 }
1143
1144 try mf.memory_map.write(io);
1138 unmap(mf);1145 unmap(mf);
1139 }1146 }
11401147
test/standalone/shared_library/build.zig+11-1
...@@ -13,7 +13,14 @@ pub fn build(b: *std.Build) void {...@@ -13,7 +13,14 @@ pub fn build(b: *std.Build) void {
13 const lib_use_llvm: []const bool = &.{ true, true, false, false };13 const lib_use_llvm: []const bool = &.{ true, true, false, false };
1414
15 for (exe_names, lib_names, lib_link_libc, lib_use_llvm) |exe_name, lib_name, dyn_libc, use_llvm| {15 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)
17 continue; // TODO: sub-compilation of compiler_rt failed (failed to link with LLD: LibCInstallationNotAvailable)24 continue; // TODO: sub-compilation of compiler_rt failed (failed to link with LLD: LibCInstallationNotAvailable)
1825
19 const lib = b.addLibrary(.{26 const lib = b.addLibrary(.{
...@@ -44,6 +51,9 @@ pub fn build(b: *std.Build) void {...@@ -44,6 +51,9 @@ pub fn build(b: *std.Build) void {
44 });51 });
45 exe.root_module.linkLibrary(lib);52 exe.root_module.linkLibrary(lib);
4653
54 b.getInstallStep().dependOn(&b.addInstallArtifact(lib, .{}).step);
55 b.getInstallStep().dependOn(&b.addInstallArtifact(exe, .{}).step);
56
47 const run_cmd = b.addRunArtifact(exe);57 const run_cmd = b.addRunArtifact(exe);
48 test_step.dependOn(&run_cmd.step);58 test_step.dependOn(&run_cmd.step);
49 }59 }