What is rustc?

Welcome to "The rustc book"! rustc is the compiler for the Rust programming language, provided by the project itself. Compilers take your source code and produce binary code, either as a library or executable.

Most Rust programmers don't invoke rustc directly, but instead do it through Cargo. It's all in service of rustc though! If you want to see how Cargo calls rustc, you can

$ cargo build --verbose

And it will print out each rustc invocation. This book can help you understand what each of these options does. Additionally, while most Rustaceans use Cargo, not all do: sometimes they integrate rustc into other build systems. This book should provide a guide to all of the options you'd need to do so.

Basic usage

Let's say you've got a little hello world program in a file hello.rs:

fn main() {
    println!("Hello, world!");
}

To turn this source code into an executable, you can use rustc:

$ rustc hello.rs
$ ./hello # on a *NIX
$ .\hello.exe # on Windows

Note that we only ever pass rustc the crate root, not every file we wish to compile. For example, if we had a main.rs that looked like this:

mod foo;

fn main() {
    foo::hello();
}

And a foo.rs that had this:


#![allow(unused)]
fn main() {
pub fn hello() {
    println!("Hello, world!");
}
}

To compile this, we'd run this command:

$ rustc main.rs

No need to tell rustc about foo.rs; the mod statements give it everything that it needs. This is different than how you would use a C compiler, where you invoke the compiler on each file, and then link everything together. In other words, the crate is a translation unit, not a particular module.

Command-line arguments

Here's a list of command-line arguments to rustc and what they do.

-h/--help: get help

This flag will print out help information for rustc.

--cfg: configure the compilation environment

This flag can turn on or off various #[cfg] settings for conditional compilation.

The value can either be a single identifier or two identifiers separated by =.

For examples, --cfg 'verbose' or --cfg 'feature="serde"'. These correspond to #[cfg(verbose)] and #[cfg(feature = "serde")] respectively.

-L: add a directory to the library search path

The -L flag adds a path to search for external crates and libraries.

The kind of search path can optionally be specified with the form -L KIND=PATH where KIND may be one of:

  • dependency — Only search for transitive dependencies in this directory.
  • crate — Only search for this crate's direct dependencies in this directory.
  • native — Only search for native libraries in this directory.
  • framework — Only search for macOS frameworks in this directory.
  • all — Search for all library kinds in this directory. This is the default if KIND is not specified.

This flag allows you to specify linking to a specific native library when building a crate.

The kind of library can optionally be specified with the form -l KIND=lib where KIND may be one of:

  • dylib — A native dynamic library.
  • static — A native static library (such as a .a archive).
  • framework — A macOS framework.

The kind of library can be specified in a #[link] attribute. If the kind is not specified in the link attribute or on the command-line, it will link a dynamic library if available, otherwise it will use a static library. If the kind is specified on the command-line, it will override the kind specified in a link attribute.

The name used in a link attribute may be overridden using the form -l ATTR_NAME:LINK_NAME where ATTR_NAME is the name in the link attribute, and LINK_NAME is the name of the actual library that will be linked.

--crate-type: a list of types of crates for the compiler to emit

This instructs rustc on which crate type to build. This flag accepts a comma-separated list of values, and may be specified multiple times. The valid crate types are:

  • lib — Generates a library kind preferred by the compiler, currently defaults to rlib.
  • rlib — A Rust static library.
  • staticlib — A native static library.
  • dylib — A Rust dynamic library.
  • cdylib — A native dynamic library.
  • bin — A runnable executable program.
  • proc-macro — Generates a format suitable for a procedural macro library that may be loaded by the compiler.

The crate type may be specified with the crate_type attribute. The --crate-type command-line value will override the crate_type attribute.

More details may be found in the linkage chapter of the reference.

--crate-name: specify the name of the crate being built

This informs rustc of the name of your crate.

--edition: specify the edition to use

This flag takes a value of 2015 or 2018. The default is 2015. More information about editions may be found in the edition guide.

--emit: specifies the types of output files to generate

This flag controls the types of output files generated by the compiler. It accepts a comma-separated list of values, and may be specified multiple times. The valid emit kinds are:

  • asm — Generates a file with the crate's assembly code. The default output filename is CRATE_NAME.s.
  • dep-info — Generates a file with Makefile syntax that indicates all the source files that were loaded to generate the crate. The default output filename is CRATE_NAME.d.
  • link — Generates the crates specified by --crate-type. The default output filenames depend on the crate type and platform. This is the default if --emit is not specified.
  • llvm-bc — Generates a binary file containing the LLVM bitcode. The default output filename is CRATE_NAME.bc.
  • llvm-ir — Generates a file containing LLVM IR. The default output filename is CRATE_NAME.ll.
  • metadata — Generates a file containing metadata about the crate. The default output filename is CRATE_NAME.rmeta.
  • mir — Generates a file containing rustc's mid-level intermediate representation. The default output filename is CRATE_NAME.mir.
  • obj — Generates a native object file. The default output filename is CRATE_NAME.o.

The output filename can be set with the -o flag. A suffix may be added to the filename with the -C extra-filename flag. The files are written to the current directory unless the --out-dir flag is used. Each emission type may also specify the output filename with the form KIND=PATH, which takes precedence over the -o flag.

--print: print compiler information

This flag prints out various information about the compiler. This flag may be specified multiple times, and the information is printed in the order the flags are specified. Specifying a --print flag will usually disable the --emit step and will only print the requested information. The valid types of print values are:

  • crate-name — The name of the crate.
  • file-names — The names of the files created by the link emit kind.
  • sysroot — Path to the sysroot.
  • target-libdir - Path to the target libdir.
  • cfg — List of cfg values. See conditional compilation for more information about cfg values.
  • target-list — List of known targets. The target may be selected with the --target flag.
  • target-cpus — List of available CPU values for the current target. The target CPU may be selected with the -C target-cpu=val flag.
  • target-features — List of available target features for the current target. Target features may be enabled with the -C target-feature=val flag. This flag is unsafe. See known issues for more details.
  • relocation-models — List of relocation models. Relocation models may be selected with the -C relocation-model=val flag.
  • code-models — List of code models. Code models may be selected with the -C code-model=val flag.
  • tls-models — List of Thread Local Storage models supported. The model may be selected with the -Z tls-model=val flag.
  • native-static-libs — This may be used when creating a staticlib crate type. If this is the only flag, it will perform a full compilation and include a diagnostic note that indicates the linker flags to use when linking the resulting static library. The note starts with the text native-static-libs: to make it easier to fetch the output.

-g: include debug information

A synonym for -C debuginfo=2.

-O: optimize your code

A synonym for -C opt-level=2.

-o: filename of the output

This flag controls the output filename.

--out-dir: directory to write the output in

The outputted crate will be written to this directory. This flag is ignored if the -o flag is used.

--explain: provide a detailed explanation of an error message

Each error of rustc's comes with an error code; this will print out a longer explanation of a given error.

--test: build a test harness

When compiling this crate, rustc will ignore your main function and instead produce a test harness. See the Tests chapter for more information about tests.

--target: select a target triple to build

This controls which target to produce.

-W: set lint warnings

This flag will set which lints should be set to the warn level.

Note: The order of these lint level arguments is taken into account, see lint level via compiler flag for more information.

-A: set lint allowed

This flag will set which lints should be set to the allow level.

Note: The order of these lint level arguments is taken into account, see lint level via compiler flag for more information.

-D: set lint denied

This flag will set which lints should be set to the deny level.

Note: The order of these lint level arguments is taken into account, see lint level via compiler flag for more information.

-F: set lint forbidden

This flag will set which lints should be set to the forbid level.

Note: The order of these lint level arguments is taken into account, see lint level via compiler flag for more information.

-Z: set unstable options

This flag will allow you to set unstable options of rustc. In order to set multiple options, the -Z flag can be used multiple times. For example: rustc -Z verbose -Z time. Specifying options with -Z is only available on nightly. To view all available options run: rustc -Z help.

--cap-lints: set the most restrictive lint level

This flag lets you 'cap' lints, for more, see here.

-C/--codegen: code generation options

This flag will allow you to set codegen options.

-V/--version: print a version

This flag will print out rustc's version.

-v/--verbose: use verbose output

This flag, when combined with other flags, makes them produce extra output.

--extern: specify where an external library is located

This flag allows you to pass the name and location for an external crate of a direct dependency. Indirect dependencies (dependencies of dependencies) are located using the -L flag. The given crate name is added to the extern prelude, similar to specifying extern crate within the root module. The given crate name does not need to match the name the library was built with.

Specifying --extern has one behavior difference from extern crate: --extern merely makes the crate a candidate for being linked; it does not actually link it unless it's actively used. In rare occasions you may wish to ensure a crate is linked even if you don't actively use it from your code: for example, if it changes the global allocator or if it contains #[no_mangle] symbols for use by other programming languages. In such cases you'll need to use extern crate.

This flag may be specified multiple times. This flag takes an argument with either of the following formats:

  • CRATENAME=PATH — Indicates the given crate is found at the given path.
  • CRATENAME — Indicates the given crate may be found in the search path, such as within the sysroot or via the -L flag.

The same crate name may be specified multiple times for different crate types. If both an rlib and dylib are found, an internal algorithm is used to decide which to use for linking. The -C prefer-dynamic flag may be used to influence which is used.

If the same crate name is specified with and without a path, the one with the path is used and the pathless flag has no effect.

--sysroot: Override the system root

The "sysroot" is where rustc looks for the crates that come with the Rust distribution; this flag allows that to be overridden.

--error-format: control how errors are produced

This flag lets you control the format of messages. Messages are printed to stderr. The valid options are:

  • human — Human-readable output. This is the default.
  • json — Structured JSON output. See the JSON chapter for more detail.
  • short — Short, one-line messages.

--color: configure coloring of output

This flag lets you control color settings of the output. The valid options are:

  • auto — Use colors if output goes to a tty. This is the default.
  • always — Always use colors.
  • never — Never colorize output.

--remap-path-prefix: remap source names in output

Remap source path prefixes in all output, including compiler diagnostics, debug information, macro expansions, etc. It takes a value of the form FROM=TO where a path prefix equal to FROM is rewritten to the value TO. The FROM may itself contain an = symbol, but the TO value may not. This flag may be specified multiple times.

This is useful for normalizing build products, for example by removing the current directory out of pathnames emitted into the object files. The replacement is purely textual, with no consideration of the current system's pathname syntax. For example --remap-path-prefix foo=bar will match foo/lib.rs but not ./foo/lib.rs.

--json: configure json messages printed by the compiler

When the --error-format=json option is passed to rustc then all of the compiler's diagnostic output will be emitted in the form of JSON blobs. The --json argument can be used in conjunction with --error-format=json to configure what the JSON blobs contain as well as which ones are emitted.

With --error-format=json the compiler will always emit any compiler errors as a JSON blob, but the following options are also available to the --json flag to customize the output:

  • diagnostic-short - json blobs for diagnostic messages should use the "short" rendering instead of the normal "human" default. This means that the output of --error-format=short will be embedded into the JSON diagnostics instead of the default --error-format=human.

  • diagnostic-rendered-ansi - by default JSON blobs in their rendered field will contain a plain text rendering of the diagnostic. This option instead indicates that the diagnostic should have embedded ANSI color codes intended to be used to colorize the message in the manner rustc typically already does for terminal outputs. Note that this is usefully combined with crates like fwdansi to translate these ANSI codes on Windows to console commands or strip-ansi-escapes if you'd like to optionally remove the ansi colors afterwards.

  • artifacts - this instructs rustc to emit a JSON blob for each artifact that is emitted. An artifact corresponds to a request from the --emit CLI argument, and as soon as the artifact is available on the filesystem a notification will be emitted.

Note that it is invalid to combine the --json argument with the --color argument, and it is required to combine --json with --error-format=json.

See the JSON chapter for more detail.

@path: load command-line flags from a path

If you specify @path on the command-line, then it will open path and read command line options from it. These options are one per line; a blank line indicates an empty option. The file can use Unix or Windows style line endings, and must be encoded as UTF-8.

Lints

In software, a "lint" is a tool used to help improve your source code. The Rust compiler contains a number of lints, and when it compiles your code, it will also run the lints. These lints may produce a warning, an error, or nothing at all, depending on how you've configured things.

Here's a small example:

$ cat main.rs
fn main() {
    let x = 5;
}
$ rustc main.rs
warning: unused variable: `x`
 --> main.rs:2:9
  |
2 |     let x = 5;
  |         ^
  |
  = note: `#[warn(unused_variables)]` on by default
  = note: to avoid this warning, consider using `_x` instead

This is the unused_variables lint, and it tells you that you've introduced a variable that you don't use in your code. That's not wrong, so it's not an error, but it might be a bug, so you get a warning.

Future-incompatible lints

Sometimes the compiler needs to be changed to fix an issue that can cause existing code to stop compiling. "Future-incompatible" lints are issued in these cases to give users of Rust a smooth transition to the new behavior. Initially, the compiler will continue to accept the problematic code and issue a warning. The warning has a description of the problem, a notice that this will become an error in the future, and a link to a tracking issue that provides detailed information and an opportunity for feedback. This gives users some time to fix the code to accommodate the change. After some time, the warning may become an error.

The following is an example of what a future-incompatible looks like:

warning: borrow of packed field is unsafe and requires unsafe function or block (error E0133)
  --> lint_example.rs:11:13
   |
11 |     let y = &x.data.0;
   |             ^^^^^^^^^
   |
   = note: `#[warn(safe_packed_borrows)]` on by default
   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
   = note: for more information, see issue #46043 <https://github.com/rust-lang/rust/issues/46043>
   = note: fields of packed structs might be misaligned: dereferencing a misaligned pointer or even just creating a misaligned reference is undefined behavior

For more information about the process and policy of future-incompatible changes, see RFC 1589.

Lint levels

In rustc, lints are divided into four levels:

  1. allow
  2. warn
  3. deny
  4. forbid

Each lint has a default level (explained in the lint listing later in this chapter), and the compiler has a default warning level. First, let's explain what these levels mean, and then we'll talk about configuration.

allow

These lints exist, but by default, do nothing. For example, consider this source:


#![allow(unused)]
fn main() {
pub fn foo() {}
}

Compiling this file produces no warnings:

$ rustc lib.rs --crate-type=lib
$

But this code violates the missing_docs lint.

These lints exist mostly to be manually turned on via configuration, as we'll talk about later in this section.

warn

The 'warn' lint level will produce a warning if you violate the lint. For example, this code runs afoul of the unused_variables lint:


#![allow(unused)]
fn main() {
pub fn foo() {
    let x = 5;
}
}

This will produce this warning:

$ rustc lib.rs --crate-type=lib
warning: unused variable: `x`
 --> lib.rs:2:9
  |
2 |     let x = 5;
  |         ^
  |
  = note: `#[warn(unused_variables)]` on by default
  = note: to avoid this warning, consider using `_x` instead

deny

A 'deny' lint produces an error if you violate it. For example, this code runs into the exceeding_bitshifts lint.

fn main() {
    100u8 << 10;
}
$ rustc main.rs
error: bitshift exceeds the type's number of bits
 --> main.rs:2:13
  |
2 |     100u8 << 10;
  |     ^^^^^^^^^^^
  |
  = note: `#[deny(exceeding_bitshifts)]` on by default

What's the difference between an error from a lint and a regular old error? Lints are configurable via levels, so in a similar way to 'allow' lints, warnings that are 'deny' by default let you allow them. Similarly, you may wish to set up a lint that is warn by default to produce an error instead. This lint level gives you that.

forbid

'forbid' is a special lint level that's stronger than 'deny'. It's the same as 'deny' in that a lint at this level will produce an error, but unlike the 'deny' level, the 'forbid' level can not be overridden to be anything lower than an error. However, lint levels may still be capped with --cap-lints (see below) so rustc --cap-lints warn will make lints set to 'forbid' just warn.

Configuring warning levels

Remember our missing_docs example from the 'allow' lint level?

$ cat lib.rs
pub fn foo() {}
$ rustc lib.rs --crate-type=lib
$

