frontend: fix linking to Windows DLLs as system libs

This commit is contained in:
Andrew Kelley 2023-08-01 17:43:09 -07:00
parent a08cc7d2ae
commit a1e21ceec8
3 changed files with 85 additions and 39 deletions

View File

@ -5618,6 +5618,11 @@ pub fn addLinkLib(comp: *Compilation, lib_name: []const u8) !void {
// to queue up a work item to produce the DLL import library for this. // to queue up a work item to produce the DLL import library for this.
const gop = try comp.bin_file.options.system_libs.getOrPut(comp.gpa, lib_name); const gop = try comp.bin_file.options.system_libs.getOrPut(comp.gpa, lib_name);
if (!gop.found_existing and comp.getTarget().os.tag == .windows) { if (!gop.found_existing and comp.getTarget().os.tag == .windows) {
gop.value_ptr.* = .{
.needed = true,
.weak = false,
.path = undefined,
};
try comp.work_queue.writeItem(.{ try comp.work_queue.writeItem(.{
.windows_import_lib = comp.bin_file.options.system_libs.count() - 1, .windows_import_lib = comp.bin_file.options.system_libs.count() - 1,
}); });

View File

@ -28,6 +28,7 @@ const target_util = @import("target.zig");
const crash_report = @import("crash_report.zig"); const crash_report = @import("crash_report.zig");
const Module = @import("Module.zig"); const Module = @import("Module.zig");
const AstGen = @import("AstGen.zig"); const AstGen = @import("AstGen.zig");
const mingw = @import("mingw.zig");
const Server = std.zig.Server; const Server = std.zig.Server;
pub const std_options = struct { pub const std_options = struct {
@ -477,6 +478,8 @@ const usage_build_generic =
\\ -needed-l[lib], Link against system library (even if unused) \\ -needed-l[lib], Link against system library (even if unused)
\\ --needed-library [lib] \\ --needed-library [lib]
\\ -L[d], --library-directory [d] Add a directory to the library search path \\ -L[d], --library-directory [d] Add a directory to the library search path
\\ -search_paths_first Search each library search path for dynamic libs then static libs
\\ -search_dylibs_first Search for dynamic libs in each library search path, then static libs.
\\ -T[script], --script [script] Use a custom linker script \\ -T[script], --script [script] Use a custom linker script
\\ --version-script [path] Provide a version .map file \\ --version-script [path] Provide a version .map file
\\ --dynamic-linker [path] Set the dynamic interpreter path (usually ld.so) \\ --dynamic-linker [path] Set the dynamic interpreter path (usually ld.so)
@ -537,8 +540,6 @@ const usage_build_generic =
\\ -install_name=[value] (Darwin) add dylib's install name \\ -install_name=[value] (Darwin) add dylib's install name
\\ --entitlements [path] (Darwin) add path to entitlements file for embedding in code signature \\ --entitlements [path] (Darwin) add path to entitlements file for embedding in code signature
\\ -pagezero_size [value] (Darwin) size of the __PAGEZERO segment in hexadecimal notation \\ -pagezero_size [value] (Darwin) size of the __PAGEZERO segment in hexadecimal notation
\\ -search_paths_first (Darwin) search each dir in library search paths for `libx.dylib` then `libx.a`
\\ -search_dylibs_first (Darwin) search `libx.dylib` in each dir in library search paths, then `libx.a`
\\ -headerpad [value] (Darwin) set minimum space for future expansion of the load commands in hexadecimal notation \\ -headerpad [value] (Darwin) set minimum space for future expansion of the load commands in hexadecimal notation
\\ -headerpad_max_install_names (Darwin) set enough space as if all paths were MAXPATHLEN \\ -headerpad_max_install_names (Darwin) set enough space as if all paths were MAXPATHLEN
\\ -dead_strip (Darwin) remove functions and data that are unreachable by the entry point or exported symbols \\ -dead_strip (Darwin) remove functions and data that are unreachable by the entry point or exported symbols
@ -2567,6 +2568,34 @@ fn buildOutputType(
} }
lib_dir_args = undefined; // From here we use lib_dirs instead. lib_dir_args = undefined; // From here we use lib_dirs instead.
const self_exe_path: ?[]const u8 = if (!process.can_spawn)
null
else
introspect.findZigExePath(arena) catch |err| {
fatal("unable to find zig self exe path: {s}", .{@errorName(err)});
};
var zig_lib_directory: Compilation.Directory = d: {
if (override_lib_dir) |unresolved_lib_dir| {
const lib_dir = try introspect.resolvePath(arena, unresolved_lib_dir);
break :d .{
.path = lib_dir,
.handle = fs.cwd().openDir(lib_dir, .{}) catch |err| {
fatal("unable to open zig lib directory '{s}': {s}", .{ lib_dir, @errorName(err) });
},
};
} else if (builtin.os.tag == .wasi) {
break :d getWasiPreopen("/lib");
} else if (self_exe_path) |p| {
break :d introspect.findZigLibDirFromSelfExe(arena, p) catch |err| {
fatal("unable to find zig installation directory: {s}", .{@errorName(err)});
};
} else {
unreachable;
}
};
defer zig_lib_directory.handle.close();
// Now that we have target info, we can find out if any of the system libraries // Now that we have target info, we can find out if any of the system libraries
// are part of libc or libc++. We remove them from the list and communicate their // are part of libc or libc++. We remove them from the list and communicate their
// existence via flags instead. // existence via flags instead.
@ -2612,6 +2641,25 @@ fn buildOutputType(
}, },
} }
if (target_info.target.os.tag == .windows) {
const exists = mingw.libExists(arena, target_info.target, zig_lib_directory, lib_name) catch |err| {
fatal("failed to check zig installation for DLL import libs: {s}", .{
@errorName(err),
});
};
if (exists) {
try resolved_system_libs.append(arena, .{
.name = lib_name,
.lib = .{
.needed = true,
.weak = false,
.path = undefined,
},
});
continue;
}
}
if (fs.path.isAbsolute(lib_name)) { if (fs.path.isAbsolute(lib_name)) {
fatal("cannot use absolute path as a system library: {s}", .{lib_name}); fatal("cannot use absolute path as a system library: {s}", .{lib_name});
} }
@ -2758,9 +2806,13 @@ fn buildOutputType(
if (failed_libs.items.len > 0) { if (failed_libs.items.len > 0) {
for (failed_libs.items) |f| { for (failed_libs.items) |f| {
const searched_paths = if (f.checked_paths.len == 0) " none" else f.checked_paths;
std.log.err("unable to find {s} system library '{s}' using strategy '{s}'. searched paths:{s}", .{ std.log.err("unable to find {s} system library '{s}' using strategy '{s}'. searched paths:{s}", .{
@tagName(f.preferred_mode), f.name, @tagName(f.strategy), f.checked_paths, @tagName(f.preferred_mode), f.name, @tagName(f.strategy), searched_paths,
}); });
if (f.preferred_mode == .Dynamic and f.strategy == .no_fallback) {
std.log.info("to link statically, pass the library as a positional argument", .{});
}
} }
process.exit(1); process.exit(1);
} }
@ -3079,35 +3131,6 @@ fn buildOutputType(
} }
} }
const self_exe_path: ?[]const u8 = if (!process.can_spawn)
null
else
introspect.findZigExePath(arena) catch |err| {
fatal("unable to find zig self exe path: {s}", .{@errorName(err)});
};
var zig_lib_directory: Compilation.Directory = d: {
if (override_lib_dir) |unresolved_lib_dir| {
const lib_dir = try introspect.resolvePath(arena, unresolved_lib_dir);
break :d .{
.path = lib_dir,
.handle = fs.cwd().openDir(lib_dir, .{}) catch |err| {
fatal("unable to open zig lib directory '{s}': {s}", .{ lib_dir, @errorName(err) });
},
};
} else if (builtin.os.tag == .wasi) {
break :d getWasiPreopen("/lib");
} else if (self_exe_path) |p| {
break :d introspect.findZigLibDirFromSelfExe(arena, p) catch |err| {
fatal("unable to find zig installation directory: {s}", .{@errorName(err)});
};
} else {
unreachable;
}
};
defer zig_lib_directory.handle.close();
var thread_pool: ThreadPool = undefined; var thread_pool: ThreadPool = undefined;
try thread_pool.init(.{ .allocator = gpa }); try thread_pool.init(.{ .allocator = gpa });
defer thread_pool.deinit(); defer thread_pool.deinit();

