| ... | ... | @@ -25,7 +25,6 @@ const CodeSignature = @import("CodeSignature.zig"); |
| 25 | 25 | const Compilation = @import("../../Compilation.zig"); |
| 26 | 26 | const DwarfInfo = @import("DwarfInfo.zig"); |
| 27 | 27 | const Dylib = @import("Dylib.zig"); |
| 28 | | const Hasher = @import("hasher.zig").ParallelHasher; |
| 29 | 28 | const MachO = @import("../MachO.zig"); |
| 30 | 29 | const Md5 = std.crypto.hash.Md5; |
| 31 | 30 | const LibStub = @import("../tapi.zig").LibStub; |
| ... | ... | @@ -44,7 +43,9 @@ pub const Zld = struct { |
| 44 | 43 | dysymtab_cmd: macho.dysymtab_command = .{}, |
| 45 | 44 | function_starts_cmd: macho.linkedit_data_command = .{ .cmd = .FUNCTION_STARTS }, |
| 46 | 45 | data_in_code_cmd: macho.linkedit_data_command = .{ .cmd = .DATA_IN_CODE }, |
| 47 | | uuid_cmd: macho.uuid_command = .{}, |
| 46 | uuid_cmd: macho.uuid_command = .{ |
| 47 | .uuid = [_]u8{0} ** 16, |
| 48 | }, |
| 48 | 49 | codesig_cmd: macho.linkedit_data_command = .{ .cmd = .CODE_SIGNATURE }, |
| 49 | 50 | |
| 50 | 51 | objects: std.ArrayListUnmanaged(Object) = .{}, |
| ... | ... | @@ -2679,7 +2680,9 @@ pub const Zld = struct { |
| 2679 | 2680 | linkedit_cmd_offset: u32, |
| 2680 | 2681 | symtab_cmd_offset: u32, |
| 2681 | 2682 | uuid_cmd_offset: u32, |
| 2683 | codesig_cmd_offset: ?u32, |
| 2682 | 2684 | }) !void { |
| 2685 | _ = comp; |
| 2683 | 2686 | switch (self.options.optimize_mode) { |
| 2684 | 2687 | .Debug => { |
| 2685 | 2688 | // In Debug we don't really care about reproducibility, so put in a random value |
| ... | ... | @@ -2689,27 +2692,34 @@ pub const Zld = struct { |
| 2689 | 2692 | conformUuid(&self.uuid_cmd.uuid); |
| 2690 | 2693 | }, |
| 2691 | 2694 | else => { |
| 2692 | | const seg = self.getLinkeditSegmentPtr(); |
| 2693 | | const max_file_size = @intCast(u32, seg.fileoff + seg.filesize); |
| 2695 | const max_file_size = self.symtab_cmd.stroff + self.symtab_cmd.strsize; |
| 2694 | 2696 | |
| 2695 | | var hashes = std.ArrayList([Md5.digest_length]u8).init(self.gpa); |
| 2696 | | defer hashes.deinit(); |
| 2697 | | |
| 2698 | | var subsections: [4]FileSubsection = undefined; |
| 2699 | | var count: usize = 2; |
| 2697 | var subsections: [5]FileSubsection = undefined; |
| 2698 | var count: usize = 0; |
| 2700 | 2699 | |
| 2701 | 2700 | // Exclude LINKEDIT segment command as it contains file size that includes stabs contribution |
| 2702 | 2701 | // and code signature. |
| 2703 | | subsections[0] = .{ |
| 2702 | subsections[count] = .{ |
| 2704 | 2703 | .start = 0, |
| 2705 | 2704 | .end = args.linkedit_cmd_offset, |
| 2706 | 2705 | }; |
| 2706 | count += 1; |
| 2707 | 2707 | |
| 2708 | 2708 | // Exclude SYMTAB and DYSYMTAB commands for the same reason. |
| 2709 | | subsections[1] = .{ |
| 2710 | | .start = args.linkedit_cmd_offset + @sizeOf(macho.segment_command_64), |
| 2709 | subsections[count] = .{ |
| 2710 | .start = subsections[count - 1].end + @sizeOf(macho.segment_command_64), |
| 2711 | 2711 | .end = args.symtab_cmd_offset, |
| 2712 | 2712 | }; |
| 2713 | count += 1; |
| 2714 | |
| 2715 | // Exclude CODE_SIGNATURE command (if present). |
| 2716 | if (args.codesig_cmd_offset) |offset| { |
| 2717 | subsections[count] = .{ |
| 2718 | .start = subsections[count - 1].end + @sizeOf(macho.symtab_command) + @sizeOf(macho.dysymtab_command), |
| 2719 | .end = offset, |
| 2720 | }; |
| 2721 | count += 1; |
| 2722 | } |
| 2713 | 2723 | |
| 2714 | 2724 | if (!self.options.strip) { |
| 2715 | 2725 | // Exclude region comprising all symbol stabs. |
| ... | ... | @@ -2726,9 +2736,13 @@ pub const Zld = struct { |
| 2726 | 2736 | if (local.stab()) break i; |
| 2727 | 2737 | } else locals.len; |
| 2728 | 2738 | const nstabs = locals.len - istab; |
| 2739 | |
| 2729 | 2740 | if (nstabs == 0) { |
| 2730 | | subsections[2] = .{ |
| 2731 | | .start = args.symtab_cmd_offset + @sizeOf(macho.symtab_command) + @sizeOf(macho.dysymtab_command), |
| 2741 | subsections[count] = .{ |
| 2742 | .start = subsections[count - 1].end + if (args.codesig_cmd_offset == null) |
| 2743 | @as(u32, @sizeOf(macho.symtab_command) + @sizeOf(macho.dysymtab_command)) |
| 2744 | else |
| 2745 | @sizeOf(macho.linkedit_data_command), |
| 2732 | 2746 | .end = max_file_size, |
| 2733 | 2747 | }; |
| 2734 | 2748 | count += 1; |
| ... | ... | @@ -2738,38 +2752,80 @@ pub const Zld = struct { |
| 2738 | 2752 | // not part of the UUID calculation anyway. |
| 2739 | 2753 | const stab_stroff = locals[istab].n_strx; |
| 2740 | 2754 | |
| 2741 | | subsections[2] = .{ |
| 2742 | | .start = args.symtab_cmd_offset + @sizeOf(macho.symtab_command) + @sizeOf(macho.dysymtab_command), |
| 2755 | subsections[count] = .{ |
| 2756 | .start = subsections[count - 1].end + if (args.codesig_cmd_offset == null) |
| 2757 | @as(u32, @sizeOf(macho.symtab_command) + @sizeOf(macho.dysymtab_command)) |
| 2758 | else |
| 2759 | @sizeOf(macho.linkedit_data_command), |
| 2743 | 2760 | .end = @intCast(u32, self.symtab_cmd.symoff + istab * @sizeOf(macho.nlist_64)), |
| 2744 | 2761 | }; |
| 2745 | | subsections[3] = .{ |
| 2746 | | .start = subsections[2].end + @intCast(u32, nstabs * @sizeOf(macho.nlist_64)), |
| 2762 | count += 1; |
| 2763 | |
| 2764 | subsections[count] = .{ |
| 2765 | .start = subsections[count - 1].end + @intCast(u32, nstabs * @sizeOf(macho.nlist_64)), |
| 2747 | 2766 | .end = self.symtab_cmd.stroff + stab_stroff, |
| 2748 | 2767 | }; |
| 2749 | | |
| 2750 | | count += 2; |
| 2768 | count += 1; |
| 2751 | 2769 | } |
| 2752 | 2770 | } else { |
| 2753 | | subsections[2] = .{ |
| 2754 | | .start = args.symtab_cmd_offset + @sizeOf(macho.symtab_command) + @sizeOf(macho.dysymtab_command), |
| 2771 | subsections[count] = .{ |
| 2772 | .start = subsections[count - 1].end + if (args.codesig_cmd_offset == null) |
| 2773 | @as(u32, @sizeOf(macho.symtab_command) + @sizeOf(macho.dysymtab_command)) |
| 2774 | else |
| 2775 | @sizeOf(macho.linkedit_data_command), |
| 2755 | 2776 | .end = max_file_size, |
| 2756 | 2777 | }; |
| 2757 | 2778 | count += 1; |
| 2758 | 2779 | } |
| 2759 | 2780 | |
| 2781 | const chunk_size = 0x4000; |
| 2782 | |
| 2783 | var rb = RingBuffer{}; |
| 2784 | var hasher = Md5.init(.{}); |
| 2785 | var buffer: [chunk_size]u8 = undefined; |
| 2786 | var hashed: usize = 0; |
| 2787 | |
| 2760 | 2788 | for (subsections[0..count]) |cut| { |
| 2761 | | std.debug.print("{x} - {x}\n", .{ cut.start, cut.end }); |
| 2762 | | try self.calcUuidHashes(comp, cut, &hashes); |
| 2789 | // std.debug.print("{x} - {x}, {x}\n", .{ cut.start, cut.end, cut.end - cut.start }); |
| 2790 | |
| 2791 | const size = cut.end - cut.start; |
| 2792 | const num_chunks = mem.alignForward(size, chunk_size) / chunk_size; |
| 2793 | |
| 2794 | var i: usize = 0; |
| 2795 | while (i < num_chunks) : (i += 1) { |
| 2796 | const fstart = cut.start + i * chunk_size; |
| 2797 | const fsize = if (fstart + chunk_size > cut.end) |
| 2798 | cut.end - fstart |
| 2799 | else |
| 2800 | chunk_size; |
| 2801 | // std.debug.print("fstart {x}, fsize {x}\n", .{ fstart, fsize }); |
| 2802 | const amt = try self.file.preadAll(buffer[0..fsize], fstart); |
| 2803 | if (amt != fsize) return error.InputOutput; |
| 2804 | |
| 2805 | // try formatBinaryBlob(buffer[0..fsize], .{ .fmt_as_str = false }, std.io.getStdOut().writer()); |
| 2806 | |
| 2807 | var leftover = rb.append(buffer[0..fsize]); |
| 2808 | while (leftover > 0) { |
| 2809 | if (rb.full()) { |
| 2810 | hasher.update(rb.getBuffer()); |
| 2811 | hashed += rb.getBuffer().len; |
| 2812 | rb.clear(); |
| 2813 | } |
| 2814 | leftover = rb.append(buffer[fsize - leftover ..]); |
| 2815 | } |
| 2816 | } |
| 2763 | 2817 | } |
| 2764 | 2818 | |
| 2765 | | const final_buffer = try self.gpa.alloc(u8, hashes.items.len * Md5.digest_length); |
| 2766 | | defer self.gpa.free(final_buffer); |
| 2767 | | |
| 2768 | | for (hashes.items) |hash, i| { |
| 2769 | | mem.copy(u8, final_buffer[i * Md5.digest_length ..][0..Md5.digest_length], &hash); |
| 2819 | if (!rb.empty()) { |
| 2820 | // try formatBinaryBlob(rb.getBuffer(), .{ .fmt_as_str = false }, std.io.getStdOut().writer()); |
| 2821 | hasher.update(rb.getBuffer()); |
| 2822 | hashed += rb.getBuffer().len; |
| 2823 | rb.clear(); |
| 2770 | 2824 | } |
| 2771 | 2825 | |
| 2772 | | Md5.hash(final_buffer, &self.uuid_cmd.uuid, .{}); |
| 2826 | // std.debug.print("hashed {x}\n", .{hashed}); |
| 2827 | |
| 2828 | hasher.final(&self.uuid_cmd.uuid); |
| 2773 | 2829 | conformUuid(&self.uuid_cmd.uuid); |
| 2774 | 2830 | }, |
| 2775 | 2831 | } |
| ... | ... | @@ -2778,6 +2834,79 @@ pub const Zld = struct { |
| 2778 | 2834 | try self.file.pwriteAll(&self.uuid_cmd.uuid, in_file); |
| 2779 | 2835 | } |
| 2780 | 2836 | |
| 2837 | const FmtBinaryBlobOpts = struct { |
| 2838 | fmt_as_str: bool = true, |
| 2839 | escape_str: bool = false, |
| 2840 | }; |
| 2841 | |
| 2842 | fn formatBinaryBlob(blob: []const u8, opts: FmtBinaryBlobOpts, writer: anytype) !void { |
| 2843 | // Format as 16-by-16-by-8 with two left column in hex, and right in ascii: |
| 2844 | // xxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxx xxxxxxxx |
| 2845 | var i: usize = 0; |
| 2846 | const step = 16; |
| 2847 | var tmp_buf: [step]u8 = undefined; |
| 2848 | while (i < blob.len) : (i += step) { |
| 2849 | const end = if (blob[i..].len >= step) step else blob[i..].len; |
| 2850 | const padding = step - blob[i .. i + end].len; |
| 2851 | if (padding > 0) { |
| 2852 | mem.set(u8, &tmp_buf, 0); |
| 2853 | } |
| 2854 | mem.copy(u8, &tmp_buf, blob[i .. i + end]); |
| 2855 | try writer.print("{x} {x:<016} {x:<016}", .{ |
| 2856 | i, std.fmt.fmtSliceHexLower(tmp_buf[0 .. step / 2]), std.fmt.fmtSliceHexLower(tmp_buf[step / 2 .. step]), |
| 2857 | }); |
| 2858 | if (opts.fmt_as_str) { |
| 2859 | if (opts.escape_str) { |
| 2860 | try writer.print(" {s}", .{std.fmt.fmtSliceEscapeLower(tmp_buf[0..step])}); |
| 2861 | } else { |
| 2862 | try writer.print(" {s}", .{tmp_buf[0..step]}); |
| 2863 | } |
| 2864 | } |
| 2865 | try writer.writeByte('\n'); |
| 2866 | } |
| 2867 | } |
| 2868 | |
| 2869 | const RingBuffer = struct { |
| 2870 | buffer: [chunk_size]u8 = undefined, |
| 2871 | pos: usize = 0, |
| 2872 | |
| 2873 | const chunk_size = 0x4000; |
| 2874 | |
| 2875 | fn append(rb: *RingBuffer, data: []u8) usize { |
| 2876 | const cpy_size = if (data.len > rb.available()) |
| 2877 | data.len - rb.available() |
| 2878 | else |
| 2879 | data.len; |
| 2880 | // std.debug.print(" appending {x} of {x} (pos {x})\n", .{ cpy_size, data.len, rb.pos }); |
| 2881 | mem.copy(u8, rb.buffer[rb.pos..], data[0..cpy_size]); |
| 2882 | rb.pos += cpy_size; |
| 2883 | const leftover = data.len - cpy_size; |
| 2884 | // std.debug.print(" leftover {x}\n", .{leftover}); |
| 2885 | // std.debug.print(" buffer {x} full\n", .{rb.pos}); |
| 2886 | return leftover; |
| 2887 | } |
| 2888 | |
| 2889 | fn available(rb: RingBuffer) usize { |
| 2890 | return rb.buffer.len - rb.pos; |
| 2891 | } |
| 2892 | |
| 2893 | fn clear(rb: *RingBuffer) void { |
| 2894 | rb.pos = 0; |
| 2895 | } |
| 2896 | |
| 2897 | fn full(rb: RingBuffer) bool { |
| 2898 | return rb.buffer.len == rb.pos; |
| 2899 | } |
| 2900 | |
| 2901 | fn empty(rb: RingBuffer) bool { |
| 2902 | return rb.pos == 0; |
| 2903 | } |
| 2904 | |
| 2905 | fn getBuffer(rb: *const RingBuffer) []const u8 { |
| 2906 | return rb.buffer[0..rb.pos]; |
| 2907 | } |
| 2908 | }; |
| 2909 | |
| 2781 | 2910 | inline fn conformUuid(out: *[Md5.digest_length]u8) void { |
| 2782 | 2911 | // LC_UUID uuids should conform to RFC 4122 UUID version 4 & UUID version 5 formats |
| 2783 | 2912 | out[6] = (out[6] & 0x0F) | (3 << 4); |
| ... | ... | @@ -2789,23 +2918,23 @@ pub const Zld = struct { |
| 2789 | 2918 | end: u32, |
| 2790 | 2919 | }; |
| 2791 | 2920 | |
| 2792 | | fn calcUuidHashes( |
| 2793 | | self: *Zld, |
| 2794 | | comp: *const Compilation, |
| 2795 | | cut: FileSubsection, |
| 2796 | | hashes: *std.ArrayList([Md5.digest_length]u8), |
| 2797 | | ) !void { |
| 2798 | | const chunk_size = 0x4000; |
| 2799 | | const total_hashes = mem.alignForward(cut.end - cut.start, chunk_size) / chunk_size; |
| 2800 | | try hashes.resize(hashes.items.len + total_hashes); |
| 2801 | | |
| 2802 | | var hasher = Hasher(Md5){}; |
| 2803 | | try hasher.hash(self.gpa, comp.thread_pool, self.file, hashes.items, .{ |
| 2804 | | .chunk_size = chunk_size, |
| 2805 | | .file_pos = cut.start, |
| 2806 | | .max_file_size = cut.end - cut.start, |
| 2807 | | }); |
| 2808 | | } |
| 2921 | // fn calcUuidHashes( |
| 2922 | // self: *Zld, |
| 2923 | // comp: *const Compilation, |
| 2924 | // cut: FileSubsection, |
| 2925 | // hashes: *std.ArrayList([Md5.digest_length]u8), |
| 2926 | // ) !void { |
| 2927 | // const chunk_size = 0x4000; |
| 2928 | // const total_hashes = mem.alignForward(cut.end - cut.start, chunk_size) / chunk_size; |
| 2929 | // try hashes.resize(hashes.items.len + total_hashes); |
| 2930 | |
| 2931 | // var hasher = Hasher(Md5){}; |
| 2932 | // try hasher.hash(self.gpa, comp.thread_pool, self.file, hashes.items, .{ |
| 2933 | // .chunk_size = chunk_size, |
| 2934 | // .file_pos = cut.start, |
| 2935 | // .max_file_size = cut.end - cut.start, |
| 2936 | // }); |
| 2937 | // } |
| 2809 | 2938 | |
| 2810 | 2939 | fn writeCodeSignaturePadding(self: *Zld, code_sig: *CodeSignature) !void { |
| 2811 | 2940 | const seg = self.getLinkeditSegmentPtr(); |
| ... | ... | @@ -4133,6 +4262,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr |
| 4133 | 4262 | if (cpu_arch == .aarch64 and (os_tag == .macos or abi == .simulator)) break :blk true; |
| 4134 | 4263 | break :blk false; |
| 4135 | 4264 | }; |
| 4265 | var codesig_cmd_offset: ?u32 = null; |
| 4136 | 4266 | var codesig: ?CodeSignature = if (requires_codesig) blk: { |
| 4137 | 4267 | // Preallocate space for the code signature. |
| 4138 | 4268 | // We need to do this at this stage so that we have the load commands with proper values |
| ... | ... | @@ -4145,6 +4275,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr |
| 4145 | 4275 | try codesig.addEntitlements(gpa, path); |
| 4146 | 4276 | } |
| 4147 | 4277 | try zld.writeCodeSignaturePadding(&codesig); |
| 4278 | codesig_cmd_offset = @sizeOf(macho.mach_header_64) + @intCast(u32, lc_buffer.items.len); |
| 4148 | 4279 | try lc_writer.writeStruct(zld.codesig_cmd); |
| 4149 | 4280 | break :blk codesig; |
| 4150 | 4281 | } else null; |
| ... | ... | @@ -4158,6 +4289,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr |
| 4158 | 4289 | .linkedit_cmd_offset = linkedit_cmd_offset, |
| 4159 | 4290 | .symtab_cmd_offset = symtab_cmd_offset, |
| 4160 | 4291 | .uuid_cmd_offset = uuid_cmd_offset, |
| 4292 | .codesig_cmd_offset = codesig_cmd_offset, |
| 4161 | 4293 | }); |
| 4162 | 4294 | |
| 4163 | 4295 | if (codesig) |*csig| { |