We can configure this lint to operate at a higher level, both with compiler flags, as well as with an attribute in the source code.

You can also "cap" lints so that the compiler can choose to ignore certain lint levels. We'll talk about that last.

Via compiler flag

The -A, -W, -D, and -F flags let you turn one or more lints into allowed, warning, deny, or forbid levels, like this:

$ rustc lib.rs --crate-type=lib -W missing-docs
warning: missing documentation for crate
 --> lib.rs:1:1
  |
1 | pub fn foo() {}
  | ^^^^^^^^^^^^
  |
  = note: requested on the command line with `-W missing-docs`

warning: missing documentation for a function
 --> lib.rs:1:1
  |
1 | pub fn foo() {}
  | ^^^^^^^^^^^^
$ rustc lib.rs --crate-type=lib -D missing-docs
error: missing documentation for crate
 --> lib.rs:1:1
  |
1 | pub fn foo() {}
  | ^^^^^^^^^^^^
  |
  = note: requested on the command line with `-D missing-docs`

error: missing documentation for a function
 --> lib.rs:1:1
  |
1 | pub fn foo() {}
  | ^^^^^^^^^^^^

error: aborting due to 2 previous errors

You can also pass each flag more than once for changing multiple lints:

$ rustc lib.rs --crate-type=lib -D missing-docs -D unused-variables

And of course, you can mix these four flags together:

$ rustc lib.rs --crate-type=lib -D missing-docs -A unused-variables

The order of these command line arguments is taken into account. The following allows the unused-variables lint, because it is the last argument for that lint:

$ rustc lib.rs --crate-type=lib -D unused-variables -A unused-variables

You can make use of this behavior by overriding the level of one specific lint out of a group of lints. The following example denies all the lints in the unused group, but explicitly allows the unused-variables lint in that group (forbid still trumps everything regardless of ordering):

$ rustc lib.rs --crate-type=lib -D unused -A unused-variables

Via an attribute

You can also modify the lint level with a crate-wide attribute:

$ cat lib.rs
#![warn(missing_docs)]

pub fn foo() {}
$ rustc lib.rs --crate-type=lib
warning: missing documentation for crate
 --> lib.rs:1:1
  |
1 | / #![warn(missing_docs)]
2 | |
3 | | pub fn foo() {}
  | |_______________^
  |
note: lint level defined here
 --> lib.rs:1:9
  |
1 | #![warn(missing_docs)]
  |         ^^^^^^^^^^^^

warning: missing documentation for a function
 --> lib.rs:3:1
  |
3 | pub fn foo() {}
  | ^^^^^^^^^^^^

All four, warn, allow, deny, and forbid all work this way.

You can also pass in multiple lints per attribute:


#![allow(unused)]
#![warn(missing_docs, unused_variables)]

fn main() {
pub fn foo() {}
}

And use multiple attributes together:


#![allow(unused)]
#![warn(missing_docs)]
#![deny(unused_variables)]

fn main() {
pub fn foo() {}
}

Capping lints

rustc supports a flag, --cap-lints LEVEL that sets the "lint cap level." This is the maximum level for all lints. So for example, if we take our code sample from the "deny" lint level above:

fn main() {
    100u8 << 10;
}

And we compile it, capping lints to warn:

$ rustc lib.rs --cap-lints warn
warning: bitshift exceeds the type's number of bits
 --> lib.rs:2:5
  |
2 |     100u8 << 10;
  |     ^^^^^^^^^^^
  |
  = note: `#[warn(exceeding_bitshifts)]` on by default

warning: this expression will panic at run-time
 --> lib.rs:2:5
  |
2 |     100u8 << 10;
  |     ^^^^^^^^^^^ attempt to shift left with overflow

It now only warns, rather than errors. We can go further and allow all lints:

$ rustc lib.rs --cap-lints allow
$

This feature is used heavily by Cargo; it will pass --cap-lints allow when compiling your dependencies, so that if they have any warnings, they do not pollute the output of your build.

Lint Groups

rustc has the concept of a "lint group", where you can toggle several warnings through one name.

For example, the nonstandard-style lint sets non-camel-case-types, non-snake-case, and non-upper-case-globals all at once. So these are equivalent:

$ rustc -D nonstandard-style
$ rustc -D non-camel-case-types -D non-snake-case -D non-upper-case-globals

Here's a list of each lint group, and the lints that they are made up of:

{{groups-table}}

Additionally, there's a bad-style lint group that's a deprecated alias for nonstandard-style.

Finally, you can also see the table above by invoking rustc -W help. This will give you the exact values for the specific compiler you have installed.

Lint listing

This section lists out all of the lints, grouped by their default lint levels.

You can also see this list by running rustc -W help.

Allowed-by-default lints

This file is auto-generated by the lint-docs script.

Warn-by-default lints

This file is auto-generated by the lint-docs script.

Deny-by-default lints

This file is auto-generated by the lint-docs script.

Codegen options

All of these options are passed to rustc via the -C flag, short for "codegen." You can see a version of this list for your exact compiler by running rustc -C help.

ar

This option is deprecated and does nothing.

code-model

This option lets you choose which code model to use.
Code models put constraints on address ranges that the program and its symbols may use.
With smaller address ranges machine instructions may be able to use more compact addressing modes.

The specific ranges depend on target architectures and addressing modes available to them.
For x86 more detailed description of its code models can be found in System V Application Binary Interface specification.

Supported values for this option are:

  • tiny - Tiny code model.
  • small - Small code model. This is the default model for majority of supported targets.
  • kernel - Kernel code model.
  • medium - Medium code model.
  • large - Large code model.

Supported values can also be discovered by running rustc --print code-models.

codegen-units

This flag controls how many code generation units the crate is split into. It takes an integer greater than 0.

When a crate is split into multiple codegen units, LLVM is able to process them in parallel. Increasing parallelism may speed up compile times, but may also produce slower code. Setting this to 1 may improve the performance of generated code, but may be slower to compile.

The default value, if not specified, is 16 for non-incremental builds. For incremental builds the default is 256 which allows caching to be more granular.

control-flow-guard

This flag controls whether LLVM enables the Windows Control Flow Guard platform security feature. This flag is currently ignored for non-Windows targets. It takes one of the following values:

  • y, yes, on, checks, or no value: enable Control Flow Guard.
  • nochecks: emit Control Flow Guard metadata without runtime enforcement checks (this should only be used for testing purposes as it does not provide security enforcement).
  • n, no, off: do not enable Control Flow Guard (the default).

debug-assertions

This flag lets you turn cfg(debug_assertions) conditional compilation on or off. It takes one of the following values:

  • y, yes, on, or no value: enable debug-assertions.
  • n, no, or off: disable debug-assertions.

If not specified, debug assertions are automatically enabled only if the opt-level is 0.

debuginfo

This flag controls the generation of debug information. It takes one of the following values:

  • 0: no debug info at all (the default).
  • 1: line tables only.
  • 2: full debug info.

Note: The -g flag is an alias for -C debuginfo=2.

default-linker-libraries

This flag controls whether or not the linker includes its default libraries. It takes one of the following values:

  • y, yes, on, or no value: include default libraries (the default).
  • n, no, or off: exclude default libraries.

For example, for gcc flavor linkers, this issues the -nodefaultlibs flag to the linker.

embed-bitcode

This flag controls whether or not the compiler embeds LLVM bitcode into object files. It takes one of the following values:

  • y, yes, on, or no value: put bitcode in rlibs (the default).
  • n, no, or off: omit bitcode from rlibs.

LLVM bitcode is required when rustc is performing link-time optimization (LTO). It is also required on some targets like iOS ones where vendors look for LLVM bitcode. Embedded bitcode will appear in rustc-generated object files inside of a section whose name is defined by the target platform. Most of the time this is .llvmbc.

The use of -C embed-bitcode=no can significantly improve compile times and reduce generated file sizes if your compilation does not actually need bitcode (e.g. if you're not compiling for iOS or you're not performing LTO). For these reasons, Cargo uses -C embed-bitcode=no whenever possible. Likewise, if you are building directly with rustc we recommend using -C embed-bitcode=no whenever you are not using LTO.

If combined with -C lto, -C embed-bitcode=no will cause rustc to abort at start-up, because the combination is invalid.

Note: if you're building Rust code with LTO then you probably don't even need the embed-bitcode option turned on. You'll likely want to use -Clinker-plugin-lto instead which skips generating object files entirely and simply replaces object files with LLVM bitcode. The only purpose for -Cembed-bitcode is when you're generating an rlib that is both being used with and without LTO. For example Rust's standard library ships with embedded bitcode since users link to it both with and without LTO.

This also may make you wonder why the default is yes for this option. The reason for that is that it's how it was for rustc 1.44 and prior. In 1.45 this option was added to turn off what had always been the default.

extra-filename

This option allows you to put extra data in each output filename. It takes a string to add as a suffix to the filename. See the --emit flag for more information.

force-frame-pointers

This flag forces the use of frame pointers. It takes one of the following values:

  • y, yes, on, or no value: force-enable frame pointers.
  • n, no, or off: do not force-enable frame pointers. This does not necessarily mean frame pointers will be removed.

The default behaviour, if frame pointers are not force-enabled, depends on the target.

force-unwind-tables

This flag forces the generation of unwind tables. It takes one of the following values:

  • y, yes, on, or no value: Unwind tables are forced to be generated.
  • n, no, or off: Unwind tables are not forced to be generated. If unwind tables are required by the target or -C panic=unwind, an error will be emitted.

The default if not specified depends on the target.

incremental

This flag allows you to enable incremental compilation, which allows rustc to save information after compiling a crate to be reused when recompiling the crate, improving re-compile times. This takes a path to a directory where incremental files will be stored.

inline-threshold

This option lets you set the default threshold for inlining a function. It takes an unsigned integer as a value. Inlining is based on a cost model, where a higher threshold will allow more inlining.

The default depends on the opt-level:

opt-levelThreshold
0N/A, only inlines always-inline functions
1N/A, only inlines always-inline functions and LLVM lifetime intrinsics
2225
3275
s75
z25

This flag lets you append a single extra argument to the linker invocation.

"Append" is significant; you can pass this flag multiple times to add multiple arguments.

This flag lets you append multiple extra arguments to the linker invocation. The options should be separated by spaces.

This flag controls whether the linker will keep dead code. It takes one of the following values:

  • y, yes, on, or no value: keep dead code.
  • n, no, or off: remove dead code (the default).

An example of when this flag might be useful is when trying to construct code coverage metrics.

On targets that support it this flag controls whether the linker will use libraries and objects shipped with Rust instead or those in the system. It takes one of the following values:

  • no value: rustc will use heuristic to disable self-contained mode if system has necessary tools.
  • y, yes, on: use only libraries/objects shipped with Rust.
  • n, no, or off: rely on the user or the linker to provide non-Rust libraries/objects.

This allows overriding cases when detection fails or user wants to use shipped libraries.

linker

This flag controls which linker rustc invokes to link your code. It takes a path to the linker executable. If this flag is not specified, the linker will be inferred based on the target. See also the linker-flavor flag for another way to specify the linker.

linker-flavor

This flag controls the linker flavor used by rustc. If a linker is given with the -C linker flag, then the linker flavor is inferred from the value provided. If no linker is given then the linker flavor is used to determine the linker to use. Every rustc target defaults to some linker flavor. Valid options are:

  • em: use Emscripten emcc.
  • gcc: use the cc executable, which is typically gcc or clang on many systems.
  • ld: use the ld executable.
  • msvc: use the link.exe executable from Microsoft Visual Studio MSVC.
  • ptx-linker: use rust-ptx-linker for Nvidia NVPTX GPGPU support.
  • wasm-ld: use the wasm-ld executable, a port of LLVM lld for WebAssembly.
  • ld64.lld: use the LLVM lld executable with the -flavor darwin flag for Apple's ld.
  • ld.lld: use the LLVM lld executable with the -flavor gnu flag for GNU binutils' ld.
  • lld-link: use the LLVM lld executable with the -flavor link flag for Microsoft's link.exe.

linker-plugin-lto

This flag defers LTO optimizations to the linker. See linker-plugin-LTO for more details. It takes one of the following values:

  • y, yes, on, or no value: enable linker plugin LTO.
  • n, no, or off: disable linker plugin LTO (the default).
  • A path to the linker plugin.

More specifically this flag will cause the compiler to replace its typical object file output with LLVM bitcode files. For example an rlib produced with -Clinker-plugin-lto will still have *.o files in it, but they'll all be LLVM bitcode instead of actual machine code. It is expected that the native platform linker is capable of loading these LLVM bitcode files and generating code at link time (typically after performing optimizations).

Note that rustc can also read its own object files produced with -Clinker-plugin-lto. If an rlib is only ever going to get used later with a -Clto compilation then you can pass -Clinker-plugin-lto to speed up compilation and avoid generating object files that aren't used.

llvm-args

This flag can be used to pass a list of arguments directly to LLVM.

The list must be separated by spaces.

Pass --help to see a list of options.

lto

This flag controls whether LLVM uses link time optimizations to produce better optimized code, using whole-program analysis, at the cost of longer linking time. It takes one of the following values:

  • y, yes, on, fat, or no value: perform "fat" LTO which attempts to perform optimizations across all crates within the dependency graph.
  • n, no, off: disables LTO.
  • thin: perform "thin" LTO. This is similar to "fat", but takes substantially less time to run while still achieving performance gains similar to "fat".

If -C lto is not specified, then the compiler will attempt to perform "thin local LTO" which performs "thin" LTO on the local crate only across its codegen units. When -C lto is not specified, LTO is disabled if codegen units is 1 or optimizations are disabled (-C opt-level=0). That is:

  • When -C lto is not specified:
    • codegen-units=1: disable LTO.
    • opt-level=0: disable LTO.
  • When -C lto is specified:
    • lto: 16 codegen units, perform fat LTO across crates.
    • codegen-units=1 + lto: 1 codegen unit, fat LTO across crates.

See also linker-plugin-lto for cross-language LTO.

metadata

This option allows you to control the metadata used for symbol mangling. This takes a space-separated list of strings. Mangled symbols will incorporate a hash of the metadata. This may be used, for example, to differentiate symbols between two different versions of the same crate being linked.

no-prepopulate-passes

This flag tells the pass manager to use an empty list of passes, instead of the usual pre-populated list of passes.

no-redzone

This flag allows you to disable the red zone. It takes one of the following values:

  • y, yes, on, or no value: disable the red zone.
  • n, no, or off: enable the red zone.

The default behaviour, if the flag is not specified, depends on the target.

no-stack-check

This option is deprecated and does nothing.

no-vectorize-loops

This flag disables loop vectorization.

no-vectorize-slp

This flag disables vectorization using superword-level parallelism.

opt-level

This flag controls the optimization level.

  • 0: no optimizations, also turns on cfg(debug_assertions) (the default).
  • 1: basic optimizations.
  • 2: some optimizations.
  • 3: all optimizations.
  • s: optimize for binary size.
  • z: optimize for binary size, but also turn off loop vectorization.

Note: The -O flag is an alias for -C opt-level=2.

The default is 0.

overflow-checks

This flag allows you to control the behavior of runtime integer overflow. When overflow-checks are enabled, a panic will occur on overflow. This flag takes one of the following values:

  • y, yes, on, or no value: enable overflow checks.
  • n, no, or off: disable overflow checks.

If not specified, overflow checks are enabled if debug-assertions are enabled, disabled otherwise.

panic

This option lets you control what happens when the code panics.

  • abort: terminate the process upon panic
  • unwind: unwind the stack upon panic

If not specified, the default depends on the target.

passes

This flag can be used to add extra LLVM passes to the compilation.

The list must be separated by spaces.

See also the no-prepopulate-passes flag.

prefer-dynamic

By default, rustc prefers to statically link dependencies. This option will indicate that dynamic linking should be used if possible if both a static and dynamic versions of a library are available. There is an internal algorithm for determining whether or not it is possible to statically or dynamically link with a dependency. For example, cdylib crate types may only use static linkage. This flag takes one of the following values:

  • y, yes, on, or no value: use dynamic linking.
  • n, no, or off: use static linking (the default).

