Commit 3cb6b6bd90

Jakub Konka <kubkon@jakubkonka.com>
2021-06-23 11:09:45
zld: merge Stub with Dylib struct
After giving it more thought, it doesn't make sense to separate the two structurally. Instead, there should be two constructors for a Dylib struct: one from binary file, and the other from a stub file. This cleans up a lot of code and opens the way for recursive parsing of re-exports from a dylib which are a hard requirement for native feel when linking frameworks.
1 parent 3f57468
Changed files (5)
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;
src/link/MachO/Stub.zig
@@ -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;
-}
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.?;
     }
 };
 
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,53 +338,46 @@ 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();
-                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;
+                if (input.kind == .dylib) {
+                    dylib.file = input.kind.dylib;
+                    try dylib.parse();
+                } else {
+                    try dylib.parseFromStub(input.kind.stub);
+                }
 
-                try stub.parse();
-                try self.lib_stubs.append(self.allocator, stub);
+                try self.dylibs.append(self.allocator, dylib);
             },
         }
     }
@@ -399,50 +387,64 @@ 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);
+        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 => {},
+                }
+            }
+        }
 
-            dylib.* = Dylib.init(self.allocator);
-            dylib.arch = self.arch.?;
-            dylib.name = try self.allocator.dupe(u8, lib);
-            dylib.file = file;
+        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);
 
-            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);
+                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 {
-                    file.close();
-                    log.warn("unknown filetype for a library: '{s}'", .{lib});
+                    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;
     };
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"