| ... | ... | @@ -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 { |
| ... | ... | @@ -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, |
| ... | ... | @@ -260,7 +287,7 @@ 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 { |
| ... | ... | @@ -268,6 +295,7 @@ pub const ElfDynLib = struct { |
| 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; |
| ... | ... | @@ -334,6 +362,8 @@ pub const WindowsDynLib = struct { |
| 334 | 362 | return openEx(path, .none); |
| 335 | 363 | } |
| 336 | 364 | |
| 365 | /// WindowsDynLib specific |
| 366 | /// Opens dynamic library with specified library loading flags. |
| 337 | 367 | pub fn openEx(path: []const u8, flags: windows.LoadLibraryFlags) Error!WindowsDynLib { |
| 338 | 368 | const path_w = windows.sliceToPrefixedFileW(null, path) catch return error.InvalidPath; |
| 339 | 369 | return openExW(path_w.span().ptr, flags); |
| ... | ... | @@ -343,15 +373,20 @@ pub const WindowsDynLib = struct { |
| 343 | 373 | return openExZ(path_c, .none); |
| 344 | 374 | } |
| 345 | 375 | |
| 376 | /// WindowsDynLib specific |
| 377 | /// Opens dynamic library with specified library loading flags. |
| 346 | 378 | pub fn openExZ(path_c: [*:0]const u8, flags: windows.LoadLibraryFlags) Error!WindowsDynLib { |
| 347 | 379 | const path_w = try windows.cStrToPrefixedFileW(null, path_c); |
| 348 | 380 | return openExW(path_w.span().ptr, flags); |
| 349 | 381 | } |
| 350 | 382 | |
| 383 | /// WindowsDynLib specific |
| 351 | 384 | pub fn openW(path_w: [*:0]const u16) Error!WindowsDynLib { |
| 352 | 385 | return openExW(path_w, .none); |
| 353 | 386 | } |
| 354 | 387 | |
| 388 | /// WindowsDynLib specific |
| 389 | /// Opens dynamic library with specified library loading flags. |
| 355 | 390 | pub fn openExW(path_w: [*:0]const u16, flags: windows.LoadLibraryFlags) Error!WindowsDynLib { |
| 356 | 391 | var offset: usize = 0; |
| 357 | 392 | if (path_w[0] == '\\' and path_w[1] == '?' and path_w[2] == '?' and path_w[3] == '\\') { |
| ... | ... | @@ -359,7 +394,7 @@ pub const WindowsDynLib = struct { |
| 359 | 394 | offset = 4; |
| 360 | 395 | } |
| 361 | 396 | |
| 362 | | return WindowsDynLib{ |
| 397 | return .{ |
| 363 | 398 | .dll = try windows.LoadLibraryExW(path_w + offset, flags), |
| 364 | 399 | }; |
| 365 | 400 | } |
| ... | ... | @@ -389,7 +424,7 @@ pub const DlDynLib = struct { |
| 389 | 424 | } |
| 390 | 425 | |
| 391 | 426 | pub fn openZ(path_c: [*:0]const u8) Error!DlDynLib { |
| 392 | | return DlDynLib{ |
| 427 | return .{ |
| 393 | 428 | .handle = system.dlopen(path_c, system.RTLD.LAZY) orelse { |
| 394 | 429 | return error.FileNotFound; |
| 395 | 430 | }, |
| ... | ... | @@ -397,7 +432,10 @@ pub const DlDynLib = struct { |
| 397 | 432 | } |
| 398 | 433 | |
| 399 | 434 | 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 | } |
| 401 | 439 | self.* = undefined; |
| 402 | 440 | } |
| 403 | 441 | |
| ... | ... | @@ -410,6 +448,13 @@ pub const DlDynLib = struct { |
| 410 | 448 | return null; |
| 411 | 449 | } |
| 412 | 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 | } |
| 413 | 458 | }; |
| 414 | 459 | |
| 415 | 460 | test "dynamic_library" { |