profile-generate

This flag allows for creating instrumented binaries that will collect profiling data for use with profile-guided optimization (PGO). The flag takes an optional argument which is the path to a directory into which the instrumented binary will emit the collected data. See the chapter on profile-guided optimization for more information.

profile-use

This flag specifies the profiling data file to be used for profile-guided optimization (PGO). The flag takes a mandatory argument which is the path to a valid .profdata file. See the chapter on profile-guided optimization for more information.

relocation-model

This option controls generation of position-independent code (PIC).

Supported values for this option are:

Primary relocation models

  • static - non-relocatable code, machine instructions may use absolute addressing modes.

  • pic - fully relocatable position independent code, machine instructions need to use relative addressing modes.
    Equivalent to the "uppercase" -fPIC or -fPIE options in other compilers, depending on the produced crate types.
    This is the default model for majority of supported targets.

Special relocation models

  • dynamic-no-pic - relocatable external references, non-relocatable code.
    Only makes sense on Darwin and is rarely used.
    If StackOverflow tells you to use this as an opt-out of PIC or PIE, don't believe it, use -C relocation-model=static instead.
  • ropi, rwpi and ropi-rwpi - relocatable code and read-only data, relocatable read-write data, and combination of both, respectively.
    Only makes sense for certain embedded ARM targets.
  • default - relocation model default to the current target.
    Only makes sense as an override for some other explicitly specified relocation model previously set on the command line.

Supported values can also be discovered by running rustc --print relocation-models.

Linking effects

In addition to codegen effects, relocation-model has effects during linking.

If the relocation model is pic and the current target supports position-independent executables (PIE), the linker will be instructed (-pie) to produce one.
If the target doesn't support both position-independent and statically linked executables, then -C target-feature=+crt-static "wins" over -C relocation-model=pic, and the linker is instructed (-static) to produce a statically linked but not position-independent executable.

remark

This flag lets you print remarks for optimization passes.

The list of passes should be separated by spaces.

all will remark on every pass.

rpath

This flag controls whether rpath is enabled. It takes one of the following values:

  • y, yes, on, or no value: enable rpath.
  • n, no, or off: disable rpath (the default).

save-temps

This flag controls whether temporary files generated during compilation are deleted once compilation finishes. It takes one of the following values:

  • y, yes, on, or no value: save temporary files.
  • n, no, or off: delete temporary files (the default).

soft-float

This option controls whether rustc generates code that emulates floating point instructions in software. It takes one of the following values:

  • y, yes, on, or no value: use soft floats.
  • n, no, or off: use hardware floats (the default).

split-debuginfo

This option controls the emission of "split debuginfo" for debug information that rustc generates. The default behavior of this option is platform-specific, and not all possible values for this option work on all platforms. Possible values are:

  • off - This is the default for platforms with ELF binaries and windows-gnu (not Windows MSVC and not macOS). This typically means that DWARF debug information can be found in the final artifact in sections of the executable. This option is not supported on Windows MSVC. On macOS this options prevents the final execution of dsymutil to generate debuginfo.

  • packed - This is the default for Windows MSVC and macOS. The term "packed" here means that all the debug information is packed into a separate file from the main executable. On Windows MSVC this is a *.pdb file, on macOS this is a *.dSYM folder, and on other platforms this is a *.dwp file.

  • unpacked - This means that debug information will be found in separate files for each compilation unit (object file). This is not supported on Windows MSVC. On macOS this means the original object files will contain debug information. On other Unix platforms this means that *.dwo files will contain debug information.

Note that packed and unpacked are gated behind -Z unstable-options on non-macOS platforms at this time.

target-cpu

This instructs rustc to generate code specifically for a particular processor.

You can run rustc --print target-cpus to see the valid options to pass here. Each target has a default base CPU. Special values include:

  • native can be passed to use the processor of the host machine.
  • generic refers to an LLVM target with minimal features but modern tuning.

target-feature

Individual targets will support different features; this flag lets you control enabling or disabling a feature. Each feature should be prefixed with a + to enable it or - to disable it.

Features from multiple -C target-feature options are combined.
Multiple features can be specified in a single option by separating them with commas - -C target-feature=+x,-y.
If some feature is specified more than once with both + and -, then values passed later override values passed earlier.
For example, -C target-feature=+x,-y,+z -Ctarget-feature=-x,+y is equivalent to -C target-feature=-x,+y,+z.

To see the valid options and an example of use, run rustc --print target-features.

Using this flag is unsafe and might result in undefined runtime behavior.

See also the target_feature attribute for controlling features per-function.

This also supports the feature +crt-static and -crt-static to control static C runtime linkage.

Each target and target-cpu has a default set of enabled features.

tune-cpu

This instructs rustc to schedule code specifically for a particular processor. This does not affect the compatibility (instruction sets or ABI), but should make your code slightly more efficient on the selected CPU.

The valid options are the same as those for target-cpu. The default is None, which LLVM translates as the target-cpu.

This is an unstable option. Use -Z tune-cpu=machine to specify a value.

Due to limitations in LLVM (12.0.0-git9218f92), this option is currently effective only for x86 targets.

JSON Output

This chapter documents the JSON structures emitted by rustc. JSON may be enabled with the --error-format=json flag. Additional options may be specified with the --json flag which can change which messages are generated, and the format of the messages.

JSON messages are emitted one per line to stderr.

If parsing the output with Rust, the cargo_metadata crate provides some support for parsing the messages.

When parsing, care should be taken to be forwards-compatible with future changes to the format. Optional values may be null. New fields may be added. Enumerated fields like "level" or "suggestion_applicability" may add new values.

Diagnostics

Diagnostic messages provide errors or possible concerns generated during compilation. rustc provides detailed information about where the diagnostic originates, along with hints and suggestions.

Diagnostics are arranged in a parent/child relationship where the parent diagnostic value is the core of the diagnostic, and the attached children provide additional context, help, and information.

Diagnostics have the following format:

{
    /* The primary message. */
    "message": "unused variable: `x`",
    /* The diagnostic code.
       Some messages may set this value to null.
    */
    "code": {
        /* A unique string identifying which diagnostic triggered. */
        "code": "unused_variables",
        /* An optional string explaining more detail about the diagnostic code. */
        "explanation": null
    },
    /* The severity of the diagnostic.
       Values may be:
       - "error": A fatal error that prevents compilation.
       - "warning": A possible error or concern.
       - "note": Additional information or context about the diagnostic.
       - "help": A suggestion on how to resolve the diagnostic.
       - "failure-note": A note attached to the message for further information.
       - "error: internal compiler error": Indicates a bug within the compiler.
    */
    "level": "warning",
    /* An array of source code locations to point out specific details about
       where the diagnostic originates from. This may be empty, for example
       for some global messages, or child messages attached to a parent.

       Character offsets are offsets of Unicode Scalar Values.
    */
    "spans": [
        {
            /* The file where the span is located.
               Note that this path may not exist. For example, if the path
               points to the standard library, and the rust src is not
               available in the sysroot, then it may point to a non-existent
               file. Beware that this may also point to the source of an
               external crate.
            */
            "file_name": "lib.rs",
            /* The byte offset where the span starts (0-based, inclusive). */
            "byte_start": 21,
            /* The byte offset where the span ends (0-based, exclusive). */
            "byte_end": 22,
            /* The first line number of the span (1-based, inclusive). */
            "line_start": 2,
            /* The last line number of the span (1-based, inclusive). */
            "line_end": 2,
            /* The first character offset of the line_start (1-based, inclusive). */
            "column_start": 9,
            /* The last character offset of the line_end (1-based, exclusive). */
            "column_end": 10,
            /* Whether or not this is the "primary" span.

               This indicates that this span is the focal point of the
               diagnostic.

               There are rare cases where multiple spans may be marked as
               primary. For example, "immutable borrow occurs here" and
               "mutable borrow ends here" can be two separate primary spans.

               The top (parent) message should always have at least one
               primary span, unless it has zero spans. Child messages may have
               zero or more primary spans.
            */
            "is_primary": true,
            /* An array of objects showing the original source code for this
               span. This shows the entire lines of text where the span is
               located. A span across multiple lines will have a separate
               value for each line.
            */
            "text": [
                {
                    /* The entire line of the original source code. */
                    "text": "    let x = 123;",
                    /* The first character offset of the line of
                       where the span covers this line (1-based, inclusive). */
                    "highlight_start": 9,
                    /* The last character offset of the line of
                       where the span covers this line (1-based, exclusive). */
                    "highlight_end": 10
                }
            ],
            /* An optional message to display at this span location.
               This is typically null for primary spans.
            */
            "label": null,
            /* An optional string of a suggested replacement for this span to
               solve the issue. Tools may try to replace the contents of the
               span with this text.
            */
            "suggested_replacement": null,
            /* An optional string that indicates the confidence of the
               "suggested_replacement". Tools may use this value to determine
               whether or not suggestions should be automatically applied.

               Possible values may be:
               - "MachineApplicable": The suggestion is definitely what the
                 user intended. This suggestion should be automatically
                 applied.
               - "MaybeIncorrect": The suggestion may be what the user
                 intended, but it is uncertain. The suggestion should result
                 in valid Rust code if it is applied.
               - "HasPlaceholders": The suggestion contains placeholders like
                 `(...)`. The suggestion cannot be applied automatically
                 because it will not result in valid Rust code. The user will
                 need to fill in the placeholders.
               - "Unspecified": The applicability of the suggestion is unknown.
            */
            "suggestion_applicability": null,
            /* An optional object indicating the expansion of a macro within
               this span.

               If a message occurs within a macro invocation, this object will
               provide details of where within the macro expansion the message
               is located.
            */
            "expansion": {
                /* The span of the macro invocation.
                   Uses the same span definition as the "spans" array.
                */
                "span": {/*...*/}
                /* Name of the macro, such as "foo!" or "#[derive(Eq)]". */
                "macro_decl_name": "some_macro!",
                /* Optional span where the relevant part of the macro is
                  defined. */
                "def_site_span": {/*...*/},
            }
        }
    ],
    /* Array of attached diagnostic messages.
       This is an array of objects using the same format as the parent
       message. Children are not nested (children do not themselves
       contain "children" definitions).
    */
    "children": [
        {
            "message": "`#[warn(unused_variables)]` on by default",
            "code": null,
            "level": "note",
            "spans": [],
            "children": [],
            "rendered": null
        },
        {
            "message": "if this is intentional, prefix it with an underscore",
            "code": null,
            "level": "help",
            "spans": [
                {
                    "file_name": "lib.rs",
                    "byte_start": 21,
                    "byte_end": 22,
                    "line_start": 2,
                    "line_end": 2,
                    "column_start": 9,
                    "column_end": 10,
                    "is_primary": true,
                    "text": [
                        {
                            "text": "    let x = 123;",
                            "highlight_start": 9,
                            "highlight_end": 10
                        }
                    ],
                    "label": null,
                    "suggested_replacement": "_x",
                    "suggestion_applicability": "MachineApplicable",
                    "expansion": null
                }
            ],
            "children": [],
            "rendered": null
        }
    ],
    /* Optional string of the rendered version of the diagnostic as displayed
       by rustc. Note that this may be influenced by the `--json` flag.
    */
    "rendered": "warning: unused variable: `x`\n --> lib.rs:2:9\n  |\n2 |     let x = 123;\n  |         ^ help: if this is intentional, prefix it with an underscore: `_x`\n  |\n  = note: `#[warn(unused_variables)]` on by default\n\n"
}

Artifact notifications

Artifact notifications are emitted when the --json=artifacts flag is used. They indicate that a file artifact has been saved to disk. More information about emit kinds may be found in the --emit flag documentation.

{
    /* The filename that was generated. */
    "artifact": "libfoo.rlib",
    /* The kind of artifact that was generated. Possible values:
       - "link": The generated crate as specified by the crate-type.
       - "dep-info": The `.d` file with dependency information in a Makefile-like syntax.
       - "metadata": The Rust `.rmeta` file containing metadata about the crate.
       - "save-analysis": A JSON file emitted by the `-Zsave-analysis` feature.
    */
    "emit": "link"
}

Tests

rustc has a built-in facility for building and running tests for a crate. More information about writing and running tests may be found in the Testing Chapter of the Rust Programming Language book.

Tests are written as free functions with the #[test] attribute. For example:


#![allow(unused)]
fn main() {
#[test]
fn it_works() {
    assert_eq!(2 + 2, 4);
}
}

Tests "pass" if they return without an error. They "fail" if they panic, or return a Result with an error.

By passing the --test option to rustc, the compiler will build the crate in a special mode to construct an executable that will run the tests in the crate. The --test flag will make the following changes:

  • The crate will be built as a bin crate type, forcing it to be an executable.
  • Links the executable with libtest, the test harness that is part of the standard library, which handles running the tests.
  • Synthesizes a main function which will process command-line arguments and run the tests. This new main function will replace any existing main function as the entry point of the executable, though the existing main will still be compiled.
  • Enables the test cfg option, which allows your code to use conditional compilation to detect if it is being built as a test.
  • Enables building of functions annotated with the test and bench attributes, which will be run by the test harness.

After the executable is created, you can run it to execute the tests and receive a report on what passes and fails. If you are using Cargo to manage your project, it has a built-in cargo test command which handles all of this automatically. An example of the output looks like this:

running 4 tests
test it_works ... ok
test check_valid_args ... ok
test invalid_characters ... ok
test walks_the_dog ... ok

test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

Note: Tests must be built with the unwind panic strategy. This is because all tests run in the same process, and they are intended to catch panics, which is not possible with the abort strategy. See the unstable -Z panic-abort-tests option for experimental support of the abort strategy by spawning tests in separate processes.

Test attributes

Tests are indicated using attributes on free functions. The following attributes are used for testing, see the linked documentation for more details:

  • #[test] — Indicates a function is a test to be run.
  • #[bench] — Indicates a function is a benchmark to be run. Benchmarks are currently unstable and only available in the nightly channel, see the unstable docs for more details.
  • #[should_panic] — Indicates that the test function will only pass if the function panics.
  • #[ignore] — Indicates that the test function will be compiled, but not run by default. See the --ignored and --include-ignored options to run these tests.

CLI arguments

The libtest harness has several command-line arguments to control its behavior.

Note: When running with cargo test, the libtest CLI arguments must be passed after the -- argument to differentiate between flags for Cargo and those for the harness. For example: cargo test -- --nocapture

Filters

Positional arguments (those without a - prefix) are treated as filters which will only run tests whose name matches one of those strings. The filter will match any substring found in the full path of the test function. For example, if the test function it_works is located in the module utils::paths::tests, then any of the filters works, path, utils::, or utils::paths::tests::it_works will match that test.

See Selection options for more options to control which tests are run.

Action options

The following options perform different actions other than running tests.

--list

Prints a list of all tests and benchmarks. Does not run any of the tests. Filters can be used to list only matching tests.

-h, --help

Displays usage information and command-line options.

Selection options

The following options change how tests are selected.

--test

This is the default mode where all tests will be run as well as running all benchmarks with only a single iteration (to ensure the benchmark works, without taking the time to actually perform benchmarking). This can be combined with the --bench flag to run both tests and perform full benchmarking.

--bench

This runs in a mode where tests are ignored, and only runs benchmarks. This can be combined with --test to run both benchmarks and tests.

--exact

This forces filters to match the full path of the test exactly. For example, if the test it_works is in the module utils::paths::tests, then only the string utils::paths::tests::it_works will match that test.

--skip FILTER

Skips any tests whose name contains the given FILTER string. This flag may be passed multiple times.

--ignored

Runs only tests that are marked with the ignore attribute.

--include-ignored

Runs both ignored and non-ignored tests.

--exclude-should-panic

Excludes tests marked with the should_panic attribute.

⚠️ 🚧 This option is unstable, and requires the -Z unstable-options flag. See tracking issue #82348 for more information.

Execution options

The following options affect how tests are executed.

--test-threads NUM_THREADS

