diff --git a/src/PazeE.Compiler/Binary/MachOCodeSignature.cs b/src/PazeE.Compiler/Binary/MachOCodeSignature.cs
new file mode 100644
index 0000000..dd1794c
--- /dev/null
+++ b/src/PazeE.Compiler/Binary/MachOCodeSignature.cs
@@ -0,0 +1,94 @@
+using System.Security.Cryptography;
+using System.Text;
+
+namespace PazeE.Compiler.Binary;
+
+/// 生成 macOS ad-hoc 代码签名(SuperBlob + CodeDirectory,SHA-256)。
+/// macOS 11+(尤其 Apple Silicon)要求所有可执行文件至少有 ad-hoc 签名,否则内核 SIGKILL。
+/// 代码签名 blob 使用大端字节序(CSBlob 规范),与 Mach-O 主体的小端序不同。
+internal static class MachOCodeSignature
+{
+ private const uint CSMAGIC_EMBEDDED_SIGNATURE = 0xFade0CC0; // SuperBlob
+ private const uint CSMAGIC_CODEDIRECTORY = 0xFade0C02; // CodeDirectory
+ private const uint CSSLOT_CODEDIRECTORY = 0; // blob index type
+ private const int CS_PAGE_SIZE = 4096;
+ private const byte CS_PAGE_SHIFT = 12; // log2(4096)
+ private const byte CS_HASHTYPE_SHA256 = 2;
+ private const byte CS_HASHSIZE_SHA256 = 32;
+ private const string DefaultIdentifier = "paze-compiled";
+
+ // CodeDirectory 固定部分(version 0x20001,无 scatterOffset/teamOffset)
+ private const int CdFixedLen = 44;
+ // SuperBlob 头(12) + 1 个 BlobIndex(8)
+ private const int SbHeaderLen = 12 + 8;
+
+ /// 计算代码签名 blob 的确切大小(字节),用于在写入头之前确定 __LINKEDIT 段尺寸。
+ public static int ComputeBlobSize(int codeLimit, string? identifier = null)
+ {
+ int identLen = Encoding.ASCII.GetBytes((identifier ?? DefaultIdentifier) + "\0").Length;
+ int nCodeSlots = (codeLimit + CS_PAGE_SIZE - 1) / CS_PAGE_SIZE;
+ int cdLength = CdFixedLen + identLen + nCodeSlots * CS_HASHSIZE_SHA256;
+ return SbHeaderLen + cdLength;
+ }
+
+ /// 生成代码签名 blob(SuperBlob 包裹 CodeDirectory)。
+ /// fileData: 整个文件内容(从 0 到签名 blob 起始位置),codeLimit: 文件中代码部分的字节数(=签名 blob 起始偏移)。
+ public static byte[] Build(byte[] fileData, int codeLimit, string? identifier = null)
+ {
+ string ident = identifier ?? DefaultIdentifier;
+ byte[] identBytes = Encoding.ASCII.GetBytes(ident + "\0");
+ int identLen = identBytes.Length;
+ int nCodeSlots = (codeLimit + CS_PAGE_SIZE - 1) / CS_PAGE_SIZE;
+
+ int identOffset = CdFixedLen;
+ int hashOffset = CdFixedLen + identLen;
+ int cdLength = hashOffset + nCodeSlots * CS_HASHSIZE_SHA256;
+
+ // ---- CodeDirectory ----
+ var cd = new List(cdLength);
+ Write32BE(cd, CSMAGIC_CODEDIRECTORY); // magic
+ Write32BE(cd, (uint)cdLength); // length
+ Write32BE(cd, 0x20001); // version
+ Write32BE(cd, 0); // flags (ad-hoc)
+ Write32BE(cd, (uint)hashOffset); // hashOffset
+ Write32BE(cd, (uint)identOffset); // identOffset
+ Write32BE(cd, 0); // nSpecialSlots
+ Write32BE(cd, (uint)nCodeSlots); // nCodeSlots
+ Write32BE(cd, (uint)codeLimit); // codeLimit
+ cd.Add(CS_HASHSIZE_SHA256); // hashSize
+ cd.Add(CS_HASHTYPE_SHA256); // hashType
+ cd.Add(0); // platform
+ cd.Add(CS_PAGE_SHIFT); // pageSize (log2)
+ Write32BE(cd, 0); // spare2
+ cd.AddRange(identBytes); // identifier (null-terminated)
+ // 代码页 SHA-256 哈希
+ using var sha256 = SHA256.Create();
+ for (int i = 0; i < nCodeSlots; i++)
+ {
+ int pageOff = i * CS_PAGE_SIZE;
+ int pageLen = Math.Min(CS_PAGE_SIZE, codeLimit - pageOff);
+ byte[] hash = sha256.ComputeHash(fileData, pageOff, pageLen);
+ cd.AddRange(hash);
+ }
+
+ // ---- SuperBlob ----
+ int sbLength = SbHeaderLen + cdLength;
+ var sb = new List(sbLength);
+ Write32BE(sb, CSMAGIC_EMBEDDED_SIGNATURE); // magic
+ Write32BE(sb, (uint)sbLength); // length
+ Write32BE(sb, 1); // count (1 blob)
+ Write32BE(sb, CSSLOT_CODEDIRECTORY); // index[0].type
+ Write32BE(sb, (uint)SbHeaderLen); // index[0].offset
+ sb.AddRange(cd); // CodeDirectory
+
+ return sb.ToArray();
+ }
+
+ private static void Write32BE(List b, uint v)
+ {
+ b.Add((byte)(v >> 24));
+ b.Add((byte)(v >> 16));
+ b.Add((byte)(v >> 8));
+ b.Add((byte)v);
+ }
+}
diff --git a/src/PazeE.Compiler/Binary/MachOWriter.cs b/src/PazeE.Compiler/Binary/MachOWriter.cs
index 3d7e5d9..e2066c5 100644
--- a/src/PazeE.Compiler/Binary/MachOWriter.cs
+++ b/src/PazeE.Compiler/Binary/MachOWriter.cs
@@ -28,6 +28,7 @@ public sealed class MachOWriter : IExecutableWriter
private const uint LC_LOAD_DYLINKER = 0x0E;
private const uint LC_DYSYMTAB = 0x0B;
private const uint LC_MAIN = 0x80000028;
+ private const uint LC_CODE_SIGNATURE = 0x1D;
private const int VM_PROT_READ = 1, VM_PROT_WRITE = 2, VM_PROT_EXECUTE = 4;
private const int S_NON_LAZY_SYMBOL_POINTERS = 0x06;
// nlist n_type
@@ -158,8 +159,9 @@ public sealed class MachOWriter : IExecutableWriter
int lcMain = 24;
int lcSymtab = 24;
int lcDysymtab = 80;
- int sizeofcmds = segTextCmd + segDataCmd + segLinkCmd + lcDylinker + lcDylib + lcMain + lcSymtab + lcDysymtab;
- int ncmds = 8;
+ int lcCodeSig = 16; // linkedit_data_command(cmd+cmdsize+dataoff+datasize)
+ int sizeofcmds = segTextCmd + segDataCmd + segLinkCmd + lcDylinker + lcDylib + lcMain + lcSymtab + lcDysymtab + lcCodeSig;
+ int ncmds = 9;
int headerSize = 32;
int textFileOff = headerSize + sizeofcmds; // __text 文件偏移
@@ -190,7 +192,11 @@ public sealed class MachOWriter : IExecutableWriter
long symtabOff = bindOff + bind.Count;
long indirectOff = symtabOff + symtab.Count;
long strtabOff = indirectOff + indirect.Count;
- long linkSegFileEnd = strtabOff + strtab.Count;
+ // 代码签名(ad-hoc):16 字节对齐后附加到 __LINKEDIT 末尾
+ long sigOff = Align(strtabOff + strtab.Count, 16);
+ int codeLimit = (int)sigOff;
+ int sigBlobSize = MachOCodeSignature.ComputeBlobSize(codeLimit);
+ long linkSegFileEnd = sigOff + sigBlobSize;
long linkSegVmEnd = linkSegVmaddr + (linkSegFileEnd - linkSegFileOff);
// ---- 填充 main 符号 n_value ----
@@ -325,6 +331,12 @@ public sealed class MachOWriter : IExecutableWriter
Write32At(f2, 0); Write32At(f2, 0); // extreloff, nextrel
Write32At(f2, 0); Write32At(f2, 0); // locreloff, nlocrel
+ // ---- LC_CODE_SIGNATURE ----
+ Write32At(f2, LC_CODE_SIGNATURE);
+ Write32At(f2, 16); // cmdsize (linkedit_data_command)
+ Write32At(f2, (uint)sigOff); // dataoff
+ Write32At(f2, (uint)sigBlobSize); // datasize
+
// ---- __TEXT 段数据 ----
f2.AddRange(text);
f2.AddRange(cstring);
@@ -341,6 +353,10 @@ public sealed class MachOWriter : IExecutableWriter
f2.AddRange(symtab);
f2.AddRange(indirect);
f2.AddRange(strtab);
+ // 代码签名(ad-hoc SHA-256):对文件 [0..codeLimit) 逐页哈希
+ while (f2.Count < sigOff) f2.Add(0);
+ byte[] sigBlob = MachOCodeSignature.Build(f2.ToArray(), codeLimit);
+ f2.AddRange(sigBlob);
return f2.ToArray();
}
diff --git a/src/PazeE.Compiler/Binary/MachOWriterArm64.cs b/src/PazeE.Compiler/Binary/MachOWriterArm64.cs
index 130fe97..9e46591 100644
--- a/src/PazeE.Compiler/Binary/MachOWriterArm64.cs
+++ b/src/PazeE.Compiler/Binary/MachOWriterArm64.cs
@@ -31,6 +31,7 @@ public sealed class MachOWriterArm64 : IExecutableWriter
private const uint LC_LOAD_DYLINKER = 0x0E;
private const uint LC_DYSYMTAB = 0x0B;
private const uint LC_MAIN = 0x80000028;
+ private const uint LC_CODE_SIGNATURE = 0x1D;
private const int VM_PROT_READ = 1, VM_PROT_WRITE = 2, VM_PROT_EXECUTE = 4;
private const int S_NON_LAZY_SYMBOL_POINTERS = 0x06;
private const byte N_EXT = 0x01, N_SECT = 0x0e;
@@ -137,8 +138,9 @@ public sealed class MachOWriterArm64 : IExecutableWriter
int lcMain = 24;
int lcSymtab = 24;
int lcDysymtab = 80;
- int sizeofcmds = segTextCmd + segDataCmd + segLinkCmd + lcDylinker + lcDylib + lcMain + lcSymtab + lcDysymtab;
- int ncmds = 8;
+ int lcCodeSig = 16; // linkedit_data_command(cmd+cmdsize+dataoff+datasize)
+ int sizeofcmds = segTextCmd + segDataCmd + segLinkCmd + lcDylinker + lcDylib + lcMain + lcSymtab + lcDysymtab + lcCodeSig;
+ int ncmds = 9;
int headerSize = 32;
int textFileOff = headerSize + sizeofcmds;
@@ -168,7 +170,11 @@ public sealed class MachOWriterArm64 : IExecutableWriter
long symtabOff = bindOff + bind.Count;
long indirectOff = symtabOff + symtab.Count;
long strtabOff = indirectOff + indirect.Count;
- long linkSegFileEnd = strtabOff + strtab.Count;
+ // 代码签名(ad-hoc):16 字节对齐后附加到 __LINKEDIT 末尾
+ long sigOff = Align(strtabOff + strtab.Count, 16);
+ int codeLimit = (int)sigOff;
+ int sigBlobSize = MachOCodeSignature.ComputeBlobSize(codeLimit);
+ long linkSegFileEnd = sigOff + sigBlobSize;
long linkSegVmEnd = linkSegVmaddr + (linkSegFileEnd - linkSegFileOff);
// ---- 填充 main 符号 n_value ----
@@ -323,6 +329,12 @@ public sealed class MachOWriterArm64 : IExecutableWriter
Write32At(f2, 0); Write32At(f2, 0);
Write32At(f2, 0); Write32At(f2, 0);
+ // ---- LC_CODE_SIGNATURE ----
+ Write32At(f2, LC_CODE_SIGNATURE);
+ Write32At(f2, 16); // cmdsize (linkedit_data_command)
+ Write32At(f2, (uint)sigOff); // dataoff
+ Write32At(f2, (uint)sigBlobSize); // datasize
+
// ---- __TEXT 段数据 ----
f2.AddRange(text);
f2.AddRange(cstring);
@@ -338,6 +350,10 @@ public sealed class MachOWriterArm64 : IExecutableWriter
f2.AddRange(symtab);
f2.AddRange(indirect);
f2.AddRange(strtab);
+ // 代码签名(ad-hoc SHA-256):对文件 [0..codeLimit) 逐页哈希
+ while (f2.Count < sigOff) f2.Add(0);
+ byte[] sigBlob = MachOCodeSignature.Build(f2.ToArray(), codeLimit);
+ f2.AddRange(sigBlob);
return f2.ToArray();
}