Skip to content

Commit aa48eed

Browse files
authored
Merge pull request #203 from rust-secure-code/bare-linker-support
Bare linker support
2 parents 5e2a1ed + b3a862d commit aa48eed

File tree

3 files changed

+66
-13
lines changed

3 files changed

+66
-13
lines changed

cargo-auditable/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ auditable-serde = {version = "0.8.0", path = "../auditable-serde"}
1818
miniz_oxide = {version = "0.8.0"}
1919
serde_json = "1.0.57"
2020
cargo_metadata = "0.18"
21-
pico-args = { version = "0.5", features = ["eq-separator"] }
21+
pico-args = { version = "0.5", features = ["eq-separator", "short-space-opt"] }
2222
serde = "1.0.147"
2323
wasm-gen = "0.1.4"
2424

cargo-auditable/src/rustc_arguments.rs

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ pub struct RustcArgs {
2121
pub out_dir: Option<PathBuf>,
2222
pub target: Option<String>,
2323
pub print: Vec<String>,
24+
pub codegen: Vec<String>,
2425
}
2526

2627
impl RustcArgs {
@@ -35,6 +36,29 @@ impl RustcArgs {
3536
}
3637
result
3738
}
39+
40+
/// Normally `rustc` uses a C compiler such as `cc` or `clang` as linker,
41+
/// and arguments to the actual linker need to be passed prefixed with `-Wl,`.
42+
/// But it is possible to configure Cargo and rustc to call a linker directly,
43+
/// and the breakage it causes is subtle enough that people just roll with it
44+
/// and complain when cargo-auditable doesn't support this configuration:
45+
/// <https://github.com/rust-secure-code/cargo-auditable/issues/202>
46+
///
47+
/// This function can tell you if a bare linker is in use
48+
/// and whether you need to prepend `-Wl,` or not.
49+
///
50+
/// Such setups are exceptionally rare and frankly it's a misconfiguration
51+
/// that will break more than just `cargo auditable`, but I am feeling generous.
52+
pub fn bare_linker(&self) -> bool {
53+
let linker_flag = self.codegen.iter().find(|s| s.starts_with("linker="));
54+
if let Some(linker_flag) = linker_flag {
55+
let linker = linker_flag.strip_prefix("linker=").unwrap();
56+
if linker.ends_with("ld") {
57+
return true;
58+
}
59+
}
60+
false
61+
}
3862
}
3963

4064
impl RustcArgs {
@@ -62,6 +86,7 @@ impl RustcArgs {
6286
})?,
6387
target: parser.opt_value_from_str("--target")?,
6488
print: parser.values_from_str("--print")?,
89+
codegen: parser.values_from_str("-C")?,
6590
})
6691
}
6792
}
@@ -147,16 +172,7 @@ mod tests {
147172

148173
#[test]
149174
fn multiple_emit_values() {
150-
let raw_rustc_args = vec![
151-
"--emit=dep-info,link",
152-
"--emit",
153-
"llvm-bc",
154-
// end of interesting args, start of boilerplate
155-
"--crate-name",
156-
"foobar",
157-
"--out-dir",
158-
"/foo/bar",
159-
];
175+
let raw_rustc_args = vec!["--emit=dep-info,link", "--emit", "llvm-bc"];
160176
let raw_rustc_args: Vec<OsString> = raw_rustc_args.into_iter().map(|s| s.into()).collect();
161177
let mut args = RustcArgs::from_vec(raw_rustc_args).unwrap();
162178

@@ -168,4 +184,29 @@ mod tests {
168184

169185
assert_eq!(args.emit, expected)
170186
}
187+
188+
#[test]
189+
fn detect_bare_linker() {
190+
let raw_rustc_args = vec!["-C", "linker=rust-lld"];
191+
let raw_rustc_args: Vec<OsString> = raw_rustc_args.into_iter().map(|s| s.into()).collect();
192+
let args = RustcArgs::from_vec(raw_rustc_args).unwrap();
193+
assert!(args.bare_linker());
194+
}
195+
196+
#[test]
197+
fn multiple_codegen_options() {
198+
let raw_rustc_args = vec!["-Clinker=clang", "-C", "link-arg=-fuse-ld=/usr/bin/mold"];
199+
let raw_rustc_args: Vec<OsString> = raw_rustc_args.into_iter().map(|s| s.into()).collect();
200+
let mut args = RustcArgs::from_vec(raw_rustc_args).unwrap();
201+
202+
let expected = vec!["linker=clang", "link-arg=-fuse-ld=/usr/bin/mold"];
203+
let mut expected: Vec<String> = expected.into_iter().map(|s| s.into()).collect();
204+
205+
args.codegen.sort();
206+
expected.sort();
207+
208+
assert_eq!(args.codegen, expected);
209+
210+
assert!(!args.bare_linker());
211+
}
171212
}

cargo-auditable/src/rustc_wrapper.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,12 @@ pub fn main(rustc_path: &OsStr) {
4343
let filename = format!(
4444
"{}_audit_data.o",
4545
args.crate_name
46+
.as_ref()
4647
.expect("rustc command is missing --crate-name")
4748
);
4849
let path = args
4950
.out_dir
51+
.as_ref()
5052
.expect("rustc command is missing --out-dir")
5153
.join(filename);
5254
std::fs::write(&path, file).expect("Unable to write output file");
@@ -57,13 +59,23 @@ pub fn main(rustc_path: &OsStr) {
5759
command.arg(linker_command);
5860
// Prevent the symbol from being removed as unused by the linker
5961
if is_apple(&target_info) {
60-
command.arg("-Clink-arg=-Wl,-u,_AUDITABLE_VERSION_INFO");
62+
if args.bare_linker() {
63+
command.arg("-Clink-arg=-u,_AUDITABLE_VERSION_INFO");
64+
} else {
65+
command.arg("-Clink-arg=-Wl,-u,_AUDITABLE_VERSION_INFO");
66+
}
6167
} else if is_msvc(&target_info) {
6268
command.arg("-Clink-arg=/INCLUDE:AUDITABLE_VERSION_INFO");
6369
} else if is_wasm(&target_info) {
6470
// We don't emit the symbol name in WASM, so nothing to do
6571
} else {
66-
command.arg("-Clink-arg=-Wl,--undefined=AUDITABLE_VERSION_INFO");
72+
// Unrecognized platform, assume it to be unix-like
73+
#[allow(clippy::collapsible_else_if)]
74+
if args.bare_linker() {
75+
command.arg("-Clink-arg=--undefined=AUDITABLE_VERSION_INFO");
76+
} else {
77+
command.arg("-Clink-arg=-Wl,--undefined=AUDITABLE_VERSION_INFO");
78+
}
6779
}
6880
} else {
6981
// create_binary_file() returned None, indicating an unsupported architecture

0 commit comments

Comments
 (0)