Sets the number of threads to use for running tests in parallel. By default, uses the amount of concurrency available on the hardware as indicated by available_concurrency.

This can also be specified with the RUST_TEST_THREADS environment variable.

--force-run-in-process

Forces the tests to run in a single process when using the abort panic strategy.

⚠️ 🚧 This only works with the unstable -Z panic-abort-tests option, and requires the -Z unstable-options flag. See tracking issue #67650 for more information.

--ensure-time

⚠️ 🚧 This option is unstable, and requires the -Z unstable-options flag. See tracking issue #64888 and the unstable docs for more information.

Output options

The following options affect the output behavior.

-q, --quiet

Displays one character per test instead of one line per test. This is an alias for --format=terse.

--nocapture

Does not capture the stdout and stderr of the test, and allows tests to print to the console. Usually the output is captured, and only displayed if the test fails.

This may also be specified by setting the RUST_TEST_NOCAPTURE environment variable set to anything but 0.

--show-output

Displays the stdout and stderr of successful tests after all tests have run.

Contrast this with --nocapture which allows tests to print while they are running, which can cause interleaved output if there are multiple tests running in parallel, --show-output ensures the output is contiguous, but requires waiting for all tests to finish.

--color COLOR

Control when colored terminal output is used. Valid options:

  • auto: Colorize if stdout is a tty and --nocapture is not used. This is the default.
  • always: Always colorize the output.
  • never: Never colorize the output.

--format FORMAT

Controls the format of the output. Valid options:

  • pretty: This is the default format, with one line per test.
  • terse: Displays only a single character per test. --quiet is an alias for this option.
  • json: Emits JSON objects, one per line. ⚠️ 🚧 This option is unstable, and requires the -Z unstable-options flag. See tracking issue #49359 for more information.

--logfile PATH

Writes the results of the tests to the given file.

--report-time FORMAT

⚠️ 🚧 This option is unstable, and requires the -Z unstable-options flag. See tracking issue #64888 and the unstable docs for more information.

Unstable options

Some CLI options are added in an "unstable" state, where they are intended for experimentation and testing to determine if the option works correctly, has the right design, and is useful. The option may not work correctly, break, or change at any time. To signal that you acknowledge that you are using an unstable option, they require passing the -Z unstable-options command-line flag.

Benchmarks

The libtest harness supports running benchmarks for functions annotated with the #[bench] attribute. Benchmarks are currently unstable, and only available on the nightly channel. More information may be found in the unstable book.

Custom test frameworks

Experimental support for using custom test harnesses is available on the nightly channel. See tracking issue #50297 and the custom_test_frameworks documentation for more information.

Platform Support

Support for different platforms ("targets") are organized into three tiers, each with a different set of guarantees. For more information on the policies for targets at each tier, see the Target Tier Policy.

Targets are identified by their "target triple" which is the string to inform the compiler what kind of output should be produced.

Tier 1 with Host Tools

Tier 1 targets can be thought of as "guaranteed to work". The Rust project builds official binary releases for each tier 1 target, and automated testing ensures that each tier 1 target builds and passes tests after each change.

Tier 1 targets with host tools additionally support running tools like rustc and cargo natively on the target, and automated testing ensures that tests pass for the host tools as well. This allows the target to be used as a development platform, not just a compilation target. For the full requirements, see Tier 1 with Host Tools in the Target Tier Policy.

All tier 1 targets with host tools support the full standard library.

targetnotes
aarch64-unknown-linux-gnuARM64 Linux (kernel 4.2, glibc 2.17+) 1
i686-pc-windows-gnu32-bit MinGW (Windows 7+)
i686-pc-windows-msvc32-bit MSVC (Windows 7+)
i686-unknown-linux-gnu32-bit Linux (kernel 2.6.32+, glibc 2.11+)
x86_64-apple-darwin64-bit macOS (10.7+, Lion+)
x86_64-pc-windows-gnu64-bit MinGW (Windows 7+)
x86_64-pc-windows-msvc64-bit MSVC (Windows 7+)
x86_64-unknown-linux-gnu64-bit Linux (kernel 2.6.32+, glibc 2.11+)
1

Stack probes support is missing on aarch64-unknown-linux-gnu, but it's planned to be implemented in the near future. The implementation is tracked on issue #77071.

Tier 1

Tier 1 targets can be thought of as "guaranteed to work". The Rust project builds official binary releases for each tier 1 target, and automated testing ensures that each tier 1 target builds and passes tests after each change. For the full requirements, see Tier 1 target policy in the Target Tier Policy.

At this time, all Tier 1 targets are Tier 1 with Host Tools.

Tier 2 with Host Tools

Tier 2 targets can be thought of as "guaranteed to build". The Rust project builds official binary releases for each tier 2 target, and automated builds ensure that each tier 2 target builds after each change. Automated tests are not always run so it's not guaranteed to produce a working build, but tier 2 targets often work to quite a good degree and patches are always welcome!

Tier 2 targets with host tools additionally support running tools like rustc and cargo natively on the target, and automated builds ensure that the host tools build as well. This allows the target to be used as a development platform, not just a compilation target. For the full requirements, see Tier 2 with Host Tools in the Target Tier Policy.

All tier 2 targets with host tools support the full standard library.

targetnotes
aarch64-apple-darwinARM64 macOS (11.0+, Big Sur+)
aarch64-pc-windows-msvcARM64 Windows MSVC
aarch64-unknown-linux-muslARM64 Linux with MUSL
arm-unknown-linux-gnueabiARMv6 Linux (kernel 3.2, glibc 2.17)
arm-unknown-linux-gnueabihfARMv6 Linux, hardfloat (kernel 3.2, glibc 2.17)
armv7-unknown-linux-gnueabihfARMv7 Linux, hardfloat (kernel 3.2, glibc 2.17)
mips-unknown-linux-gnuMIPS Linux (kernel 4.4, glibc 2.23)
mips64-unknown-linux-gnuabi64MIPS64 Linux, n64 ABI (kernel 4.4, glibc 2.23)
mips64el-unknown-linux-gnuabi64MIPS64 (LE) Linux, n64 ABI (kernel 4.4, glibc 2.23)
mipsel-unknown-linux-gnuMIPS (LE) Linux (kernel 4.4, glibc 2.23)
powerpc-unknown-linux-gnuPowerPC Linux (kernel 2.6.32, glibc 2.11)
powerpc64-unknown-linux-gnuPPC64 Linux (kernel 2.6.32, glibc 2.11)
powerpc64le-unknown-linux-gnuPPC64LE Linux (kernel 3.10, glibc 2.17)
riscv64gc-unknown-linux-gnuRISC-V Linux (kernel 4.20, glibc 2.29)
s390x-unknown-linux-gnuS390x Linux (kernel 2.6.32, glibc 2.11)
x86_64-unknown-freebsd64-bit FreeBSD
x86_64-unknown-illumosillumos
x86_64-unknown-linux-musl64-bit Linux with MUSL
x86_64-unknown-netbsdNetBSD/amd64

Tier 2

Tier 2 targets can be thought of as "guaranteed to build". The Rust project builds official binary releases for each tier 2 target, and automated builds ensure that each tier 2 target builds after each change. Automated tests are not always run so it's not guaranteed to produce a working build, but tier 2 targets often work to quite a good degree and patches are always welcome! For the full requirements, see Tier 2 target policy in the Target Tier Policy.

The std column in the table below has the following meanings:

  • ✓ indicates the full standard library is available.
  • * indicates the target only supports no_std development.
targetstdnotes
aarch64-apple-iosARM64 iOS
aarch64-fuchsiaARM64 Fuchsia
aarch64-linux-androidARM64 Android
aarch64-unknown-none-softfloat*Bare ARM64, softfloat
aarch64-unknown-none*Bare ARM64, hardfloat
arm-linux-androideabiARMv7 Android
arm-unknown-linux-musleabiARMv6 Linux with MUSL
arm-unknown-linux-musleabihfARMv6 Linux with MUSL, hardfloat
armebv7r-none-eabi*Bare ARMv7-R, Big Endian
armebv7r-none-eabihf*Bare ARMv7-R, Big Endian, hardfloat
armv5te-unknown-linux-gnueabiARMv5TE Linux (kernel 4.4, glibc 2.23)
armv5te-unknown-linux-musleabiARMv5TE Linux with MUSL
armv7-linux-androideabiARMv7a Android
armv7-unknown-linux-gnueabiARMv7 Linux (kernel 4.15, glibc 2.27)
armv7-unknown-linux-musleabiARMv7 Linux, MUSL
armv7-unknown-linux-musleabihfARMv7 Linux with MUSL
armv7a-none-eabi*Bare ARMv7-A
armv7r-none-eabi*Bare ARMv7-R
armv7r-none-eabihf*Bare ARMv7-R, hardfloat
asmjs-unknown-emscriptenasm.js via Emscripten
i586-pc-windows-msvc32-bit Windows w/o SSE
i586-unknown-linux-gnu32-bit Linux w/o SSE (kernel 4.4, glibc 2.23)
i586-unknown-linux-musl32-bit Linux w/o SSE, MUSL
i686-linux-android32-bit x86 Android
i686-unknown-freebsd32-bit FreeBSD
i686-unknown-linux-musl32-bit Linux with MUSL
mips-unknown-linux-muslMIPS Linux with MUSL
mips64-unknown-linux-muslabi64MIPS64 Linux, n64 ABI, MUSL
mips64el-unknown-linux-muslabi64MIPS64 (LE) Linux, n64 ABI, MUSL
mipsel-unknown-linux-muslMIPS (LE) Linux with MUSL
nvptx64-nvidia-cuda--emit=asm generates PTX code that runs on NVIDIA GPUs
riscv32i-unknown-none-elf*Bare RISC-V (RV32I ISA)
riscv32imac-unknown-none-elf*Bare RISC-V (RV32IMAC ISA)
riscv32imc-unknown-none-elf*Bare RISC-V (RV32IMC ISA)
riscv64gc-unknown-none-elf*Bare RISC-V (RV64IMAFDC ISA)
riscv64imac-unknown-none-elf*Bare RISC-V (RV64IMAC ISA)
sparc64-unknown-linux-gnuSPARC Linux (kernel 4.4, glibc 2.23)
sparcv9-sun-solarisSPARC Solaris 10/11, illumos
thumbv6m-none-eabi*Bare Cortex-M0, M0+, M1
thumbv7em-none-eabi*Bare Cortex-M4, M7
thumbv7em-none-eabihf*Bare Cortex-M4F, M7F, FPU, hardfloat
thumbv7m-none-eabi*Bare Cortex-M3
thumbv7neon-linux-androideabiThumb2-mode ARMv7a Android with NEON
thumbv7neon-unknown-linux-gnueabihfThumb2-mode ARMv7a Linux with NEON (kernel 4.4, glibc 2.23)
thumbv8m.base-none-eabi*ARMv8-M Baseline
thumbv8m.main-none-eabi*ARMv8-M Mainline
thumbv8m.main-none-eabihf*ARMv8-M Mainline, hardfloat
wasm32-unknown-emscriptenWebAssembly via Emscripten
wasm32-unknown-unknownWebAssembly
wasm32-wasiWebAssembly with WASI
x86_64-apple-ios64-bit x86 iOS
x86_64-fortanix-unknown-sgxFortanix ABI for 64-bit Intel SGX
x86_64-fuchsia64-bit Fuchsia
x86_64-linux-android64-bit x86 Android
x86_64-pc-solaris64-bit Solaris 10/11, illumos
x86_64-unknown-linux-gnux3264-bit Linux (x32 ABI) (kernel 4.15, glibc 2.27)
x86_64-unknown-redoxRedox OS

Tier 3

Tier 3 targets are those which the Rust codebase has support for, but which the Rust project does not build or test automatically, so they may or may not work. Official builds are not available. For the full requirements, see Tier 3 target policy in the Target Tier Policy.

The std column in the table below has the following meanings:

  • ✓ indicates the full standard library is available.
  • * indicates the target only supports no_std development.
  • ? indicates the standard library support is unknown or a work-in-progress.

The host column indicates whether the codebase includes support for building host tools.

targetstdhostnotes
aarch64-apple-ios-macabi?Apple Catalyst on ARM64
aarch64-apple-ios-sim?Apple iOS Simulator on ARM64
aarch64-apple-tvos*ARM64 tvOS
aarch64-unknown-freebsdARM64 FreeBSD
aarch64-unknown-hermit?
aarch64-unknown-linux-gnu_ilp32ARM64 Linux (ILP32 ABI)
aarch64-unknown-netbsd
aarch64-unknown-openbsdARM64 OpenBSD
aarch64-unknown-redox?ARM64 Redox OS
aarch64-uwp-windows-msvc?
aarch64-wrs-vxworks?
aarch64_be-unknown-linux-gnu_ilp32ARM64 Linux (big-endian, ILP32 ABI)
aarch64_be-unknown-linux-gnuARM64 Linux (big-endian)
armv4t-unknown-linux-gnueabi?
armv5te-unknown-linux-uclibceabi?ARMv5TE Linux with uClibc
armv6-unknown-freebsdARMv6 FreeBSD
armv6-unknown-netbsd-eabihf?
armv7-apple-iosARMv7 iOS, Cortex-a8
armv7-unknown-freebsdARMv7 FreeBSD
armv7-unknown-netbsd-eabihf
armv7-wrs-vxworks-eabihf?
armv7a-none-eabihf*ARM Cortex-A, hardfloat
armv7s-apple-ios
avr-unknown-gnu-atmega328*AVR. Requires -Z build-std=core
hexagon-unknown-linux-musl?
i386-apple-ios32-bit x86 iOS
i686-apple-darwin32-bit macOS (10.7+, Lion+)
i686-pc-windows-msvc32-bit Windows XP support
i686-unknown-haiku32-bit Haiku
i686-unknown-netbsdNetBSD/i386 with SSE2
i686-unknown-openbsd32-bit OpenBSD
i686-unknown-uefi*32-bit UEFI
i686-uwp-windows-gnu?
i686-uwp-windows-msvc?
i686-wrs-vxworks?
mips-unknown-linux-uclibcMIPS Linux with uClibc
mipsel-sony-psp*MIPS (LE) Sony PlayStation Portable (PSP)
mipsel-unknown-linux-uclibcMIPS (LE) Linux with uClibc
mipsel-unknown-none*Bare MIPS (LE) softfloat
mipsisa32r6-unknown-linux-gnu?
mipsisa32r6el-unknown-linux-gnu?
mipsisa64r6-unknown-linux-gnuabi64?
mipsisa64r6el-unknown-linux-gnuabi64?
msp430-none-elf*16-bit MSP430 microcontrollers
powerpc-unknown-linux-gnuspePowerPC SPE Linux
powerpc-unknown-linux-musl?
powerpc-unknown-netbsd
powerpc-unknown-openbsd?
powerpc-wrs-vxworks-spe?
powerpc-wrs-vxworks?
powerpc64-unknown-freebsdPPC64 FreeBSD (ELFv1 and ELFv2)
powerpc64-unknown-linux-musl?
powerpc64-wrs-vxworks?
powerpc64le-unknown-linux-musl?
riscv32gc-unknown-linux-gnuRISC-V Linux (kernel 5.4, glibc 2.33)
riscv32gc-unknown-linux-muslRISC-V Linux (kernel 5.4, musl + RISCV32 support patches)
riscv64gc-unknown-linux-muslRISC-V Linux (kernel 4.20, musl 1.2.0)
s390x-unknown-linux-muslS390x Linux (kernel 2.6.32, MUSL)
sparc-unknown-linux-gnu32-bit SPARC Linux
sparc64-unknown-netbsdNetBSD/sparc64
sparc64-unknown-openbsd?
thumbv4t-none-eabi*ARMv4T T32
thumbv7a-pc-windows-msvc?
thumbv7a-uwp-windows-msvc
thumbv7neon-unknown-linux-musleabihf?Thumb2-mode ARMv7a Linux with NEON, MUSL
wasm64-unknown-unknown*WebAssembly
x86_64-apple-ios-macabiApple Catalyst on x86_64
x86_64-apple-tvos*x86 64-bit tvOS
x86_64-pc-windows-msvc64-bit Windows XP support
x86_64-sun-solaris?Deprecated target for 64-bit Solaris 10/11, illumos
x86_64-unknown-dragonfly64-bit DragonFlyBSD
x86_64-unknown-haiku64-bit Haiku
x86_64-unknown-hermit?
x86_64-unknown-l4re-uclibc?
x86_64-unknown-none-hermitkernel?HermitCore kernel
x86_64-unknown-none-linuxkernel*Linux kernel modules
x86_64-unknown-openbsd64-bit OpenBSD
x86_64-unknown-uefi*64-bit UEFI
x86_64-uwp-windows-gnu
x86_64-uwp-windows-msvc
x86_64-wrs-vxworks?

