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:42-04:00
log3ee12f3a2aeab1aff5852466cf0c654222e034cc
treed83bb2a05aeb0e32645e3e182a55cc62cd072675
parentcef6ea05d982589d29bc312b43bde1432ea78623

Coff: linking against archives is now functional

- Fixup not aligning section header start when reading archives - Load archive members on-deman as symbols from them are referenced - Sort symbols by section before building inputs_resolved so that ranges are contiguous

1 files changed, 182 insertions(+), 81 deletions(-)

src/link/Coff.zig+182-81
...@@ -38,13 +38,10 @@ input_archive_symbol_indices: std.AutoArrayHashMapUnmanaged(String, struct {...@@ -38,13 +38,10 @@ input_archive_symbol_indices: std.AutoArrayHashMapUnmanaged(String, struct {
38 first: InputArchive.Member.Symbol.Index,38 first: InputArchive.Member.Symbol.Index,
39 last: InputArchive.Member.Symbol.Index,39 last: InputArchive.Member.Symbol.Index,
40}),40}),
41pending_input: ?InputArchive.Member.Index,
41inputs: std.ArrayList(Input),42inputs: std.ArrayList(Input),
42input_resolved: std.ArrayList(Symbol.Index),43input_resolved: std.ArrayList(Symbol.Index),
43input_sections: std.ArrayList(struct {44input_sections: std.ArrayList(Node.InputSection),
44 ii: Node.InputIndex,
45 si: Symbol.Index,
46 file_location: MappedFile.Node.FileLocation,
47}),
48input_section_pending_index: u32,45input_section_pending_index: u32,
49strings: std.HashMapUnmanaged(46strings: std.HashMapUnmanaged(
50 u32,47 u32,
...@@ -203,7 +200,7 @@ pub const Node = union(enum) {...@@ -203,7 +200,7 @@ pub const Node = union(enum) {
203200
204 pseudo_section: PseudoSectionMapIndex,201 pseudo_section: PseudoSectionMapIndex,
205 object_section: ObjectSectionMapIndex,202 object_section: ObjectSectionMapIndex,
206 input_section: InputSectionIndex,203 input_section: InputSection.Index,
207 global: GlobalMapIndex,204 global: GlobalMapIndex,
208 nav: NavMapIndex,205 nav: NavMapIndex,
209 uav: UavMapIndex,206 uav: UavMapIndex,
...@@ -292,16 +289,16 @@ pub const Node = union(enum) {...@@ -292,16 +289,16 @@ pub const Node = union(enum) {
292 return coff.inputs.items[@intFromEnum(ii)].path;289 return coff.inputs.items[@intFromEnum(ii)].path;
293 }290 }
294291
295 pub fn archiveName(ii: InputIndex, coff: *const Coff) ?[]const u8 {292 pub fn memberName(ii: InputIndex, coff: *const Coff) ?[]const u8 {
296 return coff.inputs.items[@intFromEnum(ii)].archive_name;293 return coff.inputs.items[@intFromEnum(ii)].member_name;
297 }294 }
298295
299 pub fn firstSymbol(ii: InputIndex, coff: *const Coff) Symbol.Index {296 pub fn firstSymbol(ii: InputIndex, coff: *const Coff) Symbol.Index {
300 return coff.inputs.items[@intFromEnum(ii)].first_si;297 return coff.inputs.items[@intFromEnum(ii)].first_si;
301 }298 }
302299
303 pub fn lastSymbol(ii: InputIndex, coff: *const Coff) Symbol.Index {300 pub fn endSymbol(ii: InputIndex, coff: *const Coff) Symbol.Index {
304 return coff.inputs.items[@intFromEnum(ii)].last_si;301 return coff.inputs.items[@intFromEnum(ii)].end_si;
305 }302 }
306303
307 pub fn firstResolvedGlobal(ii: InputIndex, coff: *const Coff) Input.ResolvedIndex {304 pub fn firstResolvedGlobal(ii: InputIndex, coff: *const Coff) Input.ResolvedIndex {
...@@ -309,24 +306,39 @@ pub const Node = union(enum) {...@@ -309,24 +306,39 @@ pub const Node = union(enum) {
309 }306 }
310 };307 };
311308
312 pub const InputSectionIndex = enum(u32) {309 const InputSection = struct {
313 _,310 ii: Node.InputIndex,
311 si: Symbol.Index,
312 file_location: MappedFile.Node.FileLocation,
313 first_iri: Node.InputSection.ResolvedIndex,
314314
315 pub fn input(isi: InputSectionIndex, coff: *const Coff) InputIndex {315 pub const Index = enum(u32) {
316 return coff.input_sections.items[@intFromEnum(isi)].ii;316 _,
317 }
318317
319 pub fn fileLocation(isi: InputSectionIndex, coff: *const Coff) MappedFile.Node.FileLocation {318 pub fn inputSection(isi: Index, coff: *const Coff) *InputSection {
320 return coff.input_sections.items[@intFromEnum(isi)].file_location;319 return &coff.input_sections.items[@intFromEnum(isi)];
321 }320 }
322321
323 pub fn symbol(isi: InputSectionIndex, coff: *const Coff) Symbol.Index {322 pub fn input(isi: Index, coff: *const Coff) InputIndex {
324 return coff.input_sections.items[@intFromEnum(isi)].si;323 return coff.input_sections.items[@intFromEnum(isi)].ii;
325 }324 }
326325
327 pub fn lastSymbol(isi: InputSectionIndex, coff: *const Coff) Symbol.Index {326 pub fn fileLocation(isi: Index, coff: *const Coff) MappedFile.Node.FileLocation {
328 return coff.input_sections.items[@intFromEnum(isi)].last_si;327 return coff.input_sections.items[@intFromEnum(isi)].file_location;
329 }328 }
329
330 pub fn symbol(isi: Index, coff: *const Coff) Symbol.Index {
331 return coff.input_sections.items[@intFromEnum(isi)].si;
332 }
333
334 pub fn firstResolvedSymbol(isi: Index, coff: *const Coff) ResolvedIndex {
335 return coff.input_sections.items[@intFromEnum(isi)].first_iri;
336 }
337 };
338
339 const ResolvedIndex = enum(u32) {
340 _,
341 };
330 };342 };
331343
332 pub const LazyMapRef = struct {344 pub const LazyMapRef = struct {
...@@ -398,6 +410,10 @@ pub const InputArchive = struct {...@@ -398,6 +410,10 @@ pub const InputArchive = struct {
398410
399 const Index = enum(u32) {411 const Index = enum(u32) {
400 _,412 _,
413
414 pub fn path(iai: InputArchive.Index, coff: *Coff) std.Build.Cache.Path {
415 return coff.input_archives.items[@intFromEnum(iai)].path;
416 }
401 };417 };
402418
403 pub const Member = struct {419 pub const Member = struct {
...@@ -405,10 +421,17 @@ pub const InputArchive = struct {...@@ -405,10 +421,17 @@ pub const InputArchive = struct {
405 name: String,421 name: String,
406 // This range includes the member header422 // This range includes the member header
407 file_location: MappedFile.Node.FileLocation,423 file_location: MappedFile.Node.FileLocation,
408 is_import: bool,424 flags: packed struct {
409 // TODO: Field indicating we loaded it already (ii)425 is_import: bool,
426 is_loaded: bool,
427 },
428
410 const Index = enum(u32) {429 const Index = enum(u32) {
411 _,430 _,
431
432 pub fn member(iami: InputArchive.Member.Index, coff: *Coff) *InputArchive.Member {
433 return &coff.input_archive_members.items[@intFromEnum(iami)];
434 }
412 };435 };
413436
414 pub const Symbol = struct {437 pub const Symbol = struct {
...@@ -425,14 +448,9 @@ pub const InputArchive = struct {...@@ -425,14 +448,9 @@ pub const InputArchive = struct {
425448
426pub const Input = struct {449pub const Input = struct {
427 path: std.Build.Cache.Path,450 path: std.Build.Cache.Path,
428 archive_name: ?[]const u8,451 member_name: ?[]const u8,
429 first_si: Symbol.Index,452 first_si: Symbol.Index,
430 last_si: Symbol.Index,453 end_si: Symbol.Index,
431 first_iri: ResolvedIndex,
432
433 const ResolvedIndex = enum(u32) {
434 _,
435 };
436};454};
437455
438pub const Member = struct {456pub const Member = struct {
...@@ -1336,6 +1354,7 @@ fn create(...@@ -1336,6 +1354,7 @@ fn create(
1336 .input_archive_members = .empty,1354 .input_archive_members = .empty,
1337 .input_archive_symbols = .empty,1355 .input_archive_symbols = .empty,
1338 .input_archive_symbol_indices = .empty,1356 .input_archive_symbol_indices = .empty,
1357 .pending_input = null,
1339 .inputs = .empty,1358 .inputs = .empty,
1340 .input_resolved = .empty,1359 .input_resolved = .empty,
1341 .input_sections = .empty,1360 .input_sections = .empty,
...@@ -2315,6 +2334,8 @@ fn getOrPutGlobalSymbol(...@@ -2315,6 +2334,8 @@ fn getOrPutGlobalSymbol(
2315 si.get(coff).gmi = .wrap(@intCast(sym_gop.index));2334 si.get(coff).gmi = .wrap(@intCast(sym_gop.index));
2316 sym_gop.value_ptr.* = si;2335 sym_gop.value_ptr.* = si;
2317 coff.synth_prog_node.increaseEstimatedTotalItems(1);2336 coff.synth_prog_node.increaseEstimatedTotalItems(1);
2337
2338 log.debug("globalSymbol({s}, {?s}) = {d}", .{ opts.name, opts.lib_name, si });
2318 }2339 }
23192340
2320 return sym_gop;2341 return sym_gop;
...@@ -2724,7 +2745,28 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void...@@ -2724,7 +2745,28 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void
2724 log.debug("updateSymbolTableEntry({d}) = {d}", .{ si, sym.sti });2745 log.debug("updateSymbolTableEntry({d}) = {d}", .{ si, sym.sti });
2725}2746}
27262747
2727fn flushInputSection(coff: *Coff, isi: Node.InputSectionIndex) !void {2748fn flushInputMember(coff: *Coff, iami: InputArchive.Member.Index) !void {
2749 const member = iami.member(coff);
2750 if (member.file_location.size == 0) return;
2751 assert(!member.flags.is_loaded);
2752 defer member.flags.is_loaded = true;
2753 const comp = coff.base.comp;
2754 const io = comp.io;
2755 const path = member.iai.path(coff);
2756 const file = try path.root_dir.handle.openFile(io, path.sub_path, .{});
2757 defer file.close(io);
2758 var buffer: [4096]u8 = undefined;
2759 var fr = file.reader(io, &buffer);
2760 const offset = member.file_location.offset + @sizeOf(std.coff.ArchiveMemberHeader);
2761 try fr.seekTo(offset);
2762 log.debug("flushInputMember({f}({s}))", .{ path, member.name.toSlice(coff) });
2763 try coff.loadObject(path, member.name.toSlice(coff), &fr, .{
2764 .offset = offset,
2765 .size = member.file_location.size,
2766 });
2767}
2768
2769fn flushInputSection(coff: *Coff, isi: Node.InputSection.Index) !void {
2728 const file_loc = isi.fileLocation(coff);2770 const file_loc = isi.fileLocation(coff);
2729 if (file_loc.size == 0) return;2771 if (file_loc.size == 0) return;
2730 const comp = coff.base.comp;2772 const comp = coff.base.comp;
...@@ -2740,6 +2782,11 @@ fn flushInputSection(coff: *Coff, isi: Node.InputSectionIndex) !void {...@@ -2740,6 +2782,11 @@ fn flushInputSection(coff: *Coff, isi: Node.InputSectionIndex) !void {
2740 const si = isi.symbol(coff);2782 const si = isi.symbol(coff);
2741 si.node(coff).writer(&coff.mf, gpa, &nw);2783 si.node(coff).writer(&coff.mf, gpa, &nw);
2742 defer nw.deinit();2784 defer nw.deinit();
2785 log.debug("flushInputSection({f}{f}, {s})", .{
2786 path,
2787 fmtMemberNameString(ii.memberName(coff)),
2788 isi.symbol(coff).get(coff).section_number.name(coff).toSlice(coff),
2789 });
2743 if (try nw.interface.sendFileAll(&fr, .limited(@intCast(file_loc.size))) != file_loc.size)2790 if (try nw.interface.sendFileAll(&fr, .limited(@intCast(file_loc.size))) != file_loc.size)
2744 return error.EndOfStream;2791 return error.EndOfStream;
2745 si.applyLocationRelocs(coff);2792 si.applyLocationRelocs(coff);
...@@ -3180,12 +3227,12 @@ pub fn loadInput(coff: *Coff, input: link.Input) (Io.File.Reader.SizeError ||...@@ -3180,12 +3227,12 @@ pub fn loadInput(coff: *Coff, input: link.Input) (Io.File.Reader.SizeError ||
3180 }3227 }
3181}3228}
31823229
3183fn fmtArchiveNameString(archiveName: ?[]const u8) std.fmt.Alt(?[]const u8, archiveNameStringEscape) {3230fn fmtMemberNameString(memberName: ?[]const u8) std.fmt.Alt(?[]const u8, memberNameStringEscape) {
3184 return .{ .data = archiveName };3231 return .{ .data = memberName };
3185}3232}
31863233
3187fn archiveNameStringEscape(archiveName: ?[]const u8, w: *std.Io.Writer) std.Io.Writer.Error!void {3234fn memberNameStringEscape(memberName: ?[]const u8, w: *std.Io.Writer) std.Io.Writer.Error!void {
3188 try w.print("({f})", .{std.zig.fmtString(archiveName orelse return)});3235 try w.print("({f})", .{std.zig.fmtString(memberName orelse return)});
3189}3236}
31903237
3191fn inputSectionHeaderNameSlice(3238fn inputSectionHeaderNameSlice(
...@@ -3214,7 +3261,7 @@ fn inputSectionHeaderNameSlice(...@@ -3214,7 +3261,7 @@ fn inputSectionHeaderNameSlice(
3214fn loadObject(3261fn loadObject(
3215 coff: *Coff,3262 coff: *Coff,
3216 path: std.Build.Cache.Path,3263 path: std.Build.Cache.Path,
3217 archive_name: ?[]const u8,3264 member_name: ?[]const u8,
3218 fr: *Io.File.Reader,3265 fr: *Io.File.Reader,
3219 fl: MappedFile.Node.FileLocation,3266 fl: MappedFile.Node.FileLocation,
3220) !void {3267) !void {
...@@ -3227,7 +3274,7 @@ fn loadObject(...@@ -3227,7 +3274,7 @@ fn loadObject(
3227 const is_archive = coff.isArchive();3274 const is_archive = coff.isArchive();
3228 assert(!coff.isObj());3275 assert(!coff.isObj());
32293276
3230 log.debug("loadObject({f}{f})", .{ path.fmtEscapeString(), fmtArchiveNameString(archive_name) });3277 log.debug("loadObject({f}{f})", .{ path.fmtEscapeString(), fmtMemberNameString(member_name) });
32313278
3232 const header = try r.peekStruct(std.coff.Header, coff.targetEndian());3279 const header = try r.peekStruct(std.coff.Header, coff.targetEndian());
3233 if (header.machine != target.toCoffMachine())3280 if (header.machine != target.toCoffMachine())
...@@ -3271,10 +3318,9 @@ fn loadObject(...@@ -3271,10 +3318,9 @@ fn loadObject(
3271 const input = coff.inputs.addOneAssumeCapacity();3318 const input = coff.inputs.addOneAssumeCapacity();
3272 input.* = .{3319 input.* = .{
3273 .path = path,3320 .path = path,
3274 .archive_name = if (archive_name) |m| try gpa.dupe(u8, m) else null,3321 .member_name = if (member_name) |m| try gpa.dupe(u8, m) else null,
3275 .first_si = .null,3322 .first_si = @enumFromInt(coff.symbols.items.len),
3276 .last_si = .null,3323 .end_si = @enumFromInt(coff.symbols.items.len),
3277 .first_iri = @enumFromInt(coff.input_resolved.items.len),
3278 };3324 };
32793325
3280 const string_table = string_table: {3326 const string_table = string_table: {
...@@ -3455,6 +3501,7 @@ fn loadObject(...@@ -3455,6 +3501,7 @@ fn loadObject(
3455 .offset = fl.offset + section.header.pointer_to_raw_data,3501 .offset = fl.offset + section.header.pointer_to_raw_data,
3456 .size = section.header.size_of_raw_data,3502 .size = section.header.size_of_raw_data,
3457 },3503 },
3504 .first_iri = @enumFromInt(coff.input_resolved.items.len),
3458 };3505 };
34593506
3460 log.debug("loadInputSection({s}) = {d}@{d}", .{ section.name.toSlice(coff), section.si, sym.section_number });3507 log.debug("loadInputSection({s}) = {d}@{d}", .{ section.name.toSlice(coff), section.si, sym.section_number });
...@@ -3497,8 +3544,11 @@ fn loadObject(...@@ -3497,8 +3544,11 @@ fn loadObject(
34973544
3498 var symbols: std.ArrayList(Symbol.Index) = .empty;3545 var symbols: std.ArrayList(Symbol.Index) = .empty;
3499 try symbols.ensureUnusedCapacity(gpa, header.number_of_symbols);3546 try symbols.ensureUnusedCapacity(gpa, header.number_of_symbols);
3547 var num_resolved: u32 = 0;
3548
3549 input.first_si = @enumFromInt(coff.symbols.items.len);
3550 defer input.end_si = @enumFromInt(coff.symbols.items.len);
35003551
3501 const first_si = coff.symbols.items.len;
3502 var symbol_i: u32 = 0;3552 var symbol_i: u32 = 0;
3503 while (symbol_i < header.number_of_symbols) {3553 while (symbol_i < header.number_of_symbols) {
3504 var symbol: std.coff.Symbol = undefined;3554 var symbol: std.coff.Symbol = undefined;
...@@ -3619,6 +3669,7 @@ fn loadObject(...@@ -3619,6 +3669,7 @@ fn loadObject(
3619 .{name},3669 .{name},
3620 ),3670 ),
3621 else => |sn| {3671 else => |sn| {
3672 // TODO: Should this use archive name as lib_name as well?
3622 const global_gop = try coff.getOrPutGlobalSymbol(.{ .name = name });3673 const global_gop = try coff.getOrPutGlobalSymbol(.{ .name = name });
3623 si_slice[0] = global_gop.value_ptr.*;3674 si_slice[0] = global_gop.value_ptr.*;
3624 const sym = si_slice[0].get(coff);3675 const sym = si_slice[0].get(coff);
...@@ -3631,7 +3682,7 @@ fn loadObject(...@@ -3631,7 +3682,7 @@ fn loadObject(
3631 const other_ii = isi.input(coff);3682 const other_ii = isi.input(coff);
3632 err.addNote("first seen in input '{f}{f}'", .{3683 err.addNote("first seen in input '{f}{f}'", .{
3633 other_ii.path(coff).fmtEscapeString(),3684 other_ii.path(coff).fmtEscapeString(),
3634 fmtArchiveNameString(other_ii.archiveName(coff)),3685 fmtMemberNameString(other_ii.memberName(coff)),
3635 });3686 });
3636 },3687 },
3637 .nav, .uav => err.addNote("first seen in module '{s}'", .{3688 .nav, .uav => err.addNote("first seen in module '{s}'", .{
...@@ -3643,12 +3694,8 @@ fn loadObject(...@@ -3643,12 +3694,8 @@ fn loadObject(
3643 return error.LinkFailure;3694 return error.LinkFailure;
3644 }3695 }
36453696
3646 if (global_gop.found_existing) {3697 if (global_gop.found_existing)
3647 // `input_resolved` allows visting this symbol in this input section's flushMoved,3698 num_resolved += 1;
3648 // as the previously undefined global created earlier will not be in our
3649 // contiguous first / last range.
3650 (try coff.input_resolved.addOne(gpa)).* = si_slice[0];
3651 }
36523699
3653 coff.initInputSectionSymbol(sym, sections[@intCast(@intFromEnum(sn) - 1)].si, symbol.value);3700 coff.initInputSectionSymbol(sym, sections[@intCast(@intFromEnum(sn) - 1)].si, symbol.value);
3654 },3701 },
...@@ -3657,11 +3704,6 @@ fn loadObject(...@@ -3657,11 +3704,6 @@ fn loadObject(
3657 }3704 }
3658 }3705 }
36593706
3660 if (coff.symbols.items.len > first_si) {
3661 input.first_si = @enumFromInt(first_si);
3662 input.last_si = @enumFromInt(coff.symbols.items.len - 1);
3663 }
3664
3665 const relocation_size = std.coff.Relocation.sizeOf();3707 const relocation_size = std.coff.Relocation.sizeOf();
3666 for (sections) |section| {3708 for (sections) |section| {
3667 if (section.si == .null) continue;3709 if (section.si == .null) continue;
...@@ -3697,6 +3739,38 @@ fn loadObject(...@@ -3697,6 +3739,38 @@ fn loadObject(
3697 );3739 );
3698 }3740 }
3699 }3741 }
3742
3743 const symbolLessThan = struct {
3744 fn lessThan(ctx: *Coff, lhs: Symbol.Index, rhs: Symbol.Index) bool {
3745 const lhs_sn = @intFromEnum(if (lhs == .null) .UNDEFINED else lhs.get(ctx).section_number);
3746 const rhs_sn = @intFromEnum(if (rhs == .null) .UNDEFINED else rhs.get(ctx).section_number);
3747 if (lhs_sn == rhs_sn) return @intFromEnum(lhs) < @intFromEnum(rhs);
3748 return lhs_sn < rhs_sn;
3749 }
3750 }.lessThan;
3751
3752 std.mem.sortUnstable(Symbol.Index, symbols.items, coff, symbolLessThan);
3753
3754 // Any symbols that we resolved (used to be undefined but are now defined) in this pass need to be
3755 // added to contigous ranges in `input_resolved` so they can be visited in `flushMoved`, as they
3756 // are not part of the contiguous ii.first_si / ii.last_si range.
3757 //
3758 // TODO: Should we just use this array for all symbols in this input? More memory but less get().ni misses in flushMoved
3759 try coff.input_resolved.ensureUnusedCapacity(gpa, num_resolved);
3760 var prev_isi: ?Node.InputSection.Index = null;
3761 for (symbols.items) |si| {
3762 if (si == .null or @intFromEnum(si) >= @intFromEnum(input.end_si)) continue;
3763 const ni = si.get(coff).ni;
3764 if (ni == .none) continue;
3765
3766 const isi = coff.getNode(ni).input_section;
3767 if (prev_isi != isi) {
3768 isi.inputSection(coff).first_iri = @enumFromInt(coff.input_resolved.items.len);
3769 prev_isi = isi;
3770 }
3771
3772 coff.input_resolved.addOneAssumeCapacity().* = si;
3773 }
3700}3774}
37013775
3702const ArchiveMemberHeader = struct {3776const ArchiveMemberHeader = struct {
...@@ -3813,6 +3887,7 @@ fn loadArchive(coff: *Coff, path: std.Build.Cache.Path, fr: *Io.File.Reader) !vo...@@ -3813,6 +3887,7 @@ fn loadArchive(coff: *Coff, path: std.Build.Cache.Path, fr: *Io.File.Reader) !vo
3813 var pos = fr.logicalPos();3887 var pos = fr.logicalPos();
3814 const size = try fr.getSize();3888 const size = try fr.getSize();
3815 while (pos < size) : (pos = fr.logicalPos()) {3889 while (pos < size) : (pos = fr.logicalPos()) {
3890 if ((pos & 1) != 0) r.toss(1);
3816 const header = try r.takeStruct(std.coff.ArchiveMemberHeader, target_endian);3891 const header = try r.takeStruct(std.coff.ArchiveMemberHeader, target_endian);
3817 const res = try parseArchiveMemberHeader(diags, path, &header, opt_longnames);3892 const res = try parseArchiveMemberHeader(diags, path, &header, opt_longnames);
38183893
...@@ -3891,7 +3966,7 @@ fn loadArchive(coff: *Coff, path: std.Build.Cache.Path, fr: *Io.File.Reader) !vo...@@ -3891,7 +3966,7 @@ fn loadArchive(coff: *Coff, path: std.Build.Cache.Path, fr: *Io.File.Reader) !vo
3891 coff.input_archive_members.addOneAssumeCapacity().* = .{3966 coff.input_archive_members.addOneAssumeCapacity().* = .{
3892 .iai = iai,3967 .iai = iai,
3893 .name = undefined,3968 .name = undefined,
3894 .is_import = undefined,3969 .flags = undefined,
3895 .file_location = .{3970 .file_location = .{
3896 .offset = member_offset,3971 .offset = member_offset,
3897 .size = undefined,3972 .size = undefined,
...@@ -3952,7 +4027,10 @@ fn loadArchive(coff: *Coff, path: std.Build.Cache.Path, fr: *Io.File.Reader) !vo...@@ -3952,7 +4027,10 @@ fn loadArchive(coff: *Coff, path: std.Build.Cache.Path, fr: *Io.File.Reader) !vo
3952 const member_sig = try r.peek(4);4027 const member_sig = try r.peek(4);
3953 const machine = std.mem.readInt(u16, member_sig[0..2], target_endian);4028 const machine = std.mem.readInt(u16, member_sig[0..2], target_endian);
3954 const sig = std.mem.readInt(u16, member_sig[2..4], target_endian);4029 const sig = std.mem.readInt(u16, member_sig[2..4], target_endian);
3955 member.is_import = machine == @intFromEnum(std.coff.IMAGE.FILE.MACHINE.UNKNOWN) and sig == 0xffff;4030 member.flags = .{
4031 .is_import = machine == @intFromEnum(std.coff.IMAGE.FILE.MACHINE.UNKNOWN) and sig == 0xffff,
4032 .is_loaded = false,
4033 };
3956 member.file_location.size = res.size;4034 member.file_location.size = res.size;
39574035
3958 log.debug("verifyArchiveMember({s}) = 0x{x}+{x}", .{4036 log.debug("verifyArchiveMember({s}) = 0x{x}+{x}", .{
...@@ -3961,7 +4039,7 @@ fn loadArchive(coff: *Coff, path: std.Build.Cache.Path, fr: *Io.File.Reader) !vo...@@ -3961,7 +4039,7 @@ fn loadArchive(coff: *Coff, path: std.Build.Cache.Path, fr: *Io.File.Reader) !vo
3961 member.file_location.size,4039 member.file_location.size,
3962 });4040 });
39634041
3964 if (member.is_import) {4042 if (member.flags.is_import) {
3965 const import_header = try r.peekStruct(std.coff.ImportHeader, target_endian);4043 const import_header = try r.peekStruct(std.coff.ImportHeader, target_endian);
3966 // TODO: Validate import table header fields4044 // TODO: Validate import table header fields
3967 // TODO: Use this result in flushGlobal4045 // TODO: Use this result in flushGlobal
...@@ -4356,13 +4434,13 @@ fn reportUndefs(coff: *Coff, tid: Zcu.PerThread.Id) !void {...@@ -4356,13 +4434,13 @@ fn reportUndefs(coff: *Coff, tid: Zcu.PerThread.Id) !void {
4356 // TODO: We could report the name here if we interned it in loadObject4434 // TODO: We could report the name here if we interned it in loadObject
4357 err.addNote("referenced internally by input '{f}{f}'", .{4435 err.addNote("referenced internally by input '{f}{f}'", .{
4358 other_ii.path(coff).fmtEscapeString(),4436 other_ii.path(coff).fmtEscapeString(),
4359 fmtArchiveNameString(other_ii.archiveName(coff)),4437 fmtMemberNameString(other_ii.memberName(coff)),
4360 });4438 });
4361 } else {4439 } else {
4362 err.addNote("referenced by input symbol '{s}' from '{f}{f}'", .{4440 err.addNote("referenced by input symbol '{s}' from '{f}{f}'", .{
4363 loc_sym.gmi.globalName(coff).name.toSlice(coff),4441 loc_sym.gmi.globalName(coff).name.toSlice(coff),
4364 other_ii.path(coff).fmtEscapeString(),4442 other_ii.path(coff).fmtEscapeString(),
4365 fmtArchiveNameString(other_ii.archiveName(coff)),4443 fmtMemberNameString(other_ii.memberName(coff)),
4366 });4444 });
4367 }4445 }
4368 },4446 },
...@@ -4478,21 +4556,32 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {...@@ -4478,21 +4556,32 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
4478 };4556 };
4479 break :task;4557 break :task;
4480 }4558 }
4559 if (coff.pending_input) |pending_iami| {
4560 // TODO: Prog node?
4561 coff.flushInputMember(pending_iami) catch |err| switch (err) {
4562 error.OutOfMemory => return error.OutOfMemory,
4563 else => |e| return comp.link_diags.fail(
4564 "linker failed to archive member: {t}",
4565 .{e},
4566 ),
4567 };
4568 coff.pending_input = null;
4569 break :task;
4570 }
4481 if (coff.global_pending_index < coff.globals.count()) {4571 if (coff.global_pending_index < coff.globals.count()) {
4482 const gmi: Node.GlobalMapIndex = .wrap(coff.global_pending_index);4572 const gmi: Node.GlobalMapIndex = .wrap(coff.global_pending_index);
4483 coff.global_pending_index += 1;
4484 const sub_prog_node = coff.synth_prog_node.start(4573 const sub_prog_node = coff.synth_prog_node.start(
4485 gmi.globalName(coff).name.toSlice(coff),4574 gmi.globalName(coff).name.toSlice(coff),
4486 0,4575 0,
4487 );4576 );
4488 defer sub_prog_node.end();4577 defer sub_prog_node.end();
4489 coff.flushGlobal(gmi) catch |err| switch (err) {4578 if (coff.flushGlobal(gmi) catch |err| switch (err) {
4490 else => |e| return e,4579 else => |e| return e,
4491 error.MappedFileIo => return comp.link_diags.fail(4580 error.MappedFileIo => return comp.link_diags.fail(
4492 "linker failed to lower constant: {t}",4581 "linker failed to lower constant: {t}",
4493 .{coff.mf.io_err.?},4582 .{coff.mf.io_err.?},
4494 ),4583 ),
4495 };4584 }) coff.global_pending_index += 1;
4496 break :task;4585 break :task;
4497 }4586 }
4498 var lazy_it = coff.lazy.iterator();4587 var lazy_it = coff.lazy.iterator();
...@@ -4547,7 +4636,7 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {...@@ -4547,7 +4636,7 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
4547 }4636 }
4548 // TODO: Idle task for flushing obj into lib?4637 // TODO: Idle task for flushing obj into lib?
4549 if (coff.input_section_pending_index < coff.input_sections.items.len) {4638 if (coff.input_section_pending_index < coff.input_sections.items.len) {
4550 const isi: Node.InputSectionIndex = @enumFromInt(coff.input_section_pending_index);4639 const isi: Node.InputSection.Index = @enumFromInt(coff.input_section_pending_index);
4551 coff.input_section_pending_index += 1;4640 coff.input_section_pending_index += 1;
4552 const sub_prog_node = coff.idleProgNode(tid, coff.input_prog_node, coff.getNode(isi.symbol(coff).node(coff)));4641 const sub_prog_node = coff.idleProgNode(tid, coff.input_prog_node, coff.getNode(isi.symbol(coff).node(coff)));
4553 defer sub_prog_node.end();4642 defer sub_prog_node.end();
...@@ -4559,7 +4648,7 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {...@@ -4559,7 +4648,7 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
4559 .{4648 .{
4560 isi.symbol(coff).get(coff).section_number.name(coff).toSlice(coff),4649 isi.symbol(coff).get(coff).section_number.name(coff).toSlice(coff),
4561 ii.path(coff).fmtEscapeString(),4650 ii.path(coff).fmtEscapeString(),
4562 fmtArchiveNameString(ii.archiveName(coff)),4651 fmtMemberNameString(ii.memberName(coff)),
4563 e,4652 e,
4564 },4653 },
4565 );4654 );
...@@ -4629,9 +4718,10 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {...@@ -4629,9 +4718,10 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
4629 }4718 }
4630 }4719 }
4631 if (coff.pending_uavs.count() > 0) return true;4720 if (coff.pending_uavs.count() > 0) return true;
4721 if (coff.pending_input != null) return true;
4632 if (coff.globals.count() > coff.global_pending_index) return true;4722 if (coff.globals.count() > coff.global_pending_index) return true;
4633 if (coff.symbol_table.pending.count() > 0) return true;
4634 for (&coff.lazy.values) |lazy| if (lazy.map.count() > lazy.pending_index) return true;4723 for (&coff.lazy.values) |lazy| if (lazy.map.count() > lazy.pending_index) return true;
4724 if (coff.symbol_table.pending.count() > 0) return true;
4635 if (coff.input_sections.items.len > coff.input_section_pending_index) return true;4725 if (coff.input_sections.items.len > coff.input_section_pending_index) return true;
4636 if (coff.mf.updates.items.len > 0) return true;4726 if (coff.mf.updates.items.len > 0) return true;
4637 if (coff.pending_members.count() > 0) return true;4727 if (coff.pending_members.count() > 0) return true;
...@@ -4655,7 +4745,7 @@ fn idleProgNode(...@@ -4655,7 +4745,7 @@ fn idleProgNode(
4655 const ii = isi.input(coff);4745 const ii = isi.input(coff);
4656 break :name std.fmt.bufPrint(&name, "{f}{f} {s}", .{4746 break :name std.fmt.bufPrint(&name, "{f}{f} {s}", .{
4657 ii.path(coff).fmtEscapeString(),4747 ii.path(coff).fmtEscapeString(),
4658 fmtArchiveNameString(ii.archiveName(coff)),4748 fmtMemberNameString(ii.memberName(coff)),
4659 coff.getNode(isi.symbol(coff).node(coff).parent(&coff.mf)).object_section.name(coff).toSlice(coff),4749 coff.getNode(isi.symbol(coff).node(coff).parent(&coff.mf)).object_section.name(coff).toSlice(coff),
4660 }) catch &name;4750 }) catch &name;
4661 },4751 },
...@@ -4736,7 +4826,7 @@ fn flushUav(...@@ -4736,7 +4826,7 @@ fn flushUav(
4736 si.applyLocationRelocs(coff);4826 si.applyLocationRelocs(coff);
4737}4827}
47384828
4739fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !void {4829fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
4740 const comp = coff.base.comp;4830 const comp = coff.base.comp;
4741 const gpa = comp.gpa;4831 const gpa = comp.gpa;
4742 const gn = gmi.globalName(coff);4832 const gn = gmi.globalName(coff);
...@@ -4751,7 +4841,7 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !void {...@@ -4751,7 +4841,7 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !void {
4751 gn.name,4841 gn.name,
4752 );4842 );
47534843
4754 return;4844 return true;
4755 }4845 }
47564846
4757 if (gn.lib_name.toSlice(coff)) |lib_name| {4847 if (gn.lib_name.toSlice(coff)) |lib_name| {
...@@ -4911,15 +5001,26 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !void {...@@ -4911,15 +5001,26 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !void {
4911 sym.rva = coff.computeNodeRva(sym.ni);5001 sym.rva = coff.computeNodeRva(sym.ni);
4912 si.applyLocationRelocs(coff);5002 si.applyLocationRelocs(coff);
4913 } else {5003 } else {
5004 if (coff.input_archive_symbol_indices.get(gn.name)) |index| {
5005 var iter: InputArchive.Member.Symbol.Index = index.first;
5006 while (true) {
5007 const archive_sym = &coff.input_archive_symbols.items[@intFromEnum(iter)];
5008
5009 // TODO: This implies that loading an input failing is not fatal
5010 if (!coff.input_archive_members.items[@intFromEnum(archive_sym.iami)].flags.is_loaded) {
5011 coff.pending_input = archive_sym.iami;
5012 return false;
5013 }
49145014
4915 // TODO: Check if not defined, and if in an input member5015 if (archive_sym.next == iter) break;
4916 // TODO: If so, queue a loadObject for that member5016 iter = archive_sym.next;
4917 // TODO: Return a value indicating to retry this flushGlobal5017 }
4918 // TODO: The loadObject idle task should be before the flushGlobal idle task5018 }
4919 //
4920 // TODO: Check if we can get flushGlobal before prelink, that would cause a problem
49215019
5020 // TODO: Check if we can get flushGlobal before prelink, that would cause a problem
4922 }5021 }
5022
5023 return true;
4923}5024}
49245025
4925fn flushLazy(coff: *Coff, pt: Zcu.PerThread, lmr: Node.LazyMapRef) !void {5026fn flushLazy(coff: *Coff, pt: Zcu.PerThread, lmr: Node.LazyMapRef) !void {
...@@ -5040,14 +5141,14 @@ fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void {...@@ -5040,14 +5141,14 @@ fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void {
50405141
5041 {5142 {
5042 var si = ii.firstSymbol(coff);5143 var si = ii.firstSymbol(coff);
5043 const last_si = ii.lastSymbol(coff);5144 const end_si = ii.endSymbol(coff);
5044 while (@intFromEnum(si) <= @intFromEnum(last_si)) : (si = si.next()) {5145 while (@intFromEnum(si) < @intFromEnum(end_si)) : (si = si.next()) {
5045 if (si.get(coff).ni != ni) continue;5146 if (si.get(coff).ni != ni) continue;
5046 si.flushMoved(coff);5147 si.flushMoved(coff);
5047 }5148 }
5048 }5149 }
50495150
5050 for (coff.input_resolved.items[@intFromEnum(ii.firstResolvedGlobal(coff))..]) |si| {5151 for (coff.input_resolved.items[@intFromEnum(isi.firstResolvedSymbol(coff))..]) |si| {
5051 if (si.get(coff).ni != ni) break;5152 if (si.get(coff).ni != ni) break;
5052 si.flushMoved(coff);5153 si.flushMoved(coff);
5053 }5154 }
...@@ -5606,7 +5707,7 @@ pub fn printNode(...@@ -5606,7 +5707,7 @@ pub fn printNode(
5606 const ii = isi.input(coff);5707 const ii = isi.input(coff);
5607 try w.print("({f}{f}, {s})", .{5708 try w.print("({f}{f}, {s})", .{
5608 ii.path(coff).fmtEscapeString(),5709 ii.path(coff).fmtEscapeString(),
5609 fmtArchiveNameString(ii.archiveName(coff)),5710 fmtMemberNameString(ii.memberName(coff)),
5610 coff.getNode(isi.symbol(coff).node(coff).parent(&coff.mf)).object_section.name(coff).toSlice(coff),5711 coff.getNode(isi.symbol(coff).node(coff).parent(&coff.mf)).object_section.name(coff).toSlice(coff),
5611 });5712 });
5612 },5713 },