Rust's reputation for memory safety makes it an obvious candidate for safety-critical software development. But when it comes to qualification, the code you see is not always the code that matters.

During an exploratory assessment of Rust crates, we investigated what may be required to support the broader adoption of Rust in safety-critical applications. One finding stood out: the Rust API visible in the source code does not necessarily correspond to the final implementation that must be assessed for qualification. This becomes particularly evident when examining familiar mathematical functions such as x.cos().

A Simple Rust API Can Hide Several Implementation Layers

A Simple Rust API Can Hide Several Implementation Layers

Consider familiar Rust operations such as:

x.sin()
x.cos()
x.sqrt()

To a developer, these look like ordinary Rust APIs. Without looking at the documentation or implementation, it is easy to assume that Rust provides the corresponding mathematical algorithms itself. A closer look at the standard library source quickly shows otherwise.

In the usual Rust toolchain, mathematical functions may not be implemented in Rust itself. In our example, the Rust API is lowered through compiler intrinsics and LLVM-generated calls to the platform's underlying math implementation, typically libm or an equivalent runtime math library of the host or target system. This means that qualification cannot necessarily stop at the Rust API or crate boundary.

Following cos() Through the Rust Standard Library

Following cos() Through the Rust Standard Library

The first step is to look at the Rust standard library implementation itself.

For f32::cos, the relevant method contains

#[inline]
pub fn cos(self) -> f32 {
    intrinsics::cosf32(self)
}

The crucial line is:

intrinsics::cosf32(self)

This shows that the Rust standard library method is a thin wrapper around a compiler intrinsic. It does not contain a Rust implementation of the cosine algorithm. The standard library documentation also states that the precision of this function is non-deterministic and may vary by platform, Rust version, and even between invocations within the same execution.

Following cos() Into LLVM IR

Following cos() Into LLVM IR

Fortunately, this delegation is observable.

The LLVM intermediate representation generated by the Rust compiler provides evidence of what happens beneath the Rust API. For example, LLVM IR can be generated with:

Shell

cargo rustc -- --emit=llvm-ir

For std::f32::cos, the generated IR contains the following call:

%1 = call float @llvm.cos.f32(float %self)

This is the crucial observation. Rust does not implement a cosine algorithm in its standard library method body. Instead, it calls the LLVM cosine intrinsic. LLVM is responsible for the subsequent implementation choice and target-specific lowering and linking.

This provides the next link in the qualification chain.

Following the Implementation Chain

Following the Implementation Chain

The LLVM Language Reference provides a definition of the overloaded llvm.cos.* intrinsic. It supports floating-point scalar types and vectors of floating-point types, although not every target supports every type.

According to LLVM, the llvm.cos.* intrinsics return the cosine of the operand, with the argument and return value having the same floating-point type. More importantly for qualification, the LLVM semantic definition connects the intrinsic's expected behavior to the corresponding libm cosine function.

We can therefore follow the operation through several implementation layers:

  1. Rust source calls a mathematical API such as x.cos().

  2. The Rust compiler lowers the operation through intrinsics or compiler-known operations.

  3. LLVM represents or transforms the operation using LLVM intrinsics such as llvm.cos.f32.

  4. Target-specific lowering and linking determine which concrete implementation executes the operation.

  5. The host or target runtime typically provides the mathematical implementation through libm or an equivalent vendor implementation.

This leads to an important qualification question. It is not enough to ask: Which Rust function do we use? We also need to determine: Which implementation actually performs this operation on our target?

What Actually Executes on the Target?

What Actually Executes on the Target?

The final implementation path can depend on the target architecture, ABI, optimization settings, available hardware instructions, and runtime libraries. Consequently, unchanged Rust source code does not necessarily imply an identical final implementation path across different environments.

Depending on the platform, the actual mathematical implementation might come from:

  • glibc libm,

  • newlib math functions,

  • musl math functions,

  • a vendor-specific embedded math library,

  • another runtime implementation bundled with the toolchain or operating system.

This distinction is critical from a qualification perspective because this library provides the actual implementation. It is also where the safety-relevant requirements are specified.

For a Rust operation such as:

x.cos()

the qualification scope must therefore encompass the actual implementation used for the concrete target. Just as importantly, qualification requires evidence that this implementation is the one actually linked and executed by the generated executable.

What This Means for Rust Library Qualification

What This Means for Rust Library Qualification

This gives us an important insight from our exploratory assessment: Library qualification cannot stop at the Rust crate boundary.

A simple and familiar expression in Rust source code may ultimately depend on several implementation layers beneath the API. For mathematical operations, following this chain shows that qualification involves more than the Rust library itself. It requires identifying the concrete implementation used in the deployed environment and establishing evidence that this implementation was actually linked and executed.

The distinction between the source-level API and the actual implementation boundary is therefore an important consideration when integrating Rust into safety-critical applications.

Expert Library Qualification for ISO 26262 Compliance: Choose Validas

Expert Library Qualification for ISO 26262 Compliance: Choose Validas

Library qualification is critical to ensuring the safety and reliability of software built on open-source and proprietary libraries. With over 10 years of experience, Validas provides proven qualification services covering requirements analysis, systematic testing, code coverage analysis, and comprehensive qualification evidence.

Our TÜV SÜD-certified processes help organizations meet the requirements of standards such as ISO 26262 efficiently and with confidence. We also offer dedicated expertise in Rust library qualification, combining deep Rust knowledge with insights gained from a wide range of library qualification projects.

Get expert guidance on library qualification. Schedule your free consultation.

Dr. Oscar Slotosch
Co-Founder and Executive Board Member of Validas

References

References