mirror of
https://github.com/Z3Prover/z3
synced 2026-07-04 22:36:10 +00:00
ci: validate Microsoft.Z3.dll PE Machine field is AnyCPU in nightly build validation (#9873)
`Microsoft.Z3.dll` ships with PE `Machine=0x8664` (AMD64), causing the CLR loader to reject it on arm64 .NET hosts (macOS/Linux/Windows ARM) even though the assembly is pure IL (`CorFlags.ILONLY=True`) and arm64 native libraries are already bundled in the package. ## Changes - **`.github/workflows/nightly-validation.yml`** — adds `validate-dotnet-anycpu` job to the Nightly Build Validation workflow: - Downloads the nightly NuGet package from the GitHub release - Extracts `lib/netstandard2.0/Microsoft.Z3.dll` (NuGet packages are ZIP archives) - Reads the COFF `Machine` field from the PE header using Python `struct` - Fails with an actionable error if `Machine` is `0x8664` (AMD64) or `0xAA64` (ARM64); passes for `0x014C` (i386/AnyCPU) or `0x0000` The check catches any regression where the managed wrapper is compiled architecture-specific, blocking non-x64 .NET hosts from loading it. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
This commit is contained in:
parent
b6c7ef2f68
commit
66795ea322
1 changed files with 94 additions and 0 deletions
94
.github/workflows/nightly-validation.yml
vendored
94
.github/workflows/nightly-validation.yml
vendored
|
|
@ -844,3 +844,97 @@ jobs:
|
||||||
|
|
||||||
- name: Run build script unit tests
|
- name: Run build script unit tests
|
||||||
run: python -m unittest discover -s scripts/tests -p "test_*.py" -v
|
run: python -m unittest discover -s scripts/tests -p "test_*.py" -v
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# DOTNET MANAGED WRAPPER ARCHITECTURE VALIDATION
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
validate-dotnet-anycpu:
|
||||||
|
name: "Validate Microsoft.Z3.dll is AnyCPU (issue #9863)"
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name == 'workflow_dispatch' }}
|
||||||
|
timeout-minutes: 15
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v6.0.3
|
||||||
|
|
||||||
|
- name: Download NuGet package from release
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ github.token }}
|
||||||
|
run: |
|
||||||
|
TAG="${{ github.event.inputs.release_tag }}"
|
||||||
|
if [ -z "$TAG" ]; then
|
||||||
|
TAG="Nightly"
|
||||||
|
fi
|
||||||
|
gh release download $TAG --pattern "*.nupkg" --dir nuget-packages
|
||||||
|
|
||||||
|
- name: Extract managed DLL from NuGet package
|
||||||
|
run: |
|
||||||
|
# NuGet packages are ZIP archives; exclude the symbols package
|
||||||
|
NUPKG=$(ls nuget-packages/*.nupkg | grep -v '\.symbols\.' | grep -v '\.snupkg' | head -n 1)
|
||||||
|
echo "Checking package: $NUPKG"
|
||||||
|
unzip -q "$NUPKG" "lib/netstandard2.0/Microsoft.Z3.dll" -d nupkg-extracted
|
||||||
|
|
||||||
|
- name: Check PE Machine field is AnyCPU (not architecture-specific)
|
||||||
|
run: |
|
||||||
|
python3 - <<'EOF'
|
||||||
|
import struct
|
||||||
|
import sys
|
||||||
|
|
||||||
|
dll_path = "nupkg-extracted/lib/netstandard2.0/Microsoft.Z3.dll"
|
||||||
|
|
||||||
|
with open(dll_path, 'rb') as f:
|
||||||
|
# Verify MZ magic
|
||||||
|
if f.read(2) != b'MZ':
|
||||||
|
print("ERROR: Not a valid PE file (missing MZ header)")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Read PE header offset stored at 0x3C in the DOS stub
|
||||||
|
f.seek(0x3C)
|
||||||
|
pe_offset = struct.unpack('<I', f.read(4))[0]
|
||||||
|
|
||||||
|
# Verify PE signature
|
||||||
|
f.seek(pe_offset)
|
||||||
|
if f.read(4) != b'PE\x00\x00':
|
||||||
|
print("ERROR: Missing PE\\0\\0 signature")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# COFF Machine field is the 2 bytes immediately after the PE signature
|
||||||
|
machine = struct.unpack('<H', f.read(2))[0]
|
||||||
|
|
||||||
|
machine_names = {
|
||||||
|
0x0000: "Unknown/AnyCPU",
|
||||||
|
0x014C: "i386 (AnyCPU for managed assemblies)",
|
||||||
|
0x8664: "AMD64/x64",
|
||||||
|
0xAA64: "ARM64",
|
||||||
|
0x01C0: "ARM",
|
||||||
|
0x01C4: "ARM Thumb-2",
|
||||||
|
}
|
||||||
|
machine_name = machine_names.get(machine, f"0x{machine:04X}")
|
||||||
|
print(f"Machine field: 0x{machine:04X} = {machine_name}")
|
||||||
|
|
||||||
|
if machine == 0x8664:
|
||||||
|
print()
|
||||||
|
print("FAIL: Machine is AMD64 (0x8664).")
|
||||||
|
print("This prevents loading on arm64 .NET hosts even though the assembly")
|
||||||
|
print("contains only pure IL (CorFlags.ILONLY=True).")
|
||||||
|
print("The DLL must be built as AnyCPU (Machine=0x014C) so the CLR loader")
|
||||||
|
print("accepts it on every host architecture.")
|
||||||
|
print("See issue #9863 for details.")
|
||||||
|
sys.exit(1)
|
||||||
|
elif machine == 0xAA64:
|
||||||
|
print()
|
||||||
|
print("FAIL: Machine is ARM64 (0xAA64).")
|
||||||
|
print("This prevents loading on x64 .NET hosts.")
|
||||||
|
print("The DLL must be built as AnyCPU (Machine=0x014C).")
|
||||||
|
sys.exit(1)
|
||||||
|
elif machine in (0x014C, 0x0000):
|
||||||
|
print()
|
||||||
|
print("PASS: Machine field indicates AnyCPU.")
|
||||||
|
print("Microsoft.Z3.dll will load on any .NET host architecture.")
|
||||||
|
else:
|
||||||
|
print()
|
||||||
|
print(f"FAIL: Unexpected Machine field 0x{machine:04X}.")
|
||||||
|
print("Expected 0x014C (i386/AnyCPU) for a managed-only assembly.")
|
||||||
|
sys.exit(1)
|
||||||
|
EOF
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue