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:21:00-04:00
log01db3d49535d59c70de042c60ed524bca72e31f2
tree50f5e54da4541d5cc67044075e6c9dd321f05201
parent2b48ec1e720b1def48c0a89b35e8f60f3edb38be

Coff: add support for outputing implibs

This initial implementation used the existing mingw/implib functionality, and so builds the implib completely during flush(). The intended solution for this would be to build the implib with a MappedFile, similar to the main linker output. Added two new permutations to test/standalone/shared_library that exercise the `!use_llvm` path.

2 files changed, 97 insertions(+), 5 deletions(-)

src/link/Coff.zig+88-1
......@@ -17,6 +17,8 @@ const target_util = @import("../target.zig");
1717const Type = @import("../Type.zig");
1818const Value = @import("../Value.zig");
1919const Zcu = @import("../Zcu.zig");
20const ModuleDefinition = @import("../libs/mingw/def.zig").ModuleDefinition;
21const implib = @import("../libs/mingw/implib.zig");
2022
2123base: link.File,
2224mf: MappedFile,
......@@ -287,6 +289,7 @@ pub const ExportTable = struct {
287289 pending_sort: bool = false,
288290
289291 pub const Entry = struct {
292 si: Symbol.Index,
290293 name_index: u32,
291294 name_len: u32,
292295 export_address_table_ri: Reloc.Index,
......@@ -1888,6 +1891,81 @@ pub fn updateErrorData(coff: *Coff, pt: Zcu.PerThread) !void {
18881891 };
18891892}
18901893
1894fn flushImplib(
1895 coff: *Coff,
1896 implib_file: []const u8,
1897) !void {
1898 // Emitting implibs is only valid for images
1899 assert(coff.export_table.ni != .none);
1900
1901 const comp = coff.base.comp;
1902 const gpa = comp.gpa;
1903 const io = comp.io;
1904
1905 const image_name = std.mem.sliceTo(
1906 coff.export_table.ni.slice(&coff.mf)[@sizeOf(std.coff.ExportDirectoryTable)..],
1907 0,
1908 );
1909 const machine_type = coff.targetLoad(&coff.headerPtr().machine);
1910 const members = members: {
1911 const def_arena: std.heap.ArenaAllocator = .init(gpa);
1912 var def: ModuleDefinition = .{
1913 .name = image_name,
1914 .arena = def_arena,
1915 .type = .mingw,
1916 };
1917 defer def.deinit();
1918
1919 try def.exports.ensureUnusedCapacity(
1920 def.arena.allocator(),
1921 coff.export_table.entries.count(),
1922 );
1923
1924 const name_table_slice = coff.export_table.name_table_ni.slice(&coff.mf);
1925 for (coff.export_table.entries.values(), 0..) |entry, ord| {
1926 const name = name_table_slice[entry.name_index..][0..entry.name_len];
1927 const section_number = entry.si.get(coff).section_number;
1928 const import_type: std.coff.ImportType = switch (section_number.symbol(coff)) {
1929 .data, .rdata => .DATA,
1930 .text => .CODE,
1931 else => return comp.link_diags.fail(
1932 "unsupported section for export '{s}': {s}",
1933 .{ name, &section_number.header(coff).name },
1934 ),
1935 };
1936
1937 def.exports.appendAssumeCapacity(.{
1938 .name = name,
1939 .mangled_symbol_name = null,
1940 .ext_name = null,
1941 .import_name = null,
1942 .export_as = null,
1943 .no_name = false,
1944 .ordinal = @intCast(ord),
1945 .type = import_type,
1946 .private = false,
1947 });
1948 }
1949
1950 def.fixupForImportLibraryGeneration(machine_type);
1951 break :members try implib.getMembers(gpa, def, machine_type);
1952 };
1953 defer members.deinit();
1954
1955 const lib_sub_path = try std.fs.path.join(gpa, &.{
1956 std.fs.path.dirname(coff.base.emit.sub_path) orelse "",
1957 implib_file,
1958 });
1959 defer gpa.free(lib_sub_path);
1960
1961 const lib_final_file = try coff.base.emit.root_dir.handle.createFile(io, lib_sub_path, .{ .truncate = true });
1962 defer lib_final_file.close(io);
1963 var buffer: [1024]u8 = undefined;
1964 var file_writer = lib_final_file.writer(io, &buffer);
1965 try implib.writeCoffArchive(gpa, &file_writer.interface, members);
1966 try file_writer.interface.flush();
1967}
1968
18911969pub fn flush(
18921970 coff: *Coff,
18931971 arena: std.mem.Allocator,
......@@ -1898,11 +1976,18 @@ pub fn flush(
18981976 _ = prog_node;
18991977 while (try coff.idle(tid)) {}
19001978
1901 // hack for stage2_x86_64 + coff
19021979 const comp = coff.base.comp;
1980
1981 // Implib generation should instead be done via building a MappedFile progressively
1982 if (comp.emit_implib) |implib_file|
1983 coff.flushImplib(implib_file) catch |err|
1984 return comp.link_diags.fail("flushing implib '{s}' failed: {t}", .{ implib_file, err });
1985
1986 // hack for stage2_x86_64 + coff
19031987 if (comp.compiler_rt_dyn_lib) |crt_file| {
19041988 const gpa = comp.gpa;
19051989 const io = comp.io;
1990
19061991 const compiler_rt_sub_path = try std.fs.path.join(gpa, &.{
19071992 std.fs.path.dirname(coff.base.emit.sub_path) orelse "",
19081993 std.fs.path.basename(crt_file.full_object_path.sub_path),
......@@ -2670,6 +2755,7 @@ fn updateExportsInner(
26702755 );
26712756
26722757 gop.value_ptr.* = .{
2758 .si = export_si,
26732759 .name_index = @intCast(name_index),
26742760 .name_len = @intCast(name.len),
26752761 .export_address_table_ri = @enumFromInt(coff.relocs.items.len),
......@@ -2683,6 +2769,7 @@ fn updateExportsInner(
26832769 .{ .AMD64 = .ADDR32NB },
26842770 );
26852771 } else {
2772 gop.value_ptr.si = export_si;
26862773 const reloc = gop.value_ptr.*.export_address_table_ri.get(coff);
26872774 reloc.target = export_si;
26882775 export_si.applyTargetRelocs(coff);
test/standalone/shared_library/build.zig+9-4
......@@ -7,11 +7,15 @@ pub fn build(b: *std.Build) void {
77 const optimize: std.builtin.OptimizeMode = .Debug;
88 const target = b.standardTargetOptions(.{});
99
10 const exe_names: []const []const u8 = &.{ "test", "test-dync" };
11 const lib_names: []const []const u8 = &.{ "mathtest", "mathtest-dync" };
12 const lib_link_libc: []const bool = &.{ false, true };
10 const exe_names: []const []const u8 = &.{ "test", "test-dync", "test-no-llvm", "test-no-llvm-dync" };
11 const lib_names: []const []const u8 = &.{ "mathtest", "mathtest-dync", "mathtest-no-llvm", "mathtest-no-llvm-dync" };
12 const lib_link_libc: []const bool = &.{ false, true, false, true };
13 const lib_use_llvm: []const bool = &.{ true, true, false, false };
14
15 for (exe_names, lib_names, lib_link_libc, lib_use_llvm) |exe_name, lib_name, dyn_libc, use_llvm| {
16 if (target.result.os.tag == .windows and target.result.abi == .gnu and dyn_libc and !use_llvm)
17 continue; // TODO: sub-compilation of compiler_rt failed (failed to link with LLD: LibCInstallationNotAvailable)
1318
14 for (exe_names, lib_names, lib_link_libc) |exe_name, lib_name, dyn_libc| {
1519 const lib = b.addLibrary(.{
1620 .linkage = .dynamic,
1721 .name = lib_name,
......@@ -22,6 +26,7 @@ pub fn build(b: *std.Build) void {
2226 .optimize = optimize,
2327 .link_libc = dyn_libc,
2428 }),
29 .use_llvm = use_llvm,
2530 });
2631
2732 const exe = b.addExecutable(.{