| author | |
| committer | |
| log | 601ab9a251a7952026fa9c72427344126bf62730 |
| tree | a111b9df535a32f13c584d78b6259812c960e7e8 |
| parent | 19056cb6821dd03612628e9220595576878aafe7 |
| parent | c0c5acf074a9eba01e892184489ac3e837d739bc |
| signature |
macho: ensure LINKEDIT layout follows Apple strict validation rules5 files changed, 197 insertions(+), 93 deletions(-)
lib/std/build/CheckObjectStep.zig+6-1| ... | ... | @@ -126,7 +126,7 @@ const Action = struct { |
| 126 | 126 | /// its reduced, computed value compares using `op` with the expected value, either |
| 127 | 127 | /// a literal or another extracted variable. |
| 128 | 128 | fn computeCmp(act: Action, gpa: Allocator, global_vars: anytype) !bool { |
| 129 | var op_stack = std.ArrayList(enum { add, sub, mod }).init(gpa); | |
| 129 | var op_stack = std.ArrayList(enum { add, sub, mod, mul }).init(gpa); | |
| 130 | 130 | var values = std.ArrayList(u64).init(gpa); |
| 131 | 131 | |
| 132 | 132 | var it = mem.tokenize(u8, act.phrase, " "); |
| ... | ... | @@ -137,6 +137,8 @@ const Action = struct { |
| 137 | 137 | try op_stack.append(.sub); |
| 138 | 138 | } else if (mem.eql(u8, next, "%")) { |
| 139 | 139 | try op_stack.append(.mod); |
| 140 | } else if (mem.eql(u8, next, "*")) { | |
| 141 | try op_stack.append(.mul); | |
| 140 | 142 | } else { |
| 141 | 143 | const val = std.fmt.parseInt(u64, next, 0) catch blk: { |
| 142 | 144 | break :blk global_vars.get(next) orelse { |
| ... | ... | @@ -167,6 +169,9 @@ const Action = struct { |
| 167 | 169 | .mod => { |
| 168 | 170 | reduced %= other; |
| 169 | 171 | }, |
| 172 | .mul => { | |
| 173 | reduced *= other; | |
| 174 | }, | |
| 170 | 175 | } |
| 171 | 176 | op_i += 1; |
| 172 | 177 | } |
src/link/MachO.zig+52-30| ... | ... | @@ -3358,27 +3358,36 @@ fn writeDyldInfoData(self: *MachO) !void { |
| 3358 | 3358 | try self.collectExportData(&trie); |
| 3359 | 3359 | |
| 3360 | 3360 | const link_seg = self.getLinkeditSegmentPtr(); |
| 3361 | const rebase_off = mem.alignForwardGeneric(u64, link_seg.fileoff, @alignOf(u64)); | |
| 3362 | assert(rebase_off == link_seg.fileoff); | |
| 3361 | assert(mem.isAlignedGeneric(u64, link_seg.fileoff, @alignOf(u64))); | |
| 3362 | const rebase_off = link_seg.fileoff; | |
| 3363 | 3363 | const rebase_size = try bind.rebaseInfoSize(rebase_pointers.items); |
| 3364 | log.debug("writing rebase info from 0x{x} to 0x{x}", .{ rebase_off, rebase_off + rebase_size }); | |
| 3364 | const rebase_size_aligned = mem.alignForwardGeneric(u64, rebase_size, @alignOf(u64)); | |
| 3365 | log.debug("writing rebase info from 0x{x} to 0x{x}", .{ rebase_off, rebase_off + rebase_size_aligned }); | |
| 3365 | 3366 | |
| 3366 | const bind_off = mem.alignForwardGeneric(u64, rebase_off + rebase_size, @alignOf(u64)); | |
| 3367 | const bind_off = rebase_off + rebase_size_aligned; | |
| 3367 | 3368 | const bind_size = try bind.bindInfoSize(bind_pointers.items); |
| 3368 | log.debug("writing bind info from 0x{x} to 0x{x}", .{ bind_off, bind_off + bind_size }); | |
| 3369 | const bind_size_aligned = mem.alignForwardGeneric(u64, bind_size, @alignOf(u64)); | |
| 3370 | log.debug("writing bind info from 0x{x} to 0x{x}", .{ bind_off, bind_off + bind_size_aligned }); | |
| 3369 | 3371 | |
| 3370 | const lazy_bind_off = mem.alignForwardGeneric(u64, bind_off + bind_size, @alignOf(u64)); | |
| 3372 | const lazy_bind_off = bind_off + bind_size_aligned; | |
| 3371 | 3373 | const lazy_bind_size = try bind.lazyBindInfoSize(lazy_bind_pointers.items); |
| 3372 | log.debug("writing lazy bind info from 0x{x} to 0x{x}", .{ lazy_bind_off, lazy_bind_off + lazy_bind_size }); | |
| 3374 | const lazy_bind_size_aligned = mem.alignForwardGeneric(u64, lazy_bind_size, @alignOf(u64)); | |
| 3375 | log.debug("writing lazy bind info from 0x{x} to 0x{x}", .{ | |
| 3376 | lazy_bind_off, | |
| 3377 | lazy_bind_off + lazy_bind_size_aligned, | |
| 3378 | }); | |
| 3373 | 3379 | |
| 3374 | const export_off = mem.alignForwardGeneric(u64, lazy_bind_off + lazy_bind_size, @alignOf(u64)); | |
| 3380 | const export_off = lazy_bind_off + lazy_bind_size_aligned; | |
| 3375 | 3381 | const export_size = trie.size; |
| 3376 | log.debug("writing export trie from 0x{x} to 0x{x}", .{ export_off, export_off + export_size }); | |
| 3382 | const export_size_aligned = mem.alignForwardGeneric(u64, export_size, @alignOf(u64)); | |
| 3383 | log.debug("writing export trie from 0x{x} to 0x{x}", .{ export_off, export_off + export_size_aligned }); | |
| 3377 | 3384 | |
| 3378 | const needed_size = export_off + export_size - rebase_off; | |
| 3385 | const needed_size = math.cast(usize, export_off + export_size_aligned - rebase_off) orelse | |
| 3386 | return error.Overflow; | |
| 3379 | 3387 | link_seg.filesize = needed_size; |
| 3388 | assert(mem.isAlignedGeneric(u64, link_seg.fileoff + link_seg.filesize, @alignOf(u64))); | |
| 3380 | 3389 | |
| 3381 | var buffer = try gpa.alloc(u8, math.cast(usize, needed_size) orelse return error.Overflow); | |
| 3390 | var buffer = try gpa.alloc(u8, needed_size); | |
| 3382 | 3391 | defer gpa.free(buffer); |
| 3383 | 3392 | mem.set(u8, buffer, 0); |
| 3384 | 3393 | |
| ... | ... | @@ -3407,13 +3416,13 @@ fn writeDyldInfoData(self: *MachO) !void { |
| 3407 | 3416 | try self.populateLazyBindOffsetsInStubHelper(buffer[start..end]); |
| 3408 | 3417 | |
| 3409 | 3418 | self.dyld_info_cmd.rebase_off = @intCast(u32, rebase_off); |
| 3410 | self.dyld_info_cmd.rebase_size = @intCast(u32, rebase_size); | |
| 3419 | self.dyld_info_cmd.rebase_size = @intCast(u32, rebase_size_aligned); | |
| 3411 | 3420 | self.dyld_info_cmd.bind_off = @intCast(u32, bind_off); |
| 3412 | self.dyld_info_cmd.bind_size = @intCast(u32, bind_size); | |
| 3421 | self.dyld_info_cmd.bind_size = @intCast(u32, bind_size_aligned); | |
| 3413 | 3422 | self.dyld_info_cmd.lazy_bind_off = @intCast(u32, lazy_bind_off); |
| 3414 | self.dyld_info_cmd.lazy_bind_size = @intCast(u32, lazy_bind_size); | |
| 3423 | self.dyld_info_cmd.lazy_bind_size = @intCast(u32, lazy_bind_size_aligned); | |
| 3415 | 3424 | self.dyld_info_cmd.export_off = @intCast(u32, export_off); |
| 3416 | self.dyld_info_cmd.export_size = @intCast(u32, export_size); | |
| 3425 | self.dyld_info_cmd.export_size = @intCast(u32, export_size_aligned); | |
| 3417 | 3426 | } |
| 3418 | 3427 | |
| 3419 | 3428 | fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void { |
| ... | ... | @@ -3569,13 +3578,11 @@ fn writeSymtab(self: *MachO) !SymtabCtx { |
| 3569 | 3578 | const nsyms = nlocals + nexports + nimports; |
| 3570 | 3579 | |
| 3571 | 3580 | const seg = self.getLinkeditSegmentPtr(); |
| 3572 | const offset = mem.alignForwardGeneric( | |
| 3573 | u64, | |
| 3574 | seg.fileoff + seg.filesize, | |
| 3575 | @alignOf(macho.nlist_64), | |
| 3576 | ); | |
| 3581 | const offset = seg.fileoff + seg.filesize; | |
| 3582 | assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64))); | |
| 3577 | 3583 | const needed_size = nsyms * @sizeOf(macho.nlist_64); |
| 3578 | 3584 | seg.filesize = offset + needed_size - seg.fileoff; |
| 3585 | assert(mem.isAlignedGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64))); | |
| 3579 | 3586 | |
| 3580 | 3587 | var buffer = std.ArrayList(u8).init(gpa); |
| 3581 | 3588 | defer buffer.deinit(); |
| ... | ... | @@ -3599,17 +3606,25 @@ fn writeSymtab(self: *MachO) !SymtabCtx { |
| 3599 | 3606 | } |
| 3600 | 3607 | |
| 3601 | 3608 | fn writeStrtab(self: *MachO) !void { |
| 3609 | const gpa = self.base.allocator; | |
| 3602 | 3610 | const seg = self.getLinkeditSegmentPtr(); |
| 3603 | const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64)); | |
| 3611 | const offset = seg.fileoff + seg.filesize; | |
| 3612 | assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64))); | |
| 3604 | 3613 | const needed_size = self.strtab.buffer.items.len; |
| 3605 | seg.filesize = offset + needed_size - seg.fileoff; | |
| 3614 | const needed_size_aligned = mem.alignForwardGeneric(u64, needed_size, @alignOf(u64)); | |
| 3615 | seg.filesize = offset + needed_size_aligned - seg.fileoff; | |
| 3606 | 3616 | |
| 3607 | log.debug("writing string table from 0x{x} to 0x{x}", .{ offset, offset + needed_size }); | |
| 3617 | log.debug("writing string table from 0x{x} to 0x{x}", .{ offset, offset + needed_size_aligned }); | |
| 3608 | 3618 | |
| 3609 | try self.base.file.?.pwriteAll(self.strtab.buffer.items, offset); | |
| 3619 | const buffer = try gpa.alloc(u8, math.cast(usize, needed_size_aligned) orelse return error.Overflow); | |
| 3620 | defer gpa.free(buffer); | |
| 3621 | mem.set(u8, buffer, 0); | |
| 3622 | mem.copy(u8, buffer, self.strtab.buffer.items); | |
| 3623 | ||
| 3624 | try self.base.file.?.pwriteAll(buffer, offset); | |
| 3610 | 3625 | |
| 3611 | 3626 | self.symtab_cmd.stroff = @intCast(u32, offset); |
| 3612 | self.symtab_cmd.strsize = @intCast(u32, needed_size); | |
| 3627 | self.symtab_cmd.strsize = @intCast(u32, needed_size_aligned); | |
| 3613 | 3628 | } |
| 3614 | 3629 | |
| 3615 | 3630 | const SymtabCtx = struct { |
| ... | ... | @@ -3628,15 +3643,17 @@ fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void { |
| 3628 | 3643 | const iundefsym = iextdefsym + ctx.nextdefsym; |
| 3629 | 3644 | |
| 3630 | 3645 | const seg = self.getLinkeditSegmentPtr(); |
| 3631 | const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64)); | |
| 3646 | const offset = seg.fileoff + seg.filesize; | |
| 3647 | assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64))); | |
| 3632 | 3648 | const needed_size = nindirectsyms * @sizeOf(u32); |
| 3633 | seg.filesize = offset + needed_size - seg.fileoff; | |
| 3649 | const needed_size_aligned = mem.alignForwardGeneric(u64, needed_size, @alignOf(u64)); | |
| 3650 | seg.filesize = offset + needed_size_aligned - seg.fileoff; | |
| 3634 | 3651 | |
| 3635 | log.debug("writing indirect symbol table from 0x{x} to 0x{x}", .{ offset, offset + needed_size }); | |
| 3652 | log.debug("writing indirect symbol table from 0x{x} to 0x{x}", .{ offset, offset + needed_size_aligned }); | |
| 3636 | 3653 | |
| 3637 | 3654 | var buf = std.ArrayList(u8).init(gpa); |
| 3638 | 3655 | defer buf.deinit(); |
| 3639 | try buf.ensureTotalCapacity(needed_size); | |
| 3656 | try buf.ensureTotalCapacity(math.cast(usize, needed_size_aligned) orelse return error.Overflow); | |
| 3640 | 3657 | const writer = buf.writer(); |
| 3641 | 3658 | |
| 3642 | 3659 | if (self.stubs_section_index) |sect_id| { |
| ... | ... | @@ -3675,7 +3692,12 @@ fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void { |
| 3675 | 3692 | } |
| 3676 | 3693 | } |
| 3677 | 3694 | |
| 3678 | assert(buf.items.len == needed_size); | |
| 3695 | const padding = math.cast(usize, needed_size_aligned - needed_size) orelse return error.Overflow; | |
| 3696 | if (padding > 0) { | |
| 3697 | buf.appendNTimesAssumeCapacity(0, padding); | |
| 3698 | } | |
| 3699 | ||
| 3700 | assert(buf.items.len == needed_size_aligned); | |
| 3679 | 3701 | try self.base.file.?.pwriteAll(buf.items, offset); |
| 3680 | 3702 | |
| 3681 | 3703 | self.dysymtab_cmd.nlocalsym = ctx.nlocalsym; |
src/link/MachO/zld.zig+75-39| ... | ... | @@ -2178,25 +2178,34 @@ pub const Zld = struct { |
| 2178 | 2178 | try self.collectExportData(&trie); |
| 2179 | 2179 | |
| 2180 | 2180 | const link_seg = self.getLinkeditSegmentPtr(); |
| 2181 | const rebase_off = mem.alignForwardGeneric(u64, link_seg.fileoff, @alignOf(u64)); | |
| 2182 | assert(rebase_off == link_seg.fileoff); | |
| 2181 | assert(mem.isAlignedGeneric(u64, link_seg.fileoff, @alignOf(u64))); | |
| 2182 | const rebase_off = link_seg.fileoff; | |
| 2183 | 2183 | const rebase_size = try bind.rebaseInfoSize(rebase_pointers.items); |
| 2184 | log.debug("writing rebase info from 0x{x} to 0x{x}", .{ rebase_off, rebase_off + rebase_size }); | |
| 2184 | const rebase_size_aligned = mem.alignForwardGeneric(u64, rebase_size, @alignOf(u64)); | |
| 2185 | log.debug("writing rebase info from 0x{x} to 0x{x}", .{ rebase_off, rebase_off + rebase_size_aligned }); | |
| 2185 | 2186 | |
| 2186 | const bind_off = mem.alignForwardGeneric(u64, rebase_off + rebase_size, @alignOf(u64)); | |
| 2187 | const bind_off = rebase_off + rebase_size_aligned; | |
| 2187 | 2188 | const bind_size = try bind.bindInfoSize(bind_pointers.items); |
| 2188 | log.debug("writing bind info from 0x{x} to 0x{x}", .{ bind_off, bind_off + bind_size }); | |
| 2189 | const bind_size_aligned = mem.alignForwardGeneric(u64, bind_size, @alignOf(u64)); | |
| 2190 | log.debug("writing bind info from 0x{x} to 0x{x}", .{ bind_off, bind_off + bind_size_aligned }); | |
| 2189 | 2191 | |
| 2190 | const lazy_bind_off = mem.alignForwardGeneric(u64, bind_off + bind_size, @alignOf(u64)); | |
| 2192 | const lazy_bind_off = bind_off + bind_size_aligned; | |
| 2191 | 2193 | const lazy_bind_size = try bind.lazyBindInfoSize(lazy_bind_pointers.items); |
| 2192 | log.debug("writing lazy bind info from 0x{x} to 0x{x}", .{ lazy_bind_off, lazy_bind_off + lazy_bind_size }); | |
| 2194 | const lazy_bind_size_aligned = mem.alignForwardGeneric(u64, lazy_bind_size, @alignOf(u64)); | |
| 2195 | log.debug("writing lazy bind info from 0x{x} to 0x{x}", .{ | |
| 2196 | lazy_bind_off, | |
| 2197 | lazy_bind_off + lazy_bind_size_aligned, | |
| 2198 | }); | |
| 2193 | 2199 | |
| 2194 | const export_off = mem.alignForwardGeneric(u64, lazy_bind_off + lazy_bind_size, @alignOf(u64)); | |
| 2200 | const export_off = lazy_bind_off + lazy_bind_size_aligned; | |
| 2195 | 2201 | const export_size = trie.size; |
| 2196 | log.debug("writing export trie from 0x{x} to 0x{x}", .{ export_off, export_off + export_size }); | |
| 2202 | const export_size_aligned = mem.alignForwardGeneric(u64, export_size, @alignOf(u64)); | |
| 2203 | log.debug("writing export trie from 0x{x} to 0x{x}", .{ export_off, export_off + export_size_aligned }); | |
| 2197 | 2204 | |
| 2198 | const needed_size = math.cast(usize, export_off + export_size - rebase_off) orelse return error.Overflow; | |
| 2205 | const needed_size = math.cast(usize, export_off + export_size_aligned - rebase_off) orelse | |
| 2206 | return error.Overflow; | |
| 2199 | 2207 | link_seg.filesize = needed_size; |
| 2208 | assert(mem.isAlignedGeneric(u64, link_seg.fileoff + link_seg.filesize, @alignOf(u64))); | |
| 2200 | 2209 | |
| 2201 | 2210 | var buffer = try gpa.alloc(u8, needed_size); |
| 2202 | 2211 | defer gpa.free(buffer); |
| ... | ... | @@ -2228,13 +2237,13 @@ pub const Zld = struct { |
| 2228 | 2237 | try self.populateLazyBindOffsetsInStubHelper(buffer[offset..][0..size]); |
| 2229 | 2238 | |
| 2230 | 2239 | self.dyld_info_cmd.rebase_off = @intCast(u32, rebase_off); |
| 2231 | self.dyld_info_cmd.rebase_size = @intCast(u32, rebase_size); | |
| 2240 | self.dyld_info_cmd.rebase_size = @intCast(u32, rebase_size_aligned); | |
| 2232 | 2241 | self.dyld_info_cmd.bind_off = @intCast(u32, bind_off); |
| 2233 | self.dyld_info_cmd.bind_size = @intCast(u32, bind_size); | |
| 2242 | self.dyld_info_cmd.bind_size = @intCast(u32, bind_size_aligned); | |
| 2234 | 2243 | self.dyld_info_cmd.lazy_bind_off = @intCast(u32, lazy_bind_off); |
| 2235 | self.dyld_info_cmd.lazy_bind_size = @intCast(u32, lazy_bind_size); | |
| 2244 | self.dyld_info_cmd.lazy_bind_size = @intCast(u32, lazy_bind_size_aligned); | |
| 2236 | 2245 | self.dyld_info_cmd.export_off = @intCast(u32, export_off); |
| 2237 | self.dyld_info_cmd.export_size = @intCast(u32, export_size); | |
| 2246 | self.dyld_info_cmd.export_size = @intCast(u32, export_size_aligned); | |
| 2238 | 2247 | } |
| 2239 | 2248 | |
| 2240 | 2249 | fn populateLazyBindOffsetsInStubHelper(self: *Zld, buffer: []const u8) !void { |
| ... | ... | @@ -2403,16 +2412,23 @@ pub const Zld = struct { |
| 2403 | 2412 | } |
| 2404 | 2413 | |
| 2405 | 2414 | const link_seg = self.getLinkeditSegmentPtr(); |
| 2406 | const offset = mem.alignForwardGeneric(u64, link_seg.fileoff + link_seg.filesize, @alignOf(u64)); | |
| 2415 | const offset = link_seg.fileoff + link_seg.filesize; | |
| 2416 | assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64))); | |
| 2407 | 2417 | const needed_size = buffer.items.len; |
| 2408 | link_seg.filesize = offset + needed_size - link_seg.fileoff; | |
| 2418 | const needed_size_aligned = mem.alignForwardGeneric(u64, needed_size, @alignOf(u64)); | |
| 2419 | const padding = math.cast(usize, needed_size_aligned - needed_size) orelse return error.Overflow; | |
| 2420 | if (padding > 0) { | |
| 2421 | try buffer.ensureUnusedCapacity(padding); | |
| 2422 | buffer.appendNTimesAssumeCapacity(0, padding); | |
| 2423 | } | |
| 2424 | link_seg.filesize = offset + needed_size_aligned - link_seg.fileoff; | |
| 2409 | 2425 | |
| 2410 | log.debug("writing function starts info from 0x{x} to 0x{x}", .{ offset, offset + needed_size }); | |
| 2426 | log.debug("writing function starts info from 0x{x} to 0x{x}", .{ offset, offset + needed_size_aligned }); | |
| 2411 | 2427 | |
| 2412 | 2428 | try self.file.pwriteAll(buffer.items, offset); |
| 2413 | 2429 | |
| 2414 | 2430 | self.function_starts_cmd.dataoff = @intCast(u32, offset); |
| 2415 | self.function_starts_cmd.datasize = @intCast(u32, needed_size); | |
| 2431 | self.function_starts_cmd.datasize = @intCast(u32, needed_size_aligned); | |
| 2416 | 2432 | } |
| 2417 | 2433 | |
| 2418 | 2434 | fn filterDataInCode( |
| ... | ... | @@ -2477,16 +2493,23 @@ pub const Zld = struct { |
| 2477 | 2493 | } |
| 2478 | 2494 | |
| 2479 | 2495 | const seg = self.getLinkeditSegmentPtr(); |
| 2480 | const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64)); | |
| 2496 | const offset = seg.fileoff + seg.filesize; | |
| 2497 | assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64))); | |
| 2481 | 2498 | const needed_size = out_dice.items.len * @sizeOf(macho.data_in_code_entry); |
| 2482 | seg.filesize = offset + needed_size - seg.fileoff; | |
| 2499 | const needed_size_aligned = mem.alignForwardGeneric(u64, needed_size, @alignOf(u64)); | |
| 2500 | seg.filesize = offset + needed_size_aligned - seg.fileoff; | |
| 2501 | ||
| 2502 | const buffer = try self.gpa.alloc(u8, math.cast(usize, needed_size_aligned) orelse return error.Overflow); | |
| 2503 | defer self.gpa.free(buffer); | |
| 2504 | mem.set(u8, buffer, 0); | |
| 2505 | mem.copy(u8, buffer, mem.sliceAsBytes(out_dice.items)); | |
| 2483 | 2506 | |
| 2484 | log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ offset, offset + needed_size }); | |
| 2507 | log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ offset, offset + needed_size_aligned }); | |
| 2485 | 2508 | |
| 2486 | try self.file.pwriteAll(mem.sliceAsBytes(out_dice.items), offset); | |
| 2509 | try self.file.pwriteAll(buffer, offset); | |
| 2487 | 2510 | |
| 2488 | 2511 | self.data_in_code_cmd.dataoff = @intCast(u32, offset); |
| 2489 | self.data_in_code_cmd.datasize = @intCast(u32, needed_size); | |
| 2512 | self.data_in_code_cmd.datasize = @intCast(u32, needed_size_aligned); | |
| 2490 | 2513 | } |
| 2491 | 2514 | |
| 2492 | 2515 | fn writeSymtabs(self: *Zld) !void { |
| ... | ... | @@ -2561,13 +2584,11 @@ pub const Zld = struct { |
| 2561 | 2584 | const nsyms = nlocals + nexports + nimports; |
| 2562 | 2585 | |
| 2563 | 2586 | const seg = self.getLinkeditSegmentPtr(); |
| 2564 | const offset = mem.alignForwardGeneric( | |
| 2565 | u64, | |
| 2566 | seg.fileoff + seg.filesize, | |
| 2567 | @alignOf(macho.nlist_64), | |
| 2568 | ); | |
| 2587 | const offset = seg.fileoff + seg.filesize; | |
| 2588 | assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64))); | |
| 2569 | 2589 | const needed_size = nsyms * @sizeOf(macho.nlist_64); |
| 2570 | 2590 | seg.filesize = offset + needed_size - seg.fileoff; |
| 2591 | assert(mem.isAlignedGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64))); | |
| 2571 | 2592 | |
| 2572 | 2593 | var buffer = std.ArrayList(u8).init(gpa); |
| 2573 | 2594 | defer buffer.deinit(); |
| ... | ... | @@ -2592,16 +2613,23 @@ pub const Zld = struct { |
| 2592 | 2613 | |
| 2593 | 2614 | fn writeStrtab(self: *Zld) !void { |
| 2594 | 2615 | const seg = self.getLinkeditSegmentPtr(); |
| 2595 | const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64)); | |
| 2616 | const offset = seg.fileoff + seg.filesize; | |
| 2617 | assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64))); | |
| 2596 | 2618 | const needed_size = self.strtab.buffer.items.len; |
| 2597 | seg.filesize = offset + needed_size - seg.fileoff; | |
| 2619 | const needed_size_aligned = mem.alignForwardGeneric(u64, needed_size, @alignOf(u64)); | |
| 2620 | seg.filesize = offset + needed_size_aligned - seg.fileoff; | |
| 2621 | ||
| 2622 | log.debug("writing string table from 0x{x} to 0x{x}", .{ offset, offset + needed_size_aligned }); | |
| 2598 | 2623 | |
| 2599 | log.debug("writing string table from 0x{x} to 0x{x}", .{ offset, offset + needed_size }); | |
| 2624 | const buffer = try self.gpa.alloc(u8, math.cast(usize, needed_size_aligned) orelse return error.Overflow); | |
| 2625 | defer self.gpa.free(buffer); | |
| 2626 | mem.set(u8, buffer, 0); | |
| 2627 | mem.copy(u8, buffer, self.strtab.buffer.items); | |
| 2600 | 2628 | |
| 2601 | try self.file.pwriteAll(self.strtab.buffer.items, offset); | |
| 2629 | try self.file.pwriteAll(buffer, offset); | |
| 2602 | 2630 | |
| 2603 | 2631 | self.symtab_cmd.stroff = @intCast(u32, offset); |
| 2604 | self.symtab_cmd.strsize = @intCast(u32, needed_size); | |
| 2632 | self.symtab_cmd.strsize = @intCast(u32, needed_size_aligned); | |
| 2605 | 2633 | } |
| 2606 | 2634 | |
| 2607 | 2635 | const SymtabCtx = struct { |
| ... | ... | @@ -2620,15 +2648,17 @@ pub const Zld = struct { |
| 2620 | 2648 | const iundefsym = iextdefsym + ctx.nextdefsym; |
| 2621 | 2649 | |
| 2622 | 2650 | const seg = self.getLinkeditSegmentPtr(); |
| 2623 | const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64)); | |
| 2651 | const offset = seg.fileoff + seg.filesize; | |
| 2652 | assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64))); | |
| 2624 | 2653 | const needed_size = nindirectsyms * @sizeOf(u32); |
| 2625 | seg.filesize = offset + needed_size - seg.fileoff; | |
| 2654 | const needed_size_aligned = mem.alignForwardGeneric(u64, needed_size, @alignOf(u64)); | |
| 2655 | seg.filesize = offset + needed_size_aligned - seg.fileoff; | |
| 2626 | 2656 | |
| 2627 | log.debug("writing indirect symbol table from 0x{x} to 0x{x}", .{ offset, offset + needed_size }); | |
| 2657 | log.debug("writing indirect symbol table from 0x{x} to 0x{x}", .{ offset, offset + needed_size_aligned }); | |
| 2628 | 2658 | |
| 2629 | 2659 | var buf = std.ArrayList(u8).init(gpa); |
| 2630 | 2660 | defer buf.deinit(); |
| 2631 | try buf.ensureTotalCapacity(needed_size); | |
| 2661 | try buf.ensureTotalCapacityPrecise(math.cast(usize, needed_size_aligned) orelse return error.Overflow); | |
| 2632 | 2662 | const writer = buf.writer(); |
| 2633 | 2663 | |
| 2634 | 2664 | if (self.getSectionByName("__TEXT", "__stubs")) |sect_id| { |
| ... | ... | @@ -2664,7 +2694,12 @@ pub const Zld = struct { |
| 2664 | 2694 | } |
| 2665 | 2695 | } |
| 2666 | 2696 | |
| 2667 | assert(buf.items.len == needed_size); | |
| 2697 | const padding = math.cast(usize, needed_size_aligned - needed_size) orelse return error.Overflow; | |
| 2698 | if (padding > 0) { | |
| 2699 | buf.appendNTimesAssumeCapacity(0, padding); | |
| 2700 | } | |
| 2701 | ||
| 2702 | assert(buf.items.len == needed_size_aligned); | |
| 2668 | 2703 | try self.file.pwriteAll(buf.items, offset); |
| 2669 | 2704 | |
| 2670 | 2705 | self.dysymtab_cmd.nlocalsym = ctx.nlocalsym; |
| ... | ... | @@ -2692,7 +2727,8 @@ pub const Zld = struct { |
| 2692 | 2727 | conformUuid(&self.uuid_cmd.uuid); |
| 2693 | 2728 | }, |
| 2694 | 2729 | else => { |
| 2695 | const max_file_end = self.symtab_cmd.stroff + self.symtab_cmd.strsize; | |
| 2730 | // We set the max file size to the actual strtab buffer length to exclude any strtab padding. | |
| 2731 | const max_file_end = @intCast(u32, self.symtab_cmd.stroff + self.strtab.buffer.items.len); | |
| 2696 | 2732 | |
| 2697 | 2733 | const FileSubsection = struct { |
| 2698 | 2734 | start: u32, |
test/link/macho/strict_validation/build.zig+34-15| ... | ... | @@ -32,13 +32,23 @@ pub fn build(b: *Builder) void { |
| 32 | 32 | check_exe.checkNext("exportoff {exportoff}"); |
| 33 | 33 | check_exe.checkNext("exportsize {exportsize}"); |
| 34 | 34 | |
| 35 | check_exe.checkStart("cmd FUNCTION_STARTS"); | |
| 36 | check_exe.checkNext("dataoff {fstartoff}"); | |
| 37 | check_exe.checkNext("datasize {fstartsize}"); | |
| 38 | ||
| 39 | check_exe.checkStart("cmd DATA_IN_CODE"); | |
| 40 | check_exe.checkNext("dataoff {diceoff}"); | |
| 41 | check_exe.checkNext("datasize {dicesize}"); | |
| 42 | ||
| 35 | 43 | check_exe.checkStart("cmd SYMTAB"); |
| 36 | 44 | check_exe.checkNext("symoff {symoff}"); |
| 45 | check_exe.checkNext("nsyms {symnsyms}"); | |
| 37 | 46 | check_exe.checkNext("stroff {stroff}"); |
| 38 | 47 | check_exe.checkNext("strsize {strsize}"); |
| 39 | 48 | |
| 40 | 49 | check_exe.checkStart("cmd DYSYMTAB"); |
| 41 | 50 | check_exe.checkNext("indirectsymoff {dysymoff}"); |
| 51 | check_exe.checkNext("nindirectsyms {dysymnsyms}"); | |
| 42 | 52 | |
| 43 | 53 | switch (builtin.cpu.arch) { |
| 44 | 54 | .aarch64 => { |
| ... | ... | @@ -50,42 +60,51 @@ pub fn build(b: *Builder) void { |
| 50 | 60 | else => unreachable, |
| 51 | 61 | } |
| 52 | 62 | |
| 53 | // Next check: DYLD_INFO_ONLY subsections are in order: rebase < bind < lazy < export | |
| 54 | check_exe.checkComputeCompare("rebaseoff ", .{ .op = .lt, .value = .{ .variable = "bindoff" } }); | |
| 55 | check_exe.checkComputeCompare("bindoff", .{ .op = .lt, .value = .{ .variable = "lazybindoff" } }); | |
| 56 | check_exe.checkComputeCompare("lazybindoff", .{ .op = .lt, .value = .{ .variable = "exportoff" } }); | |
| 63 | // DYLD_INFO_ONLY subsections are in order: rebase < bind < lazy < export, | |
| 64 | // and there are no gaps between them | |
| 65 | check_exe.checkComputeCompare("rebaseoff rebasesize +", .{ .op = .eq, .value = .{ .variable = "bindoff" } }); | |
| 66 | check_exe.checkComputeCompare("bindoff bindsize +", .{ .op = .eq, .value = .{ .variable = "lazybindoff" } }); | |
| 67 | check_exe.checkComputeCompare("lazybindoff lazybindsize +", .{ .op = .eq, .value = .{ .variable = "exportoff" } }); | |
| 68 | ||
| 69 | // FUNCTION_STARTS directly follows DYLD_INFO_ONLY (no gap) | |
| 70 | check_exe.checkComputeCompare("exportoff exportsize +", .{ .op = .eq, .value = .{ .variable = "fstartoff" } }); | |
| 71 | ||
| 72 | // DATA_IN_CODE directly follows FUNCTION_STARTS (no gap) | |
| 73 | check_exe.checkComputeCompare("fstartoff fstartsize +", .{ .op = .eq, .value = .{ .variable = "diceoff" } }); | |
| 74 | ||
| 75 | // SYMTAB directly follows DATA_IN_CODE (no gap) | |
| 76 | check_exe.checkComputeCompare("diceoff dicesize +", .{ .op = .eq, .value = .{ .variable = "symoff" } }); | |
| 57 | 77 | |
| 58 | // Next check: DYLD_INFO_ONLY subsections do not overlap | |
| 59 | check_exe.checkComputeCompare("rebaseoff rebasesize +", .{ .op = .lte, .value = .{ .variable = "bindoff" } }); | |
| 60 | check_exe.checkComputeCompare("bindoff bindsize +", .{ .op = .lte, .value = .{ .variable = "lazybindoff" } }); | |
| 61 | check_exe.checkComputeCompare("lazybindoff lazybindsize +", .{ .op = .lte, .value = .{ .variable = "exportoff" } }); | |
| 78 | // DYSYMTAB directly follows SYMTAB (no gap) | |
| 79 | check_exe.checkComputeCompare("symnsyms 16 symoff * +", .{ .op = .eq, .value = .{ .variable = "dysymoff" } }); | |
| 62 | 80 | |
| 63 | // Next check: we maintain order: symtab < dysymtab < strtab | |
| 64 | check_exe.checkComputeCompare("symoff", .{ .op = .lt, .value = .{ .variable = "dysymoff" } }); | |
| 65 | check_exe.checkComputeCompare("dysymoff", .{ .op = .lt, .value = .{ .variable = "stroff" } }); | |
| 81 | // STRTAB follows DYSYMTAB with possible gap | |
| 82 | check_exe.checkComputeCompare("dysymnsyms 4 dysymoff * +", .{ .op = .lte, .value = .{ .variable = "stroff" } }); | |
| 66 | 83 | |
| 67 | // Next check: all LINKEDIT sections apart from CODE_SIGNATURE are 8-bytes aligned | |
| 84 | // all LINKEDIT sections apart from CODE_SIGNATURE are 8-bytes aligned | |
| 68 | 85 | check_exe.checkComputeCompare("rebaseoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } }); |
| 69 | 86 | check_exe.checkComputeCompare("bindoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } }); |
| 70 | 87 | check_exe.checkComputeCompare("lazybindoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } }); |
| 71 | 88 | check_exe.checkComputeCompare("exportoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } }); |
| 89 | check_exe.checkComputeCompare("fstartoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } }); | |
| 90 | check_exe.checkComputeCompare("diceoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } }); | |
| 72 | 91 | check_exe.checkComputeCompare("symoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } }); |
| 73 | 92 | check_exe.checkComputeCompare("stroff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } }); |
| 74 | 93 | check_exe.checkComputeCompare("dysymoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } }); |
| 75 | 94 | |
| 76 | 95 | switch (builtin.cpu.arch) { |
| 77 | 96 | .aarch64 => { |
| 78 | // Next check: LINKEDIT segment does not extend beyond, or does not include, CODE_SIGNATURE data | |
| 97 | // LINKEDIT segment does not extend beyond, or does not include, CODE_SIGNATURE data | |
| 79 | 98 | check_exe.checkComputeCompare("fileoff filesz codesigoff codesigsize + - -", .{ |
| 80 | 99 | .op = .eq, |
| 81 | 100 | .value = .{ .literal = 0 }, |
| 82 | 101 | }); |
| 83 | 102 | |
| 84 | // Next check: CODE_SIGNATURE data offset is 16-bytes aligned | |
| 103 | // CODE_SIGNATURE data offset is 16-bytes aligned | |
| 85 | 104 | check_exe.checkComputeCompare("codesigoff 16 %", .{ .op = .eq, .value = .{ .literal = 0 } }); |
| 86 | 105 | }, |
| 87 | 106 | .x86_64 => { |
| 88 | // Next check: LINKEDIT segment does not extend beyond, or does not include, strtab data | |
| 107 | // LINKEDIT segment does not extend beyond, or does not include, strtab data | |
| 89 | 108 | check_exe.checkComputeCompare("fileoff filesz stroff strsize + - -", .{ |
| 90 | 109 | .op = .eq, |
| 91 | 110 | .value = .{ .literal = 0 }, |
test/link/macho/uuid/build.zig+30-8| ... | ... | @@ -5,23 +5,45 @@ const LibExeObjectStep = std.build.LibExeObjStep; |
| 5 | 5 | pub fn build(b: *Builder) void { |
| 6 | 6 | const test_step = b.step("test", "Test"); |
| 7 | 7 | test_step.dependOn(b.getInstallStep()); |
| 8 | testUuid(b, test_step, .ReleaseSafe, "eb1203019e453d808d4f1e71053af9af"); | |
| 9 | testUuid(b, test_step, .ReleaseFast, "eb1203019e453d808d4f1e71053af9af"); | |
| 10 | testUuid(b, test_step, .ReleaseSmall, "eb1203019e453d808d4f1e71053af9af"); | |
| 8 | ||
| 9 | // We force cross-compilation to ensure we always pick a generic CPU with constant set of CPU features. | |
| 10 | const aarch64_macos = std.zig.CrossTarget{ | |
| 11 | .cpu_arch = .aarch64, | |
| 12 | .os_tag = .macos, | |
| 13 | }; | |
| 14 | ||
| 15 | testUuid(b, test_step, .ReleaseSafe, aarch64_macos, "46b333df88f5314686fc0cba3b939ca8"); | |
| 16 | testUuid(b, test_step, .ReleaseFast, aarch64_macos, "46b333df88f5314686fc0cba3b939ca8"); | |
| 17 | testUuid(b, test_step, .ReleaseSmall, aarch64_macos, "46b333df88f5314686fc0cba3b939ca8"); | |
| 18 | ||
| 19 | const x86_64_macos = std.zig.CrossTarget{ | |
| 20 | .cpu_arch = .x86_64, | |
| 21 | .os_tag = .macos, | |
| 22 | }; | |
| 23 | ||
| 24 | testUuid(b, test_step, .ReleaseSafe, x86_64_macos, "342ac765194131e1bad5692b9e0e54a4"); | |
| 25 | testUuid(b, test_step, .ReleaseFast, x86_64_macos, "342ac765194131e1bad5692b9e0e54a4"); | |
| 26 | testUuid(b, test_step, .ReleaseSmall, x86_64_macos, "f119310e24773ecf8ec42e09d0379dad"); | |
| 11 | 27 | } |
| 12 | 28 | |
| 13 | fn testUuid(b: *Builder, test_step: *std.build.Step, mode: std.builtin.Mode, comptime exp: []const u8) void { | |
| 29 | fn testUuid( | |
| 30 | b: *Builder, | |
| 31 | test_step: *std.build.Step, | |
| 32 | mode: std.builtin.Mode, | |
| 33 | target: std.zig.CrossTarget, | |
| 34 | comptime exp: []const u8, | |
| 35 | ) void { | |
| 14 | 36 | // The calculated UUID value is independent of debug info and so it should |
| 15 | 37 | // stay the same across builds. |
| 16 | 38 | { |
| 17 | const dylib = simpleDylib(b, mode); | |
| 39 | const dylib = simpleDylib(b, mode, target); | |
| 18 | 40 | const check_dylib = dylib.checkObject(.macho); |
| 19 | 41 | check_dylib.checkStart("cmd UUID"); |
| 20 | 42 | check_dylib.checkNext("uuid " ++ exp); |
| 21 | 43 | test_step.dependOn(&check_dylib.step); |
| 22 | 44 | } |
| 23 | 45 | { |
| 24 | const dylib = simpleDylib(b, mode); | |
| 46 | const dylib = simpleDylib(b, mode, target); | |
| 25 | 47 | dylib.strip = true; |
| 26 | 48 | const check_dylib = dylib.checkObject(.macho); |
| 27 | 49 | check_dylib.checkStart("cmd UUID"); |
| ... | ... | @@ -30,10 +52,10 @@ fn testUuid(b: *Builder, test_step: *std.build.Step, mode: std.builtin.Mode, com |
| 30 | 52 | } |
| 31 | 53 | } |
| 32 | 54 | |
| 33 | fn simpleDylib(b: *Builder, mode: std.builtin.Mode) *LibExeObjectStep { | |
| 55 | fn simpleDylib(b: *Builder, mode: std.builtin.Mode, target: std.zig.CrossTarget) *LibExeObjectStep { | |
| 34 | 56 | const dylib = b.addSharedLibrary("test", null, b.version(1, 0, 0)); |
| 57 | dylib.setTarget(target); | |
| 35 | 58 | dylib.setBuildMode(mode); |
| 36 | dylib.setTarget(.{ .cpu_arch = .aarch64, .os_tag = .macos }); | |
| 37 | 59 | dylib.addCSourceFile("test.c", &.{}); |
| 38 | 60 | dylib.linkLibC(); |
| 39 | 61 | return dylib; |