Skip to content

Commit 0d99744

Browse files
committed
Merge branch 'emekoi-dynlib-load'
closes #2598
2 parents 9561e7c + fdc3132 commit 0d99744

File tree

7 files changed

+113
-21
lines changed

7 files changed

+113
-21
lines changed

lib/std/c.zig

+4
Original file line numberDiff line numberDiff line change
@@ -226,3 +226,7 @@ pub extern "c" fn pthread_cond_destroy(cond: *pthread_cond_t) c_int;
226226

227227
pub const pthread_t = *@OpaqueType();
228228
pub const FILE = @OpaqueType();
229+
230+
pub extern "c" fn dlopen(path: [*:0]const u8, mode: c_int) ?*c_void;
231+
pub extern "c" fn dlclose(handle: *c_void) c_int;
232+
pub extern "c" fn dlsym(handle: ?*c_void, symbol: [*:0]const u8) ?*c_void;

lib/std/c/linux.zig

+7
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,10 @@ const __SIZEOF_PTHREAD_MUTEX_T = if (builtin.os == .fuchsia) 40 else switch (bui
9898
},
9999
else => unreachable,
100100
};
101+
102+
pub const RTLD_LAZY = 1;
103+
pub const RTLD_NOW = 2;
104+
pub const RTLD_NOLOAD = 4;
105+
pub const RTLD_NODELETE = 4096;
106+
pub const RTLD_GLOBAL = 256;
107+
pub const RTLD_LOCAL = 0;

lib/std/dynamic_library.zig

+83-12
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ const assert = std.debug.assert;
77
const testing = std.testing;
88
const elf = std.elf;
99
const windows = std.os.windows;
10+
const system = std.os.system;
1011
const maxInt = std.math.maxInt;
1112

1213
pub const DynLib = switch (builtin.os) {
13-
.linux => LinuxDynLib,
14+
.linux => if (builtin.link_libc) DlDynlib else LinuxDynLib,
1415
.windows => WindowsDynLib,
16+
.macosx, .tvos, .watchos, .ios => DlDynlib,
1517
else => void,
1618
};
1719

@@ -99,12 +101,14 @@ pub fn linkmap_iterator(phdrs: []elf.Phdr) !LinkMap.Iterator {
99101
}
100102

