Skip to content

Commit 5ce244d

Browse files
committed
Improve auto link to std feature in co2cc.
1 parent ea85219 commit 5ce244d

3 files changed

Lines changed: 112 additions & 23 deletions

File tree

co2cc/src/lib.rs

Lines changed: 74 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -846,6 +846,9 @@ fn should_try_direct_cc_link(linker_args: &[String]) -> bool {
846846
.any(|arg| Path::new(arg).extension().and_then(|ext| ext.to_str()) == Some("a"))
847847
}
848848

849+
// TODO: We do some horrible things here, to not link rust std when std is not actually used.
850+
// We should instead work with upstream to reduce binary size when std is not actually used,
851+
// or drop this feature entirely and force a `#![yes_std]` when people want std in co2cc programs.
849852
fn link_objects(
850853
objects: &[PathBuf],
851854
linker_args: &[String],
@@ -861,34 +864,65 @@ fn link_objects(
861864

862865
if !should_try_direct_cc_link(linker_args) {
863866
let temp_dir = make_temp_dir();
864-
let rustc_link_stub = temp_dir.join("co2c_link.rs");
865-
fs::write(&rustc_link_stub, CO2C_LINK_STUB).expect("failed to write rustc linker stub");
866-
let rustc_link_args = build_link_rustc_args(
867-
&rustc_link_stub,
868-
objects,
869-
linker_args,
870-
output,
871-
opt_level,
872-
debuginfo,
873-
);
874867
let exe = std::env::var_os("CO2_RUN_SCRIPT")
875868
.map(PathBuf::from)
876869
.or_else(current_invocation_path)
877870
.or_else(|| std::env::current_exe().ok())
878871
.expect("failed to locate co2cc executable");
879-
let mut rustc_link_cmd = Command::new(&exe);
880-
rustc_link_cmd.args(&rustc_link_args);
881-
rustc_link_cmd.env("CO2_APPLET_OVERRIDE", "co2rustc");
882-
883-
let status = rustc_link_cmd
884-
.status()
885-
.expect("failed to execute co2rustc link step");
886-
let _ = fs::remove_file(&rustc_link_stub);
887-
let _ = fs::remove_dir_all(&temp_dir);
888-
if !status.success() {
889-
process::exit(1);
872+
873+
let link_with_stub = |stub: &str| -> Option<std::process::Output> {
874+
let stub_path = temp_dir.join("co2c_link.rs");
875+
let _ = fs::write(&stub_path, stub);
876+
let rustc_link_args = build_link_rustc_args(
877+
&stub_path,
878+
objects,
879+
linker_args,
880+
output,
881+
opt_level,
882+
debuginfo,
883+
);
884+
let mut cmd = Command::new(&exe);
885+
cmd.args(&rustc_link_args);
886+
cmd.env("CO2_APPLET_OVERRIDE", "co2rustc");
887+
let output = cmd.output().ok()?;
888+
let _ = fs::remove_file(&stub_path);
889+
Some(output)
890+
};
891+
892+
let forward_output = |output: &std::process::Output| {
893+
use std::io::Write;
894+
let _ = std::io::stdout().write_all(&output.stdout);
895+
let _ = std::io::stderr().write_all(&output.stderr);
896+
};
897+
898+
// Try linking with the no_std stub first (produces smaller binaries).
899+
if let Some(ref output) = link_with_stub(CO2C_LINK_STUB) {
900+
if output.status.success() {
901+
forward_output(output);
902+
let _ = fs::remove_dir_all(&temp_dir);
903+
return;
904+
}
905+
// If no_std link failed due to std::/alloc:: unresolved symbols, retry with std stub.
906+
let stderr = String::from_utf8_lossy(&output.stderr);
907+
if stderr.contains("undefined symbol: std::")
908+
|| stderr.contains("undefined symbol: <std::")
909+
|| stderr.contains("reference to `std::")
910+
|| stderr.contains("undefined symbol: alloc::")
911+
|| stderr.contains("_Unwind_Resume")
912+
{
913+
if let Some(ref retry_output) = link_with_stub(CO2C_LINK_STD_STUB) {
914+
if retry_output.status.success() {
915+
forward_output(retry_output);
916+
let _ = fs::remove_dir_all(&temp_dir);
917+
return;
918+
}
919+
}
920+
}
921+
// Forward the original error and exit
922+
forward_output(output);
890923
}
891-
return;
924+
let _ = fs::remove_dir_all(&temp_dir);
925+
process::exit(1);
892926
}
893927

894928
let temp_dir = make_temp_dir();
@@ -938,6 +972,10 @@ fn link_objects(
938972
let cc_stderr = String::from_utf8_lossy(&link_output.stderr);
939973
if !cc_stderr.contains("undefined reference to `core::")
940974
&& !cc_stderr.contains("undefined symbol: core::")
975+
&& !cc_stderr.contains("undefined symbol: std::")
976+
&& !cc_stderr.contains("undefined symbol: <std::")
977+
&& !cc_stderr.contains("reference to `std::")
978+
&& !cc_stderr.contains("undefined symbol: alloc::")
941979
{
942980
let _ = fs::remove_file(&cc_link_stub);
943981
let _ = fs::remove_file(&cc_link_stub_object);
@@ -949,8 +987,18 @@ fn link_objects(
949987
process::exit(1);
950988
}
951989

990+
let needs_full_std = cc_stderr.contains("undefined symbol: std::")
991+
|| cc_stderr.contains("undefined symbol: <std::")
992+
|| cc_stderr.contains("reference to `std::");
993+
994+
let link_stub_src = if needs_full_std {
995+
CO2C_LINK_STD_STUB
996+
} else {
997+
CO2C_LINK_STUB
998+
};
999+
9521000
let rustc_link_stub = temp_dir.join("co2c_link.rs");
953-
fs::write(&rustc_link_stub, CO2C_LINK_STUB).expect("failed to write rustc linker stub");
1001+
fs::write(&rustc_link_stub, link_stub_src).expect("failed to write rustc linker stub");
9541002
let rustc_link_args = build_link_rustc_args(
9551003
&rustc_link_stub,
9561004
objects,
@@ -1066,6 +1114,9 @@ fn panic(info: &core::panic::PanicInfo<'_>) -> ! {
10661114
pub extern "C" fn rust_eh_personality() {}
10671115
"#;
10681116

1117+
const CO2C_LINK_STD_STUB: &str = r#"#![no_main]
1118+
"#;
1119+
10691120
fn link_shared_objects(objects: &[PathBuf], linker_args: &[String], output: Option<&Path>) {
10701121
let mut cmd = Command::new("cc");
10711122
cmd.arg("-shared");

tests/co2/co2cc_can_use_std.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//@ mode: c
2+
//@ run-status: 0
3+
4+
use std::f64::consts::PI;
5+
use std::time::Instant;
6+
use std::time::Duration;
7+
use std::thread;
8+
use std::vec::Vec;
9+
use std::mem::drop;
10+
11+
int main() {
12+
if (PI < 3.14 || std::f64::consts::PI > 3.15) {
13+
return 1;
14+
}
15+
16+
Instant instant = Instant::now();
17+
thread::sleep(Duration::from_millis(10));
18+
Duration dur = instant.elapsed();
19+
if (dur.as_millis() < 5 || Duration::as_millis(&dur) > 50) {
20+
return 2;
21+
}
22+
23+
return 0;
24+
}

tests/co2/co2cc_can_use_std2.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//@ mode: c
2+
//@ run-status: 0
3+
4+
use std::vec::Vec;
5+
6+
#include <assert.h>
7+
8+
int main() {
9+
auto x = Vec::<i32>::new();
10+
x.push(4);
11+
assert(x.len() == 1);
12+
13+
return 0;
14+
}

0 commit comments

Comments
 (0)