Commit 2b3bda43e3

Jakub Konka <kubkon@jakubkonka.com>
2021-07-01 17:25:51
zld: abstract Symbol creation logic
1 parent 3622fe0
src/link/MachO/Dylib.zig
@@ -146,7 +146,12 @@ pub const CreateOpts = struct {
     id: ?Id = null,
 };
 
-pub fn createAndParseFromPath(allocator: *Allocator, arch: Arch, path: []const u8, opts: CreateOpts) Error!?[]*Dylib {
+pub fn createAndParseFromPath(
+    allocator: *Allocator,
+    arch: Arch,
+    path: []const u8,
+    opts: CreateOpts,
+) Error!?[]*Dylib {
     const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {
         error.FileNotFound => return null,
         else => |e| return e,
@@ -505,18 +510,7 @@ pub fn parseDependentLibs(self: *Dylib, out: *std.ArrayList(*Dylib)) !void {
 
 pub fn createProxy(self: *Dylib, 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,
-        },
+    return Symbol.Proxy.new(self.allocator, sym_name, .{
         .file = self,
-    };
-
-    return &proxy.base;
+    });
 }
src/link/MachO/Object.zig
@@ -9,12 +9,12 @@ const log = std.log.scoped(.object);
 const macho = std.macho;
 const mem = std.mem;
 const reloc = @import("reloc.zig");
+const parseName = @import("Zld.zig").parseName;
 
 const Allocator = mem.Allocator;
 const Arch = std.Target.Cpu.Arch;
 const Relocation = reloc.Relocation;
 const Symbol = @import("Symbol.zig");
-const parseName = @import("Zld.zig").parseName;
 
 usingnamespace @import("commands.zig");
 
@@ -437,47 +437,26 @@ pub fn parseSymbols(self: *Object) !void {
                     if (Symbol.isWeakDef(sym) or Symbol.isPext(sym)) break :linkage .linkage_unit;
                     break :linkage .global;
                 };
-                const regular = try self.allocator.create(Symbol.Regular);
-                errdefer self.allocator.destroy(regular);
-                regular.* = .{
-                    .base = .{
-                        .@"type" = .regular,
-                        .name = name,
-                    },
+                break :symbol try Symbol.Regular.new(self.allocator, name, .{
                     .linkage = linkage,
                     .address = sym.n_value,
                     .section = sym.n_sect - 1,
                     .weak_ref = Symbol.isWeakRef(sym),
                     .file = self,
-                };
-                break :symbol &regular.base;
+                });
             }
 
             if (sym.n_value != 0) {
-                const tentative = try self.allocator.create(Symbol.Tentative);
-                errdefer self.allocator.destroy(tentative);
-                tentative.* = .{
-                    .base = .{
-                        .@"type" = .tentative,
-                        .name = name,
-                    },
+                break :symbol try Symbol.Tentative.new(self.allocator, name, .{
                     .size = sym.n_value,
                     .alignment = (sym.n_desc >> 8) & 0x0f,
                     .file = self,
-                };
-                break :symbol &tentative.base;
+                });
             }
 
-            const undef = try self.allocator.create(Symbol.Unresolved);
-            errdefer self.allocator.destroy(undef);
-            undef.* = .{
-                .base = .{
-                    .@"type" = .unresolved,
-                    .name = name,
-                },
+            break :symbol try Symbol.Unresolved.new(self.allocator, name, .{
                 .file = self,
-            };
-            break :symbol &undef.base;
+            });
         };
 
         try self.symbols.append(self.allocator, symbol);
src/link/MachO/StringTable.zig
@@ -8,7 +8,6 @@ const Allocator = mem.Allocator;
 
 allocator: *Allocator,
 buffer: std.ArrayListUnmanaged(u8) = .{},
-used_offsets: std.ArrayListUnmanaged(u32) = .{},
 cache: std.StringHashMapUnmanaged(u32) = .{},
 
 pub const Error = error{OutOfMemory};
