| ... | ... | @@ -138,7 +138,11 @@ locals: std.ArrayListUnmanaged(macho.nlist_64) = .{}, |
| 138 | 138 | globals: std.ArrayListUnmanaged(macho.nlist_64) = .{}, |
| 139 | 139 | undefs: std.ArrayListUnmanaged(macho.nlist_64) = .{}, |
| 140 | 140 | symbol_resolver: std.AutoHashMapUnmanaged(u32, SymbolWithLoc) = .{}, |
| 141 | | unresolved: std.AutoArrayHashMapUnmanaged(u32, void) = .{}, |
| 141 | unresolved: std.AutoArrayHashMapUnmanaged(u32, enum { |
| 142 | none, |
| 143 | stub, |
| 144 | got, |
| 145 | }) = .{}, |
| 142 | 146 | |
| 143 | 147 | locals_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| 144 | 148 | globals_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| ... | ... | @@ -147,8 +151,6 @@ dyld_private_sym_index: ?u32 = null, |
| 147 | 151 | dyld_stub_binder_index: ?u32 = null, |
| 148 | 152 | stub_preamble_sym_index: ?u32 = null, |
| 149 | 153 | |
| 150 | | stub_helper_stubs_start_off: ?u64 = null, |
| 151 | | |
| 152 | 154 | strtab: std.ArrayListUnmanaged(u8) = .{}, |
| 153 | 155 | strtab_dir: std.HashMapUnmanaged(u32, u32, StringIndexContext, std.hash_map.default_max_load_percentage) = .{}, |
| 154 | 156 | |
| ... | ... | @@ -382,26 +384,6 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio |
| 382 | 384 | try ds.writeLocalSymbol(0); |
| 383 | 385 | } |
| 384 | 386 | |
| 385 | | { |
| 386 | | const atom = try self.createDyldPrivateAtom(); |
| 387 | | const match = MatchingSection{ |
| 388 | | .seg = self.data_segment_cmd_index.?, |
| 389 | | .sect = self.data_section_index.?, |
| 390 | | }; |
| 391 | | const vaddr = try self.allocateAtom(atom, match); |
| 392 | | try self.writeAtom(atom, match); |
| 393 | | } |
| 394 | | |
| 395 | | { |
| 396 | | const atom = try self.createStubHelperPreambleAtom(); |
| 397 | | const match = MatchingSection{ |
| 398 | | .seg = self.text_segment_cmd_index.?, |
| 399 | | .sect = self.stub_helper_section_index.?, |
| 400 | | }; |
| 401 | | const vaddr = try self.allocateAtom(atom, match); |
| 402 | | try self.writeAtom(atom, match); |
| 403 | | } |
| 404 | | |
| 405 | 387 | return self; |
| 406 | 388 | } |
| 407 | 389 | |
| ... | ... | @@ -776,8 +758,8 @@ pub fn flush(self: *MachO, comp: *Compilation) !void { |
| 776 | 758 | |
| 777 | 759 | try self.parseInputFiles(positionals.items, self.base.options.sysroot); |
| 778 | 760 | try self.parseLibs(libs.items, self.base.options.sysroot); |
| 761 | |
| 779 | 762 | try self.resolveSymbols(); |
| 780 | | try self.resolveDyldStubBinder(); |
| 781 | 763 | try self.addRpathLCs(rpath_table.keys()); |
| 782 | 764 | try self.addLoadDylibLCs(); |
| 783 | 765 | try self.addDataInCodeLC(); |
| ... | ... | @@ -799,7 +781,6 @@ pub fn flush(self: *MachO, comp: *Compilation) !void { |
| 799 | 781 | .seg = self.text_segment_cmd_index.?, |
| 800 | 782 | .sect = self.stub_helper_section_index.?, |
| 801 | 783 | }); |
| 802 | | |
| 803 | 784 | // TODO this is just a temp |
| 804 | 785 | // We already prealloc stub helper size in populateMissingMetadata(), but |
| 805 | 786 | // perhaps it's not needed after all? |
| ... | ... | @@ -807,6 +788,7 @@ pub fn flush(self: *MachO, comp: *Compilation) !void { |
| 807 | 788 | const sect = &seg.sections.items[self.stub_helper_section_index.?]; |
| 808 | 789 | sect.size -= atom.size; |
| 809 | 790 | } |
| 791 | |
| 810 | 792 | for (self.stubs.items) |_| { |
| 811 | 793 | const stub_helper_atom = try self.createStubHelperAtom(); |
| 812 | 794 | try self.allocateAtomStage1(stub_helper_atom, .{ |
| ... | ... | @@ -826,21 +808,12 @@ pub fn flush(self: *MachO, comp: *Compilation) !void { |
| 826 | 808 | .sect = self.stubs_section_index.?, |
| 827 | 809 | }); |
| 828 | 810 | } |
| 811 | |
| 829 | 812 | try self.allocateTextSegment(); |
| 830 | 813 | try self.allocateDataConstSegment(); |
| 831 | 814 | try self.allocateDataSegment(); |
| 832 | 815 | self.allocateLinkeditSegment(); |
| 833 | 816 | try self.allocateTextBlocks(); |
| 834 | | { |
| 835 | | // TODO just a temp |
| 836 | | const seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 837 | | const sect = seg.sections.items[self.stub_helper_section_index.?]; |
| 838 | | self.stub_helper_stubs_start_off = sect.offset + switch (self.base.options.target.cpu.arch) { |
| 839 | | .x86_64 => @intCast(u64, 15), |
| 840 | | .aarch64 => @intCast(u64, 6 * @sizeOf(u32)), |
| 841 | | else => unreachable, |
| 842 | | }; |
| 843 | | } |
| 844 | 817 | try self.flushZld(); |
| 845 | 818 | } else { |
| 846 | 819 | try self.flushModule(comp); |
| ... | ... | @@ -1937,13 +1910,7 @@ fn writeTextBlocks(self: *MachO) !void { |
| 1937 | 1910 | } |
| 1938 | 1911 | } |
| 1939 | 1912 | |
| 1940 | | fn createEmptyAtom( |
| 1941 | | self: *MachO, |
| 1942 | | match: MatchingSection, |
| 1943 | | local_sym_index: u32, |
| 1944 | | size: u64, |
| 1945 | | alignment: u32, |
| 1946 | | ) !*TextBlock { |
| 1913 | fn createEmptyAtom(self: *MachO, local_sym_index: u32, size: u64, alignment: u32) !*TextBlock { |
| 1947 | 1914 | const code = try self.base.allocator.alloc(u8, size); |
| 1948 | 1915 | defer self.base.allocator.free(code); |
| 1949 | 1916 | mem.set(u8, code, 0); |
| ... | ... | @@ -1964,15 +1931,20 @@ fn allocateAtom(self: *MachO, atom: *TextBlock, match: MatchingSection) !u64 { |
| 1964 | 1931 | // TODO converge with `allocateTextBlock` |
| 1965 | 1932 | const seg = self.load_commands.items[match.seg].Segment; |
| 1966 | 1933 | const sect = seg.sections.items[match.sect]; |
| 1967 | | const base_addr = if (atom.prev) |prev| blk: { |
| 1968 | | const prev_atom_sym = self.locals.items[prev.local_sym_index]; |
| 1969 | | break :blk prev_atom_sym.n_value; |
| 1934 | const sym = &self.locals.items[atom.local_sym_index]; |
| 1935 | const base_addr = if (self.blocks.get(match)) |last| blk: { |
| 1936 | const last_atom_sym = self.locals.items[last.local_sym_index]; |
| 1937 | break :blk last_atom_sym.n_value + last.size; |
| 1970 | 1938 | } else sect.addr; |
| 1971 | 1939 | const atom_alignment = try math.powi(u32, 2, atom.alignment); |
| 1972 | 1940 | const vaddr = mem.alignForwardGeneric(u64, base_addr, atom_alignment); |
| 1941 | log.debug("allocating atom for symbol {s} at address 0x{x}", .{ self.getString(sym.n_strx), vaddr }); |
| 1973 | 1942 | |
| 1974 | 1943 | // TODO we should check if we need to expand the section or not like we |
| 1975 | 1944 | // do in `allocateTextBlock`. |
| 1945 | sym.n_value = vaddr; |
| 1946 | sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1); |
| 1947 | |
| 1976 | 1948 | if (self.blocks.getPtr(match)) |last| { |
| 1977 | 1949 | last.*.next = atom; |
| 1978 | 1950 | atom.prev = last.*; |
| ... | ... | @@ -1987,15 +1959,9 @@ fn allocateAtom(self: *MachO, atom: *TextBlock, match: MatchingSection) !u64 { |
| 1987 | 1959 | fn writeAtom(self: *MachO, atom: *TextBlock, match: MatchingSection) !void { |
| 1988 | 1960 | const seg = self.load_commands.items[match.seg].Segment; |
| 1989 | 1961 | const sect = seg.sections.items[match.sect]; |
| 1990 | | |
| 1991 | | const vaddr = try self.allocateAtom(atom, match); |
| 1992 | | const sym = &self.locals.items[atom.local_sym_index]; |
| 1993 | | sym.n_value = vaddr; |
| 1994 | | sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1); |
| 1995 | | |
| 1962 | const sym = self.locals.items[atom.local_sym_index]; |
| 1963 | const file_offset = sect.offset + sym.n_value - sect.addr; |
| 1996 | 1964 | try atom.resolveRelocs(self); |
| 1997 | | |
| 1998 | | const file_offset = sect.offset + vaddr - sect.addr; |
| 1999 | 1965 | log.debug("writing atom for symbol {s} at file offset 0x{x}", .{ self.getString(sym.n_strx), file_offset }); |
| 2000 | 1966 | try self.base.file.?.pwriteAll(atom.code.items, file_offset); |
| 2001 | 1967 | try self.writeLocalSymbol(atom.local_sym_index); |
| ... | ... | @@ -2023,10 +1989,6 @@ fn allocateAtomStage1(self: *MachO, atom: *TextBlock, match: MatchingSection) !v |
| 2023 | 1989 | } |
| 2024 | 1990 | |
| 2025 | 1991 | fn createDyldPrivateAtom(self: *MachO) !*TextBlock { |
| 2026 | | const match = MatchingSection{ |
| 2027 | | .seg = self.data_segment_cmd_index.?, |
| 2028 | | .sect = self.data_section_index.?, |
| 2029 | | }; |
| 2030 | 1992 | const local_sym_index = @intCast(u32, self.locals.items.len); |
| 2031 | 1993 | try self.locals.append(self.base.allocator, .{ |
| 2032 | 1994 | .n_strx = try self.makeString("dyld_private"), |
| ... | ... | @@ -2036,15 +1998,11 @@ fn createDyldPrivateAtom(self: *MachO) !*TextBlock { |
| 2036 | 1998 | .n_value = 0, |
| 2037 | 1999 | }); |
| 2038 | 2000 | self.dyld_private_sym_index = local_sym_index; |
| 2039 | | return self.createEmptyAtom(match, local_sym_index, @sizeOf(u64), 3); |
| 2001 | return self.createEmptyAtom(local_sym_index, @sizeOf(u64), 3); |
| 2040 | 2002 | } |
| 2041 | 2003 | |
| 2042 | 2004 | fn createStubHelperPreambleAtom(self: *MachO) !*TextBlock { |
| 2043 | 2005 | const arch = self.base.options.target.cpu.arch; |
| 2044 | | const match = MatchingSection{ |
| 2045 | | .seg = self.text_segment_cmd_index.?, |
| 2046 | | .sect = self.stub_helper_section_index.?, |
| 2047 | | }; |
| 2048 | 2006 | const size: u64 = switch (arch) { |
| 2049 | 2007 | .x86_64 => 15, |
| 2050 | 2008 | .aarch64 => 6 * @sizeOf(u32), |
| ... | ... | @@ -2063,7 +2021,7 @@ fn createStubHelperPreambleAtom(self: *MachO) !*TextBlock { |
| 2063 | 2021 | .n_desc = 0, |
| 2064 | 2022 | .n_value = 0, |
| 2065 | 2023 | }); |
| 2066 | | const atom = try self.createEmptyAtom(match, local_sym_index, size, alignment); |
| 2024 | const atom = try self.createEmptyAtom(local_sym_index, size, alignment); |
| 2067 | 2025 | switch (arch) { |
| 2068 | 2026 | .x86_64 => { |
| 2069 | 2027 | try atom.relocs.ensureUnusedCapacity(self.base.allocator, 2); |
| ... | ... | @@ -2196,10 +2154,7 @@ fn createStubHelperAtom(self: *MachO) !*TextBlock { |
| 2196 | 2154 | .n_desc = 0, |
| 2197 | 2155 | .n_value = 0, |
| 2198 | 2156 | }); |
| 2199 | | const atom = try self.createEmptyAtom(.{ |
| 2200 | | .seg = self.text_segment_cmd_index.?, |
| 2201 | | .sect = self.stub_helper_section_index.?, |
| 2202 | | }, local_sym_index, stub_size, alignment); |
| 2157 | const atom = try self.createEmptyAtom(local_sym_index, stub_size, alignment); |
| 2203 | 2158 | try atom.relocs.ensureTotalCapacity(self.base.allocator, 1); |
| 2204 | 2159 | |
| 2205 | 2160 | switch (arch) { |
| ... | ... | @@ -2254,10 +2209,7 @@ fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32) !*TextBlock { |
| 2254 | 2209 | .n_desc = 0, |
| 2255 | 2210 | .n_value = 0, |
| 2256 | 2211 | }); |
| 2257 | | const atom = try self.createEmptyAtom(.{ |
| 2258 | | .seg = self.data_segment_cmd_index.?, |
| 2259 | | .sect = self.la_symbol_ptr_section_index.?, |
| 2260 | | }, local_sym_index, @sizeOf(u64), 3); |
| 2212 | const atom = try self.createEmptyAtom(local_sym_index, @sizeOf(u64), 3); |
| 2261 | 2213 | try atom.relocs.append(self.base.allocator, .{ |
| 2262 | 2214 | .offset = 0, |
| 2263 | 2215 | .where = .local, |
| ... | ... | @@ -2294,10 +2246,7 @@ fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*TextBlock { |
| 2294 | 2246 | .n_desc = 0, |
| 2295 | 2247 | .n_value = 0, |
| 2296 | 2248 | }); |
| 2297 | | const atom = try self.createEmptyAtom(.{ |
| 2298 | | .seg = self.text_segment_cmd_index.?, |
| 2299 | | .sect = self.stubs_section_index.?, |
| 2300 | | }, local_sym_index, stub_size, alignment); |
| 2249 | const atom = try self.createEmptyAtom(local_sym_index, stub_size, alignment); |
| 2301 | 2250 | switch (arch) { |
| 2302 | 2251 | .x86_64 => { |
| 2303 | 2252 | // jmp |
| ... | ... | @@ -2547,12 +2496,14 @@ fn resolveSymbolsInObject( |
| 2547 | 2496 | .where_index = undef_sym_index, |
| 2548 | 2497 | .file = object_id, |
| 2549 | 2498 | }); |
| 2550 | | _ = try self.unresolved.getOrPut(self.base.allocator, undef_sym_index); |
| 2499 | try self.unresolved.putNoClobber(self.base.allocator, undef_sym_index, .none); |
| 2551 | 2500 | } |
| 2552 | 2501 | } |
| 2553 | 2502 | } |
| 2554 | 2503 | |
| 2555 | 2504 | fn resolveSymbols(self: *MachO) !void { |
| 2505 | const use_stage1 = build_options.is_stage1 and self.base.options.use_stage1; |
| 2506 | |
| 2556 | 2507 | var tentatives = std.AutoArrayHashMap(u32, void).init(self.base.allocator); |
| 2557 | 2508 | defer tentatives.deinit(); |
| 2558 | 2509 | |
| ... | ... | @@ -2620,7 +2571,32 @@ fn resolveSymbols(self: *MachO) !void { |
| 2620 | 2571 | const resolv = self.symbol_resolver.getPtr(sym.n_strx) orelse unreachable; |
| 2621 | 2572 | resolv.local_sym_index = local_sym_index; |
| 2622 | 2573 | |
| 2623 | | _ = try self.createEmptyAtom(match, local_sym_index, size, alignment); |
| 2574 | const atom = try self.createEmptyAtom(local_sym_index, size, alignment); |
| 2575 | if (use_stage1) { |
| 2576 | try self.allocateAtomStage1(atom, match); |
| 2577 | } |
| 2578 | } |
| 2579 | |
| 2580 | try self.resolveDyldStubBinder(); |
| 2581 | if (!use_stage1) { |
| 2582 | { |
| 2583 | const atom = try self.createDyldPrivateAtom(); |
| 2584 | const match = MatchingSection{ |
| 2585 | .seg = self.data_segment_cmd_index.?, |
| 2586 | .sect = self.data_section_index.?, |
| 2587 | }; |
| 2588 | _ = try self.allocateAtom(atom, match); |
| 2589 | try self.writeAtom(atom, match); |
| 2590 | } |
| 2591 | { |
| 2592 | const atom = try self.createStubHelperPreambleAtom(); |
| 2593 | const match = MatchingSection{ |
| 2594 | .seg = self.text_segment_cmd_index.?, |
| 2595 | .sect = self.stub_helper_section_index.?, |
| 2596 | }; |
| 2597 | _ = try self.allocateAtom(atom, match); |
| 2598 | try self.writeAtom(atom, match); |
| 2599 | } |
| 2624 | 2600 | } |
| 2625 | 2601 | |
| 2626 | 2602 | // Third pass, resolve symbols in dynamic libraries. |
| ... | ... | @@ -2643,7 +2619,43 @@ fn resolveSymbols(self: *MachO) !void { |
| 2643 | 2619 | undef.n_type |= macho.N_EXT; |
| 2644 | 2620 | undef.n_desc = @intCast(u16, ordinal + 1) * macho.N_SYMBOL_RESOLVER; |
| 2645 | 2621 | |
| 2646 | | _ = self.unresolved.fetchSwapRemove(resolv.where_index); |
| 2622 | if (self.unresolved.fetchSwapRemove(resolv.where_index)) |entry| { |
| 2623 | switch (entry.value) { |
| 2624 | .none => {}, |
| 2625 | .got => return error.TODOGotHint, |
| 2626 | .stub => { |
| 2627 | const stub_helper_atom = blk: { |
| 2628 | const atom = try self.createStubHelperAtom(); |
| 2629 | const match = MatchingSection{ |
| 2630 | .seg = self.text_segment_cmd_index.?, |
| 2631 | .sect = self.stub_helper_section_index.?, |
| 2632 | }; |
| 2633 | _ = try self.allocateAtom(atom, match); |
| 2634 | try self.writeAtom(atom, match); |
| 2635 | break :blk atom; |
| 2636 | }; |
| 2637 | const laptr_atom = blk: { |
| 2638 | const atom = try self.createLazyPointerAtom(stub_helper_atom.local_sym_index); |
| 2639 | const match = MatchingSection{ |
| 2640 | .seg = self.data_segment_cmd_index.?, |
| 2641 | .sect = self.la_symbol_ptr_section_index.?, |
| 2642 | }; |
| 2643 | _ = try self.allocateAtom(atom, match); |
| 2644 | try self.writeAtom(atom, match); |
| 2645 | break :blk atom; |
| 2646 | }; |
| 2647 | { |
| 2648 | const atom = try self.createStubAtom(laptr_atom.local_sym_index); |
| 2649 | const match = MatchingSection{ |
| 2650 | .seg = self.text_segment_cmd_index.?, |
| 2651 | .sect = self.stubs_section_index.?, |
| 2652 | }; |
| 2653 | _ = try self.allocateAtom(atom, match); |
| 2654 | try self.writeAtom(atom, match); |
| 2655 | } |
| 2656 | }, |
| 2657 | } |
| 2658 | } |
| 2647 | 2659 | |
| 2648 | 2660 | continue :loop; |
| 2649 | 2661 | } |
| ... | ... | @@ -2695,7 +2707,10 @@ fn resolveSymbols(self: *MachO) !void { |
| 2695 | 2707 | // We create an empty atom for this symbol. |
| 2696 | 2708 | // TODO perhaps we should special-case special symbols? Create a separate |
| 2697 | 2709 | // linked list of atoms? |
| 2698 | | _ = try self.createEmptyAtom(match, local_sym_index, 0, 0); |
| 2710 | const atom = try self.createEmptyAtom(local_sym_index, 0, 0); |
| 2711 | if (use_stage1) { |
| 2712 | try self.allocateAtomStage1(atom, match); |
| 2713 | } |
| 2699 | 2714 | } |
| 2700 | 2715 | |
| 2701 | 2716 | for (self.unresolved.keys()) |index| { |
| ... | ... | @@ -4554,14 +4569,12 @@ pub fn addExternFn(self: *MachO, name: []const u8) !u32 { |
| 4554 | 4569 | .where = .undef, |
| 4555 | 4570 | .where_index = sym_index, |
| 4556 | 4571 | }); |
| 4557 | | _ = try self.unresolved.getOrPut(self.base.allocator, sym_index); |
| 4572 | try self.unresolved.putNoClobber(self.base.allocator, sym_index, .stub); |
| 4558 | 4573 | |
| 4559 | 4574 | const stubs_index = @intCast(u32, self.stubs.items.len); |
| 4560 | 4575 | try self.stubs.append(self.base.allocator, sym_index); |
| 4561 | 4576 | try self.stubs_map.putNoClobber(self.base.allocator, sym_index, stubs_index); |
| 4562 | 4577 | |
| 4563 | | // TODO create and write stub, stub_helper and lazy_ptr atoms |
| 4564 | | |
| 4565 | 4578 | return sym_index; |
| 4566 | 4579 | } |
| 4567 | 4580 | |
| ... | ... | @@ -5271,7 +5284,11 @@ fn writeLazyBindInfoTable(self: *MachO) !void { |
| 5271 | 5284 | } |
| 5272 | 5285 | |
| 5273 | 5286 | fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void { |
| 5274 | | if (self.stubs.items.len == 0) return; |
| 5287 | const last_atom = self.blocks.get(.{ |
| 5288 | .seg = self.text_segment_cmd_index.?, |
| 5289 | .sect = self.stub_helper_section_index.?, |
| 5290 | }) orelse return; |
| 5291 | if (last_atom.local_sym_index == self.stub_preamble_sym_index.?) return; |
| 5275 | 5292 | |
| 5276 | 5293 | var stream = std.io.fixedBufferStream(buffer); |
| 5277 | 5294 | var reader = stream.reader(); |
| ... | ... | @@ -5316,7 +5333,6 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void { |
| 5316 | 5333 | else => {}, |
| 5317 | 5334 | } |
| 5318 | 5335 | } |
| 5319 | | assert(self.stubs.items.len <= offsets.items.len); |
| 5320 | 5336 | |
| 5321 | 5337 | const stub_size: u4 = switch (self.base.options.target.cpu.arch) { |
| 5322 | 5338 | .x86_64 => 10, |
| ... | ... | @@ -5329,8 +5345,22 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void { |
| 5329 | 5345 | else => unreachable, |
| 5330 | 5346 | }; |
| 5331 | 5347 | var buf: [@sizeOf(u32)]u8 = undefined; |
| 5348 | |
| 5349 | var first_atom = last_atom; |
| 5350 | while (first_atom.prev) |prev| { |
| 5351 | first_atom = prev; |
| 5352 | } |
| 5353 | |
| 5354 | const start_off = blk: { |
| 5355 | const seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 5356 | const sect = seg.sections.items[self.stub_helper_section_index.?]; |
| 5357 | const sym = self.locals.items[first_atom.next.?.local_sym_index]; |
| 5358 | break :blk sym.n_value - sect.addr + sect.offset; |
| 5359 | }; |
| 5360 | log.warn("start_off = 0x{x}", .{start_off}); |
| 5361 | |
| 5332 | 5362 | for (self.stubs.items) |_, index| { |
| 5333 | | const placeholder_off = self.stub_helper_stubs_start_off.? + index * stub_size + off; |
| 5363 | const placeholder_off = start_off + index * stub_size + off; |
| 5334 | 5364 | mem.writeIntLittle(u32, &buf, offsets.items[index]); |
| 5335 | 5365 | try self.base.file.?.pwriteAll(&buf, placeholder_off); |
| 5336 | 5366 | } |