tamior
2024·iOS · arm64·1,000+ downloads

CPython 3.12 for iOS (arm64)

Cross-compiling Python 3.12.5 for iOS, because the jailbreak community was stuck on 3.7.


The Motivation

While working on mobile security research, I kept running into the same problem: the only Python available for jailbroken iOS was 3.7. Most modern pip packages just didn’t work with it.

I wanted to run Frida directly on the iPhone, no laptop involved. The old Python version couldn’t handle modern frida-tools.

Nobody in the jailbreak community had ported a newer version. So I built one.

Community Impact

I open-sourced it and posted on r/jailbreak. Apparently a lot of people were stuck on the same problem: developers and researchers who needed a modern Python on-device and had no good options.

GitHub repository: github.com/k1tty-xz/python3.12-ios-arm64 (1,000+ downloads).

iOS makes running Python difficult. The platform blocks dynamic code loading and has no native compiler, so you have to cross-compile on macOS and build everything for arm64-apple-ios specifically.

The Challenge

The cross-compilation itself is the first problem. The build runs on macOS but produces binaries for arm64-apple-ios, and CPython’s configure script assumes it can execute whatever it builds. It can’t.

iOS also removes standard POSIX functions. system(), forkpty(), getentropy() are just gone, and standard CPython builds fail on these immediately. Dynamic linking is messy too: you can’t assume ssl or pip’s dependencies exist on the device, so they have to be bundled.

The Implementation

I compiled OpenSSL 1.1.1 as a static library and forced CPython to link against the .a files with -lssl -lcrypto. That way the ssl module ships with the package and pip install works over HTTPS without depending on anything external.

Rather than patching hundreds of source files for the missing system calls, I used a config.site file to pre-seed the configure script. That let me set cache variables to disable unsupported features like forkpty and skip cross-compilation checks:

# config.site overrides for arm64-apple-ios cross-compilation
ac_cv_file__dev_ptmx=no
ac_cv_file__dev_ptc=no
ac_cv_func_forkpty=no
ac_cv_func_openpty=no
ac_cv_func_getentropy=no
ac_cv_func_posix_spawn=yes
ac_cv_func_sendfile=no
ac_cv_header_sys_xattr_h=yes

iOS won’t run unsigned binaries, so the pipeline iterates through every .so and .dylib and signs them with ldid:

# Iterate through compiled shared objects and sign with ldid
find "$INSTALL_PREFIX" -name "*.so" -o -name "*.dylib" | while read -r binary; do
    echo "[*] Signing $binary"
    ldid -S "$binary"
done

# Sign the main python3 binary with entitlements
ldid -Sentitlements.xml "$INSTALL_PREFIX/bin/python3.12"

The whole thing runs on GitHub Actions: fetch source, apply patches, build for arm64, package as a .deb for Sileo and Zebra.

Result

Python 3.12.5 runs fully on-device. pip works, frida-tools installs cleanly, and you don’t need a laptop in the loop.