@@ -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.
849852fn 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<'_>) -> ! {
10661114pub extern "C" fn rust_eh_personality() {}
10671115"# ;
10681116
1117+ const CO2C_LINK_STD_STUB : & str = r#"#![no_main]
1118+ "# ;
1119+
10691120fn link_shared_objects ( objects : & [ PathBuf ] , linker_args : & [ String ] , output : Option < & Path > ) {
10701121 let mut cmd = Command :: new ( "cc" ) ;
10711122 cmd. arg ( "-shared" ) ;
0 commit comments