Target Tier Policy

Table of Contents

General

Rust provides three tiers of target support:

  • Rust provides no guarantees about tier 3 targets; they exist in the codebase, but may or may not build.
  • Rust's continuous integration checks that tier 2 targets will always build, but they may or may not pass tests.
  • Rust's continuous integration checks that tier 1 targets will always build and pass tests.

Adding a new tier 3 target imposes minimal requirements; we focus primarily on avoiding disruption to other ongoing Rust development.

Tier 2 and tier 1 targets place work on Rust project developers as a whole, to avoid breaking the target. The broader Rust community may also feel more inclined to support higher-tier targets in their crates (though they are not obligated to do so). Thus, these tiers require commensurate and ongoing efforts from the maintainers of the target, to demonstrate value and to minimize any disruptions to ongoing Rust development.

This policy defines the requirements for accepting a proposed target at a given level of support.

Each tier builds on all the requirements from the previous tier, unless overridden by a stronger requirement. Targets at tier 2 and tier 1 may also provide host tools (such as rustc and cargo); each of those tiers includes a set of supplementary requirements that must be met if supplying host tools for the target. A target at tier 2 or tier 1 is not required to supply host tools, but if it does, it must meet the corresponding additional requirements for host tools.

The policy for each tier also documents the Rust governance teams that must approve the addition of any target at that tier. Those teams are responsible for reviewing and evaluating the target, based on these requirements and their own judgment. Those teams may apply additional requirements, including subjective requirements, such as to deal with issues not foreseen by this policy. (Such requirements may subsequently motivate additions to this policy.)

While these criteria attempt to document the policy, that policy still involves human judgment. Targets must fulfill the spirit of the requirements as well, as determined by the judgment of the approving teams. Reviewers and team members evaluating targets and target-specific patches should always use their own best judgment regarding the quality of work, and the suitability of a target for the Rust project. Neither this policy nor any decisions made regarding targets shall create any binding agreement or estoppel by any party.

Before filing an issue or pull request (PR) to introduce or promote a target, the target should already meet the corresponding tier requirements. This does not preclude an existing target's maintainers using issues (on the Rust repository or otherwise) to track requirements that have not yet been met, as appropriate; however, before officially proposing the introduction or promotion of a target, it should meet all of the necessary requirements. A target proposal is encouraged to quote the corresponding requirements verbatim as part of explaining how the target meets those requirements.

For a list of all supported targets and their corresponding tiers ("tier 3", "tier 2", "tier 2 with host tools", "tier 1", or "tier 1 with host tools"), see platform support.

Note that a target must have already received approval for the next lower tier, and spent a reasonable amount of time at that tier, before making a proposal for promotion to the next higher tier; this is true even if a target meets the requirements for several tiers at once. This policy leaves the precise interpretation of "reasonable amount of time" up to the approving teams; those teams may scale the amount of time required based on their confidence in the target and its demonstrated track record at its current tier. At a minimum, multiple stable releases of Rust should typically occur between promotions of a target.

The availability or tier of a target in stable Rust is not a hard stability guarantee about the future availability or tier of that target. Higher-level target tiers are an increasing commitment to the support of a target, and we will take that commitment and potential disruptions into account when evaluating the potential demotion or removal of a target that has been part of a stable release. The promotion or demotion of a target will not generally affect existing stable releases, only current development and future releases.

In this policy, the words "must" and "must not" specify absolute requirements that a target must meet to qualify for a tier. The words "should" and "should not" specify requirements that apply in almost all cases, but for which the approving teams may grant an exception for good reason. The word "may" indicates something entirely optional, and does not indicate guidance or recommendations. This language is based on IETF RFC 2119.

Tier 3 target policy

At this tier, the Rust project provides no official support for a target, so we place minimal requirements on the introduction of targets.

A proposed new tier 3 target must be reviewed and approved by a member of the compiler team based on these requirements. The reviewer may choose to gauge broader compiler team consensus via a Major Change Proposal (MCP).

A proposed target or target-specific patch that substantially changes code shared with other targets (not just target-specific code) must be reviewed and approved by the appropriate team for that shared code before acceptance.

  • A tier 3 target must have a designated developer or developers (the "target maintainers") on record to be CCed when issues arise regarding the target. (The mechanism to track and CC such developers may evolve over time.)
  • Targets must use naming consistent with any existing targets; for instance, a target for the same CPU or OS as an existing Rust target should use the same name for that CPU or OS. Targets should normally use the same names and naming conventions as used elsewhere in the broader ecosystem beyond Rust (such as in other toolchains), unless they have a very good reason to diverge. Changing the name of a target can be highly disruptive, especially once the target reaches a higher tier, so getting the name right is important even for a tier 3 target.
    • Target names should not introduce undue confusion or ambiguity unless absolutely necessary to maintain ecosystem compatibility. For example, if the name of the target makes people extremely likely to form incorrect beliefs about what it targets, the name should be changed or augmented to disambiguate it.
  • Tier 3 targets may have unusual requirements to build or use, but must not create legal issues or impose onerous legal terms for the Rust project or for Rust developers or users.
    • The target must not introduce license incompatibilities.
    • Anything added to the Rust repository must be under the standard Rust license (MIT OR Apache-2.0).
    • The target must not cause the Rust tools or libraries built for any other host (even when supporting cross-compilation to the target) to depend on any new dependency less permissive than the Rust licensing policy. This applies whether the dependency is a Rust crate that would require adding new license exceptions (as specified by the tidy tool in the rust-lang/rust repository), or whether the dependency is a native library or binary. In other words, the introduction of the target must not cause a user installing or running a version of Rust or the Rust tools to be subject to any new license requirements.
    • If the target supports building host tools (such as rustc or cargo), those host tools must not depend on proprietary (non-FOSS) libraries, other than ordinary runtime libraries supplied by the platform and commonly used by other binaries built for the target. For instance, rustc built for the target may depend on a common proprietary C runtime library or console output library, but must not depend on a proprietary code generation library or code optimization library. Rust's license permits such combinations, but the Rust project has no interest in maintaining such combinations within the scope of Rust itself, even at tier 3.
    • Targets should not require proprietary (non-FOSS) components to link a functional binary or library.
    • "onerous" here is an intentionally subjective term. At a minimum, "onerous" legal/licensing terms include but are not limited to: non-disclosure requirements, non-compete requirements, contributor license agreements (CLAs) or equivalent, "non-commercial"/"research-only"/etc terms, requirements conditional on the employer or employment of any particular Rust developers, revocable terms, any requirements that create liability for the Rust project or its developers or users, or any requirements that adversely affect the livelihood or prospects of the Rust project or its developers or users.
  • Neither this policy nor any decisions made regarding targets shall create any binding agreement or estoppel by any party. If any member of an approving Rust team serves as one of the maintainers of a target, or has any legal or employment requirement (explicit or implicit) that might affect their decisions regarding a target, they must recuse themselves from any approval decisions regarding the target's tier status, though they may otherwise participate in discussions.
    • This requirement does not prevent part or all of this policy from being cited in an explicit contract or work agreement (e.g. to implement or maintain support for a target). This requirement exists to ensure that a developer or team responsible for reviewing and approving a target does not face any legal threats or obligations that would prevent them from freely exercising their judgment in such approval, even if such judgment involves subjective matters or goes beyond the letter of these requirements.
  • Tier 3 targets should attempt to implement as much of the standard libraries as possible and appropriate (core for most targets, alloc for targets that can support dynamic memory allocation, std for targets with an operating system or equivalent layer of system-provided functionality), but may leave some code unimplemented (either unavailable or stubbed out as appropriate), whether because the target makes it impossible to implement or challenging to implement. The authors of pull requests are not obligated to avoid calling any portions of the standard library on the basis of a tier 3 target not implementing those portions.
  • The target must provide documentation for the Rust community explaining how to build for the target, using cross-compilation if possible. If the target supports running tests (even if they do not pass), the documentation must explain how to run tests for the target, using emulation if possible or dedicated hardware if necessary.
  • Tier 3 targets must not impose burden on the authors of pull requests, or other developers in the community, to maintain the target. In particular, do not post comments (automated or manual) on a PR that derail or suggest a block on the PR based on a tier 3 target. Do not send automated messages or notifications (via any medium, including via @) to a PR author or others involved with a PR regarding a tier 3 target, unless they have opted into such messages.
    • Backlinks such as those generated by the issue/PR tracker when linking to an issue or PR are not considered a violation of this policy, within reason. However, such messages (even on a separate repository) must not generate notifications to anyone involved with a PR who has not requested such notifications.
  • Patches adding or updating tier 3 targets must not break any existing tier 2 or tier 1 target, and must not knowingly break another tier 3 target without approval of either the compiler team or the maintainers of the other tier 3 target.
    • In particular, this may come up when working on closely related targets, such as variations of the same architecture with different features. Avoid introducing unconditional uses of features that another variation of the target may not have; use conditional compilation or runtime detection, as appropriate, to let each target run code supported by that target.

If a tier 3 target stops meeting these requirements, or the target maintainers no longer have interest or time, or the target shows no signs of activity and has not built for some time, or removing the target would improve the quality of the Rust codebase, we may post a PR to remove it; any such PR will be CCed to the target maintainers (and potentially other people who have previously worked on the target), to check potential interest in improving the situation.

Tier 2 target policy

At this tier, the Rust project guarantees that a target builds, and will reject patches that fail to build on a target. Thus, we place requirements that ensure the target will not block forward progress of the Rust project.

A proposed new tier 2 target must be reviewed and approved by the compiler team based on these requirements. Such review and approval may occur via a Major Change Proposal (MCP).

In addition, the infrastructure team must approve the integration of the target into Continuous Integration (CI), and the tier 2 CI-related requirements. This review and approval may take place in a PR adding the target to CI, or simply by an infrastructure team member reporting the outcome of a team discussion.

  • A tier 2 target must have value to people other than its maintainers. (It may still be a niche target, but it must not be exclusively useful for an inherently closed group.)
  • A tier 2 target must have a designated team of developers (the "target maintainers") available to consult on target-specific build-breaking issues, or if necessary to develop target-specific language or library implementation details. This team must have at least 2 developers.
    • The target maintainers should not only fix target-specific issues, but should use any such issue as an opportunity to educate the Rust community about portability to their target, and enhance documentation of the target.
  • The target must not place undue burden on Rust developers not specifically concerned with that target. Rust developers are expected to not gratuitously break a tier 2 target, but are not expected to become experts in every tier 2 target, and are not expected to provide target-specific implementations for every tier 2 target.
  • The target must provide documentation for the Rust community explaining how to build for the target using cross-compilation, and explaining how to run tests for the target. If at all possible, this documentation should show how to run Rust programs and tests for the target using emulation, to allow anyone to do so. If the target cannot be feasibly emulated, the documentation should explain how to obtain and work with physical hardware, cloud systems, or equivalent.
  • The target must document its baseline expectations for the features or versions of CPUs, operating systems, libraries, runtime environments, and similar.
  • If introducing a new tier 2 or higher target that is identical to an existing Rust target except for the baseline expectations for the features or versions of CPUs, operating systems, libraries, runtime environments, and similar, then the proposed target must document to the satisfaction of the approving teams why the specific difference in baseline expectations provides sufficient value to justify a separate target.
    • Note that in some cases, based on the usage of existing targets within the Rust community, Rust developers or a target's maintainers may wish to modify the baseline expectations of a target, or split an existing target into multiple targets with different baseline expectations. A proposal to do so will be treated similarly to the analogous promotion, demotion, or removal of a target, according to this policy, with the same team approvals required.
      • For instance, if an OS version has become obsolete and unsupported, a target for that OS may raise its baseline expectations for OS version (treated as though removing a target corresponding to the older versions), or a target for that OS may split out support for older OS versions into a lower-tier target (treated as though demoting a target corresponding to the older versions, and requiring justification for a new target at a lower tier for the older OS versions).
  • Tier 2 targets must not leave any significant portions of core or the standard library unimplemented or stubbed out, unless they cannot possibly be supported on the target.
    • The right approach to handling a missing feature from a target may depend on whether the target seems likely to develop the feature in the future. In some cases, a target may be co-developed along with Rust support, and Rust may gain new features on the target as that target gains the capabilities to support those features.
    • As an exception, a target identical to an existing tier 1 target except for lower baseline expectations for the OS, CPU, or similar, may propose to qualify as tier 2 (but not higher) without support for std if the target will primarily be used in no_std applications, to reduce the support burden for the standard library. In this case, evaluation of the proposed target's value will take this limitation into account.
  • The code generation backend for the target should not have deficiencies that invalidate Rust safety properties, as evaluated by the Rust compiler team. (This requirement does not apply to arbitrary security enhancements or mitigations provided by code generation backends, only to those properties needed to ensure safe Rust code cannot cause undefined behavior or other unsoundness.) If this requirement does not hold, the target must clearly and prominently document any such limitations as part of the target's entry in the target tier list, and ideally also via a failing test in the testsuite. The Rust compiler team must be satisfied with the balance between these limitations and the difficulty of implementing the necessary features.
    • For example, if Rust relies on a specific code generation feature to ensure that safe code cannot overflow the stack, the code generation for the target should support that feature.
    • If the Rust compiler introduces new safety properties (such as via new capabilities of a compiler backend), the Rust compiler team will determine if they consider those new safety properties a best-effort improvement for specific targets, or a required property for all Rust targets. In the latter case, the compiler team may require the maintainers of existing targets to either implement and confirm support for the property or update the target tier list with documentation of the missing property.
  • If the target supports C code, and the target has an interoperable calling convention for C code, the Rust target must support that C calling convention for the platform via extern "C". The C calling convention does not need to be the default Rust calling convention for the target, however.
  • The target must build reliably in CI, for all components that Rust's CI considers mandatory.
  • The approving teams may additionally require that a subset of tests pass in CI, such as enough to build a functional "hello world" program, ./x.py test --no-run, or equivalent "smoke tests". In particular, this requirement may apply if the target builds host tools, or if the tests in question provide substantial value via early detection of critical problems.
  • Building the target in CI must not take substantially longer than the current slowest target in CI, and should not substantially raise the maintenance burden of the CI infrastructure. This requirement is subjective, to be evaluated by the infrastructure team, and will take the community importance of the target into account.
  • Tier 2 targets should, if at all possible, support cross-compiling. Tier 2 targets should not require using the target as the host for builds, even if the target supports host tools.
  • In addition to the legal requirements for all targets (specified in the tier 3 requirements), because a tier 2 target typically involves the Rust project building and supplying various compiled binaries, incorporating the target and redistributing any resulting compiled binaries (e.g. built libraries, host tools if any) must not impose any onerous license requirements on any members of the Rust project, including infrastructure team members and those operating CI systems. This is a subjective requirement, to be evaluated by the approving teams.
    • As an exception to this, if the target's primary purpose is to build components for a Free and Open Source Software (FOSS) project licensed under "copyleft" terms (terms which require licensing other code under compatible FOSS terms), such as kernel modules or plugins, then the standard libraries for the target may potentially be subject to copyleft terms, as long as such terms are satisfied by Rust's existing practices of providing full corresponding source code. Note that anything added to the Rust repository itself must still use Rust's standard license terms.
  • Tier 2 targets must not impose burden on the authors of pull requests, or other developers in the community, to ensure that tests pass for the target. In particular, do not post comments (automated or manual) on a PR that derail or suggest a block on the PR based on tests failing for the target. Do not send automated messages or notifications (via any medium, including via @) to a PR author or others involved with a PR regarding the PR breaking tests on a tier 2 target, unless they have opted into such messages.
    • Backlinks such as those generated by the issue/PR tracker when linking to an issue or PR are not considered a violation of this policy, within reason. However, such messages (even on a separate repository) must not generate notifications to anyone involved with a PR who has not requested such notifications.
  • The target maintainers should regularly run the testsuite for the target, and should fix any test failures in a reasonably timely fashion.
  • All requirements for tier 3 apply.

