MobDev.Release.OpenSSL.CryptoNif (mob_dev v0.5.9)

Copy Markdown View Source

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 module

Different reasons on each platform; the artifact is the same.

  • Android. dlopen'd children inherit RTLD_LOCAL by default, which hides the parent's enif_* symbols from crypto.so. crypto.so's on_load then fails with "cannot locate symbol". Static linking sidesteps that — the BEAM finds crypto_nif_init via dlsym(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:

TargetArch dirToolchainExtra CFLAGSnm symbol
android_arm64aarch64-unknown-linux-androidNDK clang/llvm-arAndroid hardening: branch-protect, stack-clash, _GNU_SOURCEcrypto_nif_init
android_arm32arm-unknown-linux-androideabiNDK clang/llvm-arAndroid hardening + -march=armv7-a -mfloat-abi=softfp -mthumbcrypto_nif_init
ios_simaarch64-apple-iossimulatorxcrun (sim SDK)iOS minimal — no Android hardening_crypto_nif_init
ios_deviceaarch64-apple-iosxcrun (device SDK)iOS minimal — no Android hardening_crypto_nif_init

Phases

Each build/2 call:

  1. Precheck — OTP source + OpenSSL prefix exist; Android/iOS toolchain reachable.
  2. Compile — for each of @sources, run <cc> <cflags> -c -o obj src.
  3. Archive — <ar> rcs crypto.a obj1 obj2 ... then <ranlib> crypto.a.
  4. Verify — <nm> crypto.a, scan output for the expected symbol. Symbol missing is a :precondition_failed (means our compile didn't actually produce crypto_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

base_cflags()

@spec base_cflags() :: [String.t()]

Base CFLAGS shared across all targets. Public for testing.

build(target_id, opts \\ [])

@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_SRC env or ~/code/otp)
  • :openssl_prefix — OpenSSL install dir (default: target's default_prefix)
  • :ndk_root — Android NDK root (Android targets only)

cflags(target, openssl_prefix, otp_src)

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.

check_symbol_present(nm_output, expected_symbol, archive)

@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.

sources()

@spec sources() :: [String.t()]

Source files compiled for every target. Public so tests can pin the surface.

target_spec(atom)

Per-target spec. Public so tests can lock down the surface (especially the extra_cflags lists — silent drops there would silently weaken released binaries).

targets()

@spec targets() :: [atom()]

All known crypto-NIF targets. Same set as MobDev.Release.OpenSSL.targets/0.