@@ -22,8 +21,13 @@ pub fn init(allocator: *Allocator) Error!StringTable {
 }
 
 pub fn deinit(self: *StringTable) void {
+    {
+        var it = self.cache.keyIterator();
+        while (it.next()) |key| {
+            self.allocator.free(key.*);
+        }
+    }
     self.cache.deinit(self.allocator);
-    self.used_offsets.deinit(self.allocator);
     self.buffer.deinit(self.allocator);
 }
 
@@ -33,8 +37,6 @@ pub fn getOrPut(self: *StringTable, string: []const u8) Error!u32 {
         return off;
     }
 
-    const invalidate_cache = self.needsToGrow(string.len + 1);
-
     try self.buffer.ensureUnusedCapacity(self.allocator, string.len + 1);
     const new_off = @intCast(u32, self.buffer.items.len);
 
@@ -43,25 +45,7 @@ pub fn getOrPut(self: *StringTable, string: []const u8) Error!u32 {
     self.buffer.appendSliceAssumeCapacity(string);
     self.buffer.appendAssumeCapacity(0);
 
-    if (invalidate_cache) {
-        log.debug("invalidating cache", .{});
-        // Re-create the cache.
-        self.cache.clearRetainingCapacity();
-        for (self.used_offsets.items) |off| {
-            try self.cache.putNoClobber(self.allocator, self.get(off).?, off);
-        }
-    }
-
-    {
-        log.debug("cache:", .{});
-        var it = self.cache.iterator();
-        while (it.next()) |entry| {
-            log.debug("  | {s} => {}", .{ entry.key_ptr.*, entry.value_ptr.* });
-        }
-    }
-
-    try self.cache.putNoClobber(self.allocator, self.get(new_off).?, new_off);
-    try self.used_offsets.append(self.allocator, new_off);
+    try self.cache.putNoClobber(self.allocator, try self.allocator.dupe(u8, string), new_off);
 
     return new_off;
 }
