mirror of
https://github.com/rui314/mold.git
synced 2024-09-11 21:17:28 +03:00
[Mach-O] Compute LC_UUID as a tree hash of ad-hoc code signatures
We need to compute SHA256 hashes for a __code_signature section, so recomputing a SHA256 hash of an entire file for an LC_UUID is a waste of time. We can instead compute it as a tree hash.
This commit is contained in:
parent
ae29091aa6
commit
f55fd7d9f2
@ -380,6 +380,9 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
|
||||
ctx.arg.final_output = ctx.arg.output;
|
||||
}
|
||||
|
||||
if (ctx.arg.uuid == UUID_RANDOM)
|
||||
memcpy(ctx.uuid, get_uuid_v4().data(), 16);
|
||||
|
||||
return remaining;
|
||||
}
|
||||
|
||||
|
@ -696,9 +696,6 @@ static int do_main(int argc, char **argv) {
|
||||
OutputFile<Context<E>>::open(ctx, ctx.arg.output, output_size, 0777);
|
||||
ctx.buf = ctx.output_file->buf;
|
||||
|
||||
if (ctx.arg.uuid != UUID_NONE)
|
||||
ctx.mach_hdr.write_uuid(ctx);
|
||||
|
||||
{
|
||||
Timer t(ctx, "copy_buf");
|
||||
for (std::unique_ptr<OutputSegment<E>> &seg : ctx.segments) {
|
||||
|
@ -354,7 +354,6 @@ public:
|
||||
|
||||
void compute_size(Context<E> &ctx) override;
|
||||
void copy_buf(Context<E> &ctx) override;
|
||||
void write_uuid(Context<E> &ctx);
|
||||
};
|
||||
|
||||
template <typename E>
|
||||
|
@ -343,27 +343,6 @@ void OutputMachHeader<E>::copy_buf(Context<E> &ctx) {
|
||||
write_vector(buf + sizeof(mhdr), flatten(cmds));
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
void OutputMachHeader<E>::write_uuid(Context<E> &ctx) {
|
||||
Timer t(ctx, "write_uuid");
|
||||
|
||||
switch (ctx.arg.uuid) {
|
||||
case UUID_RANDOM:
|
||||
memcpy(ctx.uuid, get_uuid_v4().data(), 16);
|
||||
break;
|
||||
case UUID_HASH: {
|
||||
u8 buf[SHA256_SIZE];
|
||||
SHA256(ctx.buf, ctx.output_file->filesize, buf);
|
||||
memcpy(ctx.uuid, buf, 16);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
unreachable();
|
||||
}
|
||||
|
||||
copy_buf(ctx);
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
OutputSection<E> *
|
||||
OutputSection<E>::get_instance(Context<E> &ctx, std::string_view segname,
|
||||
@ -1030,6 +1009,11 @@ void CodeSignatureSection<E>::compute_size(Context<E> &ctx) {
|
||||
num_blocks * SHA256_SIZE;
|
||||
}
|
||||
|
||||
// A __code_signature section is optional for x86 macOS but mandatory
|
||||
// for ARM macOS. The section contains a cryptographic hash for each 4
|
||||
// KiB block of an executable or a dylib file. The program loader
|
||||
// verifies the hash values on the initial execution of a binary and
|
||||
// will reject it if a hash value does not match.
|
||||
template <typename E>
|
||||
void CodeSignatureSection<E>::write_signature(Context<E> &ctx) {
|
||||
u8 *buf = ctx.buf + this->hdr.offset;
|
||||
@ -1039,6 +1023,7 @@ void CodeSignatureSection<E>::write_signature(Context<E> &ctx) {
|
||||
i64 filename_size = align_to(filename.size() + 1, 16);
|
||||
i64 num_blocks = align_to(this->hdr.offset, BLOCK_SIZE) / BLOCK_SIZE;
|
||||
|
||||
// Fill code-sign header fields
|
||||
CodeSignatureHeader &sighdr = *(CodeSignatureHeader *)buf;
|
||||
buf += sizeof(sighdr);
|
||||
|
||||
@ -1074,14 +1059,40 @@ void CodeSignatureSection<E>::write_signature(Context<E> &ctx) {
|
||||
memcpy(buf, filename.data(), filename.size());
|
||||
buf += filename_size;
|
||||
|
||||
for (i64 i = 0; i < num_blocks; i++) {
|
||||
// Compute a hash value for each 4 KiB block. The block size must be
|
||||
// 4 KiB, as the macOS kernel supports only that block size for the
|
||||
// ad-hoc code signatures.
|
||||
auto compute_hash = [&](i64 i) {
|
||||
u8 *start = ctx.buf + i * BLOCK_SIZE;
|
||||
u8 *end = ctx.buf + std::min<i64>((i + 1) * BLOCK_SIZE, this->hdr.offset);
|
||||
SHA256(start, end - start, buf);
|
||||
buf += SHA256_SIZE;
|
||||
SHA256(start, end - start, buf + i * SHA256_SIZE);
|
||||
};
|
||||
|
||||
for (i64 i = 0; i < num_blocks; i++)
|
||||
compute_hash(i);
|
||||
|
||||
// A LC_UUID load command may also contain a crypto hash of the
|
||||
// entire file. We compute its value as a tree hash.
|
||||
if (ctx.arg.uuid == UUID_HASH) {
|
||||
u8 uuid[SHA256_SIZE];
|
||||
SHA256(buf, num_blocks * SHA256_SIZE, uuid);
|
||||
|
||||
// Indicate that this is UUIDv4 as defined by RFC4122.
|
||||
uuid[6] = (uuid[6] & 0b00001111) | 0b01010000;
|
||||
uuid[8] = (uuid[8] & 0b00111111) | 0b10000000;
|
||||
|
||||
memcpy(ctx.uuid, uuid, 16);
|
||||
|
||||
// Rewrite the load commands to write the updated UUID and
|
||||
// recompute code signatures for the updated blocks.
|
||||
ctx.mach_hdr.copy_buf(ctx);
|
||||
|
||||
for (i64 i = 0; i * BLOCK_SIZE < this->hdr.offset; i++)
|
||||
compute_hash(i);
|
||||
}
|
||||
|
||||
// A hack borrowed from lld.
|
||||
// A hack borrowed from lld to workaround a macOS kernel issue
|
||||
// https://openradar.appspot.com/FB8914231.
|
||||
msync(ctx.buf, ctx.output_file->filesize, MS_INVALIDATE);
|
||||
}
|
||||
|
||||
|
10
uuid.cc
10
uuid.cc
@ -11,13 +11,9 @@ std::array<u8, 16> get_uuid_v4() {
|
||||
u32 buf[4] = { rand(), rand(), rand(), rand() };
|
||||
memcpy(bytes.data(), buf, 16);
|
||||
|
||||
// Indicate that this is UUIDv4.
|
||||
bytes[6] &= 0b00001111;
|
||||
bytes[6] |= 0b01000000;
|
||||
|
||||
// Indicates that this is an RFC4122 variant.
|
||||
bytes[8] &= 0b00111111;
|
||||
bytes[8] |= 0b10000000;
|
||||
// Indicate that this is UUIDv4 as defined by RFC4122.
|
||||
bytes[6] = (bytes[6] & 0b00001111) | 0b01000000;
|
||||
bytes[8] = (bytes[8] & 0b00111111) | 0b10000000;
|
||||
return bytes;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user