kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

commit 7a56910715c61ee826a4c8edeeeaa24ad90ba9bd
parent 3684877141bf927383d6e46b8fd1d2c54dd5af34
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Fri, 12 Jun 2026 09:43:21 -0700

fix(macho): sign arm64 executables with 16 KiB code-signature pages

kit ad-hoc-signed Mach-O with 4 KiB hash pages; Apple-Silicon arm64 requires
16 KiB (the native VM page), so the kernel rejected larger arm64 binaries at load
('invalid signature' / SIGKILL) even though codesign -v passed. Carry a per-target
MCtx.cs_page_log2 (14 for KIT_ARCH_ARM_64, 12 for x86_64) and use it for the
CodeDirectory page size, slot count, and hashing.

Diffstat:
Msrc/obj/macho/link.c | 20+++++++++++++++++---
1 file changed, 17 insertions(+), 3 deletions(-)

diff --git a/src/obj/macho/link.c b/src/obj/macho/link.c @@ -76,6 +76,14 @@ #define CSSLOT_CODEDIRECTORY 0u #define CS_HASHTYPE_SHA256 2u #define CS_SHA256_LEN SHA256_DIGEST_LEN +/* Default code-signing hash page size (log2). Apple's tools sign with a page + * size that matches the target's native VM page: 4 KiB (12) on x86_64, but + * 16 KiB (14) on Apple-Silicon arm64. The kernel's loader validates the main + * executable against its own (16 KiB) pages, so a 4 KiB-page signature is + * rejected as invalid on arm64 once the image is large enough to span the + * mismatch — small images can slip through, large ones SIGKILL with "invalid + * signature". MCtx.cs_page_log2 carries the per-target choice; this is the + * x86_64 fallback. */ #define CS_PAGE_SIZE_LOG2 12u #define CS_EXECSEG_MAIN_BINARY 1u @@ -302,6 +310,7 @@ typedef struct MCtx { u32 strtab_off; u32 codesig_off; u32 codesig_size; + u8 cs_page_log2; /* code-signing hash page size, log2 (12=4K x64, 14=16K arm64) */ u32 nsyms; u8 uuid[16]; @@ -2217,7 +2226,8 @@ static void wr_u64_be(u8* p, u64 v) { /* Build the codesig blob with placeholder hashes; size is precise so * file layout is final after this. */ static void build_codesig_skeleton(MCtx* x, u32 code_limit, const char* ident) { - u32 code_page = 1u << CS_PAGE_SIZE_LOG2; /* 4096 */ + u32 page_log2 = x->cs_page_log2 ? x->cs_page_log2 : CS_PAGE_SIZE_LOG2; + u32 code_page = 1u << page_log2; u32 nslots = (code_limit + code_page - 1u) / code_page; /* CodeDirectory size: @@ -2260,7 +2270,7 @@ static void build_codesig_skeleton(MCtx* x, u32 code_limit, const char* ident) { cd[36] = (u8)CS_SHA256_LEN; cd[37] = (u8)CS_HASHTYPE_SHA256; cd[38] = 0; /* platform */ - cd[39] = (u8)CS_PAGE_SIZE_LOG2; + cd[39] = (u8)page_log2; wr_u32_be(cd + 40, 0); /* spare2 */ wr_u32_be(cd + 44, 0); /* scatterOffset */ wr_u32_be(cd + 48, 0); /* teamOffset */ @@ -2278,7 +2288,8 @@ static void build_codesig_skeleton(MCtx* x, u32 code_limit, const char* ident) { static void compute_codesig(MCtx* x, const u8* full_file, u32 file_len_excl_cs, const char* ident) { - u32 code_page = 1u << CS_PAGE_SIZE_LOG2; + u32 page_log2 = x->cs_page_log2 ? x->cs_page_log2 : CS_PAGE_SIZE_LOG2; + u32 code_page = 1u << page_log2; u32 nslots = (file_len_excl_cs + code_page - 1u) / code_page; u32 ident_len = (u32)slice_from_cstr(ident).len + 1u; u8* cd = x->codesig.data + 12 + 8; @@ -2364,6 +2375,9 @@ void link_emit_macho(LinkImage* img, Writer* w) { x.w = w; x.linker = img->linker; x.link_arch = link_arch_desc_for(img->c); + /* Apple-Silicon arm64 mains must be signed with 16 KiB hash pages (the native + * VM page); x86_64 uses 4 KiB. See CS_PAGE_SIZE_LOG2. */ + x.cs_page_log2 = (img->c->target.arch == KIT_ARCH_ARM_64) ? 14u : 12u; { const ObjFormatImpl* fmt = obj_format_lookup(KIT_OBJ_MACHO); x.macho =