Commit f8678c48ff

Jakub Konka <kubkon@jakubkonka.com>
2021-07-15 07:41:59
zld: reuse string table for symbol names
rather than manage allocs separately per symbol.
1 parent ec874a9
Changed files (4)
src/link/MachO/Object.zig
@@ -389,7 +389,7 @@ const TextBlockParser = struct {
         return switch (rreg.linkage) {
             .global => true,
             .linkage_unit => lreg.linkage == .translation_unit,
-            else => lsym.isTemp(),
+            else => lsym.isTemp(context.zld),
         };
     }
 
@@ -417,7 +417,7 @@ const TextBlockParser = struct {
             const sym = self.object.symbols.items[nlist_with_index.index];
             if (sym.payload != .regular) {
                 log.err("expected a regular symbol, found {s}", .{sym.payload});
-                log.err("  when remapping {s}", .{sym.name});
+                log.err("  when remapping {s}", .{self.zld.getString(sym.strx)});
                 return error.SymbolIsNotRegular;
             }
             assert(sym.payload.regular.local_sym_index != 0); // This means the symbol has not been properly resolved.
@@ -463,7 +463,7 @@ const TextBlockParser = struct {
                     }
                 }
             }
-            if (self.zld.globals.contains(senior_sym.name)) break :blk .global;
+            if (self.zld.globals.contains(self.zld.getString(senior_sym.strx))) break :blk .global;
             break :blk .static;
         } else null;
 
@@ -598,7 +598,11 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {
                             sectionName(sect),
                         });
                         defer self.allocator.free(name);
-                        const symbol = try Symbol.new(self.allocator, name);
+                        const symbol = try zld.allocator.create(Symbol);
+                        symbol.* = .{
+                            .strx = try zld.makeString(name),
+                            .payload = .{ .undef = .{} },
+                        };
                         try self.sections_as_symbols.putNoClobber(self.allocator, sect_id, symbol);
                         break :symbol symbol;
                     };
@@ -684,7 +688,7 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {
                     const reg = &sym.payload.regular;
                     if (reg.file) |file| {
                         if (file != self) {
-                            log.debug("deduping definition of {s} in {s}", .{ sym.name, self.name.? });
+                            log.debug("deduping definition of {s} in {s}", .{ zld.getString(sym.strx), self.name.? });
                             block.deinit();
                             self.allocator.destroy(block);
                             continue;
@@ -739,7 +743,11 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {
                     sectionName(sect),
                 });
                 defer self.allocator.free(name);
-                const symbol = try Symbol.new(self.allocator, name);
+                const symbol = try zld.allocator.create(Symbol);
+                symbol.* = .{
+                    .strx = try zld.makeString(name),
+                    .payload = .{ .undef = .{} },
+                };
                 try self.sections_as_symbols.putNoClobber(self.allocator, sect_id, symbol);
                 break :symbol symbol;
             };
@@ -812,7 +820,7 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {
                                 }
                             }
                         }
-                        if (zld.globals.contains(sym.name)) break :blk .global;
+                        if (zld.globals.contains(zld.getString(sym.strx))) break :blk .global;
                         break :blk .static;
                     } else null;
 
@@ -870,7 +878,7 @@ fn parseRelocs(
     try parser.parse();
 }
 
