Root cause of 'zsh: killed' (SIGKILL) was two missing Mach-O elements: 1. Missing MH_PIE flag (0x200000): macOS 11+ (especially Apple Silicon/arm64) requires all executables to be Position Independent. The kernel kills non-PIE binaries with SIGKILL before even checking the code signature. This is why even empty.pe (no externals) was killed. 2. Missing LC_DYLD_INFO_ONLY (0x80000022): The BIND opcodes for external symbol resolution were generated but never referenced by any load command. dyld had no way to find them, so symbols like printf were never bound. Now properly referenced via dyld_info_command. Additional changes: - Generate rebase opcodes for Abs64 fixups (required for PIE: dyld adds ASLR slide to absolute addresses in .data at runtime) - Reorganize LINKEDIT layout: rebase -> bind -> symtab -> indirect -> strtab -> sig - Update ncmds from 11 to 12, sizeofcmds += 48 (dyld_info_command size) - Applied to both MachOWriter.cs (x64) and MachOWriterArm64.cs (arm64) - Workflow: add PIE/LC_DYLD_INFO_ONLY verification + time.pe run test
211 行
8.2 KiB
YAML
211 行
8.2 KiB
YAML
name: build-macos
|
||
|
||
on:
|
||
push:
|
||
branches: [ main, master ]
|
||
tags: [ 'v*' ]
|
||
pull_request:
|
||
branches: [ main, master ]
|
||
workflow_dispatch:
|
||
|
||
jobs:
|
||
build:
|
||
name: Build paze for macOS (${{ matrix.arch }})
|
||
strategy:
|
||
fail-fast: false
|
||
matrix:
|
||
include:
|
||
# macos-13 (x64 runner) 长期排队/不分配,改用 macos-14 (arm64) 交叉编译两个架构。
|
||
# .NET 在 arm64 主机上 `publish -r osx-x64` 产出原生 x64 单文件;x64 产物经 Rosetta 2 可在 arm64 上运行(冒烟测试仍生效)。
|
||
- arch: x64
|
||
runner: macos-14
|
||
rid: osx-x64
|
||
- arch: arm64
|
||
runner: macos-14
|
||
rid: osx-arm64
|
||
runs-on: ${{ matrix.runner }}
|
||
|
||
steps:
|
||
- name: Checkout
|
||
uses: actions/checkout@v4
|
||
|
||
- name: Setup .NET 10
|
||
uses: actions/setup-dotnet@v4
|
||
with:
|
||
dotnet-version: '10.0.x'
|
||
dotnet-quality: 'preview'
|
||
|
||
- name: Restore
|
||
run: dotnet restore src/PazeE.Compiler/PazeE.Compiler.csproj
|
||
|
||
- name: Determine channel & version
|
||
id: meta
|
||
run: |
|
||
if [[ "${GITHUB_REF}" == refs/tags/v* ]]; then
|
||
CHANNEL="release"
|
||
else
|
||
CHANNEL="alpha"
|
||
fi
|
||
VERSION=$(dotnet msbuild src/PazeE.Compiler/PazeE.Compiler.csproj \
|
||
-p:BuildChannel=$CHANNEL \
|
||
-getProperty:Version \
|
||
-nologo)
|
||
echo "channel=$CHANNEL" >> "$GITHUB_OUTPUT"
|
||
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
||
echo "Channel: $CHANNEL Version: $VERSION"
|
||
|
||
- name: Publish (self-contained, ${{ matrix.rid }})
|
||
run: |
|
||
dotnet publish src/PazeE.Compiler/PazeE.Compiler.csproj \
|
||
-c Release \
|
||
-p:BuildChannel=${{ steps.meta.outputs.channel }} \
|
||
-r ${{ matrix.rid }} \
|
||
--self-contained true \
|
||
-p:PublishSingleFile=true \
|
||
-p:EnableCompressionInSingleFile=true
|
||
|
||
- name: Stage artifacts
|
||
run: |
|
||
STAGE="staging/paze-${{ steps.meta.outputs.version }}-macos-${{ matrix.arch }}"
|
||
mkdir -p "$STAGE/bin"
|
||
cp -v publish/${{ steps.meta.outputs.version }}/bin/* "$STAGE/bin/" 2>/dev/null || true
|
||
# 单文件模式下主程序重命名为 paze 并赋予执行权限
|
||
if [ -f "$STAGE/bin/paze" ]; then
|
||
chmod +x "$STAGE/bin/paze"
|
||
fi
|
||
# 包含测试源文件以便验证
|
||
mkdir -p "$STAGE/tests"
|
||
cp -v tests/*.pe "$STAGE/tests/" 2>/dev/null || true
|
||
# 生成版本说明
|
||
cat > "$STAGE/VERSION.txt" <<EOF
|
||
PazeE Compiler
|
||
Version: ${{ steps.meta.outputs.version }}
|
||
Channel: ${{ steps.meta.outputs.channel }}
|
||
Target: ${{ matrix.rid }} (self-contained .NET 10)
|
||
Commit: ${GITHUB_SHA}
|
||
Built: ${GITHUB_RUN_ID}
|
||
EOF
|
||
# 打包为 tar.gz(macOS 标准归档格式,保留 POSIX 权限)
|
||
cd staging
|
||
tar -czf ../paze-${{ steps.meta.outputs.version }}-macos-${{ matrix.arch }}.tar.gz *
|
||
cd ..
|
||
ls -lh paze-*.tar.gz
|
||
|
||
- name: Smoke test — compile hello.pe
|
||
env:
|
||
ARCH_FLAG: ${{ matrix.arch == 'arm64' && '--arch arm' || '' }}
|
||
run: |
|
||
set +e
|
||
BIN="staging/paze-${{ steps.meta.outputs.version }}-macos-${{ matrix.arch }}/bin"
|
||
PAZE="$BIN/paze"
|
||
if [ -f "$BIN/paze.dll" ] && [ ! -f "$PAZE" ]; then PAZE="dotnet $BIN/paze.dll"; fi
|
||
if [ -z "$PAZE" ] || [ ! -f "$BIN/paze" ]; then
|
||
echo "WARN: paze binary not found, listing bin/:"; ls -la "$BIN"; exit 0
|
||
fi
|
||
$PAZE tests/hello.pe --target macos $ARCH_FLAG -o /tmp/hello.macos
|
||
file /tmp/hello.macos
|
||
chmod +x /tmp/hello.macos
|
||
|
||
echo "=== verify PIE flag + LC_DYLD_INFO_ONLY ==="
|
||
otool -hv /tmp/hello.macos | head -5
|
||
otool -l /tmp/hello.macos | grep -E "LC_DYLD_INFO_ONLY|LC_CODE_SIGNATURE|LC_UUID|LC_BUILD_VERSION" || true
|
||
|
||
# 编译 time.pe(用户报告的被 kill 的程序)
|
||
$PAZE tests/time.pe --target macos $ARCH_FLAG -o /tmp/time.macos 2>&1 || true
|
||
chmod +x /tmp/time.macos 2>/dev/null || true
|
||
|
||
echo "=== codesign display (paze-generated) ==="
|
||
codesign -dv --verbose=4 /tmp/hello.macos 2>&1 || true
|
||
echo "=== codesign verify (paze-generated) ==="
|
||
codesign --verify --deep --strict /tmp/hello.macos 2>&1 && echo "SIGNATURE OK" || echo "SIGNATURE FAILED"
|
||
|
||
echo "=== try run (paze-generated signature) ==="
|
||
/tmp/hello.macos 2>&1; echo "exit code: $?"
|
||
|
||
echo "=== try run time.macos ==="
|
||
/tmp/time.macos 2>&1; echo "time exit code: $?"
|
||
|
||
# 对照实验:用系统 codesign -s - 重新签名,排除 paze 签名生成器的问题
|
||
echo "=== re-sign with system codesign -s - ==="
|
||
cp /tmp/hello.macos /tmp/hello_resigned.macos
|
||
chmod +x /tmp/hello_resigned.macos
|
||
codesign -s - --force /tmp/hello_resigned.macos 2>&1 || true
|
||
codesign -dv --verbose=4 /tmp/hello_resigned.macos 2>&1 || true
|
||
echo "=== try run (system re-signed) ==="
|
||
/tmp/hello_resigned.macos 2>&1; echo "exit code: $?"
|
||
|
||
# 对比两次签名的差异
|
||
echo "=== signature size comparison ==="
|
||
echo "paze-generated:"; codesign -d /tmp/hello.macos 2>&1 || true
|
||
echo "system-signed:"; codesign -d /tmp/hello_resigned.macos 2>&1 || true
|
||
|
||
# 空 main 测试:排除 printf/外部符号问题
|
||
echo "=== empty main test (no externals) ==="
|
||
$PAZE tests/empty.pe --target macos $ARCH_FLAG -o /tmp/empty.macos
|
||
file /tmp/empty.macos
|
||
chmod +x /tmp/empty.macos
|
||
/tmp/empty.macos 2>&1; echo "empty exit code: $?"
|
||
|
||
# AMFI 内核日志
|
||
echo "=== AMFI kernel log (last 30s) ==="
|
||
log show --predicate 'sender == "amfid" OR sender == "AppleMobileFileIntegrity"' --last 30s 2>&1 | tail -20 || true
|
||
|
||
# dyld 调试输出
|
||
echo "=== dyld debug (hello.macos) ==="
|
||
DYLD_PRINT_LIBRARIES=1 DYLD_PRINT_BINDINGS=1 /tmp/hello.macos 2>&1 | head -20; echo "dyld exit: $?"
|
||
|
||
# clang 对比测试:用 clang 编译同样逻辑的程序,对比 Mach-O 结构
|
||
echo "=== clang reference build ==="
|
||
echo 'int main(void){return 0;}' > /tmp/ref.c
|
||
clang -o /tmp/ref.macos /tmp/ref.c
|
||
file /tmp/ref.macos
|
||
/tmp/ref.macos; echo "clang ref exit: $?"
|
||
echo "--- clang ref otool -l (segments) ---"
|
||
otool -l /tmp/ref.macos 2>&1 | grep -E "LC_SEGMENT|segname|vmaddr|vmsize|fileoff|filesize|cmd LC" | head -40
|
||
echo "--- paze hello otool -l (segments) ---"
|
||
otool -l /tmp/hello.macos 2>&1 | grep -E "LC_SEGMENT|segname|vmaddr|vmsize|fileoff|filesize|cmd LC" | head -40
|
||
|
||
exit 0
|
||
|
||
- name: Dump Mach-O load commands
|
||
if: always()
|
||
run: |
|
||
echo "=== otool -L (dylib deps) ==="
|
||
otool -L /tmp/hello.macos 2>&1 || true
|
||
echo "=== otool -l (load commands) ==="
|
||
otool -l /tmp/hello.macos 2>&1 | head -150 || true
|
||
echo "=== dyld_info ==="
|
||
dyld_info /tmp/hello.macos 2>&1 || true
|
||
echo "=== dyld_info symbols ==="
|
||
dyld_info -symbols /tmp/hello.macos 2>&1 || true
|
||
|
||
- name: Upload artifact
|
||
uses: actions/upload-artifact@v4
|
||
with:
|
||
name: paze-${{ steps.meta.outputs.version }}-macos-${{ matrix.arch }}
|
||
path: paze-${{ steps.meta.outputs.version }}-macos-${{ matrix.arch }}.tar.gz
|
||
if-no-files-found: error
|
||
|
||
release:
|
||
name: Publish GitHub Release (macOS)
|
||
needs: build
|
||
if: startsWith(github.ref, 'refs/tags/v')
|
||
runs-on: ubuntu-latest
|
||
permissions:
|
||
contents: write
|
||
|
||
steps:
|
||
- name: Download artifacts
|
||
uses: actions/download-artifact@v4
|
||
with:
|
||
path: artifacts
|
||
|
||
- name: Create release
|
||
uses: softprops/action-gh-release@v2
|
||
with:
|
||
name: PazeE ${{ github.ref_name }}
|
||
tag_name: ${{ github.ref_name }}
|
||
generate_release_notes: true
|
||
files: |
|
||
artifacts/**/*.tar.gz
|