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:
agra
2026-06-12 16:56:35 +03:00
parent 2a2f43eada
commit 0bd8f3e5ce
5 changed files with 61 additions and 20 deletions

View File

@@ -80,6 +80,9 @@ pub fn cSourceCacheKey(
/// Handle returned from loadCObjectsForJIT — caller must call unload() after JIT.
pub const CImportHandle = struct {
dylib_handle: ?*anyopaque = null,
/// Where the unit's linked dylib lives for THIS run; the JIT adds it
/// as a priority symbol-search target ahead of the process images.
dylib_path: ?[:0]const u8 = null,
temp_paths: []const []const u8 = &.{},
allocator: std.mem.Allocator,
@@ -415,10 +418,12 @@ pub fn loadCObjectsForJIT(
var temp_paths = std.ArrayList([]const u8).empty;
// Write each .o buffer to a temp file
// Write each .o buffer to a temp file (per-pid names: concurrent
// `sx run` processes must not clobber each other's link inputs)
const pid = std.c.getpid();
var obj_paths = std.ArrayList([]const u8).empty;
for (obj_bufs, 0..) |buf, i| {
const path = try std.fmt.allocPrint(allocator, "/tmp/sx_c_{d}.o", .{i});
const path = try std.fmt.allocPrint(allocator, "/tmp/sx_c_{d}_{d}.o", .{ pid, i });
const start = c.LLVMGetBufferStart(buf);
const size = c.LLVMGetBufferSize(buf);
const data = @as([*]const u8, @ptrCast(start))[0..size];
@@ -432,8 +437,8 @@ pub fn loadCObjectsForJIT(
}
// Link into a shared library
const dylib_path = "/tmp/sx_c_import.dylib";
try temp_paths.append(allocator, try allocator.dupe(u8, dylib_path));
const dylib_path = try std.fmt.allocPrintSentinel(allocator, "/tmp/sx_c_import_{d}.dylib", .{pid}, 0);
try temp_paths.append(allocator, dylib_path);
var argv = std.ArrayList([]const u8).empty;
try argv.append(allocator, "cc");
@@ -477,6 +482,7 @@ pub fn loadCObjectsForJIT(
return .{
.dylib_handle = handle,
.dylib_path = dylib_path,
.temp_paths = try temp_paths.toOwnedSlice(allocator),
.allocator = allocator,
};