Replaces scripts/release/openssl/build_crypto_static_*.sh (×4).
Compiles OTP's crypto NIF C sources with -DSTATIC_ERLANG_NIF for one
target ABI and archives the result as crypto.a. Pairs with
MobDev.Release.OpenSSL (the OpenSSL build itself) to produce the
two .a files that get static-linked into the user app's main
native binary:
$OPENSSL_PREFIX/lib/libcrypto.a ← from MobDev.Release.OpenSSL
$OTP_SRC/lib/crypto/priv/lib/<arch>/crypto.a ← from this moduleWhy static-link the crypto NIF?
Different reasons on each platform; the artifact is the same.
- Android. dlopen'd children inherit
RTLD_LOCALby default, which hides the parent'senif_*symbols fromcrypto.so.crypto.so'son_loadthen fails with "cannot locate symbol". Static linking sidesteps that — the BEAM findscrypto_nif_initviadlsym(RTLD_DEFAULT)instead of dlopen. - iOS. App Store forbids loading unsigned dylib/dlopen — every NIF must be present in the final signed binary. Same artifact.
Per-target deltas (the spec)
All four targets compile the same 30 source files (@sources) with a
shared base CFLAGS list (@base_cflags). The deltas are:
| Target | Arch dir | Toolchain | Extra CFLAGS | nm symbol |
|---|---|---|---|---|
| android_arm64 | aarch64-unknown-linux-android | NDK clang/llvm-ar | Android hardening: branch-protect, stack-clash, _GNU_SOURCE | crypto_nif_init |
| android_arm32 | arm-unknown-linux-androideabi | NDK clang/llvm-ar | Android hardening + -march=armv7-a -mfloat-abi=softfp -mthumb | crypto_nif_init |
| ios_sim | aarch64-apple-iossimulator | xcrun (sim SDK) | iOS minimal — no Android hardening | _crypto_nif_init |
| ios_device | aarch64-apple-ios | xcrun (device SDK) | iOS minimal — no Android hardening | _crypto_nif_init |
Phases
Each build/2 call:
- Precheck — OTP source + OpenSSL prefix exist; Android/iOS toolchain reachable.
- Compile — for each of @sources, run
<cc> <cflags> -c -o obj src. - Archive —
<ar> rcs crypto.a obj1 obj2 ...then<ranlib> crypto.a. - Verify —
<nm> crypto.a, scan output for the expected symbol. Symbol missing is a:precondition_failed(means our compile didn't actually producecrypto_nif_init— usually because the OTP source moved out from under us).
Summary
Functions
Base CFLAGS shared across all targets. Public for testing.
Compile + archive + verify the crypto NIF for one target. Returns
{:ok, info} naming the produced archive, or a tagged error.
Assemble the full CFLAGS list for a target, given OpenSSL prefix + OTP source path. Pure function for testability — silent flag drops are the exact regression class this module exists to prevent.
Parse nm output and confirm the expected crypto_nif_init symbol
is exported (T flag in nm's output). Returns :ok or a tagged
precondition_failed.
Source files compiled for every target. Public so tests can pin the surface.
Per-target spec. Public so tests can lock down the surface
(especially the extra_cflags lists — silent drops there would
silently weaken released binaries).
All known crypto-NIF targets. Same set as MobDev.Release.OpenSSL.targets/0.
Functions
@spec base_cflags() :: [String.t()]
Base CFLAGS shared across all targets. Public for testing.
@spec build( atom(), keyword() ) :: {:ok, map()} | MobDev.Release.Errors.t()
Compile + archive + verify the crypto NIF for one target. Returns
{:ok, info} naming the produced archive, or a tagged error.
Options:
:otp_src— OTP source checkout (default:$OTP_SRCenv or~/code/otp):openssl_prefix— OpenSSL install dir (default: target'sdefault_prefix):ndk_root— Android NDK root (Android targets only)
@spec cflags(MobDev.Release.OpenSSL.CryptoNif.Target.t(), Path.t(), Path.t()) :: [ String.t() ]
Assemble the full CFLAGS list for a target, given OpenSSL prefix + OTP source path. Pure function for testability — silent flag drops are the exact regression class this module exists to prevent.
Order: base CFLAGS + per-target extras + include paths. Includes
carry the arch_dir suffix on OTP-internal headers because OTP
per-arch's erl_int_sizes_config.h lives there.
@spec check_symbol_present(binary(), String.t(), Path.t()) :: :ok | MobDev.Release.Errors.t()
Parse nm output and confirm the expected crypto_nif_init symbol
is exported (T flag in nm's output). Returns :ok or a tagged
precondition_failed.
| Public for testing — the shell version did `nm <archive> | grep -E |
| ' T crypto_nif_init$' | head -3` and silently let release ship if |
the grep returned 0 lines. This module's behaviour: missing symbol is a hard failure with a clear message.
@spec sources() :: [String.t()]
Source files compiled for every target. Public so tests can pin the surface.
@spec target_spec(atom()) :: MobDev.Release.OpenSSL.CryptoNif.Target.t()
Per-target spec. Public so tests can lock down the surface
(especially the extra_cflags lists — silent drops there would
silently weaken released binaries).
@spec targets() :: [atom()]
All known crypto-NIF targets. Same set as MobDev.Release.OpenSSL.targets/0.