A tier 2 target may be demoted or removed if it no longer meets these requirements. Any proposal for demotion or removal will be CCed to the target maintainers, and will be communicated widely to the Rust community before being dropped from a stable release. (The amount of time between such communication and the next stable release may depend on the nature and severity of the failed requirement, the timing of its discovery, whether the target has been part of a stable release yet, and whether the demotion or removal can be a planned and scheduled action.)

In some circumstances, especially if the target maintainers do not respond in a timely fashion, Rust teams may land pull requests that temporarily disable some targets in the nightly compiler, in order to implement a feature not yet supported by those targets. (As an example, this happened when introducing the 128-bit types u128 and i128.) Such a pull request will include notification and coordination with the maintainers of such targets, and will ideally happen towards the beginning of a new development cycle to give maintainers time to update their targets. The maintainers of such targets will then be expected to implement the corresponding target-specific support in order to re-enable the target. If the maintainers of such targets cannot provide such support in time for the next stable release, this may result in demoting or removing the targets.

Tier 2 with host tools

Some tier 2 targets may additionally have binaries built to run on them as a host (such as rustc and cargo). This allows the target to be used as a development platform, not just a compilation target.

A proposed new tier 2 target with host tools must be reviewed and approved by the compiler team based on these requirements. Such review and approval may occur via a Major Change Proposal (MCP).

