authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-12-27 21:39:05+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-12-27 21:39:05+01:00
log601ab9a251a7952026fa9c72427344126bf62730
treea111b9df535a32f13c584d78b6259812c960e7e8
parent19056cb6821dd03612628e9220595576878aafe7
parentc0c5acf074a9eba01e892184489ac3e837d739bc
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #14086 from ziglang/issue-14082

macho: ensure LINKEDIT layout follows Apple strict validation rules

5 files changed, 197 insertions(+), 93 deletions(-)

lib/std/build/CheckObjectStep.zig+6-1
...@@ -126,7 +126,7 @@ const Action = struct {...@@ -126,7 +126,7 @@ const Action = struct {
126 /// its reduced, computed value compares using `op` with the expected value, either126 /// its reduced, computed value compares using `op` with the expected value, either
127 /// a literal or another extracted variable.127 /// a literal or another extracted variable.
128 fn computeCmp(act: Action, gpa: Allocator, global_vars: anytype) !bool {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 var values = std.ArrayList(u64).init(gpa);130 var values = std.ArrayList(u64).init(gpa);
131131
132 var it = mem.tokenize(u8, act.phrase, " ");132 var it = mem.tokenize(u8, act.phrase, " ");
...@@ -137,6 +137,8 @@ const Action = struct {...@@ -137,6 +137,8 @@ const Action = struct {
137 try op_stack.append(.sub);137 try op_stack.append(.sub);
138 } else if (mem.eql(u8, next, "%")) {138 } else if (mem.eql(u8, next, "%")) {
139 try op_stack.append(.mod);139 try op_stack.append(.mod);
140 } else if (mem.eql(u8, next, "*")) {
141 try op_stack.append(.mul);
140 } else {142 } else {
141 const val = std.fmt.parseInt(u64, next, 0) catch blk: {143 const val = std.fmt.parseInt(u64, next, 0) catch blk: {
142 break :blk global_vars.get(next) orelse {144 break :blk global_vars.get(next) orelse {
...@@ -167,6 +169,9 @@ const Action = struct {...@@ -167,6 +169,9 @@ const Action = struct {
167 .mod => {169 .mod => {
168 reduced %= other;170 reduced %= other;
169 },171 },
172 .mul => {
173 reduced *= other;
174 },
170 }175 }
171 op_i += 1;176 op_i += 1;
172 }177 }
src/link/MachO.zig+52-30
...@@ -3358,27 +3358,36 @@ fn writeDyldInfoData(self: *MachO) !void {...@@ -3358,27 +3358,36 @@ fn writeDyldInfoData(self: *MachO) !void {
3358 try self.collectExportData(&trie);3358 try self.collectExportData(&trie);
33593359
3360 const link_seg = self.getLinkeditSegmentPtr();3360 const link_seg = self.getLinkeditSegmentPtr();
3361 const rebase_off = mem.alignForwardGeneric(u64, link_seg.fileoff, @alignOf(u64));3361 assert(mem.isAlignedGeneric(u64, link_seg.fileoff, @alignOf(u64)));
3362 assert(rebase_off == link_seg.fileoff);3362 const rebase_off = link_seg.fileoff;
3363 const rebase_size = try bind.rebaseInfoSize(rebase_pointers.items);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 });
33653366
3366 const bind_off = mem.alignForwardGeneric(u64, rebase_off + rebase_size, @alignOf(u64));3367 const bind_off = rebase_off + rebase_size_aligned;
3367 const bind_size = try bind.bindInfoSize(bind_pointers.items);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 });
33693371
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 const lazy_bind_size = try bind.lazyBindInfoSize(lazy_bind_pointers.items);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 });
33733379
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 const export_size = trie.size;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 });
33773384
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 link_seg.filesize = needed_size;3387 link_seg.filesize = needed_size;
3388 assert(mem.isAlignedGeneric(u64, link_seg.fileoff + link_seg.filesize, @alignOf(u64)));
33803389
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 defer gpa.free(buffer);3391 defer gpa.free(buffer);
3383 mem.set(u8, buffer, 0);3392 mem.set(u8, buffer, 0);
33843393
...@@ -3407,13 +3416,13 @@ fn writeDyldInfoData(self: *MachO) !void {...@@ -3407,13 +3416,13 @@ fn writeDyldInfoData(self: *MachO) !void {
3407 try self.populateLazyBindOffsetsInStubHelper(buffer[start..end]);3416 try self.populateLazyBindOffsetsInStubHelper(buffer[start..end]);
34083417
3409 self.dyld_info_cmd.rebase_off = @intCast(u32, rebase_off);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 self.dyld_info_cmd.bind_off = @intCast(u32, bind_off);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 self.dyld_info_cmd.lazy_bind_off = @intCast(u32, lazy_bind_off);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 self.dyld_info_cmd.export_off = @intCast(u32, export_off);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}
34183427
3419fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {3428fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
...@@ -3569,13 +3578,11 @@ fn writeSymtab(self: *MachO) !SymtabCtx {...@@ -3569,13 +3578,11 @@ fn writeSymtab(self: *MachO) !SymtabCtx {
3569 const nsyms = nlocals + nexports + nimports;3578 const nsyms = nlocals + nexports + nimports;
35703579
3571 const seg = self.getLinkeditSegmentPtr();3580 const seg = self.getLinkeditSegmentPtr();
3572 const offset = mem.alignForwardGeneric(3581 const offset = seg.fileoff + seg.filesize;
3573 u64,3582 assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64)));
3574 seg.fileoff + seg.filesize,
3575 @alignOf(macho.nlist_64),
3576 );
3577 const needed_size = nsyms * @sizeOf(macho.nlist_64);3583 const needed_size = nsyms * @sizeOf(macho.nlist_64);
3578 seg.filesize = offset + needed_size - seg.fileoff;3584 seg.filesize = offset + needed_size - seg.fileoff;
3585 assert(mem.isAlignedGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64)));
35793586
3580 var buffer = std.ArrayList(u8).init(gpa);3587 var buffer = std.ArrayList(u8).init(gpa);
3581 defer buffer.deinit();3588 defer buffer.deinit();
...@@ -3599,17 +3606,25 @@ fn writeSymtab(self: *MachO) !SymtabCtx {...@@ -3599,17 +3606,25 @@ fn writeSymtab(self: *MachO) !SymtabCtx {
3599}3606}
36003607
3601fn writeStrtab(self: *MachO) !void {3608fn writeStrtab(self: *MachO) !void {
3609 const gpa = self.base.allocator;
3602 const seg = self.getLinkeditSegmentPtr();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 const needed_size = self.strtab.buffer.items.len;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;
36063616
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 });
36083618
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);
36103625
3611 self.symtab_cmd.stroff = @intCast(u32, offset);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}
36143629
3615const SymtabCtx = struct {3630const SymtabCtx = struct {
...@@ -3628,15 +3643,17 @@ fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void {...@@ -3628,15 +3643,17 @@ fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void {
3628 const iundefsym = iextdefsym + ctx.nextdefsym;3643 const iundefsym = iextdefsym + ctx.nextdefsym;
36293644
3630 const seg = self.getLinkeditSegmentPtr();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 const needed_size = nindirectsyms * @sizeOf(u32);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;
36343651
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 });
36363653
3637 var buf = std.ArrayList(u8).init(gpa);3654 var buf = std.ArrayList(u8).init(gpa);
3638 defer buf.deinit();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 const writer = buf.writer();3657 const writer = buf.writer();
36413658
3642 if (self.stubs_section_index) |sect_id| {3659 if (self.stubs_section_index) |sect_id| {
...@@ -3675,7 +3692,12 @@ fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void {...@@ -3675,7 +3692,12 @@ fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void {
3675 }3692 }
3676 }3693 }
36773694
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 try self.base.file.?.pwriteAll(buf.items, offset);3701 try self.base.file.?.pwriteAll(buf.items, offset);
36803702
3681 self.dysymtab_cmd.nlocalsym = ctx.nlocalsym;3703 self.dysymtab_cmd.nlocalsym = ctx.nlocalsym;
src/link/MachO/zld.zig+75-39
...@@ -2178,25 +2178,34 @@ pub const Zld = struct {...@@ -2178,25 +2178,34 @@ pub const Zld = struct {
2178 try self.collectExportData(&trie);2178 try self.collectExportData(&trie);
21792179
2180 const link_seg = self.getLinkeditSegmentPtr();2180 const link_seg = self.getLinkeditSegmentPtr();
2181 const rebase_off = mem.alignForwardGeneric(u64, link_seg.fileoff, @alignOf(u64));2181 assert(mem.isAlignedGeneric(u64, link_seg.fileoff, @alignOf(u64)));
2182 assert(rebase_off == link_seg.fileoff);2182 const rebase_off = link_seg.fileoff;
2183 const rebase_size = try bind.rebaseInfoSize(rebase_pointers.items);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 });
21852186
2186 const bind_off = mem.alignForwardGeneric(u64, rebase_off + rebase_size, @alignOf(u64));2187 const bind_off = rebase_off + rebase_size_aligned;
2187 const bind_size = try bind.bindInfoSize(bind_pointers.items);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 });
21892191
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 const lazy_bind_size = try bind.lazyBindInfoSize(lazy_bind_pointers.items);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 });
21932199
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 const export_size = trie.size;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 });
21972204
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 link_seg.filesize = needed_size;2207 link_seg.filesize = needed_size;
2208 assert(mem.isAlignedGeneric(u64, link_seg.fileoff + link_seg.filesize, @alignOf(u64)));
22002209
2201 var buffer = try gpa.alloc(u8, needed_size);2210 var buffer = try gpa.alloc(u8, needed_size);
2202 defer gpa.free(buffer);2211 defer gpa.free(buffer);
...@@ -2228,13 +2237,13 @@ pub const Zld = struct {...@@ -2228,13 +2237,13 @@ pub const Zld = struct {
2228 try self.populateLazyBindOffsetsInStubHelper(buffer[offset..][0..size]);2237 try self.populateLazyBindOffsetsInStubHelper(buffer[offset..][0..size]);
22292238
2230 self.dyld_info_cmd.rebase_off = @intCast(u32, rebase_off);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 self.dyld_info_cmd.bind_off = @intCast(u32, bind_off);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 self.dyld_info_cmd.lazy_bind_off = @intCast(u32, lazy_bind_off);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 self.dyld_info_cmd.export_off = @intCast(u32, export_off);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 }
22392248
2240 fn populateLazyBindOffsetsInStubHelper(self: *Zld, buffer: []const u8) !void {2249 fn populateLazyBindOffsetsInStubHelper(self: *Zld, buffer: []const u8) !void {
...@@ -2403,16 +2412,23 @@ pub const Zld = struct {...@@ -2403,16 +2412,23 @@ pub const Zld = struct {
2403 }2412 }
24042413
2405 const link_seg = self.getLinkeditSegmentPtr();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 const needed_size = buffer.items.len;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;
24092425
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 });
24112427
2412 try self.file.pwriteAll(buffer.items, offset);2428 try self.file.pwriteAll(buffer.items, offset);
24132429
2414 self.function_starts_cmd.dataoff = @intCast(u32, offset);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 }
24172433
2418 fn filterDataInCode(2434 fn filterDataInCode(
...@@ -2477,16 +2493,23 @@ pub const Zld = struct {...@@ -2477,16 +2493,23 @@ pub const Zld = struct {
2477 }2493 }
24782494
2479 const seg = self.getLinkeditSegmentPtr();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 const needed_size = out_dice.items.len * @sizeOf(macho.data_in_code_entry);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));
24832506
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 });
24852508
2486 try self.file.pwriteAll(mem.sliceAsBytes(out_dice.items), offset);2509 try self.file.pwriteAll(buffer, offset);
24872510
2488 self.data_in_code_cmd.dataoff = @intCast(u32, offset);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 }
24912514
2492 fn writeSymtabs(self: *Zld) !void {2515 fn writeSymtabs(self: *Zld) !void {
...@@ -2561,13 +2584,11 @@ pub const Zld = struct {...@@ -2561,13 +2584,11 @@ pub const Zld = struct {
2561 const nsyms = nlocals + nexports + nimports;2584 const nsyms = nlocals + nexports + nimports;
25622585
2563 const seg = self.getLinkeditSegmentPtr();2586 const seg = self.getLinkeditSegmentPtr();
2564 const offset = mem.alignForwardGeneric(2587 const offset = seg.fileoff + seg.filesize;
2565 u64,2588 assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64)));
2566 seg.fileoff + seg.filesize,
2567 @alignOf(macho.nlist_64),
2568 );
2569 const needed_size = nsyms * @sizeOf(macho.nlist_64);2589 const needed_size = nsyms * @sizeOf(macho.nlist_64);
2570 seg.filesize = offset + needed_size - seg.fileoff;2590 seg.filesize = offset + needed_size - seg.fileoff;
2591 assert(mem.isAlignedGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64)));
25712592
2572 var buffer = std.ArrayList(u8).init(gpa);2593 var buffer = std.ArrayList(u8).init(gpa);
2573 defer buffer.deinit();2594 defer buffer.deinit();
...@@ -2592,16 +2613,23 @@ pub const Zld = struct {...@@ -2592,16 +2613,23 @@ pub const Zld = struct {
25922613
2593 fn writeStrtab(self: *Zld) !void {2614 fn writeStrtab(self: *Zld) !void {
2594 const seg = self.getLinkeditSegmentPtr();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 const needed_size = self.strtab.buffer.items.len;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 });
25982623
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);
26002628
2601 try self.file.pwriteAll(self.strtab.buffer.items, offset);2629 try self.file.pwriteAll(buffer, offset);
26022630
2603 self.symtab_cmd.stroff = @intCast(u32, offset);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 }
26062634
2607 const SymtabCtx = struct {2635 const SymtabCtx = struct {
...@@ -2620,15 +2648,17 @@ pub const Zld = struct {...@@ -2620,15 +2648,17 @@ pub const Zld = struct {
2620 const iundefsym = iextdefsym + ctx.nextdefsym;2648 const iundefsym = iextdefsym + ctx.nextdefsym;
26212649
2622 const seg = self.getLinkeditSegmentPtr();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 const needed_size = nindirectsyms * @sizeOf(u32);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;
26262656
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 });
26282658
2629 var buf = std.ArrayList(u8).init(gpa);2659 var buf = std.ArrayList(u8).init(gpa);
2630 defer buf.deinit();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 const writer = buf.writer();2662 const writer = buf.writer();
26332663
2634 if (self.getSectionByName("__TEXT", "__stubs")) |sect_id| {2664 if (self.getSectionByName("__TEXT", "__stubs")) |sect_id| {
...@@ -2664,7 +2694,12 @@ pub const Zld = struct {...@@ -2664,7 +2694,12 @@ pub const Zld = struct {
2664 }2694 }
2665 }2695 }
26662696
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 try self.file.pwriteAll(buf.items, offset);2703 try self.file.pwriteAll(buf.items, offset);
26692704
2670 self.dysymtab_cmd.nlocalsym = ctx.nlocalsym;2705 self.dysymtab_cmd.nlocalsym = ctx.nlocalsym;
...@@ -2692,7 +2727,8 @@ pub const Zld = struct {...@@ -2692,7 +2727,8 @@ pub const Zld = struct {
2692 conformUuid(&self.uuid_cmd.uuid);2727 conformUuid(&self.uuid_cmd.uuid);
2693 },2728 },
2694 else => {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);
26962732
2697 const FileSubsection = struct {2733 const FileSubsection = struct {
2698 start: u32,2734 start: u32,
test/link/macho/strict_validation/build.zig+34-15
...@@ -32,13 +32,23 @@ pub fn build(b: *Builder) void {...@@ -32,13 +32,23 @@ pub fn build(b: *Builder) void {
32 check_exe.checkNext("exportoff {exportoff}");32 check_exe.checkNext("exportoff {exportoff}");
33 check_exe.checkNext("exportsize {exportsize}");33 check_exe.checkNext("exportsize {exportsize}");
3434
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 check_exe.checkStart("cmd SYMTAB");43 check_exe.checkStart("cmd SYMTAB");
36 check_exe.checkNext("symoff {symoff}");44 check_exe.checkNext("symoff {symoff}");
45 check_exe.checkNext("nsyms {symnsyms}");
37 check_exe.checkNext("stroff {stroff}");46 check_exe.checkNext("stroff {stroff}");
38 check_exe.checkNext("strsize {strsize}");47 check_exe.checkNext("strsize {strsize}");
3948
40 check_exe.checkStart("cmd DYSYMTAB");49 check_exe.checkStart("cmd DYSYMTAB");
41 check_exe.checkNext("indirectsymoff {dysymoff}");50 check_exe.checkNext("indirectsymoff {dysymoff}");
51 check_exe.checkNext("nindirectsyms {dysymnsyms}");
4252
43 switch (builtin.cpu.arch) {53 switch (builtin.cpu.arch) {
44 .aarch64 => {54 .aarch64 => {
...@@ -50,42 +60,51 @@ pub fn build(b: *Builder) void {...@@ -50,42 +60,51 @@ pub fn build(b: *Builder) void {
50 else => unreachable,60 else => unreachable,
51 }61 }
5262
53 // Next check: DYLD_INFO_ONLY subsections are in order: rebase < bind < lazy < export63 // DYLD_INFO_ONLY subsections are in order: rebase < bind < lazy < export,
54 check_exe.checkComputeCompare("rebaseoff ", .{ .op = .lt, .value = .{ .variable = "bindoff" } });64 // and there are no gaps between them
55 check_exe.checkComputeCompare("bindoff", .{ .op = .lt, .value = .{ .variable = "lazybindoff" } });65 check_exe.checkComputeCompare("rebaseoff rebasesize +", .{ .op = .eq, .value = .{ .variable = "bindoff" } });
56 check_exe.checkComputeCompare("lazybindoff", .{ .op = .lt, .value = .{ .variable = "exportoff" } });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" } });
5777
58 // Next check: DYLD_INFO_ONLY subsections do not overlap78 // DYSYMTAB directly follows SYMTAB (no gap)
59 check_exe.checkComputeCompare("rebaseoff rebasesize +", .{ .op = .lte, .value = .{ .variable = "bindoff" } });79 check_exe.checkComputeCompare("symnsyms 16 symoff * +", .{ .op = .eq, .value = .{ .variable = "dysymoff" } });
60 check_exe.checkComputeCompare("bindoff bindsize +", .{ .op = .lte, .value = .{ .variable = "lazybindoff" } });
61 check_exe.checkComputeCompare("lazybindoff lazybindsize +", .{ .op = .lte, .value = .{ .variable = "exportoff" } });
6280
63 // Next check: we maintain order: symtab < dysymtab < strtab81 // STRTAB follows DYSYMTAB with possible gap
64 check_exe.checkComputeCompare("symoff", .{ .op = .lt, .value = .{ .variable = "dysymoff" } });82 check_exe.checkComputeCompare("dysymnsyms 4 dysymoff * +", .{ .op = .lte, .value = .{ .variable = "stroff" } });
65 check_exe.checkComputeCompare("dysymoff", .{ .op = .lt, .value = .{ .variable = "stroff" } });
6683
67 // Next check: all LINKEDIT sections apart from CODE_SIGNATURE are 8-bytes aligned84 // all LINKEDIT sections apart from CODE_SIGNATURE are 8-bytes aligned
68 check_exe.checkComputeCompare("rebaseoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } });85 check_exe.checkComputeCompare("rebaseoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } });
69 check_exe.checkComputeCompare("bindoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } });86 check_exe.checkComputeCompare("bindoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } });
70 check_exe.checkComputeCompare("lazybindoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } });87 check_exe.checkComputeCompare("lazybindoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } });
71 check_exe.checkComputeCompare("exportoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } });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 check_exe.checkComputeCompare("symoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } });91 check_exe.checkComputeCompare("symoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } });
73 check_exe.checkComputeCompare("stroff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } });92 check_exe.checkComputeCompare("stroff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } });
74 check_exe.checkComputeCompare("dysymoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } });93 check_exe.checkComputeCompare("dysymoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } });
7594
76 switch (builtin.cpu.arch) {95 switch (builtin.cpu.arch) {
77 .aarch64 => {96 .aarch64 => {
78 // Next check: LINKEDIT segment does not extend beyond, or does not include, CODE_SIGNATURE data97 // LINKEDIT segment does not extend beyond, or does not include, CODE_SIGNATURE data
79 check_exe.checkComputeCompare("fileoff filesz codesigoff codesigsize + - -", .{98 check_exe.checkComputeCompare("fileoff filesz codesigoff codesigsize + - -", .{
80 .op = .eq,99 .op = .eq,
81 .value = .{ .literal = 0 },100 .value = .{ .literal = 0 },
82 });101 });
83102
84 // Next check: CODE_SIGNATURE data offset is 16-bytes aligned103 // CODE_SIGNATURE data offset is 16-bytes aligned
85 check_exe.checkComputeCompare("codesigoff 16 %", .{ .op = .eq, .value = .{ .literal = 0 } });104 check_exe.checkComputeCompare("codesigoff 16 %", .{ .op = .eq, .value = .{ .literal = 0 } });
86 },105 },
87 .x86_64 => {106 .x86_64 => {
88 // Next check: LINKEDIT segment does not extend beyond, or does not include, strtab data107 // LINKEDIT segment does not extend beyond, or does not include, strtab data
89 check_exe.checkComputeCompare("fileoff filesz stroff strsize + - -", .{108 check_exe.checkComputeCompare("fileoff filesz stroff strsize + - -", .{
90 .op = .eq,109 .op = .eq,
91 .value = .{ .literal = 0 },110 .value = .{ .literal = 0 },
test/link/macho/uuid/build.zig+30-8
...@@ -5,23 +5,45 @@ const LibExeObjectStep = std.build.LibExeObjStep;...@@ -5,23 +5,45 @@ const LibExeObjectStep = std.build.LibExeObjStep;
5pub fn build(b: *Builder) void {5pub fn build(b: *Builder) void {
6 const test_step = b.step("test", "Test");6 const test_step = b.step("test", "Test");
7 test_step.dependOn(b.getInstallStep());7 test_step.dependOn(b.getInstallStep());
8 testUuid(b, test_step, .ReleaseSafe, "eb1203019e453d808d4f1e71053af9af");8
9 testUuid(b, test_step, .ReleaseFast, "eb1203019e453d808d4f1e71053af9af");9 // We force cross-compilation to ensure we always pick a generic CPU with constant set of CPU features.
10 testUuid(b, test_step, .ReleaseSmall, "eb1203019e453d808d4f1e71053af9af");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}
1228
13fn testUuid(b: *Builder, test_step: *std.build.Step, mode: std.builtin.Mode, comptime exp: []const u8) void {29fn 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 // The calculated UUID value is independent of debug info and so it should36 // The calculated UUID value is independent of debug info and so it should
15 // stay the same across builds.37 // stay the same across builds.
16 {38 {
17 const dylib = simpleDylib(b, mode);39 const dylib = simpleDylib(b, mode, target);
18 const check_dylib = dylib.checkObject(.macho);40 const check_dylib = dylib.checkObject(.macho);
19 check_dylib.checkStart("cmd UUID");41 check_dylib.checkStart("cmd UUID");
20 check_dylib.checkNext("uuid " ++ exp);42 check_dylib.checkNext("uuid " ++ exp);
21 test_step.dependOn(&check_dylib.step);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 dylib.strip = true;47 dylib.strip = true;
26 const check_dylib = dylib.checkObject(.macho);48 const check_dylib = dylib.checkObject(.macho);
27 check_dylib.checkStart("cmd UUID");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,10 +52,10 @@ fn testUuid(b: *Builder, test_step: *std.build.Step, mode: std.builtin.Mode, com
30 }52 }
31}53}
3254
33fn simpleDylib(b: *Builder, mode: std.builtin.Mode) *LibExeObjectStep {55fn simpleDylib(b: *Builder, mode: std.builtin.Mode, target: std.zig.CrossTarget) *LibExeObjectStep {
34 const dylib = b.addSharedLibrary("test", null, b.version(1, 0, 0));56 const dylib = b.addSharedLibrary("test", null, b.version(1, 0, 0));
57 dylib.setTarget(target);
35 dylib.setBuildMode(mode);58 dylib.setBuildMode(mode);
36 dylib.setTarget(.{ .cpu_arch = .aarch64, .os_tag = .macos });
37 dylib.addCSourceFile("test.c", &.{});59 dylib.addCSourceFile("test.c", &.{});
38 dylib.linkLibC();60 dylib.linkLibC();
39 return dylib;61 return dylib;