101103
pub const LinuxDynLib = struct {
104+
pub const Error = ElfLib.Error;
105+
102106
elf_lib: ElfLib,
103107
fd: i32,
104108
memory: []align(mem.page_size) u8,
105109

106110
/// Trusts the file
107-
pub fn open(path: []const u8) !DynLib {
111+
pub fn open(path: []const u8) !LinuxDynLib {
108112
const fd = try os.open(path, 0, os.O_RDONLY | os.O_CLOEXEC);
109113
errdefer os.close(fd);
110114

@@ -121,25 +125,43 @@ pub const LinuxDynLib = struct {
121125
);
122126
errdefer os.munmap(bytes);
123127

124-
return DynLib{
128+
return LinuxDynLib{
125129
.elf_lib = try ElfLib.init(bytes),
126130
.fd = fd,
127131
.memory = bytes,
128132
};
129133
}
130134

131-
pub fn close(self: *DynLib) void {
135+
pub fn openC(path_c: [*:0]const u8) !LinuxDynLib {
136+
return open(mem.toSlice(u8, path_c));
137+
}
138+
139+
pub fn close(self: *LinuxDynLib) void {
132140
os.munmap(self.memory);
133141
os.close(self.fd);
134142
self.* = undefined;
135143
}
136144

137-
pub fn lookup(self: *DynLib, name: []const u8) ?usize {
138-
return self.elf_lib.lookup("", name);
145+
pub fn lookup(self: *LinuxDynLib, comptime T: type, name: [:0]const u8) ?T {
146+
if (self.elf_lib.lookup("", name)) |symbol| {
147+
return @intToPtr(T, symbol);
148+
} else {
149+
return null;
150+
}
139151
}
140152
};
141153

142154
pub const ElfLib = struct {
155+
pub const Error = error{
156+
NotElfFile,
157+
NotDynamicLibrary,
158+
MissingDynamicLinkingInformation,
159+
BaseNotFound,
160+
ElfStringSectionNotFound,
161+
ElfSymSectionNotFound,
162+
ElfHashTableNotFound,
163+
};
164+
143165
strings: [*:0]u8,
144166
syms: [*]elf.Sym,
145167
hashtab: [*]os.Elf_Symndx,
@@ -245,13 +267,24 @@ fn checkver(def_arg: *elf.Verdef, vsym_arg: i32, vername: []const u8, strings: [
245267
}
246268

247269
pub const WindowsDynLib = struct {
270+
pub const Error = error{FileNotFound};
271+
248272
dll: windows.HMODULE,
249273

250274
pub fn open(path: []const u8) !WindowsDynLib {
251-
const wpath = try windows.sliceToPrefixedFileW(path);
275+
const path_w = try windows.sliceToPrefixedFileW(path);
276+
return openW(&path_w);
277+
}
278+
279+
pub fn openC(path_c: [*:0]const u8) !WindowsDynLib {
280+
const path_w = try windows.cStrToPrefixedFileW(path);
281+
return openW(&path_w);
282+
}
252283

284+
pub fn openW(path_w: [*:0]const u16) !WindowsDynLib {
253285
return WindowsDynLib{
254-
.dll = try windows.LoadLibraryW(&wpath),
286+
// + 4 to skip over the \??\
287+
.dll = try windows.LoadLibraryW(path_w + 4),
255288
};
256289
}
257290

@@ -260,21 +293,59 @@ pub const WindowsDynLib = struct {
260293
self.* = undefined;
261294
}
262295

263-
pub fn lookup(self: *WindowsDynLib, name: []const u8) ?usize {
264-
return @ptrToInt(windows.kernel32.GetProcAddress(self.dll, name.ptr));
296+
pub fn lookup(self: *WindowsDynLib, comptime T: type, name: [:0]const u8) ?T {
297+
if (windows.kernel32.GetProcAddress(self.dll, name.ptr)) |addr| {
298+
return @ptrCast(T, addr);
299+
} else {
300+
return null;
301+
}
302+
}
303+
};
304+
305+
pub const DlDynlib = struct {
306+
pub const Error = error{FileNotFound};
307+
308+
handle: *c_void,
309+
310+
pub fn open(path: []const u8) !DlDynlib {
311+
const path_c = try os.toPosixPath(path);
312+
return openC(&path_c);
313+
}
314+
315+
pub fn openC(path_c: [*:0]const u8) !DlDynlib {
316+
return DlDynlib{
317+
.handle = system.dlopen(path_c, system.RTLD_LAZY) orelse {
318+
return error.FileNotFound;
319+
},
320+
};
321+
}
322+
323+
pub fn close(self: *DlDynlib) void {
324+
_ = system.dlclose(self.handle);
325+
self.* = undefined;
326+
}
327+
328+
pub fn lookup(self: *DlDynlib, comptime T: type, name: [:0]const u8) ?T {
329+
// dlsym (and other dl-functions) secretly take shadow parameter - return address on stack
330+
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66826
331+
if (@call(.{ .modifier = .never_tail }, system.dlsym, .{ self.handle, name.ptr })) |symbol| {
332+
return @ptrCast(T, symbol);
333+
} else {
334+
return null;
335+
}
265336
}
266337
};
267338

268339
test "dynamic_library" {
269340
const libname = switch (builtin.os) {
270341
.linux => "invalid_so.so",
271342
.windows => "invalid_dll.dll",
272-
else => return,
343+
.macosx, .tvos, .watchos, .ios => "invalid_dylib.dylib",
344+
else => return error.SkipZigTest,
273345
};
274346

275347
const dynlib = DynLib.open(libname) catch |err| {
276348
testing.expect(err == error.FileNotFound);
277349
return;
278350
};
279-
@panic("Expected error from function");
280351
}

lib/std/os/bits/darwin.zig

+14
Original file line numberDiff line numberDiff line change
@@ -1183,6 +1183,7 @@ pub fn S_ISSOCK(m: u32) bool {
11831183
pub fn S_IWHT(m: u32) bool {
11841184
return m & S_IFMT == S_IFWHT;
11851185
}
1186+
11861187
pub const HOST_NAME_MAX = 72;
11871188

11881189
pub const AT_FDCWD = -2;
@@ -1209,3 +1210,16 @@ pub const addrinfo = extern struct {
12091210
addr: ?*sockaddr,
12101211
next: ?*addrinfo,
12111212
};
1213+
1214+
pub const RTLD_LAZY = 0x1;
1215+
pub const RTLD_NOW = 0x2;
1216+
pub const RTLD_LOCAL = 0x4;
1217+
pub const RTLD_GLOBAL = 0x8;
1218+
pub const RTLD_NOLOAD = 0x10;
1219+
pub const RTLD_NODELETE = 0x80;
1220+
pub const RTLD_FIRST = 0x100;
1221+
1222+
pub const RTLD_NEXT = @intToPtr(*c_void, ~maxInt(usize));
1223+
pub const RTLD_DEFAULT = @intToPtr(*c_void, ~maxInt(usize) - 1);
1224+
pub const RTLD_SELF = @intToPtr(*c_void, ~maxInt(usize) - 2);
1225+
pub const RTLD_MAIN_ONLY = @intToPtr(*c_void, ~maxInt(usize) - 4);

src/ir.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -6561,6 +6561,7 @@ static IrInstruction *ir_gen_prefix_op_id(IrBuilder *irb, Scope *scope, AstNode
65616561
}
65626562

65636563
static IrInstruction *ir_expr_wrap(IrBuilder *irb, Scope *scope, IrInstruction *inst, ResultLoc *result_loc) {
6564+
if (inst == irb->codegen->invalid_instruction) return inst;
65646565
ir_build_end_expr(irb, scope, inst->source_node, inst, result_loc);
65656566
return inst;
65666567
}

test/standalone.zig

+3-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1+
const std = @import("std");
12
const tests = @import("tests.zig");
2-
const builtin = @import("builtin");
3-
const is_windows = builtin.os == builtin.Os.windows;
43

54
pub fn addCases(cases: *tests.StandaloneContext) void {
65
cases.add("test/standalone/hello_world/hello.zig");
@@ -19,13 +18,10 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
1918
cases.addBuildFile("test/standalone/use_alias/build.zig");
2019
cases.addBuildFile("test/standalone/brace_expansion/build.zig");
2120
cases.addBuildFile("test/standalone/empty_env/build.zig");
22-
if (builtin.os == builtin.Os.linux) {
23-
// TODO hook up the DynLib API for windows using LoadLibraryA
24-
// TODO figure out how to make this work on darwin - probably libSystem has dlopen/dlsym in it
21+
if (std.Target.current.getOs() != .wasi) {
2522
cases.addBuildFile("test/standalone/load_dynamic_library/build.zig");
2623
}
27-
28-
if (builtin.arch == builtin.Arch.x86_64) { // TODO add C ABI support for other architectures
24+
if (std.Target.current.getArch() == .x86_64) { // TODO add C ABI support for other architectures
2925
cases.addBuildFile("test/stage1/c_abi/build.zig");
3026
}
3127
}

test/standalone/load_dynamic_library/main.zig

+1-2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@ pub fn main() !void {
99
var lib = try std.DynLib.open(dynlib_name);
1010
defer lib.close();
1111

12-
const addr = lib.lookup("add") orelse return error.SymbolNotFound;
13-
const addFn = @intToPtr(extern fn (i32, i32) i32, addr);
12+
const addFn = lib.lookup(extern fn (i32, i32) i32, "add") orelse return error.SymbolNotFound;
1413

1514
const result = addFn(12, 34);
1615
std.debug.assert(result == 46);

0 commit comments

Comments
 (0)