In addition, the infrastructure team must approve the integration of the target's host tools into Continuous Integration (CI), and the CI-related requirements for host tools. This review and approval may take place in a PR adding the target's host tools to CI, or simply by an infrastructure team member reporting the outcome of a team discussion.

  • Depending on the target, its capabilities, its performance, and the likelihood of use for any given tool, the host tools provided for a tier 2 target may include only rustc and cargo, or may include additional tools such as clippy and rustfmt.
  • Approval of host tools will take into account the additional time required to build the host tools, and the substantial additional storage required for the host tools.
  • The host tools must have direct value to people other than the target's maintainers. (It may still be a niche target, but the host tools must not be exclusively useful for an inherently closed group.) This requirement will be evaluated independently from the corresponding tier 2 requirement.
    • The requirement to provide "direct value" means that it does not suffice to argue that having host tools will help the target's maintainers more easily provide the target to others. The tools themselves must provide value to others.
  • There must be a reasonable expectation that the host tools will be used, for purposes other than to prove that they can be used.
  • The host tools must build and run reliably in CI (for all components that Rust's CI considers mandatory), though they may or may not pass tests.
  • Building host tools for the target must not take substantially longer than building host tools for other targets, and should not substantially raise the maintenance burden of the CI infrastructure.
  • The host tools must provide a substantively similar experience as on other targets, subject to reasonable target limitations.
    • Adding a substantively different interface to an existing tool, or a target-specific interface to the functionality of an existing tool, requires design and implementation approval (e.g. RFC/MCP) from the appropriate approving teams for that tool.
      • Such an interface should have a design that could potentially work for other targets with similar properties.
      • This should happen separately from the review and approval of the target, to simplify the target review and approval processes, and to simplify the review and approval processes for the proposed new interface.
    • By way of example, a target that runs within a sandbox may need to modify the handling of files, tool invocation, and similar to meet the expectations and conventions of the sandbox, but must not introduce a separate "sandboxed compilation" interface separate from the CLI interface without going through the normal approval process for such an interface. Such an interface should take into account potential other targets with similar sandboxes.
  • If the host tools for the platform would normally be expected to be signed or equivalent (e.g. if running unsigned binaries or similar involves a "developer mode" or an additional prompt), it must be possible for the Rust project's automated builds to apply the appropriate signature process, without any manual intervention by either Rust developers, target maintainers, or a third party. This process must meet the approval of the infrastructure team.
    • This process may require one-time or semi-regular manual steps by the infrastructure team, such as registration or renewal of a signing key. Any such manual process must meet the approval of the infrastructure team.
    • This process may require the execution of a legal agreement with the signature provider. Such a legal agreement may be revocable, and may potentially require a nominal fee, but must not be otherwise onerous. Any such legal agreement must meet the approval of the infrastructure team. (The infrastructure team is not expected or required to sign binding legal agreements on behalf of the Rust project; this review and approval exists to ensure no terms are onerous or cause problems for infrastructure, especially if such terms may impose requirements or obligations on people who have access to target-specific infrastructure.)
    • Changes to this process, or to any legal agreements involved, may cause a target to stop meeting this requirement.
    • This process involved must be available under substantially similar non-onerous terms to the general public. Making it available exclusively to the Rust project does not suffice.
    • This requirement exists to ensure that Rust builds, including nightly builds, can meet the necessary requirements to allow users to smoothly run the host tools.
  • Providing host tools does not exempt a target from requirements to support cross-compilation if at all possible.
  • All requirements for tier 2 apply.

A target may be promoted directly from tier 3 to tier 2 with host tools if it meets all the necessary requirements, but doing so may introduce substantial additional complexity. If in doubt, the target should qualify for tier 2 without host tools first.

Tier 1 target policy

At this tier, the Rust project guarantees that a target builds and passes all tests, and will reject patches that fail to build or pass the testsuite on a target. We hold tier 1 targets to our highest standard of requirements.

A proposed new tier 1 target must be reviewed and approved by the compiler team based on these requirements. In addition, the release team must approve the viability and value of supporting the target. For a tier 1 target, this will typically take place via a full RFC proposing the target, to be jointly reviewed and approved by the compiler team and release team.

In addition, the infrastructure team must approve the integration of the target into Continuous Integration (CI), and the tier 1 CI-related requirements. This review and approval may take place in a PR adding the target to CI, by an infrastructure team member reporting the outcome of a team discussion, or by including the infrastructure team in the RFC proposing the target.

  • Tier 1 targets must have substantial, widespread interest within the developer community, and must serve the ongoing needs of multiple production users of Rust across multiple organizations or projects. These requirements are subjective, and determined by consensus of the approving teams. A tier 1 target may be demoted or removed if it becomes obsolete or no longer meets this requirement.
  • The target maintainer team must include at least 3 developers.
  • The target must build and pass tests reliably in CI, for all components that Rust's CI considers mandatory.
    • The target must not disable an excessive number of tests or pieces of tests in the testsuite in order to do so. This is a subjective requirement.
    • If the target does not have host tools support, or if the target has low performance, the infrastructure team may choose to have CI cross-compile the testsuite from another platform, and then run the compiled tests either natively or via accurate emulation. However, the approving teams may take such performance considerations into account when determining the viability of the target or of its host tools.
  • The target must provide as much of the Rust standard library as is feasible and appropriate to provide. For instance, if the target can support dynamic memory allocation, it must provide an implementation of alloc and the associated data structures.
  • Building the target and running the testsuite for the target must not take substantially longer than other targets, and should not substantially raise the maintenance burden of the CI infrastructure.
    • In particular, if building the target takes a reasonable amount of time, but the target cannot run the testsuite in a timely fashion due to low performance of either native code or accurate emulation, that alone may prevent the target from qualifying as tier 1.
  • If running the testsuite requires additional infrastructure (such as physical systems running the target), the target maintainers must arrange to provide such resources to the Rust project, to the satisfaction and approval of the Rust infrastructure team.
    • Such resources may be provided via cloud systems, via emulation, or via physical hardware.
    • If the target requires the use of emulation to meet any of the tier requirements, the approving teams for those requirements must have high confidence in the accuracy of the emulation, such that discrepancies between emulation and native operation that affect test results will constitute a high-priority bug in either the emulation or the implementation of the target.
    • If it is not possible to run the target via emulation, these resources must additionally be sufficient for the Rust infrastructure team to make them available for access by Rust team members, for the purposes of development and testing. (Note that the responsibility for doing target-specific development to keep the target well maintained remains with the target maintainers. This requirement ensures that it is possible for other Rust developers to test the target, but does not obligate other Rust developers to make target-specific fixes.)
    • Resources provided for CI and similar infrastructure must be available for continuous exclusive use by the Rust project. Resources provided for access by Rust team members for development and testing must be available on an exclusive basis when in use, but need not be available on a continuous basis when not in use.
  • Tier 1 targets must not have a hard requirement for signed, verified, or otherwise "approved" binaries. Developers must be able to build, run, and test binaries for the target on systems they control, or provide such binaries for others to run. (Doing so may require enabling some appropriate "developer mode" on such systems, but must not require the payment of any additional fee or other consideration, or agreement to any onerous legal agreements.)
    • The Rust project may decide to supply appropriately signed binaries if doing so provides a smoother experience for developers using the target, and a tier 2 target with host tools already requires providing appropriate mechanisms that enable our infrastructure to provide such signed binaries. However, this additional tier 1 requirement ensures that Rust developers can develop and test Rust software for the target (including Rust itself), and that development or testing for the target is not limited.
  • All requirements for tier 2 apply.

A tier 1 target may be demoted if it no longer meets these requirements but still meets the requirements for a lower tier. Any proposal for demotion of a tier 1 target requires a full RFC process, with approval by the compiler and release teams. Any such proposal will be communicated widely to the Rust community, both when initially proposed and before being dropped from a stable release. A tier 1 target is highly unlikely to be directly removed without first being demoted to tier 2 or tier 3. (The amount of time between such communication and the next stable release may depend on the nature and severity of the failed requirement, the timing of its discovery, whether the target has been part of a stable release yet, and whether the demotion or removal can be a planned and scheduled action.)

Raising the baseline expectations of a tier 1 target (such as the minimum CPU features or OS version required) requires the approval of the compiler and release teams, and should be widely communicated as well, but does not necessarily require a full RFC.

Tier 1 with host tools

Some tier 1 targets may additionally have binaries built to run on them as a host (such as rustc and cargo). This allows the target to be used as a development platform, not just a compilation target.

A proposed new tier 1 target with host tools must be reviewed and approved by the compiler team based on these requirements. In addition, the release team must approve the viability and value of supporting host tools for the target. For a tier 1 target, this will typically take place via a full RFC proposing the target, to be jointly reviewed and approved by the compiler team and release team.

In addition, the infrastructure team must approve the integration of the target's host tools into Continuous Integration (CI), and the CI-related requirements for host tools. This review and approval may take place in a PR adding the target's host tools to CI, by an infrastructure team member reporting the outcome of a team discussion, or by including the infrastructure team in the RFC proposing the target.

  • Tier 1 targets with host tools should typically include all of the additional tools such as clippy and rustfmt, unless there is a target-specific reason why a tool cannot possibly make sense for the target.
    • Unlike with tier 2, for tier 1 we will not exclude specific tools on the sole basis of them being less likely to be used; rather, we'll take that into account when considering whether the target should be at tier 1 with host tools. In general, on any tier 1 target with host tools, people should be able to expect to find and install all the same components that they would for any other tier 1 target with host tools.
  • Approval of host tools will take into account the additional time required to build the host tools, and the substantial additional storage required for the host tools.
  • Host tools for the target must have substantial, widespread interest within the developer community, and must serve the ongoing needs of multiple production users of Rust across multiple organizations or projects. These requirements are subjective, and determined by consensus of the approving teams. This requirement will be evaluated independently from the corresponding tier 1 requirement; it is possible for a target to have sufficient interest for cross-compilation, but not have sufficient interest for native compilation. The host tools may be dropped if they no longer meet this requirement, even if the target otherwise qualifies as tier 1.
  • The host tools must build, run, and pass tests reliably in CI, for all components that Rust's CI considers mandatory.
    • The target must not disable an excessive number of tests or pieces of tests in the testsuite in order to do so. This is a subjective requirement.
  • Building the host tools and running the testsuite for the host tools must not take substantially longer than other targets, and should not substantially raise the maintenance burden of the CI infrastructure.
    • In particular, if building the target's host tools takes a reasonable amount of time, but the target cannot run the testsuite in a timely fashion due to low performance of either native code or accurate emulation, that alone may prevent the target from qualifying as tier 1 with host tools.
  • Providing host tools does not exempt a target from requirements to support cross-compilation if at all possible.
  • All requirements for tier 2 targets with host tools apply.
  • All requirements for tier 1 apply.

A target seeking promotion to tier 1 with host tools should typically either be tier 2 with host tools or tier 1 without host tools, to reduce the number of requirements to simultaneously review and approve.

In addition to the general process for demoting a tier 1 target, a tier 1 target with host tools may be demoted (including having its host tools dropped, or being demoted to tier 2 with host tools) if it no longer meets these requirements but still meets the requirements for a lower tier. Any proposal for demotion of a tier 1 target (with or without host tools) requires a full RFC process, with approval by the compiler and release teams. Any such proposal will be communicated widely to the Rust community, both when initially proposed and before being dropped from a stable release.

Targets

rustc is a cross-compiler by default. This means that you can use any compiler to build for any architecture. The list of targets are the possible architectures that you can build for.

To see all the options that you can set with a target, see the docs here.

To compile to a particular target, use the --target flag:

$ rustc src/main.rs --target=wasm32-unknown-unknown

Target Features

x86, and ARMv8 are two popular CPU architectures. Their instruction sets form a common baseline across most CPUs. However, some CPUs extend these with custom instruction sets, e.g. vector (AVX), bitwise manipulation (BMI) or cryptographic (AES).

Developers, who know on which CPUs their compiled code is going to run can choose to add (or remove) CPU specific instruction sets via the -C target-feature=val flag.

Please note, that this flag is generally considered as unsafe. More details can be found in this section.

Built-in Targets

rustc ships with the ability to compile to many targets automatically, we call these "built-in" targets, and they generally correspond to targets that the team is supporting directly. To see the list of built-in targets, you can run rustc --print target-list.

Typically, a target needs a compiled copy of the Rust standard library to work. If using rustup, then check out the documentation on Cross-compilation on how to download a pre-built standard library built by the official Rust distributions. Most targets will need a system linker, and possibly other things.

Custom Targets

If you'd like to build for a target that is not yet supported by rustc, you can use a "custom target specification" to define a target. These target specification files are JSON. To see the JSON for the host target, you can run:

$ rustc +nightly -Z unstable-options --print target-spec-json

To see it for a different target, add the --target flag:

$ rustc +nightly -Z unstable-options --target=wasm32-unknown-unknown --print target-spec-json

To use a custom target, see the (unstable) build-std feature of cargo.

Known Issues

This section informs you about known "gotchas". Keep in mind, that this section is (and always will be) incomplete. For suggestions and amendments, feel free to contribute to this guide.

Target Features

Most target-feature problems arise, when mixing code that have the target-feature enabled with code that have it disabled. If you want to avoid undefined behavior, it is recommended to build all code (including the standard library and imported crates) with a common set of target-features.

By default, compiling your code with the -C target-feature flag will not recompile the entire standard library and/or imported crates with matching target features. Therefore, target features are generally considered as unsafe. Using #[target_feature] on individual functions makes the function unsafe.

Examples:

Target-FeatureIssueSeen onDescriptionDetails
+soft-float
and
-sse
Segfaults and ABI mismatchesx86 and x86-64The x86 and x86_64 architecture uses SSE registers (aka xmm) for floating point operations. Using software emulated floats ("soft-floats") disables usage of xmm registers, but parts of Rust's core libraries (e.g. std::f32 or std::f64) are compiled without soft-floats and expect parameters to be passed in xmm registers. This leads to ABI mismatches.

Attempting to compile with disabled SSE causes the same error, too.
#63466

Profile Guided Optimization

rustc supports doing profile-guided optimization (PGO). This chapter describes what PGO is, what it is good for, and how it can be used.

What Is Profiled-Guided Optimization?

The basic concept of PGO is to collect data about the typical execution of a program (e.g. which branches it is likely to take) and then use this data to inform optimizations such as inlining, machine-code layout, register allocation, etc.

There are different ways of collecting data about a program's execution. One is to run the program inside a profiler (such as perf) and another is to create an instrumented binary, that is, a binary that has data collection built into it, and run that. The latter usually provides more accurate data and it is also what is supported by rustc.

Usage

Generating a PGO-optimized program involves following a workflow with four steps:

  1. Compile the program with instrumentation enabled (e.g. rustc -Cprofile-generate=/tmp/pgo-data main.rs)
  2. Run the instrumented program (e.g. ./main) which generates a default_<id>.profraw file
  3. Convert the .profraw file into a .profdata file using LLVM's llvm-profdata tool
  4. Compile the program again, this time making use of the profiling data (for example rustc -Cprofile-use=merged.profdata main.rs)

An instrumented program will create one or more .profraw files, one for each instrumented binary. E.g. an instrumented executable that loads two instrumented dynamic libraries at runtime will generate three .profraw files. Running an instrumented binary multiple times, on the other hand, will re-use the respective .profraw files, updating them in place.

These .profraw files have to be post-processed before they can be fed back into the compiler. This is done by the llvm-profdata tool. This tool is most easily installed via

rustup component add llvm-tools-preview

Note that installing the llvm-tools-preview component won't add llvm-profdata to the PATH. Rather, the tool can be found in:

~/.rustup/toolchains/<toolchain>/lib/rustlib/<target-triple>/bin/

Alternatively, an llvm-profdata coming with a recent LLVM or Clang version usually works too.

The llvm-profdata tool merges multiple .profraw files into a single .profdata file that can then be fed back into the compiler via -Cprofile-use:

# STEP 1: Compile the binary with instrumentation
rustc -Cprofile-generate=/tmp/pgo-data -O ./main.rs

# STEP 2: Run the binary a few times, maybe with common sets of args.
#         Each run will create or update `.profraw` files in /tmp/pgo-data
./main mydata1.csv
./main mydata2.csv
./main mydata3.csv

# STEP 3: Merge and post-process all the `.profraw` files in /tmp/pgo-data
llvm-profdata merge -o ./merged.profdata /tmp/pgo-data

# STEP 4: Use the merged `.profdata` file during optimization. All `rustc`
#         flags have to be the same.
rustc -Cprofile-use=./merged.profdata -O ./main.rs

A Complete Cargo Workflow

Using this feature with Cargo works very similar to using it with rustc directly. Again, we generate an instrumented binary, run it to produce data, merge the data, and feed it back into the compiler. Some things of note:

  • We use the RUSTFLAGS environment variable in order to pass the PGO compiler flags to the compilation of all crates in the program.

  • We pass the --target flag to Cargo, which prevents the RUSTFLAGS arguments to be passed to Cargo build scripts. We don't want the build scripts to generate a bunch of .profraw files.

  • We pass --release to Cargo because that's where PGO makes the most sense. In theory, PGO can also be done on debug builds but there is little reason to do so.

  • It is recommended to use absolute paths for the argument of -Cprofile-generate and -Cprofile-use. Cargo can invoke rustc with varying working directories, meaning that rustc will not be able to find the supplied .profdata file. With absolute paths this is not an issue.

  • It is good practice to make sure that there is no left-over profiling data from previous compilation sessions. Just deleting the directory is a simple way of doing so (see STEP 0 below).

This is what the entire workflow looks like:

# STEP 0: Make sure there is no left-over profiling data from previous runs
rm -rf /tmp/pgo-data

# STEP 1: Build the instrumented binaries
RUSTFLAGS="-Cprofile-generate=/tmp/pgo-data" \
    cargo build --release --target=x86_64-unknown-linux-gnu

# STEP 2: Run the instrumented binaries with some typical data
./target/x86_64-unknown-linux-gnu/release/myprogram mydata1.csv
./target/x86_64-unknown-linux-gnu/release/myprogram mydata2.csv
./target/x86_64-unknown-linux-gnu/release/myprogram mydata3.csv

# STEP 3: Merge the `.profraw` files into a `.profdata` file
llvm-profdata merge -o /tmp/pgo-data/merged.profdata /tmp/pgo-data

# STEP 4: Use the `.profdata` file for guiding optimizations
RUSTFLAGS="-Cprofile-use=/tmp/pgo-data/merged.profdata" \
    cargo build --release --target=x86_64-unknown-linux-gnu

Troubleshooting

  • It is recommended to pass -Cllvm-args=-pgo-warn-missing-function during the -Cprofile-use phase. LLVM by default does not warn if it cannot find profiling data for a given function. Enabling this warning will make it easier to spot errors in your setup.

  • There is a known issue in Cargo prior to version 1.39 that will prevent PGO from working correctly. Be sure to use Cargo 1.39 or newer when doing PGO.

Further Reading

rustc's PGO support relies entirely on LLVM's implementation of the feature and is equivalent to what Clang offers via the -fprofile-generate / -fprofile-use flags. The Profile Guided Optimization section in Clang's documentation is therefore an interesting read for anyone who wants to use PGO with Rust.

Linker-plugin-LTO

The -C linker-plugin-lto flag allows for deferring the LTO optimization to the actual linking step, which in turn allows for performing interprocedural optimizations across programming language boundaries if all the object files being linked were created by LLVM based toolchains. The prime example here would be linking Rust code together with Clang-compiled C/C++ code.

Usage

There are two main cases how linker plugin based LTO can be used:

  • compiling a Rust staticlib that is used as a C ABI dependency
  • compiling a Rust binary where rustc invokes the linker

In both cases the Rust code has to be compiled with -C linker-plugin-lto and the C/C++ code with -flto or -flto=thin so that object files are emitted as LLVM bitcode.

Rust staticlib as dependency in C/C++ program

In this case the Rust compiler just has to make sure that the object files in the staticlib are in the right format. For linking, a linker with the LLVM plugin must be used (e.g. LLD).

Using rustc directly:

# Compile the Rust staticlib
rustc --crate-type=staticlib -Clinker-plugin-lto -Copt-level=2 ./lib.rs
# Compile the C code with `-flto=thin`
clang -c -O2 -flto=thin -o main.o ./main.c
# Link everything, making sure that we use an appropriate linker
clang -flto=thin -fuse-ld=lld -L . -l"name-of-your-rust-lib" -o main -O2 ./cmain.o

Using cargo:

# Compile the Rust staticlib
RUSTFLAGS="-Clinker-plugin-lto" cargo build --release
# Compile the C code with `-flto=thin`
clang -c -O2 -flto=thin -o main.o ./main.c
# Link everything, making sure that we use an appropriate linker
clang -flto=thin -fuse-ld=lld -L . -l"name-of-your-rust-lib" -o main -O2 ./cmain.o

C/C++ code as a dependency in Rust

In this case the linker will be invoked by rustc. We again have to make sure that an appropriate linker is used.

Using rustc directly:

# Compile C code with `-flto`
clang ./clib.c -flto=thin -c -o ./clib.o -O2
# Create a static library from the C code
ar crus ./libxyz.a ./clib.o

# Invoke `rustc` with the additional arguments
rustc -Clinker-plugin-lto -L. -Copt-level=2 -Clinker=clang -Clink-arg=-fuse-ld=lld ./main.rs

Using cargo directly:

# Compile C code with `-flto`
clang ./clib.c -flto=thin -c -o ./clib.o -O2
# Create a static library from the C code
ar crus ./libxyz.a ./clib.o

# Set the linking arguments via RUSTFLAGS
RUSTFLAGS="-Clinker-plugin-lto -Clinker=clang -Clink-arg=-fuse-ld=lld" cargo build --release

Explicitly specifying the linker plugin to be used by rustc

If one wants to use a linker other than LLD, the LLVM linker plugin has to be specified explicitly. Otherwise the linker cannot read the object files. The path to the plugin is passed as an argument to the -Clinker-plugin-lto option:

rustc -Clinker-plugin-lto="/path/to/LLVMgold.so" -L. -Copt-level=2 ./main.rs

Toolchain Compatibility

In order for this kind of LTO to work, the LLVM linker plugin must be able to handle the LLVM bitcode produced by both rustc and clang.

Best results are achieved by using a rustc and clang that are based on the exact same version of LLVM. One can use rustc -vV in order to view the LLVM used by a given rustc version. Note that the version number given here is only an approximation as Rust sometimes uses unstable revisions of LLVM. However, the approximation is usually reliable.

The following table shows known good combinations of toolchain versions.

Rust VersionClang Version
Rust 1.34Clang 8
Rust 1.35Clang 8
Rust 1.36Clang 8
Rust 1.37Clang 8
Rust 1.38Clang 9
Rust 1.39Clang 9
Rust 1.40Clang 9
Rust 1.41Clang 9
Rust 1.42Clang 9
Rust 1.43Clang 9
Rust 1.44Clang 9
Rust 1.45Clang 10
Rust 1.46Clang 10

Note that the compatibility policy for this feature might change in the future.

Exploit Mitigations

This chapter documents the exploit mitigations supported by the Rust compiler, and is by no means an extensive survey of the Rust programming language’s security features.

This chapter is for software engineers working with the Rust programming language, and assumes prior knowledge of the Rust programming language and its toolchain.

Introduction

The Rust programming language provides memory[1] and thread[2] safety guarantees via its ownership[3], references and borrowing[4], and slice types[5] features. However, Unsafe Rust[6] introduces unsafe blocks, unsafe functions and methods, unsafe traits, and new types that are not subject to the borrowing rules.

Parts of the Rust standard library are implemented as safe abstractions over unsafe code (and historically have been vulnerable to memory corruption[7]). Furthermore, the Rust code and documentation encourage creating safe abstractions over unsafe code. This can cause a false sense of security if unsafe code is not properly reviewed and tested.

Unsafe Rust introduces features that do not provide the same memory and thread safety guarantees. This causes programs or libraries to be susceptible to memory corruption (CWE-119)[8] and concurrency issues (CWE-557)[9]. Modern C and C++ compilers provide exploit mitigations to increase the difficulty to exploit vulnerabilities resulting from these issues. Therefore, the Rust compiler must also support these exploit mitigations in order to mitigate vulnerabilities resulting from the use of Unsafe Rust. This chapter documents these exploit mitigations and how they apply to Rust.

This chapter does not discuss the effectiveness of these exploit mitigations as they vary greatly depending on several factors besides their design and implementation, but rather describe what they do, so their effectiveness can be understood within a given context.

Exploit mitigations

This section documents the exploit mitigations applicable to the Rust compiler when building programs for the Linux operating system on the AMD64 architecture and equivalent.1

The Rust Programming Language currently has no specification. The Rust compiler (i.e., rustc) is the language reference implementation. All references to “the Rust compiler” in this chapter refer to the language reference implementation.

Table I
Summary of exploit mitigations supported by the Rust compiler when building programs for the Linux operating system on the AMD64 architecture and equivalent.

Exploit mitigation Supported and enabled by default Since
Position-independent executable Yes 0.12.0 (2014-10-09)
Integer overflow checks Yes (enabled when debug assertions are enabled, and disabled when debug assertions are disabled) 1.1.0 (2015-06-25)
Non-executable memory regions Yes 1.8.0 (2016-04-14)
Stack clashing protection Yes 1.20.0 (2017-08-31)
Read-only relocations and immediate binding Yes 1.21.0 (2017-10-12)
Heap corruption protection Yes 1.32.0 (2019-01-17) (via operating system default or specified allocator)
Stack smashing protection No
Forward-edge control flow protection No
Backward-edge control flow protection (e.g., shadow and safe stack) No

1. See https://github.com/rust-lang/rust/tree/master/compiler/rustc_target/src/spec for a list of targets and their default options.

Position-independent executable

Position-independent executable increases the difficulty of the use of code reuse exploitation techniques, such as return-oriented programming (ROP) and variants, by generating position-independent code for the executable, and instructing the dynamic linker to load it similarly to a shared object at a random load address, thus also benefiting from address-space layout randomization (ASLR). This is also referred to as “full ASLR”.

The Rust compiler supports position-independent executable, and enables it by default since version 0.12.0 (2014-10-09)[10]–[13].

$ readelf -h target/release/hello-rust | grep Type:
  Type:                              DYN (Shared object file)

Fig. 1. Checking if an executable is a position-independent executable.

An executable with an object type of ET_DYN (i.e., shared object) and not ET_EXEC (i.e., executable) is a position-independent executable (see Fig. 1).

Integer overflow checks

Integer overflow checks protects programs from undefined and unintended behavior (which may cause vulnerabilities) by checking for results of signed and unsigned integer computations that cannot be represented in their type, resulting in an overflow or wraparound.

The Rust compiler supports integer overflow checks, and enables it when debug assertions are enabled since version 1.1.0 (2015-06-25)[14]–[20].

fn main() {
    let u: u8 = 255;
    println!("u: {}", u + 1);
}

Fig. 2. hello-rust-integer program.

$ cargo run
   Compiling hello-rust-integer v0.1.0 (/home/rcvalle/hello-rust-integer)
    Finished dev [unoptimized + debuginfo] target(s) in 0.23s
     Running `target/debug/hello-rust-integer`
thread 'main' panicked at 'attempt to add with overflow', src/main.rs:3:23
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.

Fig. 3. Build and execution of hello-rust-integer with debug assertions enabled.

$ cargo run --release
   Compiling hello-rust-integer v0.1.0 (/home/rcvalle/hello-rust-integer)
    Finished release [optimized] target(s) in 0.23s
     Running `target/release/hello-rust-integer`
u: 0

Fig. 4. Build and execution of hello-rust-integer with debug assertions disabled.

Integer overflow checks are enabled when debug assertions are enabled (see Fig. 3), and disabled when debug assertions are disabled (see Fig. 4). To enable integer overflow checks independently, use the option to control integer overflow checks, scoped attributes, or explicit checking methods such as checked_add2.

It is recommended that explicit wrapping methods such as wrapping_add be used when wrapping semantics are intended, and that explicit checking and wrapping methods always be used when using Unsafe Rust.

2. See https://doc.rust-lang.org/std/primitive.u32.html for more information on the checked, overflowing, saturating, and wrapping methods (using u32 as an example).

Non-executable memory regions

Non-executable memory regions increase the difficulty of exploitation by limiting the memory regions that can be used to execute arbitrary code. Most modern processors provide support for the operating system to mark memory regions as non executable, but it was previously emulated by software, such as in grsecurity/PaX's PAGEEXEC and SEGMEXEC, on processors that did not provide support for it. This is also known as “No Execute (NX) Bit”, “Execute Disable (XD) Bit”, “Execute Never (XN) Bit”, and others.

The Rust compiler supports non-executable memory regions, and enables it by default since its initial release, version 0.1 (2012-01-20)[21], [22], but has regressed since then[23]–[25], and enforced by default since version 1.8.0 (2016-04-14)[25].

$ readelf -l target/release/hello-rust | grep -A 1 GNU_STACK
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     0x10

Fig. 5. Checking if non-executable memory regions are enabled for a given binary.

The presence of an element of type PT_GNU_STACK in the program header table with the PF_X (i.e., executable) flag unset indicates non-executable memory regions3 are enabled for a given binary (see Fig. 5). Conversely, the presence of an element of type PT_GNU_STACK in the program header table with the PF_X flag set or the absence of an element of type PT_GNU_STACK in the program header table indicates non-executable memory regions are not enabled for a given binary.

3. See the Appendix section for more information on why it affects other memory regions besides the stack.

Stack clashing protection

Stack clashing protection protects the stack from overlapping with another memory region—allowing arbitrary data in both to be overwritten using each other—by reading from the stack pages as the stack grows to cause a page fault when attempting to read from the guard page/region. This is also referred to as “stack probes” or “stack probing”.

The Rust compiler supports stack clashing protection via stack probing, and enables it by default since version 1.20.0 (2017-08-31)[26]–[29].

Screenshot of IDA Pro listing cross references to __rust_probestack in hello-rust. Fig. 6. IDA Pro listing cross references to __rust_probestack in hello-rust.

fn hello() {
    println!("Hello, world!");
}

fn main() {
    let _: [u64; 1024] = [0; 1024];
    hello();
}

Fig 7. Modified hello-rust.

Screenshot of IDA Pro listing cross references to __rust_probestack in modified hello-rust. Fig. 8. IDA Pro listing cross references to __rust_probestack in modified hello-rust.

To check if stack clashing protection is enabled for a given binary, search for cross references to __rust_probestack. The __rust_probestack is called in the prologue of functions whose stack size is larger than a page size (see Fig. 6), and can be forced for illustration purposes by modifying the hello-rust example as seen in Fig. 7 and Fig. 8.

Read-only relocations and immediate binding

Read-only relocations protect segments containing relocations and relocation information (i.e., .init_array, .fini_array, .dynamic, and .got) from being overwritten by marking these segments read only. This is also referred to as “partial RELRO”.

The Rust compiler supports read-only relocations, and enables it by default since version 1.21.0 (2017-10-12)[30], [31].

$ readelf -l target/release/hello-rust | grep GNU_RELRO
  GNU_RELRO      0x000000000002ee00 0x000000000002fe00 0x000000000002fe00

Fig. 9. Checking if read-only relocations is enabled for a given binary.

The presence of an element of type PT_GNU_RELRO in the program header table indicates read-only relocations are enabled for a given binary (see Fig. 9). Conversely, the absence of an element of type PT_GNU_RELRO in the program header table indicates read-only relocations are not enabled for a given binary.

Immediate binding protects additional segments containing relocations (i.e., .got.plt) from being overwritten by instructing the dynamic linker to perform all relocations before transferring control to the program during startup, so all segments containing relocations can be marked read only (when combined with read-only relocations). This is also referred to as “full RELRO”.

The Rust compiler supports immediate binding, and enables it by default since version 1.21.0 (2017-10-12)[30], [31].

$ readelf -d target/release/hello-rust | grep BIND_NOW
 0x000000000000001e (FLAGS)              BIND_NOW

Fig. 10. Checking if immediate binding is enabled for a given binary.

The presence of an element with the DT_BIND_NOW tag and the DF_BIND_NOW flag4 in the dynamic section indicates immediate binding is enabled for a given binary (see Fig. 10). Conversely, the absence of an element with the DT_BIND_NOW tag and the DF_BIND_NOW flag in the dynamic section indicates immediate binding is not enabled for a given binary.

The presence of both an element of type PT_GNU_RELRO in the program header table and of an element with the DT_BIND_NOW tag and the DF_BIND_NOW flag in the dynamic section indicates full RELRO is enabled for a given binary (see Fig. 9 and Fig. 10).

4. And the DF_1_NOW flag for some link editors.

Heap corruption protection

Heap corruption protection protects memory allocated dynamically by performing several checks, such as checks for corrupted links between list elements, invalid pointers, invalid sizes, double/multiple “frees” of the same memory allocated, and many corner cases of these. These checks are implementation specific, and vary per allocator.

ARM Memory Tagging Extension (MTE), when available, will provide hardware assistance for a probabilistic mitigation to detect memory safety violations by tagging memory allocations, and automatically checking that the correct tag is used on every memory access.

Rust’s default allocator has historically been jemalloc, and it has long been the cause of issues and the subject of much discussion[32]–[38]. Consequently, it has been removed as the default allocator in favor of the operating system’s standard C library default allocator5 since version 1.32.0 (2019-01-17)[39].

fn main() {
    let mut x = Box::new([0; 1024]);

    for i in 0..1026 {
        unsafe {
            let elem = x.get_unchecked_mut(i);
            *elem = 0x4141414141414141u64;
        }
    }
}

Fig. 11. hello-rust-heap program.

$ cargo run
   Compiling hello-rust-heap v0.1.0 (/home/rcvalle/hello-rust-heap)
    Finished dev [unoptimized + debuginfo] target(s) in 0.25s
     Running `target/debug/hello-rust-heap`
free(): invalid next size (normal)
Aborted

Fig. 12. Build and execution of hello-rust-heap with debug assertions enabled.

$ cargo run --release
   Compiling hello-rust-heap v0.1.0 (/home/rcvalle/hello-rust-heap)
    Finished release [optimized] target(s) in 0.25s
     Running `target/release/hello-rust-heap`
free(): invalid next size (normal)
Aborted

Fig. 13. Build and execution of hello-rust-heap with debug assertions disabled.

Heap corruption checks are being performed when using the default allocator (i.e., the GNU Allocator) as seen in Fig. 12 and Fig. 13.

5. Linux's standard C library default allocator is the GNU Allocator, which is derived from ptmalloc (pthreads malloc) by Wolfram Gloger, which in turn is derived from dlmalloc (Doug Lea malloc) by Doug Lea.

Stack smashing protection

Stack smashing protection protects programs from stack-based buffer overflows by inserting a random guard value between local variables and the saved return instruction pointer, and checking if this value has changed when returning from a function. This is also known as “Stack Protector” or “Stack Smashing Protector (SSP)”.

The Rust compiler does not support stack smashing protection. However, more comprehensive alternatives to stack smashing protection exist, such as shadow and safe stack (see backward-edge control flow protection).

Screenshot of IDA Pro listing cross references to __stack_chk_fail in hello-rust. Fig. 14. IDA Pro listing cross references to __stack_chk_fail in hello-rust.

To check if stack smashing protection is enabled for a given binary, search for cross references to __stack_chk_fail. The only cross references to __stack_chk_fail in hello-rust are from the statically-linked libbacktrace library (see Fig. 14).

Forward-edge control flow protection

Forward-edge control flow protection protects programs from having its control flow changed/hijacked by performing checks to ensure that destinations of indirect branches are one of their valid destinations in the control flow graph. The comprehensiveness of these checks vary per implementation. This is also known as “forward-edge control flow integrity (CFI)”.

Newer processors provide hardware assistance for forward-edge control flow protection, such as ARM Branch Target Identification (BTI), ARM Pointer Authentication, and Intel Indirect Branch Tracking (IBT) as part of Intel Control-flow Enforcement Technology (CET). However, ARM BTI and Intel IBT -based implementations are less comprehensive than software-based implementations such as LLVM ControlFlowIntegrity (CFI), and the commercially available grsecurity/PaX Reuse Attack Protector (RAP).

The Rust compiler does not support forward-edge control flow protection on Linux6. There is work currently ongoing to add support for the sanitizers[40], which may or may not include support for LLVM CFI.

$ readelf -s target/release/hello-rust | grep __cfi_init

Fig. 15. Checking if LLVM CFI is enabled for a given binary.

The presence of the __cfi_init symbol (and references to __cfi_check) indicates that LLVM CFI (i.e., forward-edge control flow protection) is enabled for a given binary. Conversely, the absence of the __cfi_init symbol (and references to __cfi_check) indicates that LLVM CFI is not enabled for a given binary (see Fig. 15).

6. It supports Control Flow Guard (CFG) on Windows (see https://github.com/rust-lang/rust/issues/68793).

Backward-edge control flow protection

Shadow stack protects saved return instruction pointers from being overwritten by storing a copy of them on a separate (shadow) stack, and using these copies as authoritative values when returning from functions. This is also known as “ShadowCallStack” and “Return Flow Guard”, and is considered an implementation of backward-edge control flow protection (or “backward-edge CFI”).

Safe stack protects not only the saved return instruction pointers, but also register spills and some local variables from being overwritten by storing unsafe variables, such as large arrays, on a separate (unsafe) stack, and using these unsafe variables on the separate stack instead. This is also known as “SafeStack”, and is also considered an implementation of backward-edge control flow protection.

Both shadow and safe stack are intended to be a more comprehensive alternatives to stack smashing protection as they protect the saved return instruction pointers (and other data in the case of safe stack) from arbitrary writes and non-linear out-of-bounds writes.

Newer processors provide hardware assistance for backward-edge control flow protection, such as ARM Pointer Authentication, and Intel Shadow Stack as part of Intel CET.

The Rust compiler does not support shadow or safe stack. There is work currently ongoing to add support for the sanitizers[40], which may or may not include support for safe stack7.

$ readelf -s target/release/hello-rust | grep __safestack_init

Fig. 16. Checking if LLVM SafeStack is enabled for a given binary.

The presence of the __safestack_init symbol indicates that LLVM SafeStack is enabled for a given binary. Conversely, the absence of the __safestack_init symbol indicates that LLVM SafeStack is not enabled for a given binary (see Fig. 16).

7. The shadow stack implementation for the AMD64 architecture and equivalent in LLVM was removed due to performance and security issues.

Appendix

As of the latest version of the Linux Standard Base (LSB) Core Specification, the PT_GNU_STACK program header indicates whether the stack should be executable, and the absence of this header indicates that the stack should be executable. However, the Linux kernel currently sets the READ_IMPLIES_EXEC personality upon loading any executable with the PT_GNU_STACK program header and the PF_X flag set or with the absence of this header, resulting in not only the stack, but also all readable virtual memory mappings being executable.

An attempt to fix this was made in 2012, and another was made in 2020. The former never landed, and the latter partially fixed it, but introduced other issues—the absence of the PT_GNU_STACK program header still causes not only the stack, but also all readable virtual memory mappings to be executable in some architectures, such as IA-32 and equivalent (or causes the stack to be non-executable in some architectures, such as AMD64 and equivalent, contradicting the LSB).

The READ_IMPLIES_EXEC personality needs to be completely separated from the PT_GNU_STACK program header by having a separate option for it (or setarch -X could just be used whenever READ_IMPLIES_EXEC is needed), and the absence of the PT_GNU_STACK program header needs to have more secure defaults (unrelated to READ_IMPLIES_EXEC).

References

  1. D. Hosfelt. “Fearless security: memory safety.” Mozilla Hacks. https://hacks.mozilla.org/2019/01/fearless-security-memory-safety/.

  2. D. Hosfelt. “Fearless security: thread safety.” Mozilla Hacks. https://hacks.mozilla.org/2019/02/fearless-security-thread-safety/.

  3. S. Klabnik and C. Nichols. “What Is Ownership?.” The Rust Programming Language. https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html.

  4. S. Klabnik and C. Nichols. “References and Borrowing.” The Rust Programming Language. https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html.

  5. S. Klabnik and C. Nichols. “The Slice Type.” The Rust Programming Language. https://doc.rust-lang.org/book/ch04-03-slices.html.

  6. S. Klabnik and C. Nichols. “Unsafe Rust.” The Rust Programming Language. https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html.

  7. S. Davidoff. “How Rust’s standard library was vulnerable for years and nobody noticed.” Medium. https://medium.com/@shnatsel/how-rusts-standard-library-was-vulnerable-for-years-and-nobody-noticed-aebf0503c3d6.

  8. “Improper restriction of operations within the bounds of a memory buffer (CWE-119).” MITRE CWE List. https://cwe.mitre.org/data/definitions/119.html.

  9. “Concurrency issues (CWE-557).” MITRE CWE List. https://cwe.mitre.org/data/definitions/557.html.

  10. K. McAllister. “Memory exploit mitigations #15179.” GitHub. https://github.com/rust-lang/rust/issues/15179.

  11. K. McAllister. “RFC: Memory exploit mitigation #145.” GitHub. https://github.com/rust-lang/rfcs/pull/145.

  12. K. McAllister. “RFC: Memory exploit mitigation.” GitHub. https://github.com/kmcallister/rfcs/blob/hardening/active/0000-memory-exploit-mitigation.md.

  13. D. Micay. “Enable PIE by default on Linux for full ASLR #16340.” GitHub. https://github.com/rust-lang/rust/pull/16340.

  14. N. Matsakis. “Integer overflow #560.” GitHub. https://github.com/rust-lang/rfcs/pull/560.

  15. G. Lehel and N. Matsakis. “Integer overflow.” GitHub. https://rust-lang.github.io/rfcs/0560-integer-overflow.html.

  16. A. Turon. “Tracking issue for integer overflow (RFC 560) #22020.” GitHub. https://github.com/rust-lang/rust/issues/22020.

  17. H. Wilson. “Myths and legends about integer overflow in Rust.” Huon on the Internet. http://huonw.github.io/blog/2016/04/myths-and-legends-about-integer-overflow-in-rust/.

  18. B. Anderson. “Stabilize -C overflow-checks #1535.” GitHub. https://github.com/rust-lang/rfcs/pull/1535.

  19. B. Anderson. “Stable overflow checks.” GitHub. https://github.com/brson/rfcs/blob/overflow/text/0000-stable-overflow-checks.md.

  20. N. Froyd. “Add -C overflow-checks option #40037.” GitHub. https://github.com/rust-lang/rust/pull/40037.

  21. R. Á. de Espíndola. “rustc requires executable stack #798.” GitHub. https://github.com/rust-lang/rust/issues/798.

  22. A. Seipp. “Make sure librustrt.so is linked with a non-executable stack. #1066.” GitHub. https://github.com/rust-lang/rust/pull/1066.

  23. D. Micay. “Rust binaries should not have an executable stack #5643.” GitHub. https://github.com/rust-lang/rust/issues/5643.

  24. D. Micay. “Mark the assembly object stacks as non-executable #5647.” GitHub. https://github.com/rust-lang/rust/pull/5647.

  25. A. Clark. “Explicitly disable stack execution on linux and bsd #30859.” GitHub. https://github.com/rust-lang/rust/pull/30859.

  26. “Replace stack overflow checking with stack probes #16012.” GitHub. https://github.com/rust-lang/rust/issues/16012.

  27. B. Striegel. “Extend stack probe support to non-tier-1 platforms, and clarify policy for mitigating LLVM-dependent unsafety #43241.” GitHub. https://github.com/rust-lang/rust/issues/43241.

  28. A. Crichton. “rustc: Implement stack probes for x86 #42816.” GitHub. https://github.com/rust-lang/rust/pull/42816.

  29. A. Crichton. “Add __rust_probestack intrinsic #175.” GitHub. https://github.com/rust-lang/compiler-builtins/pull/175.

  30. B. Anderson. “Consider applying -Wl,-z,relro or -Wl,-z,relro,-z,now by default #29877.” GitHub. https://github.com/rust-lang/rust/issues/29877.

  31. J. Löthberg. “Add support for full RELRO #43170.” GitHub. https://github.com/rust-lang/rust/pull/43170.

  32. N. Matsakis. “Allocators in Rust.” Baby Steps. http://smallcultfollowing.com/babysteps/blog/2014/11/14/allocators-in-rust/.

  33. A. Crichton. “RFC: Allow changing the default allocator #1183.” GitHub. https://github.com/rust-lang/rfcs/pull/1183.

  34. A. Crichton. “RFC: Swap out jemalloc.” GitHub. https://rust-lang.github.io/rfcs/1183-swap-out-jemalloc.html.

  35. A. Crichton. “Tracking issue for changing the global, default allocator (RFC 1974) #27389.” GitHub. https://github.com/rust-lang/rust/issues/27389.

  36. S. Fackler. “Prepare global allocators for stabilization #1974.” GitHub. https://github.com/rust-lang/rfcs/pull/1974.

  37. A. Crichton. “RFC: Global allocators.” GitHub. https://rust-lang.github.io/rfcs/1974-global-allocators.html.

  38. B. Anderson. “Switch the default global allocator to System, remove alloc_jemalloc, use jemallocator in rustc #36963.” GitHub. https://github.com/rust-lang/rust/issues/36963.

  39. A. Crichton. “Remove the alloc_jemalloc crate #55238.” GitHub. https://github.com/rust-lang/rust/pull/55238.

  40. J. Aparicio. 2017. “Tracking issue for sanitizer support #39699.” https://github.com/rust-lang/rust/issues/39699.

Contributing to rustc

We'd love to have your help improving rustc! To that end, we've written a whole book on its internals, how it works, and how to get started working on it. To learn more, you'll want to check that out.

If you would like to contribute to this book, you can find its source in the rustc source at src/doc/rustc.