authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-07-01 15:11:55+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-07-15 18:49:46+02:00
log3622fe08dbdcaccb04204b48257e1d5fcbe0d164
tree10bac4d011d8ccd13ee3d550e998daa7d1678b2f
parent9c3ebe0216306b5e346ec52959de41d1b4d504d9

zld: abstract away string table with fewer allocs


4 files changed, 119 insertions(+), 80 deletions(-)

CMakeLists.txt+1
...@@ -581,6 +581,7 @@ set(ZIG_STAGE2_SOURCES...@@ -581,6 +581,7 @@ set(ZIG_STAGE2_SOURCES
581 "${CMAKE_SOURCE_DIR}/src/link/MachO/DebugSymbols.zig"581 "${CMAKE_SOURCE_DIR}/src/link/MachO/DebugSymbols.zig"
582 "${CMAKE_SOURCE_DIR}/src/link/MachO/Dylib.zig"582 "${CMAKE_SOURCE_DIR}/src/link/MachO/Dylib.zig"
583 "${CMAKE_SOURCE_DIR}/src/link/MachO/Object.zig"583 "${CMAKE_SOURCE_DIR}/src/link/MachO/Object.zig"
584 "${CMAKE_SOURCE_DIR}/src/link/MachO/StringTable.zig"
584 "${CMAKE_SOURCE_DIR}/src/link/MachO/Symbol.zig"585 "${CMAKE_SOURCE_DIR}/src/link/MachO/Symbol.zig"
585 "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"586 "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"
586 "${CMAKE_SOURCE_DIR}/src/link/MachO/Zld.zig"587 "${CMAKE_SOURCE_DIR}/src/link/MachO/Zld.zig"
src/link/MachO.zig+27-73
...@@ -26,6 +26,7 @@ const target_util = @import("../target.zig");...@@ -26,6 +26,7 @@ const target_util = @import("../target.zig");
26const DebugSymbols = @import("MachO/DebugSymbols.zig");26const DebugSymbols = @import("MachO/DebugSymbols.zig");
27const Trie = @import("MachO/Trie.zig");27const Trie = @import("MachO/Trie.zig");
28const CodeSignature = @import("MachO/CodeSignature.zig");28const CodeSignature = @import("MachO/CodeSignature.zig");
29const StringTable = @import("MachO/StringTable.zig");
29const Zld = @import("MachO/Zld.zig");30const Zld = @import("MachO/Zld.zig");
3031
31usingnamespace @import("MachO/commands.zig");32usingnamespace @import("MachO/commands.zig");
...@@ -116,9 +117,7 @@ offset_table_free_list: std.ArrayListUnmanaged(u32) = .{},...@@ -116,9 +117,7 @@ offset_table_free_list: std.ArrayListUnmanaged(u32) = .{},
116117
117stub_helper_stubs_start_off: ?u64 = null,118stub_helper_stubs_start_off: ?u64 = null,
118119
119/// Table of symbol names aka the string table.120strtab: StringTable = undefined,
120string_table: std.ArrayListUnmanaged(u8) = .{},
121string_table_directory: std.StringHashMapUnmanaged(u32) = .{},
122121
123/// Table of GOT entries.122/// Table of GOT entries.
124offset_table: std.ArrayListUnmanaged(GOTEntry) = .{},123offset_table: std.ArrayListUnmanaged(GOTEntry) = .{},
...@@ -131,9 +130,9 @@ rebase_info_dirty: bool = false,...@@ -131,9 +130,9 @@ rebase_info_dirty: bool = false,
131binding_info_dirty: bool = false,130binding_info_dirty: bool = false,
132lazy_binding_info_dirty: bool = false,131lazy_binding_info_dirty: bool = false,
133export_info_dirty: bool = false,132export_info_dirty: bool = false,
134string_table_dirty: bool = false,
135133
136string_table_needs_relocation: bool = false,134strtab_dirty: bool = false,
135strtab_needs_relocation: bool = false,
137136
138/// A list of text blocks that have surplus capacity. This list can have false137/// A list of text blocks that have surplus capacity. This list can have false
139/// positives, as functions grow and shrink over time, only sometimes being added138/// positives, as functions grow and shrink over time, only sometimes being added
...@@ -413,6 +412,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio...@@ -413,6 +412,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
413412
414pub fn createEmpty(gpa: *Allocator, options: link.Options) !*MachO {413pub fn createEmpty(gpa: *Allocator, options: link.Options) !*MachO {
415 const self = try gpa.create(MachO);414 const self = try gpa.create(MachO);
415
416 self.* = .{416 self.* = .{
417 .base = .{417 .base = .{
418 .tag = .macho,418 .tag = .macho,
...@@ -421,7 +421,9 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*MachO {...@@ -421,7 +421,9 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*MachO {
421 .file = null,421 .file = null,
422 },422 },
423 .page_size = if (options.target.cpu.arch == .aarch64) 0x4000 else 0x1000,423 .page_size = if (options.target.cpu.arch == .aarch64) 0x4000 else 0x1000,
424 .strtab = try StringTable.init(gpa),
424 };425 };
426
425 return self;427 return self;
426}428}
427429
...@@ -499,8 +501,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {...@@ -499,8 +501,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
499 assert(!self.binding_info_dirty);501 assert(!self.binding_info_dirty);
500 assert(!self.lazy_binding_info_dirty);502 assert(!self.lazy_binding_info_dirty);
501 assert(!self.export_info_dirty);503 assert(!self.export_info_dirty);
502 assert(!self.string_table_dirty);504 assert(!self.strtab_dirty);
503 assert(!self.string_table_needs_relocation);505 assert(!self.strtab_needs_relocation);
504506
505 if (target.cpu.arch == .aarch64) {507 if (target.cpu.arch == .aarch64) {
506 switch (output_mode) {508 switch (output_mode) {
...@@ -977,14 +979,7 @@ pub fn deinit(self: *MachO) void {...@@ -977,14 +979,7 @@ pub fn deinit(self: *MachO) void {
977 self.text_block_free_list.deinit(self.base.allocator);979 self.text_block_free_list.deinit(self.base.allocator);
978 self.offset_table.deinit(self.base.allocator);980 self.offset_table.deinit(self.base.allocator);
979 self.offset_table_free_list.deinit(self.base.allocator);981 self.offset_table_free_list.deinit(self.base.allocator);
980 {982 self.strtab.deinit();
981 var it = self.string_table_directory.keyIterator();
982 while (it.next()) |key| {
983 self.base.allocator.free(key.*);
984 }
985 }
986 self.string_table_directory.deinit(self.base.allocator);
987 self.string_table.deinit(self.base.allocator);
988 self.globals.deinit(self.base.allocator);983 self.globals.deinit(self.base.allocator);
989 self.globals_free_list.deinit(self.base.allocator);984 self.globals_free_list.deinit(self.base.allocator);
990 self.locals.deinit(self.base.allocator);985 self.locals.deinit(self.base.allocator);
...@@ -1202,7 +1197,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {...@@ -1202,7 +1197,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
1202 const new_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{mem.spanZ(decl.name)});1197 const new_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{mem.spanZ(decl.name)});
1203 defer self.base.allocator.free(new_name);1198 defer self.base.allocator.free(new_name);
12041199
1205 symbol.n_strx = try self.updateString(symbol.n_strx, new_name);1200 symbol.n_strx = try self.strtab.getOrPut(new_name);
1206 symbol.n_type = macho.N_SECT;1201 symbol.n_type = macho.N_SECT;
1207 symbol.n_sect = @intCast(u8, self.text_section_index.?) + 1;1202 symbol.n_sect = @intCast(u8, self.text_section_index.?) + 1;
1208 symbol.n_desc = 0;1203 symbol.n_desc = 0;
...@@ -1214,7 +1209,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {...@@ -1214,7 +1209,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
1214 const decl_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{mem.spanZ(decl.name)});1209 const decl_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{mem.spanZ(decl.name)});
1215 defer self.base.allocator.free(decl_name);1210 defer self.base.allocator.free(decl_name);
12161211
1217 const name_str_index = try self.makeString(decl_name);1212 const name_str_index = try self.strtab.getOrPut(decl_name);
1218 const addr = try self.allocateTextBlock(&decl.link.macho, code.len, required_alignment);1213 const addr = try self.allocateTextBlock(&decl.link.macho, code.len, required_alignment);
12191214
1220 log.debug("allocated text block for {s} at 0x{x}", .{ decl_name, addr });1215 log.debug("allocated text block for {s} at 0x{x}", .{ decl_name, addr });
...@@ -1404,14 +1399,14 @@ pub fn updateDeclExports(...@@ -1404,14 +1399,14 @@ pub fn updateDeclExports(
1404 if (exp.link.macho.sym_index) |i| {1399 if (exp.link.macho.sym_index) |i| {
1405 const sym = &self.globals.items[i];1400 const sym = &self.globals.items[i];
1406 sym.* = .{1401 sym.* = .{
1407 .n_strx = try self.updateString(sym.n_strx, exp_name),1402 .n_strx = try self.strtab.getOrPut(exp_name),
1408 .n_type = n_type,1403 .n_type = n_type,
1409 .n_sect = @intCast(u8, self.text_section_index.?) + 1,1404 .n_sect = @intCast(u8, self.text_section_index.?) + 1,
1410 .n_desc = n_desc,1405 .n_desc = n_desc,
1411 .n_value = decl_sym.n_value,1406 .n_value = decl_sym.n_value,
1412 };1407 };
1413 } else {1408 } else {
1414 const name_str_index = try self.makeString(exp_name);1409 const name_str_index = try self.strtab.getOrPut(exp_name);
1415 const i = if (self.globals_free_list.popOrNull()) |i| i else blk: {1410 const i = if (self.globals_free_list.popOrNull()) |i| i else blk: {
1416 _ = self.globals.addOneAssumeCapacity();1411 _ = self.globals.addOneAssumeCapacity();
1417 self.export_info_dirty = true;1412 self.export_info_dirty = true;
...@@ -1787,15 +1782,14 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -1787,15 +1782,14 @@ pub fn populateMissingMetadata(self: *MachO) !void {
1787 symtab.symoff = @intCast(u32, symtab_off);1782 symtab.symoff = @intCast(u32, symtab_off);
1788 symtab.nsyms = @intCast(u32, self.base.options.symbol_count_hint);1783 symtab.nsyms = @intCast(u32, self.base.options.symbol_count_hint);
17891784
1790 try self.string_table.append(self.base.allocator, 0); // Need a null at position 0.1785 const strtab_size = self.strtab.size();
1791 const strtab_size = self.string_table.items.len;
1792 const strtab_off = self.findFreeSpaceLinkedit(strtab_size, 1, symtab_off);1786 const strtab_off = self.findFreeSpaceLinkedit(strtab_size, 1, symtab_off);
1793 log.debug("found string table free space 0x{x} to 0x{x}", .{ strtab_off, strtab_off + strtab_size });1787 log.debug("found string table free space 0x{x} to 0x{x}", .{ strtab_off, strtab_off + strtab_size });
1794 symtab.stroff = @intCast(u32, strtab_off);1788 symtab.stroff = @intCast(u32, strtab_off);
1795 symtab.strsize = @intCast(u32, strtab_size);1789 symtab.strsize = @intCast(u32, strtab_size);
17961790
1797 self.load_commands_dirty = true;1791 self.load_commands_dirty = true;
1798 self.string_table_dirty = true;1792 self.strtab_dirty = true;
1799 }1793 }
1800 if (self.dysymtab_cmd_index == null) {1794 if (self.dysymtab_cmd_index == null) {
1801 self.dysymtab_cmd_index = @intCast(u16, self.load_commands.items.len);1795 self.dysymtab_cmd_index = @intCast(u16, self.load_commands.items.len);
...@@ -1930,7 +1924,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -1930,7 +1924,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
1930 if (!self.nonlazy_imports.contains("dyld_stub_binder")) {1924 if (!self.nonlazy_imports.contains("dyld_stub_binder")) {
1931 const index = @intCast(u32, self.nonlazy_imports.count());1925 const index = @intCast(u32, self.nonlazy_imports.count());
1932 const name = try self.base.allocator.dupe(u8, "dyld_stub_binder");1926 const name = try self.base.allocator.dupe(u8, "dyld_stub_binder");
1933 const offset = try self.makeString("dyld_stub_binder");1927 const offset = try self.strtab.getOrPut("dyld_stub_binder");
1934 try self.nonlazy_imports.putNoClobber(self.base.allocator, name, .{1928 try self.nonlazy_imports.putNoClobber(self.base.allocator, name, .{
1935 .symbol = .{1929 .symbol = .{
1936 .n_strx = offset,1930 .n_strx = offset,
...@@ -2061,49 +2055,9 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,...@@ -2061,49 +2055,9 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
2061 return vaddr;2055 return vaddr;
2062}2056}
20632057
2064fn makeString(self: *MachO, bytes: []const u8) !u32 {
2065 if (self.string_table_directory.get(bytes)) |offset| {
2066 log.debug("reusing '{s}' from string table at offset 0x{x}", .{ bytes, offset });
2067 return offset;
2068 }
2069
2070 try self.string_table.ensureCapacity(self.base.allocator, self.string_table.items.len + bytes.len + 1);
2071 const offset = @intCast(u32, self.string_table.items.len);
2072
2073 log.debug("writing new string '{s}' into string table at offset 0x{x}", .{ bytes, offset });
2074
2075 self.string_table.appendSliceAssumeCapacity(bytes);
2076 self.string_table.appendAssumeCapacity(0);
2077
2078 try self.string_table_directory.putNoClobber(
2079 self.base.allocator,
2080 try self.base.allocator.dupe(u8, bytes),
2081 offset,
2082 );
2083
2084 self.string_table_dirty = true;
2085 if (self.d_sym) |*ds|
2086 ds.string_table_dirty = true;
2087
2088 return offset;
2089}
2090
2091fn getString(self: *MachO, str_off: u32) []const u8 {
2092 assert(str_off < self.string_table.items.len);
2093 return mem.spanZ(@ptrCast([*:0]const u8, self.string_table.items.ptr + str_off));
2094}
2095
2096fn updateString(self: *MachO, old_str_off: u32, new_name: []const u8) !u32 {
2097 const existing_name = self.getString(old_str_off);
2098 if (mem.eql(u8, existing_name, new_name)) {
2099 return old_str_off;
2100 }
2101 return self.makeString(new_name);
2102}
2103
2104pub fn addExternSymbol(self: *MachO, name: []const u8) !u32 {2058pub fn addExternSymbol(self: *MachO, name: []const u8) !u32 {
2105 const index = @intCast(u32, self.lazy_imports.count());2059 const index = @intCast(u32, self.lazy_imports.count());
2106 const offset = try self.makeString(name);2060 const offset = try self.strtab.getOrPut(name);
2107 const sym_name = try self.base.allocator.dupe(u8, name);2061 const sym_name = try self.base.allocator.dupe(u8, name);
2108 const dylib_ordinal = 1; // TODO this is now hardcoded, since we only support libSystem.2062 const dylib_ordinal = 1; // TODO this is now hardcoded, since we only support libSystem.
2109 try self.lazy_imports.putNoClobber(self.base.allocator, sym_name, .{2063 try self.lazy_imports.putNoClobber(self.base.allocator, sym_name, .{
...@@ -2293,7 +2247,7 @@ fn writeOffsetTableEntry(self: *MachO, index: usize) !void {...@@ -2293,7 +2247,7 @@ fn writeOffsetTableEntry(self: *MachO, index: usize) !void {
2293 },2247 },
2294 }2248 }
2295 };2249 };
2296 const sym_name = self.getString(sym.n_strx);2250 const sym_name = self.strtab.get(sym.n_strx) orelse unreachable;
2297 log.debug("writing offset table entry [ 0x{x} => 0x{x} ({s}) ]", .{ off, sym.n_value, sym_name });2251 log.debug("writing offset table entry [ 0x{x} => 0x{x} ({s}) ]", .{ off, sym.n_value, sym_name });
2298 try self.base.file.?.pwriteAll(mem.asBytes(&sym.n_value), off);2252 try self.base.file.?.pwriteAll(mem.asBytes(&sym.n_value), off);
2299}2253}
...@@ -2592,7 +2546,7 @@ fn relocateSymbolTable(self: *MachO) !void {...@@ -2592,7 +2546,7 @@ fn relocateSymbolTable(self: *MachO) !void {
2592 const amt = try self.base.file.?.copyRangeAll(symtab.symoff, self.base.file.?, new_symoff, existing_size);2546 const amt = try self.base.file.?.copyRangeAll(symtab.symoff, self.base.file.?, new_symoff, existing_size);
2593 if (amt != existing_size) return error.InputOutput;2547 if (amt != existing_size) return error.InputOutput;
2594 symtab.symoff = @intCast(u32, new_symoff);2548 symtab.symoff = @intCast(u32, new_symoff);
2595 self.string_table_needs_relocation = true;2549 self.strtab_needs_relocation = true;
2596 }2550 }
2597 symtab.nsyms = @intCast(u32, nsyms);2551 symtab.nsyms = @intCast(u32, nsyms);
2598 self.load_commands_dirty = true;2552 self.load_commands_dirty = true;
...@@ -2791,7 +2745,7 @@ fn writeExportTrie(self: *MachO) !void {...@@ -2791,7 +2745,7 @@ fn writeExportTrie(self: *MachO) !void {
2791 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;2745 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2792 for (self.globals.items) |symbol| {2746 for (self.globals.items) |symbol| {
2793 // TODO figure out if we should put all global symbols into the export trie2747 // TODO figure out if we should put all global symbols into the export trie
2794 const name = self.getString(symbol.n_strx);2748 const name = self.strtab.get(symbol.n_strx) orelse unreachable;
2795 assert(symbol.n_value >= text_segment.inner.vmaddr);2749 assert(symbol.n_value >= text_segment.inner.vmaddr);
2796 try trie.put(.{2750 try trie.put(.{
2797 .name = name,2751 .name = name,
...@@ -3065,26 +3019,26 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {...@@ -3065,26 +3019,26 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
3065}3019}
30663020
3067fn writeStringTable(self: *MachO) !void {3021fn writeStringTable(self: *MachO) !void {
3068 if (!self.string_table_dirty) return;3022 if (!self.strtab_dirty) return;
30693023
3070 const tracy = trace(@src());3024 const tracy = trace(@src());
3071 defer tracy.end();3025 defer tracy.end();
30723026
3073 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;3027 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
3074 const allocated_size = self.allocatedSizeLinkedit(symtab.stroff);3028 const allocated_size = self.allocatedSizeLinkedit(symtab.stroff);
3075 const needed_size = mem.alignForwardGeneric(u64, self.string_table.items.len, @alignOf(u64));3029 const needed_size = mem.alignForwardGeneric(u64, self.strtab.size(), @alignOf(u64));
30763030
3077 if (needed_size > allocated_size or self.string_table_needs_relocation) {3031 if (needed_size > allocated_size or self.strtab_needs_relocation) {
3078 symtab.strsize = 0;3032 symtab.strsize = 0;
3079 symtab.stroff = @intCast(u32, self.findFreeSpaceLinkedit(needed_size, 1, symtab.symoff));3033 symtab.stroff = @intCast(u32, self.findFreeSpaceLinkedit(needed_size, 1, symtab.symoff));
3080 self.string_table_needs_relocation = false;3034 self.strtab_needs_relocation = false;
3081 }3035 }
3082 symtab.strsize = @intCast(u32, needed_size);3036 symtab.strsize = @intCast(u32, needed_size);
3083 log.debug("writing string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize });3037 log.debug("writing string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize });
30843038
3085 try self.base.file.?.pwriteAll(self.string_table.items, symtab.stroff);3039 try self.base.file.?.pwriteAll(self.strtab.asSlice(), symtab.stroff);
3086 self.load_commands_dirty = true;3040 self.load_commands_dirty = true;
3087 self.string_table_dirty = false;3041 self.strtab_dirty = false;
3088}3042}
30893043
3090fn updateLinkeditSegmentSizes(self: *MachO) !void {3044fn updateLinkeditSegmentSizes(self: *MachO) !void {
src/link/MachO/DebugSymbols.zig+7-7
...@@ -76,7 +76,7 @@ dbg_info_decl_last: ?*TextBlock = null,...@@ -76,7 +76,7 @@ dbg_info_decl_last: ?*TextBlock = null,
76debug_string_table: std.ArrayListUnmanaged(u8) = .{},76debug_string_table: std.ArrayListUnmanaged(u8) = .{},
7777
78load_commands_dirty: bool = false,78load_commands_dirty: bool = false,
79string_table_dirty: bool = false,79strtab_dirty: bool = false,
80debug_string_table_dirty: bool = false,80debug_string_table_dirty: bool = false,
81debug_abbrev_section_dirty: bool = false,81debug_abbrev_section_dirty: bool = false,
82debug_aranges_section_dirty: bool = false,82debug_aranges_section_dirty: bool = false,
...@@ -131,7 +131,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void...@@ -131,7 +131,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void
131 },131 },
132 });132 });
133 self.load_commands_dirty = true;133 self.load_commands_dirty = true;
134 self.string_table_dirty = true;134 self.strtab_dirty = true;
135 }135 }
136 if (self.pagezero_segment_cmd_index == null) {136 if (self.pagezero_segment_cmd_index == null) {
137 self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len);137 self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
...@@ -593,7 +593,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: *Allocator, options: link.Opt...@@ -593,7 +593,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: *Allocator, options: link.Opt
593 try self.writeHeader();593 try self.writeHeader();
594594
595 assert(!self.load_commands_dirty);595 assert(!self.load_commands_dirty);
596 assert(!self.string_table_dirty);596 assert(!self.strtab_dirty);
597 assert(!self.debug_abbrev_section_dirty);597 assert(!self.debug_abbrev_section_dirty);
598 assert(!self.debug_aranges_section_dirty);598 assert(!self.debug_aranges_section_dirty);
599 assert(!self.debug_string_table_dirty);599 assert(!self.debug_string_table_dirty);
...@@ -807,14 +807,14 @@ pub fn writeLocalSymbol(self: *DebugSymbols, index: usize) !void {...@@ -807,14 +807,14 @@ pub fn writeLocalSymbol(self: *DebugSymbols, index: usize) !void {
807}807}
808808
809fn writeStringTable(self: *DebugSymbols) !void {809fn writeStringTable(self: *DebugSymbols) !void {
810 if (!self.string_table_dirty) return;810 if (!self.strtab_dirty) return;
811811
812 const tracy = trace(@src());812 const tracy = trace(@src());
813 defer tracy.end();813 defer tracy.end();
814814
815 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;815 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
816 const allocated_size = self.allocatedSizeLinkedit(symtab.stroff);816 const allocated_size = self.allocatedSizeLinkedit(symtab.stroff);
817 const needed_size = mem.alignForwardGeneric(u64, self.base.string_table.items.len, @alignOf(u64));817 const needed_size = mem.alignForwardGeneric(u64, self.base.strtab.size(), @alignOf(u64));
818818
819 if (needed_size > allocated_size) {819 if (needed_size > allocated_size) {
820 symtab.strsize = 0;820 symtab.strsize = 0;
...@@ -823,9 +823,9 @@ fn writeStringTable(self: *DebugSymbols) !void {...@@ -823,9 +823,9 @@ fn writeStringTable(self: *DebugSymbols) !void {
823 symtab.strsize = @intCast(u32, needed_size);823 symtab.strsize = @intCast(u32, needed_size);
824 log.debug("writing string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize });824 log.debug("writing string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize });
825825
826 try self.file.pwriteAll(self.base.string_table.items, symtab.stroff);826 try self.file.pwriteAll(self.base.strtab.asSlice(), symtab.stroff);
827 self.load_commands_dirty = true;827 self.load_commands_dirty = true;
828 self.string_table_dirty = false;828 self.strtab_dirty = false;
829}829}
830830
831pub fn updateDeclLineNumber(self: *DebugSymbols, module: *Module, decl: *const Module.Decl) !void {831pub fn updateDeclLineNumber(self: *DebugSymbols, module: *Module, decl: *const Module.Decl) !void {
src/link/MachO/StringTable.zig created+84
...@@ -0,0 +1,84 @@
1const StringTable = @This();
2
3const std = @import("std");
4const log = std.log.scoped(.strtab);
5const mem = std.mem;
6
7const Allocator = mem.Allocator;
8
9allocator: *Allocator,
10buffer: std.ArrayListUnmanaged(u8) = .{},
11used_offsets: std.ArrayListUnmanaged(u32) = .{},
12cache: std.StringHashMapUnmanaged(u32) = .{},
13
14pub const Error = error{OutOfMemory};
15
16pub fn init(allocator: *Allocator) Error!StringTable {
17 var strtab = StringTable{
18 .allocator = allocator,
19 };
20 try strtab.buffer.append(allocator, 0);
21 return strtab;
22}
23
24pub fn deinit(self: *StringTable) void {
25 self.cache.deinit(self.allocator);
26 self.used_offsets.deinit(self.allocator);
27 self.buffer.deinit(self.allocator);
28}
29
30pub fn getOrPut(self: *StringTable, string: []const u8) Error!u32 {
31 if (self.cache.get(string)) |off| {
32 log.debug("reusing string '{s}' at offset 0x{x}", .{ string, off });
33 return off;
34 }
35
36 const invalidate_cache = self.needsToGrow(string.len + 1);
37
38 try self.buffer.ensureUnusedCapacity(self.allocator, string.len + 1);
39 const new_off = @intCast(u32, self.buffer.items.len);
40
41 log.debug("writing new string '{s}' at offset 0x{x}", .{ string, new_off });
42
43 self.buffer.appendSliceAssumeCapacity(string);
44 self.buffer.appendAssumeCapacity(0);
45
46 if (invalidate_cache) {
47 log.debug("invalidating cache", .{});
48 // Re-create the cache.
49 self.cache.clearRetainingCapacity();
50 for (self.used_offsets.items) |off| {
51 try self.cache.putNoClobber(self.allocator, self.get(off).?, off);
52 }
53 }
54
55 {
56 log.debug("cache:", .{});
57 var it = self.cache.iterator();
58 while (it.next()) |entry| {
59 log.debug(" | {s} => {}", .{ entry.key_ptr.*, entry.value_ptr.* });
60 }
61 }
62
63 try self.cache.putNoClobber(self.allocator, self.get(new_off).?, new_off);
64 try self.used_offsets.append(self.allocator, new_off);
65
66 return new_off;
67}
68
69pub fn get(self: StringTable, off: u32) ?[]const u8 {
70 if (off >= self.buffer.items.len) return null;
71 return mem.spanZ(@ptrCast([*:0]const u8, self.buffer.items.ptr + off));
72}
73
74pub fn asSlice(self: StringTable) []const u8 {
75 return self.buffer.items;
76}
77
78pub fn size(self: StringTable) u64 {
79 return self.buffer.items.len;
80}
81
82fn needsToGrow(self: StringTable, needed_space: u64) bool {
83 return self.buffer.capacity < needed_space + self.size();
84}