authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-12-10 13:24:01-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-12-10 13:24:01-05:00
log0d99744acc63ff44091f9664888b788c29b7c72a
tree378399b2b3b2b6c07aced240a83d7b361cc26109
parent9561e7c6b9fb2d9ebcbfd611196db698372ae7bd
parentfdc31321268214fbb2b0832b0d269cea56639e98
signaturelock-open Commit is signed but in an unrecognized format.

Merge branch 'emekoi-dynlib-load'

closes #2598

7 files changed, 113 insertions(+), 21 deletions(-)

lib/std/c.zig+4
......@@ -226,3 +226,7 @@ pub extern "c" fn pthread_cond_destroy(cond: *pthread_cond_t) c_int;
226226
227227pub const pthread_t = *@OpaqueType();
228228pub const FILE = @OpaqueType();
229
230pub extern "c" fn dlopen(path: [*:0]const u8, mode: c_int) ?*c_void;
231pub extern "c" fn dlclose(handle: *c_void) c_int;
232pub extern "c" fn dlsym(handle: ?*c_void, symbol: [*:0]const u8) ?*c_void;
lib/std/c/linux.zig+7
......@@ -98,3 +98,10 @@ const __SIZEOF_PTHREAD_MUTEX_T = if (builtin.os == .fuchsia) 40 else switch (bui
9898 },
9999 else => unreachable,
100100};
101
102pub const RTLD_LAZY = 1;
103pub const RTLD_NOW = 2;
104pub const RTLD_NOLOAD = 4;
105pub const RTLD_NODELETE = 4096;
106pub const RTLD_GLOBAL = 256;
107pub const RTLD_LOCAL = 0;
lib/std/dynamic_library.zig+83-12
......@@ -7,11 +7,13 @@ const assert = std.debug.assert;
77const testing = std.testing;
88const elf = std.elf;
99const windows = std.os.windows;
10const system = std.os.system;
1011const maxInt = std.math.maxInt;
1112
1213pub 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
101103pub 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
142154pub 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
247269pub 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,8 +293,46 @@ 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
305pub 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
......@@ -269,12 +340,12 @@ 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
......@@ -1183,6 +1183,7 @@ pub fn S_ISSOCK(m: u32) bool {
11831183pub fn S_IWHT(m: u32) bool {
11841184 return m & S_IFMT == S_IFWHT;
11851185}
1186
11861187pub const HOST_NAME_MAX = 72;
11871188
11881189pub const AT_FDCWD = -2;
......@@ -1209,3 +1210,16 @@ pub const addrinfo = extern struct {
12091210 addr: ?*sockaddr,
12101211 next: ?*addrinfo,
12111212};
1213
1214pub const RTLD_LAZY = 0x1;
1215pub const RTLD_NOW = 0x2;
1216pub const RTLD_LOCAL = 0x4;
1217pub const RTLD_GLOBAL = 0x8;
1218pub const RTLD_NOLOAD = 0x10;
1219pub const RTLD_NODELETE = 0x80;
1220pub const RTLD_FIRST = 0x100;
1221
1222pub const RTLD_NEXT = @intToPtr(*c_void, ~maxInt(usize));
1223pub const RTLD_DEFAULT = @intToPtr(*c_void, ~maxInt(usize) - 1);
1224pub const RTLD_SELF = @intToPtr(*c_void, ~maxInt(usize) - 2);
1225pub const RTLD_MAIN_ONLY = @intToPtr(*c_void, ~maxInt(usize) - 4);
src/ir.cpp+1
......@@ -6561,6 +6561,7 @@ static IrInstruction *ir_gen_prefix_op_id(IrBuilder *irb, Scope *scope, AstNode
65616561}
65626562
65636563static 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
......@@ -1,6 +1,5 @@
1const std = @import("std");
12const tests = @import("tests.zig");
2const builtin = @import("builtin");
3const is_windows = builtin.os == builtin.Os.windows;
43
54pub 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
......@@ -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);