Skip to content

Commit cbeab67

Browse files
authored
Merge pull request #19325 from Vexu/dynlib
std: adjust DynLib API
2 parents 1206604 + e6f74b7 commit cbeab67

File tree

3 files changed

+90
-36
lines changed

3 files changed

+90
-36
lines changed

lib/std/c.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1881,6 +1881,7 @@ pub const FILE = opaque {};
18811881
pub extern "c" fn dlopen(path: [*:0]const u8, mode: c_int) ?*anyopaque;
18821882
pub extern "c" fn dlclose(handle: *anyopaque) c_int;
18831883
pub extern "c" fn dlsym(handle: ?*anyopaque, symbol: [*:0]const u8) ?*anyopaque;
1884+
pub extern "c" fn dlerror() ?[*:0]u8;
18841885

18851886
pub extern "c" fn sync() void;
18861887
pub extern "c" fn syncfs(fd: c_int) c_int;

lib/std/dynamic_library.zig

Lines changed: 88 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,41 @@ const elf = std.elf;
77
const windows = std.os.windows;
88
const system = std.os.system;
99

10-
pub 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.
12+
pub 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

62-
pub fn linkmap_iterator(phdrs: []elf.Phdr) !LinkMap.Iterator {
89+
pub 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

98125
pub const ElfDynLib = struct {
@@ -111,10 +138,10 @@ pub const ElfDynLib = struct {
111138
ElfStringSectionNotFound,
112139
ElfSymSectionNotFound,
113140
ElfHashTableNotFound,
114-
};
141+
} || os.OpenError || os.MMapError;
115142

116143
/// Trusts the file. Malicious file will be able to execute arbitrary code.
117-
pub fn open(path: []const u8) !ElfDynLib {
144+
pub fn open(path: []const u8) Error!ElfDynLib {
118145
const fd = try os.open(path, .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0);
119146
defer os.close(fd);
120147

@@ -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,
@@ -250,7 +277,7 @@ pub const ElfDynLib = struct {
250277
}
251278

252279
/// Trusts the file. Malicious file will be able to execute arbitrary code.
253-
pub fn openZ(path_c: [*:0]const u8) !ElfDynLib {
280+
pub fn openZ(path_c: [*:0]const u8) Error!ElfDynLib {
254281
return open(mem.sliceTo(path_c, 0));
255282
}
256283

@@ -260,14 +287,15 @@ 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 {
267294
return null;
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;
@@ -314,41 +342,59 @@ fn checkver(def_arg: *elf.Verdef, vsym_arg: i32, vername: []const u8, strings: [
314342
return mem.eql(u8, vername, mem.sliceTo(strings + aux.vda_name, 0));
315343
}
316344

345+
test "ElfDynLib" {
346+
if (builtin.os.tag != .linux) {
347+
return error.SkipZigTest;
348+
}
349+
350+
try testing.expectError(error.FileNotFound, ElfDynLib.open("invalid_so.so"));
351+
}
352+
317353
pub const WindowsDynLib = struct {
318-
pub const Error = error{FileNotFound};
354+
pub const Error = error{
355+
FileNotFound,
356+
InvalidPath,
357+
} || windows.LoadLibraryError;
319358

320359
dll: windows.HMODULE,
321360

322-
pub fn open(path: []const u8) !WindowsDynLib {
361+
pub fn open(path: []const u8) Error!WindowsDynLib {
323362
return openEx(path, .none);
324363
}
325364

326-
pub fn openEx(path: []const u8, flags: windows.LoadLibraryFlags) !WindowsDynLib {
327-
const path_w = try windows.sliceToPrefixedFileW(null, path);
365+
/// WindowsDynLib specific
366+
/// Opens dynamic library with specified library loading flags.
367+
pub fn openEx(path: []const u8, flags: windows.LoadLibraryFlags) Error!WindowsDynLib {
368+
const path_w = windows.sliceToPrefixedFileW(null, path) catch return error.InvalidPath;
328369
return openExW(path_w.span().ptr, flags);
329370
}
330371

331-
pub fn openZ(path_c: [*:0]const u8) !WindowsDynLib {
372+
pub fn openZ(path_c: [*:0]const u8) Error!WindowsDynLib {
332373
return openExZ(path_c, .none);
333374
}
334375

335-
pub fn openExZ(path_c: [*:0]const u8, flags: windows.LoadLibraryFlags) !WindowsDynLib {
376+
/// WindowsDynLib specific
377+
/// Opens dynamic library with specified library loading flags.
378+
pub fn openExZ(path_c: [*:0]const u8, flags: windows.LoadLibraryFlags) Error!WindowsDynLib {
336379
const path_w = try windows.cStrToPrefixedFileW(null, path_c);
337380
return openExW(path_w.span().ptr, flags);
338381
}
339382

340-
pub fn openW(path_w: [*:0]const u16) !WindowsDynLib {
383+
/// WindowsDynLib specific
384+
pub fn openW(path_w: [*:0]const u16) Error!WindowsDynLib {
341385
return openExW(path_w, .none);
342386
}
343387

344-
pub fn openExW(path_w: [*:0]const u16, flags: windows.LoadLibraryFlags) !WindowsDynLib {
388+
/// WindowsDynLib specific
389+
/// Opens dynamic library with specified library loading flags.
390+
pub fn openExW(path_w: [*:0]const u16, flags: windows.LoadLibraryFlags) Error!WindowsDynLib {
345391
var offset: usize = 0;
346392
if (path_w[0] == '\\' and path_w[1] == '?' and path_w[2] == '?' and path_w[3] == '\\') {
347393
// + 4 to skip over the \??\
348394
offset = 4;
349395
}
350396

351-
return WindowsDynLib{
397+
return .{
352398
.dll = try windows.LoadLibraryExW(path_w + offset, flags),
353399
};
354400
}
@@ -368,25 +414,28 @@ pub const WindowsDynLib = struct {
368414
};
369415

370416
pub const DlDynLib = struct {
371-
pub const Error = error{FileNotFound};
417+
pub const Error = error{ FileNotFound, NameTooLong };
372418

373419
handle: *anyopaque,
374420

375-
pub fn open(path: []const u8) !DlDynLib {
421+
pub fn open(path: []const u8) Error!DlDynLib {
376422
const path_c = try os.toPosixPath(path);
377423
return openZ(&path_c);
378424
}
379425

380-
pub fn openZ(path_c: [*:0]const u8) !DlDynLib {
381-
return DlDynLib{
426+
pub fn openZ(path_c: [*:0]const u8) Error!DlDynLib {
427+
return .{
382428
.handle = system.dlopen(path_c, system.RTLD.LAZY) orelse {
383429
return error.FileNotFound;
384430
},
385431
};
386432
}
387433

388434
pub fn close(self: *DlDynLib) void {
389-
_ = system.dlclose(self.handle);
435+
switch (std.os.errno(system.dlclose(self.handle))) {
436+
.SUCCESS => return,
437+
else => unreachable,
438+
}
390439
self.* = undefined;
391440
}
392441

@@ -399,6 +448,13 @@ pub const DlDynLib = struct {
399448
return null;
400449
}
401450
}
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+
}
402458
};
403459

404460
test "dynamic_library" {
@@ -409,8 +465,5 @@ test "dynamic_library" {
409465
else => return error.SkipZigTest,
410466
};
411467

412-
_ = DynLib.open(libname) catch |err| {
413-
try testing.expect(err == error.FileNotFound);
414-
return;
415-
};
468+
try testing.expectError(error.FileNotFound, DynLib.open(libname));
416469
}

lib/std/os.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5979,7 +5979,7 @@ pub fn sched_getaffinity(pid: pid_t) SchedGetAffinityError!cpu_set_t {
59795979

59805980
/// Used to convert a slice to a null terminated slice on the stack.
59815981
/// TODO https://github.com/ziglang/zig/issues/287
5982-
pub fn toPosixPath(file_path: []const u8) ![MAX_PATH_BYTES - 1:0]u8 {
5982+
pub fn toPosixPath(file_path: []const u8) error{NameTooLong}![MAX_PATH_BYTES - 1:0]u8 {
59835983
if (std.debug.runtime_safety) assert(std.mem.indexOfScalar(u8, file_path, 0) == null);
59845984
var path_with_null: [MAX_PATH_BYTES - 1:0]u8 = undefined;
59855985
// >= rather than > to make room for the null byte

0 commit comments

Comments
 (0)