@@ -12,6 +12,7 @@ extern crate rustc_trans_utils;
1212
1313use super :: archive:: { ArchiveBuilder , ArchiveConfig } ;
1414use super :: linker:: Linker ;
15+ use super :: command:: Command ;
1516use super :: rpath:: RPathConfig ;
1617use super :: rpath;
1718use metadata:: METADATA_FILENAME ;
@@ -38,11 +39,12 @@ use std::ascii;
3839use std:: char;
3940use std:: env;
4041use 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 } ;
4345use std:: mem;
4446use std:: path:: { Path , PathBuf } ;
45- use std:: process:: Command ;
47+ use std:: process:: { Output , Stdio } ;
4648use std:: str;
4749use flate2:: Compression ;
4850use flate2:: write:: DeflateEncoder ;
@@ -125,8 +127,13 @@ pub fn msvc_link_exe_cmd(sess: &Session) -> (Command, Vec<(OsString, OsString)>)
125127 let tool = windows_registry:: find_tool ( target, "link.exe" ) ;
126128
127129 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+ }
128135 let envs = tool. env ( ) . to_vec ( ) ;
129- ( tool . to_command ( ) , envs)
136+ ( cmd , envs)
130137 } else {
131138 debug ! ( "Failed to locate linker." ) ;
132139 ( Command :: new ( "link.exe" ) , vec ! [ ] )
@@ -797,7 +804,9 @@ fn link_natively(sess: &Session,
797804 let mut i = 0 ;
798805 loop {
799806 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+ } ) ;
801810 if !retry_on_segfault || i > 3 {
802811 break
803812 }
@@ -875,6 +884,98 @@ fn link_natively(sess: &Session,
875884 }
876885}
877886
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+
878979fn link_args ( cmd : & mut Linker ,
879980 sess : & Session ,
880981 crate_type : config:: CrateType ,
0 commit comments