diff --git a/CMakeLists.txt b/CMakeLists.txt index edd24d282e..f279990e27 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -579,7 +579,6 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/src/link/MachO/DebugSymbols.zig" "${CMAKE_SOURCE_DIR}/src/link/MachO/Dylib.zig" "${CMAKE_SOURCE_DIR}/src/link/MachO/Object.zig" - "${CMAKE_SOURCE_DIR}/src/link/MachO/Stub.zig" "${CMAKE_SOURCE_DIR}/src/link/MachO/Symbol.zig" "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig" "${CMAKE_SOURCE_DIR}/src/link/MachO/Zld.zig" diff --git a/src/link/MachO/Dylib.zig b/src/link/MachO/Dylib.zig index bcf2433dd7..44b012905e 100644 --- a/src/link/MachO/Dylib.zig +++ b/src/link/MachO/Dylib.zig @@ -9,10 +9,12 @@ const mem = std.mem; const Allocator = mem.Allocator; const Symbol = @import("Symbol.zig"); +const LibStub = @import("../tapi.zig").LibStub; usingnamespace @import("commands.zig"); allocator: *Allocator, + arch: ?std.Target.Cpu.Arch = null, header: ?macho.mach_header_64 = null, file: ?fs.File = null, @@ -103,7 +105,7 @@ pub fn parse(self: *Dylib) !void { try self.parseSymbols(); } -pub fn readLoadCommands(self: *Dylib, reader: anytype) !void { +fn readLoadCommands(self: *Dylib, reader: anytype) !void { try self.load_commands.ensureCapacity(self.allocator, self.header.?.ncmds); var i: u16 = 0; @@ -127,7 +129,7 @@ pub fn readLoadCommands(self: *Dylib, reader: anytype) !void { } } -pub fn parseId(self: *Dylib) !void { +fn parseId(self: *Dylib) !void { const index = self.id_cmd_index orelse { log.debug("no LC_ID_DYLIB load command found; using hard-coded defaults...", .{}); self.id = .{ @@ -153,7 +155,7 @@ pub fn parseId(self: *Dylib) !void { }; } -pub fn parseSymbols(self: *Dylib) !void { +fn parseSymbols(self: *Dylib) !void { const index = self.symtab_cmd_index orelse return; const symtab_cmd = self.load_commands.items[index].Symtab; @@ -176,6 +178,86 @@ pub fn parseSymbols(self: *Dylib) !void { } } +fn addObjCClassSymbols(self: *Dylib, sym_name: []const u8) !void { + const expanded = &[_][]const u8{ + try std.fmt.allocPrint(self.allocator, "_OBJC_CLASS_$_{s}", .{sym_name}), + try std.fmt.allocPrint(self.allocator, "_OBJC_METACLASS_$_{s}", .{sym_name}), + }; + + for (expanded) |sym| { + if (self.symbols.contains(sym)) continue; + try self.symbols.putNoClobber(self.allocator, sym, .{}); + } +} + +pub fn parseFromStub(self: *Dylib, lib_stub: LibStub) !void { + if (lib_stub.inner.len == 0) return error.EmptyStubFile; + + log.debug("parsing shared library from stub '{s}'", .{self.name.?}); + + const umbrella_lib = lib_stub.inner[0]; + self.id = .{ + .name = try self.allocator.dupe(u8, umbrella_lib.install_name), + // TODO parse from the stub + .timestamp = 2, + .current_version = 0, + .compatibility_version = 0, + }; + + const target_string: []const u8 = switch (self.arch.?) { + .aarch64 => "arm64-macos", + .x86_64 => "x86_64-macos", + else => unreachable, + }; + + for (lib_stub.inner) |stub| { + if (!hasTarget(stub.targets, target_string)) continue; + + if (stub.exports) |exports| { + for (exports) |exp| { + if (!hasTarget(exp.targets, target_string)) continue; + + if (exp.symbols) |symbols| { + for (symbols) |sym_name| { + if (self.symbols.contains(sym_name)) continue; + try self.symbols.putNoClobber(self.allocator, try self.allocator.dupe(u8, sym_name), {}); + } + } + + if (exp.objc_classes) |classes| { + for (classes) |sym_name| { + try self.addObjCClassSymbols(sym_name); + } + } + } + } + + if (stub.reexports) |reexports| { + for (reexports) |reexp| { + if (!hasTarget(reexp.targets, target_string)) continue; + + for (reexp.symbols) |sym_name| { + if (self.symbols.contains(sym_name)) continue; + try self.symbols.putNoClobber(self.allocator, try self.allocator.dupe(u8, sym_name), {}); + } + } + } + + if (stub.objc_classes) |classes| { + for (classes) |sym_name| { + try self.addObjCClassSymbols(sym_name); + } + } + } +} + +fn hasTarget(targets: []const []const u8, target: []const u8) bool { + for (targets) |t| { + if (mem.eql(u8, t, target)) return true; + } + return false; +} + pub fn isDylib(file: fs.File) !bool { const header = file.reader().readStruct(macho.mach_header_64) catch |err| switch (err) { error.EndOfStream => return false, @@ -197,7 +279,7 @@ pub fn createProxy(self: *Dylib, sym_name: []const u8) !?*Symbol { .@"type" = .proxy, .name = name, }, - .file = .{ .dylib = self }, + .file = self, }; return &proxy.base; diff --git a/src/link/MachO/Stub.zig b/src/link/MachO/Stub.zig deleted file mode 100644 index 40ed63b673..0000000000 --- a/src/link/MachO/Stub.zig +++ /dev/null @@ -1,159 +0,0 @@ -const Stub = @This(); - -const std = @import("std"); -const assert = std.debug.assert; -const fs = std.fs; -const log = std.log.scoped(.stub); -const macho = std.macho; -const mem = std.mem; - -const Allocator = mem.Allocator; -const Symbol = @import("Symbol.zig"); -pub const LibStub = @import("../tapi.zig").LibStub; - -allocator: *Allocator, -arch: ?std.Target.Cpu.Arch = null, -lib_stub: ?LibStub = null, -name: ?[]const u8 = null, - -ordinal: ?u16 = null, - -id: ?Id = null, - -/// Parsed symbol table represented as hash map of symbols' -/// names. We can and should defer creating *Symbols until -/// a symbol is referenced by an object file. -symbols: std.StringArrayHashMapUnmanaged(void) = .{}, - -pub const Id = struct { - name: []const u8, - timestamp: u32, - current_version: u32, - compatibility_version: u32, - - pub fn deinit(id: *Id, allocator: *Allocator) void { - allocator.free(id.name); - } -}; - -pub fn init(allocator: *Allocator) Stub { - return .{ .allocator = allocator }; -} - -pub fn deinit(self: *Stub) void { - for (self.symbols.keys()) |key| { - self.allocator.free(key); - } - self.symbols.deinit(self.allocator); - - if (self.lib_stub) |*lib_stub| { - lib_stub.deinit(); - } - - if (self.name) |name| { - self.allocator.free(name); - } - - if (self.id) |*id| { - id.deinit(self.allocator); - } -} - -fn addObjCClassSymbols(self: *Stub, sym_name: []const u8) !void { - const expanded = &[_][]const u8{ - try std.fmt.allocPrint(self.allocator, "_OBJC_CLASS_$_{s}", .{sym_name}), - try std.fmt.allocPrint(self.allocator, "_OBJC_METACLASS_$_{s}", .{sym_name}), - }; - - for (expanded) |sym| { - if (self.symbols.contains(sym)) continue; - try self.symbols.putNoClobber(self.allocator, sym, .{}); - } -} - -pub fn parse(self: *Stub) !void { - const lib_stub = self.lib_stub orelse return error.EmptyStubFile; - if (lib_stub.inner.len == 0) return error.EmptyStubFile; - - log.debug("parsing shared library from stub '{s}'", .{self.name.?}); - - const umbrella_lib = lib_stub.inner[0]; - self.id = .{ - .name = try self.allocator.dupe(u8, umbrella_lib.install_name), - // TODO parse from the stub - .timestamp = 2, - .current_version = 0, - .compatibility_version = 0, - }; - - const target_string: []const u8 = switch (self.arch.?) { - .aarch64 => "arm64-macos", - .x86_64 => "x86_64-macos", - else => unreachable, - }; - - for (lib_stub.inner) |stub| { - if (!hasTarget(stub.targets, target_string)) continue; - - if (stub.exports) |exports| { - for (exports) |exp| { - if (!hasTarget(exp.targets, target_string)) continue; - - if (exp.symbols) |symbols| { - for (symbols) |sym_name| { - if (self.symbols.contains(sym_name)) continue; - try self.symbols.putNoClobber(self.allocator, try self.allocator.dupe(u8, sym_name), {}); - } - } - - if (exp.objc_classes) |classes| { - for (classes) |sym_name| { - try self.addObjCClassSymbols(sym_name); - } - } - } - } - - if (stub.reexports) |reexports| { - for (reexports) |reexp| { - if (!hasTarget(reexp.targets, target_string)) continue; - - for (reexp.symbols) |sym_name| { - if (self.symbols.contains(sym_name)) continue; - try self.symbols.putNoClobber(self.allocator, try self.allocator.dupe(u8, sym_name), {}); - } - } - } - - if (stub.objc_classes) |classes| { - for (classes) |sym_name| { - try self.addObjCClassSymbols(sym_name); - } - } - } -} - -fn hasTarget(targets: []const []const u8, target: []const u8) bool { - for (targets) |t| { - if (mem.eql(u8, t, target)) return true; - } - return false; -} - -pub fn createProxy(self: *Stub, sym_name: []const u8) !?*Symbol { - if (!self.symbols.contains(sym_name)) return null; - - const name = try self.allocator.dupe(u8, sym_name); - const proxy = try self.allocator.create(Symbol.Proxy); - errdefer self.allocator.destroy(proxy); - - proxy.* = .{ - .base = .{ - .@"type" = .proxy, - .name = name, - }, - .file = .{ .stub = self }, - }; - - return &proxy.base; -} diff --git a/src/link/MachO/Symbol.zig b/src/link/MachO/Symbol.zig index b2e7867ca3..0c6777009d 100644 --- a/src/link/MachO/Symbol.zig +++ b/src/link/MachO/Symbol.zig @@ -7,7 +7,6 @@ const mem = std.mem; const Allocator = mem.Allocator; const Dylib = @import("Dylib.zig"); const Object = @import("Object.zig"); -const Stub = @import("Stub.zig"); pub const Type = enum { regular, @@ -94,12 +93,9 @@ pub const Proxy = struct { address: u64, }) = .{}, - /// Dylib or stub where to locate this symbol. + /// Dylib where to locate this symbol. /// null means self-reference. - file: ?union(enum) { - dylib: *Dylib, - stub: *Stub, - } = null, + file: ?*Dylib = null, pub const base_type: Symbol.Type = .proxy; @@ -108,11 +104,8 @@ pub const Proxy = struct { } pub fn dylibOrdinal(proxy: *Proxy) u16 { - const file = proxy.file orelse return 0; - return switch (file) { - .dylib => |dylib| dylib.ordinal.?, - .stub => |stub| stub.ordinal.?, - }; + const dylib = proxy.file orelse return 0; + return dylib.ordinal.?; } }; diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig index f2c0fbf718..b46feced49 100644 --- a/src/link/MachO/Zld.zig +++ b/src/link/MachO/Zld.zig @@ -16,8 +16,8 @@ const Allocator = mem.Allocator; const Archive = @import("Archive.zig"); const CodeSignature = @import("CodeSignature.zig"); const Dylib = @import("Dylib.zig"); +const LibStub = @import("../tapi.zig").LibStub; const Object = @import("Object.zig"); -const Stub = @import("Stub.zig"); const Symbol = @import("Symbol.zig"); const Trie = @import("Trie.zig"); @@ -38,9 +38,8 @@ stack_size: u64 = 0, objects: std.ArrayListUnmanaged(*Object) = .{}, archives: std.ArrayListUnmanaged(*Archive) = .{}, dylibs: std.ArrayListUnmanaged(*Dylib) = .{}, -lib_stubs: std.ArrayListUnmanaged(*Stub) = .{}, -libsystem_stub_index: ?u16 = null, +libsystem_dylib_index: ?u16 = null, next_dylib_ordinal: u16 = 1, load_commands: std.ArrayListUnmanaged(LoadCommand) = .{}, @@ -134,10 +133,6 @@ const TlvOffset = struct { /// Default path to dyld const DEFAULT_DYLD_PATH: [*:0]const u8 = "/usr/lib/dyld"; -const LIB_SYSTEM_NAME: [*:0]const u8 = "System"; -/// TODO this should be inferred from included libSystem.tbd or similar. -const LIB_SYSTEM_PATH: [*:0]const u8 = "/usr/lib/libSystem.B.dylib"; - pub fn init(allocator: *Allocator) Zld { return .{ .allocator = allocator }; } @@ -171,12 +166,6 @@ pub fn deinit(self: *Zld) void { } self.dylibs.deinit(self.allocator); - for (self.lib_stubs.items) |stub| { - stub.deinit(); - self.allocator.destroy(stub); - } - self.lib_stubs.deinit(self.allocator); - for (self.imports.values()) |proxy| { proxy.deinit(self.allocator); self.allocator.destroy(proxy); @@ -269,20 +258,30 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8, args: L fn parseInputFiles(self: *Zld, files: []const []const u8) !void { const Input = struct { - kind: enum { - object, - archive, - dylib, - stub, - }, - origin: union { - file: fs.File, - stub: Stub.LibStub, + kind: union(enum) { + object: fs.File, + archive: fs.File, + dylib: fs.File, + stub: LibStub, }, name: []const u8, + + fn deinit(input: *@This()) void { + switch (input.kind) { + .stub => |*stub| { + stub.deinit(); + }, + else => {}, + } + } }; var classified = std.ArrayList(Input).init(self.allocator); - defer classified.deinit(); + defer { + for (classified.items) |*input| { + input.deinit(); + } + classified.deinit(); + } // First, classify input files: object, archive, dylib or stub (tbd). for (files) |file_name| { @@ -296,8 +295,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void { try_object: { if (!(try Object.isObject(file))) break :try_object; try classified.append(.{ - .kind = .object, - .origin = .{ .file = file }, + .kind = .{ .object = file }, .name = full_path, }); continue; @@ -306,8 +304,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void { try_archive: { if (!(try Archive.isArchive(file))) break :try_archive; try classified.append(.{ - .kind = .archive, - .origin = .{ .file = file }, + .kind = .{ .archive = file }, .name = full_path, }); continue; @@ -316,20 +313,18 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void { try_dylib: { if (!(try Dylib.isDylib(file))) break :try_dylib; try classified.append(.{ - .kind = .dylib, - .origin = .{ .file = file }, + .kind = .{ .dylib = file }, .name = full_path, }); continue; } try_stub: { - var lib_stub = Stub.LibStub.loadFromFile(self.allocator, file) catch { + var lib_stub = LibStub.loadFromFile(self.allocator, file) catch { break :try_stub; }; try classified.append(.{ - .kind = .stub, - .origin = .{ .stub = lib_stub }, + .kind = .{ .stub = lib_stub }, .name = full_path, }); file.close(); @@ -343,54 +338,47 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void { // Based on our classification, proceed with parsing. for (classified.items) |input| { switch (input.kind) { - .object => { + .object => |file| { const object = try self.allocator.create(Object); errdefer self.allocator.destroy(object); object.* = Object.init(self.allocator); object.arch = self.arch.?; object.name = input.name; - object.file = input.origin.file; + object.file = file; try object.parse(); try self.objects.append(self.allocator, object); }, - .archive => { + .archive => |file| { const archive = try self.allocator.create(Archive); errdefer self.allocator.destroy(archive); archive.* = Archive.init(self.allocator); archive.arch = self.arch.?; archive.name = input.name; - archive.file = input.origin.file; + archive.file = file; try archive.parse(); try self.archives.append(self.allocator, archive); }, - .dylib => { + .dylib, .stub => { const dylib = try self.allocator.create(Dylib); errdefer self.allocator.destroy(dylib); dylib.* = Dylib.init(self.allocator); dylib.arch = self.arch.?; dylib.name = input.name; - dylib.file = input.origin.file; - try dylib.parse(); + if (input.kind == .dylib) { + dylib.file = input.kind.dylib; + try dylib.parse(); + } else { + try dylib.parseFromStub(input.kind.stub); + } + try self.dylibs.append(self.allocator, dylib); }, - .stub => { - const stub = try self.allocator.create(Stub); - errdefer self.allocator.destroy(stub); - - stub.* = Stub.init(self.allocator); - stub.arch = self.arch.?; - stub.name = input.name; - stub.lib_stub = input.origin.stub; - - try stub.parse(); - try self.lib_stubs.append(self.allocator, stub); - }, } } } @@ -399,51 +387,65 @@ fn parseLibs(self: *Zld, libs: []const []const u8) !void { for (libs) |lib| { const file = try fs.cwd().openFile(lib, .{}); - if (try Dylib.isDylib(file)) { - const dylib = try self.allocator.create(Dylib); - errdefer self.allocator.destroy(dylib); - - dylib.* = Dylib.init(self.allocator); - dylib.arch = self.arch.?; - dylib.name = try self.allocator.dupe(u8, lib); - dylib.file = file; - - try dylib.parse(); - try self.dylibs.append(self.allocator, dylib); - } else { - // Try tbd stub file next. - if (Stub.LibStub.loadFromFile(self.allocator, file)) |lib_stub| { - const stub = try self.allocator.create(Stub); - errdefer self.allocator.destroy(stub); - - stub.* = Stub.init(self.allocator); - stub.arch = self.arch.?; - stub.name = try self.allocator.dupe(u8, lib); - stub.lib_stub = lib_stub; - - try stub.parse(); - try self.lib_stubs.append(self.allocator, stub); - } else |_| { - // TODO this entire logic has to be cleaned up. - try file.seekTo(0); - - if (try Archive.isArchive(file)) { - const archive = try self.allocator.create(Archive); - errdefer self.allocator.destroy(archive); - - archive.* = Archive.init(self.allocator); - archive.arch = self.arch.?; - archive.name = try self.allocator.dupe(u8, lib); - archive.file = file; - - try archive.parse(); - try self.archives.append(self.allocator, archive); - } else { - file.close(); - log.warn("unknown filetype for a library: '{s}'", .{lib}); + var kind: ?union(enum) { + archive, + dylib, + stub: LibStub, + } = kind: { + if (try Archive.isArchive(file)) break :kind .archive; + if (try Dylib.isDylib(file)) break :kind .dylib; + var lib_stub = LibStub.loadFromFile(self.allocator, file) catch { + break :kind null; + }; + break :kind .{ .stub = lib_stub }; + }; + defer { + if (kind) |*kk| { + switch (kk.*) { + .stub => |*stub| { + stub.deinit(); + }, + else => {}, } } } + + const unwrapped = kind orelse { + file.close(); + log.warn("unknown filetype for a library: '{s}'", .{lib}); + continue; + }; + switch (unwrapped) { + .archive => { + const archive = try self.allocator.create(Archive); + errdefer self.allocator.destroy(archive); + + archive.* = Archive.init(self.allocator); + archive.arch = self.arch.?; + archive.name = try self.allocator.dupe(u8, lib); + archive.file = file; + + try archive.parse(); + try self.archives.append(self.allocator, archive); + }, + .dylib, .stub => { + const dylib = try self.allocator.create(Dylib); + errdefer self.allocator.destroy(dylib); + + dylib.* = Dylib.init(self.allocator); + dylib.arch = self.arch.?; + dylib.name = try self.allocator.dupe(u8, lib); + + if (unwrapped == .dylib) { + dylib.file = file; + try dylib.parse(); + } else { + try dylib.parseFromStub(unwrapped.stub); + } + + try self.dylibs.append(self.allocator, dylib); + }, + } } } @@ -451,24 +453,24 @@ fn parseLibSystem(self: *Zld, libc_stub_path: []const u8) !void { const file = try fs.cwd().openFile(libc_stub_path, .{}); defer file.close(); - var lib_stub = try Stub.LibStub.loadFromFile(self.allocator, file); + var lib_stub = try LibStub.loadFromFile(self.allocator, file); + defer lib_stub.deinit(); - const stub = try self.allocator.create(Stub); - errdefer self.allocator.destroy(stub); + const dylib = try self.allocator.create(Dylib); + errdefer self.allocator.destroy(dylib); - stub.* = Stub.init(self.allocator); - stub.arch = self.arch.?; - stub.name = try self.allocator.dupe(u8, libc_stub_path); - stub.lib_stub = lib_stub; + dylib.* = Dylib.init(self.allocator); + dylib.arch = self.arch.?; + dylib.name = try self.allocator.dupe(u8, libc_stub_path); - try stub.parse(); + try dylib.parseFromStub(lib_stub); - self.libsystem_stub_index = @intCast(u16, self.lib_stubs.items.len); - try self.lib_stubs.append(self.allocator, stub); + self.libsystem_dylib_index = @intCast(u16, self.dylibs.items.len); + try self.dylibs.append(self.allocator, dylib); // Add LC_LOAD_DYLIB load command. - stub.ordinal = self.next_dylib_ordinal; - const dylib_id = stub.id orelse unreachable; + dylib.ordinal = self.next_dylib_ordinal; + const dylib_id = dylib.id orelse unreachable; var dylib_cmd = try createLoadDylibCommand( self.allocator, dylib_id.name, @@ -1778,25 +1780,16 @@ fn resolveSymbols(self: *Zld) !void { } self.unresolved.clearRetainingCapacity(); - var referenced = std.AutoHashMap(union(enum) { - dylib: *Dylib, - stub: *Stub, - }, void).init(self.allocator); + var referenced = std.AutoHashMap(*Dylib, void).init(self.allocator); defer referenced.deinit(); loop: while (unresolved.popOrNull()) |undef| { const proxy = self.imports.get(undef.name) orelse outer: { const proxy = inner: { - for (self.dylibs.items) |dylib| { + for (self.dylibs.items) |dylib, i| { const proxy = (try dylib.createProxy(undef.name)) orelse continue; - try referenced.put(.{ .dylib = dylib }, {}); - break :inner proxy; - } - for (self.lib_stubs.items) |stub, i| { - const proxy = (try stub.createProxy(undef.name)) orelse continue; - if (self.libsystem_stub_index.? != @intCast(u16, i)) { - // LibSystem gets its load command separately. - try referenced.put(.{ .stub = stub }, {}); + if (self.libsystem_dylib_index.? != @intCast(u16, i)) { // LibSystem gets load command seperately. + try referenced.put(dylib, {}); } break :inner proxy; } @@ -1829,33 +1822,17 @@ fn resolveSymbols(self: *Zld) !void { // Add LC_LOAD_DYLIB load command for each referenced dylib/stub. var it = referenced.iterator(); - while (it.next()) |key| { - var dylib_cmd = blk: { - switch (key.key_ptr.*) { - .dylib => |dylib| { - dylib.ordinal = self.next_dylib_ordinal; - const dylib_id = dylib.id orelse unreachable; - break :blk try createLoadDylibCommand( - self.allocator, - dylib_id.name, - dylib_id.timestamp, - dylib_id.current_version, - dylib_id.compatibility_version, - ); - }, - .stub => |stub| { - stub.ordinal = self.next_dylib_ordinal; - const dylib_id = stub.id orelse unreachable; - break :blk try createLoadDylibCommand( - self.allocator, - dylib_id.name, - dylib_id.timestamp, - dylib_id.current_version, - dylib_id.compatibility_version, - ); - }, - } - }; + while (it.next()) |entry| { + const dylib = entry.key_ptr.*; + dylib.ordinal = self.next_dylib_ordinal; + const dylib_id = dylib.id orelse unreachable; + var dylib_cmd = try createLoadDylibCommand( + self.allocator, + dylib_id.name, + dylib_id.timestamp, + dylib_id.current_version, + dylib_id.compatibility_version, + ); errdefer dylib_cmd.deinit(self.allocator); try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd }); self.next_dylib_ordinal += 1; @@ -1873,8 +1850,8 @@ fn resolveSymbols(self: *Zld) !void { } // Finally put dyld_stub_binder as an Import - const libsystem_stub = self.lib_stubs.items[self.libsystem_stub_index.?]; - const proxy = (try libsystem_stub.createProxy("dyld_stub_binder")) orelse { + const libsystem_dylib = self.dylibs.items[self.libsystem_dylib_index.?]; + const proxy = (try libsystem_dylib.createProxy("dyld_stub_binder")) orelse { log.err("undefined reference to symbol 'dyld_stub_binder'", .{}); return error.UndefinedSymbolReference; };