-pub fn symbolFromReloc(self: *Object, rel: macho.relocation_info) !*Symbol {
+pub fn symbolFromReloc(self: *Object, zld: *Zld, rel: macho.relocation_info) !*Symbol {
     const symbol = blk: {
         if (rel.r_extern == 1) {
             break :blk self.symbols.items[rel.r_symbolnum];
@@ -888,12 +896,15 @@ pub fn symbolFromReloc(self: *Object, rel: macho.relocation_info) !*Symbol {
                     sectionName(sect),
                 });
                 defer self.allocator.free(name);
-                const symbol = try Symbol.new(self.allocator, name);
-                symbol.payload = .{
-                    .regular = .{
-                        .linkage = .translation_unit,
-                        .address = sect.addr,
-                        .file = self,
+                const symbol = try zld.allocator.create(Symbol);
+                symbol.* = .{
+                    .strx = try zld.makeString(name),
+                    .payload = .{
+                        .regular = .{
+                            .linkage = .translation_unit,
+                            .address = sect.addr,
+                            .file = self,
+                        },
                     },
                 };
                 try self.sections_as_symbols.putNoClobber(self.allocator, sect_id, symbol);
src/link/MachO/reloc.zig
@@ -407,7 +407,7 @@ pub const Relocation = struct {
                 const dc_seg = zld.load_commands.items[zld.data_const_segment_cmd_index.?].Segment;
                 const got = dc_seg.sections.items[zld.got_section_index.?];
                 const got_index = self.target.got_index orelse {
-                    log.err("expected GOT entry for symbol '{s}'", .{self.target.name});
+                    log.err("expected GOT entry for symbol '{s}'", .{zld.getString(self.target.strx)});
                     log.err("  this is an internal linker error", .{});
                     return error.FailedToResolveRelocationTarget;
                 };
@@ -446,8 +446,8 @@ pub const Relocation = struct {
 
                     break :blk reg.address;
                 },
-                .proxy => |proxy| {
-                    if (mem.eql(u8, self.target.name, "__tlv_bootstrap")) {
+                .proxy => {
+                    if (mem.eql(u8, zld.getString(self.target.strx), "__tlv_bootstrap")) {
                         break :blk 0; // Dynamically bound by dyld.
                     }
 
@@ -460,7 +460,9 @@ pub const Relocation = struct {
                     break :blk stubs.addr + stubs_index * stubs.reserved2;
                 },
                 else => {
-                    log.err("failed to resolve symbol '{s}' as a relocation target", .{self.target.name});
+                    log.err("failed to resolve symbol '{s}' as a relocation target", .{
+                        zld.getString(self.target.strx),
+                    });
                     log.err("  this is an internal linker error", .{});
                     return error.FailedToResolveRelocationTarget;
                 },
@@ -634,7 +636,10 @@ pub const Parser = struct {
                 out_rel.target.got_index = index;
                 try self.zld.got_entries.append(self.zld.allocator, out_rel.target);
 
-                log.debug("adding GOT entry for symbol {s} at index {}", .{ out_rel.target.name, index });
+                log.debug("adding GOT entry for symbol {s} at index {}", .{
+                    self.zld.getString(out_rel.target.strx),
+                    index,
+                });
             } else if (out_rel.payload == .unsigned) {
                 const sym = out_rel.target;
                 switch (sym.payload) {
@@ -697,14 +702,14 @@ pub const Parser = struct {
                 sym.stubs_index = index;
                 try self.zld.stubs.append(self.zld.allocator, sym);
 
-                log.debug("adding stub entry for symbol {s} at index {}", .{ sym.name, index });
+                log.debug("adding stub entry for symbol {s} at index {}", .{ self.zld.getString(sym.strx), index });
             }
         }
     }
 
     fn parseBaseRelInfo(self: *Parser, rel: macho.relocation_info) !Relocation {
         const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr);
-        const target = try self.object.symbolFromReloc(rel);
+        const target = try self.object.symbolFromReloc(self.zld, rel);
         return Relocation{
             .offset = offset,
             .target = target,
@@ -888,7 +893,7 @@ pub const Parser = struct {
         assert(rel.r_pcrel == 0);
         assert(self.subtractor == null);
 
-        self.subtractor = try self.object.symbolFromReloc(rel);
+        self.subtractor = try self.object.symbolFromReloc(self.zld, rel);
     }
 
     fn parseLoad(self: *Parser, rel: macho.relocation_info) !Relocation {
src/link/MachO/Symbol.zig
@@ -11,8 +11,8 @@ const Dylib = @import("Dylib.zig");
 const Object = @import("Object.zig");
 const Zld = @import("Zld.zig");
 
-/// Symbol name. Owned slice.
-name: []const u8,
+/// Offset into the string table.
+strx: u32,
 
 /// Index in GOT table for indirection.
 got_index: ?u32 = null,
@@ -160,26 +160,11 @@ pub const Undefined = struct {
     }
 };
 
-/// Create new undefined symbol.
-pub fn new(allocator: *Allocator, name: []const u8) !*Symbol {
-    const new_sym = try allocator.create(Symbol);
-    errdefer allocator.destroy(new_sym);
-
-    new_sym.* = .{
-        .name = try allocator.dupe(u8, name),
-        .payload = .{
-            .undef = .{},
-        },
-    };
-
-    return new_sym;
-}
-
 pub fn format(self: Symbol, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
     _ = fmt;
     _ = options;
     try std.fmt.format(writer, "Symbol {{", .{});
-    try std.fmt.format(writer, ".name = {s}, ", .{self.name});
+    try std.fmt.format(writer, ".strx = {d}, ", .{self.strx});
     if (self.got_index) |got_index| {
         try std.fmt.format(writer, ".got_index = {}, ", .{got_index});
     }
@@ -190,11 +175,12 @@ pub fn format(self: Symbol, comptime fmt: []const u8, options: std.fmt.FormatOpt
     try std.fmt.format(writer, "}}", .{});
 }
 
-pub fn isTemp(symbol: Symbol) bool {
+pub fn isTemp(symbol: Symbol, zld: *Zld) bool {
+    const sym_name = zld.getString(symbol.strx);
     switch (symbol.payload) {
         .regular => |regular| {
             if (regular.linkage == .translation_unit) {
-                return mem.startsWith(u8, symbol.name, "l") or mem.startsWith(u8, symbol.name, "L");
+                return mem.startsWith(u8, sym_name, "l") or mem.startsWith(u8, sym_name, "L");
             }
         },
         else => {},
@@ -202,24 +188,12 @@ pub fn isTemp(symbol: Symbol) bool {
     return false;
 }
 
-pub fn needsTlvOffset(self: Symbol, zld: *Zld) bool {
-    if (self.payload != .regular) return false;
-
-    const reg = self.payload.regular;
-    const seg = zld.load_command.items[reg.segment_id].Segment;
-    const sect = seg.sections.items[reg.section_id];
-    const sect_type = commands.sectionType(sect);
-
-    return sect_type == macho.S_THREAD_LOCAL_VARIABLES;
-}
-
 pub fn asNlist(symbol: *Symbol, zld: *Zld) !macho.nlist_64 {
-    const n_strx = try zld.makeString(symbol.name);
     const nlist = nlist: {
         switch (symbol.payload) {
             .regular => |regular| {
                 var nlist = macho.nlist_64{
-                    .n_strx = n_strx,
+                    .n_strx = symbol.strx,
                     .n_type = macho.N_SECT,
                     .n_sect = regular.sectionId(zld),
                     .n_desc = 0,
@@ -239,7 +213,7 @@ pub fn asNlist(symbol: *Symbol, zld: *Zld) !macho.nlist_64 {
             .tentative => {
                 // TODO
                 break :nlist macho.nlist_64{
-                    .n_strx = n_strx,
+                    .n_strx = symbol.strx,
                     .n_type = macho.N_UNDF,
                     .n_sect = 0,
                     .n_desc = 0,
@@ -248,7 +222,7 @@ pub fn asNlist(symbol: *Symbol, zld: *Zld) !macho.nlist_64 {
             },
             .proxy => |proxy| {
                 break :nlist macho.nlist_64{
-                    .n_strx = n_strx,
+                    .n_strx = symbol.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,
@@ -258,7 +232,7 @@ pub fn asNlist(symbol: *Symbol, zld: *Zld) !macho.nlist_64 {
             .undef => {
                 // TODO
                 break :nlist macho.nlist_64{
-                    .n_strx = n_strx,
+                    .n_strx = symbol.strx,
                     .n_type = macho.N_UNDF,
                     .n_sect = 0,
                     .n_desc = 0,
@@ -270,10 +244,6 @@ pub fn asNlist(symbol: *Symbol, zld: *Zld) !macho.nlist_64 {
     return nlist;
 }
 
-pub fn deinit(symbol: *Symbol, allocator: *Allocator) void {
-    allocator.free(symbol.name);
-}
-
 pub fn isStab(sym: macho.nlist_64) bool {
     return (macho.N_STAB & sym.n_type) != 0;
 }
src/link/MachO/Zld.zig
@@ -113,7 +113,6 @@ stub_helper_stubs_start_off: ?u64 = null,
 blocks: std.AutoHashMapUnmanaged(MatchingSection, *TextBlock) = .{},
 
 strtab: std.ArrayListUnmanaged(u8) = .{},
-strtab_cache: std.StringHashMapUnmanaged(u32) = .{},
 
 has_dices: bool = false,
 has_stabs: bool = false,
@@ -171,7 +170,7 @@ pub const TextBlock = struct {
                         .n_value = reg.address,
                     });
                     nlists.appendAssumeCapacity(.{
-                        .n_strx = try zld.makeString(sym.name),
+                        .n_strx = sym.strx,
                         .n_type = macho.N_FUN,
                         .n_sect = section_id,
                         .n_desc = 0,
@@ -194,7 +193,7 @@ pub const TextBlock = struct {
                 },
                 .global => {
                     try nlists.append(.{
-                        .n_strx = try zld.makeString(sym.name),
+                        .n_strx = sym.strx,
                         .n_type = macho.N_GSYM,
                         .n_sect = 0,
                         .n_desc = 0,
@@ -203,7 +202,7 @@ pub const TextBlock = struct {
                 },
                 .static => {
                     try nlists.append(.{
-                        .n_strx = try zld.makeString(sym.name),
+                        .n_strx = sym.strx,
                         .n_type = macho.N_STSYM,
                         .n_sect = reg.sectionId(zld),
                         .n_desc = 0,
@@ -349,26 +348,20 @@ pub fn deinit(self: *Zld) void {
     self.dylibs.deinit(self.allocator);
 
     for (self.imports.items) |sym| {
-        sym.deinit(self.allocator);
         self.allocator.destroy(sym);
     }
     self.imports.deinit(self.allocator);
 
     for (self.locals.items) |sym| {
-        sym.deinit(self.allocator);
         self.allocator.destroy(sym);
     }
     self.locals.deinit(self.allocator);
 
+    for (self.globals.keys()) |key| {
+        self.allocator.free(key);
+    }
     self.globals.deinit(self.allocator);
 
-    {
-        var it = self.strtab_cache.keyIterator();
-        while (it.next()) |key| {
-            self.allocator.free(key.*);
-        }
-    }
-    self.strtab_cache.deinit(self.allocator);
     self.strtab.deinit(self.allocator);
 
     // TODO dealloc all blocks
@@ -1168,7 +1161,7 @@ fn allocateTextBlocks(self: *Zld) !void {
             sym.payload.regular.address = base_addr;
 
             log.debug("    {s}: start=0x{x}, end=0x{x}, size={}, align={}", .{
-                sym.name,
+                self.getString(sym.strx),
                 base_addr,
                 base_addr + block.size,
                 block.size,
@@ -1231,7 +1224,7 @@ fn writeTextBlocks(self: *Zld) !void {
 
                 const sym = self.locals.items[block.local_sym_index];
                 log.debug("    {s}: start=0x{x}, end=0x{x}, size={}, align={}", .{
-                    sym.name,
+                    self.getString(sym.strx),
                     aligned_base_off,
                     aligned_base_off + block.size,
                     block.size,
@@ -1552,14 +1545,17 @@ fn resolveSymbolsInObject(self: *Zld, object: *Object) !void {
 
         if (Symbol.isSect(sym) and !Symbol.isExt(sym)) {
             // Regular symbol local to translation unit
-            const symbol = try Symbol.new(self.allocator, sym_name);
-            symbol.payload = .{
-                .regular = .{
-                    .linkage = .translation_unit,
-                    .address = sym.n_value,
-                    .weak_ref = Symbol.isWeakRef(sym),
-                    .file = object,
-                    .local_sym_index = @intCast(u32, self.locals.items.len),
+            const symbol = try self.allocator.create(Symbol);
+            symbol.* = .{
+                .strx = try self.makeString(sym_name),
+                .payload = .{
+                    .regular = .{
+                        .linkage = .translation_unit,
+                        .address = sym.n_value,
+                        .weak_ref = Symbol.isWeakRef(sym),
+                        .file = object,
+                        .local_sym_index = @intCast(u32, self.locals.items.len),
+                    },
                 },
             };
             try self.locals.append(self.allocator, symbol);
@@ -1569,9 +1565,13 @@ fn resolveSymbolsInObject(self: *Zld, object: *Object) !void {
 
         const symbol = self.globals.get(sym_name) orelse symbol: {
             // Insert new global symbol.
-            const symbol = try Symbol.new(self.allocator, sym_name);
-            symbol.payload.undef.file = object;
-            try self.globals.putNoClobber(self.allocator, symbol.name, symbol);
+            const symbol = try self.allocator.create(Symbol);
+            symbol.* = .{
+                .strx = try self.makeString(sym_name),
+                .payload = .{ .undef = .{ .file = object } },
+            };
+            const alloc_name = try self.allocator.dupe(u8, sym_name);
+            try self.globals.putNoClobber(self.allocator, alloc_name, symbol);
             break :symbol symbol;
         };
 
@@ -1628,7 +1628,8 @@ fn resolveSymbolsInObject(self: *Zld, object: *Object) !void {
 fn resolveSymbols(self: *Zld) !void {
     // TODO mimicking insertion of null symbol from incremental linker.
     // This will need to moved.
-    const null_sym = try Symbol.new(self.allocator, "");
+    const null_sym = try self.allocator.create(Symbol);
+    null_sym.* = .{ .strx = 0, .payload = .{ .undef = .{} } };
     try self.locals.append(self.allocator, null_sym);
 
     // First pass, resolve symbols in provided objects.
@@ -1639,12 +1640,13 @@ fn resolveSymbols(self: *Zld) !void {
     // Second pass, resolve symbols in static libraries.
     var sym_it = self.globals.iterator();
     while (sym_it.next()) |entry| {
+        const sym_name = entry.key_ptr.*;
         const symbol = entry.value_ptr.*;
         if (symbol.payload != .undef) continue;
 
         for (self.archives.items) |archive| {
             // Check if the entry exists in a static archive.
-            const offsets = archive.toc.get(symbol.name) orelse {
+            const offsets = archive.toc.get(sym_name) orelse {
                 // No hit.
                 continue;
             };
@@ -1734,21 +1736,27 @@ fn resolveSymbols(self: *Zld) !void {
     // Third pass, resolve symbols in dynamic libraries.
     {
         // Put dyld_stub_binder as an undefined special symbol.
-        const symbol = try Symbol.new(self.allocator, "dyld_stub_binder");
+        const symbol = try self.allocator.create(Symbol);
+        symbol.* = .{
+            .strx = try self.makeString("dyld_stub_binder"),
+            .payload = .{ .undef = .{} },
+        };
         const index = @intCast(u32, self.got_entries.items.len);
         symbol.got_index = index;
         try self.got_entries.append(self.allocator, symbol);
-        try self.globals.putNoClobber(self.allocator, symbol.name, symbol);
+        const alloc_name = try self.allocator.dupe(u8, "dyld_stub_binder");
+        try self.globals.putNoClobber(self.allocator, alloc_name, symbol);
     }
 
     var referenced = std.AutoHashMap(*Dylib, void).init(self.allocator);
     defer referenced.deinit();
 
-    loop: for (self.globals.values()) |symbol| {
+    loop: for (self.globals.keys()) |sym_name| {
+        const symbol = self.globals.get(sym_name).?;
         if (symbol.payload != .undef) continue;
 
         for (self.dylibs.items) |dylib| {
-            if (!dylib.symbols.contains(symbol.name)) continue;
+            if (!dylib.symbols.contains(sym_name)) continue;
 
             try referenced.put(dylib, {});
             const index = @intCast(u32, self.imports.items.len);
@@ -1798,10 +1806,11 @@ fn resolveSymbols(self: *Zld) !void {
     }
 
     var has_undefined = false;
-    for (self.globals.values()) |symbol| {
+    for (self.globals.keys()) |sym_name| {
+        const symbol = self.globals.get(sym_name).?;
         if (symbol.payload != .undef) continue;
 
-        log.err("undefined reference to symbol '{s}'", .{symbol.name});
+        log.err("undefined reference to symbol '{s}'", .{sym_name});
         if (symbol.payload.undef.file) |file| {
             log.err("  | referenced in {s}", .{file.name.?});
         }
@@ -2344,7 +2353,7 @@ fn writeBindInfoTable(self: *Zld) !void {
                 .offset = base_offset + sym.got_index.? * @sizeOf(u64),
                 .segment_id = segment_id,
                 .dylib_ordinal = proxy.dylibOrdinal(),
-                .name = sym.name,
+                .name = self.getString(sym.strx),
             });
         }
     }
@@ -2372,7 +2381,7 @@ fn writeBindInfoTable(self: *Zld) !void {
                         .offset = binding.offset + base_offset,
                         .segment_id = match.seg,
                         .dylib_ordinal = proxy.dylibOrdinal(),
-                        .name = bind_sym.name,
+                        .name = self.getString(bind_sym.strx),
                     });
                 }
 
@@ -2395,7 +2404,7 @@ fn writeBindInfoTable(self: *Zld) !void {
             .offset = base_offset,
             .segment_id = segment_id,
             .dylib_ordinal = proxy.dylibOrdinal(),
-            .name = sym.name,
+            .name = self.getString(sym.strx),
         });
     }
 
@@ -2435,7 +2444,7 @@ fn writeLazyBindInfoTable(self: *Zld) !void {
                 .offset = base_offset + sym.stubs_index.? * @sizeOf(u64),
                 .segment_id = segment_id,
                 .dylib_ordinal = proxy.dylibOrdinal(),
-                .name = sym.name,
+                .name = self.getString(sym.strx),
             });
         }
     }
@@ -2547,7 +2556,7 @@ fn writeExportInfo(self: *Zld) !void {
         if (sym.payload != .regular) continue;
         const reg = sym.payload.regular;
         if (reg.linkage != .global) continue;
-        try sorted_globals.append(sym.name);
+        try sorted_globals.append(self.getString(sym.strx));
     }
 
     std.sort.sort([]const u8, sorted_globals.items, {}, Sorter.lessThan);
@@ -2556,10 +2565,10 @@ fn writeExportInfo(self: *Zld) !void {
         const sym = self.globals.get(sym_name) orelse unreachable;
         const reg = sym.payload.regular;
 
-        log.debug("  | putting '{s}' defined at 0x{x}", .{ sym.name, reg.address });
+        log.debug("  | putting '{s}' defined at 0x{x}", .{ sym_name, reg.address });
 
         try trie.put(.{
-            .name = sym.name,
+            .name = sym_name,
             .vmaddr_offset = reg.address - base_address,
             .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR,
         });
@@ -2597,7 +2606,7 @@ fn writeSymbolTable(self: *Zld) !void {
 
     for (self.locals.items) |symbol, i| {
         if (i == 0) continue; // skip null symbol
-        if (symbol.isTemp()) continue; // TODO when merging codepaths, this should go into freelist
+        if (symbol.isTemp(self)) continue; // TODO when merging codepaths, this should go into freelist
 
         const reg = symbol.payload.regular;
         const nlist = try symbol.asNlist(self);
@@ -2673,7 +2682,7 @@ fn writeSymbolTable(self: *Zld) !void {
         const nlist = try sym.asNlist(self);
         const id = @intCast(u32, undefs.items.len);
         try undefs.append(nlist);
-        try undef_dir.putNoClobber(sym.name, id);
+        try undef_dir.putNoClobber(self.getString(sym.strx), id);
     }
 
     const nlocals = locals.items.len;
@@ -2735,7 +2744,8 @@ fn writeSymbolTable(self: *Zld) !void {
 
     stubs.reserved1 = 0;
     for (self.stubs.items) |sym| {
-        const id = undef_dir.get(sym.name) orelse unreachable;
+        const sym_name = self.getString(sym.strx);
+        const id = undef_dir.get(sym_name) orelse unreachable;
         try writer.writeIntLittle(u32, dysymtab.iundefsym + id);
     }
 
@@ -2743,7 +2753,8 @@ fn writeSymbolTable(self: *Zld) !void {
     for (self.got_entries.items) |sym| {
         switch (sym.payload) {
             .proxy => {
-                const id = undef_dir.get(sym.name) orelse unreachable;
+                const sym_name = self.getString(sym.strx);
+                const id = undef_dir.get(sym_name) orelse unreachable;
                 try writer.writeIntLittle(u32, dysymtab.iundefsym + id);
             },
             else => {
@@ -2754,7 +2765,8 @@ fn writeSymbolTable(self: *Zld) !void {
 
     la_symbol_ptr.reserved1 = got.reserved1 + ngot_entries;
     for (self.stubs.items) |sym| {
-        const id = undef_dir.get(sym.name) orelse unreachable;
+        const sym_name = self.getString(sym.strx);
+        const id = undef_dir.get(sym_name) orelse unreachable;
         try writer.writeIntLittle(u32, dysymtab.iundefsym + id);
     }
 
@@ -2940,11 +2952,6 @@ fn writeHeader(self: *Zld) !void {
 }
 
 pub fn makeString(self: *Zld, string: []const u8) !u32 {
-    if (self.strtab_cache.get(string)) |off| {
-        log.debug("reusing string '{s}' at offset 0x{x}", .{ string, off });
-        return off;
-    }
-
     try self.strtab.ensureUnusedCapacity(self.allocator, string.len + 1);
     const new_off = @intCast(u32, self.strtab.items.len);
 
@@ -2953,12 +2960,10 @@ pub fn makeString(self: *Zld, string: []const u8) !u32 {
     self.strtab.appendSliceAssumeCapacity(string);
     self.strtab.appendAssumeCapacity(0);
 
-    try self.strtab_cache.putNoClobber(self.allocator, try self.allocator.dupe(u8, string), new_off);
-
     return new_off;
 }
 
-pub fn getString(self: *Zld, off: u32) ?[]const u8 {
+pub fn getString(self: *Zld, off: u32) []const u8 {
     assert(off < self.strtab.items.len);
     return mem.spanZ(@ptrCast([*:0]const u8, self.strtab.items.ptr + off));
 }