lib/sys/src/dynamic.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

   1 const std = @import("std");
   2 const builtin = @import("builtin");
   3 const capabilities = @import("capabilities.zig");
   4 const sys_allocator = @import("allocator.zig");
   5 const sys_env = @import("env.zig");
   6 const sys_memory = @import("memory.zig");
   7 const elf = std.elf;
   8 const posix = std.posix;
   9 
  10 pub const required_capabilities = capabilities.host(&.{ .allocator, .dynamic_loading, .environment, .filesystem, .memory_mapping });
  11 
  12 const ElfRepairLocalError = error{
  13     FileTooBig,
  14     InvalidElfImage,
  15     UnsupportedElfRelocation,
  16 };
  17 
  18 pub const ElfRepairError =
  19     std.Io.File.OpenError ||
  20     std.Io.File.StatError ||
  21     sys_memory.MapError ||
  22     ElfRepairLocalError;
  23 
  24 const NativeElfLocalError = error{
  25     FileTooBig,
  26     InvalidElfImage,
  27     NativeElfUnsupported,
  28     NativeElfUnsupportedRelocation,
  29     NativeElfStringSectionNotFound,
  30     NativeElfSymbolSectionNotFound,
  31     NativeElfHashTableNotFound,
  32     OutOfMemory,
  33 };
  34 
  35 pub const NativeElfError =
  36     std.Io.File.OpenError ||
  37     std.Io.File.StatError ||
  38     sys_memory.MapError ||
  39     NativeElfLocalError;
  40 
  41 pub const LibraryError = std.DynLib.Error || ElfRepairError || NativeElfError;
  42 pub const OpenFirstError = LibraryError || error{NoCandidate};
  43 
  44 pub const Library = struct {
  45     raw: LibraryImpl,
  46 
  47     pub fn open(path: []const u8) LibraryError!Library {
  48         if (canUseNativeElfPlatform()) {
  49             if (openNativeElf(path)) |library| {
  50                 return library;
  51             } else |err| switch (err) {
  52                 error.NativeElfUnsupported,
  53                 error.NativeElfUnsupportedRelocation,
  54                 error.NativeElfHashTableNotFound,
  55                 error.FileNotFound,
  56                 => {},
  57                 else => |native_error| return native_error,
  58             }
  59         }
  60 
  61         var raw = try std.DynLib.open(path);
  62         errdefer raw.close();
  63         if (comptime usesStdElfDynLib()) {
  64             const require_repair = std.mem.indexOfScalar(u8, path, '/') != null;
  65             repairElfImage(path, &raw) catch |err| {
  66                 if (require_repair) return err;
  67             };
  68         }
  69         return .{ .raw = .{ .std_dynlib = raw } };
  70     }
  71 
  72     pub fn openNativeElf(path: []const u8) NativeElfError!Library {
  73         const allocator = sys_allocator.processAllocator();
  74         const native_path = try resolveNativeElfOpenPath(allocator, path);
  75         defer allocator.free(native_path);
  76         return .{ .raw = .{ .native_elf = try openNativeElfShared(allocator, native_path) } };
  77     }
  78 
  79     pub fn close(self: *Library) void {
  80         switch (self.raw) {
  81             .std_dynlib => |*raw| raw.close(),
  82             .native_elf => |*native| native.close(),
  83         }
  84     }
  85 
  86     pub fn lookup(self: *Library, comptime T: type, symbol: [:0]const u8) ?T {
  87         return switch (self.raw) {
  88             .std_dynlib => |*raw| raw.lookup(T, symbol),
  89             .native_elf => |*native| native.lookup(T, symbol),
  90         };
  91     }
  92 
  93     pub fn isNativeElf(self: *const Library) bool {
  94         return switch (self.raw) {
  95             .native_elf => true,
  96             .std_dynlib => false,
  97         };
  98     }
  99 };
 100 
 101 pub fn openRuntimeFfiPlugin(path: []const u8) LibraryError!Library {
 102     if (runtimeFfiPluginsRequireNativeElf()) return Library.openNativeElf(path);
 103     return Library.open(path);
 104 }
 105 
 106 pub fn runtimeFfiPluginsRequireNativeElf() bool {
 107     return canUseNativeElfPlatform();
 108 }
 109 
 110 const LibraryImpl = union(enum) {
 111     std_dynlib: std.DynLib,
 112     native_elf: NativeElfHandle,
 113 };
 114 
 115 const NativeElfHandle = struct {
 116     shared: *NativeElfSharedLibrary,
 117 
 118     fn close(self: *NativeElfHandle) void {
 119         releaseNativeElfShared(self.shared);
 120         self.* = undefined;
 121     }
 122 
 123     fn lookup(self: *const NativeElfHandle, comptime T: type, name: [:0]const u8) ?T {
 124         return self.shared.library.lookup(T, name);
 125     }
 126 
 127     fn lookupAddressVersioned(self: *const NativeElfHandle, name: []const u8, version: ?[]const u8) ?usize {
 128         return self.shared.library.lookupAddressVersioned(name, version);
 129     }
 130 
 131     fn lookupTlsSymbolVersioned(self: *const NativeElfHandle, name: []const u8, version: ?[]const u8) ?NativeElfTlsSymbol {
 132         return self.shared.library.lookupTlsSymbolVersioned(name, version);
 133     }
 134 };
 135 
 136 const NativeElfSharedLibrary = struct {
 137     path: []u8,
 138     ref_count: usize,
 139     library: NativeElfLibrary,
 140 };
 141 
 142 const NativeElfRegistry = struct {
 143     mutex: std.atomic.Mutex = .unlocked,
 144     libraries: std.ArrayList(*NativeElfSharedLibrary) = .empty,
 145 
 146     fn lock(self: *NativeElfRegistry) void {
 147         while (!self.mutex.tryLock()) std.atomic.spinLoopHint();
 148     }
 149 };
 150 
 151 var native_elf_registry: NativeElfRegistry = .{};
 152 
 153 const NativeElfIfuncResolver = *const fn () callconv(.c) usize;
 154 
 155 const NativeElfLibrary = struct {
 156     allocator: std.mem.Allocator,
 157     memory: []align(std.heap.page_size_min) u8,
 158     load_bias: usize,
 159     strings: [*:0]u8,
 160     syms: [*]elf.Sym,
 161     symbol_count: usize,
 162     versyms: ?[*]elf.Versym,
 163     verdef: usize,
 164     verdef_count: usize,
 165     dependencies: []NativeElfHandle,
 166     tls: NativeElfTlsAllocation,
 167     fini: usize,
 168     fini_array: usize,
 169     fini_array_count: usize,
 170 
 171     fn openUncached(path: []const u8) NativeElfError!NativeElfLibrary {
 172         if (!canUseNativeElfPlatform() or !hasLibraryPathSeparator(path)) return error.NativeElfUnsupported;
 173 
 174         const io = std.Options.debug_io;
 175         var file = if (path.len > 0 and path[0] == '/')
 176             try std.Io.Dir.openFileAbsolute(io, path, .{})
 177         else
 178             try std.Io.Dir.cwd().openFile(io, path, .{});
 179         defer file.close(io);
 180 
 181         const stat = try file.stat(io);
 182         const size = std.math.cast(usize, stat.size) orelse return error.FileTooBig;
 183         if (size < @sizeOf(elf.Ehdr)) return error.InvalidElfImage;
 184         const page_size = std.heap.pageSize();
 185         const file_bytes = try sys_memory.mapPrivateFile(file.handle, size, .{ .read = true }, 0);
 186         defer sys_memory.unmap(file_bytes);
 187 
 188         const eh: *const elf.Ehdr = @ptrCast(file_bytes.ptr);
 189         if (!std.mem.eql(u8, eh.e_ident[0..4], elf.MAGIC)) return error.InvalidElfImage;
 190         if (eh.e_type != elf.ET.DYN) return error.InvalidElfImage;
 191         if (eh.e_phentsize != @sizeOf(elf.Phdr)) return error.InvalidElfImage;
 192         if (eh.e_phoff + @as(usize, eh.e_phentsize) * eh.e_phnum > size) return error.InvalidElfImage;
 193 
 194         var load_count: usize = 0;
 195         var min_vaddr: usize = std.math.maxInt(usize);
 196         var max_vaddr: usize = 0;
 197         var dynamic_vaddr: ?usize = null;
 198         var relro_vaddr: usize = 0;
 199         var relro_memsz: usize = 0;
 200         var relro_count: usize = 0;
 201         var maybe_tls_image: ?NativeElfTlsImage = null;
 202         var i: usize = 0;
 203         var ph_addr = @intFromPtr(file_bytes.ptr) + eh.e_phoff;
 204         while (i < eh.e_phnum) : ({
 205             i += 1;
 206             ph_addr += eh.e_phentsize;
 207         }) {
 208             const ph: *const elf.Phdr = @ptrFromInt(ph_addr);
 209             switch (ph.p_type) {
 210                 elf.PT_LOAD => {
 211                     const vaddr = std.math.cast(usize, ph.p_vaddr) orelse return error.InvalidElfImage;
 212                     const memsz = std.math.cast(usize, ph.p_memsz) orelse return error.InvalidElfImage;
 213                     const filesz = std.math.cast(usize, ph.p_filesz) orelse return error.InvalidElfImage;
 214                     const offset = std.math.cast(usize, ph.p_offset) orelse return error.InvalidElfImage;
 215                     if (memsz < filesz) return error.InvalidElfImage;
 216                     if (offset > size or filesz > size - offset) return error.InvalidElfImage;
 217                     const segment_end = checkedAdd(vaddr, memsz) orelse return error.InvalidElfImage;
 218                     min_vaddr = @min(min_vaddr, std.mem.alignBackward(usize, vaddr, page_size));
 219                     max_vaddr = @max(max_vaddr, std.mem.alignForward(usize, segment_end, page_size));
 220                     load_count += 1;
 221                 },
 222                 elf.PT_DYNAMIC => {
 223                     dynamic_vaddr = std.math.cast(usize, ph.p_vaddr) orelse return error.InvalidElfImage;
 224                 },
 225                 elf.PT_GNU_RELRO => {
 226                     relro_vaddr = std.math.cast(usize, ph.p_vaddr) orelse return error.InvalidElfImage;
 227                     relro_memsz = std.math.cast(usize, ph.p_memsz) orelse return error.InvalidElfImage;
 228                     relro_count += 1;
 229                 },
 230                 elf.PT_TLS => {
 231                     if (maybe_tls_image != null) return error.InvalidElfImage;
 232                     maybe_tls_image = try NativeElfTlsImage.fromProgramHeader(ph, size);
 233                 },
 234                 else => {},
 235             }
 236         }
 237 
 238         if (load_count == 0) return error.InvalidElfImage;
 239         if (relro_count > 1) return error.InvalidElfImage;
 240         if (maybe_tls_image != null and !canUseNativeElfTls()) return error.NativeElfUnsupported;
 241         if (max_vaddr <= min_vaddr) return error.InvalidElfImage;
 242         const memory_size = max_vaddr - min_vaddr;
 243         const memory = try sys_memory.mapAnonymous(memory_size, .{});
 244         errdefer sys_memory.unmap(memory);
 245 
 246         const allocator = sys_allocator.processAllocator();
 247         const load_bias = @intFromPtr(memory.ptr) - min_vaddr;
 248         try loadSegments(file.handle, file_bytes, size, load_bias, eh, page_size);
 249         var tls = try NativeElfTlsAllocation.init(allocator, maybe_tls_image, load_bias);
 250         errdefer tls.deinit(allocator);
 251         const dynamic = dynamic_vaddr orelse return error.InvalidElfImage;
 252 
 253         var maybe_strings: ?[*:0]u8 = null;
 254         var maybe_syms: ?[*]elf.Sym = null;
 255         var maybe_symbol_count: ?usize = null;
 256         var maybe_gnu_hash: ?*elf.gnu_hash.Header = null;
 257         var maybe_versyms: ?[*]elf.Versym = null;
 258         var verdef: usize = 0;
 259         var verdef_count: usize = 0;
 260         var verneed: usize = 0;
 261         var verneed_count: usize = 0;
 262         var needed_offsets: std.ArrayList(usize) = .empty;
 263         defer needed_offsets.deinit(allocator);
 264         var maybe_runpath_offset: ?usize = null;
 265         var maybe_rpath_offset: ?usize = null;
 266         var init: usize = 0;
 267         var init_array: usize = 0;
 268         var init_array_size: usize = 0;
 269         var fini: usize = 0;
 270         var fini_array: usize = 0;
 271         var fini_array_size: usize = 0;
 272         const dynv: [*]usize = @ptrFromInt(load_bias + dynamic);
 273         i = 0;
 274         while (dynv[i] != 0) : (i += 2) {
 275             const value = dynv[i + 1];
 276             switch (dynv[i]) {
 277                 elf.DT_NEEDED => try needed_offsets.append(allocator, value),
 278                 elf.DT_STRTAB => maybe_strings = @ptrFromInt(load_bias + value),
 279                 elf.DT_SYMTAB => maybe_syms = @ptrFromInt(load_bias + value),
 280                 elf.DT_HASH => {
 281                     const hash: [*]const u32 = @ptrFromInt(load_bias + value);
 282                     maybe_symbol_count = hash[1];
 283                 },
 284                 elf.DT_GNU_HASH => maybe_gnu_hash = @ptrFromInt(load_bias + value),
 285                 elf.DT_VERSYM => maybe_versyms = @ptrFromInt(load_bias + value),
 286                 elf.DT_VERDEF => verdef = value,
 287                 elf.DT_VERDEFNUM => verdef_count = value,
 288                 elf.DT_VERNEED => verneed = value,
 289                 elf.DT_VERNEEDNUM => verneed_count = value,
 290                 elf.DT_RUNPATH => maybe_runpath_offset = value,
 291                 elf.DT_RPATH => maybe_rpath_offset = value,
 292                 elf.DT_INIT => init = value,
 293                 elf.DT_INIT_ARRAY => init_array = value,
 294                 elf.DT_INIT_ARRAYSZ => init_array_size = value,
 295                 elf.DT_FINI => fini = value,
 296                 elf.DT_FINI_ARRAY => fini_array = value,
 297                 elf.DT_FINI_ARRAYSZ => fini_array_size = value,
 298                 else => {},
 299             }
 300         }
 301 
 302         const strings = maybe_strings orelse return error.NativeElfStringSectionNotFound;
 303         const syms = maybe_syms orelse return error.NativeElfSymbolSectionNotFound;
 304         const symbol_count = if (maybe_gnu_hash) |gnu_hash|
 305             try gnuHashSymbolCount(gnu_hash)
 306         else
 307             maybe_symbol_count orelse return error.NativeElfHashTableNotFound;
 308         const runpath = if (maybe_runpath_offset) |offset|
 309             std.mem.sliceTo(strings + offset, 0)
 310         else
 311             null;
 312         const rpath = if (maybe_rpath_offset) |offset|
 313             std.mem.sliceTo(strings + offset, 0)
 314         else
 315             null;
 316         const dependencies = try loadNativeElfDependencies(allocator, path, strings, needed_offsets.items, rpath, runpath);
 317         errdefer closeNativeElfDependencies(allocator, dependencies);
 318         try applyNativeElfRelocations(load_bias, dynamic, syms, strings, symbol_count, maybe_versyms, verneed, verneed_count, &tls, dependencies);
 319         try protectNativeElfRelro(load_bias, relro_vaddr, relro_memsz, page_size);
 320         try runNativeElfInitializers(load_bias, init, init_array, init_array_size);
 321 
 322         return .{
 323             .allocator = allocator,
 324             .memory = memory,
 325             .load_bias = load_bias,
 326             .strings = strings,
 327             .syms = syms,
 328             .symbol_count = symbol_count,
 329             .versyms = maybe_versyms,
 330             .verdef = verdef,
 331             .verdef_count = verdef_count,
 332             .dependencies = dependencies,
 333             .tls = tls,
 334             .fini = fini,
 335             .fini_array = fini_array,
 336             .fini_array_count = fini_array_size / @sizeOf(usize),
 337         };
 338     }
 339 
 340     pub fn close(self: *NativeElfLibrary) void {
 341         runNativeElfFinalizers(self.load_bias, self.fini, self.fini_array, self.fini_array_count);
 342         self.tls.deinit(self.allocator);
 343         closeNativeElfDependencies(self.allocator, self.dependencies);
 344         sys_memory.unmap(self.memory);
 345         self.* = undefined;
 346     }
 347 
 348     pub fn lookup(self: *const NativeElfLibrary, comptime T: type, name: [:0]const u8) ?T {
 349         return if (self.lookupAddress(name)) |address| @as(T, @ptrFromInt(address)) else null;
 350     }
 351 
 352     fn lookupAddress(self: *const NativeElfLibrary, name: []const u8) ?usize {
 353         return self.lookupAddressVersioned(name, null);
 354     }
 355 
 356     fn lookupAddressVersioned(self: *const NativeElfLibrary, name: []const u8, version: ?[]const u8) ?usize {
 357         return lookupNativeElfAddress(self.load_bias, self.syms, self.strings, self.symbol_count, self.versyms, self.verdef, self.verdef_count, name, version);
 358     }
 359 
 360     fn lookupTlsSymbolVersioned(self: *const NativeElfLibrary, name: []const u8, version: ?[]const u8) ?NativeElfTlsSymbol {
 361         return lookupNativeElfTlsSymbol(self.load_bias, self.syms, self.strings, self.symbol_count, self.versyms, self.verdef, self.verdef_count, self.tls, name, version);
 362     }
 363 };
 364 
 365 const NativeElfTlsImage = struct {
 366     vaddr: usize,
 367     filesz: usize,
 368     memsz: usize,
 369     offset: usize,
 370     alignment: std.mem.Alignment,
 371 
 372     fn fromProgramHeader(ph: *const elf.Phdr, file_len: usize) NativeElfError!NativeElfTlsImage {
 373         const vaddr = std.math.cast(usize, ph.p_vaddr) orelse return error.InvalidElfImage;
 374         const filesz = std.math.cast(usize, ph.p_filesz) orelse return error.InvalidElfImage;
 375         const memsz = std.math.cast(usize, ph.p_memsz) orelse return error.InvalidElfImage;
 376         const offset = std.math.cast(usize, ph.p_offset) orelse return error.InvalidElfImage;
 377         const alignment_value = std.math.cast(usize, ph.p_align) orelse return error.InvalidElfImage;
 378         if (memsz < filesz) return error.InvalidElfImage;
 379         if (offset > file_len or filesz > file_len - offset) return error.InvalidElfImage;
 380         return .{
 381             .vaddr = vaddr,
 382             .filesz = filesz,
 383             .memsz = memsz,
 384             .offset = offset,
 385             .alignment = try nativeElfTlsAlignment(alignment_value),
 386         };
 387     }
 388 };
 389 
 390 const NativeElfTlsAllocation = struct {
 391     module_id: usize = 0,
 392     payload_len: usize = 0,
 393 
 394     fn init(allocator: std.mem.Allocator, maybe_image: ?NativeElfTlsImage, load_bias: usize) NativeElfError!NativeElfTlsAllocation {
 395         const image = maybe_image orelse return .{};
 396         const template_address = checkedAdd(load_bias, image.vaddr) orelse return error.InvalidElfImage;
 397         const module_id = try native_elf_tls_registry.register(allocator, .{
 398             .module_id = 0,
 399             .template_address = template_address,
 400             .filesz = image.filesz,
 401             .memsz = image.memsz,
 402             .alignment = image.alignment,
 403         });
 404         return .{
 405             .module_id = module_id,
 406             .payload_len = image.memsz,
 407         };
 408     }
 409 
 410     fn deinit(self: *NativeElfTlsAllocation, allocator: std.mem.Allocator) void {
 411         if (self.module_id == 0) return;
 412         native_elf_tls_registry.unregister(allocator, self.module_id);
 413         self.* = .{};
 414     }
 415 
 416     fn active(self: *const NativeElfTlsAllocation) bool {
 417         return self.module_id != 0;
 418     }
 419 };
 420 
 421 const NativeElfTlsSymbol = struct {
 422     module_id: usize,
 423     offset: usize,
 424     payload_len: usize,
 425 };
 426 
 427 const NativeElfTlsRecord = struct {
 428     module_id: usize,
 429     template_address: usize,
 430     filesz: usize,
 431     memsz: usize,
 432     alignment: std.mem.Alignment,
 433     blocks: std.ArrayList(NativeElfThreadTlsBlock) = .empty,
 434 
 435     fn address(self: *NativeElfTlsRecord, allocator: std.mem.Allocator, thread_id: std.Thread.Id, offset: usize) NativeElfError!usize {
 436         for (self.blocks.items) |*block| {
 437             if (block.thread_id != thread_id) continue;
 438             return block.address(offset) orelse return error.NativeElfUnsupportedRelocation;
 439         }
 440         var block = try NativeElfThreadTlsBlock.init(allocator, self, thread_id);
 441         errdefer block.deinit(allocator);
 442         const block_address = block.address(offset) orelse return error.NativeElfUnsupportedRelocation;
 443         try self.blocks.append(allocator, block);
 444         return block_address;
 445     }
 446 
 447     fn deinit(self: *NativeElfTlsRecord, allocator: std.mem.Allocator) void {
 448         for (self.blocks.items) |*block| block.deinit(allocator);
 449         self.blocks.deinit(allocator);
 450         self.* = undefined;
 451     }
 452 };
 453 
 454 const NativeElfThreadTlsBlock = struct {
 455     module_id: usize,
 456     thread_id: std.Thread.Id,
 457     memory: []u8,
 458     payload_len: usize,
 459     alignment: std.mem.Alignment,
 460 
 461     fn init(allocator: std.mem.Allocator, module: *const NativeElfTlsRecord, thread_id: std.Thread.Id) NativeElfError!NativeElfThreadTlsBlock {
 462         const allocation_len = @max(module.memsz, 1);
 463         const ptr = allocator.rawAlloc(allocation_len, module.alignment, @returnAddress()) orelse return error.OutOfMemory;
 464         errdefer allocator.rawFree(ptr[0..allocation_len], module.alignment, @returnAddress());
 465         const memory = ptr[0..allocation_len];
 466         if (module.filesz != 0) {
 467             const template: [*]const u8 = @ptrFromInt(module.template_address);
 468             @memcpy(memory[0..module.filesz], template[0..module.filesz]);
 469         }
 470         if (module.memsz > module.filesz) {
 471             @memset(memory[module.filesz..module.memsz], 0);
 472         }
 473         return .{
 474             .module_id = module.module_id,
 475             .thread_id = thread_id,
 476             .memory = memory,
 477             .payload_len = module.memsz,
 478             .alignment = module.alignment,
 479         };
 480     }
 481 
 482     fn deinit(self: *NativeElfThreadTlsBlock, allocator: std.mem.Allocator) void {
 483         allocator.rawFree(self.memory, self.alignment, @returnAddress());
 484         self.* = undefined;
 485     }
 486 
 487     fn address(self: *const NativeElfThreadTlsBlock, offset: usize) ?usize {
 488         if (offset >= self.payload_len) return null;
 489         return checkedAdd(@intFromPtr(self.memory.ptr), offset);
 490     }
 491 };
 492 
 493 const NativeElfTlsRegistry = struct {
 494     mutex: std.atomic.Mutex = .unlocked,
 495     next_module_id: usize = 1,
 496     records: std.ArrayList(NativeElfTlsRecord) = .empty,
 497 
 498     fn register(self: *NativeElfTlsRegistry, allocator: std.mem.Allocator, record: NativeElfTlsRecord) NativeElfError!usize {
 499         self.lock();
 500         defer self.mutex.unlock();
 501         const module_id = self.next_module_id;
 502         const next_module_id = checkedAdd(module_id, 1) orelse return error.NativeElfUnsupported;
 503         var module_record = record;
 504         module_record.module_id = module_id;
 505         try self.records.append(allocator, module_record);
 506         self.next_module_id = next_module_id;
 507         return module_id;
 508     }
 509 
 510     fn unregister(self: *NativeElfTlsRegistry, allocator: std.mem.Allocator, module_id: usize) void {
 511         self.lock();
 512         defer self.mutex.unlock();
 513         var index: usize = 0;
 514         while (index < self.records.items.len) : (index += 1) {
 515             if (self.records.items[index].module_id == module_id) {
 516                 var record = self.records.swapRemove(index);
 517                 record.deinit(allocator);
 518                 return;
 519             }
 520         }
 521     }
 522 
 523     fn address(self: *NativeElfTlsRegistry, allocator: std.mem.Allocator, module_id: usize, thread_id: std.Thread.Id, offset: usize) NativeElfError!usize {
 524         self.lock();
 525         defer self.mutex.unlock();
 526         for (self.records.items) |*record| {
 527             if (record.module_id != module_id) continue;
 528             return record.address(allocator, thread_id, offset);
 529         }
 530         return error.NativeElfUnsupportedRelocation;
 531     }
 532 
 533     fn lock(self: *NativeElfTlsRegistry) void {
 534         while (!self.mutex.tryLock()) std.atomic.spinLoopHint();
 535     }
 536 };
 537 
 538 const NativeElfTlsIndex = extern struct {
 539     module_id: usize,
 540     offset: usize,
 541 };
 542 
 543 var native_elf_tls_registry: NativeElfTlsRegistry = .{};
 544 
 545 fn nativeElfTlsGetAddr(index: *const NativeElfTlsIndex) callconv(.c) *anyopaque {
 546     const address = native_elf_tls_registry.address(sys_allocator.processAllocator(), index.module_id, std.Thread.getCurrentId(), index.offset) catch @trap();
 547     return @ptrFromInt(address);
 548 }
 549 
 550 fn nativeElfTlsAlignment(alignment_value: usize) NativeElfError!std.mem.Alignment {
 551     if (alignment_value == 0) return .@"1";
 552     if (!std.math.isPowerOfTwo(alignment_value)) return error.InvalidElfImage;
 553     return .fromByteUnits(alignment_value);
 554 }
 555 
 556 fn canUseNativeElfTls() bool {
 557     return builtin.cpu.arch == .x86_64;
 558 }
 559 
 560 fn lookupNativeElfAddress(
 561     load_bias: usize,
 562     syms: [*]elf.Sym,
 563     strings: [*:0]u8,
 564     symbol_count: usize,
 565     versyms: ?[*]elf.Versym,
 566     verdef: usize,
 567     verdef_count: usize,
 568     name: []const u8,
 569     version: ?[]const u8,
 570 ) ?usize {
 571     const ok_types = (1 << elf.STT_NOTYPE | 1 << elf.STT_OBJECT | 1 << elf.STT_FUNC | 1 << elf.STT_COMMON | 1 << elf.STT_GNU_IFUNC);
 572     const ok_binds = (1 << elf.STB_GLOBAL | 1 << elf.STB_WEAK | 1 << elf.STB_GNU_UNIQUE);
 573 
 574     var i: usize = 0;
 575     while (i < symbol_count) : (i += 1) {
 576         const symbol = syms[i];
 577         if (symbol.st_shndx == 0) continue;
 578         if (0 == (@as(u32, 1) << @as(u5, @intCast(nativeElfSymbolType(symbol))) & ok_types)) continue;
 579         if (0 == (@as(u32, 1) << @as(u5, @intCast(symbol.st_info >> 4)) & ok_binds)) continue;
 580         if (!std.mem.eql(u8, name, std.mem.sliceTo(strings + symbol.st_name, 0))) continue;
 581         if (version) |requested| {
 582             if (!nativeElfSymbolMatchesVersion(load_bias, strings, versyms, verdef, verdef_count, i, requested)) continue;
 583         } else if (!nativeElfSymbolHasDefaultVersion(versyms, i)) continue;
 584         return nativeElfSymbolAddress(load_bias, symbol);
 585     }
 586 
 587     return null;
 588 }
 589 
 590 fn nativeElfSymbolType(symbol: elf.Sym) u8 {
 591     return symbol.st_info & 0xf;
 592 }
 593 
 594 fn nativeElfSymbolBind(symbol: elf.Sym) u8 {
 595     return symbol.st_info >> 4;
 596 }
 597 
 598 fn nativeElfSymbolAddress(load_bias: usize, symbol: elf.Sym) usize {
 599     const address = load_bias + symbol.st_value;
 600     if (nativeElfSymbolType(symbol) == elf.STT_GNU_IFUNC) return @as(NativeElfIfuncResolver, @ptrFromInt(address))();
 601     return address;
 602 }
 603 
 604 fn nativeElfSymbolHasDefaultVersion(versyms: ?[*]elf.Versym, symbol_index: usize) bool {
 605     const table = versyms orelse return true;
 606     return !table[symbol_index].HIDDEN;
 607 }
 608 
 609 fn nativeElfSymbolMatchesVersion(
 610     base: usize,
 611     strings: [*:0]u8,
 612     versyms: ?[*]elf.Versym,
 613     verdef: usize,
 614     verdef_count: usize,
 615     symbol_index: usize,
 616     requested: []const u8,
 617 ) bool {
 618     const table = versyms orelse return false;
 619     const version_name = nativeElfDefinedVersionName(base, strings, table[symbol_index], verdef, verdef_count) orelse return false;
 620     return std.mem.eql(u8, requested, version_name);
 621 }
 622 
 623 fn nativeElfDefinedVersionName(base: usize, strings: [*:0]u8, versym: elf.Versym, verdef: usize, verdef_count: usize) ?[]const u8 {
 624     if (versym.VERSION <= @backingInt(elf.VER_NDX.GLOBAL) or verdef == 0) return null;
 625 
 626     var definition_addr = base + verdef;
 627     var index: usize = 0;
 628     while (index < verdef_count) : (index += 1) {
 629         const definition: *const elf.Verdef = @ptrFromInt(definition_addr);
 630         if (@backingInt(definition.ndx) == versym.VERSION) {
 631             const aux: *const elf.Verdaux = @ptrFromInt(definition_addr + definition.aux);
 632             return std.mem.sliceTo(strings + aux.name, 0);
 633         }
 634         if (definition.next == 0) break;
 635         definition_addr += definition.next;
 636     }
 637     return null;
 638 }
 639 
 640 fn lookupNativeElfTlsSymbol(
 641     load_bias: usize,
 642     syms: [*]elf.Sym,
 643     strings: [*:0]u8,
 644     symbol_count: usize,
 645     versyms: ?[*]elf.Versym,
 646     verdef: usize,
 647     verdef_count: usize,
 648     tls: NativeElfTlsAllocation,
 649     name: []const u8,
 650     version: ?[]const u8,
 651 ) ?NativeElfTlsSymbol {
 652     if (!tls.active()) return null;
 653     const ok_binds = (1 << elf.STB_GLOBAL | 1 << elf.STB_WEAK | 1 << elf.STB_GNU_UNIQUE);
 654 
 655     var i: usize = 0;
 656     while (i < symbol_count) : (i += 1) {
 657         const symbol = syms[i];
 658         if (symbol.st_shndx == 0) continue;
 659         if (symbol.st_info & 0xf != elf.STT_TLS) continue;
 660         if (0 == (@as(u32, 1) << @as(u5, @intCast(symbol.st_info >> 4)) & ok_binds)) continue;
 661         if (!std.mem.eql(u8, name, std.mem.sliceTo(strings + symbol.st_name, 0))) continue;
 662         if (version) |requested| {
 663             if (!nativeElfSymbolMatchesVersion(load_bias, strings, versyms, verdef, verdef_count, i, requested)) continue;
 664         } else if (!nativeElfSymbolHasDefaultVersion(versyms, i)) continue;
 665         const symbol_offset = std.math.cast(usize, symbol.st_value) orelse return null;
 666         if (symbol_offset >= tls.payload_len) return null;
 667         return .{
 668             .module_id = tls.module_id,
 669             .offset = symbol_offset,
 670             .payload_len = tls.payload_len,
 671         };
 672     }
 673 
 674     return null;
 675 }
 676 
 677 fn gnuHashSymbolCount(header: *elf.gnu_hash.Header) NativeElfError!usize {
 678     const header_offset = @intFromPtr(header);
 679     const bloom_offset = header_offset + @sizeOf(elf.gnu_hash.Header);
 680     const buckets_offset = bloom_offset + header.bloom_size * @sizeOf(usize);
 681     const chain_offset = buckets_offset + header.nbuckets * @sizeOf(u32);
 682     const buckets: [*]const u32 = @ptrFromInt(buckets_offset);
 683     const chains: [*]const elf.gnu_hash.ChainEntry = @ptrFromInt(chain_offset);
 684 
 685     var symbol_count: usize = header.symoffset;
 686     for (buckets[0..header.nbuckets]) |bucket| {
 687         if (bucket == 0) continue;
 688         if (bucket < header.symoffset) return error.InvalidElfImage;
 689         var chain_index: usize = bucket - header.symoffset;
 690         while (true) : (chain_index += 1) {
 691             symbol_count = @max(symbol_count, chain_index + header.symoffset + 1);
 692             if (chains[chain_index].end_of_chain) break;
 693         }
 694     }
 695     return symbol_count;
 696 }
 697 
 698 fn canUseNativeElfPlatform() bool {
 699     return builtin.os.tag == .linux;
 700 }
 701 
 702 fn hasLibraryPathSeparator(path: []const u8) bool {
 703     return std.mem.indexOfScalar(u8, path, '/') != null;
 704 }
 705 
 706 fn loadSegments(
 707     file_handle: std.posix.fd_t,
 708     file_bytes: []align(std.heap.page_size_min) const u8,
 709     file_len: usize,
 710     load_bias: usize,
 711     eh: *const elf.Ehdr,
 712     page_size: usize,
 713 ) NativeElfError!void {
 714     var i: usize = 0;
 715     var ph_addr = @intFromPtr(file_bytes.ptr) + eh.e_phoff;
 716     while (i < eh.e_phnum) : ({
 717         i += 1;
 718         ph_addr += eh.e_phentsize;
 719     }) {
 720         const ph: *const elf.Phdr = @ptrFromInt(ph_addr);
 721         if (ph.p_type != elf.PT_LOAD) continue;
 722 
 723         const vaddr = std.math.cast(usize, ph.p_vaddr) orelse return error.InvalidElfImage;
 724         const memsz = std.math.cast(usize, ph.p_memsz) orelse return error.InvalidElfImage;
 725         const filesz = std.math.cast(usize, ph.p_filesz) orelse return error.InvalidElfImage;
 726         const offset = std.math.cast(usize, ph.p_offset) orelse return error.InvalidElfImage;
 727         if (offset > file_len or filesz > file_len - offset) return error.InvalidElfImage;
 728         const segment_addr = checkedAdd(load_bias, vaddr) orelse return error.InvalidElfImage;
 729         const map_addr = std.mem.alignBackward(usize, segment_addr, page_size);
 730         const page_delta = segment_addr - map_addr;
 731         const map_len = std.mem.alignForward(usize, checkedAdd(page_delta, memsz) orelse return error.InvalidElfImage, page_size);
 732         const ptr: [*]align(std.heap.page_size_min) u8 = @ptrFromInt(map_addr);
 733         const writable = (ph.p_flags & elf.PF_W) != 0;
 734 
 735         if (!writable and memsz == filesz) {
 736             const file_offset = std.mem.alignBackward(usize, offset, page_size);
 737             if (page_delta != offset - file_offset) return error.InvalidElfImage;
 738             _ = try sys_memory.mapPrivateFileFixed(
 739                 file_handle,
 740                 ptr,
 741                 map_len,
 742                 elfToProtection(ph.p_flags),
 743                 file_offset,
 744             );
 745         } else {
 746             const segment = try sys_memory.mapAnonymousFixed(
 747                 ptr,
 748                 map_len,
 749                 .{ .read = true, .write = true, .execute = writable and (ph.p_flags & elf.PF_X) != 0 },
 750             );
 751             @memcpy(segment[page_delta..][0..filesz], file_bytes[offset..][0..filesz]);
 752             if (memsz > filesz) {
 753                 @memset(segment[page_delta + filesz .. page_delta + memsz], 0);
 754             }
 755             if (!writable) {
 756                 try protectNativeElfMemory(segment, elfToProtection(ph.p_flags));
 757             }
 758         }
 759     }
 760 }
 761 
 762 test "native ELF loader zero fills read-only segment tails" {
 763     if (builtin.os.tag != .linux) return error.SkipZigTest;
 764 
 765     var image: [std.heap.page_size_max * 2]u8 align(std.heap.page_size_min) = @splat(0);
 766     const eh: *elf.Ehdr = @ptrCast(&image);
 767     eh.e_phoff = @sizeOf(elf.Ehdr);
 768     eh.e_phentsize = @sizeOf(elf.Phdr);
 769     eh.e_phnum = 1;
 770 
 771     const ph: *elf.Phdr = @ptrCast(@alignCast(image[@sizeOf(elf.Ehdr)..].ptr));
 772     const page_size = std.heap.pageSize();
 773     const source_offset = page_size;
 774     const source = [_]u8{ 0x10, 0x20, 0x30, 0x40 };
 775     @memcpy(image[source_offset..][0..source.len], &source);
 776     ph.* = .{
 777         .p_type = elf.PT_LOAD,
 778         .p_flags = elf.PF_R,
 779         .p_offset = source_offset,
 780         .p_vaddr = 0,
 781         .p_paddr = 0,
 782         .p_filesz = source.len,
 783         .p_memsz = page_size,
 784         .p_align = page_size,
 785     };
 786 
 787     const memory = try sys_memory.mapAnonymous(page_size, .{});
 788     defer sys_memory.unmap(memory);
 789     try loadSegments(-1, &image, image.len, @intFromPtr(memory.ptr), eh, page_size);
 790     try std.testing.expectEqualSlices(u8, &source, memory[0..source.len]);
 791     try std.testing.expect(std.mem.allEqual(u8, memory[source.len..page_size], 0));
 792 }
 793 
 794 fn loadNativeElfDependencies(
 795     allocator: std.mem.Allocator,
 796     owner_path: []const u8,
 797     strings: [*:0]u8,
 798     needed_offsets: []const usize,
 799     rpath: ?[]const u8,
 800     runpath: ?[]const u8,
 801 ) NativeElfError![]NativeElfHandle {
 802     var dependencies: std.ArrayList(NativeElfHandle) = .empty;
 803     errdefer {
 804         for (dependencies.items) |*dependency| dependency.close();
 805         dependencies.deinit(allocator);
 806     }
 807 
 808     for (needed_offsets) |offset| {
 809         const needed_name = std.mem.sliceTo(strings + offset, 0);
 810         const dependency_path = try resolveNeededLibrary(allocator, owner_path, needed_name, rpath, runpath);
 811         defer allocator.free(dependency_path);
 812         try dependencies.append(allocator, try openNativeElfShared(allocator, dependency_path));
 813     }
 814 
 815     return dependencies.toOwnedSlice(allocator);
 816 }
 817 
 818 fn openNativeElfShared(allocator: std.mem.Allocator, path: []const u8) NativeElfError!NativeElfHandle {
 819     if (retainNativeElfShared(path)) |handle| return handle;
 820 
 821     var library = try NativeElfLibrary.openUncached(path);
 822     errdefer library.close();
 823 
 824     const stored_path = try allocator.dupe(u8, path);
 825     errdefer allocator.free(stored_path);
 826 
 827     const shared = try allocator.create(NativeElfSharedLibrary);
 828     errdefer allocator.destroy(shared);
 829     shared.* = .{
 830         .path = stored_path,
 831         .ref_count = 1,
 832         .library = library,
 833     };
 834 
 835     native_elf_registry.lock();
 836     defer native_elf_registry.mutex.unlock();
 837     for (native_elf_registry.libraries.items) |existing| {
 838         if (std.mem.eql(u8, existing.path, path)) {
 839             existing.ref_count += 1;
 840             library.close();
 841             allocator.free(stored_path);
 842             allocator.destroy(shared);
 843             return .{ .shared = existing };
 844         }
 845     }
 846     try native_elf_registry.libraries.append(allocator, shared);
 847     return .{ .shared = shared };
 848 }
 849 
 850 fn retainNativeElfShared(path: []const u8) ?NativeElfHandle {
 851     native_elf_registry.lock();
 852     defer native_elf_registry.mutex.unlock();
 853     for (native_elf_registry.libraries.items) |shared| {
 854         if (!std.mem.eql(u8, shared.path, path)) continue;
 855         shared.ref_count += 1;
 856         return .{ .shared = shared };
 857     }
 858     return null;
 859 }
 860 
 861 fn resolveNativeElfOpenPath(allocator: std.mem.Allocator, path: []const u8) NativeElfError![]u8 {
 862     if (!canUseNativeElfPlatform()) return error.NativeElfUnsupported;
 863     if (hasLibraryPathSeparator(path)) return allocator.dupe(u8, path);
 864     if (path.len == 0) return error.FileNotFound;
 865     return resolvePathlessLibrary(allocator, path);
 866 }
 867 
 868 fn releaseNativeElfShared(shared: *NativeElfSharedLibrary) void {
 869     const allocator = sys_allocator.processAllocator();
 870     var owned: ?*NativeElfSharedLibrary = null;
 871     native_elf_registry.lock();
 872     if (shared.ref_count > 1) {
 873         shared.ref_count -= 1;
 874     } else {
 875         const index = for (native_elf_registry.libraries.items, 0..) |candidate, index| {
 876             if (candidate == shared) break index;
 877         } else native_elf_registry.libraries.items.len;
 878         if (index < native_elf_registry.libraries.items.len) {
 879             _ = native_elf_registry.libraries.swapRemove(index);
 880         }
 881         owned = shared;
 882     }
 883     native_elf_registry.mutex.unlock();
 884 
 885     if (owned) |library| {
 886         library.library.close();
 887         allocator.free(library.path);
 888         allocator.destroy(library);
 889     }
 890 }
 891 
 892 fn closeNativeElfDependencies(allocator: std.mem.Allocator, dependencies: []NativeElfHandle) void {
 893     for (dependencies) |*dependency| dependency.close();
 894     allocator.free(dependencies);
 895 }
 896 
 897 fn resolveNeededLibrary(
 898     allocator: std.mem.Allocator,
 899     owner_path: []const u8,
 900     needed_name: []const u8,
 901     rpath: ?[]const u8,
 902     runpath: ?[]const u8,
 903 ) NativeElfError![]u8 {
 904     if (hasLibraryPathSeparator(needed_name)) return allocator.dupe(u8, needed_name);
 905 
 906     if (rpath != null and runpath == null) {
 907         if (try resolveLibraryInSearchPath(allocator, owner_path, needed_name, rpath.?)) |path| return path;
 908     }
 909 
 910     if (sys_env.getConstant("LD_LIBRARY_PATH")) |paths| {
 911         if (try resolveLibraryInSearchPath(allocator, owner_path, needed_name, paths)) |path| return path;
 912     }
 913 
 914     if (runpath) |paths| {
 915         if (try resolveLibraryInSearchPath(allocator, owner_path, needed_name, paths)) |path| return path;
 916     }
 917 
 918     const origin = std.fs.path.dirname(owner_path) orelse ".";
 919     const origin_candidate = try std.fs.path.join(allocator, &.{ origin, needed_name });
 920     if (pathExists(origin_candidate)) return origin_candidate;
 921     allocator.free(origin_candidate);
 922 
 923     return resolveDefaultLibrary(allocator, needed_name);
 924 }
 925 
 926 fn resolvePathlessLibrary(allocator: std.mem.Allocator, name: []const u8) NativeElfError![]u8 {
 927     if (sys_env.getConstant("LD_LIBRARY_PATH")) |paths| {
 928         if (try resolveLibraryInSearchPath(allocator, ".", name, paths)) |path| return path;
 929     }
 930     return resolveDefaultLibrary(allocator, name);
 931 }
 932 
 933 fn resolveLibraryInSearchPath(
 934     allocator: std.mem.Allocator,
 935     owner_path: []const u8,
 936     name: []const u8,
 937     search_path: []const u8,
 938 ) NativeElfError!?[]u8 {
 939     var iterator = std.mem.splitScalar(u8, search_path, ':');
 940     while (iterator.next()) |entry| {
 941         const path_entry = if (entry.len == 0) "." else entry;
 942         const expanded = try expandLibrarySearchEntry(allocator, owner_path, path_entry);
 943         defer allocator.free(expanded);
 944         const candidate = try std.fs.path.join(allocator, &.{ expanded, name });
 945         if (pathExists(candidate)) return candidate;
 946         allocator.free(candidate);
 947     }
 948     return null;
 949 }
 950 
 951 fn resolveDefaultLibrary(allocator: std.mem.Allocator, name: []const u8) NativeElfError![]u8 {
 952     for (nativeDefaultLibraryDirectories()) |directory| {
 953         const candidate = try std.fs.path.join(allocator, &.{ directory, name });
 954         if (pathExists(candidate)) return candidate;
 955         allocator.free(candidate);
 956     }
 957     return error.FileNotFound;
 958 }
 959 
 960 const generic_library_directories = [_][]const u8{
 961     "/lib",
 962     "/usr/lib",
 963     "/usr/local/lib",
 964     "/lib64",
 965     "/usr/lib64",
 966     "/usr/local/lib64",
 967 };
 968 
 969 const x86_64_linux_library_directories = [_][]const u8{
 970     "/lib/x86_64-linux-gnu",
 971     "/usr/lib/x86_64-linux-gnu",
 972     "/usr/local/lib/x86_64-linux-gnu",
 973     "/lib",
 974     "/usr/lib",
 975     "/usr/local/lib",
 976     "/lib64",
 977     "/usr/lib64",
 978     "/usr/local/lib64",
 979 };
 980 
 981 const x86_linux_library_directories = [_][]const u8{
 982     "/lib/i386-linux-gnu",
 983     "/usr/lib/i386-linux-gnu",
 984     "/lib/i686-linux-gnu",
 985     "/usr/lib/i686-linux-gnu",
 986     "/lib",
 987     "/usr/lib",
 988     "/usr/local/lib",
 989 };
 990 
 991 const aarch64_linux_library_directories = [_][]const u8{
 992     "/lib/aarch64-linux-gnu",
 993     "/usr/lib/aarch64-linux-gnu",
 994     "/usr/local/lib/aarch64-linux-gnu",
 995     "/lib",
 996     "/usr/lib",
 997     "/usr/local/lib",
 998 };
 999 
