| ... | @@ -143,11 +143,11 @@ string_table_needs_relocation: bool = false, | ... | @@ -143,11 +143,11 @@ string_table_needs_relocation: bool = false, |
| 143 | /// or removed from the freelist. | 143 | /// or removed from the freelist. |
| 144 | /// | 144 | /// |
| 145 | /// 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 |
| 146 | /// 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 |
| 147 | /// 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 |
| 148 | /// ideal_capacity or more. | 148 | /// ideal_capacity or more. |
| 149 | /// | 149 | /// |
| 150 | /// Ideal capacity is defined by size * alloc_num / alloc_den. | 150 | /// Ideal capacity is defined by size + (size / ideal_factor). |
| 151 | /// | 151 | /// |
| 152 | /// Overcapacity is measured by actual_capacity - ideal_capacity. Note that | 152 | /// Overcapacity is measured by actual_capacity - ideal_capacity. Note that |
| 153 | /// 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 |
| ... | @@ -192,9 +192,9 @@ pub const StubFixup = struct { | ... | @@ -192,9 +192,9 @@ pub const StubFixup = struct { |
| 192 | len: usize, | 192 | len: usize, |
| 193 | }; | 193 | }; |
| 194 | | 194 | |
| 195 | /// `alloc_num / alloc_den` is the factor of padding when allocating. | 195 | /// When allocating, the ideal_capacity is calculated by |
| 196 | pub const alloc_num = 4; | 196 | /// actual_capacity + (actual_capacity / ideal_factor) |
| 197 | pub const alloc_den = 3; | 197 | const ideal_factor = 2; |
| 198 | | 198 | |
| 199 | /// Default path to dyld | 199 | /// Default path to dyld |
| 200 | /// 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 |
| ... | @@ -214,7 +214,7 @@ const LIB_SYSTEM_PATH: [*:0]const u8 = DEFAULT_LIB_SEARCH_PATH ++ "/libSystem.B. | ... | @@ -214,7 +214,7 @@ const LIB_SYSTEM_PATH: [*:0]const u8 = DEFAULT_LIB_SEARCH_PATH ++ "/libSystem.B. |
| 214 | /// 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 |
| 215 | /// (plus extra for reserved capacity). | 215 | /// (plus extra for reserved capacity). |
| 216 | const minimum_text_block_size = 64; | 216 | const minimum_text_block_size = 64; |
| 217 | const min_text_capacity = minimum_text_block_size * alloc_num / alloc_den; | 217 | const min_text_capacity = padToIdeal(minimum_text_block_size); |
| 218 | | 218 | |
| 219 | pub const TextBlock = struct { | 219 | pub const TextBlock = struct { |
| 220 | /// 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. |
| ... | @@ -277,7 +277,7 @@ pub const TextBlock = struct { | ... | @@ -277,7 +277,7 @@ pub const TextBlock = struct { |
| 277 | 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]; |
| 278 | 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]; |
| 279 | const cap = next_sym.n_value - self_sym.n_value; | 279 | const cap = next_sym.n_value - self_sym.n_value; |
| 280 | const ideal_cap = self.size * alloc_num / alloc_den; | 280 | const ideal_cap = padToIdeal(self.size); |
| 281 | if (cap <= ideal_cap) return false; | 281 | if (cap <= ideal_cap) return false; |
| 282 | const surplus = cap - ideal_cap; | 282 | const surplus = cap - ideal_cap; |
| 283 | return surplus >= min_text_capacity; | 283 | return surplus >= min_text_capacity; |
| ... | @@ -873,7 +873,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { | ... | @@ -873,7 +873,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 873 | 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; |
| 874 | const text_section = text_segment.sections.items[self.text_section_index.?]; | 874 | const text_section = text_segment.sections.items[self.text_section_index.?]; |
| 875 | 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); |
| 876 | const needed_size = @sizeOf(macho.linkedit_data_command) * alloc_num / alloc_den; | 876 | const needed_size = padToIdeal(@sizeOf(macho.linkedit_data_command)); |
| 877 | | 877 | |
| 878 | if (needed_size + after_last_cmd_offset > text_section.offset) { | 878 | if (needed_size + after_last_cmd_offset > text_section.offset) { |
| 879 | 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.", .{}); |
| ... | @@ -943,7 +943,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { | ... | @@ -943,7 +943,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 943 | 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; |
| 944 | const text_section = text_segment.sections.items[self.text_section_index.?]; | 944 | const text_section = text_segment.sections.items[self.text_section_index.?]; |
| 945 | 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); |
| 946 | const needed_size = @sizeOf(macho.linkedit_data_command) * alloc_num / alloc_den; | 946 | const needed_size = padToIdeal(@sizeOf(macho.linkedit_data_command)); |
| 947 | | 947 | |
| 948 | if (needed_size + after_last_cmd_offset > text_section.offset) { | 948 | if (needed_size + after_last_cmd_offset > text_section.offset) { |
| 949 | 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.", .{}); |
| ... | @@ -1491,7 +1491,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -1491,7 +1491,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1491 | 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; |
| 1492 | 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; |
| 1493 | 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; |
| 1494 | 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); |
| 1495 | | 1495 | |
| 1496 | 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 }); |
| 1497 | | 1497 | |
| ... | @@ -1656,7 +1656,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -1656,7 +1656,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1656 | const address_and_offset = self.nextSegmentAddressAndOffset(); | 1656 | const address_and_offset = self.nextSegmentAddressAndOffset(); |
| 1657 | | 1657 | |
| 1658 | const ideal_size = @sizeOf(u64) * self.base.options.symbol_count_hint; | 1658 | const ideal_size = @sizeOf(u64) * self.base.options.symbol_count_hint; |
| 1659 | 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); |
| 1660 | | 1660 | |
| 1661 | 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 }); |
| 1662 | | 1662 | |
| ... | @@ -1713,7 +1713,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -1713,7 +1713,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1713 | const address_and_offset = self.nextSegmentAddressAndOffset(); | 1713 | const address_and_offset = self.nextSegmentAddressAndOffset(); |
| 1714 | | 1714 | |
| 1715 | 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; |
| 1716 | 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); |
| 1717 | | 1717 | |
| 1718 | 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 }); |
| 1719 | | 1719 | |
| ... | @@ -2133,7 +2133,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -2133,7 +2133,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 2133 | 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 { |
| 2134 | 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; |
| 2135 | const text_section = &text_segment.sections.items[self.text_section_index.?]; | 2135 | const text_section = &text_segment.sections.items[self.text_section_index.?]; |
| 2136 | const new_block_ideal_capacity = new_block_size * alloc_num / alloc_den; | 2136 | const new_block_ideal_capacity = padToIdeal(new_block_size); |
| 2137 | | 2137 | |
| 2138 | // 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, |
| 2139 | // and possibly removing a free list node. | 2139 | // and possibly removing a free list node. |
| ... | @@ -2153,13 +2153,8 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, | ... | @@ -2153,13 +2153,8 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, |
| 2153 | // Is it enough that we could fit this new text block? | 2153 | // Is it enough that we could fit this new text block? |
| 2154 | const sym = self.local_symbols.items[big_block.local_sym_index]; | 2154 | const sym = self.local_symbols.items[big_block.local_sym_index]; |
| 2155 | const capacity = big_block.capacity(self.*); | 2155 | const capacity = big_block.capacity(self.*); |
| 2156 | const ideal_capacity_end_vaddr: u64 = ideal_cap: { | 2156 | const ideal_capacity = padToIdeal(capacity); |
| 2157 | if (math.mul(u64, @divTrunc(capacity, alloc_den), alloc_num)) |cap| { | 2157 | const ideal_capacity_end_vaddr = sym.n_value + ideal_capacity; |
| 2158 | break :ideal_cap math.add(u64, sym.n_value, cap) catch math.maxInt(u64); | | |
| 2159 | } else |_| { | | |
| 2160 | break :ideal_cap math.maxInt(u64); | | |
| 2161 | } | | |
| 2162 | }; | | |
| 2163 | const capacity_end_vaddr = sym.n_value + capacity; | 2158 | const capacity_end_vaddr = sym.n_value + capacity; |
| 2164 | 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; |
| 2165 | const new_start_vaddr = mem.alignBackwardGeneric(u64, new_start_vaddr_unaligned, alignment); | 2160 | const new_start_vaddr = mem.alignBackwardGeneric(u64, new_start_vaddr_unaligned, alignment); |
| ... | @@ -2190,7 +2185,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, | ... | @@ -2190,7 +2185,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, |
| 2190 | const last_symbol = self.local_symbols.items[last.local_sym_index]; | 2185 | const last_symbol = self.local_symbols.items[last.local_sym_index]; |
| 2191 | // 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, |
| 2192 | // 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. |
| 2193 | const ideal_capacity = last.size * alloc_num / alloc_den; | 2188 | const ideal_capacity = padToIdeal(last.size); |
| 2194 | const ideal_capacity_end_vaddr = last_symbol.n_value + ideal_capacity; | 2189 | const ideal_capacity_end_vaddr = last_symbol.n_value + ideal_capacity; |
| 2195 | 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); |
| 2196 | block_placement = last; | 2191 | block_placement = last; |
| ... | @@ -2365,7 +2360,7 @@ fn allocatedSizeLinkedit(self: *MachO, start: u64) u64 { | ... | @@ -2365,7 +2360,7 @@ fn allocatedSizeLinkedit(self: *MachO, start: u64) u64 { |
| 2365 | } | 2360 | } |
| 2366 | | 2361 | |
| 2367 | 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 { |
| 2368 | const increased_size = satMul(size, alloc_num) / alloc_den; | 2363 | const increased_size = padToIdeal(size); |
| 2369 | const test_end = off + increased_size; | 2364 | const test_end = off + increased_size; |
| 2370 | if (end > off and start < test_end) { | 2365 | if (end > off and start < test_end) { |
| 2371 | return test_end; | 2366 | return test_end; |
| ... | @@ -2374,7 +2369,7 @@ inline fn checkForCollision(start: u64, end: u64, off: u64, size: u64) ?u64 { | ... | @@ -2374,7 +2369,7 @@ inline fn checkForCollision(start: u64, end: u64, off: u64, size: u64) ?u64 { |
| 2374 | } | 2369 | } |
| 2375 | | 2370 | |
| 2376 | fn detectAllocCollisionLinkedit(self: *MachO, start: u64, size: u64) ?u64 { | 2371 | fn detectAllocCollisionLinkedit(self: *MachO, start: u64, size: u64) ?u64 { |
| 2377 | const end = start + satMul(size, alloc_num) / alloc_den; | 2372 | const end = start + padToIdeal(size); |
| 2378 | | 2373 | |
| 2379 | // __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 |
| 2380 | // special-case it. | 2375 | // special-case it. |
| ... | @@ -2455,12 +2450,6 @@ fn findFreeSpaceLinkedit(self: *MachO, object_size: u64, min_alignment: u16, sta | ... | @@ -2455,12 +2450,6 @@ fn findFreeSpaceLinkedit(self: *MachO, object_size: u64, min_alignment: u16, sta |
| 2455 | return st; | 2450 | return st; |
| 2456 | } | 2451 | } |
| 2457 | | 2452 | |
| 2458 | /// Saturating multiplication | | |
| 2459 | pub fn satMul(a: anytype, b: anytype) @TypeOf(a, b) { | | |
| 2460 | const T = @TypeOf(a, b); | | |
| 2461 | return std.math.mul(T, a, b) catch std.math.maxInt(T); | | |
| 2462 | } | | |
| 2463 | | | |
| 2464 | fn writeOffsetTableEntry(self: *MachO, index: usize) !void { | 2453 | fn writeOffsetTableEntry(self: *MachO, index: usize) !void { |
| 2465 | 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; |
| 2466 | const sect = &text_segment.sections.items[self.got_section_index.?]; | 2455 | const sect = &text_segment.sections.items[self.got_section_index.?]; |
| ... | @@ -3275,3 +3264,9 @@ fn fixupInfoCommon(self: *MachO, buffer: []u8, dylib_ordinal: u32) !void { | ... | @@ -3275,3 +3264,9 @@ fn fixupInfoCommon(self: *MachO, buffer: []u8, dylib_ordinal: u32) !void { |
| 3275 | } | 3264 | } |
| 3276 | } | 3265 | } |
| 3277 | } | 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 | } |