View File

@ -283,7 +283,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
defer arena_allocator.deinit(); defer arena_allocator.deinit();
const arena = arena_allocator.allocator(); const arena = arena_allocator.allocator();
const def_file_path = findDef(comp, arena, lib_name) catch |err| switch (err) { const def_file_path = findDef(arena, comp.getTarget(), comp.zig_lib_directory, lib_name) catch |err| switch (err) {
error.FileNotFound => { error.FileNotFound => {
log.debug("no {s}.def file available to make a DLL import {s}.lib", .{ lib_name, lib_name }); log.debug("no {s}.def file available to make a DLL import {s}.lib", .{ lib_name, lib_name });
// In this case we will end up putting foo.lib onto the linker line and letting the linker // In this case we will end up putting foo.lib onto the linker line and letting the linker
@ -431,10 +431,28 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
}); });
} }
/// This function body is verbose but all it does is test 3 different paths and see if a .def file exists. pub fn libExists(
fn findDef(comp: *Compilation, allocator: Allocator, lib_name: []const u8) ![]u8 { allocator: Allocator,
const target = comp.getTarget(); target: std.Target,
zig_lib_directory: Cache.Directory,
lib_name: []const u8,
) !bool {
const s = findDef(allocator, target, zig_lib_directory, lib_name) catch |err| switch (err) {
error.FileNotFound => return false,
else => |e| return e,
};
defer allocator.free(s);
return true;
}
/// This function body is verbose but all it does is test 3 different paths and
/// see if a .def file exists.
fn findDef(
allocator: Allocator,
target: std.Target,
zig_lib_directory: Cache.Directory,
lib_name: []const u8,
) ![]u8 {
const lib_path = switch (target.cpu.arch) { const lib_path = switch (target.cpu.arch) {
.x86 => "lib32", .x86 => "lib32",
.x86_64 => "lib64", .x86_64 => "lib64",
@ -451,7 +469,7 @@ fn findDef(comp: *Compilation, allocator: Allocator, lib_name: []const u8) ![]u8
{ {
// Try the archtecture-specific path first. // Try the archtecture-specific path first.
const fmt_path = "libc" ++ s ++ "mingw" ++ s ++ "{s}" ++ s ++ "{s}.def"; const fmt_path = "libc" ++ s ++ "mingw" ++ s ++ "{s}" ++ s ++ "{s}.def";
if (comp.zig_lib_directory.path) |p| { if (zig_lib_directory.path) |p| {
try override_path.writer().print("{s}" ++ s ++ fmt_path, .{ p, lib_path, lib_name }); try override_path.writer().print("{s}" ++ s ++ fmt_path, .{ p, lib_path, lib_name });
} else { } else {
try override_path.writer().print(fmt_path, .{ lib_path, lib_name }); try override_path.writer().print(fmt_path, .{ lib_path, lib_name });
@ -468,7 +486,7 @@ fn findDef(comp: *Compilation, allocator: Allocator, lib_name: []const u8) ![]u8
// Try the generic version. // Try the generic version.
override_path.shrinkRetainingCapacity(0); override_path.shrinkRetainingCapacity(0);
const fmt_path = "libc" ++ s ++ "mingw" ++ s ++ "lib-common" ++ s ++ "{s}.def"; const fmt_path = "libc" ++ s ++ "mingw" ++ s ++ "lib-common" ++ s ++ "{s}.def";
if (comp.zig_lib_directory.path) |p| { if (zig_lib_directory.path) |p| {
try override_path.writer().print("{s}" ++ s ++ fmt_path, .{ p, lib_name }); try override_path.writer().print("{s}" ++ s ++ fmt_path, .{ p, lib_name });
} else { } else {
try override_path.writer().print(fmt_path, .{lib_name}); try override_path.writer().print(fmt_path, .{lib_name});
@ -485,7 +503,7 @@ fn findDef(comp: *Compilation, allocator: Allocator, lib_name: []const u8) ![]u8
// Try the generic version and preprocess it. // Try the generic version and preprocess it.
override_path.shrinkRetainingCapacity(0); override_path.shrinkRetainingCapacity(0);
const fmt_path = "libc" ++ s ++ "mingw" ++ s ++ "lib-common" ++ s ++ "{s}.def.in"; const fmt_path = "libc" ++ s ++ "mingw" ++ s ++ "lib-common" ++ s ++ "{s}.def.in";
if (comp.zig_lib_directory.path) |p| { if (zig_lib_directory.path) |p| {
try override_path.writer().print("{s}" ++ s ++ fmt_path, .{ p, lib_name }); try override_path.writer().print("{s}" ++ s ++ fmt_path, .{ p, lib_name });
} else { } else {
try override_path.writer().print(fmt_path, .{lib_name}); try override_path.writer().print(fmt_path, .{lib_name});