@@ -12,6 +12,7 @@ extern crate rustc_trans_utils;
12
12
13
13
use super :: archive:: { ArchiveBuilder , ArchiveConfig } ;
14
14
use super :: linker:: Linker ;
15
+ use super :: command:: Command ;
15
16
use super :: rpath:: RPathConfig ;
16
17
use super :: rpath;
17
18
use metadata:: METADATA_FILENAME ;
@@ -38,11 +39,12 @@ use std::ascii;
38
39
use std:: char;
39
40
use std:: env;
40
41
use std:: ffi:: OsString ;
41
- use std:: fs;
42
- use std:: io:: { self , Read , Write } ;
42
+ use std:: fmt;
43
+ use std:: fs:: { self , File } ;
44
+ use std:: io:: { self , Read , Write , BufWriter } ;
43
45
use std:: mem;
44
46
use std:: path:: { Path , PathBuf } ;
45
- use std:: process:: Command ;
47
+ use std:: process:: { Output , Stdio } ;
46
48
use std:: str;
47
49
use flate2:: Compression ;
48
50
use flate2:: write:: DeflateEncoder ;
@@ -125,8 +127,13 @@ pub fn msvc_link_exe_cmd(sess: &Session) -> (Command, Vec<(OsString, OsString)>)
125
127
let tool = windows_registry:: find_tool ( target, "link.exe" ) ;
126
128
127
129
if let Some ( tool) = tool {
130
+ let mut cmd = Command :: new ( tool. path ( ) ) ;
131
+ cmd. args ( tool. args ( ) ) ;
132
+ for & ( ref k, ref v) in tool. env ( ) {
133
+ cmd. env ( k, v) ;
134
+ }
128
135
let envs = tool. env ( ) . to_vec ( ) ;
129
- ( tool . to_command ( ) , envs)
136
+ ( cmd , envs)
130
137
} else {
131
138
debug ! ( "Failed to locate linker." ) ;
132
139
( Command :: new ( "link.exe" ) , vec ! [ ] )
@@ -797,7 +804,9 @@ fn link_natively(sess: &Session,
797
804
let mut i = 0 ;
798
805
loop {
799
806
i += 1 ;
800
- prog = time ( sess. time_passes ( ) , "running linker" , || cmd. output ( ) ) ;
807
+ prog = time ( sess. time_passes ( ) , "running linker" , || {
808
+ exec_linker ( sess, & mut cmd, tmpdir)
809
+ } ) ;
801
810
if !retry_on_segfault || i > 3 {
802
811
break
803
812
}
@@ -875,6 +884,98 @@ fn link_natively(sess: &Session,
875
884
}
876
885
}
877
886
887
+ fn exec_linker ( sess : & Session , cmd : & mut Command , tmpdir : & Path )
888
+ -> io:: Result < Output >
889
+ {
890
+ // When attempting to spawn the linker we run a risk of blowing out the
891
+ // size limits for spawning a new process with respect to the arguments
892
+ // we pass on the command line.
893
+ //
894
+ // Here we attempt to handle errors from the OS saying "your list of
895
+ // arguments is too big" by reinvoking the linker again with an `@`-file
896
+ // that contains all the arguments. The theory is that this is then
897
+ // accepted on all linkers and the linker will read all its options out of
898
+ // there instead of looking at the command line.
899
+ match cmd. command ( ) . stdout ( Stdio :: piped ( ) ) . stderr ( Stdio :: piped ( ) ) . spawn ( ) {
900
+ Ok ( child) => return child. wait_with_output ( ) ,
901
+ Err ( ref e) if command_line_too_big ( e) => { }
902
+ Err ( e) => return Err ( e)
903
+ }
904
+
905
+ let file = tmpdir. join ( "linker-arguments" ) ;
906
+ let mut cmd2 = Command :: new ( cmd. get_program ( ) ) ;
907
+ cmd2. arg ( format ! ( "@{}" , file. display( ) ) ) ;
908
+ for & ( ref k, ref v) in cmd. get_env ( ) {
909
+ cmd2. env ( k, v) ;
910
+ }
911
+ let mut f = BufWriter :: new ( File :: create ( & file) ?) ;
912
+ for arg in cmd. get_args ( ) {
913
+ writeln ! ( f, "{}" , Escape {
914
+ arg: arg. to_str( ) . unwrap( ) ,
915
+ is_like_msvc: sess. target. target. options. is_like_msvc,
916
+ } ) ?;
917
+ }
918
+ f. into_inner ( ) ?;
919
+ return cmd2. output ( ) ;
920
+
921
+ #[ cfg( unix) ]
922
+ fn command_line_too_big ( err : & io:: Error ) -> bool {
923
+ err. raw_os_error ( ) == Some ( :: libc:: E2BIG )
924
+ }
925
+
926
+ #[ cfg( windows) ]
927
+ fn command_line_too_big ( err : & io:: Error ) -> bool {
928
+ const ERROR_FILENAME_EXCED_RANGE : i32 = 206 ;
929
+ err. raw_os_error ( ) == Some ( ERROR_FILENAME_EXCED_RANGE )
930
+ }
931
+
932
+ struct Escape < ' a > {
933
+ arg : & ' a str ,
934
+ is_like_msvc : bool ,
935
+ }
936
+
937
+ impl < ' a > fmt:: Display for Escape < ' a > {
938
+ fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
939
+ if self . is_like_msvc {
940
+ // This is "documented" at
941
+ // https://msdn.microsoft.com/en-us/library/4xdcbak7.aspx
942
+ //
943
+ // Unfortunately there's not a great specification of the
944
+ // syntax I could find online (at least) but some local
945
+ // testing showed that this seemed sufficient-ish to catch
946
+ // at least a few edge cases.
947
+ write ! ( f, "\" " ) ?;
948
+ for c in self . arg . chars ( ) {
949
+ match c {
950
+ '"' => write ! ( f, "\\ {}" , c) ?,
951
+ c => write ! ( f, "{}" , c) ?,
952
+ }
953
+ }
954
+ write ! ( f, "\" " ) ?;
955
+ } else {
956
+ // This is documented at https://linux.die.net/man/1/ld, namely:
957
+ //
958
+ // > Options in file are separated by whitespace. A whitespace
959
+ // > character may be included in an option by surrounding the
960
+ // > entire option in either single or double quotes. Any
961
+ // > character (including a backslash) may be included by
962
+ // > prefixing the character to be included with a backslash.
963
+ //
964
+ // We put an argument on each line, so all we need to do is
965
+ // ensure the line is interpreted as one whole argument.
966
+ for c in self . arg . chars ( ) {
967
+ match c {
968
+ '\\' |
969
+ ' ' => write ! ( f, "\\ {}" , c) ?,
970
+ c => write ! ( f, "{}" , c) ?,
971
+ }
972
+ }
973
+ }
974
+ Ok ( ( ) )
975
+ }
976
+ }
977
+ }
978
+
878
979
fn link_args ( cmd : & mut Linker ,
879
980
sess : & Session ,
880
981
crate_type : config:: CrateType ,
0 commit comments