diff --git a/std/c/darwin.zig b/std/c/darwin.zig index f2e8120a0e87..c21134ff64cf 100644 --- a/std/c/darwin.zig +++ b/std/c/darwin.zig @@ -56,3 +56,7 @@ pub fn sigaddset(set: *sigset_t, signo: u5) void { } pub extern "c" fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) c_int; + +pub extern "c" fn dlopen(path: [*]const u8, mode: c_int) ?*c_void; +pub extern "c" fn dlclose(handle: *c_void) c_int; +pub extern "c" fn dlsym(handle: ?*c_void, symbol: [*]const u8) ?*c_void; diff --git a/std/dynamic_library.zig b/std/dynamic_library.zig index 341378801970..5ca276bb43e7 100644 --- a/std/dynamic_library.zig +++ b/std/dynamic_library.zig @@ -7,11 +7,13 @@ const assert = std.debug.assert; const testing = std.testing; const elf = std.elf; const windows = std.os.windows; +const system = std.os.system; const maxInt = std.math.maxInt; pub const DynLib = switch (builtin.os) { - .linux => LinuxDynLib, + .linux => if (builtin.link_libc) DlDynlib else LinuxDynLib, .windows => WindowsDynLib, + .macosx, .tvos, .watchos, .ios => DlDynlib, else => void, }; @@ -99,12 +101,14 @@ pub fn linkmap_iterator(phdrs: []elf.Phdr) !LinkMap.Iterator { } pub const LinuxDynLib = struct { + pub const Error = ElfLib.Error; + elf_lib: ElfLib, fd: i32, memory: []align(mem.page_size) u8, /// Trusts the file - pub fn open(path: []const u8) !DynLib { + pub fn open(path: []const u8) !LinuxDynLib { const fd = try os.open(path, 0, os.O_RDONLY | os.O_CLOEXEC); errdefer os.close(fd); @@ -121,25 +125,39 @@ pub const LinuxDynLib = struct { ); errdefer os.munmap(bytes); - return DynLib{ + return LinuxDynLib{ .elf_lib = try ElfLib.init(bytes), .fd = fd, .memory = bytes, }; } - pub fn close(self: *DynLib) void { + pub fn close(self: *LinuxDynLib) void { os.munmap(self.memory); os.close(self.fd); self.* = undefined; } - pub fn lookup(self: *DynLib, name: []const u8) ?usize { - return self.elf_lib.lookup("", name); + pub fn lookup(self: *LinuxDynLib, comptime T: type, name: []const u8) ?T { + if (self.elf_lib.lookup("", name)) |symbol| { + return @ptrCast(T, symbol); + } else { + return null; + } } }; pub const ElfLib = struct { + pub const Error = error{ + NotElfFile, + NotDynamicLibrary, + MissingDynamicLinkingInformation, + BaseNotFound, + ElfStringSectionNotFound, + ElfSymSectionNotFound, + ElfHashTableNotFound, + }; + strings: [*]u8, syms: [*]elf.Sym, hashtab: [*]os.Elf_Symndx, @@ -245,11 +263,12 @@ fn checkver(def_arg: *elf.Verdef, vsym_arg: i32, vername: []const u8, strings: [ } pub const WindowsDynLib = struct { + pub const Error = error{FileNotFound}; + dll: windows.HMODULE, pub fn open(path: []const u8) !WindowsDynLib { const wpath = try windows.sliceToPrefixedFileW(path); - return WindowsDynLib{ .dll = try windows.LoadLibraryW(&wpath), }; @@ -260,8 +279,57 @@ pub const WindowsDynLib = struct { self.* = undefined; } - pub fn lookup(self: *WindowsDynLib, name: []const u8) ?usize { - return @ptrToInt(windows.kernel32.GetProcAddress(self.dll, name.ptr)); + pub fn lookupC(self: *WindowsDynLib, comptime T: type, name: [*]const u8) ?T { + if (windows.kernel32.GetProcAddress(self.dll, name)) |addr| { + return @ptrCast(T, addr); + } else { + return null; + } + } + + pub fn lookup(self: *DlDynlib, comptime T: type, comptime max_name_len: usize, name: []const u8) ?T { + const c_name: [max_name_len]u8 = undefined; + mem.copy(&c_name, name); + c_name[name.len] = 0; + return self.lookupC(T, &c_name); + } +}; + +pub const DlDynlib = struct { + pub const Error = error{FileNotFound}; + + handle: *c_void, + + pub fn open(path: []const u8) !DlDynlib { + if (!builtin.link_libc and !os.darwin.is_the_target) { + @compileError("DlDynlib requires libc"); + } + + return DlDynlib{ + .handle = system.dlopen(path.ptr, system.RTLD_LAZY) orelse { + return error.FileNotFound; + }, + }; + } + + pub fn close(self: *DlDynlib) void { + _ = system.dlclose(self.handle); + self.* = undefined; + } + + pub fn lookupC(self: *DlDynlib, comptime T: type, name: [*]const u8) ?T { + if (system.dlsym(self.handle, name)) |symbol| { + return @ptrCast(T, symbol); + } else { + return null; + } + } + + pub fn lookup(self: *DlDynlib, comptime T: type, comptime max_name_len: usize, name: []const u8) ?T { + const c_name: [max_name_len]u8 = undefined; + mem.copy(&c_name, name); + c_name[name.len] = 0; + return self.lookupC(T, &c_name); } }; @@ -269,6 +337,7 @@ test "dynamic_library" { const libname = switch (builtin.os) { .linux => "invalid_so.so", .windows => "invalid_dll.dll", + .macosx, .tvos, .watchos, .ios => "invalid_dylib.dylib", else => return, }; @@ -276,5 +345,4 @@ test "dynamic_library" { testing.expect(err == error.FileNotFound); return; }; - @panic("Expected error from function"); } diff --git a/std/os/bits/darwin.zig b/std/os/bits/darwin.zig index 483d4cda908f..4640d9ce2c18 100644 --- a/std/os/bits/darwin.zig +++ b/std/os/bits/darwin.zig @@ -1175,3 +1175,16 @@ pub fn S_ISSOCK(m: u32) bool { pub fn S_IWHT(m: u32) bool { return m & S_IFMT == S_IFWHT; } + +pub const RTLD_LAZY = 0x1; +pub const RTLD_NOW = 0x2; +pub const RTLD_LOCAL = 0x4; +pub const RTLD_GLOBAL = 0x8; +pub const RTLD_NOLOAD = 0x10; +pub const RTLD_NODELETE = 0x80; +pub const RTLD_FIRST = 0x100; + +pub const RTLD_NEXT = @intToPtr(*c_void, ~maxInt(usize)); +pub const RTLD_DEFAULT = @intToPtr(*c_void, ~maxInt(usize) - 1); +pub const RTLD_SELF = @intToPtr(*c_void, ~maxInt(usize) - 2); +pub const RTLD_MAIN_ONLY = @intToPtr(*c_void, ~maxInt(usize) - 4); diff --git a/std/os/bits/linux.zig b/std/os/bits/linux.zig index 0617378da9f5..dcb79c55ca78 100644 --- a/std/os/bits/linux.zig +++ b/std/os/bits/linux.zig @@ -1,4 +1,4 @@ -const builtin = @import("builtin"); + const builtin = @import("builtin"); const std = @import("../../std.zig"); const maxInt = std.math.maxInt; usingnamespace @import("../bits.zig"); diff --git a/test/standalone.zig b/test/standalone.zig index f3a1f735daad..7abe8fa229fb 100644 --- a/test/standalone.zig +++ b/test/standalone.zig @@ -19,10 +19,11 @@ pub fn addCases(cases: *tests.StandaloneContext) void { cases.addBuildFile("test/standalone/use_alias/build.zig"); cases.addBuildFile("test/standalone/brace_expansion/build.zig"); cases.addBuildFile("test/standalone/empty_env/build.zig"); - if (builtin.os == builtin.Os.linux) { - // TODO hook up the DynLib API for windows using LoadLibraryA - // TODO figure out how to make this work on darwin - probably libSystem has dlopen/dlsym in it - cases.addBuildFile("test/standalone/load_dynamic_library/build.zig"); + switch (builtin.os) { + .linux, .windows, .macosx, .tvos, .watchos, .ios => { + cases.addBuildFile("test/standalone/load_dynamic_library/build.zig"); + }, + else => {}, } if (builtin.arch == builtin.Arch.x86_64) { // TODO add C ABI support for other architectures