| ... | @@ -120,6 +120,7 @@ stub_helper_stubs_start_off: ?u64 = null, | ... | @@ -120,6 +120,7 @@ stub_helper_stubs_start_off: ?u64 = null, |
| 120 | | 120 | |
| 121 | /// Table of symbol names aka the string table. | 121 | /// Table of symbol names aka the string table. |
| 122 | string_table: std.ArrayListUnmanaged(u8) = .{}, | 122 | string_table: std.ArrayListUnmanaged(u8) = .{}, |
| | 123 | string_table_directory: std.StringHashMapUnmanaged(u32) = .{}, |
| 123 | | 124 | |
| 124 | /// Table of trampolines to the actual symbols in __text section. | 125 | /// Table of trampolines to the actual symbols in __text section. |
| 125 | offset_table: std.ArrayListUnmanaged(u64) = .{}, | 126 | offset_table: std.ArrayListUnmanaged(u64) = .{}, |
| ... | @@ -142,11 +143,11 @@ string_table_needs_relocation: bool = false, | ... | @@ -142,11 +143,11 @@ string_table_needs_relocation: bool = false, |
| 142 | /// or removed from the freelist. | 143 | /// or removed from the freelist. |
| 143 | /// | 144 | /// |
| 144 | /// A text block has surplus capacity when its overcapacity value is greater than | 145 | /// A text block has surplus capacity when its overcapacity value is greater than |
| 145 | /// minimum_text_block_size * alloc_num / alloc_den. That is, when it has so | 146 | /// padToIdeal(minimum_text_block_size). That is, when it has so |
| 146 | /// much extra capacity, that we could fit a small new symbol in it, itself with | 147 | /// much extra capacity, that we could fit a small new symbol in it, itself with |
| 147 | /// ideal_capacity or more. | 148 | /// ideal_capacity or more. |
| 148 | /// | 149 | /// |
| 149 | /// Ideal capacity is defined by size * alloc_num / alloc_den. | 150 | /// Ideal capacity is defined by size + (size / ideal_factor). |
| 150 | /// | 151 | /// |
| 151 | /// Overcapacity is measured by actual_capacity - ideal_capacity. Note that | 152 | /// Overcapacity is measured by actual_capacity - ideal_capacity. Note that |
| 152 | /// overcapacity can be negative. A simple way to have negative overcapacity is to | 153 | /// overcapacity can be negative. A simple way to have negative overcapacity is to |
| ... | @@ -191,9 +192,9 @@ pub const StubFixup = struct { | ... | @@ -191,9 +192,9 @@ pub const StubFixup = struct { |
| 191 | len: usize, | 192 | len: usize, |
| 192 | }; | 193 | }; |
| 193 | | 194 | |
| 194 | /// `alloc_num / alloc_den` is the factor of padding when allocating. | 195 | /// When allocating, the ideal_capacity is calculated by |
| 195 | pub const alloc_num = 4; | 196 | /// actual_capacity + (actual_capacity / ideal_factor) |
| 196 | pub const alloc_den = 3; | 197 | const ideal_factor = 2; |
| 197 | | 198 | |
| 198 | /// Default path to dyld | 199 | /// Default path to dyld |
| 199 | /// TODO instead of hardcoding it, we should probably look through some env vars and search paths | 200 | /// TODO instead of hardcoding it, we should probably look through some env vars and search paths |
| ... | @@ -213,7 +214,7 @@ const LIB_SYSTEM_PATH: [*:0]const u8 = DEFAULT_LIB_SEARCH_PATH ++ "/libSystem.B. | ... | @@ -213,7 +214,7 @@ const LIB_SYSTEM_PATH: [*:0]const u8 = DEFAULT_LIB_SEARCH_PATH ++ "/libSystem.B. |
| 213 | /// it as a possible place to put new symbols, it must have enough room for this many bytes | 214 | /// it as a possible place to put new symbols, it must have enough room for this many bytes |
| 214 | /// (plus extra for reserved capacity). | 215 | /// (plus extra for reserved capacity). |
| 215 | const minimum_text_block_size = 64; | 216 | const minimum_text_block_size = 64; |
| 216 | const min_text_capacity = minimum_text_block_size * alloc_num / alloc_den; | 217 | const min_text_capacity = padToIdeal(minimum_text_block_size); |
| 217 | | 218 | |
| 218 | pub const TextBlock = struct { | 219 | pub const TextBlock = struct { |
| 219 | /// Each decl always gets a local symbol with the fully qualified name. | 220 | /// Each decl always gets a local symbol with the fully qualified name. |
| ... | @@ -276,7 +277,7 @@ pub const TextBlock = struct { | ... | @@ -276,7 +277,7 @@ pub const TextBlock = struct { |
| 276 | const self_sym = macho_file.local_symbols.items[self.local_sym_index]; | 277 | const self_sym = macho_file.local_symbols.items[self.local_sym_index]; |
| 277 | const next_sym = macho_file.local_symbols.items[next.local_sym_index]; | 278 | const next_sym = macho_file.local_symbols.items[next.local_sym_index]; |
| 278 | const cap = next_sym.n_value - self_sym.n_value; | 279 | const cap = next_sym.n_value - self_sym.n_value; |
| 279 | const ideal_cap = self.size * alloc_num / alloc_den; | 280 | const ideal_cap = padToIdeal(self.size); |
| 280 | if (cap <= ideal_cap) return false; | 281 | if (cap <= ideal_cap) return false; |
| 281 | const surplus = cap - ideal_cap; | 282 | const surplus = cap - ideal_cap; |
| 282 | return surplus >= min_text_capacity; | 283 | return surplus >= min_text_capacity; |
| ... | @@ -872,7 +873,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { | ... | @@ -872,7 +873,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 872 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; | 873 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 873 | const text_section = text_segment.sections.items[self.text_section_index.?]; | 874 | const text_section = text_segment.sections.items[self.text_section_index.?]; |
| 874 | const after_last_cmd_offset = self.header.?.sizeofcmds + @sizeOf(macho.mach_header_64); | 875 | const after_last_cmd_offset = self.header.?.sizeofcmds + @sizeOf(macho.mach_header_64); |
| 875 | const needed_size = @sizeOf(macho.linkedit_data_command) * alloc_num / alloc_den; | 876 | const needed_size = padToIdeal(@sizeOf(macho.linkedit_data_command)); |
| 876 | | 877 | |
| 877 | if (needed_size + after_last_cmd_offset > text_section.offset) { | 878 | if (needed_size + after_last_cmd_offset > text_section.offset) { |
| 878 | log.err("Unable to extend padding between the end of load commands and start of __text section.", .{}); | 879 | log.err("Unable to extend padding between the end of load commands and start of __text section.", .{}); |
| ... | @@ -942,7 +943,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { | ... | @@ -942,7 +943,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 942 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; | 943 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 943 | const text_section = text_segment.sections.items[self.text_section_index.?]; | 944 | const text_section = text_segment.sections.items[self.text_section_index.?]; |
| 944 | const after_last_cmd_offset = self.header.?.sizeofcmds + @sizeOf(macho.mach_header_64); | 945 | const after_last_cmd_offset = self.header.?.sizeofcmds + @sizeOf(macho.mach_header_64); |
| 945 | const needed_size = @sizeOf(macho.linkedit_data_command) * alloc_num / alloc_den; | 946 | const needed_size = padToIdeal(@sizeOf(macho.linkedit_data_command)); |
| 946 | | 947 | |
| 947 | if (needed_size + after_last_cmd_offset > text_section.offset) { | 948 | if (needed_size + after_last_cmd_offset > text_section.offset) { |
| 948 | log.err("Unable to extend padding between the end of load commands and start of __text section.", .{}); | 949 | log.err("Unable to extend padding between the end of load commands and start of __text section.", .{}); |
| ... | @@ -1023,6 +1024,13 @@ pub fn deinit(self: *MachO) void { | ... | @@ -1023,6 +1024,13 @@ pub fn deinit(self: *MachO) void { |
| 1023 | self.text_block_free_list.deinit(self.base.allocator); | 1024 | self.text_block_free_list.deinit(self.base.allocator); |
| 1024 | self.offset_table.deinit(self.base.allocator); | 1025 | self.offset_table.deinit(self.base.allocator); |
| 1025 | self.offset_table_free_list.deinit(self.base.allocator); | 1026 | self.offset_table_free_list.deinit(self.base.allocator); |
| | 1027 | { |
| | 1028 | var it = self.string_table_directory.iterator(); |
| | 1029 | while (it.next()) |entry| { |
| | 1030 | self.base.allocator.free(entry.key); |
| | 1031 | } |
| | 1032 | } |
| | 1033 | self.string_table_directory.deinit(self.base.allocator); |
| 1026 | self.string_table.deinit(self.base.allocator); | 1034 | self.string_table.deinit(self.base.allocator); |
| 1027 | self.global_symbols.deinit(self.base.allocator); | 1035 | self.global_symbols.deinit(self.base.allocator); |
| 1028 | self.global_symbol_free_list.deinit(self.base.allocator); | 1036 | self.global_symbol_free_list.deinit(self.base.allocator); |
| ... | @@ -1229,14 +1237,16 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { | ... | @@ -1229,14 +1237,16 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 1229 | const this_addr = symbol.n_value + fixup.start; | 1237 | const this_addr = symbol.n_value + fixup.start; |
| 1230 | switch (self.base.options.target.cpu.arch) { | 1238 | switch (self.base.options.target.cpu.arch) { |
| 1231 | .x86_64 => { | 1239 | .x86_64 => { |
| 1232 | const displacement = @intCast(u32, target_addr - this_addr - fixup.len); | 1240 | assert(target_addr >= this_addr + fixup.len); |
| | 1241 | const displacement = try math.cast(u32, target_addr - this_addr - fixup.len); |
| 1233 | var placeholder = code_buffer.items[fixup.start + fixup.len - @sizeOf(u32) ..][0..@sizeOf(u32)]; | 1242 | var placeholder = code_buffer.items[fixup.start + fixup.len - @sizeOf(u32) ..][0..@sizeOf(u32)]; |
| 1234 | mem.writeIntSliceLittle(u32, placeholder, displacement); | 1243 | mem.writeIntSliceLittle(u32, placeholder, displacement); |
| 1235 | }, | 1244 | }, |
| 1236 | .aarch64 => { | 1245 | .aarch64 => { |
| 1237 | const displacement = @intCast(u27, target_addr - this_addr); | 1246 | assert(target_addr >= this_addr); |
| | 1247 | const displacement = try math.cast(u27, target_addr - this_addr); |
| 1238 | var placeholder = code_buffer.items[fixup.start..][0..fixup.len]; | 1248 | var placeholder = code_buffer.items[fixup.start..][0..fixup.len]; |
| 1239 | mem.writeIntSliceLittle(u32, placeholder, aarch64.Instruction.b(@intCast(i28, displacement)).toU32()); | 1249 | mem.writeIntSliceLittle(u32, placeholder, aarch64.Instruction.b(@as(i28, displacement)).toU32()); |
| 1240 | }, | 1250 | }, |
| 1241 | else => unreachable, // unsupported target architecture | 1251 | else => unreachable, // unsupported target architecture |
| 1242 | } | 1252 | } |
| ... | @@ -1249,14 +1259,16 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { | ... | @@ -1249,14 +1259,16 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 1249 | const text_addr = symbol.n_value + fixup.start; | 1259 | const text_addr = symbol.n_value + fixup.start; |
| 1250 | switch (self.base.options.target.cpu.arch) { | 1260 | switch (self.base.options.target.cpu.arch) { |
| 1251 | .x86_64 => { | 1261 | .x86_64 => { |
| 1252 | const displacement = @intCast(u32, stub_addr - text_addr - fixup.len); | 1262 | assert(stub_addr >= text_addr + fixup.len); |
| | 1263 | const displacement = try math.cast(u32, stub_addr - text_addr - fixup.len); |
| 1253 | var placeholder = code_buffer.items[fixup.start + fixup.len - @sizeOf(u32) ..][0..@sizeOf(u32)]; | 1264 | var placeholder = code_buffer.items[fixup.start + fixup.len - @sizeOf(u32) ..][0..@sizeOf(u32)]; |
| 1254 | mem.writeIntSliceLittle(u32, placeholder, displacement); | 1265 | mem.writeIntSliceLittle(u32, placeholder, displacement); |
| 1255 | }, | 1266 | }, |
| 1256 | .aarch64 => { | 1267 | .aarch64 => { |
| 1257 | const displacement = @intCast(u32, stub_addr - text_addr); | 1268 | assert(stub_addr >= text_addr); |
| | 1269 | const displacement = try math.cast(i28, stub_addr - text_addr); |
| 1258 | var placeholder = code_buffer.items[fixup.start..][0..fixup.len]; | 1270 | var placeholder = code_buffer.items[fixup.start..][0..fixup.len]; |
| 1259 | mem.writeIntSliceLittle(u32, placeholder, aarch64.Instruction.bl(@intCast(i28, displacement)).toU32()); | 1271 | mem.writeIntSliceLittle(u32, placeholder, aarch64.Instruction.bl(displacement).toU32()); |
| 1260 | }, | 1272 | }, |
| 1261 | else => unreachable, // unsupported target architecture | 1273 | else => unreachable, // unsupported target architecture |
| 1262 | } | 1274 | } |
| ... | @@ -1479,7 +1491,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -1479,7 +1491,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1479 | const program_code_size_hint = self.base.options.program_code_size_hint; | 1491 | const program_code_size_hint = self.base.options.program_code_size_hint; |
| 1480 | const offset_table_size_hint = @sizeOf(u64) * self.base.options.symbol_count_hint; | 1492 | const offset_table_size_hint = @sizeOf(u64) * self.base.options.symbol_count_hint; |
| 1481 | const ideal_size = self.header_pad + program_code_size_hint + 3 * offset_table_size_hint; | 1493 | const ideal_size = self.header_pad + program_code_size_hint + 3 * offset_table_size_hint; |
| 1482 | const needed_size = mem.alignForwardGeneric(u64, satMul(ideal_size, alloc_num) / alloc_den, self.page_size); | 1494 | const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size); |
| 1483 | | 1495 | |
| 1484 | log.debug("found __TEXT segment free space 0x{x} to 0x{x}", .{ 0, needed_size }); | 1496 | log.debug("found __TEXT segment free space 0x{x} to 0x{x}", .{ 0, needed_size }); |
| 1485 | | 1497 | |
| ... | @@ -1644,7 +1656,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -1644,7 +1656,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1644 | const address_and_offset = self.nextSegmentAddressAndOffset(); | 1656 | const address_and_offset = self.nextSegmentAddressAndOffset(); |
| 1645 | | 1657 | |
| 1646 | const ideal_size = @sizeOf(u64) * self.base.options.symbol_count_hint; | 1658 | const ideal_size = @sizeOf(u64) * self.base.options.symbol_count_hint; |
| 1647 | const needed_size = mem.alignForwardGeneric(u64, satMul(ideal_size, alloc_num) / alloc_den, self.page_size); | 1659 | const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size); |
| 1648 | | 1660 | |
| 1649 | log.debug("found __DATA_CONST segment free space 0x{x} to 0x{x}", .{ address_and_offset.offset, address_and_offset.offset + needed_size }); | 1661 | log.debug("found __DATA_CONST segment free space 0x{x} to 0x{x}", .{ address_and_offset.offset, address_and_offset.offset + needed_size }); |
| 1650 | | 1662 | |
| ... | @@ -1701,7 +1713,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -1701,7 +1713,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1701 | const address_and_offset = self.nextSegmentAddressAndOffset(); | 1713 | const address_and_offset = self.nextSegmentAddressAndOffset(); |
| 1702 | | 1714 | |
| 1703 | const ideal_size = 2 * @sizeOf(u64) * self.base.options.symbol_count_hint; | 1715 | const ideal_size = 2 * @sizeOf(u64) * self.base.options.symbol_count_hint; |
| 1704 | const needed_size = mem.alignForwardGeneric(u64, satMul(ideal_size, alloc_num) / alloc_den, self.page_size); | 1716 | const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size); |
| 1705 | | 1717 | |
| 1706 | log.debug("found __DATA segment free space 0x{x} to 0x{x}", .{ address_and_offset.offset, address_and_offset.offset + needed_size }); | 1718 | log.debug("found __DATA segment free space 0x{x} to 0x{x}", .{ address_and_offset.offset, address_and_offset.offset + needed_size }); |
| 1707 | | 1719 | |
| ... | @@ -2074,7 +2086,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -2074,7 +2086,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 2074 | code[1] = 0x8d; | 2086 | code[1] = 0x8d; |
| 2075 | code[2] = 0x1d; | 2087 | code[2] = 0x1d; |
| 2076 | { | 2088 | { |
| 2077 | const displacement = @intCast(u32, data.addr - stub_helper.addr - 7); | 2089 | const displacement = try math.cast(u32, data.addr - stub_helper.addr - 7); |
| 2078 | mem.writeIntLittle(u32, code[3..7], displacement); | 2090 | mem.writeIntLittle(u32, code[3..7], displacement); |
| 2079 | } | 2091 | } |
| 2080 | // push %r11 | 2092 | // push %r11 |
| ... | @@ -2084,7 +2096,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -2084,7 +2096,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 2084 | code[9] = 0xff; | 2096 | code[9] = 0xff; |
| 2085 | code[10] = 0x25; | 2097 | code[10] = 0x25; |
| 2086 | { | 2098 | { |
| 2087 | const displacement = @intCast(u32, got.addr - stub_helper.addr - code_size); | 2099 | const displacement = try math.cast(u32, got.addr - stub_helper.addr - code_size); |
| 2088 | mem.writeIntLittle(u32, code[11..], displacement); | 2100 | mem.writeIntLittle(u32, code[11..], displacement); |
| 2089 | } | 2101 | } |
| 2090 | self.stub_helper_stubs_start_off = stub_helper.offset + code_size; | 2102 | self.stub_helper_stubs_start_off = stub_helper.offset + code_size; |
| ... | @@ -2093,8 +2105,8 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -2093,8 +2105,8 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 2093 | .aarch64 => { | 2105 | .aarch64 => { |
| 2094 | var code: [4 * @sizeOf(u32)]u8 = undefined; | 2106 | var code: [4 * @sizeOf(u32)]u8 = undefined; |
| 2095 | { | 2107 | { |
| 2096 | const displacement = data.addr - stub_helper.addr; | 2108 | const displacement = try math.cast(i21, data.addr - stub_helper.addr); |
| 2097 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x17, @intCast(i21, displacement)).toU32()); | 2109 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x17, displacement).toU32()); |
| 2098 | } | 2110 | } |
| 2099 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.stp( | 2111 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.stp( |
| 2100 | .x16, | 2112 | .x16, |
| ... | @@ -2103,9 +2115,10 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -2103,9 +2115,10 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 2103 | aarch64.Instruction.LoadStorePairOffset.pre_index(-16), | 2115 | aarch64.Instruction.LoadStorePairOffset.pre_index(-16), |
| 2104 | ).toU32()); | 2116 | ).toU32()); |
| 2105 | { | 2117 | { |
| 2106 | const displacement = got.addr - stub_helper.addr - 2 * @sizeOf(u32); | 2118 | const displacement = try math.divExact(u64, got.addr - stub_helper.addr - 2 * @sizeOf(u32), 4); |
| | 2119 | const literal = try math.cast(u19, displacement); |
| 2107 | mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.ldr(.x16, .{ | 2120 | mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.ldr(.x16, .{ |
| 2108 | .literal = @intCast(u19, displacement / 4), | 2121 | .literal = literal, |
| 2109 | }).toU32()); | 2122 | }).toU32()); |
| 2110 | } | 2123 | } |
| 2111 | mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.br(.x16).toU32()); | 2124 | mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.br(.x16).toU32()); |
| ... | @@ -2120,7 +2133,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -2120,7 +2133,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 2120 | fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 { | 2133 | fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 { |
| 2121 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; | 2134 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 2122 | const text_section = &text_segment.sections.items[self.text_section_index.?]; | 2135 | const text_section = &text_segment.sections.items[self.text_section_index.?]; |
| 2123 | const new_block_ideal_capacity = new_block_size * alloc_num / alloc_den; | 2136 | const new_block_ideal_capacity = padToIdeal(new_block_size); |
| 2124 | | 2137 | |
| 2125 | // We use these to indicate our intention to update metadata, placing the new block, | 2138 | // We use these to indicate our intention to update metadata, placing the new block, |
| 2126 | // and possibly removing a free list node. | 2139 | // and possibly removing a free list node. |
| ... | @@ -2140,7 +2153,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, | ... | @@ -2140,7 +2153,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, |
| 2140 | // Is it enough that we could fit this new text block? | 2153 | // Is it enough that we could fit this new text block? |
| 2141 | const sym = self.local_symbols.items[big_block.local_sym_index]; | 2154 | const sym = self.local_symbols.items[big_block.local_sym_index]; |
| 2142 | const capacity = big_block.capacity(self.*); | 2155 | const capacity = big_block.capacity(self.*); |
| 2143 | const ideal_capacity = capacity * alloc_num / alloc_den; | 2156 | const ideal_capacity = padToIdeal(capacity); |
| 2144 | const ideal_capacity_end_vaddr = sym.n_value + ideal_capacity; | 2157 | const ideal_capacity_end_vaddr = sym.n_value + ideal_capacity; |
| 2145 | const capacity_end_vaddr = sym.n_value + capacity; | 2158 | const capacity_end_vaddr = sym.n_value + capacity; |
| 2146 | const new_start_vaddr_unaligned = capacity_end_vaddr - new_block_ideal_capacity; | 2159 | const new_start_vaddr_unaligned = capacity_end_vaddr - new_block_ideal_capacity; |
| ... | @@ -2172,7 +2185,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, | ... | @@ -2172,7 +2185,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, |
| 2172 | const last_symbol = self.local_symbols.items[last.local_sym_index]; | 2185 | const last_symbol = self.local_symbols.items[last.local_sym_index]; |
| 2173 | // TODO We should pad out the excess capacity with NOPs. For executables, | 2186 | // TODO We should pad out the excess capacity with NOPs. For executables, |
| 2174 | // no padding seems to be OK, but it will probably not be for objects. | 2187 | // no padding seems to be OK, but it will probably not be for objects. |
| 2175 | const ideal_capacity = last.size * alloc_num / alloc_den; | 2188 | const ideal_capacity = padToIdeal(last.size); |
| 2176 | const ideal_capacity_end_vaddr = last_symbol.n_value + ideal_capacity; | 2189 | const ideal_capacity_end_vaddr = last_symbol.n_value + ideal_capacity; |
| 2177 | const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment); | 2190 | const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment); |
| 2178 | block_placement = last; | 2191 | block_placement = last; |
| ... | @@ -2230,14 +2243,26 @@ pub fn makeStaticString(comptime bytes: []const u8) [16]u8 { | ... | @@ -2230,14 +2243,26 @@ pub fn makeStaticString(comptime bytes: []const u8) [16]u8 { |
| 2230 | } | 2243 | } |
| 2231 | | 2244 | |
| 2232 | fn makeString(self: *MachO, bytes: []const u8) !u32 { | 2245 | fn makeString(self: *MachO, bytes: []const u8) !u32 { |
| | 2246 | if (self.string_table_directory.get(bytes)) |offset| { |
| | 2247 | log.debug("reusing '{s}' from string table at offset 0x{x}", .{ bytes, offset }); |
| | 2248 | return offset; |
| | 2249 | } |
| | 2250 | |
| 2233 | try self.string_table.ensureCapacity(self.base.allocator, self.string_table.items.len + bytes.len + 1); | 2251 | try self.string_table.ensureCapacity(self.base.allocator, self.string_table.items.len + bytes.len + 1); |
| 2234 | const offset = @intCast(u32, self.string_table.items.len); | 2252 | const offset = @intCast(u32, self.string_table.items.len); |
| 2235 | log.debug("writing '{s}' into the string table at offset 0x{x}", .{ bytes, offset }); | 2253 | log.debug("writing new string '{s}' into string table at offset 0x{x}", .{ bytes, offset }); |
| 2236 | self.string_table.appendSliceAssumeCapacity(bytes); | 2254 | self.string_table.appendSliceAssumeCapacity(bytes); |
| 2237 | self.string_table.appendAssumeCapacity(0); | 2255 | self.string_table.appendAssumeCapacity(0); |
| | 2256 | try self.string_table_directory.putNoClobber( |
| | 2257 | self.base.allocator, |
| | 2258 | try self.base.allocator.dupe(u8, bytes), |
| | 2259 | offset, |
| | 2260 | ); |
| | 2261 | |
| 2238 | self.string_table_dirty = true; | 2262 | self.string_table_dirty = true; |
| 2239 | if (self.d_sym) |*ds| | 2263 | if (self.d_sym) |*ds| |
| 2240 | ds.string_table_dirty = true; | 2264 | ds.string_table_dirty = true; |
| | 2265 | |
| 2241 | return offset; | 2266 | return offset; |
| 2242 | } | 2267 | } |
| 2243 | | 2268 | |
| ... | @@ -2335,7 +2360,7 @@ fn allocatedSizeLinkedit(self: *MachO, start: u64) u64 { | ... | @@ -2335,7 +2360,7 @@ fn allocatedSizeLinkedit(self: *MachO, start: u64) u64 { |
| 2335 | } | 2360 | } |
| 2336 | | 2361 | |
| 2337 | inline fn checkForCollision(start: u64, end: u64, off: u64, size: u64) ?u64 { | 2362 | inline fn checkForCollision(start: u64, end: u64, off: u64, size: u64) ?u64 { |
| 2338 | const increased_size = satMul(size, alloc_num) / alloc_den; | 2363 | const increased_size = padToIdeal(size); |
| 2339 | const test_end = off + increased_size; | 2364 | const test_end = off + increased_size; |
| 2340 | if (end > off and start < test_end) { | 2365 | if (end > off and start < test_end) { |
| 2341 | return test_end; | 2366 | return test_end; |
| ... | @@ -2344,7 +2369,7 @@ inline fn checkForCollision(start: u64, end: u64, off: u64, size: u64) ?u64 { | ... | @@ -2344,7 +2369,7 @@ inline fn checkForCollision(start: u64, end: u64, off: u64, size: u64) ?u64 { |
| 2344 | } | 2369 | } |
| 2345 | | 2370 | |
| 2346 | fn detectAllocCollisionLinkedit(self: *MachO, start: u64, size: u64) ?u64 { | 2371 | fn detectAllocCollisionLinkedit(self: *MachO, start: u64, size: u64) ?u64 { |
| 2347 | const end = start + satMul(size, alloc_num) / alloc_den; | 2372 | const end = start + padToIdeal(size); |
| 2348 | | 2373 | |
| 2349 | // __LINKEDIT is a weird segment where sections get their own load commands so we | 2374 | // __LINKEDIT is a weird segment where sections get their own load commands so we |
| 2350 | // special-case it. | 2375 | // special-case it. |
| ... | @@ -2425,12 +2450,6 @@ fn findFreeSpaceLinkedit(self: *MachO, object_size: u64, min_alignment: u16, sta | ... | @@ -2425,12 +2450,6 @@ fn findFreeSpaceLinkedit(self: *MachO, object_size: u64, min_alignment: u16, sta |
| 2425 | return st; | 2450 | return st; |
| 2426 | } | 2451 | } |
| 2427 | | 2452 | |
| 2428 | /// Saturating multiplication | | |
| 2429 | pub fn satMul(a: anytype, b: anytype) @TypeOf(a, b) { | | |
| 2430 | const T = @TypeOf(a, b); | | |
| 2431 | return std.math.mul(T, a, b) catch std.math.maxInt(T); | | |
| 2432 | } | | |
| 2433 | | | |
| 2434 | fn writeOffsetTableEntry(self: *MachO, index: usize) !void { | 2453 | fn writeOffsetTableEntry(self: *MachO, index: usize) !void { |
| 2435 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; | 2454 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 2436 | const sect = &text_segment.sections.items[self.got_section_index.?]; | 2455 | const sect = &text_segment.sections.items[self.got_section_index.?]; |
| ... | @@ -2445,8 +2464,8 @@ fn writeOffsetTableEntry(self: *MachO, index: usize) !void { | ... | @@ -2445,8 +2464,8 @@ fn writeOffsetTableEntry(self: *MachO, index: usize) !void { |
| 2445 | var code: [8]u8 = undefined; | 2464 | var code: [8]u8 = undefined; |
| 2446 | switch (self.base.options.target.cpu.arch) { | 2465 | switch (self.base.options.target.cpu.arch) { |
| 2447 | .x86_64 => { | 2466 | .x86_64 => { |
| 2448 | const pos_symbol_off = @intCast(u31, vmaddr - self.offset_table.items[index] + 7); | 2467 | const pos_symbol_off = try math.cast(u31, vmaddr - self.offset_table.items[index] + 7); |
| 2449 | const symbol_off = @bitCast(u32, @intCast(i32, pos_symbol_off) * -1); | 2468 | const symbol_off = @bitCast(u32, @as(i32, pos_symbol_off) * -1); |
| 2450 | // lea %rax, [rip - disp] | 2469 | // lea %rax, [rip - disp] |
| 2451 | code[0] = 0x48; | 2470 | code[0] = 0x48; |
| 2452 | code[1] = 0x8D; | 2471 | code[1] = 0x8D; |
| ... | @@ -2456,8 +2475,8 @@ fn writeOffsetTableEntry(self: *MachO, index: usize) !void { | ... | @@ -2456,8 +2475,8 @@ fn writeOffsetTableEntry(self: *MachO, index: usize) !void { |
| 2456 | code[7] = 0xC3; | 2475 | code[7] = 0xC3; |
| 2457 | }, | 2476 | }, |
| 2458 | .aarch64 => { | 2477 | .aarch64 => { |
| 2459 | const pos_symbol_off = @intCast(u20, vmaddr - self.offset_table.items[index]); | 2478 | const pos_symbol_off = try math.cast(u20, vmaddr - self.offset_table.items[index]); |
| 2460 | const symbol_off = @intCast(i21, pos_symbol_off) * -1; | 2479 | const symbol_off = @as(i21, pos_symbol_off) * -1; |
| 2461 | // adr x0, #-disp | 2480 | // adr x0, #-disp |
| 2462 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x0, symbol_off).toU32()); | 2481 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x0, symbol_off).toU32()); |
| 2463 | // ret x28 | 2482 | // ret x28 |
| ... | @@ -2503,16 +2522,19 @@ fn writeStub(self: *MachO, index: u32) !void { | ... | @@ -2503,16 +2522,19 @@ fn writeStub(self: *MachO, index: u32) !void { |
| 2503 | defer self.base.allocator.free(code); | 2522 | defer self.base.allocator.free(code); |
| 2504 | switch (self.base.options.target.cpu.arch) { | 2523 | switch (self.base.options.target.cpu.arch) { |
| 2505 | .x86_64 => { | 2524 | .x86_64 => { |
| 2506 | const displacement = @intCast(u32, la_ptr_addr - stub_addr - stubs.reserved2); | 2525 | assert(la_ptr_addr >= stub_addr + stubs.reserved2); |
| | 2526 | const displacement = try math.cast(u32, la_ptr_addr - stub_addr - stubs.reserved2); |
| 2507 | // jmp | 2527 | // jmp |
| 2508 | code[0] = 0xff; | 2528 | code[0] = 0xff; |
| 2509 | code[1] = 0x25; | 2529 | code[1] = 0x25; |
| 2510 | mem.writeIntLittle(u32, code[2..][0..4], displacement); | 2530 | mem.writeIntLittle(u32, code[2..][0..4], displacement); |
| 2511 | }, | 2531 | }, |
| 2512 | .aarch64 => { | 2532 | .aarch64 => { |
| 2513 | const displacement = la_ptr_addr - stub_addr; | 2533 | assert(la_ptr_addr >= stub_addr); |
| | 2534 | const displacement = try math.divExact(u64, la_ptr_addr - stub_addr, 4); |
| | 2535 | const literal = try math.cast(u19, displacement); |
| 2514 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.x16, .{ | 2536 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.x16, .{ |
| 2515 | .literal = @intCast(u19, displacement / 4), | 2537 | .literal = literal, |
| 2516 | }).toU32()); | 2538 | }).toU32()); |
| 2517 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.br(.x16).toU32()); | 2539 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.br(.x16).toU32()); |
| 2518 | }, | 2540 | }, |
| ... | @@ -2535,7 +2557,10 @@ fn writeStubInStubHelper(self: *MachO, index: u32) !void { | ... | @@ -2535,7 +2557,10 @@ fn writeStubInStubHelper(self: *MachO, index: u32) !void { |
| 2535 | defer self.base.allocator.free(code); | 2557 | defer self.base.allocator.free(code); |
| 2536 | switch (self.base.options.target.cpu.arch) { | 2558 | switch (self.base.options.target.cpu.arch) { |
| 2537 | .x86_64 => { | 2559 | .x86_64 => { |
| 2538 | const displacement = @intCast(i32, @intCast(i64, stub_helper.offset) - @intCast(i64, stub_off) - stub_size); | 2560 | const displacement = try math.cast( |
| | 2561 | i32, |
| | 2562 | @intCast(i64, stub_helper.offset) - @intCast(i64, stub_off) - stub_size, |
| | 2563 | ); |
| 2539 | // pushq | 2564 | // pushq |
| 2540 | code[0] = 0x68; | 2565 | code[0] = 0x68; |
| 2541 | mem.writeIntLittle(u32, code[1..][0..4], 0x0); // Just a placeholder populated in `populateLazyBindOffsetsInStubHelper`. | 2566 | mem.writeIntLittle(u32, code[1..][0..4], 0x0); // Just a placeholder populated in `populateLazyBindOffsetsInStubHelper`. |
| ... | @@ -2544,11 +2569,11 @@ fn writeStubInStubHelper(self: *MachO, index: u32) !void { | ... | @@ -2544,11 +2569,11 @@ fn writeStubInStubHelper(self: *MachO, index: u32) !void { |
| 2544 | mem.writeIntLittle(u32, code[6..][0..4], @bitCast(u32, displacement)); | 2569 | mem.writeIntLittle(u32, code[6..][0..4], @bitCast(u32, displacement)); |
| 2545 | }, | 2570 | }, |
| 2546 | .aarch64 => { | 2571 | .aarch64 => { |
| 2547 | const displacement = @intCast(i64, stub_helper.offset) - @intCast(i64, stub_off) - 4; | 2572 | const displacement = try math.cast(i28, @intCast(i64, stub_helper.offset) - @intCast(i64, stub_off) - 4); |
| 2548 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.w16, .{ | 2573 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.w16, .{ |
| 2549 | .literal = 0x2, | 2574 | .literal = @divExact(stub_size - @sizeOf(u32), 4), |
| 2550 | }).toU32()); | 2575 | }).toU32()); |
| 2551 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.b(@intCast(i28, displacement)).toU32()); | 2576 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.b(displacement).toU32()); |
| 2552 | mem.writeIntLittle(u32, code[8..12], 0x0); // Just a placeholder populated in `populateLazyBindOffsetsInStubHelper`. | 2577 | mem.writeIntLittle(u32, code[8..12], 0x0); // Just a placeholder populated in `populateLazyBindOffsetsInStubHelper`. |
| 2553 | }, | 2578 | }, |
| 2554 | else => unreachable, | 2579 | else => unreachable, |
| ... | @@ -3239,3 +3264,9 @@ fn fixupInfoCommon(self: *MachO, buffer: []u8, dylib_ordinal: u32) !void { | ... | @@ -3239,3 +3264,9 @@ fn fixupInfoCommon(self: *MachO, buffer: []u8, dylib_ordinal: u32) !void { |
| 3239 | } | 3264 | } |
| 3240 | } | 3265 | } |
| 3241 | } | 3266 | } |
| | 3267 | |
| | 3268 | pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) { |
| | 3269 | // TODO https://github.com/ziglang/zig/issues/1284 |
| | 3270 | return std.math.add(@TypeOf(actual_size), actual_size, actual_size / ideal_factor) catch |
| | 3271 | std.math.maxInt(@TypeOf(actual_size)); |
| | 3272 | } |