@@ -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 ;
46
48
use std:: str;
47
49
use flate2:: Compression ;
48
50
use flate2:: write:: DeflateEncoder ;
@@ -759,7 +761,9 @@ fn link_natively(sess: &Session,
759
761
let mut i = 0 ;
760
762
loop {
761
763
i += 1 ;
762
- prog = time ( sess. time_passes ( ) , "running linker" , || cmd. output ( ) ) ;
764
+ prog = time ( sess. time_passes ( ) , "running linker" , || {
765
+ exec_linker ( sess, & mut cmd, tmpdir)
766
+ } ) ;
763
767
if !retry_on_segfault || i > 3 {
764
768
break
765
769
}
@@ -837,6 +841,98 @@ fn link_natively(sess: &Session,
837
841
}
838
842
}
839
843
844
+ fn exec_linker ( sess : & Session , cmd : & mut Command , tmpdir : & Path )
845
+ -> io:: Result < Output >
846
+ {
847
+ // When attempting to spawn the linker we run a risk of blowing out the
848
+ // size limits for spawning a new process with respect to the arguments
849
+ // we pass on the command line.
850
+ //
851
+ // Here we attempt to handle errors from the OS saying "your list of
852
+ // arguments is too big" by reinvoking the linker again with an `@`-file
853
+ // that contains all the arguments. The theory is that this is then
854
+ // accepted on all linkers and the linker will read all its options out of
855
+ // there instead of looking at the command line.
856
+ match cmd. spawn ( ) {
857
+ Ok ( child) => return child. wait_with_output ( ) ,
858
+ Err ( ref e) if command_line_too_big ( e) => { }
859
+ Err ( e) => return Err ( e)
860
+ }
861
+
862
+ let file = tmpdir. join ( "linker-arguments" ) ;
863
+ let mut cmd2 = Command :: new ( cmd. get_program ( ) ) ;
864
+ cmd2. arg ( format ! ( "@{}" , file. display( ) ) ) ;
865
+ for & ( ref k, ref v) in cmd. get_env ( ) {
866
+ cmd2. env ( k, v) ;
867
+ }
868
+ let mut f = BufWriter :: new ( File :: create ( & file) ?) ;
869
+ for arg in cmd. get_args ( ) {
870
+ writeln ! ( f, "{}" , Escape {
871
+ arg: arg. to_str( ) . unwrap( ) ,
872
+ is_like_msvc: sess. target. target. options. is_like_msvc,
873
+ } ) ?;
874
+ }
875
+ f. into_inner ( ) ?;
876
+ return cmd2. output ( ) ;
877
+
878
+ #[ cfg( unix) ]
879
+ fn command_line_too_big ( err : & io:: Error ) -> bool {
880
+ err. raw_os_error ( ) == Some ( :: libc:: E2BIG )
881
+ }
882
+
883
+ #[ cfg( windows) ]
884
+ fn command_line_too_big ( err : & io:: Error ) -> bool {
885
+ const ERROR_FILENAME_EXCED_RANGE : i32 = 206 ;
886
+ err. raw_os_error ( ) == Some ( ERROR_FILENAME_EXCED_RANGE )
887
+ }
888
+
889
+ struct Escape < ' a > {
890
+ arg : & ' a str ,
891
+ is_like_msvc : bool ,
892
+ }
893
+
894
+ impl < ' a > fmt:: Display for Escape < ' a > {
895
+ fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
896
+ if self . is_like_msvc {
897
+ // This is "documented" at
898
+ // https://msdn.microsoft.com/en-us/library/4xdcbak7.aspx
899
+ //
900
+ // Unfortunately there's not a great specification of the
901
+ // syntax I could find online (at least) but some local
902
+ // testing showed that this seemed sufficient-ish to catch
903
+ // at least a few edge cases.
904
+ write ! ( f, "\" " ) ?;
905
+ for c in self . arg . chars ( ) {
906
+ match c {
907
+ '"' => write ! ( f, "\\ {}" , c) ?,
908
+ c => write ! ( f, "{}" , c) ?,
909
+ }
910
+ }
911
+ write ! ( f, "\" " ) ?;
912
+ } else {
913
+ // This is documented at https://linux.die.net/man/1/ld, namely:
914
+ //
915
+ // > Options in file are separated by whitespace. A whitespace
916
+ // > character may be included in an option by surrounding the
917
+ // > entire option in either single or double quotes. Any
918
+ // > character (including a backslash) may be included by
919
+ // > prefixing the character to be included with a backslash.
920
+ //
921
+ // We put an argument on each line, so all we need to do is
922
+ // ensure the line is interpreted as one whole argument.
923
+ for c in self . arg . chars ( ) {
924
+ match c {
925
+ '\\' |
926
+ ' ' => write ! ( f, "\\ {}" , c) ?,
927
+ c => write ! ( f, "{}" , c) ?,
928
+ }
929
+ }
930
+ }
931
+ Ok ( ( ) )
932
+ }
933
+ }
934
+ }
935
+
840
936
fn link_args ( cmd : & mut Linker ,
841
937
sess : & Session ,
842
938
crate_type : config:: CrateType ,
0 commit comments