| ... | @@ -26,6 +26,7 @@ const target_util = @import("../target.zig"); | ... | @@ -26,6 +26,7 @@ const target_util = @import("../target.zig"); |
| 26 | const DebugSymbols = @import("MachO/DebugSymbols.zig"); | 26 | const DebugSymbols = @import("MachO/DebugSymbols.zig"); |
| 27 | const Trie = @import("MachO/Trie.zig"); | 27 | const Trie = @import("MachO/Trie.zig"); |
| 28 | const CodeSignature = @import("MachO/CodeSignature.zig"); | 28 | const CodeSignature = @import("MachO/CodeSignature.zig"); |
| | 29 | const StringTable = @import("MachO/StringTable.zig"); |
| 29 | const Zld = @import("MachO/Zld.zig"); | 30 | const Zld = @import("MachO/Zld.zig"); |
| 30 | | 31 | |
| 31 | usingnamespace @import("MachO/commands.zig"); | 32 | usingnamespace @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) = .{}, |
| 116 | | 117 | |
| 117 | stub_helper_stubs_start_off: ?u64 = null, | 118 | stub_helper_stubs_start_off: ?u64 = null, |
| 118 | | 119 | |
| 119 | /// Table of symbol names aka the string table. | 120 | strtab: StringTable = undefined, |
| 120 | string_table: std.ArrayListUnmanaged(u8) = .{}, | | |
| 121 | string_table_directory: std.StringHashMapUnmanaged(u32) = .{}, | | |
| 122 | | 121 | |
| 123 | /// Table of GOT entries. | 122 | /// Table of GOT entries. |
| 124 | offset_table: std.ArrayListUnmanaged(GOTEntry) = .{}, | 123 | offset_table: std.ArrayListUnmanaged(GOTEntry) = .{}, |
| ... | @@ -131,9 +130,9 @@ rebase_info_dirty: bool = false, | ... | @@ -131,9 +130,9 @@ rebase_info_dirty: bool = false, |
| 131 | binding_info_dirty: bool = false, | 130 | binding_info_dirty: bool = false, |
| 132 | lazy_binding_info_dirty: bool = false, | 131 | lazy_binding_info_dirty: bool = false, |
| 133 | export_info_dirty: bool = false, | 132 | export_info_dirty: bool = false, |
| 134 | string_table_dirty: bool = false, | | |
| 135 | | 133 | |
| 136 | string_table_needs_relocation: bool = false, | 134 | strtab_dirty: bool = false, |
| | 135 | strtab_needs_relocation: bool = false, |
| 137 | | 136 | |
| 138 | /// A list of text blocks that have surplus capacity. This list can have false | 137 | /// 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 added | 138 | /// 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 |
| 413 | | 412 | |
| 414 | pub fn createEmpty(gpa: *Allocator, options: link.Options) !*MachO { | 413 | pub 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 | } |
| 427 | | 429 | |
| ... | @@ -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); |
| 504 | | 506 | |
| 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); |
| 1204 | | 1199 | |
| 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); |
| 1216 | | 1211 | |
| 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); |
| 1219 | | 1214 | |
| 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); |
| 1789 | | 1784 | |
| 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); |
| 1796 | | 1790 | |
| 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 | } |
| 2063 | | 2057 | |
| 2064 | fn 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 | | | |
| 2091 | fn 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 | | | |
| 2096 | fn 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 | | | |
| 2104 | pub fn addExternSymbol(self: *MachO, name: []const u8) !u32 { | 2058 | pub 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 trie | 2747 | // 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 | } |
| 3066 | | 3020 | |
| 3067 | fn writeStringTable(self: *MachO) !void { | 3021 | fn writeStringTable(self: *MachO) !void { |
| 3068 | if (!self.string_table_dirty) return; | 3022 | if (!self.strtab_dirty) return; |
| 3069 | | 3023 | |
| 3070 | const tracy = trace(@src()); | 3024 | const tracy = trace(@src()); |
| 3071 | defer tracy.end(); | 3025 | defer tracy.end(); |
| 3072 | | 3026 | |
| 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)); |
| 3076 | | 3030 | |
| 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 }); |
| 3084 | | 3038 | |
| 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 | } |
| 3089 | | 3043 | |
| 3090 | fn updateLinkeditSegmentSizes(self: *MachO) !void { | 3044 | fn updateLinkeditSegmentSizes(self: *MachO) !void { |