authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2024-03-17 00:01:28+02:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2024-03-17 01:20:08+02:00
logec2465542cd7bfd58f7bf72552b6f332af2c03f9
tree79bd54aaf995afd85655d500e9172f6a7e392bd0
parent7628785983486c612bcadc9a14bd784b627a5d21

std: adjust DynLib API

The cross-platform functions now use an error set that contains all possible errors on every platform.

2 files changed, 63 insertions(+), 17 deletions(-)

lib/std/c.zig+1
......@@ -1881,6 +1881,7 @@ pub const FILE = opaque {};
18811881pub extern "c" fn dlopen(path: [*:0]const u8, mode: c_int) ?*anyopaque;
18821882pub extern "c" fn dlclose(handle: *anyopaque) c_int;
18831883pub extern "c" fn dlsym(handle: ?*anyopaque, symbol: [*:0]const u8) ?*anyopaque;
1884pub extern "c" fn dlerror() ?[*:0]u8;
18841885
18851886pub extern "c" fn sync() void;
18861887pub extern "c" fn syncfs(fd: c_int) c_int;
lib/std/dynamic_library.zig+62-17
......@@ -7,14 +7,41 @@ const elf = std.elf;
77const windows = std.os.windows;
88const system = std.os.system;
99
10pub const DynLib = switch (builtin.os.tag) {
11 .linux => if (!builtin.link_libc or builtin.abi == .musl and builtin.link_mode == .static)
12 ElfDynLib
13 else
14 DlDynLib,
15 .windows => WindowsDynLib,
16 .macos, .tvos, .watchos, .ios, .freebsd, .netbsd, .openbsd, .dragonfly, .solaris, .illumos => DlDynLib,
17 else => void,
10/// Cross-platform dynamic library loading and symbol lookup.
11/// Platform-specific functionality is available through the `inner` field.
12pub const DynLib = struct {
13 const InnerType = switch (builtin.os.tag) {
14 .linux => if (!builtin.link_libc or builtin.abi == .musl and builtin.link_mode == .static)
15 ElfDynLib
16 else
17 DlDynLib,
18 .windows => WindowsDynLib,
19 .macos, .tvos, .watchos, .ios, .freebsd, .netbsd, .openbsd, .dragonfly, .solaris, .illumos => DlDynLib,
20 else => @compileError("unsupported platform"),
21 };
22
23 inner: InnerType,
24
25 pub const Error = ElfDynLib.Error || DlDynLib.Error || WindowsDynLib.Error;
26
27 /// Trusts the file. Malicious file will be able to execute arbitrary code.
28 pub fn open(path: []const u8) Error!DynLib {
29 return .{ .inner = try InnerType.open(path) };
30 }
31
32 /// Trusts the file. Malicious file will be able to execute arbitrary code.
33 pub fn openZ(path_c: [*:0]const u8) Error!DynLib {
34 return .{ .inner = try InnerType.open(path_c) };
35 }
36
37 /// Trusts the file.
38 pub fn close(self: *DynLib) void {
39 return self.inner.close();
40 }
41
42 pub fn lookup(self: *DynLib, comptime T: type, name: [:0]const u8) ?T {
43 return self.inner.lookup(T, name);
44 }
1845};
1946
2047// The link_map structure is not completely specified beside the fields
......@@ -59,12 +86,12 @@ pub fn get_DYNAMIC() ?[*]elf.Dyn {
5986 return @extern([*]elf.Dyn, .{ .name = "_DYNAMIC", .linkage = .weak });
6087}
6188
62pub fn linkmap_iterator(phdrs: []elf.Phdr) !LinkMap.Iterator {
89pub fn linkmap_iterator(phdrs: []elf.Phdr) error{InvalidExe}!LinkMap.Iterator {
6390 _ = phdrs;
6491 const _DYNAMIC = get_DYNAMIC() orelse {
6592 // No PT_DYNAMIC means this is either a statically-linked program or a
6693 // badly corrupted dynamically-linked one.
67 return LinkMap.Iterator{ .current = null };
94 return .{ .current = null };
6895 };
6996
7097 const link_map_ptr = init: {
......@@ -89,10 +116,10 @@ pub fn linkmap_iterator(phdrs: []elf.Phdr) !LinkMap.Iterator {
89116 else => {},
90117 }
91118 }
92 return LinkMap.Iterator{ .current = null };
119 return .{ .current = null };
93120 };
94121
95 return LinkMap.Iterator{ .current = link_map_ptr };
122 return .{ .current = link_map_ptr };
96123}
97124
98125pub const ElfDynLib = struct {
......@@ -239,7 +266,7 @@ pub const ElfDynLib = struct {
239266 }
240267 }
241268
242 return ElfDynLib{
269 return .{
243270 .memory = all_loaded_mem,
244271 .strings = maybe_strings orelse return error.ElfStringSectionNotFound,
245272 .syms = maybe_syms orelse return error.ElfSymSectionNotFound,
......@@ -260,7 +287,7 @@ pub const ElfDynLib = struct {
260287 self.* = undefined;
261288 }
262289
263 pub fn lookup(self: *ElfDynLib, comptime T: type, name: [:0]const u8) ?T {
290 pub fn lookup(self: *const ElfDynLib, comptime T: type, name: [:0]const u8) ?T {
264291 if (self.lookupAddress("", name)) |symbol| {
265292 return @as(T, @ptrFromInt(symbol));
266293 } else {
......@@ -268,6 +295,7 @@ pub const ElfDynLib = struct {
268295 }
269296 }
270297
298 /// ElfDynLib specific
271299 /// Returns the address of the symbol
272300 pub fn lookupAddress(self: *const ElfDynLib, vername: []const u8, name: []const u8) ?usize {
273301 const maybe_versym = if (self.verdef == null) null else self.versym;
......@@ -334,6 +362,8 @@ pub const WindowsDynLib = struct {
334362 return openEx(path, .none);
335363 }
336364
365 /// WindowsDynLib specific
366 /// Opens dynamic library with specified library loading flags.
337367 pub fn openEx(path: []const u8, flags: windows.LoadLibraryFlags) Error!WindowsDynLib {
338368 const path_w = windows.sliceToPrefixedFileW(null, path) catch return error.InvalidPath;
339369 return openExW(path_w.span().ptr, flags);
......@@ -343,15 +373,20 @@ pub const WindowsDynLib = struct {
343373 return openExZ(path_c, .none);
344374 }
345375
376 /// WindowsDynLib specific
377 /// Opens dynamic library with specified library loading flags.
346378 pub fn openExZ(path_c: [*:0]const u8, flags: windows.LoadLibraryFlags) Error!WindowsDynLib {
347379 const path_w = try windows.cStrToPrefixedFileW(null, path_c);
348380 return openExW(path_w.span().ptr, flags);
349381 }
350382
383 /// WindowsDynLib specific
351384 pub fn openW(path_w: [*:0]const u16) Error!WindowsDynLib {
352385 return openExW(path_w, .none);
353386 }
354387
388 /// WindowsDynLib specific
389 /// Opens dynamic library with specified library loading flags.
355390 pub fn openExW(path_w: [*:0]const u16, flags: windows.LoadLibraryFlags) Error!WindowsDynLib {
356391 var offset: usize = 0;
357392 if (path_w[0] == '\\' and path_w[1] == '?' and path_w[2] == '?' and path_w[3] == '\\') {
......@@ -359,7 +394,7 @@ pub const WindowsDynLib = struct {
359394 offset = 4;
360395 }
361396
362 return WindowsDynLib{
397 return .{
363398 .dll = try windows.LoadLibraryExW(path_w + offset, flags),
364399 };
365400 }
......@@ -389,7 +424,7 @@ pub const DlDynLib = struct {
389424 }
390425
391426 pub fn openZ(path_c: [*:0]const u8) Error!DlDynLib {
392 return DlDynLib{
427 return .{
393428 .handle = system.dlopen(path_c, system.RTLD.LAZY) orelse {
394429 return error.FileNotFound;
395430 },
......@@ -397,7 +432,10 @@ pub const DlDynLib = struct {
397432 }
398433
399434 pub fn close(self: *DlDynLib) void {
400 _ = system.dlclose(self.handle);
435 switch (std.os.errno(system.dlclose(self.handle))) {
436 .SUCCESS => return,
437 else => unreachable,
438 }
401439 self.* = undefined;
402440 }
403441
......@@ -410,6 +448,13 @@ pub const DlDynLib = struct {
410448 return null;
411449 }
412450 }
451
452 /// DlDynLib specific
453 /// Returns human readable string describing most recent error than occurred from `lookup`
454 /// or `null` if no error has occurred since initialization or when `getError` was last called.
455 pub fn getError() ?[:0]const u8 {
456 return mem.span(system.dlerror());
457 }
413458};
414459
415460test "dynamic_library" {