1000 const arm_linux_library_directories = [_][]const u8{
1001     "/lib/arm-linux-gnueabihf",
1002     "/usr/lib/arm-linux-gnueabihf",
1003     "/lib/arm-linux-gnueabi",
1004     "/usr/lib/arm-linux-gnueabi",
1005     "/lib",
1006     "/usr/lib",
1007     "/usr/local/lib",
1008 };
1009 
1010 const riscv64_linux_library_directories = [_][]const u8{
1011     "/lib/riscv64-linux-gnu",
1012     "/usr/lib/riscv64-linux-gnu",
1013     "/usr/local/lib/riscv64-linux-gnu",
1014     "/lib",
1015     "/usr/lib",
1016     "/usr/local/lib",
1017 };
1018 
1019 const powerpc64le_linux_library_directories = [_][]const u8{
1020     "/lib/powerpc64le-linux-gnu",
1021     "/usr/lib/powerpc64le-linux-gnu",
1022     "/usr/local/lib/powerpc64le-linux-gnu",
1023     "/lib",
1024     "/usr/lib",
1025     "/usr/local/lib",
1026 };
1027 
1028 const s390x_linux_library_directories = [_][]const u8{
1029     "/lib/s390x-linux-gnu",
1030     "/usr/lib/s390x-linux-gnu",
1031     "/usr/local/lib/s390x-linux-gnu",
1032     "/lib",
1033     "/usr/lib",
1034     "/usr/local/lib",
1035 };
1036 
1037 const loongarch64_linux_library_directories = [_][]const u8{
1038     "/lib/loongarch64-linux-gnu",
1039     "/usr/lib/loongarch64-linux-gnu",
1040     "/usr/local/lib/loongarch64-linux-gnu",
1041     "/lib",
1042     "/usr/lib",
1043     "/usr/local/lib",
1044 };
1045 
1046 fn nativeDefaultLibraryDirectories() []const []const u8 {
1047     if (comptime builtin.os.tag != .linux) return &generic_library_directories;
1048     return switch (builtin.cpu.arch) {
1049         .x86_64 => &x86_64_linux_library_directories,
1050         .x86 => &x86_linux_library_directories,
1051         .aarch64, .aarch64_be => &aarch64_linux_library_directories,
1052         .arm, .armeb, .thumb, .thumbeb => &arm_linux_library_directories,
1053         .riscv64, .riscv64be => &riscv64_linux_library_directories,
1054         .powerpc64le => &powerpc64le_linux_library_directories,
1055         .s390x => &s390x_linux_library_directories,
1056         .loongarch64 => &loongarch64_linux_library_directories,
1057         else => &generic_library_directories,
1058     };
1059 }
1060 
1061 fn nativeDefaultLibraryDirectoriesContain(path: []const u8) bool {
1062     for (nativeDefaultLibraryDirectories()) |directory| {
1063         if (std.mem.eql(u8, directory, path)) return true;
1064     }
1065     return false;
1066 }
1067 
1068 fn expandLibrarySearchEntry(
1069     allocator: std.mem.Allocator,
1070     owner_path: []const u8,
1071     entry: []const u8,
1072 ) NativeElfError![]u8 {
1073     if (!std.mem.startsWith(u8, entry, "$ORIGIN")) return allocator.dupe(u8, entry);
1074 
1075     const origin = std.fs.path.dirname(owner_path) orelse ".";
1076     if (entry.len == "$ORIGIN".len) return allocator.dupe(u8, origin);
1077     if (entry.len > "$ORIGIN".len and entry["$ORIGIN".len] == '/') {
1078         return std.fs.path.join(allocator, &.{ origin, entry["$ORIGIN".len + 1 ..] });
1079     }
1080     return allocator.dupe(u8, entry);
1081 }
1082 
1083 fn pathExists(path: []const u8) bool {
1084     const io = std.Options.debug_io;
1085     var file = if (path.len > 0 and path[0] == '/')
1086         std.Io.Dir.openFileAbsolute(io, path, .{}) catch return false
1087     else
1088         std.Io.Dir.cwd().openFile(io, path, .{}) catch return false;
1089     file.close(io);
1090     return true;
1091 }
1092 
1093 fn applyNativeElfRelocations(
1094     base: usize,
1095     dynamic_vaddr: usize,
1096     syms: [*]elf.Sym,
1097     strings: [*:0]u8,
1098     symbol_count: usize,
1099     versyms: ?[*]elf.Versym,
1100     verneed: usize,
1101     verneed_count: usize,
1102     tls: *const NativeElfTlsAllocation,
1103     dependencies: []const NativeElfHandle,
1104 ) NativeElfError!void {
1105     var rela: usize = 0;
1106     var rela_size: usize = 0;
1107     var rela_entry_size: usize = @sizeOf(elf.Rela);
1108     var rel: usize = 0;
1109     var rel_size: usize = 0;
1110     var rel_entry_size: usize = @sizeOf(elf.Rel);
1111     var relr: usize = 0;
1112     var relr_size: usize = 0;
1113     var relr_entry_size: usize = @sizeOf(elf.Relr);
1114     var jmprel: usize = 0;
1115     var pltrel_size: usize = 0;
1116     var pltrel: usize = elf.DT_RELA;
1117 
1118     const dynv: [*]usize = @ptrFromInt(base + dynamic_vaddr);
1119     var i: usize = 0;
1120     while (dynv[i] != 0) : (i += 2) {
1121         switch (dynv[i]) {
1122             elf.DT_RELA => rela = dynv[i + 1],
1123             elf.DT_RELASZ => rela_size = dynv[i + 1],
1124             elf.DT_RELAENT => rela_entry_size = dynv[i + 1],
1125             elf.DT_REL => rel = dynv[i + 1],
1126             elf.DT_RELSZ => rel_size = dynv[i + 1],
1127             elf.DT_RELENT => rel_entry_size = dynv[i + 1],
1128             elf.DT_RELR => relr = dynv[i + 1],
1129             elf.DT_RELRSZ => relr_size = dynv[i + 1],
1130             elf.DT_RELRENT => relr_entry_size = dynv[i + 1],
1131             elf.DT_JMPREL => jmprel = dynv[i + 1],
1132             elf.DT_PLTRELSZ => pltrel_size = dynv[i + 1],
1133             elf.DT_PLTREL => pltrel = dynv[i + 1],
1134             else => {},
1135         }
1136     }
1137 
1138     if (rel != 0) {
1139         if (rel_entry_size != @sizeOf(elf.Rel)) return error.NativeElfUnsupportedRelocation;
1140         const rels: []const elf.Rel = @ptrCast(@alignCast(
1141             @as([*]align(@alignOf(elf.Rel)) const u8, @ptrFromInt(base + rel))[0..rel_size],
1142         ));
1143         for (rels) |r| {
1144             try applyNativeElfRel(base, syms, strings, symbol_count, versyms, verneed, verneed_count, tls, dependencies, r);
1145         }
1146     }
1147 
1148     if (rela != 0) {
1149         if (rela_entry_size != @sizeOf(elf.Rela)) return error.NativeElfUnsupportedRelocation;
1150         const relas: []const elf.Rela = @ptrCast(@alignCast(
1151             @as([*]align(@alignOf(elf.Rela)) const u8, @ptrFromInt(base + rela))[0..rela_size],
1152         ));
1153         for (relas) |r| {
1154             try applyNativeElfRela(base, syms, strings, symbol_count, versyms, verneed, verneed_count, tls, dependencies, r);
1155         }
1156     }
1157 
1158     if (jmprel != 0 or pltrel_size != 0) {
1159         switch (pltrel) {
1160             elf.DT_REL => {
1161                 if (pltrel_size % @sizeOf(elf.Rel) != 0) return error.NativeElfUnsupportedRelocation;
1162                 const rels: []const elf.Rel = @ptrCast(@alignCast(
1163                     @as([*]align(@alignOf(elf.Rel)) const u8, @ptrFromInt(base + jmprel))[0..pltrel_size],
1164                 ));
1165                 for (rels) |r| {
1166                     try applyNativeElfRel(base, syms, strings, symbol_count, versyms, verneed, verneed_count, tls, dependencies, r);
1167                 }
1168             },
1169             elf.DT_RELA => {
1170                 if (pltrel_size % @sizeOf(elf.Rela) != 0) return error.NativeElfUnsupportedRelocation;
1171                 const relas: []const elf.Rela = @ptrCast(@alignCast(
1172                     @as([*]align(@alignOf(elf.Rela)) const u8, @ptrFromInt(base + jmprel))[0..pltrel_size],
1173                 ));
1174                 for (relas) |r| {
1175                     try applyNativeElfRela(base, syms, strings, symbol_count, versyms, verneed, verneed_count, tls, dependencies, r);
1176                 }
1177             },
1178             else => return error.NativeElfUnsupportedRelocation,
1179         }
1180     }
1181 
1182     if (relr != 0) {
1183         applyRelativeRelrRelocations(base, relr, relr_size, relr_entry_size) catch return error.NativeElfUnsupportedRelocation;
1184     }
1185 }
1186 
1187 fn applyNativeElfRel(
1188     base: usize,
1189     syms: [*]elf.Sym,
1190     strings: [*:0]u8,
1191     symbol_count: usize,
1192     versyms: ?[*]elf.Versym,
1193     verneed: usize,
1194     verneed_count: usize,
1195     tls: *const NativeElfTlsAllocation,
1196     dependencies: []const NativeElfHandle,
1197     relocation: elf.Rel,
1198 ) NativeElfError!void {
1199     const target: *usize = @ptrFromInt(base + relocation.r_offset);
1200     switch (relocation.r_type()) {
1201         relativeRelocationKind() => target.* += base,
1202         else => try applyNativeElfSymbolRelocation(base, syms, strings, symbol_count, versyms, verneed, verneed_count, tls, dependencies, relocation.r_sym(), relocation.r_type(), relocation.r_offset, 0),
1203     }
1204 }
1205 
1206 fn applyNativeElfRela(
1207     base: usize,
1208     syms: [*]elf.Sym,
1209     strings: [*:0]u8,
1210     symbol_count: usize,
1211     versyms: ?[*]elf.Versym,
1212     verneed: usize,
1213     verneed_count: usize,
1214     tls: *const NativeElfTlsAllocation,
1215     dependencies: []const NativeElfHandle,
1216     relocation: elf.Rela,
1217 ) NativeElfError!void {
1218     if (irelativeRelocationKind()) |irelative_kind| {
1219         if (relocation.r_type() == irelative_kind) {
1220             if (relocation.r_sym() != 0) return error.NativeElfUnsupportedRelocation;
1221             const resolver_address = try addSigned(base, relocation.r_addend);
1222             @as(*usize, @ptrFromInt(base + relocation.r_offset)).* = @as(NativeElfIfuncResolver, @ptrFromInt(resolver_address))();
1223             return;
1224         }
1225     }
1226     switch (relocation.r_type()) {
1227         relativeRelocationKind() => @as(*usize, @ptrFromInt(base + relocation.r_offset)).* = try addSigned(base, relocation.r_addend),
1228         else => try applyNativeElfSymbolRelocation(base, syms, strings, symbol_count, versyms, verneed, verneed_count, tls, dependencies, relocation.r_sym(), relocation.r_type(), relocation.r_offset, relocation.r_addend),
1229     }
1230 }
1231 
1232 fn applyNativeElfSymbolRelocation(
1233     base: usize,
1234     syms: [*]elf.Sym,
1235     strings: [*:0]u8,
1236     symbol_count: usize,
1237     versyms: ?[*]elf.Versym,
1238     verneed: usize,
1239     verneed_count: usize,
1240     tls: *const NativeElfTlsAllocation,
1241     dependencies: []const NativeElfHandle,
1242     symbol_index: usize,
1243     relocation_kind: u32,
1244     offset: usize,
1245     addend: isize,
1246 ) NativeElfError!void {
1247     const absolute_kind = absoluteRelocationKind();
1248     const glob_dat_kind = globDatRelocationKind();
1249     const jump_slot_kind = jumpSlotRelocationKind();
1250     if (try applyNativeElfTlsRelocation(base, syms, strings, symbol_count, versyms, verneed, verneed_count, tls, dependencies, symbol_index, relocation_kind, offset, addend)) return;
1251     if ((absolute_kind == null or relocation_kind != absolute_kind.?) and
1252         (glob_dat_kind == null or relocation_kind != glob_dat_kind.?) and
1253         (jump_slot_kind == null or relocation_kind != jump_slot_kind.?))
1254     {
1255         return error.NativeElfUnsupportedRelocation;
1256     }
1257     if (symbol_index >= symbol_count) return error.NativeElfUnsupportedRelocation;
1258 
1259     const symbol = syms[symbol_index];
1260     const name = std.mem.sliceTo(strings + symbol.st_name, 0);
1261     const address = if (symbol.st_shndx != 0)
1262         nativeElfSymbolAddress(base, symbol)
1263     else blk: {
1264         if (nativeElfBuiltinSymbolAddress(name)) |address| break :blk address;
1265         const version = try nativeElfNeededVersionName(base, strings, versyms, verneed, verneed_count, symbol_index);
1266         if (resolveNativeElfDependencySymbol(dependencies, name, version)) |address| break :blk address;
1267         if (nativeElfSymbolBind(symbol) == elf.STB_WEAK) break :blk 0;
1268         return error.NativeElfUnsupportedRelocation;
1269     };
1270     @as(*usize, @ptrFromInt(base + offset)).* = try addSigned(address, addend);
1271 }
1272 
1273 fn applyNativeElfTlsRelocation(
1274     base: usize,
1275     syms: [*]elf.Sym,
1276     strings: [*:0]u8,
1277     symbol_count: usize,
1278     versyms: ?[*]elf.Versym,
1279     verneed: usize,
1280     verneed_count: usize,
1281     tls: *const NativeElfTlsAllocation,
1282     dependencies: []const NativeElfHandle,
1283     symbol_index: usize,
1284     relocation_kind: u32,
1285     offset: usize,
1286     addend: isize,
1287 ) NativeElfError!bool {
1288     if (tlsModuleRelocationKind()) |module_kind| {
1289         if (relocation_kind == module_kind) {
1290             if (symbol_index == 0) {
1291                 if (!tls.active()) return error.NativeElfUnsupportedRelocation;
1292                 @as(*usize, @ptrFromInt(base + offset)).* = try addSigned(tls.module_id, addend);
1293                 return true;
1294             }
1295             const symbol = try nativeElfRelocationTlsSymbol(base, syms, strings, symbol_count, versyms, verneed, verneed_count, tls, dependencies, symbol_index);
1296             @as(*usize, @ptrFromInt(base + offset)).* = try addSigned(symbol.module_id, addend);
1297             return true;
1298         }
1299     }
1300     if (tlsOffsetRelocationKind()) |offset_kind| {
1301         if (relocation_kind == offset_kind) {
1302             const symbol = try nativeElfRelocationTlsSymbol(base, syms, strings, symbol_count, versyms, verneed, verneed_count, tls, dependencies, symbol_index);
1303             const tls_offset = try addSigned(symbol.offset, addend);
1304             if (tls_offset >= symbol.payload_len) return error.NativeElfUnsupportedRelocation;
1305             @as(*usize, @ptrFromInt(base + offset)).* = tls_offset;
1306             return true;
1307         }
1308     }
1309     return false;
1310 }
1311 
1312 fn nativeElfRelocationTlsSymbol(
1313     base: usize,
1314     syms: [*]elf.Sym,
1315     strings: [*:0]u8,
1316     symbol_count: usize,
1317     versyms: ?[*]elf.Versym,
1318     verneed: usize,
1319     verneed_count: usize,
1320     tls: *const NativeElfTlsAllocation,
1321     dependencies: []const NativeElfHandle,
1322     symbol_index: usize,
1323 ) NativeElfError!NativeElfTlsSymbol {
1324     if (symbol_index >= symbol_count) return error.NativeElfUnsupportedRelocation;
1325     const symbol = syms[symbol_index];
1326     if (symbol.st_shndx != 0) {
1327         if (!tls.active()) return error.NativeElfUnsupportedRelocation;
1328         if (symbol.st_info & 0xf != elf.STT_TLS) return error.NativeElfUnsupportedRelocation;
1329         const symbol_offset = std.math.cast(usize, symbol.st_value) orelse return error.NativeElfUnsupportedRelocation;
1330         if (symbol_offset >= tls.payload_len) return error.NativeElfUnsupportedRelocation;
1331         return .{
1332             .module_id = tls.module_id,
1333             .offset = symbol_offset,
1334             .payload_len = tls.payload_len,
1335         };
1336     }
1337 
1338     const name = std.mem.sliceTo(strings + symbol.st_name, 0);
1339     if (symbol.st_info & 0xf != elf.STT_TLS) return error.NativeElfUnsupportedRelocation;
1340     const version = try nativeElfNeededVersionName(base, strings, versyms, verneed, verneed_count, symbol_index);
1341     return resolveNativeElfDependencyTlsSymbol(dependencies, name, version) orelse error.NativeElfUnsupportedRelocation;
1342 }
1343 
1344 fn nativeElfBuiltinSymbolAddress(name: []const u8) ?usize {
1345     if (canUseNativeElfTls() and std.mem.eql(u8, name, "__tls_get_addr")) return @intFromPtr(&nativeElfTlsGetAddr);
1346     return null;
1347 }
1348 
1349 fn resolveNativeElfDependencySymbol(dependencies: []const NativeElfHandle, name: []const u8, version: ?[]const u8) ?usize {
1350     for (dependencies) |*dependency| {
1351         if (dependency.lookupAddressVersioned(name, version)) |address| return address;
1352     }
1353     for (dependencies) |*dependency| {
1354         if (resolveNativeElfDependencySymbol(dependency.shared.library.dependencies, name, version)) |address| return address;
1355     }
1356     return null;
1357 }
1358 
1359 fn resolveNativeElfDependencyTlsSymbol(dependencies: []const NativeElfHandle, name: []const u8, version: ?[]const u8) ?NativeElfTlsSymbol {
1360     for (dependencies) |*dependency| {
1361         if (dependency.lookupTlsSymbolVersioned(name, version)) |symbol| return symbol;
1362     }
1363     for (dependencies) |*dependency| {
1364         if (resolveNativeElfDependencyTlsSymbol(dependency.shared.library.dependencies, name, version)) |symbol| return symbol;
1365     }
1366     return null;
1367 }
1368 
1369 const ElfVerneed = if (@sizeOf(usize) == 8) elf.Elf64_Verneed else elf.Elf32_Verneed;
1370 
1371 fn nativeElfNeededVersionName(
1372     base: usize,
1373     strings: [*:0]u8,
1374     versyms: ?[*]elf.Versym,
1375     verneed: usize,
1376     verneed_count: usize,
1377     symbol_index: usize,
1378 ) NativeElfError!?[]const u8 {
1379     const table = versyms orelse return null;
1380     const requested = table[symbol_index];
1381     if (requested.VERSION <= @backingInt(elf.VER_NDX.GLOBAL)) return null;
1382     if (verneed == 0) return error.NativeElfUnsupportedRelocation;
1383 
1384     var need_addr = base + verneed;
1385     var need_index: usize = 0;
1386     while (need_index < verneed_count) : (need_index += 1) {
1387         const need: *const ElfVerneed = @ptrFromInt(need_addr);
1388         var aux_addr = need_addr + need.vn_aux;
1389         var aux_index: usize = 0;
1390         while (aux_index < need.vn_cnt) : (aux_index += 1) {
1391             const aux: *const elf.Vernaux = @ptrFromInt(aux_addr);
1392             if (aux.other == requested.VERSION) return std.mem.sliceTo(strings + aux.name, 0);
1393             if (aux.next == 0) break;
1394             aux_addr += aux.next;
1395         }
1396         if (need.vn_next == 0) break;
1397         need_addr += need.vn_next;
1398     }
1399     return error.NativeElfUnsupportedRelocation;
1400 }
1401 
1402 fn runNativeElfInitializers(base: usize, init: usize, init_array: usize, init_array_size: usize) NativeElfError!void {
1403     if (init != 0) callNativeElfInit(base + init);
1404     if (init_array_size % @sizeOf(usize) != 0) return error.InvalidElfImage;
1405     const count = init_array_size / @sizeOf(usize);
1406     if (count == 0) return;
1407     const functions: [*]const usize = @ptrFromInt(base + init_array);
1408     var index: usize = 0;
1409     while (index < count) : (index += 1) {
1410         if (functions[index] != 0) callNativeElfInit(functions[index]);
1411     }
1412 }
1413 
1414 fn runNativeElfFinalizers(base: usize, fini: usize, fini_array: usize, fini_array_count: usize) void {
1415     if (fini_array_count != 0) {
1416         const functions: [*]const usize = @ptrFromInt(base + fini_array);
1417         var index = fini_array_count;
1418         while (index > 0) {
1419             index -= 1;
1420             if (functions[index] != 0) callNativeElfInit(functions[index]);
1421         }
1422     }
1423     if (fini != 0) callNativeElfInit(base + fini);
1424 }
1425 
1426 fn protectNativeElfRelro(base: usize, relro_vaddr: usize, relro_memsz: usize, page_size: usize) NativeElfError!void {
1427     if (relro_memsz == 0) return;
1428     const relro_start = checkedAdd(base, relro_vaddr) orelse return error.InvalidElfImage;
1429     const relro_end = checkedAdd(relro_start, relro_memsz) orelse return error.InvalidElfImage;
1430     const page_start = std.mem.alignBackward(usize, relro_start, page_size);
1431     const page_end = std.mem.alignForward(usize, relro_end, page_size);
1432     if (page_end <= page_start) return;
1433     const ptr: [*]align(std.heap.page_size_min) u8 = @ptrFromInt(page_start);
1434     try protectNativeElfMemory(ptr[0 .. page_end - page_start], .{ .read = true });
1435 }
1436 
1437 fn protectNativeElfMemory(
1438     memory: []align(std.heap.page_size_min) u8,
1439     protection: sys_memory.Protection,
1440 ) NativeElfError!void {
1441     sys_memory.protect(memory, protection) catch |err| switch (err) {
1442         error.InvalidMapping => return error.InvalidElfImage,
1443         error.OutOfMemory => return error.OutOfMemory,
1444         error.AccessDenied, error.PermissionDenied, error.UnsupportedPlatform, error.ProtectFailed => return error.NativeElfUnsupported,
1445     };
1446 }
1447 
1448 fn callNativeElfInit(address: usize) void {
1449     const InitFn = *const fn () callconv(.c) void;
1450     @as(InitFn, @ptrFromInt(address))();
1451 }
1452 
1453 fn elfToProtection(elf_prot: u64) sys_memory.Protection {
1454     return .{
1455         .read = (elf_prot & elf.PF_R) != 0,
1456         .write = (elf_prot & elf.PF_W) != 0,
1457         .execute = (elf_prot & elf.PF_X) != 0,
1458     };
1459 }
1460 
1461 fn checkedAdd(left: usize, right: usize) ?usize {
1462     const result, const overflow = @addWithOverflow(left, right);
1463     if (overflow != 0) return null;
1464     return result;
1465 }
1466 
1467 fn addSigned(base: usize, addend: isize) NativeElfError!usize {
1468     if (addend >= 0) return checkedAdd(base, @intCast(addend)) orelse error.InvalidElfImage;
1469     if (addend == std.math.minInt(isize)) return error.InvalidElfImage;
1470     const magnitude: usize = @intCast(-addend);
1471     if (base < magnitude) return error.InvalidElfImage;
1472     return base - magnitude;
1473 }
1474 
1475 pub const LibraryCandidate = struct {
1476     os: ?std.Target.Os.Tag = null,
1477     name: []const u8,
1478 
1479     pub fn matchesHost(self: LibraryCandidate) bool {
1480         return self.os == null or self.os.? == builtin.os.tag;
1481     }
1482 };
1483 
1484 const cuda_driver_candidates = [_]LibraryCandidate{
1485     .{ .os = .linux, .name = "libcuda.so.1" },
1486     .{ .os = .linux, .name = "libcuda.so" },
1487 };
1488 
1489 const vulkan_loader_candidates = [_]LibraryCandidate{
1490     .{ .os = .linux, .name = "libvulkan.so.1" },
1491     .{ .os = .linux, .name = "libvulkan.so" },
1492     .{ .os = .macos, .name = "libvulkan.1.dylib" },
1493     .{ .os = .windows, .name = "vulkan-1.dll" },
1494 };
1495 
1496 const x11_library_candidates = [_]LibraryCandidate{
1497     .{ .os = .linux, .name = "libX11.so.6" },
1498     .{ .os = .linux, .name = "libX11.so" },
1499 };
1500 
1501 pub fn cudaDriverCandidates() []const LibraryCandidate {
1502     return &cuda_driver_candidates;
1503 }
1504 
1505 pub fn vulkanLoaderCandidates() []const LibraryCandidate {
1506     return &vulkan_loader_candidates;
1507 }
1508 
1509 pub fn x11LibraryCandidates() []const LibraryCandidate {
1510     return &x11_library_candidates;
1511 }
1512 
1513 pub fn hasHostCandidate(candidates: []const LibraryCandidate) bool {
1514     for (candidates) |candidate| {
1515         if (candidate.matchesHost()) return true;
1516     }
1517     return false;
1518 }
1519 
1520 pub fn openFirst(candidates: []const LibraryCandidate) OpenFirstError!Library {
1521     var matched = false;
1522     var last_error: ?LibraryError = null;
1523 
1524     for (candidates) |candidate| {
1525         if (!candidate.matchesHost()) continue;
1526         matched = true;
1527         return Library.open(candidate.name) catch |err| {
1528             last_error = err;
1529             continue;
1530         };
1531     }
1532 
1533     if (!matched) return error.NoCandidate;
1534     return last_error.?;
1535 }
1536 
1537 pub fn openFirstNativeElfOnLinux(candidates: []const LibraryCandidate) OpenFirstError!Library {
1538     var matched = false;
1539     var last_error: ?LibraryError = null;
1540 
1541     for (candidates) |candidate| {
1542         if (!candidate.matchesHost()) continue;
1543         matched = true;
1544         if (comptime builtin.os.tag == .linux) {
1545             return Library.openNativeElf(candidate.name) catch |err| {
1546                 last_error = err;
1547                 continue;
1548             };
1549         }
1550         return Library.open(candidate.name) catch |err| {
1551             last_error = err;
1552             continue;
1553         };
1554     }
1555 
1556     if (!matched) return error.NoCandidate;
1557     return last_error.?;
1558 }
1559 
1560 pub fn isLoadable(candidates: []const LibraryCandidate) bool {
1561     var library = openFirst(candidates) catch return false;
1562     library.close();
1563     return true;
1564 }
1565 
1566 pub fn openSystemLibrary(candidates: []const LibraryCandidate) OpenFirstError!Library {
1567     if (comptime usesStdElfDynLib()) return openFirstNativeElfOnLinux(candidates);
1568 
1569     var matched = false;
1570     var last_error: ?LibraryError = null;
1571     for (candidates) |candidate| {
1572         if (!candidate.matchesHost()) continue;
1573         matched = true;
1574         const raw = openHostSystemLibrary(candidate.name) catch |err| {
1575             last_error = err;
1576             continue;
1577         };
1578         return .{ .raw = .{ .std_dynlib = raw } };
1579     }
1580 
1581     if (!matched) return error.NoCandidate;
1582     return last_error.?;
1583 }
1584 
1585 fn openHostSystemLibrary(name: []const u8) LibraryError!std.DynLib {
1586     return std.DynLib.open(name) catch |err| {
1587         if (comptime builtin.os.tag != .linux) return err;
1588         if (std.mem.indexOfScalar(u8, name, '/') != null) return err;
1589 
1590         const allocator = sys_allocator.processAllocator();
1591         const path = try resolvePathlessLibrary(allocator, name);
1592         defer allocator.free(path);
1593         return std.DynLib.open(path);
1594     };
1595 }
1596 
1597 pub fn openCudaDriver() OpenFirstError!Library {
1598     return openSystemLibrary(cudaDriverCandidates());
1599 }
1600 
1601 pub fn openVulkanLoader() OpenFirstError!Library {
1602     return openSystemLibrary(vulkanLoaderCandidates());
1603 }
1604 
1605 pub fn openX11Library() OpenFirstError!Library {
1606     return openSystemLibrary(x11LibraryCandidates());
1607 }
1608 
1609 pub fn isCudaDriverLoadable() bool {
1610     var library = openCudaDriver() catch return false;
1611     library.close();
1612     return true;
1613 }
1614 
1615 pub fn isVulkanLoaderLoadable() bool {
1616     var library = openVulkanLoader() catch return false;
1617     library.close();
1618     return true;
1619 }
1620 
1621 test "library candidates are host-filtered" {
1622     const other_os: std.Target.Os.Tag = switch (builtin.os.tag) {
1623         .linux => .macos,
1624         else => .linux,
1625     };
1626 
1627     try std.testing.expect(hasHostCandidate(&.{
1628         .{ .os = other_os, .name = "sys_missing_library" },
1629         .{ .os = builtin.os.tag, .name = "sys_missing_library" },
1630     }));
1631     try std.testing.expect(!hasHostCandidate(&.{
1632         .{ .os = other_os, .name = "sys_missing_library" },
1633     }));
1634 }
1635 
1636 test "openFirst reports missing host candidates" {
1637     const other_os: std.Target.Os.Tag = switch (builtin.os.tag) {
1638         .linux => .macos,
1639         else => .linux,
1640     };
1641 
1642     try std.testing.expectError(error.NoCandidate, openFirst(&.{
1643         .{ .os = other_os, .name = "sys_missing_library" },
1644     }));
1645 }
1646 
1647 test "openSystemLibrary reports missing host candidates" {
1648     const other_os: std.Target.Os.Tag = switch (builtin.os.tag) {
1649         .linux => .macos,
1650         else => .linux,
1651     };
1652 
1653     try std.testing.expectError(error.NoCandidate, openSystemLibrary(&.{
1654         .{ .os = other_os, .name = "sys_missing_library" },
1655     }));
1656 }
1657 
1658 test "native-on-Linux openFirst reports missing host candidates" {
1659     const other_os: std.Target.Os.Tag = switch (builtin.os.tag) {
1660         .linux => .macos,
1661         else => .linux,
1662     };
1663 
1664     try std.testing.expectError(error.NoCandidate, openFirstNativeElfOnLinux(&.{
1665         .{ .os = other_os, .name = "sys_missing_library" },
1666     }));
1667 }
1668 
1669 test "native default library directories include linux multiarch paths" {
1670     if (builtin.os.tag != .linux) return;
1671 
1672     switch (builtin.cpu.arch) {
1673         .x86_64 => {
1674             try std.testing.expect(nativeDefaultLibraryDirectoriesContain("/lib/x86_64-linux-gnu"));
1675             try std.testing.expect(nativeDefaultLibraryDirectoriesContain("/usr/lib/x86_64-linux-gnu"));
1676         },
1677         .aarch64, .aarch64_be => {
1678             try std.testing.expect(nativeDefaultLibraryDirectoriesContain("/lib/aarch64-linux-gnu"));
1679             try std.testing.expect(nativeDefaultLibraryDirectoriesContain("/usr/lib/aarch64-linux-gnu"));
1680         },
1681         .riscv64, .riscv64be => {
1682             try std.testing.expect(nativeDefaultLibraryDirectoriesContain("/lib/riscv64-linux-gnu"));
1683             try std.testing.expect(nativeDefaultLibraryDirectoriesContain("/usr/lib/riscv64-linux-gnu"));
1684         },
1685         else => {},
1686     }
1687 }
1688 
1689 fn usesStdElfDynLib() bool {
1690     return builtin.os.tag == .linux and (!builtin.link_libc or builtin.abi == .musl and builtin.link_mode == .static);
1691 }
1692 
1693 fn repairElfImage(path: []const u8, raw: *std.DynLib) ElfRepairError!void {
1694     if (comptime !usesStdElfDynLib()) {
1695         return;
1696     }
1697 
1698     const io = std.Options.debug_io;
1699     var file = if (path.len > 0 and path[0] == '/')
1700         try std.Io.Dir.openFileAbsolute(io, path, .{})
1701     else
1702         try std.Io.Dir.cwd().openFile(io, path, .{});
1703     defer file.close(io);
1704 
1705     const stat = try file.stat(io);
1706     const size = std.math.cast(usize, stat.size) orelse return error.FileTooBig;
1707     if (size < @sizeOf(elf.Ehdr)) return error.InvalidElfImage;
1708     const file_bytes = try sys_memory.mapPrivateFile(file.handle, size, .{ .read = true }, 0);
1709     defer sys_memory.unmap(file_bytes);
1710 
1711     const eh: *const elf.Ehdr = @ptrCast(file_bytes.ptr);
1712     if (!std.mem.eql(u8, eh.e_ident[0..4], elf.MAGIC)) return error.InvalidElfImage;
1713     if (eh.e_type != elf.ET.DYN) return error.InvalidElfImage;
1714     if (eh.e_phoff + @as(usize, eh.e_phentsize) * eh.e_phnum > size) return error.InvalidElfImage;
1715 
1716     const base = @intFromPtr(raw.inner.memory.ptr);
1717     const file_addr = @intFromPtr(file_bytes.ptr);
1718     var dynamic_vaddr: ?usize = null;
1719     var i: usize = 0;
1720     var ph_addr = file_addr + eh.e_phoff;
1721     while (i < eh.e_phnum) : ({
1722         i += 1;
1723         ph_addr += eh.e_phentsize;
1724     }) {
1725         const ph: *const elf.Phdr = @ptrFromInt(ph_addr);
1726         switch (ph.p_type) {
1727             elf.PT_LOAD => if ((ph.p_flags & elf.PF_W) != 0) {
1728                 if (ph.p_memsz < ph.p_filesz) return error.InvalidElfImage;
1729                 if (ph.p_offset + ph.p_filesz > size) return error.InvalidElfImage;
1730                 const segment: [*]u8 = @ptrFromInt(base + ph.p_vaddr);
1731                 const source = file_bytes[ph.p_offset..][0..ph.p_filesz];
1732                 @memcpy(segment[0..ph.p_filesz], source);
1733                 if (ph.p_memsz > ph.p_filesz) {
1734                     @memset(segment[ph.p_filesz..ph.p_memsz], 0);
1735                 }
1736             },
1737             elf.PT_DYNAMIC => dynamic_vaddr = ph.p_vaddr,
1738             else => {},
1739         }
1740     }
1741 
1742     try applyElfRelativeRelocations(base, dynamic_vaddr orelse return error.InvalidElfImage);
1743 }
1744 
1745 fn applyElfRelativeRelocations(base: usize, dynamic_vaddr: usize) ElfRepairError!void {
1746     var rela: usize = 0;
1747     var rela_size: usize = 0;
1748     var rela_entry_size: usize = @sizeOf(elf.Rela);
1749     var rel: usize = 0;
1750     var rel_size: usize = 0;
1751     var rel_entry_size: usize = @sizeOf(elf.Rel);
1752     var relr: usize = 0;
1753     var relr_size: usize = 0;
1754     var relr_entry_size: usize = @sizeOf(elf.Relr);
1755 
1756     const dynv: [*]usize = @ptrFromInt(base + dynamic_vaddr);
1757     var i: usize = 0;
1758     while (dynv[i] != 0) : (i += 2) {
1759         switch (dynv[i]) {
1760             elf.DT_RELA => rela = dynv[i + 1],
1761             elf.DT_RELASZ => rela_size = dynv[i + 1],
1762             elf.DT_RELAENT => rela_entry_size = dynv[i + 1],
1763             elf.DT_REL => rel = dynv[i + 1],
1764             elf.DT_RELSZ => rel_size = dynv[i + 1],
1765             elf.DT_RELENT => rel_entry_size = dynv[i + 1],
1766             elf.DT_RELR => relr = dynv[i + 1],
1767             elf.DT_RELRSZ => relr_size = dynv[i + 1],
1768             elf.DT_RELRENT => relr_entry_size = dynv[i + 1],
1769             else => {},
1770         }
1771     }
1772 
1773     if (rel != 0) {
1774         if (rel_entry_size != @sizeOf(elf.Rel)) return error.UnsupportedElfRelocation;
1775         const rels: []const elf.Rel = @ptrCast(@alignCast(
1776             @as([*]align(@alignOf(elf.Rel)) const u8, @ptrFromInt(base + rel))[0..rel_size],
1777         ));
1778         for (rels) |r| {
1779             if (r.r_type() != relativeRelocationKind()) continue;
1780             @as(*usize, @ptrFromInt(base + r.r_offset)).* += base;
1781         }
1782     }
1783 
1784     if (rela != 0) {
1785         if (rela_entry_size != @sizeOf(elf.Rela)) return error.UnsupportedElfRelocation;
1786         const relas: []const elf.Rela = @ptrCast(@alignCast(
1787             @as([*]align(@alignOf(elf.Rela)) const u8, @ptrFromInt(base + rela))[0..rela_size],
1788         ));
1789         for (relas) |r| {
1790             if (r.r_type() != relativeRelocationKind()) continue;
1791             @as(*usize, @ptrFromInt(base + r.r_offset)).* = base + @as(usize, @bitCast(r.r_addend));
1792         }
1793     }
1794 
1795     if (relr != 0) {
1796         applyRelativeRelrRelocations(base, relr, relr_size, relr_entry_size) catch return error.UnsupportedElfRelocation;
1797     }
1798 }
1799 
1800 fn applyRelativeRelrRelocations(base: usize, relr: usize, relr_size: usize, relr_entry_size: usize) error{UnsupportedRelrRelocation}!void {
1801     if (relr_entry_size != @sizeOf(elf.Relr)) return error.UnsupportedRelrRelocation;
1802     const relrs: []const elf.Relr = @ptrCast(
1803         @as([*]align(@alignOf(elf.Relr)) const u8, @ptrFromInt(base + relr))[0..relr_size],
1804     );
1805     var current: [*]usize = undefined;
1806     for (relrs) |r| {
1807         if ((r & 1) == 0) {
1808             current = @ptrFromInt(base + r);
1809             current[0] += base;
1810             current += 1;
1811         } else {
1812             var bit: usize = 1;
1813             while (bit < @bitSizeOf(elf.Relr)) : (bit += 1) {
1814                 const shift: std.math.Log2Int(elf.Relr) = @intCast(bit);
1815                 if (((r >> shift) & 1) != 0) current[bit - 1] += base;
1816             }
1817             current += @bitSizeOf(elf.Relr) - 1;
1818         }
1819     }
1820 }
1821 
1822 fn relativeRelocationKind() u32 {
1823     return switch (builtin.cpu.arch) {
1824         .x86 => 8,
1825         .x86_64 => 8,
1826         .arc, .arceb => 56,
1827         .arm, .armeb, .thumb, .thumbeb => 23,
1828         .aarch64, .aarch64_be => 1027,
1829         .alpha => 27,
1830         .csky => 9,
1831         .hexagon => 35,
1832         .kvx => 39,
1833         .loongarch32, .loongarch64 => 3,
1834         .m68k => 22,
1835         .microblaze, .microblazeel => 16,
1836         .mips, .mipsel, .mips64, .mips64el => 128,
1837         .or1k => 21,
1838         .powerpc, .powerpcle, .powerpc64, .powerpc64le => 22,
1839         .riscv32, .riscv32be, .riscv64, .riscv64be => 3,
1840         .s390x => 12,
1841         .sh, .sheb => 165,
1842         .sparc, .sparc64 => 22,
1843         else => @compileError("missing relative relocation kind"),
1844     };
1845 }
1846 
1847 fn absoluteRelocationKind() ?u32 {
1848     return switch (builtin.cpu.arch) {
1849         .x86, .x86_64 => 1,
1850         .aarch64, .aarch64_be => 257,
1851         else => null,
1852     };
1853 }
1854 
1855 fn globDatRelocationKind() ?u32 {
1856     return switch (builtin.cpu.arch) {
1857         .x86, .x86_64 => 6,
1858         .aarch64, .aarch64_be => 1025,
1859         else => null,
1860     };
1861 }
1862 
1863 fn jumpSlotRelocationKind() ?u32 {
1864     return switch (builtin.cpu.arch) {
1865         .x86, .x86_64 => 7,
1866         .aarch64, .aarch64_be => 1026,
1867         else => null,
1868     };
1869 }
1870 
1871 fn irelativeRelocationKind() ?u32 {
1872     return switch (builtin.cpu.arch) {
1873         .x86_64 => @backingInt(elf.R_X86_64.IRELATIVE),
1874         else => null,
1875     };
1876 }
1877 
1878 fn tlsModuleRelocationKind() ?u32 {
1879     return switch (builtin.cpu.arch) {
1880         .x86_64 => @backingInt(elf.R_X86_64.DTPMOD64),
1881         else => null,
1882     };
1883 }
1884 
1885 fn tlsOffsetRelocationKind() ?u32 {
1886     return switch (builtin.cpu.arch) {
1887         .x86_64 => @backingInt(elf.R_X86_64.DTPOFF64),
1888         else => null,
1889     };
1890 }