Cargo meets Autotools

(and CMake)


Yiming Jing (荆一明)

Creative Commons License

About me

  • Security scientist at Baidu Security/X-Lab;
  • Author of MesaLink, providing OpenSSL-compatible C APIs for the Rust TLS stack.
  • In production at Baidu since 08/2018;
  • 10 million monthly active users as of 12/2018;
  • 1.0.0 released on 04/02/2019.

About Baidu Security/X-Lab

  • Baidu is the 2nd largest search engine in the world;
  • Baidu X-Lab is led by Dr. Lenx Wei (韦韬), Chief Security Scientist (T11) of Baidu.

Memory safe projects lineup

Outline of this talk

Background: What is MesaLink?

"Hey, why don't you use cargo install?"

Autotools: ./configure && make

CMake: cmake .. && cmake --build .

Summary and Takeaways

Background

  • 2014: Heartbleed;
  • 2015: BoringSSL forked from OpenSSL;
  • 2017.10: We published a short paper on Hybrid Memory Safety at ACM CCS 2017;
  • 2017.11: We implemented SSL/SSL_CTX with Rust FFI on rustls, webpki, and *ring*.

So we have the MesaLink project.

  • A memory safe and OpenSSL-compatible TLS library

  • C bindings for rustls, modeled after OpenSSL

  • Long term goal: drop-in replacement for OpenSSL

What is this talk about

  • How we build and distribute MesaLink as a C library;

  • Gluing Cargo and Autotools/CMake together.

More details about Rust FFI at my RustFest Rome talk, “One Thousand Ways to Die in Rust FFI”

Why don’t you use cargo install?

  • “Cargo can only install packages that have binary targets”, the Rust book, §14.4;

  • Unlike openssl-sys, MesaLink is for C callers. Rust crates should depend on rustls, not MesaLink;

  • We’d like to hide rustc/cargo behind a build system that OpenSSL users are familiar with.

Cargo meets Autotools

./configure && make

1. Cargo.toml: a tale of two libs

        [lib]
        name = "mesalink"
        crate-type = ["staticlib", "cdylib"]
  • cdylib: a shared object without SONAME or version info; it can be dlopened but can’t be linked;

  • staticlib: an archive of objects.

But we need a linkable shared library.

2. build.rs: Generating A .LA file

Use libtool-rust or draft your own

    dlname='libmesalink.so.15'
    library_names='libmesalink.so.15.0.0 \
                   libmesalink.so.15 libmesalink.so'

    old_library='libmesalink.a'

    # Linux
    inherited_linker_flags=' -pthread -lm -ldl'
    # macOS
    inherited_linker_flags=' -lm -ldl -lresolv \
                            -framework Security'

3. src/include.am: libtool to help

    libmesalink_la_SOURCES = \
        $(NULL)
    libmesalink_la_LIBADD = \
        # libmesalink.la generated by build.rs
        $(MESALINK_LIB_LA)
    libmesalink_la_LDFLAGS = \
        -export-dynamic \
        -version-info ${MESALINK_LIBRARY_VERSION} \
        -export-symbols-regex "^mesalink_.*" \
        -Wl,--gc-sections \ # LTO
        $(AM_LDFLAGS)

4. configure.ac: setting options

Pass options to cargo and headers:

AC_ARG_ENABLE([tls13],
    [AS_HELP_STRING([--enable-tls13],
    [Enable TLS 1.3 (default: enabled)])],
    [ ENABLE_TLS13=$enableval ],
    [ ENABLE_TLS13=yes ]
    )

if test "$ENABLE_TLS13" = "yes"
then
    CARGO_FEATURES=$CARGO_FEATURES" tls13"
    CONFIG_OPTIONS="$CONFIG_OPTIONS HAVE_TLS13"
else
    CONFIG_OPTIONS="$CONFIG_OPTIONS NO_TLS13"
fi

5. Calling Cargo

Pass features, target, and linker options to rustc:

cargo rustc --release
    --no-default-features
    --features $(CARGO_FEATURES)

    ### Cross Compile ###
    --target arm-unknown-linux-gnueabi
    -- -C linker=arm-linux-gnu-eabi-gcc

asciicast

Cargo meets CMake

cmake .. && cmake --build .

1. A tale of two libs, again

GNU Libtool does not get along with M$ Windows;

Without libtool, we have to use cdylib;

Luckily, we can pass linker options to rustc.

2. Staging linker args

Use -soname on Linux:

    set(CARGO_LINKER_ARGS "\
        -C linker=${CMAKE_C_COMPILER} \
        -C link-arg=-Wl,-soname \
        -C link-arg=-Wl,${SONAME}" VERBATIM)

2. Staging linker args (cont’d)

Use -install_name on macOS:

set(CARGO_LINKER_ARGS "\
    -C linker=${CMAKE_C_COMPILER} \
    -C link-arg=-Wl,-install_name \
    -C link-arg=-Wl,${SONAME} \
    -C link-arg=-Wl,-compatibility_version \
    -C link-arg=-Wl,${PROJECT_VERSION_MAJOR} \
    -C link-arg=-Wl,-current_version \
    -C link-arg=-Wl,${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}"\
    VERBATIM)

3. CMakeLists.txt: Setting Options

Pass options to cargo and headers:

configure_file(${PROJECT_SOURCE_DIR}/mesalink/options.h.in \
    ${PROJECT_SOURCE_DIR}/mesalink/options.h @ONLY)

option(HAVE_TLS13 "Enable TLS 1.3 (default: enabled)" ON)
if(HAVE_TLS13)
    string(APPEND CONFIG_FEATURES tls13,)
endif()

4. Calling Cargo

Pass features, target, and linker options to rustc:

RUSTFLAGS="-C linker=/usr/bin/cc \
    -C link-arg=-Wl,-soname \
    -C link-arg=-Wl,libmesalink.so.15"

cargo build --release
    --no-default-features
    --features $(CARGO_FEATURES)

5. Cross compiling in CMake

See cmake/arm-linux-gnueabi.toolchain.cmake

# apt-get install gcc-arm-linux-gnueabi g++-arm-linux-gnueabi \
#    gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf \
#    libc6-armel-cross libc6-dev-armel-cross

# rustup target add arm-unknown-linux-gnueabi

set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)
set(CMAKE_C_COMPILER arm-linux-gnueabi-gcc)
set(CMAKE_CXX_COMPILER arm-linux-gnueabi-g++)
set(RUST_TARGET arm-unknown-linux-gnueabi)

6. Bonus: Windows installer

Generate an NSIS installer with CPack

mkdir build && cd build
cmake -G "Visual Studio 15 2017 Win64" ..
cmake --build .

cpack -D CPACK_GENERATOR="NSIS64"

windows_installer

asciicast

Autotools vs Cmake: a summary

Crate-typeLibraryLTOInstall
Autotoolsstaticliblibtoolldlibtool
CMakecdylibrustcrustccmake/cpack

Takeaways

  • The Rust toolchain is a few steps away from distributing a configurable C library and its headers;

  • Autotools/CMake bridges the gap and brings a better experience to Rust non-users.

Acknowledgements

Thank you

🔗 https://mesalink.io

✉️ jingyiming@baidu.com


Give us a ⭐ on Github if you like this project!