authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-03-17 20:19:51-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-03-17 20:19:51-07:00
logcbeab678a50e48ba55f39b391fcfe98823fe3c8c
treea03afad1fe8ceeaf8b51563ee1cbf15d9f50e330
parent12066042621ca7920d67d339b54c0a2220d40696
parente6f74b78ef48bc1d23ccd98d042534f0bbe3eb80
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #19325 from Vexu/dynlib

std: adjust DynLib API

3 files changed, 90 insertions(+), 36 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+88-35
......@@ -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 {
......@@ -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,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;
......@@ -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
345test "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
317353pub 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,17 +414,17 @@ pub const WindowsDynLib = struct {
368414};
369415
370416pub 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 },
......@@ -386,7 +432,10 @@ pub const DlDynLib = struct {
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
404460test "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+1-1
......@@ -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
5982pub fn toPosixPath(file_path: []const u8) ![MAX_PATH_BYTES - 1:0]u8 {
5982pub 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