This crate provides low level rust bindings for [s2n-tls](https://github.com/aws/s2n-tls) which are autogenerated with [bindgen](https://github.com/rust-lang/rust-bindgen)
This crate is not intended for direct consumption by end consumers. Interested developers should instead look at the [s2n-tls](https://crates.io/crates/s2n-tls) or [s2n-tls-tokio](https://crates.io/crates/s2n-tls-tokio) crates. These provide higher-level, more ergonomic bindings than the `s2n-tls-sys` crate.
The `s2n-tls-sys` bindings crate contains the raw C code of `s2n-tls`. By default, it follows this build process:
1. Use the system C compiler to build `libs2n.a`
2. Link the built `libs2n.a` to the Rust bindings
3. Link against `aws-lc` through the `aws-lc-rs` crate
## Bring your own libs2n with `s2n-tls-sys` crate
You can customize above build process to use your own pre-built libs2n. This is useful if you want the bindings to be built with a non-default libcrypto. Currently, the default libcrypto when generating rust bindings is `aws-lc`. Here's how you can do that:
1. Clone [s2n-tls](https://github.com/aws/s2n-tls) and compile your preferred configuration of s2n-tls.
You may choose to link against a specific libcrypto at this step. For more information, see [Building with a specific libcrypto](https://github.com/aws/s2n-tls/blob/main/docs/BUILD.md#building-with-a-specific-libcrypto).
Also see [Building s2n-tls](https://github.com/aws/s2n-tls/blob/main/docs/BUILD.md#building-s2n-tls) for further guidance on configuring s2n-tls for your own use case.
2. `cd` into your rust project and set environment variables to your libs2n artifacts.
This tells the bindings to link to pre-built libs2n when running the build script for s2n-tls-sys
```
export S2N_TLS_LIB_DIR=<PATH_TO_ROOT_OF_S2N_TLS>/build/lib
export S2N_TLS_INCLUDE_DIR=<PATH_TO_ROOT_OF_S2N_TLS>/api
export LD_LIBRARY_PATH=$S2N_TLS_LIB_DIR:$LD_LIBRARY_PATH
```
`S2N_TLS_LIB_DIR` points to the folder containing `libs2n.a`/`libs2n.so` artifact that you would like s2n-tls-sys to link against.
`S2N_TLS_INCLUDE_DIR` points to the folder containing header files for `libs2n.a`/`libs2n.so` artifact.
`LD_LIBRARY_PATH` adds the path to `libs2n.a`/`libs2n.so` artifact for dynamic linker's search path.
3. Build your project. This triggers the build script for s2n-tls-sys
```
cargo build
```
## Performance note: Rust ≥ 1.90, LTO, and the linker
`s2n-tls-sys`'s build script compiles the vendored `libs2n` C code with link-time optimization (LTO) in release/optimized builds. The two compilers emit different kinds of LTO objects, and the linkers consume them differently:
- **GCC** emits fat-LTO objects (GIMPLE + native code). GNU `bfd` performs LTO on them; other linkers (including `rust-lld`) ignore the LTO and link the embedded native code — the link succeeds, just without LTO.
- **Clang** emits LLVM bitcode. `rust-lld` consumes it, but a plain GNU `ld` cannot and the link **fails** with "File format not recognized" (this is why an earlier change, [#3968](https://github.com/aws/s2n-tls/pull/3968), restricted LTO to GCC).
Starting with Rust 1.90, the default linker on `x86_64-unknown-linux-gnu` [switched from GNU `bfd` to `rust-lld`](https://blog.rust-lang.org/2025/09/01/rust-lld-on-1.90.0-stable/). Since `rust-lld` can't consume GCC's fat-LTO objects, a GCC build on that target silently drops the LTO pass, costing ~17-22 µs per TLS handshake (~2-4%).
### What the build script does automatically
The build script picks the LTO approach based on the linker:
- **When `rust-lld` is the linker** (the default on `x86_64-unknown-linux-gnu`, Rust ≥ 1.90): it **prefers Clang** — if `CC` is unset and `clang` is on `PATH`, it compiles `libs2n` with Clang so `rust-lld` can perform LTO. Installing `clang` is all most consumers need to do.
- **Otherwise** (GNU `bfd`, macOS `ld`, aarch64, etc.): it uses GCC fat-LTO objects, which link with any linker. Clang bitcode LTO is deliberately *not* enabled here, to avoid the GNU `ld` link failure above.
### If you must use GCC on Rust ≥ 1.90
If the build falls back to GCC on `x86_64-unknown-linux-gnu` with Rust ≥ 1.90, the build script emits a `cargo:warning` because LTO will be silently dropped. To restore LTO you have two options:
1. **Install `clang`** (recommended) and the build script will pick it up automatically, no flags needed.
2. **Opt out of `rust-lld`** so the GNU `bfd` linker is used, which can consume GCC's fat-LTO objects:
```
RUSTFLAGS="-Clinker-features=-lld"
```
Or in `.cargo/config.toml`:
```toml
[target.x86_64-unknown-linux-gnu]
rustflags = ["-Clinker-features=-lld"]
```
Consumers on aarch64 or non-Linux targets are unaffected because only `x86_64-unknown-linux-gnu` has the `rust-lld` default.