diff --git a/src/link/MachO.zig b/src/link/MachO.zig index afd2ec4eaa..6c2059fcdc 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -143,11 +143,11 @@ string_table_needs_relocation: bool = false, /// or removed from the freelist. /// /// A text block has surplus capacity when its overcapacity value is greater than -/// minimum_text_block_size * alloc_num / alloc_den. That is, when it has so +/// padToIdeal(minimum_text_block_size). That is, when it has so /// much extra capacity, that we could fit a small new symbol in it, itself with /// ideal_capacity or more. /// -/// Ideal capacity is defined by size * alloc_num / alloc_den. +/// Ideal capacity is defined by size + (size / ideal_factor). /// /// Overcapacity is measured by actual_capacity - ideal_capacity. Note that /// overcapacity can be negative. A simple way to have negative overcapacity is to @@ -192,9 +192,9 @@ pub const StubFixup = struct { len: usize, }; -/// `alloc_num / alloc_den` is the factor of padding when allocating. -pub const alloc_num = 4; -pub const alloc_den = 3; +/// When allocating, the ideal_capacity is calculated by +/// actual_capacity + (actual_capacity / ideal_factor) +const ideal_factor = 2; /// Default path to dyld /// 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. /// it as a possible place to put new symbols, it must have enough room for this many bytes /// (plus extra for reserved capacity). const minimum_text_block_size = 64; -const min_text_capacity = minimum_text_block_size * alloc_num / alloc_den; +const min_text_capacity = padToIdeal(minimum_text_block_size); pub const TextBlock = struct { /// Each decl always gets a local symbol with the fully qualified name. @@ -277,7 +277,7 @@ pub const TextBlock = struct { const self_sym = macho_file.local_symbols.items[self.local_sym_index]; const next_sym = macho_file.local_symbols.items[next.local_sym_index]; const cap = next_sym.n_value - self_sym.n_value; - const ideal_cap = self.size * alloc_num / alloc_den; + const ideal_cap = padToIdeal(self.size); if (cap <= ideal_cap) return false; const surplus = cap - ideal_cap; return surplus >= min_text_capacity; @@ -873,7 +873,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; const text_section = text_segment.sections.items[self.text_section_index.?]; const after_last_cmd_offset = self.header.?.sizeofcmds + @sizeOf(macho.mach_header_64); - const needed_size = @sizeOf(macho.linkedit_data_command) * alloc_num / alloc_den; + const needed_size = padToIdeal(@sizeOf(macho.linkedit_data_command)); if (needed_size + after_last_cmd_offset > text_section.offset) { 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 { const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; const text_section = text_segment.sections.items[self.text_section_index.?]; const after_last_cmd_offset = self.header.?.sizeofcmds + @sizeOf(macho.mach_header_64); - const needed_size = @sizeOf(macho.linkedit_data_command) * alloc_num / alloc_den; + const needed_size = padToIdeal(@sizeOf(macho.linkedit_data_command)); if (needed_size + after_last_cmd_offset > text_section.offset) { 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 { const program_code_size_hint = self.base.options.program_code_size_hint; const offset_table_size_hint = @sizeOf(u64) * self.base.options.symbol_count_hint; const ideal_size = self.header_pad + program_code_size_hint + 3 * offset_table_size_hint; - const needed_size = mem.alignForwardGeneric(u64, satMul(ideal_size, alloc_num) / alloc_den, self.page_size); + const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size); log.debug("found __TEXT segment free space 0x{x} to 0x{x}", .{ 0, needed_size }); @@ -1656,7 +1656,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { const address_and_offset = self.nextSegmentAddressAndOffset(); const ideal_size = @sizeOf(u64) * self.base.options.symbol_count_hint; - const needed_size = mem.alignForwardGeneric(u64, satMul(ideal_size, alloc_num) / alloc_den, self.page_size); + const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size); log.debug("found __DATA_CONST segment free space 0x{x} to 0x{x}", .{ address_and_offset.offset, address_and_offset.offset + needed_size }); @@ -1713,7 +1713,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { const address_and_offset = self.nextSegmentAddressAndOffset(); const ideal_size = 2 * @sizeOf(u64) * self.base.options.symbol_count_hint; - const needed_size = mem.alignForwardGeneric(u64, satMul(ideal_size, alloc_num) / alloc_den, self.page_size); + const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size); log.debug("found __DATA segment free space 0x{x} to 0x{x}", .{ address_and_offset.offset, address_and_offset.offset + needed_size }); @@ -2133,7 +2133,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 { const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; const text_section = &text_segment.sections.items[self.text_section_index.?]; - const new_block_ideal_capacity = new_block_size * alloc_num / alloc_den; + const new_block_ideal_capacity = padToIdeal(new_block_size); // We use these to indicate our intention to update metadata, placing the new block, // and possibly removing a free list node. @@ -2153,13 +2153,8 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, // Is it enough that we could fit this new text block? const sym = self.local_symbols.items[big_block.local_sym_index]; const capacity = big_block.capacity(self.*); - const ideal_capacity_end_vaddr: u64 = ideal_cap: { - if (math.mul(u64, @divTrunc(capacity, alloc_den), alloc_num)) |cap| { - break :ideal_cap math.add(u64, sym.n_value, cap) catch math.maxInt(u64); - } else |_| { - break :ideal_cap math.maxInt(u64); - } - }; + const ideal_capacity = padToIdeal(capacity); + const ideal_capacity_end_vaddr = sym.n_value + ideal_capacity; const capacity_end_vaddr = sym.n_value + capacity; const new_start_vaddr_unaligned = capacity_end_vaddr - new_block_ideal_capacity; 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, const last_symbol = self.local_symbols.items[last.local_sym_index]; // TODO We should pad out the excess capacity with NOPs. For executables, // no padding seems to be OK, but it will probably not be for objects. - const ideal_capacity = last.size * alloc_num / alloc_den; + const ideal_capacity = padToIdeal(last.size); const ideal_capacity_end_vaddr = last_symbol.n_value + ideal_capacity; const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment); block_placement = last; @@ -2365,7 +2360,7 @@ fn allocatedSizeLinkedit(self: *MachO, start: u64) u64 { } inline fn checkForCollision(start: u64, end: u64, off: u64, size: u64) ?u64 { - const increased_size = satMul(size, alloc_num) / alloc_den; + const increased_size = padToIdeal(size); const test_end = off + increased_size; if (end > off and start < test_end) { return test_end; @@ -2374,7 +2369,7 @@ inline fn checkForCollision(start: u64, end: u64, off: u64, size: u64) ?u64 { } fn detectAllocCollisionLinkedit(self: *MachO, start: u64, size: u64) ?u64 { - const end = start + satMul(size, alloc_num) / alloc_den; + const end = start + padToIdeal(size); // __LINKEDIT is a weird segment where sections get their own load commands so we // special-case it. @@ -2455,12 +2450,6 @@ fn findFreeSpaceLinkedit(self: *MachO, object_size: u64, min_alignment: u16, sta return st; } -/// Saturating multiplication -pub fn satMul(a: anytype, b: anytype) @TypeOf(a, b) { - const T = @TypeOf(a, b); - return std.math.mul(T, a, b) catch std.math.maxInt(T); -} - fn writeOffsetTableEntry(self: *MachO, index: usize) !void { const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; const sect = &text_segment.sections.items[self.got_section_index.?]; @@ -3275,3 +3264,9 @@ fn fixupInfoCommon(self: *MachO, buffer: []u8, dylib_ordinal: u32) !void { } } } + +pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) { + // TODO https://github.com/ziglang/zig/issues/1284 + return std.math.add(@TypeOf(actual_size), actual_size, actual_size / ideal_factor) catch + std.math.maxInt(@TypeOf(actual_size)); +} diff --git a/src/link/MachO/DebugSymbols.zig b/src/link/MachO/DebugSymbols.zig index fc58c1e552..15aa86be51 100644 --- a/src/link/MachO/DebugSymbols.zig +++ b/src/link/MachO/DebugSymbols.zig @@ -18,9 +18,7 @@ const link = @import("../../link.zig"); const MachO = @import("../MachO.zig"); const SrcFn = MachO.SrcFn; const TextBlock = MachO.TextBlock; -const satMul = MachO.satMul; -const alloc_num = MachO.alloc_num; -const alloc_den = MachO.alloc_den; +const padToIdeal = MachO.padToIdeal; const makeStaticString = MachO.makeStaticString; usingnamespace @import("commands.zig"); @@ -207,7 +205,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void const linkedit = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; const ideal_size: u16 = 200 + 128 + 160 + 250; - const needed_size = mem.alignForwardGeneric(u64, satMul(ideal_size, alloc_num) / alloc_den, page_size); + const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), page_size); const off = linkedit.inner.fileoff + linkedit.inner.filesize; const vmaddr = linkedit.inner.vmaddr + linkedit.inner.vmsize; @@ -804,7 +802,7 @@ fn allocatedSizeLinkedit(self: *DebugSymbols, start: u64) u64 { } fn detectAllocCollisionLinkedit(self: *DebugSymbols, start: u64, size: u64) ?u64 { - const end = start + satMul(size, alloc_num) / alloc_den; + const end = start + padToIdeal(size); if (self.symtab_cmd_index) |idx| outer: { if (self.load_commands.items.len == idx) break :outer; @@ -812,7 +810,7 @@ fn detectAllocCollisionLinkedit(self: *DebugSymbols, start: u64, size: u64) ?u64 { // Symbol table const symsize = symtab.nsyms * @sizeOf(macho.nlist_64); - const increased_size = satMul(symsize, alloc_num) / alloc_den; + const increased_size = padToIdeal(symsize); const test_end = symtab.symoff + increased_size; if (end > symtab.symoff and start < test_end) { return test_end; @@ -820,7 +818,7 @@ fn detectAllocCollisionLinkedit(self: *DebugSymbols, start: u64, size: u64) ?u64 } { // String table - const increased_size = satMul(symtab.strsize, alloc_num) / alloc_den; + const increased_size = padToIdeal(symtab.strsize); const test_end = symtab.stroff + increased_size; if (end > symtab.stroff and start < test_end) { return test_end; @@ -1099,7 +1097,7 @@ pub fn commitDeclDebugInfo( last.next = src_fn; self.dbg_line_fn_last = src_fn; - src_fn.off = last.off + (last.len * alloc_num / alloc_den); + src_fn.off = last.off + padToIdeal(last.len); } } else if (src_fn.prev == null) { // Append new function. @@ -1108,14 +1106,14 @@ pub fn commitDeclDebugInfo( last.next = src_fn; self.dbg_line_fn_last = src_fn; - src_fn.off = last.off + (last.len * alloc_num / alloc_den); + src_fn.off = last.off + padToIdeal(last.len); } } else { // This is the first function of the Line Number Program. self.dbg_line_fn_first = src_fn; self.dbg_line_fn_last = src_fn; - src_fn.off = self.dbgLineNeededHeaderBytes(module) * alloc_num / alloc_den; + src_fn.off = padToIdeal(self.dbgLineNeededHeaderBytes(module)); } const last_src_fn = self.dbg_line_fn_last.?; @@ -1259,7 +1257,7 @@ fn updateDeclDebugInfoAllocation( last.dbg_info_next = text_block; self.dbg_info_decl_last = text_block; - text_block.dbg_info_off = last.dbg_info_off + (last.dbg_info_len * alloc_num / alloc_den); + text_block.dbg_info_off = last.dbg_info_off + padToIdeal(last.dbg_info_len); } } else if (text_block.dbg_info_prev == null) { // Append new Decl. @@ -1268,14 +1266,14 @@ fn updateDeclDebugInfoAllocation( last.dbg_info_next = text_block; self.dbg_info_decl_last = text_block; - text_block.dbg_info_off = last.dbg_info_off + (last.dbg_info_len * alloc_num / alloc_den); + text_block.dbg_info_off = last.dbg_info_off + padToIdeal(last.dbg_info_len); } } else { // This is the first Decl of the .debug_info self.dbg_info_decl_first = text_block; self.dbg_info_decl_last = text_block; - text_block.dbg_info_off = self.dbgInfoNeededHeaderBytes() * alloc_num / alloc_den; + text_block.dbg_info_off = padToIdeal(self.dbgInfoNeededHeaderBytes()); } } diff --git a/src/link/MachO/commands.zig b/src/link/MachO/commands.zig index e94ca1c8e5..baea36b4e6 100644 --- a/src/link/MachO/commands.zig +++ b/src/link/MachO/commands.zig @@ -10,9 +10,7 @@ const assert = std.debug.assert; const Allocator = std.mem.Allocator; const MachO = @import("../MachO.zig"); const makeStaticString = MachO.makeStaticString; -const satMul = MachO.satMul; -const alloc_num = MachO.alloc_num; -const alloc_den = MachO.alloc_den; +const padToIdeal = MachO.padToIdeal; pub const LoadCommand = union(enum) { Segment: SegmentCommand, @@ -214,9 +212,9 @@ pub const SegmentCommand = struct { } fn detectAllocCollision(self: SegmentCommand, start: u64, size: u64) ?u64 { - const end = start + satMul(size, alloc_num) / alloc_den; + const end = start + padToIdeal(size); for (self.sections.items) |section| { - const increased_size = satMul(section.size, alloc_num) / alloc_den; + const increased_size = padToIdeal(section.size); const test_end = section.offset + increased_size; if (end > section.offset and start < test_end) { return test_end;