@@ -78,7 +62,3 @@ pub fn asSlice(self: StringTable) []const u8 {
 pub fn size(self: StringTable) u64 {
     return self.buffer.items.len;
 }
-
-fn needsToGrow(self: StringTable, needed_space: u64) bool {
-    return self.buffer.capacity < needed_space + self.size();
-}
src/link/MachO/Symbol.zig
@@ -7,6 +7,7 @@ const mem = std.mem;
 const Allocator = mem.Allocator;
 const Dylib = @import("Dylib.zig");
 const Object = @import("Object.zig");
+const StringTable = @import("StringTable.zig");
 
 pub const Type = enum {
     regular,
@@ -19,7 +20,7 @@ pub const Type = enum {
 @"type": Type,
 
 /// Symbol name. Owned slice.
-name: []u8,
+name: []const u8,
 
 /// Alias of.
 alias: ?*Symbol = null,
@@ -43,23 +44,14 @@ pub const Regular = struct {
     section: u8,
 
     /// Whether the symbol is a weak ref.
-    weak_ref: bool,
+    weak_ref: bool = false,
 
     /// Object file where to locate this symbol.
-    file: *Object,
+    /// null means self-reference.
+    file: ?*Object = null,
 
     /// Debug stab if defined.
-    stab: ?struct {
-        /// Stab kind
-        kind: enum {
-            function,
-            global,
-            static,
-        },
-
-        /// Size of the stab.
-        size: u64,
-    } = null,
+    stab: ?Stab = null,
 
     /// True if symbol was already committed into the final
     /// symbol table.
@@ -73,6 +65,68 @@ pub const Regular = struct {
         global,
     };
 
+    pub const Stab = struct {
+        /// Stab kind
+        kind: enum {
+            function,
+            global,
+            static,
+        },
+
+        /// Size of the stab.
+        size: u64,
+    };
+
+    const Opts = struct {
+        linkage: Linkage = .translation_unit,
+        address: u64 = 0,
+        section: u8 = 0,
+        weak_ref: bool = false,
+        file: ?*Object = null,
+        stab: ?Stab = null,
+    };
+
+    pub fn new(allocator: *Allocator, name: []const u8, opts: Opts) !*Symbol {
+        const reg = try allocator.create(Regular);
+        errdefer allocator.destroy(reg);
+
+        reg.* = .{
+            .base = .{
+                .@"type" = .regular,
+                .name = try allocator.dupe(u8, name),
+            },
+            .linkage = opts.linkage,
+            .address = opts.address,
+            .section = opts.section,
+            .weak_ref = opts.weak_ref,
+            .file = opts.file,
+            .stab = opts.stab,
+        };
+
+        return &reg.base;
+    }
+
+    pub fn asNlist(regular: *Regular, strtab: *StringTable) !macho.nlist_64 {
+        const n_strx = try strtab.getOrPut(regular.base.name);
+        var nlist = macho.nlist_64{
+            .n_strx = n_strx,
+            .n_type = macho.N_SECT,
+            .n_sect = regular.section,
+            .n_desc = 0,
+            .n_value = regular.address,
+        };
+
+        if (regular.linkage != .translation_unit) {
+            nlist.n_type |= macho.N_EXT;
+        }
+        if (regular.linkage == .linkage_unit) {
+            nlist.n_type |= macho.N_PEXT;
+            nlist.n_desc |= macho.N_WEAK_DEF;
+        }
+
+        return nlist;
+    }
+
     pub fn isTemp(regular: *Regular) bool {
         if (regular.linkage == .translation_unit) {
             return mem.startsWith(u8, regular.base.name, "l") or mem.startsWith(u8, regular.base.name, "L");
@@ -97,6 +151,36 @@ pub const Proxy = struct {
 
     pub const base_type: Symbol.Type = .proxy;
 
+    const Opts = struct {
+        file: ?*Dylib = null,
+    };
+
+    pub fn new(allocator: *Allocator, name: []const u8, opts: Opts) !*Symbol {
+        const proxy = try allocator.create(Proxy);
+        errdefer allocator.destroy(proxy);
+
+        proxy.* = .{
+            .base = .{
+                .@"type" = .proxy,
+                .name = try allocator.dupe(u8, name),
+            },
+            .file = opts.file,
+        };
+
+        return &proxy.base;
+    }
+
+    pub fn asNlist(proxy: *Proxy, strtab: *StringTable) !macho.nlist_64 {
+        const n_strx = try strtab.getOrPut(proxy.base.name);
+        return macho.nlist_64{
+            .n_strx = n_strx,
+            .n_type = macho.N_UNDF | macho.N_EXT,
+            .n_sect = 0,
+            .n_desc = (proxy.dylibOrdinal() * macho.N_SYMBOL_RESOLVER) | macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY,
+            .n_value = 0,
+        };
+    }
+
     pub fn deinit(proxy: *Proxy, allocator: *Allocator) void {
         proxy.bind_info.deinit(allocator);
     }
@@ -115,6 +199,36 @@ pub const Unresolved = struct {
     file: ?*Object = null,
 
     pub const base_type: Symbol.Type = .unresolved;
+
+    const Opts = struct {
+        file: ?*Object = null,
+    };
+
+    pub fn new(allocator: *Allocator, name: []const u8, opts: Opts) !*Symbol {
+        const undef = try allocator.create(Unresolved);
+        errdefer allocator.destroy(undef);
+
+        undef.* = .{
+            .base = .{
+                .@"type" = .unresolved,
+                .name = try allocator.dupe(u8, name),
+            },
+            .file = opts.file,
+        };
+
+        return &undef.base;
+    }
+
+    pub fn asNlist(undef: *Unresolved, strtab: *StringTable) !macho.nlist_64 {
+        const n_strx = try strtab.getOrPut(undef.base.name);
+        return macho.nlist_64{
+            .n_strx = n_strx,
+            .n_type = macho.N_UNDF,
+            .n_sect = 0,
+            .n_desc = 0,
+            .n_value = 0,
+        };
+    }
 };
 
 pub const Tentative = struct {
@@ -127,13 +241,49 @@ pub const Tentative = struct {
     alignment: u16,
 
     /// File where this symbol was referenced.
-    file: *Object,
+    file: ?*Object = null,
 
     pub const base_type: Symbol.Type = .tentative;
+
+    const Opts = struct {
+        size: u64 = 0,
+        alignment: u16 = 0,
+        file: ?*Object = null,
+    };
+
+    pub fn new(allocator: *Allocator, name: []const u8, opts: Opts) !*Symbol {
+        const tent = try allocator.create(Tentative);
+        errdefer allocator.destroy(tent);
+
+        tent.* = .{
+            .base = .{
+                .@"type" = .tentative,
+                .name = try allocator.dupe(u8, name),
+            },
+            .size = opts.size,
+            .alignment = opts.alignment,
+            .file = opts.file,
+        };
+
+        return &tent.base;
+    }
+
+    pub fn asNlist(tent: *Tentative, strtab: *StringTable) !macho.nlist_64 {
+        // TODO
+        const n_strx = try strtab.getOrPut(tent.base.name);
+        return macho.nlist_64{
+            .n_strx = n_strx,
+            .n_type = macho.N_UNDF,
+            .n_sect = 0,
+            .n_desc = 0,
+            .n_value = 0,
+        };
+    }
 };
 
 pub fn deinit(base: *Symbol, allocator: *Allocator) void {
     allocator.free(base.name);
+
     switch (base.@"type") {
         .proxy => @fieldParentPtr(Proxy, "base", base).deinit(allocator),
         else => {},
@@ -154,6 +304,15 @@ pub fn getTopmostAlias(base: *Symbol) *Symbol {
     return base;
 }
 
+pub fn asNlist(base: *Symbol, strtab: *StringTable) !macho.nlist_64 {
+    return switch (base.tag) {
+        .regular => @fieldParentPtr(Regular, "base", base).asNlist(strtab),
+        .proxy => @fieldParentPtr(Proxy, "base", base).asNlist(strtab),
+        .unresolved => @fieldParentPtr(Unresolved, "base", base).asNlist(strtab),
+        .tentative => @fieldParentPtr(Tentative, "base", base).asNlist(strtab),
+    };
+}
+
 pub fn isStab(sym: macho.nlist_64) bool {
     return (macho.N_STAB & sym.n_type) != 0;
 }
src/link/MachO/Zld.zig
@@ -17,6 +17,7 @@ const Archive = @import("Archive.zig");
 const CodeSignature = @import("CodeSignature.zig");
 const Dylib = @import("Dylib.zig");
 const Object = @import("Object.zig");
+const StringTable = @import("StringTable.zig");
 const Symbol = @import("Symbol.zig");
 const Trie = @import("Trie.zig");
 
@@ -24,6 +25,7 @@ usingnamespace @import("commands.zig");
 usingnamespace @import("bind.zig");
 
 allocator: *Allocator,
+strtab: StringTable,
 
 target: ?std.Target = null,
 page_size: ?u16 = null,
@@ -109,9 +111,6 @@ tentatives: std.StringArrayHashMapUnmanaged(*Symbol) = .{},
 /// Set if the linker found tentative definitions in any of the objects.
 tentative_defs_offset: u64 = 0,
 
-strtab: std.ArrayListUnmanaged(u8) = .{},
-strtab_dir: std.StringHashMapUnmanaged(u32) = .{},
-
 threadlocal_offsets: std.ArrayListUnmanaged(TlvOffset) = .{}, // TODO merge with Symbol abstraction
 local_rebases: std.ArrayListUnmanaged(Pointer) = .{},
 stubs: std.ArrayListUnmanaged(*Symbol) = .{},
@@ -138,8 +137,11 @@ const TlvOffset = struct {
 /// Default path to dyld
 const DEFAULT_DYLD_PATH: [*:0]const u8 = "/usr/lib/dyld";
 
-pub fn init(allocator: *Allocator) Zld {
-    return .{ .allocator = allocator };
+pub fn init(allocator: *Allocator) !Zld {
+    return Zld{
+        .allocator = allocator,
+        .strtab = try StringTable.init(allocator),
+    };
 }
 
 pub fn deinit(self: *Zld) void {
@@ -180,15 +182,7 @@ pub fn deinit(self: *Zld) void {
     self.tentatives.deinit(self.allocator);
     self.globals.deinit(self.allocator);
     self.unresolved.deinit(self.allocator);
-    self.strtab.deinit(self.allocator);
-
-    {
-        var it = self.strtab_dir.keyIterator();
-        while (it.next()) |key| {
-            self.allocator.free(key.*);
-        }
-    }
-    self.strtab_dir.deinit(self.allocator);
+    self.strtab.deinit();
 }
 
 pub fn closeFiles(self: Zld) void {
@@ -1137,16 +1131,7 @@ fn allocateTentativeSymbols(self: *Zld) !void {
     // Convert tentative definitions into regular symbols.
     for (self.tentatives.values()) |sym| {
         const tent = sym.cast(Symbol.Tentative) orelse unreachable;
-        const reg = try self.allocator.create(Symbol.Regular);
-        errdefer self.allocator.destroy(reg);
-
-        reg.* = .{
-            .base = .{
-                .@"type" = .regular,
-                .name = try self.allocator.dupe(u8, tent.base.name),
-                .got_index = tent.base.got_index,
-                .stubs_index = tent.base.stubs_index,
-            },
+        const reg = try Symbol.Regular.new(self.allocator, tent.base.name, .{
             .linkage = .global,
             .address = base_address,
             .section = section,
@@ -1156,16 +1141,18 @@ fn allocateTentativeSymbols(self: *Zld) !void {
                 .kind = .global,
                 .size = 0,
             },
-        };
+        });
+        reg.got_index = tent.base.got_index;
+        reg.stubs_index = tent.base.stubs_index;
 
-        try self.globals.putNoClobber(self.allocator, reg.base.name, &reg.base);
-        tent.base.alias = &reg.base;
+        try self.globals.putNoClobber(self.allocator, reg.name, reg);
+        tent.base.alias = reg;
 
         if (tent.base.got_index) |idx| {
-            self.got_entries.items[idx] = &reg.base;
+            self.got_entries.items[idx] = reg;
         }
         if (tent.base.stubs_index) |idx| {
-            self.stubs.items[idx] = &reg.base;
+            self.stubs.items[idx] = reg;
         }
 
         const address = mem.alignForwardGeneric(u64, base_address + tent.size, alignment);
@@ -1615,15 +1602,8 @@ fn resolveSymbols(self: *Zld) !void {
     {
         const name = try self.allocator.dupe(u8, "dyld_stub_binder");
         errdefer self.allocator.free(name);
-        const undef = try self.allocator.create(Symbol.Unresolved);
-        errdefer self.allocator.destroy(undef);
-        undef.* = .{
-            .base = .{
-                .@"type" = .unresolved,
-                .name = name,
-            },
-        };
-        try unresolved.append(&undef.base);
+        const undef = try Symbol.Unresolved.new(self.allocator, name, .{});
+        try unresolved.append(undef);
     }
 
     var referenced = std.AutoHashMap(*Dylib, void).init(self.allocator);
@@ -1641,16 +1621,7 @@ fn resolveSymbols(self: *Zld) !void {
                     // TODO this is just a temp patch until I work out what to actually
                     // do with ___dso_handle and __mh_execute_header symbols which are
                     // synthetically created by the linker on macOS.
-                    const name = try self.allocator.dupe(u8, undef.name);
-                    const proxy = try self.allocator.create(Symbol.Proxy);
-                    errdefer self.allocator.destroy(proxy);
-                    proxy.* = .{
-                        .base = .{
-                            .@"type" = .proxy,
-                            .name = name,
-                        },
-                    };
-                    break :inner &proxy.base;
+                    break :inner try Symbol.Proxy.new(self.allocator, undef.name, .{});
                 }
 
                 self.unresolved.putAssumeCapacityNoClobber(undef.name, undef);
@@ -2126,7 +2097,6 @@ fn populateMetadata(self: *Zld) !void {
                 .strsize = 0,
             },
         });
-        try self.strtab.append(self.allocator, 0);
     }
 
     if (self.dysymtab_cmd_index == null) {
@@ -2752,7 +2722,7 @@ fn writeDebugInfo(self: *Zld) !void {
         const dirname = std.fs.path.dirname(tu_path) orelse "./";
         // Current dir
         try stabs.append(.{
-            .n_strx = try self.makeString(tu_path[0 .. dirname.len + 1]),
+            .n_strx = try self.strtab.getOrPut(tu_path[0 .. dirname.len + 1]),
             .n_type = macho.N_SO,
             .n_sect = 0,
             .n_desc = 0,
@@ -2760,7 +2730,7 @@ fn writeDebugInfo(self: *Zld) !void {
         });
         // Artifact name
         try stabs.append(.{
-            .n_strx = try self.makeString(tu_path[dirname.len + 1 ..]),
+            .n_strx = try self.strtab.getOrPut(tu_path[dirname.len + 1 ..]),
             .n_type = macho.N_SO,
             .n_sect = 0,
             .n_desc = 0,
@@ -2768,7 +2738,7 @@ fn writeDebugInfo(self: *Zld) !void {
         });
         // Path to object file with debug info
         try stabs.append(.{
-            .n_strx = try self.makeString(object.name.?),
+            .n_strx = try self.strtab.getOrPut(object.name.?),
             .n_type = macho.N_OSO,
             .n_sect = 0,
             .n_desc = 1,
@@ -2801,7 +2771,7 @@ fn writeDebugInfo(self: *Zld) !void {
                         .n_value = reg.address,
                     });
                     try stabs.append(.{
-                        .n_strx = try self.makeString(sym.name),
+                        .n_strx = try self.strtab.getOrPut(sym.name),
                         .n_type = macho.N_FUN,
                         .n_sect = reg.section,
                         .n_desc = 0,
@@ -2824,7 +2794,7 @@ fn writeDebugInfo(self: *Zld) !void {
                 },
                 .global => {
                     try stabs.append(.{
-                        .n_strx = try self.makeString(sym.name),
+                        .n_strx = try self.strtab.getOrPut(sym.name),
                         .n_type = macho.N_GSYM,
                         .n_sect = 0,
                         .n_desc = 0,
@@ -2833,7 +2803,7 @@ fn writeDebugInfo(self: *Zld) !void {
                 },
                 .static => {
                     try stabs.append(.{
-                        .n_strx = try self.makeString(sym.name),
+                        .n_strx = try self.strtab.getOrPut(sym.name),
                         .n_type = macho.N_STSYM,
                         .n_sect = reg.section,
                         .n_desc = 0,
@@ -2892,24 +2862,14 @@ fn writeSymbolTable(self: *Zld) !void {
             if (reg.isTemp()) continue;
             if (reg.visited) continue;
 
+            const nlist = try reg.asNlist(&self.strtab);
+
             switch (reg.linkage) {
                 .translation_unit => {
-                    try locals.append(.{
-                        .n_strx = try self.makeString(sym.name),
-                        .n_type = macho.N_SECT,
-                        .n_sect = reg.section,
-                        .n_desc = 0,
-                        .n_value = reg.address,
-                    });
+                    try locals.append(nlist);
                 },
                 else => {
-                    try exports.append(.{
-                        .n_strx = try self.makeString(sym.name),
-                        .n_type = macho.N_SECT | macho.N_EXT,
-                        .n_sect = reg.section,
-                        .n_desc = 0,
-                        .n_value = reg.address,
-                    });
+                    try exports.append(nlist);
                 },
             }
 
@@ -2922,13 +2882,8 @@ fn writeSymbolTable(self: *Zld) !void {
 
     for (self.imports.values()) |sym| {
         const proxy = sym.cast(Symbol.Proxy) orelse unreachable;
-        try undefs.append(.{
-            .n_strx = try self.makeString(sym.name),
-            .n_type = macho.N_UNDF | macho.N_EXT,
-            .n_sect = 0,
-            .n_desc = (proxy.dylibOrdinal() * macho.N_SYMBOL_RESOLVER) | macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY,
-            .n_value = 0,
-        });
+        const nlist = try proxy.asNlist(&self.strtab);
+        try undefs.append(nlist);
     }
 
     const nlocals = locals.items.len;
@@ -3017,14 +2972,14 @@ fn writeStringTable(self: *Zld) !void {
     const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
     const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
     symtab.stroff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
-    symtab.strsize = @intCast(u32, mem.alignForwardGeneric(u64, self.strtab.items.len, @alignOf(u64)));
+    symtab.strsize = @intCast(u32, mem.alignForwardGeneric(u64, self.strtab.size(), @alignOf(u64)));
     seg.inner.filesize += symtab.strsize;
 
     log.debug("writing string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize });
 
-    try self.file.?.pwriteAll(self.strtab.items, symtab.stroff);
+    try self.file.?.pwriteAll(self.strtab.asSlice(), symtab.stroff);
 
-    if (symtab.strsize > self.strtab.items.len and self.target.?.cpu.arch == .x86_64) {
+    if (symtab.strsize > self.strtab.size() and self.target.?.cpu.arch == .x86_64) {
         // This is the last section, so we need to pad it out.
         try self.file.?.pwriteAll(&[_]u8{0}, seg.inner.fileoff + seg.inner.filesize - 1);
     }
@@ -3173,26 +3128,6 @@ fn writeHeader(self: *Zld) !void {
     try self.file.?.pwriteAll(mem.asBytes(&header), 0);
 }
 
-fn makeString(self: *Zld, bytes: []const u8) !u32 {
-    if (self.strtab_dir.get(bytes)) |offset| {
-        log.debug("reusing '{s}' from string table at offset 0x{x}", .{ bytes, offset });
-        return offset;
-    }
-
-    try self.strtab.ensureCapacity(self.allocator, self.strtab.items.len + bytes.len + 1);
-    const offset = @intCast(u32, self.strtab.items.len);
-    log.debug("writing new string '{s}' into string table at offset 0x{x}", .{ bytes, offset });
-    self.strtab.appendSliceAssumeCapacity(bytes);
-    self.strtab.appendAssumeCapacity(0);
-    try self.strtab_dir.putNoClobber(self.allocator, try self.allocator.dupe(u8, bytes), offset);
-    return offset;
-}
-
-fn getString(self: *const Zld, str_off: u32) []const u8 {
-    assert(str_off < self.strtab.items.len);
-    return mem.spanZ(@ptrCast([*:0]const u8, self.strtab.items.ptr + str_off));
-}
-
 pub fn parseName(name: *const [16]u8) []const u8 {
     const len = mem.indexOfScalar(u8, name, @as(u8, 0)) orelse name.len;
     return name[0..len];
src/link/MachO.zig
@@ -724,7 +724,7 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void {
             try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});
         }
     } else {
-        var zld = Zld.init(self.base.allocator);
+        var zld = try Zld.init(self.base.allocator);
         defer {
             zld.closeFiles();
             zld.deinit();