@@ -7,14 +7,41 @@ const elf = std.elf;
7
7
const windows = std .os .windows ;
8
8
const system = std .os .system ;
9
9
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
+ }
18
45
};
19
46
20
47
// The link_map structure is not completely specified beside the fields
@@ -59,12 +86,12 @@ pub fn get_DYNAMIC() ?[*]elf.Dyn {
59
86
return @extern ([* ]elf .Dyn , .{ .name = "_DYNAMIC" , .linkage = .weak });
60
87
}
61
88
62
- pub fn linkmap_iterator (phdrs : []elf.Phdr ) ! LinkMap.Iterator {
89
+ pub fn linkmap_iterator (phdrs : []elf.Phdr ) error { InvalidExe } ! LinkMap.Iterator {
63
90
_ = phdrs ;
64
91
const _DYNAMIC = get_DYNAMIC () orelse {
65
92
// No PT_DYNAMIC means this is either a statically-linked program or a
66
93
// badly corrupted dynamically-linked one.
67
- return LinkMap.Iterator { .current = null };
94
+ return . { .current = null };
68
95
};
69
96
70
97
const link_map_ptr = init : {
@@ -89,10 +116,10 @@ pub fn linkmap_iterator(phdrs: []elf.Phdr) !LinkMap.Iterator {
89
116
else = > {},
90
117
}
91
118
}
92
- return LinkMap.Iterator { .current = null };
119
+ return . { .current = null };
93
120
};
94
121
95
- return LinkMap.Iterator { .current = link_map_ptr };
122
+ return . { .current = link_map_ptr };
96
123
}
97
124
98
125
pub const ElfDynLib = struct {
@@ -111,10 +138,10 @@ pub const ElfDynLib = struct {
111
138
ElfStringSectionNotFound ,
112
139
ElfSymSectionNotFound ,
113
140
ElfHashTableNotFound ,
114
- };
141
+ } || os . OpenError || os . MMapError ;
115
142
116
143
/// 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 {
118
145
const fd = try os .open (path , .{ .ACCMODE = .RDONLY , .CLOEXEC = true }, 0 );
119
146
defer os .close (fd );
120
147
@@ -239,7 +266,7 @@ pub const ElfDynLib = struct {
239
266
}
240
267
}
241
268
242
- return ElfDynLib {
269
+ return . {
243
270
.memory = all_loaded_mem ,
244
271
.strings = maybe_strings orelse return error .ElfStringSectionNotFound ,
245
272
.syms = maybe_syms orelse return error .ElfSymSectionNotFound ,
@@ -250,7 +277,7 @@ pub const ElfDynLib = struct {
250
277
}
251
278
252
279
/// 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 {
254
281
return open (mem .sliceTo (path_c , 0 ));
255
282
}
256
283
@@ -260,14 +287,15 @@ pub const ElfDynLib = struct {
260
287
self .* = undefined ;
261
288
}
262
289
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 {
264
291
if (self .lookupAddress ("" , name )) | symbol | {
265
292
return @as (T , @ptrFromInt (symbol ));
266
293
} else {
267
294
return null ;
268
295
}
269
296
}
270
297
298
+ /// ElfDynLib specific
271
299
/// Returns the address of the symbol
272
300
pub fn lookupAddress (self : * const ElfDynLib , vername : []const u8 , name : []const u8 ) ? usize {
273
301
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: [
314
342
return mem .eql (u8 , vername , mem .sliceTo (strings + aux .vda_name , 0 ));
315
343
}
316
344
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
+
317
353
pub const WindowsDynLib = struct {
318
- pub const Error = error {FileNotFound };
354
+ pub const Error = error {
355
+ FileNotFound ,
356
+ InvalidPath ,
357
+ } || windows .LoadLibraryError ;
319
358
320
359
dll : windows.HMODULE ,
321
360
322
- pub fn open (path : []const u8 ) ! WindowsDynLib {
361
+ pub fn open (path : []const u8 ) Error ! WindowsDynLib {
323
362
return openEx (path , .none );
324
363
}
325
364
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 ;
328
369
return openExW (path_w .span ().ptr , flags );
329
370
}
330
371
331
- pub fn openZ (path_c : [* :0 ]const u8 ) ! WindowsDynLib {
372
+ pub fn openZ (path_c : [* :0 ]const u8 ) Error ! WindowsDynLib {
332
373
return openExZ (path_c , .none );
333
374
}
334
375
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 {
336
379
const path_w = try windows .cStrToPrefixedFileW (null , path_c );
337
380
return openExW (path_w .span ().ptr , flags );
338
381
}
339
382
340
- pub fn openW (path_w : [* :0 ]const u16 ) ! WindowsDynLib {
383
+ /// WindowsDynLib specific
384
+ pub fn openW (path_w : [* :0 ]const u16 ) Error ! WindowsDynLib {
341
385
return openExW (path_w , .none );
342
386
}
343
387
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 {
345
391
var offset : usize = 0 ;
346
392
if (path_w [0 ] == '\\ ' and path_w [1 ] == '?' and path_w [2 ] == '?' and path_w [3 ] == '\\ ' ) {
347
393
// + 4 to skip over the \??\
348
394
offset = 4 ;
349
395
}
350
396
351
- return WindowsDynLib {
397
+ return . {
352
398
.dll = try windows .LoadLibraryExW (path_w + offset , flags ),
353
399
};
354
400
}
@@ -368,25 +414,28 @@ pub const WindowsDynLib = struct {
368
414
};
369
415
370
416
pub const DlDynLib = struct {
371
- pub const Error = error {FileNotFound };
417
+ pub const Error = error { FileNotFound , NameTooLong };
372
418
373
419
handle : * anyopaque ,
374
420
375
- pub fn open (path : []const u8 ) ! DlDynLib {
421
+ pub fn open (path : []const u8 ) Error ! DlDynLib {
376
422
const path_c = try os .toPosixPath (path );
377
423
return openZ (& path_c );
378
424
}
379
425
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 . {
382
428
.handle = system .dlopen (path_c , system .RTLD .LAZY ) orelse {
383
429
return error .FileNotFound ;
384
430
},
385
431
};
386
432
}
387
433
388
434
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
+ }
390
439
self .* = undefined ;
391
440
}
392
441
@@ -399,6 +448,13 @@ pub const DlDynLib = struct {
399
448
return null ;
400
449
}
401
450
}
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
+ }
402
458
};
403
459
404
460
test "dynamic_library" {
@@ -409,8 +465,5 @@ test "dynamic_library" {
409
465
else = > return error .SkipZigTest ,
410
466
};
411
467
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 ));
416
469
}
0 commit comments