Skip to content

macOS wheels statically link an OpenSSL compiled without optimisation #15702

Description

@moathfarajat-commits

The OpenSSL in the macOS wheels is built with no -O flag, so clang compiles it at -O0. OpenSSL records its own compiler line, and in the arm64 wheel of 50.0.1 it reads:

compiler: cc -fPIC -arch arm64 -mmacosx-version-min=11.0 -DL_ENDIAN -DOPENSSL_PIC -D_REENTRANT -DNDEBUG

The manylinux x86-64 wheel of the same release has -O3 there.

Cause. The "Build OpenSSL" step in pyca/infra's .github/workflows/build-macos-openssl.yml sets CFLAGS in its environment (-mmacosx-version-min=11.0 on arm64, -mmacosx-version-min=10.12 -march=core2 on x86-64). OpenSSL's Configure treats an environment CFLAGS as a replacement for the target's defaults, not an addition, so the -O3 -Wall that darwin64-arm64-cc normally uses is dropped. With OpenSSL 4.0.2:

$ CFLAGS="-mmacosx-version-min=11.0" perl Configure darwin64-arm64-cc no-shared
$ grep '^CFLAGS=' Makefile
CFLAGS=-mmacosx-version-min=11.0

$ perl Configure darwin64-arm64-cc no-shared -mmacosx-version-min=11.0
$ grep '^CFLAGS=' Makefile
CFLAGS=-O3 -Wall -mmacosx-version-min=11.0

Which wheels. Every macOS wheel I checked: arm64 3.4.8, 35.0.0, 36.0.0 and 50.0.1; x86-64 3.4; and both slices of the 42.0.0 universal2 wheel.

Effect. Whatever OpenSSL implements in C runs about ten times slower. Whatever has an assembly path barely changes, which is probably why this has gone unnoticed: SHA-2, SHA-3 and P-256 look normal. On arm64 every post-quantum algorithm is C. Apple M1 Pro, cryptography 50.0.1 over OpenSSL 4.0.2, the wheel against the same two releases built from source with -O3, median of 1,000 calls in ms:

Operation OpenSSL path -O3 wheel ratio
SHA3-256, 64 KiB assembly 0.0722 0.0725 1.0×
ECDSA P-256 sign assembly 0.0221 0.0262 1.2×
Ed25519 sign C + assembly 0.0295 0.0923 3.1×
ML-DSA-65 sign C 0.4871 4.4833 9.2×
ML-DSA-65 verify C 0.1075 0.8487 7.9×
ML-KEM-768 encapsulate C 0.0130 0.1323 10.2×
ML-KEM-768 decapsulate C 0.0181 0.1995 11.0×

To check an installation:

python -c "import cryptography,glob,os;so=glob.glob(os.path.join(os.path.dirname(cryptography.__file__),'hazmat','bindings','_rust*.so'))[0];b=open(so,'rb').read();i=b.find(b'compiler: ');print(b[i:b.index(b'\0',i)].decode())"

I found this while benchmarking ML-DSA for a measurement study. A fix is in pyca/infra#796.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions