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;...@@ -226,3 +226,7 @@ pub extern "c" fn pthread_cond_destroy(cond: *pthread_cond_t) c_int;
226226
227pub const pthread_t = *@OpaqueType();227pub const pthread_t = *@OpaqueType();
228pub const FILE = @OpaqueType();228pub 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...@@ -98,3 +98,10 @@ const __SIZEOF_PTHREAD_MUTEX_T = if (builtin.os == .fuchsia) 40 else switch (bui
98 },98 },
99 else => unreachable,99 else => unreachable,
100};100};
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;...@@ -7,11 +7,13 @@ const assert = std.debug.assert;
7const testing = std.testing;7const testing = std.testing;
8const elf = std.elf;8const elf = std.elf;
9const windows = std.os.windows;9const windows = std.os.windows;
10const system = std.os.system;
10const maxInt = std.math.maxInt;11const maxInt = std.math.maxInt;
1112
12pub const DynLib = switch (builtin.os) {13pub const DynLib = switch (builtin.os) {
13 .linux => LinuxDynLib,14 .linux => if (builtin.link_libc) DlDynlib else LinuxDynLib,
14 .windows => WindowsDynLib,15 .windows => WindowsDynLib,
16 .macosx, .tvos, .watchos, .ios => DlDynlib,
15 else => void,17 else => void,
16};18};
1719
...@@ -99,12 +101,14 @@ pub fn linkmap_iterator(phdrs: []elf.Phdr) !LinkMap.Iterator {...@@ -99,12 +101,14 @@ pub fn linkmap_iterator(phdrs: []elf.Phdr) !LinkMap.Iterator {
99}101}
100102
101pub const LinuxDynLib = struct {103pub const LinuxDynLib = struct {
104 pub const Error = ElfLib.Error;
105
102 elf_lib: ElfLib,106 elf_lib: ElfLib,
103 fd: i32,107 fd: i32,
104 memory: []align(mem.page_size) u8,108 memory: []align(mem.page_size) u8,
105109
106 /// Trusts the file110 /// Trusts the file
107 pub fn open(path: []const u8) !DynLib {111 pub fn open(path: []const u8) !LinuxDynLib {
108 const fd = try os.open(path, 0, os.O_RDONLY | os.O_CLOEXEC);112 const fd = try os.open(path, 0, os.O_RDONLY | os.O_CLOEXEC);
109 errdefer os.close(fd);113 errdefer os.close(fd);
110114
...@@ -121,25 +125,43 @@ pub const LinuxDynLib = struct {...@@ -121,25 +125,43 @@ pub const LinuxDynLib = struct {
121 );125 );
122 errdefer os.munmap(bytes);126 errdefer os.munmap(bytes);
123127
124 return DynLib{128 return LinuxDynLib{
125 .elf_lib = try ElfLib.init(bytes),129 .elf_lib = try ElfLib.init(bytes),
126 .fd = fd,130 .fd = fd,
127 .memory = bytes,131 .memory = bytes,
128 };132 };
129 }133 }
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 {
132 os.munmap(self.memory);140 os.munmap(self.memory);
133 os.close(self.fd);141 os.close(self.fd);
134 self.* = undefined;142 self.* = undefined;
135 }143 }
136144
137 pub fn lookup(self: *DynLib, name: []const u8) ?usize {145 pub fn lookup(self: *LinuxDynLib, comptime T: type, name: [:0]const u8) ?T {
138 return self.elf_lib.lookup("", name);146 if (self.elf_lib.lookup("", name)) |symbol| {
147 return @intToPtr(T, symbol);
148 } else {
149 return null;
150 }
139 }151 }
140};152};
141153
142pub const ElfLib = struct {154pub const ElfLib = struct {
155 pub const Error = error{
156 NotElfFile,
157 NotDynamicLibrary,
158 MissingDynamicLinkingInformation,
159 BaseNotFound,
160 ElfStringSectionNotFound,
161 ElfSymSectionNotFound,
162 ElfHashTableNotFound,
163 };
164
143 strings: [*:0]u8,165 strings: [*:0]u8,
144 syms: [*]elf.Sym,166 syms: [*]elf.Sym,
145 hashtab: [*]os.Elf_Symndx,167 hashtab: [*]os.Elf_Symndx,
...@@ -245,13 +267,24 @@ fn checkver(def_arg: *elf.Verdef, vsym_arg: i32, vername: []const u8, strings: [...@@ -245,13 +267,24 @@ fn checkver(def_arg: *elf.Verdef, vsym_arg: i32, vername: []const u8, strings: [
245}267}
246268
247pub const WindowsDynLib = struct {269pub const WindowsDynLib = struct {
270 pub const Error = error{FileNotFound};
271
248 dll: windows.HMODULE,272 dll: windows.HMODULE,
249273
250 pub fn open(path: []const u8) !WindowsDynLib {274 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 {
253 return WindowsDynLib{285 return WindowsDynLib{
254 .dll = try windows.LoadLibraryW(&wpath),286 // + 4 to skip over the \??\
287 .dll = try windows.LoadLibraryW(path_w + 4),
255 };288 };
256 }289 }
257290
...@@ -260,8 +293,46 @@ pub const WindowsDynLib = struct {...@@ -260,8 +293,46 @@ pub const WindowsDynLib = struct {
260 self.* = undefined;293 self.* = undefined;
261 }294 }
262295
263 pub fn lookup(self: *WindowsDynLib, name: []const u8) ?usize {296 pub fn lookup(self: *WindowsDynLib, comptime T: type, name: [:0]const u8) ?T {
264 return @ptrToInt(windows.kernel32.GetProcAddress(self.dll, name.ptr));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 }
265 }336 }
266};337};
267338
...@@ -269,12 +340,12 @@ test "dynamic_library" {...@@ -269,12 +340,12 @@ test "dynamic_library" {
269 const libname = switch (builtin.os) {340 const libname = switch (builtin.os) {
270 .linux => "invalid_so.so",341 .linux => "invalid_so.so",
271 .windows => "invalid_dll.dll",342 .windows => "invalid_dll.dll",
272 else => return,343 .macosx, .tvos, .watchos, .ios => "invalid_dylib.dylib",
344 else => return error.SkipZigTest,
273 };345 };
274346
275 const dynlib = DynLib.open(libname) catch |err| {347 const dynlib = DynLib.open(libname) catch |err| {
276 testing.expect(err == error.FileNotFound);348 testing.expect(err == error.FileNotFound);
277 return;349 return;
278 };350 };
279 @panic("Expected error from function");
280}351}
lib/std/os/bits/darwin.zig+14
...@@ -1183,6 +1183,7 @@ pub fn S_ISSOCK(m: u32) bool {...@@ -1183,6 +1183,7 @@ pub fn S_ISSOCK(m: u32) bool {
1183pub fn S_IWHT(m: u32) bool {1183pub fn S_IWHT(m: u32) bool {
1184 return m & S_IFMT == S_IFWHT;1184 return m & S_IFMT == S_IFWHT;
1185}1185}
1186
1186pub const HOST_NAME_MAX = 72;1187pub const HOST_NAME_MAX = 72;
11871188
1188pub const AT_FDCWD = -2;1189pub const AT_FDCWD = -2;
...@@ -1209,3 +1210,16 @@ pub const addrinfo = extern struct {...@@ -1209,3 +1210,16 @@ pub const addrinfo = extern struct {
1209 addr: ?*sockaddr,1210 addr: ?*sockaddr,
1210 next: ?*addrinfo,1211 next: ?*addrinfo,
1211};1212};
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...@@ -6561,6 +6561,7 @@ static IrInstruction *ir_gen_prefix_op_id(IrBuilder *irb, Scope *scope, AstNode
6561}6561}
65626562
6563static IrInstruction *ir_expr_wrap(IrBuilder *irb, Scope *scope, IrInstruction *inst, ResultLoc *result_loc) {6563static IrInstruction *ir_expr_wrap(IrBuilder *irb, Scope *scope, IrInstruction *inst, ResultLoc *result_loc) {
6564 if (inst == irb->codegen->invalid_instruction) return inst;
6564 ir_build_end_expr(irb, scope, inst->source_node, inst, result_loc);6565 ir_build_end_expr(irb, scope, inst->source_node, inst, result_loc);
6565 return inst;6566 return inst;
6566}6567}
test/standalone.zig+3-7
...@@ -1,6 +1,5 @@...@@ -1,6 +1,5 @@
1const std = @import("std");
1const tests = @import("tests.zig");2const tests = @import("tests.zig");
2const builtin = @import("builtin");
3const is_windows = builtin.os == builtin.Os.windows;
43
5pub fn addCases(cases: *tests.StandaloneContext) void {4pub fn addCases(cases: *tests.StandaloneContext) void {
6 cases.add("test/standalone/hello_world/hello.zig");5 cases.add("test/standalone/hello_world/hello.zig");
...@@ -19,13 +18,10 @@ pub fn addCases(cases: *tests.StandaloneContext) void {...@@ -19,13 +18,10 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
19 cases.addBuildFile("test/standalone/use_alias/build.zig");18 cases.addBuildFile("test/standalone/use_alias/build.zig");
20 cases.addBuildFile("test/standalone/brace_expansion/build.zig");19 cases.addBuildFile("test/standalone/brace_expansion/build.zig");
21 cases.addBuildFile("test/standalone/empty_env/build.zig");20 cases.addBuildFile("test/standalone/empty_env/build.zig");
22 if (builtin.os == builtin.Os.linux) {21 if (std.Target.current.getOs() != .wasi) {
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
25 cases.addBuildFile("test/standalone/load_dynamic_library/build.zig");22 cases.addBuildFile("test/standalone/load_dynamic_library/build.zig");
26 }23 }
2724 if (std.Target.current.getArch() == .x86_64) { // TODO add C ABI support for other architectures
28 if (builtin.arch == builtin.Arch.x86_64) { // TODO add C ABI support for other architectures
29 cases.addBuildFile("test/stage1/c_abi/build.zig");25 cases.addBuildFile("test/stage1/c_abi/build.zig");
30 }26 }
31}27}
test/standalone/load_dynamic_library/main.zig+1-2
...@@ -9,8 +9,7 @@ pub fn main() !void {...@@ -9,8 +9,7 @@ pub fn main() !void {
9 var lib = try std.DynLib.open(dynlib_name);9 var lib = try std.DynLib.open(dynlib_name);
10 defer lib.close();10 defer lib.close();
1111
12 const addr = lib.lookup("add") orelse return error.SymbolNotFound;12 const addFn = lib.lookup(extern fn (i32, i32) i32, "add") orelse return error.SymbolNotFound;
13 const addFn = @intToPtr(extern fn (i32, i32) i32, addr);
1413
15 const result = addFn(12, 34);14 const result = addFn(12, 34);
16 std.debug.assert(result == 46);15 std.debug.assert(result == 46);