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
log652902ac7f424a719abf89a6532e207d03368728
treedaecf9d1b0a3cc0f13ae502a1be1f37e7a010372
parent6ff2b9b9137c514b2750e40bf345824461a5aabf

Coff: support loading import libraries

- Ignore duplicate inputs - Fix handling COMDAT sections with no COMDAT symbol

1 files changed, 362 insertions(+), 167 deletions(-)

src/link/Coff.zig+362-167
......@@ -31,6 +31,7 @@ long_names_table: LongNamesTable,
3131import_table: ImportTable,
3232export_table: ExportTable,
3333symbol_table: SymbolTable,
34inputs: std.ArrayHashMapUnmanaged(std.Build.Cache.Path, void, std.Build.Cache.Path.TableAdapter, false),
3435input_archives: std.ArrayList(InputArchive),
3536input_archive_members: std.ArrayList(InputArchive.Member),
3637input_archive_symbols: std.ArrayList(InputArchive.Member.Symbol),
......@@ -39,7 +40,7 @@ input_archive_symbol_indices: std.AutoArrayHashMapUnmanaged(String, struct {
3940 last: InputArchive.Member.Symbol.Index,
4041}),
4142pending_input: ?InputArchive.Member.Index,
42inputs: std.ArrayList(Input),
43input_objects: std.ArrayList(InputObject),
4344input_symbols: std.ArrayList(Symbol.Index),
4445input_sections: std.ArrayList(Node.InputSection),
4546input_section_pending_index: u32,
......@@ -254,6 +255,10 @@ pub const Node = union(enum) {
254255 return coff.globals.keys()[gmi.unwrap().?];
255256 }
256257
258 pub fn globalNameMutable(gmi: GlobalMapIndex, coff: *Coff) *GlobalName {
259 return &coff.globals.keys()[gmi.unwrap().?];
260 }
261
257262 pub fn symbol(gmi: GlobalMapIndex, coff: *const Coff) Symbol.Index {
258263 return coff.globals.values()[gmi.unwrap().?];
259264 }
......@@ -283,20 +288,8 @@ pub const Node = union(enum) {
283288 }
284289 };
285290
286 pub const InputIndex = enum(u32) {
287 _,
288
289 pub fn path(ii: InputIndex, coff: *const Coff) std.Build.Cache.Path {
290 return coff.inputs.items[@intFromEnum(ii)].path;
291 }
292
293 pub fn memberName(ii: InputIndex, coff: *const Coff) ?[]const u8 {
294 return coff.inputs.items[@intFromEnum(ii)].member_name;
295 }
296 };
297
298291 const InputSection = struct {
299 ii: Node.InputIndex,
292 ioi: InputObject.Index,
300293 si: Symbol.Index,
301294 file_location: MappedFile.Node.FileLocation,
302295 first_li: Node.InputSection.LocalIndex,
......@@ -309,8 +302,8 @@ pub const Node = union(enum) {
309302 return &coff.input_sections.items[@intFromEnum(isi)];
310303 }
311304
312 pub fn input(isi: Index, coff: *const Coff) InputIndex {
313 return coff.input_sections.items[@intFromEnum(isi)].ii;
305 pub fn input(isi: Index, coff: *const Coff) InputObject.Index {
306 return coff.input_sections.items[@intFromEnum(isi)].ioi;
314307 }
315308
316309 pub fn fileLocation(isi: Index, coff: *const Coff) MappedFile.Node.FileLocation {
......@@ -409,10 +402,20 @@ pub const InputArchive = struct {
409402 pub const Member = struct {
410403 iai: InputArchive.Index,
411404 name: String,
412 // This range includes the member header
413 file_location: MappedFile.Node.FileLocation,
405 content: union(enum) {
406 // This range includes the member header
407 object: MappedFile.Node.FileLocation,
408 import: struct {
409 symbol_name: String,
410 lib_name: String,
411 // Either ordinal or hint, depending on value of name_type
412 import_ordinal_hint: u16,
413 type: std.coff.ImportType,
414 name_type: std.coff.ImportNameType,
415 },
416 },
414417 flags: packed struct {
415 is_import: bool,
418 // Set if an attempt was made to load this member
416419 is_loaded: bool,
417420 },
418421
......@@ -436,10 +439,22 @@ pub const InputArchive = struct {
436439 };
437440};
438441
439pub const Input = struct {
442pub const InputObject = struct {
440443 path: std.Build.Cache.Path,
441444 member_name: ?[]const u8,
442445 source_name: String.Optional,
446
447 pub const Index = enum(u32) {
448 _,
449
450 pub fn path(ioi: Index, coff: *const Coff) std.Build.Cache.Path {
451 return coff.input_objects.items[@intFromEnum(ioi)].path;
452 }
453
454 pub fn memberName(ioi: Index, coff: *const Coff) ?[]const u8 {
455 return coff.input_objects.items[@intFromEnum(ioi)].member_name;
456 }
457 };
443458};
444459
445460pub const Member = struct {
......@@ -952,7 +967,7 @@ pub const Symbol = struct {
952967 };
953968
954969 comptime {
955 if (!std.debug.runtime_safety) std.debug.assert(@sizeOf(Symbol) == 32);
970 if (!std.debug.runtime_safety) std.debug.assert(@sizeOf(Symbol) == 36);
956971 }
957972};
958973
......@@ -1381,12 +1396,13 @@ fn create(
13811396 .pending = .empty,
13821397 .pending_shrink = false,
13831398 },
1399 .inputs = .empty,
13841400 .input_archives = .empty,
13851401 .input_archive_members = .empty,
13861402 .input_archive_symbols = .empty,
13871403 .input_archive_symbol_indices = .empty,
13881404 .pending_input = null,
1389 .inputs = .empty,
1405 .input_objects = .empty,
13901406 .input_symbols = .empty,
13911407 .input_sections = .empty,
13921408 .input_section_pending_index = 0,
......@@ -1447,11 +1463,12 @@ pub fn deinit(coff: *Coff) void {
14471463 coff.export_table.entries.deinit(gpa);
14481464 coff.symbol_table.strings.deinit(gpa);
14491465 coff.symbol_table.pending.deinit(gpa);
1466 coff.inputs.deinit(gpa);
14501467 coff.input_archives.deinit(gpa);
14511468 coff.input_archive_members.deinit(gpa);
14521469 coff.input_archive_symbols.deinit(gpa);
14531470 coff.input_archive_symbol_indices.deinit(gpa);
1454 coff.inputs.deinit(gpa);
1471 coff.input_objects.deinit(gpa);
14551472 coff.input_symbols.deinit(gpa);
14561473 coff.input_sections.deinit(gpa);
14571474 coff.strings.deinit(gpa);
......@@ -2772,23 +2789,28 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void
27722789
27732790fn flushInputMember(coff: *Coff, iami: InputArchive.Member.Index) !void {
27742791 const member = iami.member(coff);
2775 if (member.file_location.size == 0) return;
27762792 assert(!member.flags.is_loaded);
27772793 defer member.flags.is_loaded = true;
2778 const comp = coff.base.comp;
2779 const io = comp.io;
2780 const path = member.iai.path(coff);
2781 const file = try path.root_dir.handle.openFile(io, path.sub_path, .{});
2782 defer file.close(io);
2783 var buffer: [4096]u8 = undefined;
2784 var fr = file.reader(io, &buffer);
2785 const offset = member.file_location.offset + @sizeOf(std.coff.ArchiveMemberHeader);
2786 try fr.seekTo(offset);
2787 log.debug("flushInputMember({f}({s}))", .{ path, member.name.toSlice(coff) });
2788 try coff.loadObject(path, member.name.toSlice(coff), &fr, .{
2789 .offset = offset,
2790 .size = member.file_location.size,
2791 });
2794 switch (member.content) {
2795 .import => unreachable,
2796 .object => |file_location| {
2797 if (file_location.size == 0) return;
2798 const comp = coff.base.comp;
2799 const io = comp.io;
2800 const path = member.iai.path(coff);
2801 const file = try path.root_dir.handle.openFile(io, path.sub_path, .{});
2802 defer file.close(io);
2803 var buffer: [4096]u8 = undefined;
2804 var fr = file.reader(io, &buffer);
2805 const offset = file_location.offset + @sizeOf(std.coff.ArchiveMemberHeader);
2806 try fr.seekTo(offset);
2807 log.debug("flushInputMember({f}({s}))", .{ path, member.name.toSlice(coff) });
2808 try coff.loadObject(path, member.name.toSlice(coff), &fr, .{
2809 .offset = offset,
2810 .size = file_location.size,
2811 });
2812 },
2813 }
27922814}
27932815
27942816fn flushInputSection(coff: *Coff, isi: Node.InputSection.Index) !void {
......@@ -2797,8 +2819,8 @@ fn flushInputSection(coff: *Coff, isi: Node.InputSection.Index) !void {
27972819 const comp = coff.base.comp;
27982820 const io = comp.io;
27992821 const gpa = comp.gpa;
2800 const ii = isi.input(coff);
2801 const path = ii.path(coff);
2822 const ioi = isi.input(coff);
2823 const path = ioi.path(coff);
28022824 const file = try path.root_dir.handle.openFile(io, path.sub_path, .{});
28032825 defer file.close(io);
28042826 var fr = file.reader(io, &.{});
......@@ -2809,7 +2831,7 @@ fn flushInputSection(coff: *Coff, isi: Node.InputSection.Index) !void {
28092831 defer nw.deinit();
28102832 log.debug("flushInputSection({f}{f}, {s})", .{
28112833 path,
2812 fmtMemberNameString(ii.memberName(coff)),
2834 fmtMemberNameString(ioi.memberName(coff)),
28132835 isi.symbol(coff).get(coff).section_number.name(coff).toSlice(coff),
28142836 });
28152837 if (try nw.interface.sendFileAll(&fr, .limited(@intCast(file_loc.size))) != file_loc.size)
......@@ -3216,7 +3238,14 @@ pub fn addReloc(
32163238
32173239pub fn loadInput(coff: *Coff, input: link.Input) (Io.File.Reader.SizeError ||
32183240 Io.File.Reader.Error || MappedFile.Error || error{ WriteFailed, EndOfStream, BadMagic, LinkFailure })!void {
3219 const io = coff.base.comp.io;
3241 const comp = coff.base.comp;
3242 const io = comp.io;
3243
3244 const path = input.path() orelse unreachable;
3245 const gop = try coff.inputs.getOrPut(comp.gpa, path);
3246 if (gop.found_existing) return;
3247 errdefer _ = coff.inputs.swapRemove(path);
3248
32203249 var buf: [4096]u8 = undefined;
32213250 switch (input) {
32223251 .object => |object| {
......@@ -3340,9 +3369,9 @@ fn loadObject(
33403369 symbol_table_end + string_table_len > fl.size)
33413370 return diags.failParse(path, "bad string table", .{});
33423371
3343 const ii: Node.InputIndex = @enumFromInt(coff.inputs.items.len);
3344 try coff.inputs.ensureUnusedCapacity(gpa, 1);
3345 const input = coff.inputs.addOneAssumeCapacity();
3372 const ioi: InputObject.Index = @enumFromInt(coff.input_objects.items.len);
3373 try coff.input_objects.ensureUnusedCapacity(gpa, 1);
3374 const input = coff.input_objects.addOneAssumeCapacity();
33463375 input.* = .{
33473376 .path = path,
33483377 .member_name = if (member_name) |m| try gpa.dupe(u8, m) else null,
......@@ -3743,8 +3772,12 @@ fn loadObject(
37433772 else => |e| return e,
37443773 }) |arg| {
37453774 // Microsoft tools emit 3 space characters into this section even with /Zl
3746 if (arg.len > 0)
3747 return diags.failParse(path, "unsupported argument in .drectve section: `{s}`", .{arg});
3775 if (arg.len == 0) continue;
3776
3777 if (std.mem.cutPrefix(u8, arg, "-exclude-symbols:")) |rest| {
3778 // TODO: When implementing mingw auto-exports, use this to not export this symbol
3779 _ = rest;
3780 } else return diags.failParse(path, "unsupported argument in .drectve section: `{s}`", .{arg});
37483781 }
37493782 }
37503783
......@@ -3784,29 +3817,37 @@ fn loadObject(
37843817 };
37853818 },
37863819 else => |comdat| {
3787 const psi = section.comdat_psi.unwrap() orelse
3788 return diags.failParse(
3789 path,
3790 "COMDAT section symbol 0x{x} had no COMDAT symbol",
3791 .{pending_symbols.keys()[section.psi.unwrap().?]},
3792 );
3793
3820 const psi = section.comdat_psi.unwrap() orelse section.psi.unwrap().?;
37943821 const symbol = &pending_symbols.values()[psi];
3795 switch (symbol.value) {
3796 .section, .weak_external => unreachable,
3822 const si = existing: switch (symbol.value) {
3823 .weak_external => unreachable,
37973824 .static => break :comdat .include,
3798 else => {},
3799 }
3825 .section => {
3826 assert(section.comdat_psi == .none);
3827 if (coff.object_section_table.get(section.name)) |si|
3828 break :existing si
3829 else if (coff.pseudo_section_table.get(section.name)) |si|
3830 break :existing si
3831 else if (coff.section_table.get(section.name)) |s|
3832 break :existing s.si
3833 else
3834 break :comdat .include;
3835 },
3836 else => {
3837 const global_gop = try coff.getOrPutGlobalSymbol(.{
3838 .name = symbol.name.toSlice(coff),
3839 .lib_name = null,
3840 });
3841 if (!global_gop.found_existing) {
3842 symbol.si = global_gop.value_ptr.*;
3843 break :comdat .include;
3844 }
38003845
3801 // TODO: Do we need to use lib_name here?
3802 const global_gop = try coff.getOrPutGlobalSymbol(.{ .name = symbol.name.toSlice(coff), .lib_name = null });
3803 if (!global_gop.found_existing) {
3804 symbol.si = global_gop.value_ptr.*;
3805 break :comdat .include;
3806 }
3846 break :existing global_gop.value_ptr.*;
3847 },
3848 };
38073849
38083850 const index = pending_symbols.keys()[psi];
3809 const si = global_gop.value_ptr.*;
38103851 switch (comdat) {
38113852 .NODUPLICATES => return coff.failMultipleDefinitions(
38123853 path,
......@@ -3816,12 +3857,17 @@ fn loadObject(
38163857 si,
38173858 .duplicate,
38183859 ),
3819 .ANY => break :comdat .skip,
3860 .ANY => {
3861 symbol.si = si;
3862 break :comdat .skip;
3863 },
38203864 .SAME_SIZE => {
38213865 // TODO: Verify that this node isn't resized after creation
38223866 _, const size = si.get(coff).ni.location(&coff.mf).resolve(&coff.mf);
3823 if (size == section.header.size_of_raw_data)
3867 if (size == section.header.size_of_raw_data) {
3868 symbol.si = si;
38243869 break :comdat .skip;
3870 }
38253871
38263872 return coff.failMultipleDefinitions(
38273873 path,
......@@ -3840,8 +3886,10 @@ fn loadObject(
38403886 else => std.hash.crc.Crc32Jamcrc.hash(sym.ni.slice(&coff.mf)),
38413887 };
38423888
3843 if (existing_crc == section.comdat_crc)
3889 if (existing_crc == section.comdat_crc) {
3890 symbol.si = si;
38443891 break :comdat .skip;
3892 }
38453893
38463894 return coff.failMultipleDefinitions(
38473895 path,
......@@ -3876,7 +3924,16 @@ fn loadObject(
38763924 continue :comdat root_result;
38773925 },
38783926 .include => {},
3879 .skip => continue,
3927 .skip => {
3928 assert(switch (section.comdat) {
3929 .NONE, .ASSOCIATIVE => true,
3930 else => if (section.comdat_psi.unwrap()) |psi|
3931 pending_symbols.values()[psi].si != .null
3932 else
3933 pending_symbols.values()[section.psi.unwrap().?].si != .null,
3934 });
3935 continue;
3936 },
38803937 .pending => unreachable,
38813938 }
38823939
......@@ -3915,7 +3972,7 @@ fn loadObject(
39153972 sym.section_number = section.parent_si.get(coff).section_number;
39163973
39173974 coff.input_sections.addOneAssumeCapacity().* = .{
3918 .ii = ii,
3975 .ioi = ioi,
39193976 .si = section.si,
39203977 .file_location = .{
39213978 .offset = fl.offset + section.header.pointer_to_raw_data,
......@@ -3987,8 +4044,7 @@ fn loadObject(
39874044 symbol.si = coff.addSymbolAssumeCapacity();
39884045 },
39894046 .external => {
3990 // COMDAT symbols were created when enumerating the sections
3991 assert(section.comdat == .NONE);
4047 // TODO: Assert this is not the comdat leader
39924048 const global_gop = try coff.getOrPutGlobalSymbol(.{ .name = symbol.name.toSlice(coff) });
39934049 symbol.si = global_gop.value_ptr.*;
39944050
......@@ -4086,20 +4142,26 @@ fn loadObject(
40864142
40874143 try coff.input_symbols.ensureUnusedCapacity(gpa, num_included_symbols + num_included_sections);
40884144 var prev_sn: Symbol.SectionNumber = .UNDEFINED;
4145 var include_section = true;
40894146 for (pending_symbols.values()) |symbol| {
40904147 // The symbol may have not been included, or it's an undefined external
40914148 if (symbol.si == .null or symbol.si.get(coff).ni == .none) continue;
4092 assert(coff.getNode(symbol.si.get(coff).ni) == .input_section);
40934149
40944150 if (prev_sn != symbol.section_number) {
40954151 prev_sn = symbol.section_number;
40964152
40974153 const section = &sections[symbol.section_number.toIndex()];
4098 const isi = coff.getNode(section.si.get(coff).ni).input_section;
4099 isi.inputSection(coff).first_li = @enumFromInt(coff.input_symbols.items.len);
4154 include_section = section.comdat_result == .include;
4155 if (include_section) {
4156 const isi = coff.getNode(section.si.get(coff).ni).input_section;
4157 isi.inputSection(coff).first_li = @enumFromInt(coff.input_symbols.items.len);
4158 }
41004159 }
41014160
4102 coff.input_symbols.addOneAssumeCapacity().* = symbol.si;
4161 if (include_section) {
4162 assert(coff.getNode(symbol.si.get(coff).ni) == .input_section);
4163 coff.input_symbols.addOneAssumeCapacity().* = symbol.si;
4164 }
41034165 }
41044166}
41054167
......@@ -4123,16 +4185,15 @@ fn failMultipleDefinitions(
41234185
41244186 switch (coff.getNode(existing_si.get(coff).ni)) {
41254187 .input_section => |isi| {
4126 const other_ii = isi.input(coff);
4188 const other_ioi = isi.input(coff);
41274189 err.addNote("first seen in input '{f}{f}'", .{
4128 other_ii.path(coff).fmtEscapeString(),
4129 fmtMemberNameString(other_ii.memberName(coff)),
4190 other_ioi.path(coff).fmtEscapeString(),
4191 fmtMemberNameString(other_ioi.memberName(coff)),
41304192 });
41314193 },
41324194 .nav, .uav => err.addNote("first seen in module '{s}'", .{
41334195 coff.base.comp.zcu.?.root_mod.fully_qualified_name,
41344196 }),
4135 //else => |_, tag| err.addNote("TODO multiple def for {t}", .{tag}),
41364197 else => unreachable,
41374198 }
41384199
......@@ -4158,6 +4219,7 @@ const ArchiveMemberHeader = struct {
41584219 size: u34,
41594220};
41604221
4222/// Return value lifetime is that of `header`
41614223fn parseArchiveMemberHeader(
41624224 diags: *link.Diags,
41634225 path: std.Build.Cache.Path,
......@@ -4346,10 +4408,14 @@ fn loadArchive(coff: *Coff, path: std.Build.Cache.Path, fr: *Io.File.Reader) !vo
43464408 coff.input_archive_members.addOneAssumeCapacity().* = .{
43474409 .iai = iai,
43484410 .name = undefined,
4349 .flags = undefined,
4350 .file_location = .{
4351 .offset = member_offset,
4352 .size = undefined,
4411 .content = .{
4412 .object = .{
4413 .offset = member_offset,
4414 .size = undefined,
4415 },
4416 },
4417 .flags = .{
4418 .is_loaded = false,
43534419 },
43544420 };
43554421
......@@ -4394,9 +4460,9 @@ fn loadArchive(coff: *Coff, path: std.Build.Cache.Path, fr: *Io.File.Reader) !vo
43944460 else => {},
43954461 };
43964462
4397 // Validate / read names and sizes of all the referenced members
4463 // Validate / read names and sizes of all the referenced members, enumerate imports
43984464 for (coff.input_archive_members.items[first_iami..]) |*member| {
4399 try fr.seekTo(member.file_location.offset);
4465 try fr.seekTo(member.content.object.offset);
44004466
44014467 const header = try r.takeStruct(std.coff.ArchiveMemberHeader, target_endian);
44024468 const res = try parseArchiveMemberHeader(diags, path, &header, opt_longnames);
......@@ -4405,28 +4471,74 @@ fn loadArchive(coff: *Coff, path: std.Build.Cache.Path, fr: *Io.File.Reader) !vo
44054471 member.name = coff.getOrPutStringAssumeCapacity(res.name);
44064472
44074473 const member_sig = try r.peek(4);
4408 const machine = std.mem.readInt(u16, member_sig[0..2], target_endian);
4474 const machine: std.coff.IMAGE.FILE.MACHINE =
4475 @enumFromInt(std.mem.readInt(u16, member_sig[0..2], target_endian));
44094476 const sig = std.mem.readInt(u16, member_sig[2..4], target_endian);
4410 member.flags = .{
4411 .is_import = machine == @intFromEnum(std.coff.IMAGE.FILE.MACHINE.UNKNOWN) and sig == 0xffff,
4412 .is_loaded = false,
4413 };
4414 member.file_location.size = res.size;
44154477
44164478 log.debug("verifyArchiveMember({s}) = 0x{x}+{x}", .{
44174479 res.name,
4418 member.file_location.offset,
4419 member.file_location.size,
4480 member.content.object.offset,
4481 res.size,
44204482 });
44214483
4422 if (member.flags.is_import) {
4423 const import_header = try r.peekStruct(std.coff.ImportHeader, target_endian);
4424 // TODO: Validate import table header fields
4425 // TODO: Use this result in flushGlobal
4426 return diags.failParse(path, "TODO implement parsing import headers: {t} {t}", .{
4484 const expected_machine = comp.root_mod.resolved_target.result.toCoffMachine();
4485 if (machine == std.coff.IMAGE.FILE.MACHINE.UNKNOWN and sig == 0xffff) {
4486 const import_header = try r.takeStruct(std.coff.ImportHeader, target_endian);
4487 const strings = r.take(import_header.size_of_data) catch |err| switch (err) {
4488 error.EndOfStream => return diags.failParse(path, "invalid data size in import header '{s}'", .{res.name}),
4489 else => |e| return e,
4490 };
4491
4492 var split = std.mem.splitScalar(u8, strings, 0);
4493 const symbol_name = split.next() orelse
4494 return diags.failParse(path, "invalid symbol name string in import header '{s}'", .{res.name});
4495 var lib_name = split.next() orelse
4496 return diags.failParse(path, "invalid dll name string in import header '{s}' ('{s}')", .{ res.name, symbol_name });
4497
4498 if (import_header.machine != expected_machine)
4499 return diags.failParse(path, "machine mismatch in import header '{s}' ('{s}'): expected {t}, found {t}", .{
4500 res.name,
4501 symbol_name,
4502 expected_machine,
4503 machine,
4504 });
4505
4506 const ext = ".dll";
4507 if (!std.mem.endsWith(u8, lib_name, ext))
4508 return diags.failParse(
4509 path,
4510 "unexpected extension for import '{s} ('{s}'): '{s}'",
4511 .{ res.name, symbol_name, lib_name },
4512 );
4513
4514 lib_name = lib_name[0 .. lib_name.len - ext.len];
4515 log.debug("verifyArchiveImportHeader({s}, {s}, {s}) = {t} ({t})", .{
4516 res.name,
4517 symbol_name,
4518 lib_name,
44274519 import_header.types.type,
44284520 import_header.types.name_type,
44294521 });
4522
4523 try coff.ensureManyUnusedStringCapacity(2, strings.len - ext.len);
4524 member.content = .{
4525 .import = .{
4526 .symbol_name = coff.getOrPutStringAssumeCapacity(symbol_name),
4527 .lib_name = coff.getOrPutStringAssumeCapacity(lib_name),
4528 .import_ordinal_hint = import_header.hint,
4529 .type = import_header.types.type,
4530 .name_type = import_header.types.name_type,
4531 },
4532 };
4533 } else {
4534 member.content.object.size = res.size;
4535 if (machine != expected_machine) {
4536 return diags.failParse(path, "machine mismatch in member header '{s}': expected {t}, found {t}", .{
4537 res.name,
4538 expected_machine,
4539 machine,
4540 });
4541 }
44304542 }
44314543 }
44324544}
......@@ -4808,18 +4920,18 @@ fn reportUndefs(coff: *Coff, tid: Zcu.PerThread.Id) !void {
48084920 const loc_sym = loc_si.get(coff);
48094921 switch (coff.getNode(loc_sym.ni)) {
48104922 .input_section => |isi| {
4811 const other_ii = isi.input(coff);
4923 const other_ioi = isi.input(coff);
48124924 if (loc_sym.gmi == .none) {
48134925 // TODO: We could report the name here if we interned it in loadObject
48144926 err.addNote("referenced internally by input '{f}{f}'", .{
4815 other_ii.path(coff).fmtEscapeString(),
4816 fmtMemberNameString(other_ii.memberName(coff)),
4927 other_ioi.path(coff).fmtEscapeString(),
4928 fmtMemberNameString(other_ioi.memberName(coff)),
48174929 });
48184930 } else {
48194931 err.addNote("referenced by input symbol '{s}' from '{f}{f}'", .{
48204932 loc_sym.gmi.globalName(coff).name.toSlice(coff),
4821 other_ii.path(coff).fmtEscapeString(),
4822 fmtMemberNameString(other_ii.memberName(coff)),
4933 other_ioi.path(coff).fmtEscapeString(),
4934 fmtMemberNameString(other_ioi.memberName(coff)),
48234935 });
48244936 }
48254937 },
......@@ -5010,13 +5122,13 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
50105122 defer sub_prog_node.end();
50115123 coff.flushInputSection(isi) catch |err| switch (err) {
50125124 else => |e| {
5013 const ii = isi.input(coff);
5125 const ioi = isi.input(coff);
50145126 return comp.link_diags.fail(
50155127 "linker failed to read input section '{s}' from \"{f}{f}\": {t}",
50165128 .{
50175129 isi.symbol(coff).get(coff).section_number.name(coff).toSlice(coff),
5018 ii.path(coff).fmtEscapeString(),
5019 fmtMemberNameString(ii.memberName(coff)),
5130 ioi.path(coff).fmtEscapeString(),
5131 fmtMemberNameString(ioi.memberName(coff)),
50205132 e,
50215133 },
50225134 );
......@@ -5110,10 +5222,10 @@ fn idleProgNode(
51105222 .image_section => |si| std.mem.sliceTo(&si.get(coff).section_number.header(coff).name, 0),
51115223 inline .pseudo_section, .object_section => |smi| smi.name(coff).toSlice(coff),
51125224 .input_section => |isi| {
5113 const ii = isi.input(coff);
5225 const ioi = isi.input(coff);
51145226 break :name std.fmt.bufPrint(&name, "{f}{f} {s}", .{
5115 ii.path(coff).fmtEscapeString(),
5116 fmtMemberNameString(ii.memberName(coff)),
5227 ioi.path(coff).fmtEscapeString(),
5228 fmtMemberNameString(ioi.memberName(coff)),
51175229 coff.getNode(isi.symbol(coff).node(coff).parent(&coff.mf)).object_section.name(coff).toSlice(coff),
51185230 }) catch &name;
51195231 },
......@@ -5197,7 +5309,7 @@ fn flushUav(
51975309fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
51985310 const comp = coff.base.comp;
51995311 const gpa = comp.gpa;
5200 const gn = gmi.globalName(coff);
5312 const gn = gmi.globalNameMutable(coff);
52015313 const si = gmi.symbol(coff);
52025314 const sym = si.get(coff);
52035315
......@@ -5217,8 +5329,142 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
52175329 return true;
52185330 }
52195331
5220 if (gn.lib_name.toSlice(coff)) |lib_name| {
5221 const name = gn.name.toSlice(coff);
5332 const Import = struct {
5333 lib_name: String,
5334 ref: union(enum) {
5335 name: struct {
5336 str: []const u8,
5337 hint: ?u16,
5338 },
5339 ordinal: u16,
5340 },
5341 };
5342
5343 const opt_import: ?Import = if (gn.lib_name == .none and sym.ni == .none) import: {
5344 switch (sym.value) {
5345 .alias_si => |alias_si| {
5346 assert(sym.section_number == .UNDEFINED);
5347 assert(sym.loc_relocs == .none);
5348
5349 const alias_sym = alias_si.get(coff);
5350 var ri = sym.target_relocs;
5351 while (ri != .none) {
5352 const reloc = ri.get(coff);
5353 assert(reloc.target == si);
5354 reloc.target = alias_si;
5355 if (reloc.next == .none) {
5356 reloc.next = alias_sym.target_relocs;
5357 if (alias_sym.target_relocs != .none)
5358 alias_sym.target_relocs.get(coff).prev = ri;
5359 }
5360 ri = reloc.next;
5361 }
5362
5363 sym.target_relocs = .none;
5364 coff.globals.values()[gmi.unwrap().?] = alias_si;
5365 alias_si.applyTargetRelocs(coff);
5366
5367 log.debug(
5368 "flushGlobal({s}, null) alias {d}->{d}",
5369 .{ gmi.globalName(coff).name.toSlice(coff), si, alias_si },
5370 );
5371 return true;
5372 },
5373 .size => {},
5374 .input_offset => unreachable,
5375 }
5376
5377 if (coff.input_archive_symbol_indices.get(gmi.globalName(coff).name)) |index| {
5378 var iter: InputArchive.Member.Symbol.Index = index.first;
5379 while (true) {
5380 const archive_sym = &coff.input_archive_symbols.items[@intFromEnum(iter)];
5381 const member = &coff.input_archive_members.items[@intFromEnum(archive_sym.iami)];
5382 if (!member.flags.is_loaded) {
5383 switch (member.content) {
5384 .import => |import| switch (import.type) {
5385 .CODE,
5386 .DATA,
5387 => {
5388 defer member.flags.is_loaded = true;
5389 // gn.lib_name = import.lib_name.toOptional();
5390 // try coff.globals.setKey(gpa, gmi.unwrap().?, gn.*);
5391
5392 // Switch this global to an import
5393 switch (import.name_type) {
5394 .NAME,
5395 .NAME_NOPREFIX,
5396 .NAME_UNDECORATE,
5397 => |tag| {
5398 var name: []const u8 = import.symbol_name.toSlice(coff);
5399 if (!(std.mem.eql(u8, name, gn.name.toSlice(coff))))
5400 return comp.link_diags.fail("import '{s}' has mismatched symbol name: '{s}'", .{
5401 import.symbol_name.toSlice(coff),
5402 gn.name.toSlice(coff),
5403 });
5404
5405 name = if (tag == .NAME) name else name: {
5406 name = std.mem.trimStart(u8, name, "?@_");
5407 if (tag == .NAME_UNDECORATE)
5408 name = std.mem.sliceTo(name, '@');
5409 break :name name;
5410 };
5411
5412 break :import .{
5413 .lib_name = import.lib_name,
5414 .ref = .{
5415 .name = .{
5416 .str = name,
5417 .hint = import.import_ordinal_hint,
5418 },
5419 },
5420 };
5421 },
5422 .ORDINAL => break :import .{
5423 .lib_name = import.lib_name,
5424 .ref = .{ .ordinal = import.import_ordinal_hint },
5425 },
5426 else => |t| return comp.link_diags.fail("TODO handle name_type {t}", .{t}),
5427 }
5428 },
5429 .CONST => return comp.link_diags.fail("TODO handle import type CONST", .{}),
5430 else => |t| return comp.link_diags.fail("invalid import type: {d}", .{t}),
5431 },
5432 .object => {
5433 // Try loading the input member and then retry
5434 coff.pending_input = archive_sym.iami;
5435 return false;
5436 },
5437 }
5438 }
5439
5440 if (archive_sym.next == iter) break;
5441 iter = archive_sym.next;
5442 }
5443 }
5444
5445 break :import null;
5446 } else if (gn.lib_name.unwrap()) |lib_name| .{
5447 .lib_name = lib_name,
5448 .ref = .{
5449 .name = .{
5450 .str = gn.name.toSlice(coff),
5451 .hint = null,
5452 },
5453 },
5454 } else null;
5455
5456 if (opt_import) |import| {
5457 assert(sym.ni == .none);
5458 const lib_name = import.lib_name.toSlice(coff);
5459 const name = switch (import.ref) {
5460 .name => |n| n.str,
5461 .ordinal => return comp.link_diags.fail("TODO handle imports via ordinal", .{}),
5462 };
5463
5464 log.debug("flushGlobalImport({s}, {s})", .{ name, lib_name });
5465
5466 // TODO: Handle hint
5467
52225468 try coff.nodes.ensureUnusedCapacity(gpa, 4);
52235469 try coff.symbols.ensureUnusedCapacity(gpa, 1);
52245470
......@@ -5371,57 +5617,6 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
53715617 coff.nodes.appendAssumeCapacity(.{ .global = gmi });
53725618 sym.rva = coff.computeNodeRva(sym.ni);
53735619 si.applyLocationRelocs(coff);
5374 } else if (sym.ni == .none) {
5375 switch (sym.value) {
5376 .alias_si => |alias_si| {
5377 assert(sym.section_number == .UNDEFINED);
5378 assert(sym.loc_relocs == .none);
5379
5380 const alias_sym = alias_si.get(coff);
5381 var ri = sym.target_relocs;
5382 while (ri != .none) {
5383 const reloc = ri.get(coff);
5384 assert(reloc.target == si);
5385 reloc.target = alias_si;
5386 if (reloc.next == .none) {
5387 reloc.next = alias_sym.target_relocs;
5388 if (alias_sym.target_relocs != .none)
5389 alias_sym.target_relocs.get(coff).prev = ri;
5390 }
5391 ri = reloc.next;
5392 }
5393
5394 sym.target_relocs = .none;
5395 coff.globals.values()[gmi.unwrap().?] = alias_si;
5396 alias_si.applyTargetRelocs(coff);
5397
5398 log.debug("flushGlobal({s}, {?s}) alias {d}->{d}", .{
5399 gmi.globalName(coff).name.toSlice(coff),
5400 gmi.globalName(coff).lib_name.toSlice(coff),
5401 si,
5402 alias_si,
5403 });
5404
5405 return true;
5406 },
5407 .size => {},
5408 .input_offset => unreachable,
5409 }
5410
5411 if (coff.input_archive_symbol_indices.get(gmi.globalName(coff).name)) |index| {
5412 var iter: InputArchive.Member.Symbol.Index = index.first;
5413 while (true) {
5414 const archive_sym = &coff.input_archive_symbols.items[@intFromEnum(iter)];
5415 if (!coff.input_archive_members.items[@intFromEnum(archive_sym.iami)].flags.is_loaded) {
5416 // Try loading the input member and then retry
5417 coff.pending_input = archive_sym.iami;
5418 return false;
5419 }
5420
5421 if (archive_sym.next == iter) break;
5422 iter = archive_sym.next;
5423 }
5424 }
54255620 }
54265621
54275622 return true;
......@@ -6097,10 +6292,10 @@ pub fn printNode(
60976292 std.mem.sliceTo(&si.get(coff).section_number.header(coff).name, 0),
60986293 }),
60996294 .input_section => |isi| {
6100 const ii = isi.input(coff);
6295 const ioi = isi.input(coff);
61016296 try w.print("({f}{f}, {s})", .{
6102 ii.path(coff).fmtEscapeString(),
6103 fmtMemberNameString(ii.memberName(coff)),
6297 ioi.path(coff).fmtEscapeString(),
6298 fmtMemberNameString(ioi.memberName(coff)),
61046299 coff.getNode(isi.symbol(coff).node(coff).parent(&coff.mf)).object_section.name(coff).toSlice(coff),
61056300 });
61066301 },