authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-01-14 21:55:26+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-01-15 08:16:47+01:00
log2faf8c53d226cec1445156cafdec59af5e909fb2
tree1b177f07c11e52e99fc569bd217b754c6db7151e
parent2b0e3ee228e01473cf880f719db9bde5b8f34d25

macho: use target arch page_size for codesig

It turns out I was wrong and we can set the page size to the actual page size used by the target architecture when dividing the binary into chunks and calculating a hash of each chunk for embedding within the adhoc code signature. This shaves of a considerable amount of bytes since we divide the code signature section by at least 2x. I've also unified the `write` interface of `CodeSignature` struct to follow that used in every other bit of `MachO`; namely, the functions now accept a writer instead of a buffer, therefore, there is no need to manually track where to write each struct field anymore.

2 files changed, 64 insertions(+), 54 deletions(-)

src/link/MachO.zig+8-3
...@@ -2671,7 +2671,11 @@ fn writeCodeSignaturePadding(self: *MachO) !void {...@@ -2671,7 +2671,11 @@ fn writeCodeSignaturePadding(self: *MachO) !void {
2671 const linkedit_segment = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;2671 const linkedit_segment = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
2672 const code_sig_cmd = &self.load_commands.items[self.code_signature_cmd_index.?].LinkeditData;2672 const code_sig_cmd = &self.load_commands.items[self.code_signature_cmd_index.?].LinkeditData;
2673 const fileoff = linkedit_segment.inner.fileoff + linkedit_segment.inner.filesize;2673 const fileoff = linkedit_segment.inner.fileoff + linkedit_segment.inner.filesize;
2674 const needed_size = CodeSignature.calcCodeSignaturePadding(self.base.options.emit.?.sub_path, fileoff);2674 const needed_size = CodeSignature.calcCodeSignaturePaddingSize(
2675 self.base.options.emit.?.sub_path,
2676 fileoff,
2677 self.page_size,
2678 );
26752679
2676 if (code_sig_cmd.datasize < needed_size) {2680 if (code_sig_cmd.datasize < needed_size) {
2677 code_sig_cmd.dataoff = @intCast(u32, fileoff);2681 code_sig_cmd.dataoff = @intCast(u32, fileoff);
...@@ -2697,7 +2701,7 @@ fn writeCodeSignature(self: *MachO) !void {...@@ -2697,7 +2701,7 @@ fn writeCodeSignature(self: *MachO) !void {
2697 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;2701 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2698 const code_sig_cmd = self.load_commands.items[self.code_signature_cmd_index.?].LinkeditData;2702 const code_sig_cmd = self.load_commands.items[self.code_signature_cmd_index.?].LinkeditData;
26992703
2700 var code_sig = CodeSignature.init(self.base.allocator);2704 var code_sig = CodeSignature.init(self.base.allocator, self.page_size);
2701 defer code_sig.deinit();2705 defer code_sig.deinit();
2702 try code_sig.calcAdhocSignature(2706 try code_sig.calcAdhocSignature(
2703 self.base.file.?,2707 self.base.file.?,
...@@ -2709,7 +2713,8 @@ fn writeCodeSignature(self: *MachO) !void {...@@ -2709,7 +2713,8 @@ fn writeCodeSignature(self: *MachO) !void {
27092713
2710 var buffer = try self.base.allocator.alloc(u8, code_sig.size());2714 var buffer = try self.base.allocator.alloc(u8, code_sig.size());
2711 defer self.base.allocator.free(buffer);2715 defer self.base.allocator.free(buffer);
2712 code_sig.write(buffer);2716 var stream = std.io.fixedBufferStream(buffer);
2717 try code_sig.write(stream.writer());
27132718
2714 log.debug("writing code signature from 0x{x} to 0x{x}", .{ code_sig_cmd.dataoff, code_sig_cmd.dataoff + buffer.len });2719 log.debug("writing code signature from 0x{x} to 0x{x}", .{ code_sig_cmd.dataoff, code_sig_cmd.dataoff + buffer.len });
27152720
src/link/MachO/CodeSignature.zig+56-51
...@@ -11,7 +11,6 @@ const Allocator = mem.Allocator;...@@ -11,7 +11,6 @@ const Allocator = mem.Allocator;
11const Sha256 = std.crypto.hash.sha2.Sha256;11const Sha256 = std.crypto.hash.sha2.Sha256;
1212
13const hash_size: u8 = 32;13const hash_size: u8 = 32;
14const page_size: u16 = 0x1000;
1514
16const CodeDirectory = struct {15const CodeDirectory = struct {
17 inner: macho.CodeDirectory,16 inner: macho.CodeDirectory,
...@@ -21,45 +20,53 @@ const CodeDirectory = struct {...@@ -21,45 +20,53 @@ const CodeDirectory = struct {
21 return self.inner.length;20 return self.inner.length;
22 }21 }
2322
24 fn write(self: CodeDirectory, buffer: []u8) void {23 fn write(self: CodeDirectory, writer: anytype) !void {
25 assert(buffer.len >= self.inner.length);24 try writer.writeIntBig(u32, self.inner.magic);
2625 try writer.writeIntBig(u32, self.inner.length);
27 mem.writeIntBig(u32, buffer[0..4], self.inner.magic);26 try writer.writeIntBig(u32, self.inner.version);
28 mem.writeIntBig(u32, buffer[4..8], self.inner.length);27 try writer.writeIntBig(u32, self.inner.flags);
29 mem.writeIntBig(u32, buffer[8..12], self.inner.version);28 try writer.writeIntBig(u32, self.inner.hashOffset);
30 mem.writeIntBig(u32, buffer[12..16], self.inner.flags);29 try writer.writeIntBig(u32, self.inner.identOffset);
31 mem.writeIntBig(u32, buffer[16..20], self.inner.hashOffset);30 try writer.writeIntBig(u32, self.inner.nSpecialSlots);
32 mem.writeIntBig(u32, buffer[20..24], self.inner.identOffset);31 try writer.writeIntBig(u32, self.inner.nCodeSlots);
33 mem.writeIntBig(u32, buffer[24..28], self.inner.nSpecialSlots);32 try writer.writeIntBig(u32, self.inner.codeLimit);
34 mem.writeIntBig(u32, buffer[28..32], self.inner.nCodeSlots);33 try writer.writeByte(self.inner.hashSize);
35 mem.writeIntBig(u32, buffer[32..36], self.inner.codeLimit);34 try writer.writeByte(self.inner.hashType);
36 buffer[36] = self.inner.hashSize;35 try writer.writeByte(self.inner.platform);
37 buffer[37] = self.inner.hashType;36 try writer.writeByte(self.inner.pageSize);
38 buffer[38] = self.inner.platform;37 try writer.writeIntBig(u32, self.inner.spare2);
39 buffer[39] = self.inner.pageSize;38 try writer.writeIntBig(u32, self.inner.scatterOffset);
40 mem.writeIntBig(u32, buffer[40..44], self.inner.spare2);39 try writer.writeIntBig(u32, self.inner.teamOffset);
41 mem.writeIntBig(u32, buffer[44..48], self.inner.scatterOffset);40 try writer.writeIntBig(u32, self.inner.spare3);
42 mem.writeIntBig(u32, buffer[48..52], self.inner.teamOffset);41 try writer.writeIntBig(u64, self.inner.codeLimit64);
43 mem.writeIntBig(u32, buffer[52..56], self.inner.spare3);42 try writer.writeIntBig(u64, self.inner.execSegBase);
44 mem.writeIntBig(u64, buffer[56..64], self.inner.codeLimit64);43 try writer.writeIntBig(u64, self.inner.execSegLimit);
45 mem.writeIntBig(u64, buffer[64..72], self.inner.execSegBase);44 try writer.writeIntBig(u64, self.inner.execSegFlags);
46 mem.writeIntBig(u64, buffer[72..80], self.inner.execSegLimit);45 try writer.writeAll(self.data.items);
47 mem.writeIntBig(u64, buffer[80..88], self.inner.execSegFlags);
48
49 mem.copy(u8, buffer[88..], self.data.items);
50 }46 }
51};47};
5248
53allocator: *Allocator,49allocator: *Allocator,
50
51/// Code signature blob header.
54inner: macho.SuperBlob = .{52inner: macho.SuperBlob = .{
55 .magic = macho.CSMAGIC_EMBEDDED_SIGNATURE,53 .magic = macho.CSMAGIC_EMBEDDED_SIGNATURE,
56 .length = @sizeOf(macho.SuperBlob),54 .length = @sizeOf(macho.SuperBlob),
57 .count = 0,55 .count = 0,
58},56},
57
58/// CodeDirectory header which holds the hash of the binary.
59cdir: ?CodeDirectory = null,59cdir: ?CodeDirectory = null,
6060
61pub fn init(allocator: *Allocator) CodeSignature {61/// Page size is dependent on the target cpu architecture.
62 return .{ .allocator = allocator };62/// For x86_64 that's 4KB, whereas for aarch64, that's 16KB.
63page_size: u16,
64
65pub fn init(allocator: *Allocator, page_size: u16) CodeSignature {
66 return .{
67 .allocator = allocator,
68 .page_size = page_size,
69 };
63}70}
6471
65pub fn calcAdhocSignature(72pub fn calcAdhocSignature(
...@@ -88,7 +95,7 @@ pub fn calcAdhocSignature(...@@ -88,7 +95,7 @@ pub fn calcAdhocSignature(
88 .hashSize = hash_size,95 .hashSize = hash_size,
89 .hashType = macho.CS_HASHTYPE_SHA256,96 .hashType = macho.CS_HASHTYPE_SHA256,
90 .platform = 0,97 .platform = 0,
91 .pageSize = @truncate(u8, std.math.log2(page_size)),98 .pageSize = @truncate(u8, std.math.log2(self.page_size)),
92 .spare2 = 0,99 .spare2 = 0,
93 .scatterOffset = 0,100 .scatterOffset = 0,
94 .teamOffset = 0,101 .teamOffset = 0,
...@@ -100,10 +107,10 @@ pub fn calcAdhocSignature(...@@ -100,10 +107,10 @@ pub fn calcAdhocSignature(
100 },107 },
101 };108 };
102109
103 const total_pages = mem.alignForward(file_size, page_size) / page_size;110 const total_pages = mem.alignForward(file_size, self.page_size) / self.page_size;
104111
105 var hash: [hash_size]u8 = undefined;112 var hash: [hash_size]u8 = undefined;
106 var buffer = try self.allocator.alloc(u8, page_size);113 var buffer = try self.allocator.alloc(u8, self.page_size);
107 defer self.allocator.free(buffer);114 defer self.allocator.free(buffer);
108115
109 try cdir.data.ensureCapacity(self.allocator, total_pages * hash_size + id.len + 1);116 try cdir.data.ensureCapacity(self.allocator, total_pages * hash_size + id.len + 1);
...@@ -119,8 +126,8 @@ pub fn calcAdhocSignature(...@@ -119,8 +126,8 @@ pub fn calcAdhocSignature(
119 cdir.inner.hashOffset = cdir.inner.identOffset + @intCast(u32, id.len) + 1;126 cdir.inner.hashOffset = cdir.inner.identOffset + @intCast(u32, id.len) + 1;
120 var i: usize = 0;127 var i: usize = 0;
121 while (i < total_pages) : (i += 1) {128 while (i < total_pages) : (i += 1) {
122 const fstart = i * page_size;129 const fstart = i * self.page_size;
123 const fsize = if (fstart + page_size > file_size) file_size - fstart else page_size;130 const fsize = if (fstart + self.page_size > file_size) file_size - fstart else self.page_size;
124 const len = try file.preadAll(buffer, fstart);131 const len = try file.preadAll(buffer, fstart);
125 assert(fsize <= len);132 assert(fsize <= len);
126133
...@@ -142,12 +149,11 @@ pub fn size(self: CodeSignature) u32 {...@@ -142,12 +149,11 @@ pub fn size(self: CodeSignature) u32 {
142 return self.inner.length;149 return self.inner.length;
143}150}
144151
145pub fn write(self: CodeSignature, buffer: []u8) void {152pub fn write(self: CodeSignature, writer: anytype) !void {
146 assert(buffer.len >= self.inner.length);153 try self.writeHeader(writer);
147 self.writeHeader(buffer);
148 const offset: u32 = @sizeOf(macho.SuperBlob) + @sizeOf(macho.BlobIndex);154 const offset: u32 = @sizeOf(macho.SuperBlob) + @sizeOf(macho.BlobIndex);
149 writeBlobIndex(macho.CSSLOT_CODEDIRECTORY, offset, buffer[@sizeOf(macho.SuperBlob)..]);155 try writeBlobIndex(macho.CSSLOT_CODEDIRECTORY, offset, writer);
150 self.cdir.?.write(buffer[offset..]);156 try self.cdir.?.write(writer);
151}157}
152158
153pub fn deinit(self: *CodeSignature) void {159pub fn deinit(self: *CodeSignature) void {
...@@ -156,31 +162,30 @@ pub fn deinit(self: *CodeSignature) void {...@@ -156,31 +162,30 @@ pub fn deinit(self: *CodeSignature) void {
156 }162 }
157}163}
158164
159fn writeHeader(self: CodeSignature, buffer: []u8) void {165fn writeHeader(self: CodeSignature, writer: anytype) !void {
160 assert(buffer.len >= @sizeOf(macho.SuperBlob));166 try writer.writeIntBig(u32, self.inner.magic);
161 mem.writeIntBig(u32, buffer[0..4], self.inner.magic);167 try writer.writeIntBig(u32, self.inner.length);
162 mem.writeIntBig(u32, buffer[4..8], self.inner.length);168 try writer.writeIntBig(u32, self.inner.count);
163 mem.writeIntBig(u32, buffer[8..12], self.inner.count);
164}169}
165170
166fn writeBlobIndex(tt: u32, offset: u32, buffer: []u8) void {171fn writeBlobIndex(tt: u32, offset: u32, writer: anytype) !void {
167 assert(buffer.len >= @sizeOf(macho.BlobIndex));172 try writer.writeIntBig(u32, tt);
168 mem.writeIntBig(u32, buffer[0..4], tt);173 try writer.writeIntBig(u32, offset);
169 mem.writeIntBig(u32, buffer[4..8], offset);
170}174}
171175
172test "CodeSignature header" {176test "CodeSignature header" {
173 var code_sig = CodeSignature.init(testing.allocator);177 var code_sig = CodeSignature.init(testing.allocator, 0x1000);
174 defer code_sig.deinit();178 defer code_sig.deinit();
175179
176 var buffer: [@sizeOf(macho.SuperBlob)]u8 = undefined;180 var buffer: [@sizeOf(macho.SuperBlob)]u8 = undefined;
177 code_sig.writeHeader(&buffer);181 var stream = std.io.fixedBufferStream(&buffer);
182 try code_sig.writeHeader(stream.writer());
178183
179 const expected = &[_]u8{ 0xfa, 0xde, 0x0c, 0xc0, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0x0 };184 const expected = &[_]u8{ 0xfa, 0xde, 0x0c, 0xc0, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0x0 };
180 testing.expect(mem.eql(u8, expected, &buffer));185 testing.expect(mem.eql(u8, expected, &buffer));
181}186}
182187
183pub fn calcCodeSignaturePadding(id: []const u8, file_size: u64) u32 {188pub fn calcCodeSignaturePaddingSize(id: []const u8, file_size: u64, page_size: u16) u32 {
184 const ident_size = id.len + 1;189 const ident_size = id.len + 1;
185 const total_pages = mem.alignForwardGeneric(u64, file_size, page_size) / page_size;190 const total_pages = mem.alignForwardGeneric(u64, file_size, page_size) / page_size;
186 const hashed_size = total_pages * hash_size;191 const hashed_size = total_pages * hash_size;