feat(C2): unit-first JIT symbol resolution — program-owned dylibs beat process images
runJITFromObject now takes priority dylibs (the #import c unit's linked objects first, then #library deps in declaration order) and attaches a per-path search generator for each AHEAD of the process-wide fallback, so a vendored symbol can never lose to a same-named export of an image the host process happens to carry (libz via LLVM, libsqlite3 via CoreServices). loadLibrary reports the name dlopen succeeded on; the c-import handle records its dylib path; temp link inputs are per-pid so concurrent runs can't clobber each other. Flips the C0.3 shadowing pin to from_unit: true.
This commit is contained in:
27
src/main.zig
27
src/main.zig
@@ -270,15 +270,21 @@ pub fn main(init: std.process.Init) !void {
|
||||
defer c_handle.unload(io);
|
||||
timer.record("c-import");
|
||||
|
||||
// dlopen #library dependencies so JIT can resolve foreign symbols
|
||||
// dlopen #library dependencies so JIT can resolve foreign symbols.
|
||||
// Program-owned dylibs (the #import c unit first, then #library
|
||||
// deps in declaration order) also become PRIORITY search targets
|
||||
// for the JIT, consulted before the process-wide fallback.
|
||||
const libs = extractLibraries(allocator, root) catch std.process.exit(1);
|
||||
var lib_handles = std.ArrayList(*anyopaque).empty;
|
||||
var priority_dylibs = std.ArrayList([:0]const u8).empty;
|
||||
if (c_handle.dylib_path) |cp| priority_dylibs.append(allocator, cp) catch {};
|
||||
defer {
|
||||
for (lib_handles.items) |h| _ = std.c.dlclose(h);
|
||||
}
|
||||
for (libs) |lib_name| {
|
||||
if (loadLibrary(allocator, lib_name, target_config.lib_paths)) |handle| {
|
||||
lib_handles.append(allocator, handle) catch {};
|
||||
if (loadLibrary(allocator, lib_name, target_config.lib_paths)) |loaded| {
|
||||
lib_handles.append(allocator, loaded.handle) catch {};
|
||||
priority_dylibs.append(allocator, loaded.path) catch {};
|
||||
} else {
|
||||
const e = std.c.dlerror();
|
||||
if (e) |msg| std.debug.print("warning: could not load library '{s}': {s}\n", .{ lib_name, std.mem.span(msg) });
|
||||
@@ -304,7 +310,7 @@ pub fn main(init: std.process.Init) !void {
|
||||
const marker = "--- build done ---\n";
|
||||
_ = std.c.write(1, marker.ptr, marker.len);
|
||||
}
|
||||
const exit_code = sx.target.runJITFromObject(obj_buf) catch {
|
||||
const exit_code = sx.target.runJITFromObject(obj_buf, priority_dylibs.items) catch {
|
||||
// JIT failed — fall back to AOT
|
||||
timer.record("jit-fail");
|
||||
runAOT(allocator, io, path, target_config, &timer, enable_cache, stdlib_paths) catch std.process.exit(1);
|
||||
@@ -924,8 +930,15 @@ fn extractFrameworks(allocator: std.mem.Allocator, root: *const sx.ast.Node) ![]
|
||||
return try fws.toOwnedSlice(allocator);
|
||||
}
|
||||
|
||||
const LoadedLibrary = struct {
|
||||
handle: *anyopaque,
|
||||
/// The name dlopen succeeded on (full path or bare name) — reused
|
||||
/// verbatim as the JIT's priority search target for this library.
|
||||
path: [:0]const u8,
|
||||
};
|
||||
|
||||
/// Try to dlopen a library by name, searching user paths, host paths, and common naming conventions.
|
||||
fn loadLibrary(allocator: std.mem.Allocator, lib_name: []const u8, user_lib_paths: []const []const u8) ?*anyopaque {
|
||||
fn loadLibrary(allocator: std.mem.Allocator, lib_name: []const u8, user_lib_paths: []const []const u8) ?LoadedLibrary {
|
||||
const is_macos = comptime @import("builtin").os.tag == .macos;
|
||||
const suffixes: []const []const u8 = if (is_macos) &.{ ".dylib", ".so" } else &.{ ".so", ".dylib" };
|
||||
|
||||
@@ -944,7 +957,7 @@ fn loadLibrary(allocator: std.mem.Allocator, lib_name: []const u8, user_lib_path
|
||||
for (paths) |dir| {
|
||||
for (suffixes) |sfx| {
|
||||
const full = std.fmt.allocPrintSentinel(allocator, "{s}/lib{s}{s}", .{ dir, lib_name, sfx }, 0) catch continue;
|
||||
if (std.c.dlopen(full.ptr, .{ .NOW = true })) |h| return h;
|
||||
if (std.c.dlopen(full.ptr, .{ .NOW = true })) |h| return .{ .handle = h, .path = full };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -952,7 +965,7 @@ fn loadLibrary(allocator: std.mem.Allocator, lib_name: []const u8, user_lib_path
|
||||
// Fallback: bare name (let dlopen search its default paths)
|
||||
for (suffixes) |sfx| {
|
||||
const bare = std.fmt.allocPrintSentinel(allocator, "lib{s}{s}", .{ lib_name, sfx }, 0) catch continue;
|
||||
if (std.c.dlopen(bare.ptr, .{ .NOW = true })) |h| return h;
|
||||
if (std.c.dlopen(bare.ptr, .{ .NOW = true })) |h| return .{ .handle = h, .path = bare };
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
Reference in New Issue
Block a user