authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2026-09-20 02:20:35+02:00
committergravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2026-09-20 02:20:35+02:00
log3bfb2999471d921cfdf461c3c12f6caedb45904b
treedfa1f059f7c0b15eb1ec67a57fd331c1ad211b1c
parent1175a3e992fc30361febf3517827a5d0ada49b5b
parent68680bbd00ae43cd25d8c613d666bd1554d67131

Merge pull request 'Coff: Miscellaneous fixes' (#36887) from kcbanner/zig:coff_fixes into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/36887

5 files changed, 257 insertions(+), 199 deletions(-)

lib/compiler/objdump.zig+13-11
......@@ -828,19 +828,21 @@ const coff = struct {
828828 defer gpa.free(string_table);
829829
830830 if (d.opts.strings) {
831 if (d.element(.@"table-header"))
832 try w.print(
833 \\String Table (0x{x} bytes):
834 \\
835 , .{string_table.len});
831 if (string_table.len > 0) {
832 if (d.element(.@"table-header"))
833 try w.print(
834 \\String Table (0x{x} bytes):
835 \\
836 , .{string_table.len});
836837
837 var sr = Io.Reader.fixed(string_table[@sizeOf(u32)..]);
838 while (try sr.takeDelimiter(0)) |str| {
839 try w.writeAll(str);
840 try w.writeByte('\n');
841 }
838 var sr = Io.Reader.fixed(string_table[@sizeOf(u32)..]);
839 while (try sr.takeDelimiter(0)) |str| {
840 try w.writeAll(str);
841 try w.writeByte('\n');
842 }
842843
843 if (d.element(.newlines)) try w.writeByte('\n');
844 if (d.element(.newlines)) try w.writeByte('\n');
845 } else try w.writeAll("String table not present\n");
844846 }
845847
846848 var sections: std.ArrayList(Section) = .empty;
src/Compilation.zig+3-2
......@@ -3528,7 +3528,7 @@ fn emitFromCObject(
35283528/// Having the file open for writing is problematic as far as executing the
35293529/// binary is concerned. This will remove the write flag, or close the file,
35303530/// or whatever is needed so that it can be executed.
3531/// After this, one must call` makeFileWritable` before calling `update`.
3531/// After this, one must call `makeFileWritable` before calling `update`.
35323532pub fn makeBinFileExecutable(comp: *Compilation) !void {
35333533 if (!dev.env.supports(.make_executable)) return;
35343534 const lf = comp.bin_file orelse return;
......@@ -3537,7 +3537,8 @@ pub fn makeBinFileExecutable(comp: *Compilation) !void {
35373537
35383538pub fn makeBinFileWritable(comp: *Compilation) !void {
35393539 const lf = comp.bin_file orelse return;
3540 return lf.makeWritable();
3540 if (lf.canMakeExecutable())
3541 try lf.makeWritable();
35413542}
35423543
35433544const Header = extern struct {
src/link.zig+14-8
......@@ -689,18 +689,24 @@ pub const File = struct {
689689 return macho.reopenDebugInfo();
690690 }
691691
692 pub fn canMakeExecutable(base: *File) bool {
693 const comp = base.comp;
694 return switch (comp.config.output_mode) {
695 .Obj => false,
696 .Lib => switch (comp.config.link_mode) {
697 .static => false,
698 .dynamic => true,
699 },
700 .Exe => true,
701 };
702 }
703
692704 pub fn makeExecutable(base: *File) !void {
693705 dev.check(.make_executable);
694706 const comp = base.comp;
695707 const io = comp.io;
696 switch (comp.config.output_mode) {
697 .Obj => return,
698 .Lib => switch (comp.config.link_mode) {
699 .static => return,
700 .dynamic => {},
701 },
702 .Exe => {},
703 }
708 if (!base.canMakeExecutable())
709 return;
704710 switch (base.tag) {
705711 .lld => assert(base.file == null),
706712 .elf => if (base.file) |f| {
src/link/Coff.zig+225-170
......@@ -278,7 +278,7 @@ pub const Node = union(enum) {
278278 return coff.globals.values()[gmi.unwrap().?].si;
279279 }
280280
281 pub fn libName(gmi: GlobalMapIndex, coff: *const Coff) String.Optional {
281 pub fn libName(gmi: GlobalMapIndex, coff: *const Coff) ExternLibName {
282282 return coff.globals.values()[gmi.unwrap().?].lib_name;
283283 }
284284 };
......@@ -647,15 +647,12 @@ pub const SymbolTable = struct {
647647 };
648648
649649 pub const SymbolName = union(enum) {
650 short: []const u8,
650 short: [8]u8,
651651 long: StringIndex,
652652
653653 pub fn store(name: SymbolName, coff: *const Coff, field: *[8]u8) void {
654654 switch (name) {
655 .short => |s| {
656 @memcpy(field[0..s.len], s);
657 @memset(field[s.len..], 0);
658 },
655 .short => |s| field.* = s,
659656 .long => |l| {
660657 @memset(field[0..4], 0);
661658 std.mem.writePackedInt(u32, field[4..], 0, @backingInt(l), coff.targetEndian());
......@@ -864,7 +861,7 @@ pub const Section = struct {
864861
865862pub const Global = struct {
866863 si: Symbol.Index,
867 lib_name: String.Optional,
864 lib_name: ExternLibName,
868865};
869866
870867pub const WeakExternalStrat = enum(u3) {
......@@ -2263,6 +2260,11 @@ pub fn initBuiltins(coff: *Coff) !void {
22632260 }
22642261
22652262 defer coff.flushSectionMerges() catch unreachable;
2263 if (coff.isImage()) {
2264 // In images, the .tls section is a read-only template
2265 try coff.section_merges.put(gpa, .@".tls", .@".rdata");
2266 }
2267
22662268 if (coff.isImage() and target.isMinGW() and comp.config.link_libc) {
22672269 try coff.symbols.ensureUnusedCapacity(gpa, 8);
22682270 try coff.globals.ensureUnusedCapacity(gpa, 2);
......@@ -2723,29 +2725,42 @@ fn getString(coff: *Coff, string: []const u8) String.Optional {
27232725 return .none;
27242726}
27252727
2726/// If the name does not fit in the symbol header, adds it to the symbol table string table.
2727/// If the caller knows this name already has a String associated with it, they can avoid
2728/// a redundant call to `getOrPutString` by specifying `opt_string`.
2729/// The lifetime of the return value matches that of `name`.
2730fn getOrPutSymbolName(coff: *Coff, name: []const u8, opt_string: ?String) !SymbolTable.SymbolName {
2728/// If the name does not fit within inline storage, adds it to the symbol table string table.
2729fn getOrPutSymbolName(coff: *Coff, name: union(enum) {
2730 slice: []const u8,
2731 string: String,
2732}) !SymbolTable.SymbolName {
27312733 assert(!coff.isImage());
27322734 const gpa = coff.base.comp.gpa;
2735 const slice = switch (name) {
2736 .slice => |s| s,
2737 .string => |s| s.toSlice(coff),
2738 };
2739
2740 if (slice.len > header_name_max_len) {
2741 const string = switch (name) {
2742 .slice => |s| try coff.getOrPutString(s),
2743 .string => |s| s,
2744 };
27332745
2734 return if (name.len > header_name_max_len) name: {
2735 const string = opt_string orelse try coff.getOrPutString(name);
27362746 const string_gop = try coff.symbol_table.strings.getOrPut(gpa, string);
27372747 if (!string_gop.found_existing) {
27382748 const string_index = coff.symbol_table.strings_ni.location(&coff.mf).resolve(&coff.mf)[1];
27392749 string_gop.value_ptr.* = @fromBackingInt(@intCast(string_index));
27402750
2741 try coff.symbol_table.strings_ni.resizeLeaf(gpa, &coff.mf, string_index + name.len + 1);
2742 const slice = coff.symbol_table.strings_ni.slice(&coff.mf);
2743 @memcpy(slice[@intCast(string_index)..][0..name.len], name);
2744 slice[@intCast(string_index + name.len)] = 0;
2751 try coff.symbol_table.strings_ni.resizeLeaf(gpa, &coff.mf, string_index + slice.len + 1);
2752 const storage = coff.symbol_table.strings_ni.slice(&coff.mf);
2753 @memcpy(storage[@intCast(string_index)..][0..slice.len], slice);
2754 storage[@intCast(string_index + slice.len)] = 0;
27452755 }
27462756
2747 break :name .{ .long = string_gop.value_ptr.* };
2748 } else .{ .short = name };
2757 return .{ .long = string_gop.value_ptr.* };
2758 } else {
2759 var symbol_name: SymbolTable.SymbolName = .{ .short = undefined };
2760 @memcpy(symbol_name.short[0..slice.len], slice);
2761 @memset(symbol_name.short[slice.len..], 0);
2762 return symbol_name;
2763 }
27492764}
27502765
27512766/// `len` does not include null terminators
......@@ -2776,37 +2791,43 @@ fn getOrPutStringAssumeCapacity(coff: *Coff, string: []const u8) String {
27762791 return @fromBackingInt(@intCast(gop.key_ptr.*));
27772792}
27782793
2779const GlobalOptions = struct {
2780 name: []const u8,
2781 lib_name: ?[]const u8 = null,
2782 type: Symbol.Type = .unknown,
2783 dll_storage_class: Symbol.DllStorageClass = .default,
2794const ExternLibName = union(enum) {
2795 // Symbol can be supplied from anywhere
2796 any,
2797 // Symbol can be supplied by any LibC input
2798 lib_c,
2799 // Symbol must be supplied by a specific input
2800 explicit: String,
27842801};
27852802
2803const FmtExternLibName = struct { coff: *Coff, lib_name: ExternLibName };
2804
2805fn fmtExternLibName(coff: *Coff, lib_name: ExternLibName) std.fmt.Alt(FmtExternLibName, formatExternLibName) {
2806 return .{ .data = .{ .coff = coff, .lib_name = lib_name } };
2807}
2808
2809fn formatExternLibName(data: FmtExternLibName, w: *Io.Writer) Io.Writer.Error!void {
2810 try w.writeAll(switch (data.lib_name) {
2811 .any => "*",
2812 .lib_c => "c",
2813 .explicit => |str| str.toSlice(data.coff),
2814 });
2815}
2816
27862817fn getOrPutGlobalSymbol(
27872818 coff: *Coff,
2788 opts: GlobalOptions,
2819 opts: struct {
2820 name: String,
2821 lib_name: ExternLibName = .any,
2822 type: Symbol.Type = .unknown,
2823 dll_storage_class: Symbol.DllStorageClass = .default,
2824 },
27892825) !std.array_hash_map.Auto(String, Global).GetOrPutResult {
27902826 const comp = coff.base.comp;
27912827 const gpa = comp.gpa;
27922828 try coff.symbols.ensureUnusedCapacity(gpa, 1);
27932829
2794 const lib_name: String.Optional = if (opts.lib_name) |lib_name| lib_name: {
2795 const is_libc = std.zig.target.isLibCLibName(&comp.root_mod.resolved_target.result, lib_name);
2796 if (is_libc) {
2797 // This is guaranteed by Sema.handleExternLibName
2798 if (!comp.config.link_libc) unreachable;
2799
2800 // TODO: The user has requested this symbol come from libc, but this logic allows
2801 // it to come from anywhere. We need to know what inputs are libc inputs,
2802 // and set a flag to only search them for this symbol.
2803 break :lib_name .none;
2804 }
2805
2806 break :lib_name (try coff.getOrPutString(lib_name)).toOptional();
2807 } else .none;
2808
2809 const sym_gop = try coff.globals.getOrPut(gpa, try coff.getOrPutString(opts.name));
2830 const sym_gop = try coff.globals.getOrPut(gpa, opts.name);
28102831 if (!sym_gop.found_existing) {
28112832 const si = coff.addSymbolAssumeCapacity();
28122833 const sym = si.get(coff);
......@@ -2815,11 +2836,11 @@ fn getOrPutGlobalSymbol(
28152836 sym.flags.dll_storage_class = opts.dll_storage_class;
28162837 sym_gop.value_ptr.* = .{
28172838 .si = si,
2818 .lib_name = lib_name,
2839 .lib_name = opts.lib_name,
28192840 };
28202841 coff.synth_prog_node.increaseEstimatedTotalItems(1);
28212842
2822 log.debug("globalSymbol({s}, {?s}) = {d}", .{ opts.name, opts.lib_name, si });
2843 log.debug("globalSymbol({s}, {f}) = {d}", .{ opts.name.toSlice(coff), fmtExternLibName(coff, opts.lib_name), si });
28232844 }
28242845
28252846 return sym_gop;
......@@ -2832,8 +2853,36 @@ fn getDefinedGlobal(coff: *Coff, name: []const u8) Symbol.Index {
28322853 return .null;
28332854}
28342855
2835pub fn globalSymbol(coff: *Coff, opts: GlobalOptions) !Symbol.Index {
2836 const gop = try coff.getOrPutGlobalSymbol(opts);
2856pub fn globalSymbol(coff: *Coff, opts: struct {
2857 name: []const u8,
2858 lib_name: ?[]const u8 = null,
2859 type: Symbol.Type = .unknown,
2860 dll_storage_class: Symbol.DllStorageClass = .default,
2861}) !Symbol.Index {
2862 const comp = coff.base.comp;
2863 const lib_name: ExternLibName = if (opts.lib_name) |lib_name| lib_name: {
2864 const is_libc = std.zig.target.isLibCLibName(
2865 &comp.root_mod.resolved_target.result,
2866 lib_name,
2867 );
2868
2869 if (is_libc) {
2870 // This is guaranteed by Sema.handleExternLibName
2871 if (!comp.config.link_libc) unreachable;
2872 break :lib_name .lib_c;
2873 }
2874
2875 try coff.ensureManyUnusedStringCapacity(2, opts.name.len + lib_name.len + 2);
2876 break :lib_name .{ .explicit = coff.getOrPutStringAssumeCapacity(lib_name) };
2877 } else .any;
2878
2879 try coff.ensureUnusedStringCapacity(opts.name.len);
2880 const gop = try coff.getOrPutGlobalSymbol(.{
2881 .name = coff.getOrPutStringAssumeCapacity(opts.name),
2882 .lib_name = lib_name,
2883 .type = opts.type,
2884 .dll_storage_class = opts.dll_storage_class,
2885 });
28372886 return gop.value_ptr.si;
28382887}
28392888
......@@ -3107,7 +3156,7 @@ fn ensureMemberSymbol(coff: *Coff, mi: Member.Index, name: String) !void {
31073156 coff.targetStore(coff.secondLinkerMemberNumSymbolsPtr(), @backingInt(mfli) + 1);
31083157 if (!needs_sort) {
31093158 @memmove(slice[new_header_size..][0..coff.lib_string_len], slice[old_header_size..][0..coff.lib_string_len]);
3110 @memcpy(slice[new_header_size + coff.lib_string_len ..][0..name_slice.len], name_slice[0..name_slice.len]);
3159 @memcpy(slice[new_header_size + coff.lib_string_len ..][0..name_slice.len], name_slice);
31113160 slice[new_header_size + coff.lib_string_len + name_slice.len] = 0;
31123161 } else if (!old_needs_sort) {
31133162 // The entire string table is rebuilt in flushMember after sorting
......@@ -3136,9 +3185,8 @@ fn flushSymbolTableEntry(coff: *Coff, index: u32) !void {
31363185 const entry = coff.symbolTableEntryPtr(sti.*) orelse entry: {
31373186 const symbol_name, const num_aux_symbols: u8, const complex_type: std.coff.ComplexType =
31383187 if (sym.gmi != .none) blk: {
3139 const name = sym.gmi.name(coff);
31403188 break :blk .{
3141 try coff.getOrPutSymbolName(name.toSlice(coff), name),
3189 try coff.getOrPutSymbolName(.{ .string = sym.gmi.name(coff) }),
31423190 @intFromBool(sym.flags.weak_external_strat != .none),
31433191 if (Symbol.Index.text.get(coff).section_number == sym.section_number)
31443192 .FUNCTION
......@@ -3147,7 +3195,7 @@ fn flushSymbolTableEntry(coff: *Coff, index: u32) !void {
31473195 };
31483196 } else blk: switch (coff.getNode(sym.ni.unwrap().?)) {
31493197 .image_section => .{
3150 try coff.getOrPutSymbolName(&sym.section_number.header(coff).name, null),
3198 try coff.getOrPutSymbolName(.{ .slice = &sym.section_number.header(coff).name }),
31513199 1,
31523200 .NULL,
31533201 },
......@@ -3156,7 +3204,7 @@ fn flushSymbolTableEntry(coff: *Coff, index: u32) !void {
31563204 const ip = &zcu.intern_pool;
31573205 const nav = ip.getNav(nmi.navIndex(coff));
31583206 break :blk .{
3159 try coff.getOrPutSymbolName(nav.fqn.toSlice(ip), null),
3207 try coff.getOrPutSymbolName(.{ .slice = nav.fqn.toSlice(ip) }),
31603208 0,
31613209 if (ip.isFunctionType(nav.resolved.?.type)) .FUNCTION else .NULL,
31623210 };
......@@ -3165,7 +3213,7 @@ fn flushSymbolTableEntry(coff: *Coff, index: u32) !void {
31653213 var name_buf: [std.fmt.count("__anon_{d}", .{std.math.maxInt(u32)})]u8 = undefined;
31663214 const name = std.mem.print(&name_buf, "__anon_{d}", .{umi}) catch unreachable;
31673215 break :blk .{
3168 try coff.getOrPutSymbolName(name, null),
3216 try coff.getOrPutSymbolName(.{ .slice = name }),
31693217 0,
31703218 .NULL,
31713219 };
......@@ -3179,9 +3227,8 @@ fn flushSymbolTableEntry(coff: *Coff, index: u32) !void {
31793227 lazy_sym.kind, mi,
31803228 }) catch unreachable;
31813229
3182 const string = try coff.getOrPutString(name);
31833230 break :blk .{
3184 try coff.getOrPutSymbolName(string.toSlice(coff), string),
3231 try coff.getOrPutSymbolName(.{ .slice = name }),
31853232 0,
31863233 if (tag == .lazy_code) .FUNCTION else .NULL,
31873234 };
......@@ -3412,8 +3459,8 @@ fn addSection(coff: *Coff, name: String, flags: std.coff.SectionHeader.Flags) !S
34123459 if (coff.targetEndian() != native_endian)
34133460 std.mem.byteSwapAllFields(std.coff.SectionHeader, section);
34143461
3415 const name_slice = name.toSlice(coff);
34163462 if (coff.isImage()) {
3463 const name_slice = name.toSlice(coff);
34173464 @memcpy(section.name[0..name_slice.len], name_slice);
34183465 @memset(section.name[name_slice.len..], 0);
34193466 switch (coff.optionalHeaderPtr()) {
......@@ -3423,7 +3470,7 @@ fn addSection(coff: *Coff, name: String, flags: std.coff.SectionHeader.Flags) !S
34233470 ),
34243471 }
34253472 } else {
3426 (try coff.getOrPutSymbolName(name_slice, name)).store(coff, &section.name);
3473 (try coff.getOrPutSymbolName(.{ .string = name })).store(coff, &section.name);
34273474 try coff.pendingSymbolTableEntry(si);
34283475 }
34293476
......@@ -3542,21 +3589,26 @@ fn objectSectionMapIndex(
35423589 attributes: ObjectSectionAttributes,
35433590) !Node.ObjectSectionMapIndex {
35443591 const gpa = coff.base.comp.gpa;
3545 const name_slice = name.toSlice(coff);
3546 // TODO: Should this be a section merge instead?
3547 const effective_attributes = if (coff.isImage() and std.mem.startsWith(u8, name_slice, ".tls")) attr: {
3548 // In images, the .tls section is a read-only template
3549 var attr = attributes;
3550 attr.write = false;
3551 break :attr attr;
3552 } else attributes;
35533592
35543593 const object_section_gop = try coff.object_section_table.getOrPut(gpa, name);
35553594 const osmi: Node.ObjectSectionMapIndex = @fromBackingInt(@intCast(object_section_gop.index));
35563595 const sym = if (!object_section_gop.found_existing) sym: {
3557 try coff.ensureUnusedStringCapacity(name_slice.len);
3558 const parent_name = coff.getOrPutStringAssumeCapacity(coff.objectSectionParentName(name_slice));
3559 const parent = (try coff.pseudoSectionMapIndex(parent_name, alignment, effective_attributes)).symbol(coff);
3596 const name_slice, const parent_name = blk: {
3597 var name_slice = name.toSlice(coff);
3598 const parent_name_slice = coff.objectSectionParentName(name_slice);
3599 const parent_name = if (parent_name_slice.len < name_slice.len) parent_name: {
3600 try coff.ensureUnusedStringCapacity(parent_name_slice.len);
3601 name_slice = name.toSlice(coff);
3602 break :parent_name coff.getOrPutStringAssumeCapacity(name_slice[0..parent_name_slice.len]);
3603 } else name;
3604
3605 break :blk .{ name_slice, parent_name };
3606 };
3607
3608 coff.string_bytes.lockPointers();
3609 defer coff.string_bytes.unlockPointers();
3610
3611 const parent = (try coff.pseudoSectionMapIndex(parent_name, alignment, attributes)).symbol(coff);
35603612 try coff.nodes.ensureUnusedCapacity(gpa, 1);
35613613 try coff.symbols.ensureUnusedCapacity(gpa, 1);
35623614 const parent_ni = parent.node(coff);
......@@ -3608,7 +3660,7 @@ fn objectSectionMapIndex(
36083660 name,
36093661 .object,
36103662 .fromFlags(sym.section_number.header(coff).flags),
3611 effective_attributes,
3663 attributes,
36123664 );
36133665
36143666 return osmi;
......@@ -4542,7 +4594,7 @@ fn loadObject(
45424594 },
45434595 .external => {
45444596 const global_gop = try coff.getOrPutGlobalSymbol(.{
4545 .name = symbol.name.toSlice(coff),
4597 .name = symbol.name,
45464598 });
45474599
45484600 // TODO: What if the same symbol is incorrectly defined twice in this obj?
......@@ -4749,7 +4801,7 @@ fn loadObject(
47494801 continue;
47504802 },
47514803 .weak_external => |alias_index| {
4752 const global_gop = try coff.getOrPutGlobalSymbol(.{ .name = symbol.name.toSlice(coff) });
4804 const global_gop = try coff.getOrPutGlobalSymbol(.{ .name = symbol.name });
47534805 symbol.si = global_gop.value_ptr.si;
47544806 if (!global_gop.found_existing or symbol.si.get(coff).ni == .none) {
47554807 const sym = symbol.si.get(coff);
......@@ -4788,7 +4840,7 @@ fn loadObject(
47884840 break :sym value;
47894841 },
47904842 .external => |value| {
4791 const global_gop = try coff.getOrPutGlobalSymbol(.{ .name = symbol.name.toSlice(coff) });
4843 const global_gop = try coff.getOrPutGlobalSymbol(.{ .name = symbol.name });
47924844 symbol.si = global_gop.value_ptr.si;
47934845 if (global_gop.found_existing)
47944846 return coff.failMultipleDefinitions(
......@@ -4824,7 +4876,7 @@ fn loadObject(
48244876 },
48254877 .external => {
48264878 assert(index != section.comdat_psi.unwrap());
4827 const global_gop = try coff.getOrPutGlobalSymbol(.{ .name = symbol.name.toSlice(coff) });
4879 const global_gop = try coff.getOrPutGlobalSymbol(.{ .name = symbol.name });
48284880 symbol.si = global_gop.value_ptr.si;
48294881
48304882 const sym = symbol.si.get(coff);
......@@ -4908,7 +4960,7 @@ fn loadObject(
49084960 assert(symbol.section_number == .UNDEFINED);
49094961 switch (symbol.value) {
49104962 .external => |size| {
4911 const global_gop = try coff.getOrPutGlobalSymbol(.{ .name = symbol.name.toSlice(coff) });
4963 const global_gop = try coff.getOrPutGlobalSymbol(.{ .name = symbol.name });
49124964 symbol.si = global_gop.value_ptr.si;
49134965 if (!global_gop.found_existing or symbol.si.get(coff).ni == .none) {
49144966 const sym = symbol.si.get(coff);
......@@ -5963,12 +6015,12 @@ fn resolve(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
59636015 break :task;
59646016 }
59656017 if (coff.pending_input) |pending_iami| {
5966 const name_slice = pending_iami.member(coff).name.toSlice(coff);
59676018 const sub_prog_node = coff.input_prog_node.start(
5968 name_slice,
6019 pending_iami.member(coff).name.toSlice(coff),
59696020 0,
59706021 );
59716022 defer sub_prog_node.end();
6023
59726024 coff.pending_input = null;
59736025 coff.flushInputMember(pending_iami) catch |err| switch (err) {
59746026 error.OutOfMemory => return error.OutOfMemory,
......@@ -5976,7 +6028,7 @@ fn resolve(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
59766028 "linker failed to load archive member '{f}{f}': {t}",
59776029 .{
59786030 pending_iami.member(coff).iai.path(coff),
5979 fmtMemberNameString(name_slice),
6031 fmtMemberNameString(pending_iami.member(coff).name.toSlice(coff)),
59806032 e,
59816033 },
59826034 ),
......@@ -6246,9 +6298,9 @@ fn aliasGlobal(coff: *Coff, gmi: Node.GlobalMapIndex, alias_si: Symbol.Index) !v
62466298 assert(sym.section_number == .UNDEFINED);
62476299 assert(sym.loc_relocs == .none);
62486300
6249 log.debug("aliasGlobal({s}, {?s}) {d}->{d} ({?s})", .{
6301 log.debug("aliasGlobal({s}, {f}) {d}->{d} ({?s})", .{
62506302 gmi.name(coff).toSlice(coff),
6251 gmi.libName(coff).toSlice(coff),
6303 fmtExternLibName(coff, gmi.libName(coff)),
62526304 si,
62536305 alias_si,
62546306 if (alias_sym.gmi != .none) alias_sym.gmi.name(coff).toSlice(coff) else null,
......@@ -6286,10 +6338,10 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
62866338 const si = gmi.symbol(coff);
62876339
62886340 log.debug(
6289 "flushGlobal({s}, {?s}) = n{d} {d}@{d}",
6341 "flushGlobal({s}, {f}) = n{d} {d}@{d}",
62906342 .{
62916343 name.toSlice(coff),
6292 gmi.libName(coff).toSlice(coff),
6344 fmtExternLibName(coff, gmi.libName(coff)),
62936345 si.get(coff).ni,
62946346 si,
62956347 si.get(coff).section_number,
......@@ -6322,16 +6374,18 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
63226374
63236375 const import: Import = import: {
63246376 const sym = si.get(coff);
6325 const name_slice = name.toSlice(coff);
6326 const imp_match = std.mem.startsWith(u8, name_slice, imp_prefix);
6377 const imp_match, const name_len = blk: {
6378 const name_slice = name.toSlice(coff);
6379 break :blk .{ std.mem.startsWith(u8, name_slice, imp_prefix), name_slice.len };
6380 };
63276381
63286382 // Globals may have the __imp_ prefix already if they are undef externals from another input.
63296383 assert(sym.flags.dll_storage_class != .dllexport);
63306384 const search_name, const is_imp = if (imp_match or sym.flags.dll_storage_class != .dllimport)
63316385 .{ name, imp_match }
63326386 else name: {
6333 try coff.ensureUnusedStringCapacity(imp_prefix.len + name_slice.len);
6334 const imp_name = try gpa.print(imp_prefix ++ "{s}", .{name_slice});
6387 try coff.ensureUnusedStringCapacity(imp_prefix.len + name_len);
6388 const imp_name = try gpa.print(imp_prefix ++ "{s}", .{name.toSlice(coff)});
63356389 defer gpa.free(imp_name);
63366390 break :name .{ coff.getOrPutStringAssumeCapacity(imp_name), true };
63376391 };
......@@ -6364,68 +6418,73 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
63646418 while (true) {
63656419 const archive_sym = &coff.input_archive_symbols.items[@backingInt(iter)];
63666420 const member = &coff.input_archive_members.items[@backingInt(archive_sym.iami)];
6367 member: switch (member.content) {
6368 .object => if (!member.flags.is_loaded) {
6369 if (gmi.libName(coff).unwrap()) |lib_name|
6370 if (!std.ascii.eqlIgnoreCase(
6371 lib_name.toSlice(coff),
6372 member.iai.path(coff).stem(),
6373 )) break :member;
6374
6375 // Try loading the input member and then retry.
6376 // This could still be a member containing imports
6377 // that use the older non-IMPORT_HEADER method.
6378 coff.pending_input = archive_sym.iami;
6379 return false;
6380 },
6381 .import => |import| {
6382 if (gmi.libName(coff).unwrap()) |lib_name|
6383 if (!std.ascii.eqlIgnoreCase(
6384 import.lib_name.toSlice(coff),
6385 lib_name.toSlice(coff),
6386 )) break :member;
6387
6388 const imp_name: String.Optional = name: switch (import.name_type) {
6389 .NAME,
6390 .NAME_NOPREFIX,
6391 .NAME_UNDECORATE,
6392 => |tag| {
6393 const symbol_name: []const u8 = import.symbol_name.toSlice(coff);
6394 const end_match = std.mem.endsWith(u8, name_slice, symbol_name);
6395 const len_delta = name_slice.len -% symbol_name.len;
6396 if (!end_match or
6397 (!imp_match and len_delta != 0) or
6398 (imp_match and len_delta != imp_prefix.len))
6399 return comp.link_diags.fail(
6400 "global '{s}' has mismatched symbol name in import header: '{s}'",
6401 .{
6402 name.toSlice(coff),
6403 import.symbol_name.toSlice(coff),
6404 },
6405 );
6406
6407 const imp_name = if (tag == .NAME) import.symbol_name else undecorated: {
6408 var imp_name = std.mem.trimStart(u8, symbol_name, "?@_");
6409 if (tag == .NAME_UNDECORATE)
6410 imp_name = std.mem.sliceTo(imp_name, '@');
6421 const lib_name_matches = match: switch (gmi.libName(coff)) {
6422 .any => true,
6423 .lib_c => {
6424 // TODO: Need a flag to indicate if this member is from a libc archive
6425 const is_libc_archive = true;
64116426
6412 try coff.ensureUnusedStringCapacity(imp_name.len);
6413 break :undecorated coff.getOrPutStringAssumeCapacity(imp_name);
6414 };
6427 break :match is_libc_archive;
6428 },
6429 .explicit => |lib_name| std.ascii.eqlIgnoreCase(
6430 lib_name.toSlice(coff),
6431 member.iai.path(coff).stem(),
6432 ),
6433 };
64156434
6416 break :name imp_name.toOptional();
6417 },
6418 .ORDINAL => break :name .none,
6419 else => |t| return comp.link_diags.fail("TODO handle name_type {t}", .{t}),
6420 };
6435 if (lib_name_matches) {
6436 switch (member.content) {
6437 .object => if (!member.flags.is_loaded) {
6438 // Try loading the input member and then retry.
6439 // This could still be a member containing imports
6440 // that use the older non-IMPORT_HEADER method.
6441 coff.pending_input = archive_sym.iami;
6442 return false;
6443 },
6444 .import => |import| {
6445 const imp_name: String.Optional = name: switch (import.name_type) {
6446 .NAME,
6447 .NAME_NOPREFIX,
6448 .NAME_UNDECORATE,
6449 => |tag| {
6450 const symbol_name: []const u8 = import.symbol_name.toSlice(coff);
6451 const name_slice = name.toSlice(coff);
6452 const end_match = std.mem.endsWith(u8, name_slice, symbol_name);
6453 const len_delta = name_slice.len -% symbol_name.len;
6454 if (!end_match or
6455 (!imp_match and len_delta != 0) or
6456 (imp_match and len_delta != imp_prefix.len))
6457 return comp.link_diags.fail(
6458 "global '{s}' has mismatched symbol name in import header: '{s}'",
6459 .{
6460 name.toSlice(coff),
6461 import.symbol_name.toSlice(coff),
6462 },
6463 );
6464
6465 const imp_name = if (tag == .NAME) import.symbol_name else undecorated: {
6466 var imp_name = std.mem.trimStart(u8, symbol_name, "?@_");
6467 if (tag == .NAME_UNDECORATE)
6468 imp_name = std.mem.sliceTo(imp_name, '@');
6469
6470 try coff.ensureUnusedStringCapacity(imp_name.len);
6471 break :undecorated coff.getOrPutStringAssumeCapacity(imp_name);
6472 };
6473
6474 break :name imp_name.toOptional();
6475 },
6476 .ORDINAL => break :name .none,
6477 else => |t| return comp.link_diags.fail("TODO handle name_type {t}", .{t}),
6478 };
64216479
6422 break :import .{
6423 .lib_name = import.lib_name,
6424 .name = imp_name,
6425 .ordinal_hint = import.import_ordinal_hint,
6426 .kind = if (import.type == .CODE and !is_imp) .thunk else .iat_ptr,
6427 };
6428 },
6480 break :import .{
6481 .lib_name = import.lib_name,
6482 .name = imp_name,
6483 .ordinal_hint = import.import_ordinal_hint,
6484 .kind = if (import.type == .CODE and !is_imp) .thunk else .iat_ptr,
6485 };
6486 },
6487 }
64296488 }
64306489
64316490 if (archive_sym.next == iter) break;
......@@ -6442,7 +6501,7 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
64426501 // Convert an unresolved weak external that itself refers to an undef external
64436502 // into a (possibly new) global, so it can be resolved separately.
64446503 const alias_gop = try coff.getOrPutGlobalSymbol(.{
6445 .name = sym.value.weak_alias_name.toSlice(coff),
6504 .name = sym.value.weak_alias_name,
64466505 });
64476506 try coff.aliasGlobal(gmi, alias_gop.value_ptr.si);
64486507 return true;
......@@ -6462,12 +6521,15 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
64626521 // This is necessary for certain ntdll symbols, such as LdrRegisterDllNotification,
64636522 // which are not in the implib.
64646523 if (sym.flags.type != .unknown) {
6465 if (gmi.libName(coff).unwrap()) |lib_name| break :import .{
6466 .lib_name = lib_name,
6467 .name = name.toOptional(),
6468 .ordinal_hint = 0,
6469 .kind = if (sym.flags.type == .code) .thunk else .iat_ptr,
6470 };
6524 switch (gmi.libName(coff)) {
6525 .explicit => |lib_name| break :import .{
6526 .lib_name = lib_name,
6527 .name = name.toOptional(),
6528 .ordinal_hint = 0,
6529 .kind = if (sym.flags.type == .code) .thunk else .iat_ptr,
6530 },
6531 else => {},
6532 }
64716533 }
64726534
64736535 return true;
......@@ -6478,10 +6540,10 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
64786540
64796541 const target_endian = coff.targetEndian();
64806542 const addr_info = coff.targetAddrInfo();
6481 const lib_name = import.lib_name.toSlice(coff);
6543 const lib_name_slice = import.lib_name.toSlice(coff);
64826544 const gop = try coff.import_table.entries.getOrPutAdapted(
64836545 gpa,
6484 lib_name,
6546 lib_name_slice,
64856547 ImportTable.Adapter{ .coff = coff },
64866548 );
64876549 const import_hint_name_align: Alignment = .@"2";
......@@ -6493,7 +6555,7 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
64936555 @sizeOf(std.coff.ImportDirectoryEntry) * (gop.index + 2),
64946556 );
64956557 const import_hint_name_table_len =
6496 import_hint_name_align.forward(lib_name.len + ".dll".len + 1);
6558 import_hint_name_align.forward(lib_name_slice.len + ".dll".len + 1);
64976559 const idata_section_ni = coff.import_table.ni.parent(&coff.mf).unwrap().?;
64986560 const import_lookup_table_ni = try idata_section_ni.addFloatingChild(gpa, &coff.mf, .{
64996561 .size = addr_info.size * 2,
......@@ -6528,9 +6590,9 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
65286590 .hint_name_len = @intCast(import_hint_name_table_len),
65296591 };
65306592 const import_hint_name_slice = import_hint_name_table_ni.slice(&coff.mf);
6531 @memcpy(import_hint_name_slice[0..lib_name.len], lib_name);
6532 @memcpy(import_hint_name_slice[lib_name.len..][0..".dll".len], ".dll");
6533 @memset(import_hint_name_slice[lib_name.len + ".dll".len ..], 0);
6593 @memcpy(import_hint_name_slice[0..lib_name_slice.len], lib_name_slice);
6594 @memcpy(import_hint_name_slice[lib_name_slice.len..][0..".dll".len], ".dll");
6595 @memset(import_hint_name_slice[lib_name_slice.len + ".dll".len ..], 0);
65346596 coff.nodes.appendAssumeCapacity(.{ .import_lookup_table = @fromBackingInt(@intCast(gop.index)) });
65356597 coff.nodes.appendAssumeCapacity(.{ .import_address_table = @fromBackingInt(@intCast(gop.index)) });
65366598 coff.nodes.appendAssumeCapacity(.{ .import_hint_name_table = @fromBackingInt(@intCast(gop.index)) });
......@@ -6555,7 +6617,7 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
65556617
65566618 log.debug(
65576619 "flushGlobalImport({s}, {?s}, {d}, {s})",
6558 .{ name.toSlice(coff), import.name.toSlice(coff), import.ordinal_hint, lib_name },
6620 .{ name.toSlice(coff), import.name.toSlice(coff), import.ordinal_hint, lib_name_slice },
65596621 );
65606622
65616623 const iat_symbol_gop = try coff.import_table.iat_symbol_indices.getOrPut(gpa, .{
......@@ -7662,8 +7724,10 @@ fn fmtGlobalName(coff: *Coff, gmi: Node.GlobalMapIndex) std.fmt.Alt(FmtGlobalNam
76627724fn globalNameEscape(data: FmtGlobalName, w: *std.Io.Writer) std.Io.Writer.Error!void {
76637725 if (data.gmi == .none) return;
76647726 try w.writeAll(data.gmi.name(data.coff).toSlice(data.coff));
7665 if (data.gmi.libName(data.coff).unwrap()) |lib_name|
7666 try w.print("({s})", .{lib_name.toSlice(data.coff)});
7727 switch (data.gmi.libName(data.coff)) {
7728 .explicit => |lib_name| try w.print(" ({s}.dll)", .{lib_name.toSlice(data.coff)}),
7729 else => {},
7730 }
76677731}
76687732
76697733fn printNodeName(
......@@ -7705,12 +7769,7 @@ fn printNodeName(
77057769 inline .pseudo_section, .object_section => |smi| try w.print("({s})", .{
77067770 smi.name(coff).toSlice(coff),
77077771 }),
7708 .import_thunk,
7709 => |gmi| {
7710 try w.writeByte('(');
7711 if (gmi.libName(coff).toSlice(coff)) |lib_name| try w.print("{s}.dll, ", .{lib_name});
7712 try w.print("{s})", .{gmi.name(coff).toSlice(coff)});
7713 },
7772 .import_thunk => |gmi| try w.print("({f})", .{fmtGlobalName(coff, gmi)}),
77147773 .nav => |nmi| {
77157774 const zcu = coff.base.comp.zcu.?;
77167775 const ip = &zcu.intern_pool;
......@@ -7736,11 +7795,7 @@ fn printNodeName(
77367795 }),
77377796 .builtin => |si| {
77387797 const sym = si.get(coff);
7739 if (sym.gmi != .none) {
7740 try w.writeByte('(');
7741 if (sym.gmi.libName(coff).toSlice(coff)) |lib_name| try w.print("{s}.dll, ", .{lib_name});
7742 try w.print("{s})", .{sym.gmi.name(coff).toSlice(coff)});
7743 }
7798 if (sym.gmi != .none) try w.print("({f})", .{fmtGlobalName(coff, sym.gmi)});
77447799 },
77457800 }
77467801}
src/main.zig+2-8
......@@ -4440,12 +4440,8 @@ fn serve(
44404440 continue;
44414441 }
44424442
4443 if (comp.config.output_mode == .Exe) {
4444 try comp.makeBinFileWritable();
4445 }
4446
4443 try comp.makeBinFileWritable();
44474444 try comp.update(main_progress_node);
4448
44494445 try comp.makeBinFileExecutable();
44504446 try serveUpdateResults(&server, comp);
44514447 },
......@@ -4476,9 +4472,7 @@ fn serve(
44764472 try comp.hotCodeSwap(main_progress_node, pid);
44774473 try serveUpdateResults(&server, comp);
44784474 } else {
4479 if (comp.config.output_mode == .Exe) {
4480 try comp.makeBinFileWritable();
4481 }
4475 try comp.makeBinFileWritable();
44824476 try comp.update(main_progress_node);
44834477 try comp.makeBinFileExecutable();
44844478 try serveUpdateResults(&server, comp);