| ... | ... | @@ -26,6 +26,7 @@ const target_util = @import("../target.zig"); |
| 26 | 26 | const DebugSymbols = @import("MachO/DebugSymbols.zig"); |
| 27 | 27 | const Trie = @import("MachO/Trie.zig"); |
| 28 | 28 | const CodeSignature = @import("MachO/CodeSignature.zig"); |
| 29 | const StringTable = @import("MachO/StringTable.zig"); |
| 29 | 30 | const Zld = @import("MachO/Zld.zig"); |
| 30 | 31 | |
| 31 | 32 | usingnamespace @import("MachO/commands.zig"); |
| ... | ... | @@ -116,9 +117,7 @@ offset_table_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| 116 | 117 | |
| 117 | 118 | stub_helper_stubs_start_off: ?u64 = null, |
| 118 | 119 | |
| 119 | | /// Table of symbol names aka the string table. |
| 120 | | string_table: std.ArrayListUnmanaged(u8) = .{}, |
| 121 | | string_table_directory: std.StringHashMapUnmanaged(u32) = .{}, |
| 120 | strtab: StringTable = undefined, |
| 122 | 121 | |
| 123 | 122 | /// Table of GOT entries. |
| 124 | 123 | offset_table: std.ArrayListUnmanaged(GOTEntry) = .{}, |
| ... | ... | @@ -131,9 +130,9 @@ rebase_info_dirty: bool = false, |
| 131 | 130 | binding_info_dirty: bool = false, |
| 132 | 131 | lazy_binding_info_dirty: bool = false, |
| 133 | 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 | 137 | /// A list of text blocks that have surplus capacity. This list can have false |
| 139 | 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 | 412 | |
| 414 | 413 | pub fn createEmpty(gpa: *Allocator, options: link.Options) !*MachO { |
| 415 | 414 | const self = try gpa.create(MachO); |
| 415 | |
| 416 | 416 | self.* = .{ |
| 417 | 417 | .base = .{ |
| 418 | 418 | .tag = .macho, |
| ... | ... | @@ -421,7 +421,9 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*MachO { |
| 421 | 421 | .file = null, |
| 422 | 422 | }, |
| 423 | 423 | .page_size = if (options.target.cpu.arch == .aarch64) 0x4000 else 0x1000, |
| 424 | .strtab = try StringTable.init(gpa), |
| 424 | 425 | }; |
| 426 | |
| 425 | 427 | return self; |
| 426 | 428 | } |
| 427 | 429 | |
| ... | ... | @@ -499,8 +501,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { |
| 499 | 501 | assert(!self.binding_info_dirty); |
| 500 | 502 | assert(!self.lazy_binding_info_dirty); |
| 501 | 503 | assert(!self.export_info_dirty); |
| 502 | | assert(!self.string_table_dirty); |
| 503 | | assert(!self.string_table_needs_relocation); |
| 504 | assert(!self.strtab_dirty); |
| 505 | assert(!self.strtab_needs_relocation); |
| 504 | 506 | |
| 505 | 507 | if (target.cpu.arch == .aarch64) { |
| 506 | 508 | switch (output_mode) { |
| ... | ... | @@ -977,14 +979,7 @@ pub fn deinit(self: *MachO) void { |
| 977 | 979 | self.text_block_free_list.deinit(self.base.allocator); |
| 978 | 980 | self.offset_table.deinit(self.base.allocator); |
| 979 | 981 | self.offset_table_free_list.deinit(self.base.allocator); |
| 980 | | { |
| 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); |
| 982 | self.strtab.deinit(); |
| 988 | 983 | self.globals.deinit(self.base.allocator); |
| 989 | 984 | self.globals_free_list.deinit(self.base.allocator); |
| 990 | 985 | self.locals.deinit(self.base.allocator); |
| ... | ... | @@ -1202,7 +1197,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 1202 | 1197 | const new_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{mem.spanZ(decl.name)}); |
| 1203 | 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 | 1201 | symbol.n_type = macho.N_SECT; |
| 1207 | 1202 | symbol.n_sect = @intCast(u8, self.text_section_index.?) + 1; |
| 1208 | 1203 | symbol.n_desc = 0; |
| ... | ... | @@ -1214,7 +1209,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 1214 | 1209 | const decl_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{mem.spanZ(decl.name)}); |
| 1215 | 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 | 1213 | const addr = try self.allocateTextBlock(&decl.link.macho, code.len, required_alignment); |
| 1219 | 1214 | |
| 1220 | 1215 | log.debug("allocated text block for {s} at 0x{x}", .{ decl_name, addr }); |
| ... | ... | @@ -1404,14 +1399,14 @@ pub fn updateDeclExports( |
| 1404 | 1399 | if (exp.link.macho.sym_index) |i| { |
| 1405 | 1400 | const sym = &self.globals.items[i]; |
| 1406 | 1401 | sym.* = .{ |
| 1407 | | .n_strx = try self.updateString(sym.n_strx, exp_name), |
| 1402 | .n_strx = try self.strtab.getOrPut(exp_name), |
| 1408 | 1403 | .n_type = n_type, |
| 1409 | 1404 | .n_sect = @intCast(u8, self.text_section_index.?) + 1, |
| 1410 | 1405 | .n_desc = n_desc, |
| 1411 | 1406 | .n_value = decl_sym.n_value, |
| 1412 | 1407 | }; |
| 1413 | 1408 | } else { |
| 1414 | | const name_str_index = try self.makeString(exp_name); |
| 1409 | const name_str_index = try self.strtab.getOrPut(exp_name); |
| 1415 | 1410 | const i = if (self.globals_free_list.popOrNull()) |i| i else blk: { |
| 1416 | 1411 | _ = self.globals.addOneAssumeCapacity(); |
| 1417 | 1412 | self.export_info_dirty = true; |
| ... | ... | @@ -1787,15 +1782,14 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1787 | 1782 | symtab.symoff = @intCast(u32, symtab_off); |
| 1788 | 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. |
| 1791 | | const strtab_size = self.string_table.items.len; |
| 1785 | const strtab_size = self.strtab.size(); |
| 1792 | 1786 | const strtab_off = self.findFreeSpaceLinkedit(strtab_size, 1, symtab_off); |
| 1793 | 1787 | log.debug("found string table free space 0x{x} to 0x{x}", .{ strtab_off, strtab_off + strtab_size }); |
| 1794 | 1788 | symtab.stroff = @intCast(u32, strtab_off); |
| 1795 | 1789 | symtab.strsize = @intCast(u32, strtab_size); |
| 1796 | 1790 | |
| 1797 | 1791 | self.load_commands_dirty = true; |
| 1798 | | self.string_table_dirty = true; |
| 1792 | self.strtab_dirty = true; |
| 1799 | 1793 | } |
| 1800 | 1794 | if (self.dysymtab_cmd_index == null) { |
| 1801 | 1795 | self.dysymtab_cmd_index = @intCast(u16, self.load_commands.items.len); |
| ... | ... | @@ -1930,7 +1924,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1930 | 1924 | if (!self.nonlazy_imports.contains("dyld_stub_binder")) { |
| 1931 | 1925 | const index = @intCast(u32, self.nonlazy_imports.count()); |
| 1932 | 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 | 1928 | try self.nonlazy_imports.putNoClobber(self.base.allocator, name, .{ |
| 1935 | 1929 | .symbol = .{ |
| 1936 | 1930 | .n_strx = offset, |
| ... | ... | @@ -2061,49 +2055,9 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, |
| 2061 | 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 | 2058 | pub fn addExternSymbol(self: *MachO, name: []const u8) !u32 { |
| 2105 | 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 | 2061 | const sym_name = try self.base.allocator.dupe(u8, name); |
| 2108 | 2062 | const dylib_ordinal = 1; // TODO this is now hardcoded, since we only support libSystem. |
| 2109 | 2063 | try self.lazy_imports.putNoClobber(self.base.allocator, sym_name, .{ |
| ... | ... | @@ -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 | 2251 | log.debug("writing offset table entry [ 0x{x} => 0x{x} ({s}) ]", .{ off, sym.n_value, sym_name }); |
| 2298 | 2252 | try self.base.file.?.pwriteAll(mem.asBytes(&sym.n_value), off); |
| 2299 | 2253 | } |
| ... | ... | @@ -2592,7 +2546,7 @@ fn relocateSymbolTable(self: *MachO) !void { |
| 2592 | 2546 | const amt = try self.base.file.?.copyRangeAll(symtab.symoff, self.base.file.?, new_symoff, existing_size); |
| 2593 | 2547 | if (amt != existing_size) return error.InputOutput; |
| 2594 | 2548 | symtab.symoff = @intCast(u32, new_symoff); |
| 2595 | | self.string_table_needs_relocation = true; |
| 2549 | self.strtab_needs_relocation = true; |
| 2596 | 2550 | } |
| 2597 | 2551 | symtab.nsyms = @intCast(u32, nsyms); |
| 2598 | 2552 | self.load_commands_dirty = true; |
| ... | ... | @@ -2791,7 +2745,7 @@ fn writeExportTrie(self: *MachO) !void { |
| 2791 | 2745 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 2792 | 2746 | for (self.globals.items) |symbol| { |
| 2793 | 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 | 2749 | assert(symbol.n_value >= text_segment.inner.vmaddr); |
| 2796 | 2750 | try trie.put(.{ |
| 2797 | 2751 | .name = name, |
| ... | ... | @@ -3065,26 +3019,26 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void { |
| 3065 | 3019 | } |
| 3066 | 3020 | |
| 3067 | 3021 | fn writeStringTable(self: *MachO) !void { |
| 3068 | | if (!self.string_table_dirty) return; |
| 3022 | if (!self.strtab_dirty) return; |
| 3069 | 3023 | |
| 3070 | 3024 | const tracy = trace(@src()); |
| 3071 | 3025 | defer tracy.end(); |
| 3072 | 3026 | |
| 3073 | 3027 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; |
| 3074 | 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 | 3032 | symtab.strsize = 0; |
| 3079 | 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 | 3036 | symtab.strsize = @intCast(u32, needed_size); |
| 3083 | 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 | 3040 | self.load_commands_dirty = true; |
| 3087 | | self.string_table_dirty = false; |
| 3041 | self.strtab_dirty = false; |
| 3088 | 3042 | } |
| 3089 | 3043 | |
| 3090 | 3044 | fn updateLinkeditSegmentSizes(self: *MachO) !void { |