-
-
Notifications
You must be signed in to change notification settings - Fork 14.9k
CFI: Fix LTO for #![no_builtins] crates with CFI
#156024
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
Open
rcvalle
wants to merge
1
commit into
rust-lang:main
Choose a base branch
from
rcvalle:rust-cfi-fix-142284
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
10 changes: 10 additions & 0 deletions
10
tests/run-make-cargo/sanitizer-cfi-build-std-clang/Cargo.toml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| # Workspace mirroring the examples in <https://github.com/rcvalle/rust-cfi-examples>. | ||
| [workspace] | ||
| resolver = "2" | ||
| members = [ | ||
| "invalid-branch-target-abort", | ||
| "indirect-arity-mismatch-abort", | ||
| "indirect-type-mismatch-abort", | ||
| "cross-lang-cfi-types-crate-abort", | ||
| "cross-lang-cfi-types-crate-not-abort", | ||
| ] |
8 changes: 8 additions & 0 deletions
8
.../run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-abort/Cargo.toml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| [package] | ||
| name = "cross-lang-cfi-types-crate-abort" | ||
| version = "0.1.0" | ||
| edition = "2021" | ||
| build = "build.rs" | ||
|
|
||
| [dependencies] | ||
| cfi-types = "0.0.5" |
61 changes: 61 additions & 0 deletions
61
tests/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-abort/build.rs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| use std::env; | ||
| use std::path::{Path, PathBuf}; | ||
| use std::process::Command; | ||
|
|
||
| fn llvm_ar_path() -> PathBuf { | ||
| if let Ok(d) = env::var("LLVM_BIN_DIR") { | ||
| let llvm_ar = Path::new(d.trim_end_matches('/')).join("llvm-ar"); | ||
| if llvm_ar.exists() { | ||
| return llvm_ar; | ||
| } | ||
| } | ||
| if let Ok(clang) = env::var("CLANG") { | ||
| let clang = Path::new(&clang); | ||
| if let Some(clang_dir) = clang.parent() { | ||
| let llvm_ar = clang_dir.join("llvm-ar"); | ||
| if llvm_ar.exists() { | ||
| return llvm_ar; | ||
| } | ||
| } | ||
| } | ||
| PathBuf::from("llvm-ar") | ||
| } | ||
|
|
||
| fn main() { | ||
| let out_dir = env::var("OUT_DIR").expect("OUT_DIR"); | ||
| let manifest_dir = env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR"); | ||
| let c_src = Path::new(&manifest_dir).join("src/foo.c"); | ||
| let bc_path = Path::new(&out_dir).join("foo.bc"); | ||
| let a_path = Path::new(&out_dir).join("libfoo.a"); | ||
|
|
||
| let clang = | ||
| env::var("CC").or_else(|_| env::var("CLANG")).unwrap_or_else(|_| "clang".to_string()); | ||
| let llvm_ar = llvm_ar_path(); | ||
|
|
||
| let st = Command::new(&clang) | ||
| .args([ | ||
| "-Wall", | ||
| "-flto=thin", | ||
| "-fsanitize=cfi", | ||
| "-fvisibility=hidden", | ||
| "-c", | ||
| "-emit-llvm", | ||
| "-o", | ||
| ]) | ||
| .arg(&bc_path) | ||
| .arg(&c_src) | ||
| .status() | ||
| .unwrap_or_else(|e| panic!("failed to spawn `{clang}`: {e}")); | ||
| assert!(st.success(), "`{clang}` failed with {st}"); | ||
|
|
||
| let st = Command::new(&llvm_ar) | ||
| .args(["rcs", a_path.to_str().unwrap(), bc_path.to_str().unwrap()]) | ||
| .status() | ||
| .unwrap_or_else(|e| panic!("failed to spawn `{}`: {e}", llvm_ar.display())); | ||
| assert!(st.success(), "`{}` failed with {st}", llvm_ar.display()); | ||
|
|
||
| println!("cargo:rustc-link-search=native={out_dir}"); | ||
| println!("cargo:rustc-link-lib=static=foo"); | ||
| println!("cargo:rerun-if-changed={}", c_src.display()); | ||
| println!("cargo:rerun-if-changed=build.rs"); | ||
| } |
5 changes: 5 additions & 0 deletions
5
...s/run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-abort/src/foo.c
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| int | ||
| do_twice(int (*fn)(int), int arg) | ||
| { | ||
| return fn(arg) + fn(arg); | ||
| } |
36 changes: 36 additions & 0 deletions
36
...run-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-abort/src/main.rs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| // This example demonstrates redirecting control flow using an indirect | ||
| // branch/call to a function with different return and parameter types than the | ||
| // return type expected and arguments intended/passed at the call/branch site, | ||
| // across the FFI boundary using the `cfi_types` crate for cross-language LLVM | ||
| // CFI. | ||
|
|
||
| use std::mem; | ||
|
|
||
| use cfi_types::{c_int, c_long}; | ||
|
|
||
| #[link(name = "foo")] | ||
| unsafe extern "C" { | ||
| fn do_twice(f: unsafe extern "C" fn(c_int) -> c_int, arg: i32) -> i32; | ||
| } | ||
|
|
||
| unsafe extern "C" fn add_one(x: c_int) -> c_int { | ||
| c_int(x.0 + 1) | ||
| } | ||
|
|
||
| unsafe extern "C" fn add_two(x: c_long) -> c_long { | ||
| c_long(x.0 + 2) | ||
| } | ||
|
|
||
| fn main() { | ||
| let answer = unsafe { do_twice(add_one, 5) }; | ||
|
|
||
| println!("The answer is: {}", answer); | ||
|
|
||
| println!("With CFI enabled, you should not see the next answer"); | ||
| let f: unsafe extern "C" fn(c_int) -> c_int = unsafe { | ||
| mem::transmute::<*const u8, unsafe extern "C" fn(c_int) -> c_int>(add_two as *const u8) | ||
| }; | ||
| let next_answer = unsafe { do_twice(f, 5) }; | ||
|
|
||
| println!("The next answer is: {}", next_answer); | ||
| } |
8 changes: 8 additions & 0 deletions
8
...-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-not-abort/Cargo.toml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| [package] | ||
| name = "cross-lang-cfi-types-crate-not-abort" | ||
| version = "0.1.0" | ||
| edition = "2021" | ||
| build = "build.rs" | ||
|
|
||
| [dependencies] | ||
| cfi-types = "0.0.5" |
61 changes: 61 additions & 0 deletions
61
...un-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-not-abort/build.rs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| use std::env; | ||
| use std::path::{Path, PathBuf}; | ||
| use std::process::Command; | ||
|
|
||
| fn llvm_ar_path() -> PathBuf { | ||
| if let Ok(d) = env::var("LLVM_BIN_DIR") { | ||
| let llvm_ar = Path::new(d.trim_end_matches('/')).join("llvm-ar"); | ||
| if llvm_ar.exists() { | ||
| return llvm_ar; | ||
| } | ||
| } | ||
| if let Ok(clang) = env::var("CLANG") { | ||
| let clang = Path::new(&clang); | ||
| if let Some(clang_dir) = clang.parent() { | ||
| let llvm_ar = clang_dir.join("llvm-ar"); | ||
| if llvm_ar.exists() { | ||
| return llvm_ar; | ||
| } | ||
| } | ||
| } | ||
| PathBuf::from("llvm-ar") | ||
| } | ||
|
|
||
| fn main() { | ||
| let out_dir = env::var("OUT_DIR").expect("OUT_DIR"); | ||
| let manifest_dir = env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR"); | ||
| let c_src = Path::new(&manifest_dir).join("src/foo.c"); | ||
| let bc_path = Path::new(&out_dir).join("foo.bc"); | ||
| let a_path = Path::new(&out_dir).join("libfoo.a"); | ||
|
|
||
| let clang = | ||
| env::var("CC").or_else(|_| env::var("CLANG")).unwrap_or_else(|_| "clang".to_string()); | ||
| let llvm_ar = llvm_ar_path(); | ||
|
|
||
| let st = Command::new(&clang) | ||
| .args([ | ||
| "-Wall", | ||
| "-flto=thin", | ||
| "-fsanitize=cfi", | ||
| "-fvisibility=hidden", | ||
| "-c", | ||
| "-emit-llvm", | ||
| "-o", | ||
| ]) | ||
| .arg(&bc_path) | ||
| .arg(&c_src) | ||
| .status() | ||
| .unwrap_or_else(|e| panic!("failed to spawn `{clang}`: {e}")); | ||
| assert!(st.success(), "`{clang}` failed with {st}"); | ||
|
|
||
| let st = Command::new(&llvm_ar) | ||
| .args(["rcs", a_path.to_str().unwrap(), bc_path.to_str().unwrap()]) | ||
| .status() | ||
| .unwrap_or_else(|e| panic!("failed to spawn `{}`: {e}", llvm_ar.display())); | ||
| assert!(st.success(), "`{}` failed with {st}", llvm_ar.display()); | ||
|
|
||
| println!("cargo:rustc-link-search=native={out_dir}"); | ||
| println!("cargo:rustc-link-lib=static=foo"); | ||
| println!("cargo:rerun-if-changed={}", c_src.display()); | ||
| println!("cargo:rerun-if-changed=build.rs"); | ||
| } |
23 changes: 23 additions & 0 deletions
23
...n-make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-not-abort/src/foo.c
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| #include <stdio.h> | ||
| #include <stdlib.h> | ||
|
|
||
| // This definition has the type id "_ZTSFvlE". | ||
| void | ||
| hello_from_c(long arg) | ||
| { | ||
| printf("Hello from C!\n"); | ||
| } | ||
|
|
||
| // This definition has the type id "_ZTSFvPFvlElE"--this can be ignored for the | ||
| // purposes of this example. | ||
| void | ||
| indirect_call_from_c(void (*fn)(long), long arg) | ||
| { | ||
| // This call site tests whether the destination pointer is a member of the | ||
| // group derived from the same type id of the fn declaration, which has the | ||
| // type id "_ZTSFvlE". | ||
| // | ||
| // Notice that since the test is at the call site and generated by Clang, | ||
| // the type id used in the test is encoded by Clang. | ||
| fn(arg); | ||
| } |
67 changes: 67 additions & 0 deletions
67
...make-cargo/sanitizer-cfi-build-std-clang/cross-lang-cfi-types-crate-not-abort/src/main.rs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| use cfi_types::c_long; | ||
|
|
||
| #[link(name = "foo")] | ||
| extern "C" { | ||
| // This declaration has the type id "_ZTSFvlE" because it uses the CFI types | ||
| // for cross-language LLVM CFI support. The cfi_types crate provides a new | ||
| // set of C types as user-defined types using the cfi_encoding attribute and | ||
| // repr(transparent) to be used for cross-language LLVM CFI support. This | ||
| // new set of C types allows the Rust compiler to identify and correctly | ||
| // encode C types in extern "C" function types indirectly called across the | ||
| // FFI boundary when CFI is enabled. | ||
| fn hello_from_c(_: c_long); | ||
|
|
||
| // This declaration has the type id "_ZTSFvPFvlElE" because it uses the CFI | ||
| // types for cross-language LLVM CFI support--this can be ignored for the | ||
| // purposes of this example. | ||
| fn indirect_call_from_c(f: unsafe extern "C" fn(c_long), arg: c_long); | ||
| } | ||
|
|
||
| // This definition has the type id "_ZTSFvlE" because it uses the CFI types for | ||
| // cross-language LLVM CFI support, similarly to the hello_from_c declaration | ||
| // above. | ||
| unsafe extern "C" fn hello_from_rust(_: c_long) { | ||
| println!("Hello, world!"); | ||
| } | ||
|
|
||
| // This definition has the type id "_ZTSFvlE" because it uses the CFI types for | ||
| // cross-language LLVM CFI support, similarly to the hello_from_c declaration | ||
| // above. | ||
| unsafe extern "C" fn hello_from_rust_again(_: c_long) { | ||
| println!("Hello from Rust again!"); | ||
| } | ||
|
|
||
| // This definition would also have the type id "_ZTSFvPFvlElE" because it uses | ||
| // the CFI types for cross-language LLVM CFI support, similarly to the | ||
| // hello_from_c declaration above--this can be ignored for the purposes of this | ||
| // example. | ||
| fn indirect_call(f: unsafe extern "C" fn(c_long), arg: c_long) { | ||
| // This indirect call site tests whether the destination pointer is a member | ||
| // of the group derived from the same type id of the f declaration, which | ||
| // has the type id "_ZTSFvlE" because it uses the CFI types for | ||
| // cross-language LLVM CFI support, similarly to the hello_from_c | ||
| // declaration above. | ||
| unsafe { f(arg) } | ||
| } | ||
|
|
||
| // This definition has the type id "_ZTSFvvE"--this can be ignored for the | ||
| // purposes of this example. | ||
| fn main() { | ||
| // This demonstrates an indirect call within Rust-only code using the same | ||
| // encoding for hello_from_rust and the test at the indirect call site at | ||
| // indirect_call (i.e., "_ZTSFvlE"). | ||
| indirect_call(hello_from_rust, c_long(5)); | ||
|
|
||
| // This demonstrates an indirect call across the FFI boundary with the Rust | ||
| // compiler and Clang using the same encoding for hello_from_c and the test | ||
| // at the indirect call site at indirect_call (i.e., "_ZTSFvlE"). | ||
| indirect_call(hello_from_c, c_long(5)); | ||
|
|
||
| // This demonstrates an indirect call to a function passed as a callback | ||
| // across the FFI boundary with the Rust compiler and Clang the same | ||
| // encoding for the passed-callback declaration and the test at the indirect | ||
| // call site at indirect_call_from_c (i.e., "_ZTSFvlE"). | ||
| unsafe { | ||
| indirect_call_from_c(hello_from_rust_again, c_long(5)); | ||
| } | ||
| } |
11 changes: 11 additions & 0 deletions
11
...ake-cargo/sanitizer-cfi-build-std-clang/cross-lang-integer-normalization-abort/Cargo.toml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| [package] | ||
| name = "cross-lang-integer-normalization-abort" | ||
| version = "0.1.0" | ||
| edition = "2021" | ||
| build = "build.rs" | ||
|
|
||
| # Not a member of the parent `sanitizer-cfi-build-std-clang` workspace so it can | ||
| # be built with different `RUSTFLAGS` (i.e., integer normalization). | ||
| [workspace] | ||
| members = ["."] | ||
| resolver = "2" |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason we don't use LTO for compiler-builtins is that doing so breaks the weak linkage that compiler-builtins uses. We use a legacy interface for implementing LTO that treats weak and strong symbols identically. We tried to make compiler-builtins participate in LTO before in #113923, but this resulted in the regression #118609.
In any case just emitting bitcode is not enough. You also need to updated
ignored_for_lto:rust/compiler/rustc_codegen_ssa/src/back/link.rs
Lines 1406 to 1422 in 0469a92
View changes since the review
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change only makes it participate on LTO when using both
-Clinker-plugin-lto(not rustc LTO) and-Zsanitizer=cfi, which expects aclangorld.lldlinker already and thus doesn't cause the regressions listed on #146133, with the regression tests added by @dianqk now passing. However, I see that the regressions you mention are different ones. Are there any regression tests for them I can verify?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cargo uses
-Clinker-plugin-ltowhen compiling rlib crates even when not doing linker plugin LTO to skip unnecessary codegen for them.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a bit confused about what is the issue you're referring to.
ignored_for_ltois for rustc LTO (not proper LTO). The #142284 issue is when using both-Clinker-plugin-lto(proper LTO) and-Zsanitizer=cfi, where LTO is deferred to the linker; the fix needed was to ensure those crates emit LLVM bitcode so proper LTO can run theLowerTypeTestspass and lowerllvm.type.testintrinsics and related metadata. (Note the issue #142284 doesn't happen with rust LTO: #142284 (comment) because these are properly lowered already.) Would you mind providing more details? Maybe a regression test or a reproducer?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking wrong in my earlier comments. I however did just remember that you tried to land pretty much the same PR previously as #145368, which then had to be reverted in #146133 due to regressions. Has anything changed since then to fix those regressions even when compiling compiler-builtins with LTO enabled?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, in my previous attempt, I changed it to emit LLVM bitcode for whenever
-Clinker-plugin-ltowas used so when people used it with older non-LLVM (system) linkers, these didn't work with the LLVM bitcode. In this attempt, I changed it to emit LLVM bitcode only when CFI is enabled, which already requires a newer LLVM linker that works with the LLVM bitcode and wouldn't work with older non-LLVM (system) linkers anyway.What I was unsure is whether there could be any case where emitting LLVM bitcode for
compiler_builtins(even only when CFI is enabled) could fail for some other reason such as #118609 that you mentioned, but since it's currently blocking the use of CFI with-Zbuild-std, and I haven't seen any issues so far, and don't have a regression test or a reproducer for it, I'd rather unblock it and fix forward any issues that might appear.But before merging it, I still want to add a couple more tests and will let you know when it's ready.