diff --git a/lib/std/c.zig b/lib/std/c.zig index d9062a125d..7f8f46ce4d 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -1881,6 +1881,7 @@ pub const FILE = opaque {}; pub extern "c" fn dlopen(path: [*:0]const u8, mode: c_int) ?*anyopaque; pub extern "c" fn dlclose(handle: *anyopaque) c_int; pub extern "c" fn dlsym(handle: ?*anyopaque, symbol: [*:0]const u8) ?*anyopaque; +pub extern "c" fn dlerror() ?[*:0]u8; pub extern "c" fn sync() void; pub extern "c" fn syncfs(fd: c_int) c_int; diff --git a/lib/std/dynamic_library.zig b/lib/std/dynamic_library.zig index ea54750f9a..5a17f38429 100644 --- a/lib/std/dynamic_library.zig +++ b/lib/std/dynamic_library.zig @@ -7,14 +7,41 @@ const elf = std.elf; const windows = std.os.windows; const system = std.os.system; -pub const DynLib = switch (builtin.os.tag) { - .linux => if (!builtin.link_libc or builtin.abi == .musl and builtin.link_mode == .static) - ElfDynLib - else - DlDynLib, - .windows => WindowsDynLib, - .macos, .tvos, .watchos, .ios, .freebsd, .netbsd, .openbsd, .dragonfly, .solaris, .illumos => DlDynLib, - else => void, +/// Cross-platform dynamic library loading and symbol lookup. +/// Platform-specific functionality is available through the `inner` field. +pub const DynLib = struct { + const InnerType = switch (builtin.os.tag) { + .linux => if (!builtin.link_libc or builtin.abi == .musl and builtin.link_mode == .static) + ElfDynLib + else + DlDynLib, + .windows => WindowsDynLib, + .macos, .tvos, .watchos, .ios, .freebsd, .netbsd, .openbsd, .dragonfly, .solaris, .illumos => DlDynLib, + else => @compileError("unsupported platform"), + }; + + inner: InnerType, + + pub const Error = ElfDynLib.Error || DlDynLib.Error || WindowsDynLib.Error; + + /// Trusts the file. Malicious file will be able to execute arbitrary code. + pub fn open(path: []const u8) Error!DynLib { + return .{ .inner = try InnerType.open(path) }; + } + + /// Trusts the file. Malicious file will be able to execute arbitrary code. + pub fn openZ(path_c: [*:0]const u8) Error!DynLib { + return .{ .inner = try InnerType.open(path_c) }; + } + + /// Trusts the file. + pub fn close(self: *DynLib) void { + return self.inner.close(); + } + + pub fn lookup(self: *DynLib, comptime T: type, name: [:0]const u8) ?T { + return self.inner.lookup(T, name); + } }; // The link_map structure is not completely specified beside the fields @@ -59,12 +86,12 @@ pub fn get_DYNAMIC() ?[*]elf.Dyn { return @extern([*]elf.Dyn, .{ .name = "_DYNAMIC", .linkage = .weak }); } -pub fn linkmap_iterator(phdrs: []elf.Phdr) !LinkMap.Iterator { +pub fn linkmap_iterator(phdrs: []elf.Phdr) error{InvalidExe}!LinkMap.Iterator { _ = phdrs; const _DYNAMIC = get_DYNAMIC() orelse { // No PT_DYNAMIC means this is either a statically-linked program or a // badly corrupted dynamically-linked one. - return LinkMap.Iterator{ .current = null }; + return .{ .current = null }; }; const link_map_ptr = init: { @@ -89,10 +116,10 @@ pub fn linkmap_iterator(phdrs: []elf.Phdr) !LinkMap.Iterator { else => {}, } } - return LinkMap.Iterator{ .current = null }; + return .{ .current = null }; }; - return LinkMap.Iterator{ .current = link_map_ptr }; + return .{ .current = link_map_ptr }; } pub const ElfDynLib = struct { @@ -239,7 +266,7 @@ pub const ElfDynLib = struct { } } - return ElfDynLib{ + return .{ .memory = all_loaded_mem, .strings = maybe_strings orelse return error.ElfStringSectionNotFound, .syms = maybe_syms orelse return error.ElfSymSectionNotFound, @@ -260,7 +287,7 @@ pub const ElfDynLib = struct { self.* = undefined; } - pub fn lookup(self: *ElfDynLib, comptime T: type, name: [:0]const u8) ?T { + pub fn lookup(self: *const ElfDynLib, comptime T: type, name: [:0]const u8) ?T { if (self.lookupAddress("", name)) |symbol| { return @as(T, @ptrFromInt(symbol)); } else { @@ -268,6 +295,7 @@ pub const ElfDynLib = struct { } } + /// ElfDynLib specific /// Returns the address of the symbol pub fn lookupAddress(self: *const ElfDynLib, vername: []const u8, name: []const u8) ?usize { const maybe_versym = if (self.verdef == null) null else self.versym; @@ -334,6 +362,8 @@ pub const WindowsDynLib = struct { return openEx(path, .none); } + /// WindowsDynLib specific + /// Opens dynamic library with specified library loading flags. pub fn openEx(path: []const u8, flags: windows.LoadLibraryFlags) Error!WindowsDynLib { const path_w = windows.sliceToPrefixedFileW(null, path) catch return error.InvalidPath; return openExW(path_w.span().ptr, flags); @@ -343,15 +373,20 @@ pub const WindowsDynLib = struct { return openExZ(path_c, .none); } + /// WindowsDynLib specific + /// Opens dynamic library with specified library loading flags. pub fn openExZ(path_c: [*:0]const u8, flags: windows.LoadLibraryFlags) Error!WindowsDynLib { const path_w = try windows.cStrToPrefixedFileW(null, path_c); return openExW(path_w.span().ptr, flags); } + /// WindowsDynLib specific pub fn openW(path_w: [*:0]const u16) Error!WindowsDynLib { return openExW(path_w, .none); } + /// WindowsDynLib specific + /// Opens dynamic library with specified library loading flags. pub fn openExW(path_w: [*:0]const u16, flags: windows.LoadLibraryFlags) Error!WindowsDynLib { var offset: usize = 0; if (path_w[0] == '\\' and path_w[1] == '?' and path_w[2] == '?' and path_w[3] == '\\') { @@ -359,7 +394,7 @@ pub const WindowsDynLib = struct { offset = 4; } - return WindowsDynLib{ + return .{ .dll = try windows.LoadLibraryExW(path_w + offset, flags), }; } @@ -389,7 +424,7 @@ pub const DlDynLib = struct { } pub fn openZ(path_c: [*:0]const u8) Error!DlDynLib { - return DlDynLib{ + return .{ .handle = system.dlopen(path_c, system.RTLD.LAZY) orelse { return error.FileNotFound; }, @@ -397,7 +432,10 @@ pub const DlDynLib = struct { } pub fn close(self: *DlDynLib) void { - _ = system.dlclose(self.handle); + switch (std.os.errno(system.dlclose(self.handle))) { + .SUCCESS => return, + else => unreachable, + } self.* = undefined; } @@ -410,6 +448,13 @@ pub const DlDynLib = struct { return null; } } + + /// DlDynLib specific + /// Returns human readable string describing most recent error than occurred from `lookup` + /// or `null` if no error has occurred since initialization or when `getError` was last called. + pub fn getError() ?[:0]const u8 { + return mem.span(system.dlerror()); + } }; test "dynamic_library" {