| author | |
| committer | |
| log | 7b5bd3a93f1c54609cdab19952a6f4b3fc8a20fa |
| tree | 3fed06a5b7ac8802689af1656454ae0267855ca0 |
| parent | 939e4d81e11e801be4eb8fcaddbd27e6b8d3491c |
| parent | eb1050b83aecc3b681901613eeb316030f08ad12 |
| signature |
macho: parallelize UUID hash calculation at the expense of full compatibility with ld649 files changed, 149 insertions(+), 380 deletions(-)
CMakeLists.txt+2| ... | ... | @@ -594,8 +594,10 @@ set(ZIG_STAGE2_SOURCES |
| 594 | 594 | "${CMAKE_SOURCE_DIR}/src/link/MachO/dead_strip.zig" |
| 595 | 595 | "${CMAKE_SOURCE_DIR}/src/link/MachO/eh_frame.zig" |
| 596 | 596 | "${CMAKE_SOURCE_DIR}/src/link/MachO/fat.zig" |
| 597 | "${CMAKE_SOURCE_DIR}/src/link/MachO/hasher.zig" | |
| 597 | 598 | "${CMAKE_SOURCE_DIR}/src/link/MachO/load_commands.zig" |
| 598 | 599 | "${CMAKE_SOURCE_DIR}/src/link/MachO/thunks.zig" |
| 600 | "${CMAKE_SOURCE_DIR}/src/link/MachO/uuid.zig" | |
| 599 | 601 | "${CMAKE_SOURCE_DIR}/src/link/MachO/zld.zig" |
| 600 | 602 | "${CMAKE_SOURCE_DIR}/src/link/Plan9.zig" |
| 601 | 603 | "${CMAKE_SOURCE_DIR}/src/link/Plan9/aout.zig" |
src/link/MachO.zig+14-7| ... | ... | @@ -13,6 +13,7 @@ const mem = std.mem; |
| 13 | 13 | const meta = std.meta; |
| 14 | 14 | |
| 15 | 15 | const aarch64 = @import("../arch/aarch64/bits.zig"); |
| 16 | const calcUuid = @import("MachO/uuid.zig").calcUuid; | |
| 16 | 17 | const codegen = @import("../codegen.zig"); |
| 17 | 18 | const dead_strip = @import("MachO/dead_strip.zig"); |
| 18 | 19 | const fat = @import("MachO/fat.zig"); |
| ... | ... | @@ -756,11 +757,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No |
| 756 | 757 | }); |
| 757 | 758 | try load_commands.writeBuildVersionLC(&self.base.options, lc_writer); |
| 758 | 759 | |
| 759 | if (self.cold_start) { | |
| 760 | std.crypto.random.bytes(&self.uuid_cmd.uuid); | |
| 761 | Md5.hash(&self.uuid_cmd.uuid, &self.uuid_cmd.uuid, .{}); | |
| 762 | conformUuid(&self.uuid_cmd.uuid); | |
| 763 | } | |
| 760 | const uuid_cmd_offset = @sizeOf(macho.mach_header_64) + @intCast(u32, lc_buffer.items.len); | |
| 764 | 761 | try lc_writer.writeStruct(self.uuid_cmd); |
| 765 | 762 | |
| 766 | 763 | try load_commands.writeLoadDylibLCs(self.dylibs.items, self.referenced_dylibs.keys(), lc_writer); |
| ... | ... | @@ -769,10 +766,10 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No |
| 769 | 766 | try lc_writer.writeStruct(self.codesig_cmd); |
| 770 | 767 | } |
| 771 | 768 | |
| 772 | try self.base.file.?.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64)); | |
| 773 | ||
| 774 | 769 | const ncmds = load_commands.calcNumOfLCs(lc_buffer.items); |
| 770 | try self.base.file.?.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64)); | |
| 775 | 771 | try self.writeHeader(ncmds, @intCast(u32, lc_buffer.items.len)); |
| 772 | try self.writeUuid(comp, uuid_cmd_offset, requires_codesig); | |
| 776 | 773 | |
| 777 | 774 | if (codesig) |*csig| { |
| 778 | 775 | try self.writeCodeSignature(comp, csig); // code signing always comes last |
| ... | ... | @@ -3510,6 +3507,16 @@ fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void { |
| 3510 | 3507 | self.dysymtab_cmd.nindirectsyms = nindirectsyms; |
| 3511 | 3508 | } |
| 3512 | 3509 | |
| 3510 | fn writeUuid(self: *MachO, comp: *const Compilation, uuid_cmd_offset: u32, has_codesig: bool) !void { | |
| 3511 | const file_size = if (!has_codesig) blk: { | |
| 3512 | const seg = self.getLinkeditSegmentPtr(); | |
| 3513 | break :blk seg.fileoff + seg.filesize; | |
| 3514 | } else self.codesig_cmd.dataoff; | |
| 3515 | try calcUuid(comp, self.base.file.?, file_size, &self.uuid_cmd.uuid); | |
| 3516 | const offset = uuid_cmd_offset + @sizeOf(macho.load_command); | |
| 3517 | try self.base.file.?.pwriteAll(&self.uuid_cmd.uuid, offset); | |
| 3518 | } | |
| 3519 | ||
| 3513 | 3520 | fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void { |
| 3514 | 3521 | const seg = self.getLinkeditSegmentPtr(); |
| 3515 | 3522 | // Code signature data has to be 16-bytes aligned for Apple tools to recognize the file |
src/link/MachO/CodeSignature.zig+6-59| ... | ... | @@ -7,11 +7,10 @@ const log = std.log.scoped(.link); |
| 7 | 7 | const macho = std.macho; |
| 8 | 8 | const mem = std.mem; |
| 9 | 9 | const testing = std.testing; |
| 10 | const ThreadPool = std.Thread.Pool; | |
| 11 | const WaitGroup = std.Thread.WaitGroup; | |
| 12 | 10 | |
| 13 | 11 | const Allocator = mem.Allocator; |
| 14 | 12 | const Compilation = @import("../../Compilation.zig"); |
| 13 | const Hasher = @import("hasher.zig").ParallelHasher; | |
| 15 | 14 | const Sha256 = std.crypto.hash.sha2.Sha256; |
| 16 | 15 | |
| 17 | 16 | const hash_size = Sha256.digest_length; |
| ... | ... | @@ -289,7 +288,11 @@ pub fn writeAdhocSignature( |
| 289 | 288 | self.code_directory.inner.nCodeSlots = total_pages; |
| 290 | 289 | |
| 291 | 290 | // Calculate hash for each page (in file) and write it to the buffer |
| 292 | try self.parallelHash(gpa, comp.thread_pool, opts.file, opts.file_size); | |
| 291 | var hasher = Hasher(Sha256){ .allocator = gpa, .thread_pool = comp.thread_pool }; | |
| 292 | try hasher.hash(opts.file, self.code_directory.code_slots.items, .{ | |
| 293 | .chunk_size = self.page_size, | |
| 294 | .max_file_size = opts.file_size, | |
| 295 | }); | |
| 293 | 296 | |
| 294 | 297 | try blobs.append(.{ .code_directory = &self.code_directory }); |
| 295 | 298 | header.length += @sizeOf(macho.BlobIndex); |
| ... | ... | @@ -348,62 +351,6 @@ pub fn writeAdhocSignature( |
| 348 | 351 | } |
| 349 | 352 | } |
| 350 | 353 | |
| 351 | fn parallelHash( | |
| 352 | self: *CodeSignature, | |
| 353 | gpa: Allocator, | |
| 354 | pool: *ThreadPool, | |
| 355 | file: fs.File, | |
| 356 | file_size: u32, | |
| 357 | ) !void { | |
| 358 | var wg: WaitGroup = .{}; | |
| 359 | ||
| 360 | const total_num_chunks = mem.alignForward(usize, file_size, self.page_size) / self.page_size; | |
| 361 | assert(self.code_directory.code_slots.items.len >= total_num_chunks); | |
| 362 | ||
| 363 | const buffer = try gpa.alloc(u8, self.page_size * total_num_chunks); | |
| 364 | defer gpa.free(buffer); | |
| 365 | ||
| 366 | const results = try gpa.alloc(fs.File.PReadError!usize, total_num_chunks); | |
| 367 | defer gpa.free(results); | |
| 368 | ||
| 369 | { | |
| 370 | wg.reset(); | |
| 371 | defer wg.wait(); | |
| 372 | ||
| 373 | var i: usize = 0; | |
| 374 | while (i < total_num_chunks) : (i += 1) { | |
| 375 | const fstart = i * self.page_size; | |
| 376 | const fsize = if (fstart + self.page_size > file_size) | |
| 377 | file_size - fstart | |
| 378 | else | |
| 379 | self.page_size; | |
| 380 | wg.start(); | |
| 381 | try pool.spawn(worker, .{ | |
| 382 | file, | |
| 383 | fstart, | |
| 384 | buffer[fstart..][0..fsize], | |
| 385 | &self.code_directory.code_slots.items[i], | |
| 386 | &results[i], | |
| 387 | &wg, | |
| 388 | }); | |
| 389 | } | |
| 390 | } | |
| 391 | for (results) |result| _ = try result; | |
| 392 | } | |
| 393 | ||
| 394 | fn worker( | |
| 395 | file: fs.File, | |
| 396 | fstart: usize, | |
| 397 | buffer: []u8, | |
| 398 | out: *[hash_size]u8, | |
| 399 | err: *fs.File.PReadError!usize, | |
| 400 | wg: *WaitGroup, | |
| 401 | ) void { | |
| 402 | defer wg.finish(); | |
| 403 | err.* = file.preadAll(buffer, fstart); | |
| 404 | Sha256.hash(buffer, out, .{}); | |
| 405 | } | |
| 406 | ||
| 407 | 354 | pub fn size(self: CodeSignature) u32 { |
| 408 | 355 | var ssize: u32 = @sizeOf(macho.SuperBlob) + @sizeOf(macho.BlobIndex) + self.code_directory.size(); |
| 409 | 356 | if (self.requirements) |req| { |
src/link/MachO/hasher.zig created+71| ... | ... | @@ -0,0 +1,71 @@ |
| 1 | const std = @import("std"); | |
| 2 | const assert = std.debug.assert; | |
| 3 | const fs = std.fs; | |
| 4 | const mem = std.mem; | |
| 5 | ||
| 6 | const Allocator = mem.Allocator; | |
| 7 | const ThreadPool = std.Thread.Pool; | |
| 8 | const WaitGroup = std.Thread.WaitGroup; | |
| 9 | ||
| 10 | pub fn ParallelHasher(comptime Hasher: type) type { | |
| 11 | const hash_size = Hasher.digest_length; | |
| 12 | ||
| 13 | return struct { | |
| 14 | allocator: Allocator, | |
| 15 | thread_pool: *ThreadPool, | |
| 16 | ||
| 17 | pub fn hash(self: Self, file: fs.File, out: [][hash_size]u8, opts: struct { | |
| 18 | chunk_size: u64 = 0x4000, | |
| 19 | max_file_size: ?u64 = null, | |
| 20 | }) !void { | |
| 21 | var wg: WaitGroup = .{}; | |
| 22 | ||
| 23 | const file_size = blk: { | |
| 24 | const file_size = opts.max_file_size orelse try file.getEndPos(); | |
| 25 | break :blk std.math.cast(usize, file_size) orelse return error.Overflow; | |
| 26 | }; | |
| 27 | const chunk_size = std.math.cast(usize, opts.chunk_size) orelse return error.Overflow; | |
| 28 | ||
| 29 | const buffer = try self.allocator.alloc(u8, chunk_size * out.len); | |
| 30 | defer self.allocator.free(buffer); | |
| 31 | ||
| 32 | const results = try self.allocator.alloc(fs.File.PReadError!usize, out.len); | |
| 33 | defer self.allocator.free(results); | |
| 34 | ||
| 35 | { | |
| 36 | wg.reset(); | |
| 37 | defer wg.wait(); | |
| 38 | ||
| 39 | for (out, results, 0..) |*out_buf, *result, i| { | |
| 40 | const fstart = i * chunk_size; | |
| 41 | const fsize = if (fstart + chunk_size > file_size) file_size - fstart else chunk_size; | |
| 42 | wg.start(); | |
| 43 | try self.thread_pool.spawn(worker, .{ | |
| 44 | file, | |
| 45 | fstart, | |
| 46 | buffer[fstart..][0..fsize], | |
| 47 | &(out_buf.*), | |
| 48 | &(result.*), | |
| 49 | &wg, | |
| 50 | }); | |
| 51 | } | |
| 52 | } | |
| 53 | for (results) |result| _ = try result; | |
| 54 | } | |
| 55 | ||
| 56 | fn worker( | |
| 57 | file: fs.File, | |
| 58 | fstart: usize, | |
| 59 | buffer: []u8, | |
| 60 | out: *[hash_size]u8, | |
| 61 | err: *fs.File.PReadError!usize, | |
| 62 | wg: *WaitGroup, | |
| 63 | ) void { | |
| 64 | defer wg.finish(); | |
| 65 | err.* = file.preadAll(buffer, fstart); | |
| 66 | Hasher.hash(buffer, out, .{}); | |
| 67 | } | |
| 68 | ||
| 69 | const Self = @This(); | |
| 70 | }; | |
| 71 | } |
src/link/MachO/uuid.zig created+45| ... | ... | @@ -0,0 +1,45 @@ |
| 1 | const std = @import("std"); | |
| 2 | const fs = std.fs; | |
| 3 | const mem = std.mem; | |
| 4 | ||
| 5 | const Allocator = mem.Allocator; | |
| 6 | const Compilation = @import("../../Compilation.zig"); | |
| 7 | const Md5 = std.crypto.hash.Md5; | |
| 8 | const Hasher = @import("hasher.zig").ParallelHasher; | |
| 9 | ||
| 10 | /// Calculates Md5 hash of each chunk in parallel and then hashes all Md5 hashes to produce | |
| 11 | /// the final digest. | |
| 12 | /// While this is NOT a correct MD5 hash of the contents, this methodology is used by LLVM/LLD | |
| 13 | /// and we will use it too as it seems accepted by Apple OSes. | |
| 14 | /// TODO LLD also hashes the output filename to disambiguate between same builds with different | |
| 15 | /// output files. Should we also do that? | |
| 16 | pub fn calcUuid(comp: *const Compilation, file: fs.File, file_size: u64, out: *[Md5.digest_length]u8) !void { | |
| 17 | const num_chunks = comp.thread_pool.threads.len * 0x10; | |
| 18 | const chunk_size = @divTrunc(file_size, num_chunks); | |
| 19 | const actual_num_chunks = if (@rem(file_size, num_chunks) > 0) num_chunks + 1 else num_chunks; | |
| 20 | ||
| 21 | const hashes = try comp.gpa.alloc([Md5.digest_length]u8, actual_num_chunks); | |
| 22 | defer comp.gpa.free(hashes); | |
| 23 | ||
| 24 | var hasher = Hasher(Md5){ .allocator = comp.gpa, .thread_pool = comp.thread_pool }; | |
| 25 | try hasher.hash(file, hashes, .{ | |
| 26 | .chunk_size = chunk_size, | |
| 27 | .max_file_size = file_size, | |
| 28 | }); | |
| 29 | ||
| 30 | const final_buffer = try comp.gpa.alloc(u8, actual_num_chunks * Md5.digest_length); | |
| 31 | defer comp.gpa.free(final_buffer); | |
| 32 | ||
| 33 | for (hashes, 0..) |hash, i| { | |
| 34 | mem.copy(u8, final_buffer[i * Md5.digest_length ..][0..Md5.digest_length], &hash); | |
| 35 | } | |
| 36 | ||
| 37 | Md5.hash(final_buffer, out, .{}); | |
| 38 | conform(out); | |
| 39 | } | |
| 40 | ||
| 41 | inline fn conform(out: *[Md5.digest_length]u8) void { | |
| 42 | // LC_UUID uuids should conform to RFC 4122 UUID version 4 & UUID version 5 formats | |
| 43 | out[6] = (out[6] & 0x0F) | (3 << 4); | |
| 44 | out[8] = (out[8] & 0x3F) | 0x80; | |
| 45 | } |
src/link/MachO/zld.zig+11-159| ... | ... | @@ -9,14 +9,15 @@ const math = std.math; |
| 9 | 9 | const mem = std.mem; |
| 10 | 10 | |
| 11 | 11 | const aarch64 = @import("../../arch/aarch64/bits.zig"); |
| 12 | const calcUuid = @import("uuid.zig").calcUuid; | |
| 12 | 13 | const dead_strip = @import("dead_strip.zig"); |
| 13 | 14 | const eh_frame = @import("eh_frame.zig"); |
| 14 | 15 | const fat = @import("fat.zig"); |
| 15 | 16 | const link = @import("../../link.zig"); |
| 16 | 17 | const load_commands = @import("load_commands.zig"); |
| 18 | const stub_helpers = @import("stubs.zig"); | |
| 17 | 19 | const thunks = @import("thunks.zig"); |
| 18 | 20 | const trace = @import("../../tracy.zig").trace; |
| 19 | const stub_helpers = @import("stubs.zig"); | |
| 20 | 21 | |
| 21 | 22 | const Allocator = mem.Allocator; |
| 22 | 23 | const Archive = @import("Archive.zig"); |
| ... | ... | @@ -2575,150 +2576,14 @@ pub const Zld = struct { |
| 2575 | 2576 | self.dysymtab_cmd.nindirectsyms = nindirectsyms; |
| 2576 | 2577 | } |
| 2577 | 2578 | |
| 2578 | fn writeUuid(self: *Zld, comp: *const Compilation, args: struct { | |
| 2579 | linkedit_cmd_offset: u32, | |
| 2580 | symtab_cmd_offset: u32, | |
| 2581 | uuid_cmd_offset: u32, | |
| 2582 | codesig_cmd_offset: ?u32, | |
| 2583 | }) !void { | |
| 2584 | _ = comp; | |
| 2585 | switch (self.options.optimize_mode) { | |
| 2586 | .Debug => { | |
| 2587 | // In Debug we don't really care about reproducibility, so put in a random value | |
| 2588 | // and be done with it. | |
| 2589 | std.crypto.random.bytes(&self.uuid_cmd.uuid); | |
| 2590 | Md5.hash(&self.uuid_cmd.uuid, &self.uuid_cmd.uuid, .{}); | |
| 2591 | conformUuid(&self.uuid_cmd.uuid); | |
| 2592 | }, | |
| 2593 | else => { | |
| 2594 | // We set the max file size to the actual strtab buffer length to exclude any strtab padding. | |
| 2595 | const max_file_end = @intCast(u32, self.symtab_cmd.stroff + self.strtab.buffer.items.len); | |
| 2596 | ||
| 2597 | const FileSubsection = struct { | |
| 2598 | start: u32, | |
| 2599 | end: u32, | |
| 2600 | }; | |
| 2601 | ||
| 2602 | var subsections: [5]FileSubsection = undefined; | |
| 2603 | var count: usize = 0; | |
| 2604 | ||
| 2605 | // Exclude LINKEDIT segment command as it contains file size that includes stabs contribution | |
| 2606 | // and code signature. | |
| 2607 | subsections[count] = .{ | |
| 2608 | .start = 0, | |
| 2609 | .end = args.linkedit_cmd_offset, | |
| 2610 | }; | |
| 2611 | count += 1; | |
| 2612 | ||
| 2613 | // Exclude SYMTAB and DYSYMTAB commands for the same reason. | |
| 2614 | subsections[count] = .{ | |
| 2615 | .start = subsections[count - 1].end + @sizeOf(macho.segment_command_64), | |
| 2616 | .end = args.symtab_cmd_offset, | |
| 2617 | }; | |
| 2618 | count += 1; | |
| 2619 | ||
| 2620 | // Exclude CODE_SIGNATURE command (if present). | |
| 2621 | if (args.codesig_cmd_offset) |offset| { | |
| 2622 | subsections[count] = .{ | |
| 2623 | .start = subsections[count - 1].end + @sizeOf(macho.symtab_command) + @sizeOf(macho.dysymtab_command), | |
| 2624 | .end = offset, | |
| 2625 | }; | |
| 2626 | count += 1; | |
| 2627 | } | |
| 2628 | ||
| 2629 | if (!self.options.strip) { | |
| 2630 | // Exclude region comprising all symbol stabs. | |
| 2631 | const nlocals = self.dysymtab_cmd.nlocalsym; | |
| 2632 | ||
| 2633 | const locals = try self.gpa.alloc(macho.nlist_64, nlocals); | |
| 2634 | defer self.gpa.free(locals); | |
| 2635 | ||
| 2636 | const locals_buf = @ptrCast([*]u8, locals.ptr)[0 .. @sizeOf(macho.nlist_64) * nlocals]; | |
| 2637 | const amt = try self.file.preadAll(locals_buf, self.symtab_cmd.symoff); | |
| 2638 | if (amt != locals_buf.len) return error.InputOutput; | |
| 2639 | ||
| 2640 | const istab: usize = for (locals, 0..) |local, i| { | |
| 2641 | if (local.stab()) break i; | |
| 2642 | } else locals.len; | |
| 2643 | const nstabs = locals.len - istab; | |
| 2644 | ||
| 2645 | if (nstabs == 0) { | |
| 2646 | subsections[count] = .{ | |
| 2647 | .start = subsections[count - 1].end + if (args.codesig_cmd_offset == null) | |
| 2648 | @as(u32, @sizeOf(macho.symtab_command) + @sizeOf(macho.dysymtab_command)) | |
| 2649 | else | |
| 2650 | @sizeOf(macho.linkedit_data_command), | |
| 2651 | .end = max_file_end, | |
| 2652 | }; | |
| 2653 | count += 1; | |
| 2654 | } else { | |
| 2655 | // Exclude a subsection of the strtab with names of the stabs. | |
| 2656 | // We do not care about anything succeeding strtab as it is the code signature data which is | |
| 2657 | // not part of the UUID calculation anyway. | |
| 2658 | const stab_stroff = locals[istab].n_strx; | |
| 2659 | ||
| 2660 | subsections[count] = .{ | |
| 2661 | .start = subsections[count - 1].end + if (args.codesig_cmd_offset == null) | |
| 2662 | @as(u32, @sizeOf(macho.symtab_command) + @sizeOf(macho.dysymtab_command)) | |
| 2663 | else | |
| 2664 | @sizeOf(macho.linkedit_data_command), | |
| 2665 | .end = @intCast(u32, self.symtab_cmd.symoff + istab * @sizeOf(macho.nlist_64)), | |
| 2666 | }; | |
| 2667 | count += 1; | |
| 2668 | ||
| 2669 | subsections[count] = .{ | |
| 2670 | .start = subsections[count - 1].end + @intCast(u32, nstabs * @sizeOf(macho.nlist_64)), | |
| 2671 | .end = self.symtab_cmd.stroff + stab_stroff, | |
| 2672 | }; | |
| 2673 | count += 1; | |
| 2674 | } | |
| 2675 | } else { | |
| 2676 | subsections[count] = .{ | |
| 2677 | .start = subsections[count - 1].end + if (args.codesig_cmd_offset == null) | |
| 2678 | @as(u32, @sizeOf(macho.symtab_command) + @sizeOf(macho.dysymtab_command)) | |
| 2679 | else | |
| 2680 | @sizeOf(macho.linkedit_data_command), | |
| 2681 | .end = max_file_end, | |
| 2682 | }; | |
| 2683 | count += 1; | |
| 2684 | } | |
| 2685 | ||
| 2686 | const chunk_size = 0x4000; | |
| 2687 | ||
| 2688 | var hasher = Md5.init(.{}); | |
| 2689 | var buffer: [chunk_size]u8 = undefined; | |
| 2690 | ||
| 2691 | for (subsections[0..count]) |cut| { | |
| 2692 | const size = cut.end - cut.start; | |
| 2693 | const num_chunks = mem.alignForward(usize, size, chunk_size) / chunk_size; | |
| 2694 | ||
| 2695 | var i: usize = 0; | |
| 2696 | while (i < num_chunks) : (i += 1) { | |
| 2697 | const fstart = cut.start + i * chunk_size; | |
| 2698 | const fsize = if (fstart + chunk_size > cut.end) | |
| 2699 | cut.end - fstart | |
| 2700 | else | |
| 2701 | chunk_size; | |
| 2702 | const amt = try self.file.preadAll(buffer[0..fsize], fstart); | |
| 2703 | if (amt != fsize) return error.InputOutput; | |
| 2704 | ||
| 2705 | hasher.update(buffer[0..fsize]); | |
| 2706 | } | |
| 2707 | } | |
| 2708 | ||
| 2709 | hasher.final(&self.uuid_cmd.uuid); | |
| 2710 | conformUuid(&self.uuid_cmd.uuid); | |
| 2711 | }, | |
| 2712 | } | |
| 2713 | ||
| 2714 | const in_file = args.uuid_cmd_offset + @sizeOf(macho.load_command); | |
| 2715 | try self.file.pwriteAll(&self.uuid_cmd.uuid, in_file); | |
| 2716 | } | |
| 2717 | ||
| 2718 | inline fn conformUuid(out: *[Md5.digest_length]u8) void { | |
| 2719 | // LC_UUID uuids should conform to RFC 4122 UUID version 4 & UUID version 5 formats | |
| 2720 | out[6] = (out[6] & 0x0F) | (3 << 4); | |
| 2721 | out[8] = (out[8] & 0x3F) | 0x80; | |
| 2579 | fn writeUuid(self: *Zld, comp: *const Compilation, uuid_cmd_offset: u32, has_codesig: bool) !void { | |
| 2580 | const file_size = if (!has_codesig) blk: { | |
| 2581 | const seg = self.getLinkeditSegmentPtr(); | |
| 2582 | break :blk seg.fileoff + seg.filesize; | |
| 2583 | } else self.codesig_cmd.dataoff; | |
| 2584 | try calcUuid(comp, self.file, file_size, &self.uuid_cmd.uuid); | |
| 2585 | const offset = uuid_cmd_offset + @sizeOf(macho.load_command); | |
| 2586 | try self.file.pwriteAll(&self.uuid_cmd.uuid, offset); | |
| 2722 | 2587 | } |
| 2723 | 2588 | |
| 2724 | 2589 | fn writeCodeSignaturePadding(self: *Zld, code_sig: *CodeSignature) !void { |
| ... | ... | @@ -4041,16 +3906,11 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr |
| 4041 | 3906 | const lc_writer = lc_buffer.writer(); |
| 4042 | 3907 | |
| 4043 | 3908 | try zld.writeSegmentHeaders(lc_writer); |
| 4044 | const linkedit_cmd_offset = @sizeOf(macho.mach_header_64) + @intCast(u32, lc_buffer.items.len - @sizeOf(macho.segment_command_64)); | |
| 4045 | ||
| 4046 | 3909 | try lc_writer.writeStruct(zld.dyld_info_cmd); |
| 4047 | 3910 | try lc_writer.writeStruct(zld.function_starts_cmd); |
| 4048 | 3911 | try lc_writer.writeStruct(zld.data_in_code_cmd); |
| 4049 | ||
| 4050 | const symtab_cmd_offset = @sizeOf(macho.mach_header_64) + @intCast(u32, lc_buffer.items.len); | |
| 4051 | 3912 | try lc_writer.writeStruct(zld.symtab_cmd); |
| 4052 | 3913 | try lc_writer.writeStruct(zld.dysymtab_cmd); |
| 4053 | ||
| 4054 | 3914 | try load_commands.writeDylinkerLC(lc_writer); |
| 4055 | 3915 | |
| 4056 | 3916 | if (zld.options.output_mode == .Exe) { |
| ... | ... | @@ -4088,22 +3948,14 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr |
| 4088 | 3948 | |
| 4089 | 3949 | try load_commands.writeLoadDylibLCs(zld.dylibs.items, zld.referenced_dylibs.keys(), lc_writer); |
| 4090 | 3950 | |
| 4091 | var codesig_cmd_offset: ?u32 = null; | |
| 4092 | 3951 | if (requires_codesig) { |
| 4093 | codesig_cmd_offset = @sizeOf(macho.mach_header_64) + @intCast(u32, lc_buffer.items.len); | |
| 4094 | 3952 | try lc_writer.writeStruct(zld.codesig_cmd); |
| 4095 | 3953 | } |
| 4096 | 3954 | |
| 4097 | 3955 | const ncmds = load_commands.calcNumOfLCs(lc_buffer.items); |
| 4098 | 3956 | try zld.file.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64)); |
| 4099 | 3957 | try zld.writeHeader(ncmds, @intCast(u32, lc_buffer.items.len)); |
| 4100 | ||
| 4101 | try zld.writeUuid(comp, .{ | |
| 4102 | .linkedit_cmd_offset = linkedit_cmd_offset, | |
| 4103 | .symtab_cmd_offset = symtab_cmd_offset, | |
| 4104 | .uuid_cmd_offset = uuid_cmd_offset, | |
| 4105 | .codesig_cmd_offset = codesig_cmd_offset, | |
| 4106 | }); | |
| 3958 | try zld.writeUuid(comp, uuid_cmd_offset, requires_codesig); | |
| 4107 | 3959 | |
| 4108 | 3960 | if (codesig) |*csig| { |
| 4109 | 3961 | try zld.writeCodeSignature(comp, csig); // code signing always comes last |
test/link.zig-9| ... | ... | @@ -164,15 +164,6 @@ pub const cases = [_]Case{ |
| 164 | 164 | .build_root = "test/link/macho/unwind_info", |
| 165 | 165 | .import = @import("link/macho/unwind_info/build.zig"), |
| 166 | 166 | }, |
| 167 | // TODO: re-enable this test. It currently has some incompatibilities with | |
| 168 | // the new build system API. In particular, it depends on installing the build | |
| 169 | // artifacts, which should be unnecessary, and it has a custom build step that | |
| 170 | // prints directly to stderr instead of failing the step with an error message. | |
| 171 | //.{ | |
| 172 | // .build_root = "test/link/macho/uuid", | |
| 173 | // .import = @import("link/macho/uuid/build.zig"), | |
| 174 | //}, | |
| 175 | ||
| 176 | 167 | .{ |
| 177 | 168 | .build_root = "test/link/macho/weak_library", |
| 178 | 169 | .import = @import("link/macho/weak_library/build.zig"), |
test/link/macho/uuid/build.zig deleted-144| ... | ... | @@ -1,144 +0,0 @@ |
| 1 | const std = @import("std"); | |
| 2 | const FileSource = std.Build.FileSource; | |
| 3 | const Step = std.Build.Step; | |
| 4 | ||
| 5 | pub const requires_symlinks = true; | |
| 6 | ||
| 7 | pub fn build(b: *std.Build) void { | |
| 8 | const test_step = b.step("test", "Test"); | |
| 9 | b.default_step = test_step; | |
| 10 | ||
| 11 | // We force cross-compilation to ensure we always pick a generic CPU with | |
| 12 | // constant set of CPU features. | |
| 13 | const aarch64_macos = std.zig.CrossTarget{ | |
| 14 | .cpu_arch = .aarch64, | |
| 15 | .os_tag = .macos, | |
| 16 | }; | |
| 17 | ||
| 18 | testUuid(b, test_step, .ReleaseSafe, aarch64_macos); | |
| 19 | testUuid(b, test_step, .ReleaseFast, aarch64_macos); | |
| 20 | testUuid(b, test_step, .ReleaseSmall, aarch64_macos); | |
| 21 | ||
| 22 | const x86_64_macos = std.zig.CrossTarget{ | |
| 23 | .cpu_arch = .x86_64, | |
| 24 | .os_tag = .macos, | |
| 25 | }; | |
| 26 | ||
| 27 | testUuid(b, test_step, .ReleaseSafe, x86_64_macos); | |
| 28 | testUuid(b, test_step, .ReleaseFast, x86_64_macos); | |
| 29 | testUuid(b, test_step, .ReleaseSmall, x86_64_macos); | |
| 30 | } | |
| 31 | ||
| 32 | fn testUuid( | |
| 33 | b: *std.Build, | |
| 34 | test_step: *std.Build.Step, | |
| 35 | optimize: std.builtin.OptimizeMode, | |
| 36 | target: std.zig.CrossTarget, | |
| 37 | ) void { | |
| 38 | // The calculated UUID value is independent of debug info and so it should | |
| 39 | // stay the same across builds. | |
| 40 | { | |
| 41 | const dylib = simpleDylib(b, optimize, target); | |
| 42 | const install_step = b.addInstallArtifact(dylib); | |
| 43 | install_step.dest_sub_path = "test1.dylib"; | |
| 44 | install_step.step.dependOn(&dylib.step); | |
| 45 | } | |
| 46 | { | |
| 47 | const dylib = simpleDylib(b, optimize, target); | |
| 48 | dylib.strip = true; | |
| 49 | const install_step = b.addInstallArtifact(dylib); | |
| 50 | install_step.dest_sub_path = "test2.dylib"; | |
| 51 | install_step.step.dependOn(&dylib.step); | |
| 52 | } | |
| 53 | ||
| 54 | const cmp_step = CompareUuid.create(b, "test1.dylib", "test2.dylib"); | |
| 55 | test_step.dependOn(&cmp_step.step); | |
| 56 | } | |
| 57 | ||
| 58 | fn simpleDylib( | |
| 59 | b: *std.Build, | |
| 60 | optimize: std.builtin.OptimizeMode, | |
| 61 | target: std.zig.CrossTarget, | |
| 62 | ) *std.Build.Step.Compile { | |
| 63 | const dylib = b.addSharedLibrary(.{ | |
| 64 | .name = "test", | |
| 65 | .version = .{ .major = 1, .minor = 0, .patch = 0 }, | |
| 66 | .optimize = optimize, | |
| 67 | .target = target, | |
| 68 | }); | |
| 69 | dylib.addCSourceFile("test.c", &.{}); | |
| 70 | dylib.linkLibC(); | |
| 71 | return dylib; | |
| 72 | } | |
| 73 | ||
| 74 | const CompareUuid = struct { | |
| 75 | pub const base_id = .custom; | |
| 76 | ||
| 77 | step: Step, | |
| 78 | lhs: []const u8, | |
| 79 | rhs: []const u8, | |
| 80 | ||
| 81 | pub fn create(owner: *std.Build, lhs: []const u8, rhs: []const u8) *CompareUuid { | |
| 82 | const self = owner.allocator.create(CompareUuid) catch @panic("OOM"); | |
| 83 | self.* = CompareUuid{ | |
| 84 | .step = Step.init(.{ | |
| 85 | .id = base_id, | |
| 86 | .name = owner.fmt("compare uuid: {s} and {s}", .{ | |
| 87 | lhs, | |
| 88 | rhs, | |
| 89 | }), | |
| 90 | .owner = owner, | |
| 91 | .makeFn = make, | |
| 92 | }), | |
| 93 | .lhs = lhs, | |
| 94 | .rhs = rhs, | |
| 95 | }; | |
| 96 | return self; | |
| 97 | } | |
| 98 | ||
| 99 | fn make(step: *Step, prog_node: *std.Progress.Node) anyerror!void { | |
| 100 | _ = prog_node; | |
| 101 | const b = step.owner; | |
| 102 | const self = @fieldParentPtr(CompareUuid, "step", step); | |
| 103 | const gpa = b.allocator; | |
| 104 | ||
| 105 | var lhs_uuid: [16]u8 = undefined; | |
| 106 | const lhs_path = b.getInstallPath(.lib, self.lhs); | |
| 107 | try parseUuid(gpa, lhs_path, &lhs_uuid); | |
| 108 | ||
| 109 | var rhs_uuid: [16]u8 = undefined; | |
| 110 | const rhs_path = b.getInstallPath(.lib, self.rhs); | |
| 111 | try parseUuid(gpa, rhs_path, &rhs_uuid); | |
| 112 | ||
| 113 | try std.testing.expectEqualStrings(&lhs_uuid, &rhs_uuid); | |
| 114 | } | |
| 115 | ||
| 116 | fn parseUuid(gpa: std.mem.Allocator, path: []const u8, uuid: *[16]u8) anyerror!void { | |
| 117 | const max_bytes: usize = 20 * 1024 * 1024; | |
| 118 | const data = try std.fs.cwd().readFileAllocOptions( | |
| 119 | gpa, | |
| 120 | path, | |
| 121 | max_bytes, | |
| 122 | null, | |
| 123 | @alignOf(u64), | |
| 124 | null, | |
| 125 | ); | |
| 126 | var stream = std.io.fixedBufferStream(data); | |
| 127 | const reader = stream.reader(); | |
| 128 | ||
| 129 | const hdr = try reader.readStruct(std.macho.mach_header_64); | |
| 130 | if (hdr.magic != std.macho.MH_MAGIC_64) { | |
| 131 | return error.InvalidMagicNumber; | |
| 132 | } | |
| 133 | ||
| 134 | var it = std.macho.LoadCommandIterator{ | |
| 135 | .ncmds = hdr.ncmds, | |
| 136 | .buffer = data[@sizeOf(std.macho.mach_header_64)..][0..hdr.sizeofcmds], | |
| 137 | }; | |
| 138 | const cmd = while (it.next()) |cmd| switch (cmd.cmd()) { | |
| 139 | .UUID => break cmd.cast(std.macho.uuid_command).?, | |
| 140 | else => {}, | |
| 141 | } else return error.UuidLoadCommandNotFound; | |
| 142 | std.mem.copy(u8, uuid, &cmd.uuid); | |
| 143 | } | |
| 144 | }; |
test/link/macho/uuid/test.c deleted-2| ... | ... | @@ -1,2 +0,0 @@ |
| 1 | void test() {} | |
| 2 |