Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rustc-book: Document -C target-feature=+crt-static #71586

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 74 additions & 9 deletions src/doc/rustc/src/codegen-options/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -402,12 +402,35 @@ machine. Each target has a default base CPU.

## 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. Separate multiple features with commas.
This option controls either CPU instruction set extensions that can be used during code generation,
or static linking.

To see the valid options and an example of use, run `rustc --print
target-features`.
In both cases `-C target-feature=+x` enables feature `x` and `-C target-feature=-x` disables it.

Multiple features can be specified by separating them with commas - `-C target-feature=+x,-y`.

For each supported feature `x` `target_feature = "x"` becomes a `cfg` predicate usable early during
compilation.
Code configured with `cfg(target_feature = "x")` is kept if the feature `x` is enabled,
and removed otherwise.

If the enabled feature is not supported for the current target, the behavior is unspecified.
<!-- There are multiple bugs here
- Unknown CPU features go straight to LLVM and appear as LLVM warnings.
- At configure time `crt-static` predicate is set even if the target doesn't support `crt-static`.
- At link time `crt-static` is sometimes ignored
if the target doesn't support it and sometimes not.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, I strongly suspect that #69519 was a hack rather than a proper solution, but I need some time to prepare a musl setup and verify things. (Proc-macros shouldn't be any different to cdylibs and dylibs with regards to linking behavior.)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Proc-macros shouldn't be any different to cdylibs and dylibs with regards to linking behavior

That was the problem 😉

TL;DR It's a hack.

Musl target is a "weirdo". Because of the legacy decisions it defaults to static linking so it cannot produce dylibs, cdylibs and proc-macros. Now lack of proc-macro support doesn't matter when cross-compiling from the host which supports them to musl.
It becomes difficult when using musl as the host toolchain. Since it defaults to static linking proc-macros don't work. To build them one had to use target-feature=-crt-static and lose ability to statically link binaries.

Copy link
Contributor Author

@petrochenkov petrochenkov Apr 27, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mati865
First, musl toolchain actually supports statically linking self-contained .sos, despite the flag in rustc target spec saying otherwise, I tried it (at least with "hello world"-like examples).

Second, that doesn't mean that we want to use it for proc macros (as well as cdylibs and dylibs) at the same time as we are building a statically linked executable.

So, one common RUSTFLAGS="-Ctarget-feature=+crt-static" (or a target spec default) in this case brings collateral damage on the crates which it is not supposed to apply to.

I see multiple possible solutions here some of which could be more principled than what #69519 did.

  • Automatically enable -Ctarget-feature=-crt-static for proc macro crates or all libraries in Cargo, modifying RUSTFLAGS. Question: how to opt-out?
  • Introduce a new options -Ctarget-feature=+crt-static-dylib controlling static linking of libraries instead of -Ctarget-feature=-crt-static. It would almost never be used on Linux (not sure about windows-msvc and wasm).
  • Keep the existing meaning of crt-static, but introduce a new option -C disable-crt-static-for-dylibs or something. Cargo would then use it for proc macro crates or all libraries. Question: how to opt-out?
  • Ignore +crt-static for dylibs if the target doesn't support it. This is fragile, musl actually supports it despite the current value of the flag of the musl target spec. If the flag is enabled, proc macros will break.
  • Ignore +crt-static for dylibs always. They are almost never used on Linux (not sure about windows-msvc and wasm). +crt-static-dylib looks strictly better.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Have two crt-static defaults in target specs - one for executables and one for libraries. Solves one half of the problem, but explicit +crt-static in RUSTFLAGS will still cause collateral damage.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll turn this into a proper issue.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-->

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

#### CPU instruction set extensions

Target features for CPU instruction set extensions can enable or disable
architecture-dependent CPU features, like `+neon` or `-sse`.

Each target and [`target-cpu`](#target-cpu) has a default set of enabled
features.

Using this flag is unsafe and might result in [undefined runtime
behavior](../targets/known-issues.md).
Expand All @@ -416,11 +439,53 @@ See also the [`target_feature`
attribute](../../reference/attributes/codegen.md#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](../../reference/linkage.html#static-and-dynamic-c-runtimes).
#### Static linking

Each target and [`target-cpu`](#target-cpu) has a default set of enabled
features.
Target feature `crt-static` can be used to enable or disable static linking,
with the default depending on the current target.
`-C target-feature=+crt-static` is similar to `-static` in other compilers,
standard C library in particular is linked statically in this case.

The exact meaning of `+crt-static` depends heavily
on the current target and the produced crate type.

- ELF-based executables.
If the target supports statically linked executables,
then the linker will be instructed (`-static`) to produce one.
The executable will be self-contained,
will contain code from all the used libraries, including `libc`, inside it,
and will be able to run without a dynamic loader (no `INTERP` header).

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.

- ELF-based shared libraries.
If the target supports statically linked shared libraries,
then the linker will be instructed (`-shared -static`) to produce one.
The shared library will contain code from all the used libraries, including `libc`, inside it.
This option is not widely supported or used.

- PE-based executables (Windows).
The produced executable will contain code from all the user-level libraries, including
[C Run-Time Libraries (CRT)](https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-library-features),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What user-level libraries will it statically link to other than the CRT? For windows-msvc crt-static only controls whether the CRT (consisting of the VC Runtime and the UCRT) is statically linked and nothing else is affected. Also it only applies to windows-msvc while windows-gnu does not support crt-static.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@retep998

What user-level libraries will it statically link to other than the CRT?

Rust crates.
And it should do that for native dependencies as well (#71647), but that doesn't make any difference on Windows since there's no libmylib.a/libmylib.so separation.

If after that anything is still linked dynamically, then we get into the *WARNING!* clause below. It's not as dangerous in the Windows case though, since the goal there is not to produce a fully statically linked executable, like in the ELF case.

Also it only applies to windows-msvc while windows-gnu does not support crt-static.

Good catch.
This paragraph needs the same "If the target supports..." condition as the ELF paragraphs above.

Copy link
Member

@retep998 retep998 Apr 28, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To me crt-static on Windows always had the impression that the only thing it controlled (and ever intended to control) was whether it linked to the static or dynamic CRT. I was not aware that it had any effect on whether a rust crate was linked statically or dynamically, as by default it will link rust crates statically unless you specify -Cprefer-dynamic.

but may still link dynamically to system libraries like `kernel32.dll`.

- PE-based shared libraries (Windows).
The produced shared library will contain code from all the user-level libraries, including
[C Run-Time Libraries (CRT)](https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-library-features),
but may still link dynamically to system libraries like `kernel32.dll`.

*WARNING!* If some libraries (native or Rust crates) in the dependency tree exist only in the
dynamic variant they may be linked dynamically breaking the self-contained-ness property,
re-adding the `INTERP` header for ELF executables, and possibly working incorrectly due to mismatch
between dynamically linked libraries and injected startup objects specific to static linking.
A request to turn this case into an error is tracked in
[issue #39998](https://github.com/rust-lang/rust/issues/39998).

An alternative description of this feature can be found in the
[reference](../../reference/linkage.html#static-and-dynamic-c-runtimes).

## bitcode-in-rlib

Expand Down
1 change: 1 addition & 0 deletions src/librustc_interface/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ pub fn add_configuration(

cfg.extend(codegen_backend.target_features(sess).into_iter().map(|feat| (tf, Some(feat))));

// FIXME: The predicate is currently set even if the target doesn't support `crt-static`.
if sess.crt_static_feature(None) {
cfg.insert((tf, Some(Symbol::intern("crt-static"))));
}
Expand Down