文件
PazeE-Language/.github/workflows/build-macos.yml
T
JGZ_YES 2ea642875f fix(macos): add MH_PIE flag + LC_DYLD_INFO_ONLY to fix SIGKILL on macOS
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
2026-08-05 14:10:34 +08:00

211 行
8.2 KiB
YAML
原始文件 Blame 文件历史

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
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.gzmacOS 标准归档格式,保留 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