]>
Commit | Line | Data |
---|---|---|
a2a8927a | 1 | use rustc_arena::TypedArena; |
136023e0 | 2 | use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; |
a2a8927a | 3 | use rustc_data_structures::memmap::Mmap; |
3dfed10e | 4 | use rustc_data_structures::temp_dir::MaybeTempDir; |
136023e0 | 5 | use rustc_errors::{ErrorReported, Handler}; |
ba9703b0 XL |
6 | use rustc_fs_util::fix_windows_verbatim_for_gcc; |
7 | use rustc_hir::def_id::CrateNum; | |
ba9703b0 | 8 | use rustc_middle::middle::dependency_format::Linkage; |
17df50a5 | 9 | use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, LdImpl, Strip}; |
a2a8927a | 10 | use rustc_session::config::{OutputFilenames, OutputType, PrintRequest, SplitDwarfKind}; |
c295e0f8 | 11 | use rustc_session::cstore::DllImport; |
ba9703b0 XL |
12 | use rustc_session::output::{check_file_is_writeable, invalid_output_for_target, out_filename}; |
13 | use rustc_session::search_paths::PathKind; | |
f9f354fc | 14 | use rustc_session::utils::NativeLibKind; |
dfeec247 XL |
15 | /// For all the linkers we support, and information they might |
16 | /// need out of the shared crate context before we get rid of it. | |
ba9703b0 | 17 | use rustc_session::{filesearch, Session}; |
dfeec247 | 18 | use rustc_span::symbol::Symbol; |
f035d41b | 19 | use rustc_target::spec::crt_objects::{CrtObjects, CrtObjectsFallback}; |
5869c6ff | 20 | use rustc_target::spec::{LinkOutputKind, LinkerFlavor, LldFlavor, SplitDebuginfo}; |
cdc7bbd5 | 21 | use rustc_target::spec::{PanicStrategy, RelocModel, RelroLevel, SanitizerSet, Target}; |
a1dfa0c6 | 22 | |
c295e0f8 | 23 | use super::archive::{find_library, ArchiveBuilder}; |
a1dfa0c6 | 24 | use super::command::Command; |
f9f354fc | 25 | use super::linker::{self, Linker}; |
a2a8927a | 26 | use super::metadata::create_rmeta_file; |
48663c56 | 27 | use super::rpath::{self, RPathConfig}; |
fc512014 XL |
28 | use crate::{ |
29 | looks_like_rust_object_file, CodegenResults, CompiledModule, CrateInfo, NativeLib, | |
30 | METADATA_FILENAME, | |
31 | }; | |
a1dfa0c6 XL |
32 | |
33 | use cc::windows_registry; | |
94222f64 | 34 | use regex::Regex; |
3dfed10e | 35 | use tempfile::Builder as TempFileBuilder; |
48663c56 | 36 | |
a2a8927a | 37 | use std::borrow::Borrow; |
dfeec247 | 38 | use std::ffi::OsString; |
a2a8927a XL |
39 | use std::fs::{File, OpenOptions}; |
40 | use std::io::{BufWriter, Write}; | |
c295e0f8 | 41 | use std::lazy::OnceCell; |
a1dfa0c6 | 42 | use std::path::{Path, PathBuf}; |
dfeec247 | 43 | use std::process::{ExitStatus, Output, Stdio}; |
f035d41b | 44 | use std::{ascii, char, env, fmt, fs, io, mem, str}; |
a1dfa0c6 | 45 | |
6a06907d | 46 | pub fn ensure_removed(diag_handler: &Handler, path: &Path) { |
a1dfa0c6 | 47 | if let Err(e) = fs::remove_file(path) { |
6a06907d XL |
48 | if e.kind() != io::ErrorKind::NotFound { |
49 | diag_handler.err(&format!("failed to remove {}: {}", path.display(), e)); | |
50 | } | |
a1dfa0c6 XL |
51 | } |
52 | } | |
53 | ||
48663c56 XL |
54 | /// Performs the linkage portion of the compilation phase. This will generate all |
55 | /// of the requested outputs for this compilation session. | |
dfeec247 XL |
56 | pub fn link_binary<'a, B: ArchiveBuilder<'a>>( |
57 | sess: &'a Session, | |
58 | codegen_results: &CodegenResults, | |
59 | outputs: &OutputFilenames, | |
136023e0 | 60 | ) -> Result<(), ErrorReported> { |
dfeec247 | 61 | let _timer = sess.timer("link_binary"); |
48663c56 | 62 | let output_metadata = sess.opts.output_types.contains_key(&OutputType::Metadata); |
f9f354fc | 63 | for &crate_type in sess.crate_types().iter() { |
48663c56 | 64 | // Ignore executable crates if we have -Z no-codegen, as they will error. |
dfeec247 XL |
65 | if (sess.opts.debugging_opts.no_codegen || !sess.opts.output_types.should_codegen()) |
66 | && !output_metadata | |
f9f354fc | 67 | && crate_type == CrateType::Executable |
dfeec247 | 68 | { |
48663c56 XL |
69 | continue; |
70 | } | |
71 | ||
72 | if invalid_output_for_target(sess, crate_type) { | |
dfeec247 XL |
73 | bug!( |
74 | "invalid output type `{:?}` for target os `{}`", | |
75 | crate_type, | |
76 | sess.opts.target_triple | |
77 | ); | |
48663c56 XL |
78 | } |
79 | ||
dfeec247 XL |
80 | sess.time("link_binary_check_files_are_writeable", || { |
81 | for obj in codegen_results.modules.iter().filter_map(|m| m.object.as_ref()) { | |
82 | check_file_is_writeable(obj, sess); | |
83 | } | |
84 | }); | |
48663c56 | 85 | |
5869c6ff | 86 | if outputs.outputs.should_link() { |
3dfed10e XL |
87 | let tmpdir = TempFileBuilder::new() |
88 | .prefix("rustc") | |
89 | .tempdir() | |
90 | .unwrap_or_else(|err| sess.fatal(&format!("couldn't create a temp dir: {}", err))); | |
91 | let path = MaybeTempDir::new(tmpdir, sess.opts.cg.save_temps); | |
136023e0 XL |
92 | let out_filename = out_filename( |
93 | sess, | |
94 | crate_type, | |
95 | outputs, | |
a2a8927a | 96 | codegen_results.crate_info.local_crate_name.as_str(), |
136023e0 | 97 | ); |
48663c56 | 98 | match crate_type { |
f9f354fc | 99 | CrateType::Rlib => { |
dfeec247 | 100 | let _timer = sess.timer("link_rlib"); |
136023e0 XL |
101 | link_rlib::<B>( |
102 | sess, | |
103 | codegen_results, | |
104 | RlibFlavor::Normal, | |
105 | &out_filename, | |
106 | &path, | |
107 | )? | |
108 | .build(); | |
48663c56 | 109 | } |
f9f354fc | 110 | CrateType::Staticlib => { |
136023e0 | 111 | link_staticlib::<B>(sess, codegen_results, &out_filename, &path)?; |
48663c56 XL |
112 | } |
113 | _ => { | |
114 | link_natively::<B>( | |
115 | sess, | |
116 | crate_type, | |
117 | &out_filename, | |
118 | codegen_results, | |
3dfed10e | 119 | path.as_ref(), |
48663c56 XL |
120 | ); |
121 | } | |
122 | } | |
416331ca | 123 | if sess.opts.json_artifact_notifications { |
dc9dc135 | 124 | sess.parse_sess.span_diagnostic.emit_artifact_notification(&out_filename, "link"); |
48663c56 | 125 | } |
3c0e092e XL |
126 | |
127 | if sess.prof.enabled() { | |
128 | if let Some(artifact_name) = out_filename.file_name() { | |
129 | // Record size for self-profiling | |
130 | let file_size = std::fs::metadata(&out_filename).map(|m| m.len()).unwrap_or(0); | |
131 | ||
132 | sess.prof.artifact_size( | |
133 | "linked_artifact", | |
134 | artifact_name.to_string_lossy(), | |
135 | file_size, | |
136 | ); | |
137 | } | |
138 | } | |
48663c56 | 139 | } |
48663c56 XL |
140 | } |
141 | ||
a2a8927a | 142 | // Remove the temporary object file and metadata if we aren't saving temps. |
dfeec247 | 143 | sess.time("link_binary_remove_temps", || { |
a2a8927a XL |
144 | // If the user requests that temporaries are saved, don't delete any. |
145 | if sess.opts.cg.save_temps { | |
146 | return; | |
147 | } | |
fc512014 | 148 | |
a2a8927a XL |
149 | let remove_temps_from_module = |module: &CompiledModule| { |
150 | if let Some(ref obj) = module.object { | |
151 | ensure_removed(sess.diagnostic(), obj); | |
48663c56 | 152 | } |
a2a8927a | 153 | }; |
fc512014 | 154 | |
a2a8927a XL |
155 | // Otherwise, always remove the metadata and allocator module temporaries. |
156 | if let Some(ref metadata_module) = codegen_results.metadata_module { | |
157 | remove_temps_from_module(metadata_module); | |
158 | } | |
159 | ||
160 | if let Some(ref allocator_module) = codegen_results.allocator_module { | |
161 | remove_temps_from_module(allocator_module); | |
162 | } | |
163 | ||
164 | // If no requested outputs require linking, then the object temporaries should | |
165 | // be kept. | |
166 | if !sess.opts.output_types.should_link() { | |
167 | return; | |
168 | } | |
169 | ||
170 | // Potentially keep objects for their debuginfo. | |
171 | let (preserve_objects, preserve_dwarf_objects) = preserve_objects_for_their_debuginfo(sess); | |
172 | debug!(?preserve_objects, ?preserve_dwarf_objects); | |
173 | ||
174 | for module in &codegen_results.modules { | |
175 | if !preserve_objects { | |
176 | remove_temps_from_module(module); | |
48663c56 | 177 | } |
fc512014 | 178 | |
a2a8927a XL |
179 | if !preserve_dwarf_objects { |
180 | if let Some(ref obj) = module.dwarf_object { | |
181 | ensure_removed(sess.diagnostic(), obj); | |
182 | } | |
48663c56 XL |
183 | } |
184 | } | |
dfeec247 | 185 | }); |
a1dfa0c6 | 186 | |
136023e0 | 187 | Ok(()) |
a1dfa0c6 XL |
188 | } |
189 | ||
e74abb32 XL |
190 | pub fn each_linked_rlib( |
191 | info: &CrateInfo, | |
192 | f: &mut dyn FnMut(CrateNum, &Path), | |
193 | ) -> Result<(), String> { | |
136023e0 | 194 | let crates = info.used_crates.iter(); |
e74abb32 XL |
195 | let mut fmts = None; |
196 | for (ty, list) in info.dependency_formats.iter() { | |
197 | match ty { | |
f9f354fc XL |
198 | CrateType::Executable |
199 | | CrateType::Staticlib | |
200 | | CrateType::Cdylib | |
201 | | CrateType::ProcMacro => { | |
e74abb32 XL |
202 | fmts = Some(list); |
203 | break; | |
204 | } | |
205 | _ => {} | |
206 | } | |
207 | } | |
3c0e092e XL |
208 | let Some(fmts) = fmts else { |
209 | return Err("could not find formats for rlibs".to_string()); | |
a1dfa0c6 | 210 | }; |
136023e0 | 211 | for &cnum in crates { |
a1dfa0c6 | 212 | match fmts.get(cnum.as_usize() - 1) { |
ba9703b0 | 213 | Some(&Linkage::NotLinked | &Linkage::IncludedFromDylib) => continue, |
a1dfa0c6 | 214 | Some(_) => {} |
dfeec247 | 215 | None => return Err("could not find formats for rlibs".to_string()), |
a1dfa0c6 XL |
216 | } |
217 | let name = &info.crate_name[&cnum]; | |
136023e0 | 218 | let used_crate_source = &info.used_crate_source[&cnum]; |
5099ac24 FG |
219 | if let Some((path, _)) = &used_crate_source.rlib { |
220 | f(cnum, &path); | |
136023e0 | 221 | } else { |
5099ac24 FG |
222 | if used_crate_source.rmeta.is_some() { |
223 | return Err(format!( | |
224 | "could not find rlib for: `{}`, found rmeta (metadata) file", | |
225 | name | |
226 | )); | |
227 | } else { | |
228 | return Err(format!("could not find rlib for: `{}`", name)); | |
229 | } | |
230 | } | |
a1dfa0c6 XL |
231 | } |
232 | Ok(()) | |
233 | } | |
234 | ||
48663c56 XL |
235 | /// We use a temp directory here to avoid races between concurrent rustc processes, |
236 | /// such as builds in the same directory using the same filename for metadata while | |
237 | /// building an `.rlib` (stomping over one another), or writing an `.rmeta` into a | |
238 | /// directory being searched for `extern crate` (observing an incomplete file). | |
239 | /// The returned path is the temporary file containing the complete metadata. | |
17df50a5 | 240 | pub fn emit_metadata(sess: &Session, metadata: &[u8], tmpdir: &MaybeTempDir) -> PathBuf { |
3dfed10e | 241 | let out_filename = tmpdir.as_ref().join(METADATA_FILENAME); |
17df50a5 | 242 | let result = fs::write(&out_filename, metadata); |
48663c56 XL |
243 | |
244 | if let Err(e) = result { | |
245 | sess.fatal(&format!("failed to write {}: {}", out_filename.display(), e)); | |
246 | } | |
247 | ||
248 | out_filename | |
249 | } | |
250 | ||
fc512014 XL |
251 | /// Create an 'rlib'. |
252 | /// | |
253 | /// An rlib in its current incarnation is essentially a renamed .a file. The rlib primarily contains | |
254 | /// the object file of the crate, but it also contains all of the object files from native | |
255 | /// libraries. This is done by unzipping native libraries and inserting all of the contents into | |
256 | /// this archive. | |
dfeec247 XL |
257 | fn link_rlib<'a, B: ArchiveBuilder<'a>>( |
258 | sess: &'a Session, | |
259 | codegen_results: &CodegenResults, | |
260 | flavor: RlibFlavor, | |
261 | out_filename: &Path, | |
3dfed10e | 262 | tmpdir: &MaybeTempDir, |
136023e0 | 263 | ) -> Result<B, ErrorReported> { |
48663c56 | 264 | info!("preparing rlib to {:?}", out_filename); |
c295e0f8 XL |
265 | |
266 | let lib_search_paths = archive_search_paths(sess); | |
267 | ||
48663c56 XL |
268 | let mut ab = <B as ArchiveBuilder>::new(sess, out_filename, None); |
269 | ||
a2a8927a XL |
270 | for m in &codegen_results.modules { |
271 | if let Some(obj) = m.object.as_ref() { | |
272 | ab.add_file(obj); | |
273 | } | |
274 | ||
275 | if let Some(dwarf_obj) = m.dwarf_object.as_ref() { | |
276 | ab.add_file(dwarf_obj); | |
277 | } | |
48663c56 XL |
278 | } |
279 | ||
280 | // Note that in this loop we are ignoring the value of `lib.cfg`. That is, | |
281 | // we may not be configured to actually include a static library if we're | |
282 | // adding it here. That's because later when we consume this rlib we'll | |
283 | // decide whether we actually needed the static library or not. | |
284 | // | |
285 | // To do this "correctly" we'd need to keep track of which libraries added | |
286 | // which object files to the archive. We don't do that here, however. The | |
287 | // #[link(cfg(..))] feature is unstable, though, and only intended to get | |
288 | // liblibc working. In that sense the check below just indicates that if | |
289 | // there are any libraries we want to omit object files for at link time we | |
290 | // just exclude all custom object files. | |
291 | // | |
292 | // Eventually if we want to stabilize or flesh out the #[link(cfg(..))] | |
293 | // feature then we'll need to figure out how to record what objects were | |
294 | // loaded from the libraries found here and then encode that into the | |
295 | // metadata of the rlib we're generating somehow. | |
296 | for lib in codegen_results.crate_info.used_libraries.iter() { | |
297 | match lib.kind { | |
c295e0f8 XL |
298 | NativeLibKind::Static { bundle: None | Some(true), whole_archive: Some(true) } |
299 | if flavor == RlibFlavor::Normal => | |
300 | { | |
301 | // Don't allow mixing +bundle with +whole_archive since an rlib may contain | |
302 | // multiple native libs, some of which are +whole-archive and some of which are | |
303 | // -whole-archive and it isn't clear how we can currently handle such a | |
304 | // situation correctly. | |
305 | // See https://github.com/rust-lang/rust/issues/88085#issuecomment-901050897 | |
306 | sess.err( | |
307 | "the linking modifiers `+bundle` and `+whole-archive` are not compatible \ | |
308 | with each other when generating rlibs", | |
309 | ); | |
310 | } | |
17df50a5 XL |
311 | NativeLibKind::Static { bundle: None | Some(true), .. } => {} |
312 | NativeLibKind::Static { bundle: Some(false), .. } | |
313 | | NativeLibKind::Dylib { .. } | |
314 | | NativeLibKind::Framework { .. } | |
f9f354fc XL |
315 | | NativeLibKind::RawDylib |
316 | | NativeLibKind::Unspecified => continue, | |
48663c56 XL |
317 | } |
318 | if let Some(name) = lib.name { | |
c295e0f8 XL |
319 | let location = |
320 | find_library(name, lib.verbatim.unwrap_or(false), &lib_search_paths, sess); | |
321 | ab.add_archive(&location, |_| false).unwrap_or_else(|e| { | |
322 | sess.fatal(&format!( | |
323 | "failed to add native library {}: {}", | |
324 | location.to_string_lossy(), | |
325 | e | |
326 | )); | |
327 | }); | |
48663c56 XL |
328 | } |
329 | } | |
330 | ||
17df50a5 | 331 | for (raw_dylib_name, raw_dylib_imports) in |
136023e0 | 332 | collate_raw_dylibs(sess, &codegen_results.crate_info.used_libraries)? |
17df50a5 XL |
333 | { |
334 | ab.inject_dll_import_lib(&raw_dylib_name, &raw_dylib_imports, tmpdir); | |
335 | } | |
336 | ||
48663c56 XL |
337 | // Note that it is important that we add all of our non-object "magical |
338 | // files" *after* all of the object files in the archive. The reason for | |
339 | // this is as follows: | |
340 | // | |
341 | // * When performing LTO, this archive will be modified to remove | |
342 | // objects from above. The reason for this is described below. | |
343 | // | |
344 | // * When the system linker looks at an archive, it will attempt to | |
345 | // determine the architecture of the archive in order to see whether its | |
346 | // linkable. | |
347 | // | |
348 | // The algorithm for this detection is: iterate over the files in the | |
349 | // archive. Skip magical SYMDEF names. Interpret the first file as an | |
350 | // object file. Read architecture from the object file. | |
351 | // | |
352 | // * As one can probably see, if "metadata" and "foo.bc" were placed | |
353 | // before all of the objects, then the architecture of this archive would | |
354 | // not be correctly inferred once 'foo.o' is removed. | |
355 | // | |
356 | // Basically, all this means is that this code should not move above the | |
357 | // code above. | |
358 | match flavor { | |
359 | RlibFlavor::Normal => { | |
17df50a5 XL |
360 | // metadata in rlib files is wrapped in a "dummy" object file for |
361 | // the target platform so the rlib can be processed entirely by | |
362 | // normal linkers for the platform. | |
a2a8927a | 363 | let metadata = create_rmeta_file(sess, codegen_results.metadata.raw_data()); |
17df50a5 | 364 | ab.add_file(&emit_metadata(sess, &metadata, tmpdir)); |
48663c56 XL |
365 | } |
366 | ||
367 | RlibFlavor::StaticlibBase => { | |
dfeec247 | 368 | let obj = codegen_results.allocator_module.as_ref().and_then(|m| m.object.as_ref()); |
48663c56 XL |
369 | if let Some(obj) = obj { |
370 | ab.add_file(obj); | |
371 | } | |
372 | } | |
373 | } | |
5099ac24 | 374 | |
136023e0 | 375 | return Ok(ab); |
17df50a5 XL |
376 | } |
377 | ||
378 | /// Extract all symbols defined in raw-dylib libraries, collated by library name. | |
379 | /// | |
380 | /// If we have multiple extern blocks that specify symbols defined in the same raw-dylib library, | |
381 | /// then the CodegenResults value contains one NativeLib instance for each block. However, the | |
382 | /// linker appears to expect only a single import library for each library used, so we need to | |
383 | /// collate the symbols together by library name before generating the import libraries. | |
136023e0 XL |
384 | fn collate_raw_dylibs( |
385 | sess: &Session, | |
386 | used_libraries: &[NativeLib], | |
387 | ) -> Result<Vec<(String, Vec<DllImport>)>, ErrorReported> { | |
388 | // Use index maps to preserve original order of imports and libraries. | |
389 | let mut dylib_table = FxIndexMap::<String, FxIndexMap<Symbol, &DllImport>>::default(); | |
17df50a5 XL |
390 | |
391 | for lib in used_libraries { | |
392 | if lib.kind == NativeLibKind::RawDylib { | |
136023e0 XL |
393 | let ext = if matches!(lib.verbatim, Some(true)) { "" } else { ".dll" }; |
394 | let name = format!("{}{}", lib.name.expect("unnamed raw-dylib library"), ext); | |
395 | let imports = dylib_table.entry(name.clone()).or_default(); | |
396 | for import in &lib.dll_imports { | |
397 | if let Some(old_import) = imports.insert(import.name, import) { | |
398 | // FIXME: when we add support for ordinals, figure out if we need to do anything | |
399 | // if we have two DllImport values with the same name but different ordinals. | |
400 | if import.calling_convention != old_import.calling_convention { | |
401 | sess.span_err( | |
402 | import.span, | |
403 | &format!( | |
404 | "multiple declarations of external function `{}` from \ | |
405 | library `{}` have different calling conventions", | |
406 | import.name, name, | |
407 | ), | |
408 | ); | |
409 | } | |
410 | } | |
411 | } | |
17df50a5 XL |
412 | } |
413 | } | |
136023e0 XL |
414 | sess.compile_status()?; |
415 | Ok(dylib_table | |
17df50a5 | 416 | .into_iter() |
136023e0 XL |
417 | .map(|(name, imports)| { |
418 | (name, imports.into_iter().map(|(_, import)| import.clone()).collect()) | |
17df50a5 | 419 | }) |
136023e0 | 420 | .collect()) |
48663c56 XL |
421 | } |
422 | ||
fc512014 XL |
423 | /// Create a static archive. |
424 | /// | |
425 | /// This is essentially the same thing as an rlib, but it also involves adding all of the upstream | |
426 | /// crates' objects into the archive. This will slurp in all of the native libraries of upstream | |
427 | /// dependencies as well. | |
428 | /// | |
429 | /// Additionally, there's no way for us to link dynamic libraries, so we warn about all dynamic | |
430 | /// library dependencies that they're not linked in. | |
431 | /// | |
432 | /// There's no need to include metadata in a static archive, so ensure to not link in the metadata | |
433 | /// object file (and also don't prepare the archive with a metadata file). | |
dfeec247 XL |
434 | fn link_staticlib<'a, B: ArchiveBuilder<'a>>( |
435 | sess: &'a Session, | |
436 | codegen_results: &CodegenResults, | |
437 | out_filename: &Path, | |
3dfed10e | 438 | tempdir: &MaybeTempDir, |
136023e0 | 439 | ) -> Result<(), ErrorReported> { |
dfeec247 | 440 | let mut ab = |
136023e0 | 441 | link_rlib::<B>(sess, codegen_results, RlibFlavor::StaticlibBase, out_filename, tempdir)?; |
48663c56 XL |
442 | let mut all_native_libs = vec![]; |
443 | ||
e74abb32 | 444 | let res = each_linked_rlib(&codegen_results.crate_info, &mut |cnum, path| { |
48663c56 XL |
445 | let name = &codegen_results.crate_info.crate_name[&cnum]; |
446 | let native_libs = &codegen_results.crate_info.native_libraries[&cnum]; | |
447 | ||
448 | // Here when we include the rlib into our staticlib we need to make a | |
449 | // decision whether to include the extra object files along the way. | |
450 | // These extra object files come from statically included native | |
451 | // libraries, but they may be cfg'd away with #[link(cfg(..))]. | |
452 | // | |
453 | // This unstable feature, though, only needs liblibc to work. The only | |
454 | // use case there is where musl is statically included in liblibc.rlib, | |
455 | // so if we don't want the included version we just need to skip it. As | |
456 | // a result the logic here is that if *any* linked library is cfg'd away | |
457 | // we just skip all object files. | |
458 | // | |
459 | // Clearly this is not sufficient for a general purpose feature, and | |
460 | // we'd want to read from the library's metadata to determine which | |
461 | // object files come from where and selectively skip them. | |
17df50a5 XL |
462 | let skip_object_files = native_libs.iter().any(|lib| { |
463 | matches!(lib.kind, NativeLibKind::Static { bundle: None | Some(true), .. }) | |
464 | && !relevant_lib(sess, lib) | |
465 | }); | |
c295e0f8 XL |
466 | |
467 | let lto = are_upstream_rust_objects_already_included(sess) | |
468 | && !ignored_for_lto(sess, &codegen_results.crate_info, cnum); | |
469 | ||
470 | // Ignoring obj file starting with the crate name | |
471 | // as simple comparison is not enough - there | |
472 | // might be also an extra name suffix | |
473 | let obj_start = name.as_str().to_owned(); | |
474 | ||
475 | ab.add_archive(path, move |fname: &str| { | |
476 | // Ignore metadata files, no matter the name. | |
477 | if fname == METADATA_FILENAME { | |
478 | return true; | |
479 | } | |
480 | ||
481 | // Don't include Rust objects if LTO is enabled | |
482 | if lto && looks_like_rust_object_file(fname) { | |
483 | return true; | |
484 | } | |
485 | ||
486 | // Otherwise if this is *not* a rust object and we're skipping | |
487 | // objects then skip this file | |
488 | if skip_object_files && (!fname.starts_with(&obj_start) || !fname.ends_with(".o")) { | |
489 | return true; | |
490 | } | |
491 | ||
492 | // ok, don't skip this | |
493 | false | |
494 | }) | |
dfeec247 | 495 | .unwrap(); |
48663c56 XL |
496 | |
497 | all_native_libs.extend(codegen_results.crate_info.native_libraries[&cnum].iter().cloned()); | |
498 | }); | |
499 | if let Err(e) = res { | |
500 | sess.fatal(&e); | |
501 | } | |
502 | ||
48663c56 XL |
503 | ab.build(); |
504 | ||
505 | if !all_native_libs.is_empty() { | |
506 | if sess.opts.prints.contains(&PrintRequest::NativeStaticLibs) { | |
507 | print_native_static_libs(sess, &all_native_libs); | |
508 | } | |
509 | } | |
136023e0 XL |
510 | |
511 | Ok(()) | |
48663c56 XL |
512 | } |
513 | ||
fc512014 XL |
514 | fn escape_stdout_stderr_string(s: &[u8]) -> String { |
515 | str::from_utf8(s).map(|s| s.to_owned()).unwrap_or_else(|_| { | |
516 | let mut x = "Non-UTF-8 output: ".to_string(); | |
517 | x.extend(s.iter().flat_map(|&b| ascii::escape_default(b)).map(char::from)); | |
518 | x | |
519 | }) | |
520 | } | |
521 | ||
a2a8927a XL |
522 | /// Use `thorin` (rust implementation of a dwarf packaging utility) to link DWARF objects into a |
523 | /// DWARF package. | |
524 | fn link_dwarf_object<'a>( | |
525 | sess: &'a Session, | |
526 | cg_results: &CodegenResults, | |
527 | executable_out_filename: &Path, | |
528 | ) { | |
fc512014 | 529 | let dwp_out_filename = executable_out_filename.with_extension("dwp"); |
a2a8927a | 530 | debug!(?dwp_out_filename, ?executable_out_filename); |
fc512014 | 531 | |
a2a8927a XL |
532 | #[derive(Default)] |
533 | struct ThorinSession<Relocations> { | |
534 | arena_data: TypedArena<Vec<u8>>, | |
535 | arena_mmap: TypedArena<Mmap>, | |
536 | arena_relocations: TypedArena<Relocations>, | |
fc512014 | 537 | } |
fc512014 | 538 | |
a2a8927a XL |
539 | impl<Relocations> ThorinSession<Relocations> { |
540 | fn alloc_mmap<'arena>(&'arena self, data: Mmap) -> &'arena Mmap { | |
541 | (*self.arena_mmap.alloc(data)).borrow() | |
fc512014 | 542 | } |
a2a8927a | 543 | } |
fc512014 | 544 | |
a2a8927a XL |
545 | impl<Relocations> thorin::Session<Relocations> for ThorinSession<Relocations> { |
546 | fn alloc_data<'arena>(&'arena self, data: Vec<u8>) -> &'arena [u8] { | |
547 | (*self.arena_data.alloc(data)).borrow() | |
548 | } | |
fc512014 | 549 | |
a2a8927a XL |
550 | fn alloc_relocation<'arena>(&'arena self, data: Relocations) -> &'arena Relocations { |
551 | (*self.arena_relocations.alloc(data)).borrow() | |
552 | } | |
553 | ||
554 | fn read_input<'arena>(&'arena self, path: &Path) -> std::io::Result<&'arena [u8]> { | |
555 | let file = File::open(&path)?; | |
556 | let mmap = (unsafe { Mmap::map(file) })?; | |
557 | Ok(self.alloc_mmap(mmap)) | |
558 | } | |
559 | } | |
560 | ||
561 | match sess.time("run_thorin", || -> Result<(), thorin::Error> { | |
562 | let thorin_sess = ThorinSession::default(); | |
563 | let mut package = thorin::DwarfPackage::new(&thorin_sess); | |
564 | ||
565 | // Input objs contain .o/.dwo files from the current crate. | |
566 | match sess.opts.debugging_opts.split_dwarf_kind { | |
567 | SplitDwarfKind::Single => { | |
568 | for input_obj in cg_results.modules.iter().filter_map(|m| m.object.as_ref()) { | |
569 | package.add_input_object(input_obj)?; | |
570 | } | |
fc512014 | 571 | } |
a2a8927a XL |
572 | SplitDwarfKind::Split => { |
573 | for input_obj in cg_results.modules.iter().filter_map(|m| m.dwarf_object.as_ref()) { | |
574 | package.add_input_object(input_obj)?; | |
575 | } | |
576 | } | |
577 | } | |
fc512014 | 578 | |
a2a8927a XL |
579 | // Input rlibs contain .o/.dwo files from dependencies. |
580 | let input_rlibs = cg_results | |
581 | .crate_info | |
582 | .used_crate_source | |
583 | .values() | |
584 | .filter_map(|csource| csource.rlib.as_ref()) | |
585 | .map(|(path, _)| path); | |
586 | for input_rlib in input_rlibs { | |
587 | debug!(?input_rlib); | |
588 | package.add_input_object(input_rlib)?; | |
589 | } | |
590 | ||
591 | // Failing to read the referenced objects is expected for dependencies where the path in the | |
592 | // executable will have been cleaned by Cargo, but the referenced objects will be contained | |
593 | // within rlibs provided as inputs. | |
594 | // | |
595 | // If paths have been remapped, then .o/.dwo files from the current crate also won't be | |
596 | // found, but are provided explicitly above. | |
597 | // | |
598 | // Adding an executable is primarily done to make `thorin` check that all the referenced | |
599 | // dwarf objects are found in the end. | |
600 | package.add_executable( | |
601 | &executable_out_filename, | |
602 | thorin::MissingReferencedObjectBehaviour::Skip, | |
603 | )?; | |
604 | ||
605 | let output = package.finish()?.write()?; | |
606 | let mut output_stream = BufWriter::new( | |
607 | OpenOptions::new() | |
608 | .read(true) | |
609 | .write(true) | |
610 | .create(true) | |
611 | .truncate(true) | |
612 | .open(dwp_out_filename)?, | |
613 | ); | |
614 | output_stream.write_all(&output)?; | |
615 | output_stream.flush()?; | |
616 | ||
617 | Ok(()) | |
618 | }) { | |
619 | Ok(()) => {} | |
620 | Err(e) => { | |
621 | sess.struct_err("linking dwarf objects with thorin failed") | |
622 | .note(&format!("{:?}", e)) | |
623 | .emit(); | |
fc512014 XL |
624 | } |
625 | } | |
626 | } | |
627 | ||
628 | /// Create a dynamic library or executable. | |
629 | /// | |
630 | /// This will invoke the system linker/cc to create the resulting file. This links to all upstream | |
631 | /// files as well. | |
dfeec247 XL |
632 | fn link_natively<'a, B: ArchiveBuilder<'a>>( |
633 | sess: &'a Session, | |
f9f354fc | 634 | crate_type: CrateType, |
dfeec247 XL |
635 | out_filename: &Path, |
636 | codegen_results: &CodegenResults, | |
637 | tmpdir: &Path, | |
dfeec247 | 638 | ) { |
48663c56 | 639 | info!("preparing {:?} to {:?}", crate_type, out_filename); |
ba9703b0 XL |
640 | let (linker_path, flavor) = linker_and_flavor(sess); |
641 | let mut cmd = linker_with_args::<B>( | |
642 | &linker_path, | |
643 | flavor, | |
644 | sess, | |
645 | crate_type, | |
646 | tmpdir, | |
647 | out_filename, | |
648 | codegen_results, | |
ba9703b0 | 649 | ); |
48663c56 | 650 | |
f9f354fc XL |
651 | linker::disable_localization(&mut cmd); |
652 | ||
29967ef6 | 653 | for &(ref k, ref v) in &sess.target.link_env { |
48663c56 XL |
654 | cmd.env(k, v); |
655 | } | |
29967ef6 | 656 | for k in &sess.target.link_env_remove { |
e1599b0c XL |
657 | cmd.env_remove(k); |
658 | } | |
48663c56 | 659 | |
5099ac24 | 660 | if sess.opts.prints.contains(&PrintRequest::LinkArgs) { |
48663c56 XL |
661 | println!("{:?}", &cmd); |
662 | } | |
663 | ||
664 | // May have not found libraries in the right formats. | |
665 | sess.abort_if_errors(); | |
666 | ||
667 | // Invoke the system linker | |
48663c56 XL |
668 | info!("{:?}", &cmd); |
669 | let retry_on_segfault = env::var("RUSTC_RETRY_LINKER_ON_SEGFAULT").is_ok(); | |
94222f64 XL |
670 | let unknown_arg_regex = |
671 | Regex::new(r"(unknown|unrecognized) (command line )?(option|argument)").unwrap(); | |
48663c56 XL |
672 | let mut prog; |
673 | let mut i = 0; | |
674 | loop { | |
675 | i += 1; | |
ba9703b0 | 676 | prog = sess.time("run_linker", || exec_linker(sess, &cmd, out_filename, tmpdir)); |
48663c56 XL |
677 | let output = match prog { |
678 | Ok(ref output) => output, | |
679 | Err(_) => break, | |
680 | }; | |
681 | if output.status.success() { | |
dfeec247 | 682 | break; |
48663c56 XL |
683 | } |
684 | let mut out = output.stderr.clone(); | |
685 | out.extend(&output.stdout); | |
686 | let out = String::from_utf8_lossy(&out); | |
687 | ||
94222f64 XL |
688 | // Check to see if the link failed with an error message that indicates it |
689 | // doesn't recognize the -no-pie option. If so, reperform the link step | |
690 | // without it. This is safe because if the linker doesn't support -no-pie | |
691 | // then it should not default to linking executables as pie. Different | |
692 | // versions of gcc seem to use different quotes in the error message so | |
693 | // don't check for them. | |
29967ef6 | 694 | if sess.target.linker_is_gnu |
dfeec247 | 695 | && flavor != LinkerFlavor::Ld |
94222f64 | 696 | && unknown_arg_regex.is_match(&out) |
dfeec247 XL |
697 | && out.contains("-no-pie") |
698 | && cmd.get_args().iter().any(|e| e.to_string_lossy() == "-no-pie") | |
699 | { | |
48663c56 XL |
700 | info!("linker output: {:?}", out); |
701 | warn!("Linker does not support -no-pie command line option. Retrying without."); | |
702 | for arg in cmd.take_args() { | |
703 | if arg.to_string_lossy() != "-no-pie" { | |
704 | cmd.arg(arg); | |
705 | } | |
706 | } | |
707 | info!("{:?}", &cmd); | |
708 | continue; | |
709 | } | |
dc9dc135 | 710 | |
f035d41b XL |
711 | // Detect '-static-pie' used with an older version of gcc or clang not supporting it. |
712 | // Fallback from '-static-pie' to '-static' in that case. | |
29967ef6 | 713 | if sess.target.linker_is_gnu |
f035d41b | 714 | && flavor != LinkerFlavor::Ld |
94222f64 | 715 | && unknown_arg_regex.is_match(&out) |
f035d41b XL |
716 | && (out.contains("-static-pie") || out.contains("--no-dynamic-linker")) |
717 | && cmd.get_args().iter().any(|e| e.to_string_lossy() == "-static-pie") | |
718 | { | |
719 | info!("linker output: {:?}", out); | |
720 | warn!( | |
721 | "Linker does not support -static-pie command line option. Retrying with -static instead." | |
722 | ); | |
723 | // Mirror `add_(pre,post)_link_objects` to replace CRT objects. | |
724 | let self_contained = crt_objects_fallback(sess, crate_type); | |
29967ef6 | 725 | let opts = &sess.target; |
f035d41b XL |
726 | let pre_objects = if self_contained { |
727 | &opts.pre_link_objects_fallback | |
728 | } else { | |
729 | &opts.pre_link_objects | |
730 | }; | |
731 | let post_objects = if self_contained { | |
732 | &opts.post_link_objects_fallback | |
733 | } else { | |
734 | &opts.post_link_objects | |
735 | }; | |
736 | let get_objects = |objects: &CrtObjects, kind| { | |
737 | objects | |
738 | .get(&kind) | |
739 | .iter() | |
740 | .copied() | |
741 | .flatten() | |
742 | .map(|obj| get_object_file_path(sess, obj, self_contained).into_os_string()) | |
743 | .collect::<Vec<_>>() | |
744 | }; | |
745 | let pre_objects_static_pie = get_objects(pre_objects, LinkOutputKind::StaticPicExe); | |
746 | let post_objects_static_pie = get_objects(post_objects, LinkOutputKind::StaticPicExe); | |
747 | let mut pre_objects_static = get_objects(pre_objects, LinkOutputKind::StaticNoPicExe); | |
748 | let mut post_objects_static = get_objects(post_objects, LinkOutputKind::StaticNoPicExe); | |
749 | // Assume that we know insertion positions for the replacement arguments from replaced | |
750 | // arguments, which is true for all supported targets. | |
751 | assert!(pre_objects_static.is_empty() || !pre_objects_static_pie.is_empty()); | |
752 | assert!(post_objects_static.is_empty() || !post_objects_static_pie.is_empty()); | |
753 | for arg in cmd.take_args() { | |
754 | if arg.to_string_lossy() == "-static-pie" { | |
755 | // Replace the output kind. | |
756 | cmd.arg("-static"); | |
757 | } else if pre_objects_static_pie.contains(&arg) { | |
758 | // Replace the pre-link objects (replace the first and remove the rest). | |
759 | cmd.args(mem::take(&mut pre_objects_static)); | |
760 | } else if post_objects_static_pie.contains(&arg) { | |
761 | // Replace the post-link objects (replace the first and remove the rest). | |
762 | cmd.args(mem::take(&mut post_objects_static)); | |
763 | } else { | |
764 | cmd.arg(arg); | |
765 | } | |
766 | } | |
767 | info!("{:?}", &cmd); | |
768 | continue; | |
769 | } | |
770 | ||
dc9dc135 XL |
771 | // Here's a terribly awful hack that really shouldn't be present in any |
772 | // compiler. Here an environment variable is supported to automatically | |
773 | // retry the linker invocation if the linker looks like it segfaulted. | |
774 | // | |
775 | // Gee that seems odd, normally segfaults are things we want to know | |
776 | // about! Unfortunately though in rust-lang/rust#38878 we're | |
777 | // experiencing the linker segfaulting on Travis quite a bit which is | |
778 | // causing quite a bit of pain to land PRs when they spuriously fail | |
779 | // due to a segfault. | |
780 | // | |
781 | // The issue #38878 has some more debugging information on it as well, | |
782 | // but this unfortunately looks like it's just a race condition in | |
783 | // macOS's linker with some thread pool working in the background. It | |
784 | // seems that no one currently knows a fix for this so in the meantime | |
785 | // we're left with this... | |
48663c56 | 786 | if !retry_on_segfault || i > 3 { |
dfeec247 | 787 | break; |
48663c56 XL |
788 | } |
789 | let msg_segv = "clang: error: unable to execute command: Segmentation fault: 11"; | |
dfeec247 | 790 | let msg_bus = "clang: error: unable to execute command: Bus error: 10"; |
dc9dc135 XL |
791 | if out.contains(msg_segv) || out.contains(msg_bus) { |
792 | warn!( | |
c295e0f8 | 793 | ?cmd, %out, |
dc9dc135 | 794 | "looks like the linker segfaulted when we tried to call it, \ |
c295e0f8 | 795 | automatically retrying again", |
dc9dc135 XL |
796 | ); |
797 | continue; | |
48663c56 XL |
798 | } |
799 | ||
dc9dc135 XL |
800 | if is_illegal_instruction(&output.status) { |
801 | warn!( | |
c295e0f8 | 802 | ?cmd, %out, status = %output.status, |
dc9dc135 | 803 | "looks like the linker hit an illegal instruction when we \ |
c295e0f8 | 804 | tried to call it, automatically retrying again.", |
dc9dc135 XL |
805 | ); |
806 | continue; | |
807 | } | |
808 | ||
809 | #[cfg(unix)] | |
810 | fn is_illegal_instruction(status: &ExitStatus) -> bool { | |
811 | use std::os::unix::prelude::*; | |
812 | status.signal() == Some(libc::SIGILL) | |
813 | } | |
814 | ||
6a06907d | 815 | #[cfg(not(unix))] |
dc9dc135 XL |
816 | fn is_illegal_instruction(_status: &ExitStatus) -> bool { |
817 | false | |
818 | } | |
48663c56 XL |
819 | } |
820 | ||
821 | match prog { | |
822 | Ok(prog) => { | |
48663c56 XL |
823 | if !prog.status.success() { |
824 | let mut output = prog.stderr.clone(); | |
825 | output.extend_from_slice(&prog.stdout); | |
136023e0 XL |
826 | let escaped_output = escape_stdout_stderr_string(&output); |
827 | let mut err = sess.struct_err(&format!( | |
dfeec247 | 828 | "linking with `{}` failed: {}", |
ba9703b0 | 829 | linker_path.display(), |
dfeec247 | 830 | prog.status |
136023e0 XL |
831 | )); |
832 | err.note(&format!("{:?}", &cmd)).note(&escaped_output); | |
833 | if escaped_output.contains("undefined reference to") { | |
834 | err.help( | |
835 | "some `extern` functions couldn't be found; some native libraries may \ | |
836 | need to be installed or have their path specified", | |
837 | ); | |
838 | err.note("use the `-l` flag to specify native libraries to link"); | |
839 | err.note("use the `cargo:rustc-link-lib` directive to specify the native \ | |
840 | libraries to link with Cargo (see https://doc.rust-lang.org/cargo/reference/build-scripts.html#cargorustc-link-libkindname)"); | |
841 | } | |
842 | err.emit(); | |
f9f354fc XL |
843 | |
844 | // If MSVC's `link.exe` was expected but the return code | |
845 | // is not a Microsoft LNK error then suggest a way to fix or | |
846 | // install the Visual Studio build tools. | |
847 | if let Some(code) = prog.status.code() { | |
29967ef6 | 848 | if sess.target.is_like_msvc |
f9f354fc XL |
849 | && flavor == LinkerFlavor::Msvc |
850 | // Respect the command line override | |
851 | && sess.opts.cg.linker.is_none() | |
852 | // Match exactly "link.exe" | |
853 | && linker_path.to_str() == Some("link.exe") | |
854 | // All Microsoft `link.exe` linking error codes are | |
855 | // four digit numbers in the range 1000 to 9999 inclusive | |
856 | && (code < 1000 || code > 9999) | |
857 | { | |
858 | let is_vs_installed = windows_registry::find_vs_version().is_ok(); | |
859 | let has_linker = windows_registry::find_tool( | |
860 | &sess.opts.target_triple.triple(), | |
861 | "link.exe", | |
862 | ) | |
863 | .is_some(); | |
864 | ||
865 | sess.note_without_error("`link.exe` returned an unexpected error"); | |
866 | if is_vs_installed && has_linker { | |
867 | // the linker is broken | |
868 | sess.note_without_error( | |
869 | "the Visual Studio build tools may need to be repaired \ | |
870 | using the Visual Studio installer", | |
871 | ); | |
872 | sess.note_without_error( | |
873 | "or a necessary component may be missing from the \ | |
874 | \"C++ build tools\" workload", | |
875 | ); | |
876 | } else if is_vs_installed { | |
877 | // the linker is not installed | |
878 | sess.note_without_error( | |
879 | "in the Visual Studio installer, ensure the \ | |
880 | \"C++ build tools\" workload is selected", | |
881 | ); | |
882 | } else { | |
883 | // visual studio is not installed | |
884 | sess.note_without_error( | |
885 | "you may need to install Visual Studio build tools with the \ | |
886 | \"C++ build tools\" workload", | |
887 | ); | |
888 | } | |
889 | } | |
890 | } | |
891 | ||
48663c56 XL |
892 | sess.abort_if_errors(); |
893 | } | |
fc512014 XL |
894 | info!("linker stderr:\n{}", escape_stdout_stderr_string(&prog.stderr)); |
895 | info!("linker stdout:\n{}", escape_stdout_stderr_string(&prog.stdout)); | |
dfeec247 | 896 | } |
48663c56 XL |
897 | Err(e) => { |
898 | let linker_not_found = e.kind() == io::ErrorKind::NotFound; | |
899 | ||
900 | let mut linker_error = { | |
901 | if linker_not_found { | |
ba9703b0 | 902 | sess.struct_err(&format!("linker `{}` not found", linker_path.display())) |
48663c56 | 903 | } else { |
ba9703b0 XL |
904 | sess.struct_err(&format!( |
905 | "could not exec the linker `{}`", | |
906 | linker_path.display() | |
907 | )) | |
48663c56 XL |
908 | } |
909 | }; | |
910 | ||
911 | linker_error.note(&e.to_string()); | |
912 | ||
913 | if !linker_not_found { | |
914 | linker_error.note(&format!("{:?}", &cmd)); | |
915 | } | |
916 | ||
917 | linker_error.emit(); | |
918 | ||
29967ef6 | 919 | if sess.target.is_like_msvc && linker_not_found { |
416331ca XL |
920 | sess.note_without_error( |
921 | "the msvc targets depend on the msvc linker \ | |
922 | but `link.exe` was not found", | |
923 | ); | |
924 | sess.note_without_error( | |
5099ac24 | 925 | "please ensure that VS 2013, VS 2015, VS 2017, VS 2019 or VS 2022 \ |
416331ca XL |
926 | was installed with the Visual C++ option", |
927 | ); | |
48663c56 XL |
928 | } |
929 | sess.abort_if_errors(); | |
930 | } | |
931 | } | |
932 | ||
5869c6ff XL |
933 | match sess.split_debuginfo() { |
934 | // If split debug information is disabled or located in individual files | |
935 | // there's nothing to do here. | |
936 | SplitDebuginfo::Off | SplitDebuginfo::Unpacked => {} | |
937 | ||
938 | // If packed split-debuginfo is requested, but the final compilation | |
939 | // doesn't actually have any debug information, then we skip this step. | |
940 | SplitDebuginfo::Packed if sess.opts.debuginfo == DebugInfo::None => {} | |
941 | ||
942 | // On macOS the external `dsymutil` tool is used to create the packed | |
943 | // debug information. Note that this will read debug information from | |
944 | // the objects on the filesystem which we'll clean up later. | |
945 | SplitDebuginfo::Packed if sess.target.is_like_osx => { | |
946 | let prog = Command::new("dsymutil").arg(out_filename).output(); | |
947 | match prog { | |
948 | Ok(prog) => { | |
949 | if !prog.status.success() { | |
950 | let mut output = prog.stderr.clone(); | |
951 | output.extend_from_slice(&prog.stdout); | |
952 | sess.struct_warn(&format!( | |
953 | "processing debug info with `dsymutil` failed: {}", | |
954 | prog.status | |
955 | )) | |
956 | .note(&escape_string(&output)) | |
957 | .emit(); | |
958 | } | |
fc512014 | 959 | } |
5869c6ff | 960 | Err(e) => sess.fatal(&format!("unable to run `dsymutil`: {}", e)), |
fc512014 | 961 | } |
48663c56 | 962 | } |
5869c6ff XL |
963 | |
964 | // On MSVC packed debug information is produced by the linker itself so | |
965 | // there's no need to do anything else here. | |
966 | SplitDebuginfo::Packed if sess.target.is_like_msvc => {} | |
967 | ||
968 | // ... and otherwise we're processing a `*.dwp` packed dwarf file. | |
a2a8927a XL |
969 | // |
970 | // We cannot rely on the .o paths in the exectuable because they may have been | |
971 | // remapped by --remap-path-prefix and therefore invalid, so we need to provide | |
972 | // the .o/.dwo paths explicitly. | |
973 | SplitDebuginfo::Packed => link_dwarf_object(sess, codegen_results, out_filename), | |
48663c56 | 974 | } |
17df50a5 | 975 | |
3c0e092e XL |
976 | let strip = strip_value(sess); |
977 | ||
17df50a5 | 978 | if sess.target.is_like_osx { |
3c0e092e | 979 | match strip { |
c295e0f8 XL |
980 | Strip::Debuginfo => strip_symbols_in_osx(sess, &out_filename, Some("-S")), |
981 | Strip::Symbols => strip_symbols_in_osx(sess, &out_filename, None), | |
982 | Strip::None => {} | |
17df50a5 XL |
983 | } |
984 | } | |
985 | } | |
986 | ||
3c0e092e XL |
987 | // Temporarily support both -Z strip and -C strip |
988 | fn strip_value(sess: &Session) -> Strip { | |
989 | match (sess.opts.debugging_opts.strip, sess.opts.cg.strip) { | |
990 | (s, Strip::None) => s, | |
991 | (_, s) => s, | |
992 | } | |
993 | } | |
994 | ||
c295e0f8 XL |
995 | fn strip_symbols_in_osx<'a>(sess: &'a Session, out_filename: &Path, option: Option<&str>) { |
996 | let mut cmd = Command::new("strip"); | |
997 | if let Some(option) = option { | |
998 | cmd.arg(option); | |
999 | } | |
1000 | let prog = cmd.arg(out_filename).output(); | |
17df50a5 XL |
1001 | match prog { |
1002 | Ok(prog) => { | |
1003 | if !prog.status.success() { | |
1004 | let mut output = prog.stderr.clone(); | |
1005 | output.extend_from_slice(&prog.stdout); | |
1006 | sess.struct_warn(&format!( | |
1007 | "stripping debug info with `strip` failed: {}", | |
1008 | prog.status | |
1009 | )) | |
1010 | .note(&escape_string(&output)) | |
1011 | .emit(); | |
1012 | } | |
1013 | } | |
1014 | Err(e) => sess.fatal(&format!("unable to run `strip`: {}", e)), | |
1015 | } | |
1016 | } | |
1017 | ||
17df50a5 XL |
1018 | fn escape_string(s: &[u8]) -> String { |
1019 | str::from_utf8(s).map(|s| s.to_owned()).unwrap_or_else(|_| { | |
1020 | let mut x = "Non-UTF-8 output: ".to_string(); | |
1021 | x.extend(s.iter().flat_map(|&b| ascii::escape_default(b)).map(char::from)); | |
1022 | x | |
1023 | }) | |
48663c56 XL |
1024 | } |
1025 | ||
17df50a5 | 1026 | fn add_sanitizer_libraries(sess: &Session, crate_type: CrateType, linker: &mut dyn Linker) { |
3dfed10e XL |
1027 | // On macOS the runtimes are distributed as dylibs which should be linked to |
1028 | // both executables and dynamic shared objects. Everywhere else the runtimes | |
1029 | // are currently distributed as static liraries which should be linked to | |
1030 | // executables only. | |
1031 | let needs_runtime = match crate_type { | |
1032 | CrateType::Executable => true, | |
29967ef6 | 1033 | CrateType::Dylib | CrateType::Cdylib | CrateType::ProcMacro => sess.target.is_like_osx, |
3dfed10e XL |
1034 | CrateType::Rlib | CrateType::Staticlib => false, |
1035 | }; | |
1036 | ||
1037 | if !needs_runtime { | |
dfeec247 XL |
1038 | return; |
1039 | } | |
3dfed10e | 1040 | |
f035d41b XL |
1041 | let sanitizer = sess.opts.debugging_opts.sanitizer; |
1042 | if sanitizer.contains(SanitizerSet::ADDRESS) { | |
1043 | link_sanitizer_runtime(sess, linker, "asan"); | |
1044 | } | |
1045 | if sanitizer.contains(SanitizerSet::LEAK) { | |
1046 | link_sanitizer_runtime(sess, linker, "lsan"); | |
1047 | } | |
1048 | if sanitizer.contains(SanitizerSet::MEMORY) { | |
1049 | link_sanitizer_runtime(sess, linker, "msan"); | |
1050 | } | |
1051 | if sanitizer.contains(SanitizerSet::THREAD) { | |
1052 | link_sanitizer_runtime(sess, linker, "tsan"); | |
1053 | } | |
6a06907d XL |
1054 | if sanitizer.contains(SanitizerSet::HWADDRESS) { |
1055 | link_sanitizer_runtime(sess, linker, "hwasan"); | |
1056 | } | |
f035d41b | 1057 | } |
dfeec247 | 1058 | |
f035d41b | 1059 | fn link_sanitizer_runtime(sess: &Session, linker: &mut dyn Linker, name: &str) { |
3c0e092e | 1060 | fn find_sanitizer_runtime(sess: &Session, filename: &str) -> PathBuf { |
5869c6ff XL |
1061 | let session_tlib = |
1062 | filesearch::make_target_lib_path(&sess.sysroot, sess.opts.target_triple.triple()); | |
3c0e092e | 1063 | let path = session_tlib.join(filename); |
5869c6ff XL |
1064 | if path.exists() { |
1065 | return session_tlib; | |
1066 | } else { | |
1067 | let default_sysroot = filesearch::get_or_default_sysroot(); | |
1068 | let default_tlib = filesearch::make_target_lib_path( | |
1069 | &default_sysroot, | |
1070 | sess.opts.target_triple.triple(), | |
1071 | ); | |
1072 | return default_tlib; | |
1073 | } | |
1074 | } | |
1075 | ||
74b04a01 XL |
1076 | let channel = option_env!("CFG_RELEASE_CHANNEL") |
1077 | .map(|channel| format!("-{}", channel)) | |
1078 | .unwrap_or_default(); | |
dfeec247 | 1079 | |
cdc7bbd5 XL |
1080 | if sess.target.is_like_osx { |
1081 | // On Apple platforms, the sanitizer is always built as a dylib, and | |
1082 | // LLVM will link to `@rpath/*.dylib`, so we need to specify an | |
1083 | // rpath to the library as well (the rpath should be absolute, see | |
1084 | // PR #41352 for details). | |
1085 | let filename = format!("rustc{}_rt.{}", channel, name); | |
1086 | let path = find_sanitizer_runtime(&sess, &filename); | |
1087 | let rpath = path.to_str().expect("non-utf8 component in path"); | |
1088 | linker.args(&["-Wl,-rpath", "-Xlinker", rpath]); | |
17df50a5 | 1089 | linker.link_dylib(Symbol::intern(&filename), false, true); |
cdc7bbd5 XL |
1090 | } else { |
1091 | let filename = format!("librustc{}_rt.{}.a", channel, name); | |
1092 | let path = find_sanitizer_runtime(&sess, &filename).join(&filename); | |
1093 | linker.link_whole_rlib(&path); | |
dfeec247 XL |
1094 | } |
1095 | } | |
1096 | ||
a1dfa0c6 XL |
1097 | /// Returns a boolean indicating whether the specified crate should be ignored |
1098 | /// during LTO. | |
1099 | /// | |
1100 | /// Crates ignored during LTO are not lumped together in the "massive object | |
1101 | /// file" that we create and are linked in their normal rlib states. See | |
1102 | /// comments below for what crates do not participate in LTO. | |
1103 | /// | |
1104 | /// It's unusual for a crate to not participate in LTO. Typically only | |
1105 | /// compiler-specific and unstable crates have a reason to not participate in | |
1106 | /// LTO. | |
1107 | pub fn ignored_for_lto(sess: &Session, info: &CrateInfo, cnum: CrateNum) -> bool { | |
1108 | // If our target enables builtin function lowering in LLVM then the | |
1109 | // crates providing these functions don't participate in LTO (e.g. | |
1110 | // no_builtins or compiler builtins crates). | |
29967ef6 | 1111 | !sess.target.no_builtins |
dfeec247 | 1112 | && (info.compiler_builtins == Some(cnum) || info.is_no_builtins.contains(&cnum)) |
a1dfa0c6 XL |
1113 | } |
1114 | ||
17df50a5 XL |
1115 | // This functions tries to determine the appropriate linker (and corresponding LinkerFlavor) to use |
1116 | pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) { | |
a1dfa0c6 XL |
1117 | fn infer_from( |
1118 | sess: &Session, | |
1119 | linker: Option<PathBuf>, | |
1120 | flavor: Option<LinkerFlavor>, | |
1121 | ) -> Option<(PathBuf, LinkerFlavor)> { | |
1122 | match (linker, flavor) { | |
1123 | (Some(linker), Some(flavor)) => Some((linker, flavor)), | |
1124 | // only the linker flavor is known; use the default linker for the selected flavor | |
dfeec247 XL |
1125 | (None, Some(flavor)) => Some(( |
1126 | PathBuf::from(match flavor { | |
1127 | LinkerFlavor::Em => { | |
1128 | if cfg!(windows) { | |
1129 | "emcc.bat" | |
1130 | } else { | |
1131 | "emcc" | |
1132 | } | |
1133 | } | |
ba9703b0 XL |
1134 | LinkerFlavor::Gcc => { |
1135 | if cfg!(any(target_os = "solaris", target_os = "illumos")) { | |
1136 | // On historical Solaris systems, "cc" may have | |
1137 | // been Sun Studio, which is not flag-compatible | |
1138 | // with "gcc". This history casts a long shadow, | |
1139 | // and many modern illumos distributions today | |
1140 | // ship GCC as "gcc" without also making it | |
1141 | // available as "cc". | |
1142 | "gcc" | |
1143 | } else { | |
1144 | "cc" | |
1145 | } | |
1146 | } | |
dfeec247 XL |
1147 | LinkerFlavor::Ld => "ld", |
1148 | LinkerFlavor::Msvc => "link.exe", | |
1149 | LinkerFlavor::Lld(_) => "lld", | |
1150 | LinkerFlavor::PtxLinker => "rust-ptx-linker", | |
17df50a5 | 1151 | LinkerFlavor::BpfLinker => "bpf-linker", |
5099ac24 | 1152 | LinkerFlavor::L4Bender => "l4-bender", |
dfeec247 XL |
1153 | }), |
1154 | flavor, | |
1155 | )), | |
a1dfa0c6 | 1156 | (Some(linker), None) => { |
dfeec247 XL |
1157 | let stem = linker.file_stem().and_then(|stem| stem.to_str()).unwrap_or_else(|| { |
1158 | sess.fatal("couldn't extract file stem from specified linker") | |
1159 | }); | |
a1dfa0c6 XL |
1160 | |
1161 | let flavor = if stem == "emcc" { | |
1162 | LinkerFlavor::Em | |
532ac7d7 XL |
1163 | } else if stem == "gcc" |
1164 | || stem.ends_with("-gcc") | |
1165 | || stem == "clang" | |
1166 | || stem.ends_with("-clang") | |
1167 | { | |
a1dfa0c6 | 1168 | LinkerFlavor::Gcc |
17df50a5 XL |
1169 | } else if stem == "wasm-ld" || stem.ends_with("-wasm-ld") { |
1170 | LinkerFlavor::Lld(LldFlavor::Wasm) | |
a1dfa0c6 XL |
1171 | } else if stem == "ld" || stem == "ld.lld" || stem.ends_with("-ld") { |
1172 | LinkerFlavor::Ld | |
1173 | } else if stem == "link" || stem == "lld-link" { | |
1174 | LinkerFlavor::Msvc | |
1175 | } else if stem == "lld" || stem == "rust-lld" { | |
29967ef6 | 1176 | LinkerFlavor::Lld(sess.target.lld_flavor) |
a1dfa0c6 XL |
1177 | } else { |
1178 | // fall back to the value in the target spec | |
29967ef6 | 1179 | sess.target.linker_flavor |
a1dfa0c6 XL |
1180 | }; |
1181 | ||
1182 | Some((linker, flavor)) | |
dfeec247 | 1183 | } |
a1dfa0c6 XL |
1184 | (None, None) => None, |
1185 | } | |
1186 | } | |
1187 | ||
1188 | // linker and linker flavor specified via command line have precedence over what the target | |
1189 | // specification specifies | |
0731742a | 1190 | if let Some(ret) = infer_from(sess, sess.opts.cg.linker.clone(), sess.opts.cg.linker_flavor) { |
a1dfa0c6 XL |
1191 | return ret; |
1192 | } | |
1193 | ||
1194 | if let Some(ret) = infer_from( | |
1195 | sess, | |
29967ef6 XL |
1196 | sess.target.linker.clone().map(PathBuf::from), |
1197 | Some(sess.target.linker_flavor), | |
a1dfa0c6 XL |
1198 | ) { |
1199 | return ret; | |
1200 | } | |
1201 | ||
1202 | bug!("Not enough information provided to determine how to invoke the linker"); | |
1203 | } | |
48663c56 | 1204 | |
a2a8927a XL |
1205 | /// Returns a pair of boolean indicating whether we should preserve the object and |
1206 | /// dwarf object files on the filesystem for their debug information. This is often | |
1207 | /// useful with split-dwarf like schemes. | |
1208 | fn preserve_objects_for_their_debuginfo(sess: &Session) -> (bool, bool) { | |
48663c56 XL |
1209 | // If the objects don't have debuginfo there's nothing to preserve. |
1210 | if sess.opts.debuginfo == config::DebugInfo::None { | |
a2a8927a | 1211 | return (false, false); |
48663c56 XL |
1212 | } |
1213 | ||
1214 | // If we're only producing artifacts that are archives, no need to preserve | |
1215 | // the objects as they're losslessly contained inside the archives. | |
a2a8927a XL |
1216 | if sess.crate_types().iter().all(|&x| x.is_archive()) { |
1217 | return (false, false); | |
1218 | } | |
1219 | ||
1220 | match (sess.split_debuginfo(), sess.opts.debugging_opts.split_dwarf_kind) { | |
1221 | // If there is no split debuginfo then do not preserve objects. | |
1222 | (SplitDebuginfo::Off, _) => (false, false), | |
1223 | // If there is packed split debuginfo, then the debuginfo in the objects | |
1224 | // has been packaged and the objects can be deleted. | |
1225 | (SplitDebuginfo::Packed, _) => (false, false), | |
1226 | // If there is unpacked split debuginfo and the current target can not use | |
1227 | // split dwarf, then keep objects. | |
1228 | (SplitDebuginfo::Unpacked, _) if !sess.target_can_use_split_dwarf() => (true, false), | |
1229 | // If there is unpacked split debuginfo and the target can use split dwarf, then | |
1230 | // keep the object containing that debuginfo (whether that is an object file or | |
1231 | // dwarf object file depends on the split dwarf kind). | |
1232 | (SplitDebuginfo::Unpacked, SplitDwarfKind::Single) => (true, false), | |
1233 | (SplitDebuginfo::Unpacked, SplitDwarfKind::Split) => (false, true), | |
48663c56 | 1234 | } |
48663c56 XL |
1235 | } |
1236 | ||
c295e0f8 | 1237 | fn archive_search_paths(sess: &Session) -> Vec<PathBuf> { |
48663c56 XL |
1238 | sess.target_filesearch(PathKind::Native).search_path_dirs() |
1239 | } | |
1240 | ||
c295e0f8 | 1241 | #[derive(PartialEq)] |
48663c56 XL |
1242 | enum RlibFlavor { |
1243 | Normal, | |
1244 | StaticlibBase, | |
1245 | } | |
1246 | ||
f9f354fc | 1247 | fn print_native_static_libs(sess: &Session, all_native_libs: &[NativeLib]) { |
dfeec247 XL |
1248 | let lib_args: Vec<_> = all_native_libs |
1249 | .iter() | |
48663c56 XL |
1250 | .filter(|l| relevant_lib(sess, l)) |
1251 | .filter_map(|lib| { | |
1252 | let name = lib.name?; | |
1253 | match lib.kind { | |
17df50a5 XL |
1254 | NativeLibKind::Static { bundle: Some(false), .. } |
1255 | | NativeLibKind::Dylib { .. } | |
f9f354fc | 1256 | | NativeLibKind::Unspecified => { |
17df50a5 | 1257 | let verbatim = lib.verbatim.unwrap_or(false); |
29967ef6 | 1258 | if sess.target.is_like_msvc { |
17df50a5 XL |
1259 | Some(format!("{}{}", name, if verbatim { "" } else { ".lib" })) |
1260 | } else if sess.target.linker_is_gnu { | |
1261 | Some(format!("-l{}{}", if verbatim { ":" } else { "" }, name)) | |
48663c56 XL |
1262 | } else { |
1263 | Some(format!("-l{}", name)) | |
1264 | } | |
dfeec247 | 1265 | } |
17df50a5 | 1266 | NativeLibKind::Framework { .. } => { |
48663c56 XL |
1267 | // ld-only syntax, since there are no frameworks in MSVC |
1268 | Some(format!("-framework {}", name)) | |
dfeec247 | 1269 | } |
48663c56 | 1270 | // These are included, no need to print them |
17df50a5 XL |
1271 | NativeLibKind::Static { bundle: None | Some(true), .. } |
1272 | | NativeLibKind::RawDylib => None, | |
48663c56 XL |
1273 | } |
1274 | }) | |
1275 | .collect(); | |
1276 | if !lib_args.is_empty() { | |
dfeec247 XL |
1277 | sess.note_without_error( |
1278 | "Link against the following native artifacts when linking \ | |
48663c56 | 1279 | against this static library. The order and any duplication \ |
dfeec247 XL |
1280 | can be significant on some platforms.", |
1281 | ); | |
48663c56 XL |
1282 | // Prefix for greppability |
1283 | sess.note_without_error(&format!("native-static-libs: {}", &lib_args.join(" "))); | |
1284 | } | |
1285 | } | |
1286 | ||
f035d41b | 1287 | fn get_object_file_path(sess: &Session, name: &str, self_contained: bool) -> PathBuf { |
48663c56 XL |
1288 | let fs = sess.target_filesearch(PathKind::Native); |
1289 | let file_path = fs.get_lib_path().join(name); | |
1290 | if file_path.exists() { | |
dfeec247 | 1291 | return file_path; |
48663c56 | 1292 | } |
f035d41b XL |
1293 | // Special directory with objects used only in self-contained linkage mode |
1294 | if self_contained { | |
1295 | let file_path = fs.get_self_contained_lib_path().join(name); | |
1296 | if file_path.exists() { | |
1297 | return file_path; | |
1298 | } | |
1299 | } | |
48663c56 XL |
1300 | for search_path in fs.search_paths() { |
1301 | let file_path = search_path.dir.join(name); | |
1302 | if file_path.exists() { | |
dfeec247 | 1303 | return file_path; |
48663c56 XL |
1304 | } |
1305 | } | |
1306 | PathBuf::from(name) | |
1307 | } | |
1308 | ||
ba9703b0 | 1309 | fn exec_linker( |
dfeec247 | 1310 | sess: &Session, |
ba9703b0 | 1311 | cmd: &Command, |
dfeec247 XL |
1312 | out_filename: &Path, |
1313 | tmpdir: &Path, | |
1314 | ) -> io::Result<Output> { | |
48663c56 XL |
1315 | // When attempting to spawn the linker we run a risk of blowing out the |
1316 | // size limits for spawning a new process with respect to the arguments | |
1317 | // we pass on the command line. | |
1318 | // | |
1319 | // Here we attempt to handle errors from the OS saying "your list of | |
1320 | // arguments is too big" by reinvoking the linker again with an `@`-file | |
1321 | // that contains all the arguments. The theory is that this is then | |
1322 | // accepted on all linkers and the linker will read all its options out of | |
1323 | // there instead of looking at the command line. | |
1324 | if !cmd.very_likely_to_exceed_some_spawn_limit() { | |
1325 | match cmd.command().stdout(Stdio::piped()).stderr(Stdio::piped()).spawn() { | |
1326 | Ok(child) => { | |
1327 | let output = child.wait_with_output(); | |
1328 | flush_linked_file(&output, out_filename)?; | |
1329 | return output; | |
1330 | } | |
1331 | Err(ref e) if command_line_too_big(e) => { | |
1332 | info!("command line to linker was too big: {}", e); | |
1333 | } | |
dfeec247 | 1334 | Err(e) => return Err(e), |
48663c56 XL |
1335 | } |
1336 | } | |
1337 | ||
1338 | info!("falling back to passing arguments to linker via an @-file"); | |
1339 | let mut cmd2 = cmd.clone(); | |
1340 | let mut args = String::new(); | |
1341 | for arg in cmd2.take_args() { | |
dfeec247 | 1342 | args.push_str( |
29967ef6 XL |
1343 | &Escape { arg: arg.to_str().unwrap(), is_like_msvc: sess.target.is_like_msvc } |
1344 | .to_string(), | |
dfeec247 | 1345 | ); |
1b1a35ee | 1346 | args.push('\n'); |
48663c56 XL |
1347 | } |
1348 | let file = tmpdir.join("linker-arguments"); | |
29967ef6 | 1349 | let bytes = if sess.target.is_like_msvc { |
48663c56 XL |
1350 | let mut out = Vec::with_capacity((1 + args.len()) * 2); |
1351 | // start the stream with a UTF-16 BOM | |
1352 | for c in std::iter::once(0xFEFF).chain(args.encode_utf16()) { | |
1353 | // encode in little endian | |
1354 | out.push(c as u8); | |
1355 | out.push((c >> 8) as u8); | |
1356 | } | |
1357 | out | |
1358 | } else { | |
1359 | args.into_bytes() | |
1360 | }; | |
1361 | fs::write(&file, &bytes)?; | |
1362 | cmd2.arg(format!("@{}", file.display())); | |
1363 | info!("invoking linker {:?}", cmd2); | |
1364 | let output = cmd2.output(); | |
1365 | flush_linked_file(&output, out_filename)?; | |
1366 | return output; | |
1367 | ||
6a06907d | 1368 | #[cfg(not(windows))] |
48663c56 XL |
1369 | fn flush_linked_file(_: &io::Result<Output>, _: &Path) -> io::Result<()> { |
1370 | Ok(()) | |
1371 | } | |
1372 | ||
1373 | #[cfg(windows)] | |
dfeec247 XL |
1374 | fn flush_linked_file( |
1375 | command_output: &io::Result<Output>, | |
1376 | out_filename: &Path, | |
1377 | ) -> io::Result<()> { | |
48663c56 XL |
1378 | // On Windows, under high I/O load, output buffers are sometimes not flushed, |
1379 | // even long after process exit, causing nasty, non-reproducible output bugs. | |
1380 | // | |
1381 | // File::sync_all() calls FlushFileBuffers() down the line, which solves the problem. | |
1382 | // | |
1383 | // А full writeup of the original Chrome bug can be found at | |
1384 | // randomascii.wordpress.com/2018/02/25/compiler-bug-linker-bug-windows-kernel-bug/amp | |
1385 | ||
1386 | if let &Ok(ref out) = command_output { | |
1387 | if out.status.success() { | |
1388 | if let Ok(of) = fs::OpenOptions::new().write(true).open(out_filename) { | |
1389 | of.sync_all()?; | |
1390 | } | |
1391 | } | |
1392 | } | |
1393 | ||
1394 | Ok(()) | |
1395 | } | |
1396 | ||
1397 | #[cfg(unix)] | |
1398 | fn command_line_too_big(err: &io::Error) -> bool { | |
1399 | err.raw_os_error() == Some(::libc::E2BIG) | |
1400 | } | |
1401 | ||
1402 | #[cfg(windows)] | |
1403 | fn command_line_too_big(err: &io::Error) -> bool { | |
1404 | const ERROR_FILENAME_EXCED_RANGE: i32 = 206; | |
1405 | err.raw_os_error() == Some(ERROR_FILENAME_EXCED_RANGE) | |
1406 | } | |
1407 | ||
6a06907d XL |
1408 | #[cfg(not(any(unix, windows)))] |
1409 | fn command_line_too_big(_: &io::Error) -> bool { | |
1410 | false | |
1411 | } | |
1412 | ||
48663c56 XL |
1413 | struct Escape<'a> { |
1414 | arg: &'a str, | |
1415 | is_like_msvc: bool, | |
1416 | } | |
1417 | ||
1418 | impl<'a> fmt::Display for Escape<'a> { | |
1419 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
1420 | if self.is_like_msvc { | |
1421 | // This is "documented" at | |
dfeec247 | 1422 | // https://docs.microsoft.com/en-us/cpp/build/reference/at-specify-a-linker-response-file |
48663c56 XL |
1423 | // |
1424 | // Unfortunately there's not a great specification of the | |
1425 | // syntax I could find online (at least) but some local | |
1426 | // testing showed that this seemed sufficient-ish to catch | |
1427 | // at least a few edge cases. | |
1428 | write!(f, "\"")?; | |
1429 | for c in self.arg.chars() { | |
1430 | match c { | |
1431 | '"' => write!(f, "\\{}", c)?, | |
1432 | c => write!(f, "{}", c)?, | |
1433 | } | |
1434 | } | |
1435 | write!(f, "\"")?; | |
1436 | } else { | |
1437 | // This is documented at https://linux.die.net/man/1/ld, namely: | |
1438 | // | |
1439 | // > Options in file are separated by whitespace. A whitespace | |
1440 | // > character may be included in an option by surrounding the | |
1441 | // > entire option in either single or double quotes. Any | |
1442 | // > character (including a backslash) may be included by | |
1443 | // > prefixing the character to be included with a backslash. | |
1444 | // | |
1445 | // We put an argument on each line, so all we need to do is | |
1446 | // ensure the line is interpreted as one whole argument. | |
1447 | for c in self.arg.chars() { | |
1448 | match c { | |
1449 | '\\' | ' ' => write!(f, "\\{}", c)?, | |
1450 | c => write!(f, "{}", c)?, | |
1451 | } | |
1452 | } | |
1453 | } | |
1454 | Ok(()) | |
1455 | } | |
1456 | } | |
1457 | } | |
1458 | ||
f9f354fc XL |
1459 | fn link_output_kind(sess: &Session, crate_type: CrateType) -> LinkOutputKind { |
1460 | let kind = match (crate_type, sess.crt_static(Some(crate_type)), sess.relocation_model()) { | |
5869c6ff | 1461 | (CrateType::Executable, _, _) if sess.is_wasi_reactor() => LinkOutputKind::WasiReactorExe, |
c295e0f8 XL |
1462 | (CrateType::Executable, false, RelocModel::Pic | RelocModel::Pie) => { |
1463 | LinkOutputKind::DynamicPicExe | |
1464 | } | |
f9f354fc | 1465 | (CrateType::Executable, false, _) => LinkOutputKind::DynamicNoPicExe, |
c295e0f8 XL |
1466 | (CrateType::Executable, true, RelocModel::Pic | RelocModel::Pie) => { |
1467 | LinkOutputKind::StaticPicExe | |
1468 | } | |
f9f354fc XL |
1469 | (CrateType::Executable, true, _) => LinkOutputKind::StaticNoPicExe, |
1470 | (_, true, _) => LinkOutputKind::StaticDylib, | |
1471 | (_, false, _) => LinkOutputKind::DynamicDylib, | |
ba9703b0 | 1472 | }; |
f9f354fc XL |
1473 | |
1474 | // Adjust the output kind to target capabilities. | |
29967ef6 | 1475 | let opts = &sess.target; |
f9f354fc XL |
1476 | let pic_exe_supported = opts.position_independent_executables; |
1477 | let static_pic_exe_supported = opts.static_position_independent_executables; | |
1478 | let static_dylib_supported = opts.crt_static_allows_dylibs; | |
1479 | match kind { | |
1480 | LinkOutputKind::DynamicPicExe if !pic_exe_supported => LinkOutputKind::DynamicNoPicExe, | |
1481 | LinkOutputKind::StaticPicExe if !static_pic_exe_supported => LinkOutputKind::StaticNoPicExe, | |
1482 | LinkOutputKind::StaticDylib if !static_dylib_supported => LinkOutputKind::DynamicDylib, | |
1483 | _ => kind, | |
ba9703b0 | 1484 | } |
f9f354fc | 1485 | } |
ba9703b0 | 1486 | |
1b1a35ee XL |
1487 | // Returns true if linker is located within sysroot |
1488 | fn detect_self_contained_mingw(sess: &Session) -> bool { | |
1489 | let (linker, _) = linker_and_flavor(&sess); | |
1490 | // Assume `-C linker=rust-lld` as self-contained mode | |
1491 | if linker == Path::new("rust-lld") { | |
1492 | return true; | |
1493 | } | |
1494 | let linker_with_extension = if cfg!(windows) && linker.extension().is_none() { | |
1495 | linker.with_extension("exe") | |
1496 | } else { | |
1497 | linker | |
1498 | }; | |
1499 | for dir in env::split_paths(&env::var_os("PATH").unwrap_or_default()) { | |
1500 | let full_path = dir.join(&linker_with_extension); | |
1501 | // If linker comes from sysroot assume self-contained mode | |
1502 | if full_path.is_file() && !full_path.starts_with(&sess.sysroot) { | |
1503 | return false; | |
1504 | } | |
1505 | } | |
1506 | true | |
1507 | } | |
1508 | ||
f9f354fc XL |
1509 | /// Whether we link to our own CRT objects instead of relying on gcc to pull them. |
1510 | /// We only provide such support for a very limited number of targets. | |
1511 | fn crt_objects_fallback(sess: &Session, crate_type: CrateType) -> bool { | |
1b1a35ee | 1512 | if let Some(self_contained) = sess.opts.cg.link_self_contained { |
f035d41b XL |
1513 | return self_contained; |
1514 | } | |
1515 | ||
29967ef6 | 1516 | match sess.target.crt_objects_fallback { |
f9f354fc XL |
1517 | // FIXME: Find a better heuristic for "native musl toolchain is available", |
1518 | // based on host and linker path, for example. | |
1519 | // (https://github.com/rust-lang/rust/pull/71769#issuecomment-626330237). | |
1520 | Some(CrtObjectsFallback::Musl) => sess.crt_static(Some(crate_type)), | |
3dfed10e | 1521 | Some(CrtObjectsFallback::Mingw) => { |
29967ef6 XL |
1522 | sess.host == sess.target |
1523 | && sess.target.vendor != "uwp" | |
1b1a35ee | 1524 | && detect_self_contained_mingw(&sess) |
3dfed10e | 1525 | } |
f9f354fc XL |
1526 | // FIXME: Figure out cases in which WASM needs to link with a native toolchain. |
1527 | Some(CrtObjectsFallback::Wasm) => true, | |
1528 | None => false, | |
ba9703b0 XL |
1529 | } |
1530 | } | |
1531 | ||
f9f354fc XL |
1532 | /// Add pre-link object files defined by the target spec. |
1533 | fn add_pre_link_objects( | |
1534 | cmd: &mut dyn Linker, | |
1535 | sess: &Session, | |
1536 | link_output_kind: LinkOutputKind, | |
f035d41b | 1537 | self_contained: bool, |
f9f354fc | 1538 | ) { |
29967ef6 | 1539 | let opts = &sess.target; |
f035d41b XL |
1540 | let objects = |
1541 | if self_contained { &opts.pre_link_objects_fallback } else { &opts.pre_link_objects }; | |
f9f354fc | 1542 | for obj in objects.get(&link_output_kind).iter().copied().flatten() { |
f035d41b | 1543 | cmd.add_object(&get_object_file_path(sess, obj, self_contained)); |
ba9703b0 | 1544 | } |
f9f354fc XL |
1545 | } |
1546 | ||
1547 | /// Add post-link object files defined by the target spec. | |
1548 | fn add_post_link_objects( | |
1549 | cmd: &mut dyn Linker, | |
1550 | sess: &Session, | |
1551 | link_output_kind: LinkOutputKind, | |
f035d41b | 1552 | self_contained: bool, |
f9f354fc | 1553 | ) { |
29967ef6 | 1554 | let opts = &sess.target; |
f035d41b XL |
1555 | let objects = |
1556 | if self_contained { &opts.post_link_objects_fallback } else { &opts.post_link_objects }; | |
f9f354fc | 1557 | for obj in objects.get(&link_output_kind).iter().copied().flatten() { |
f035d41b | 1558 | cmd.add_object(&get_object_file_path(sess, obj, self_contained)); |
ba9703b0 XL |
1559 | } |
1560 | } | |
1561 | ||
1562 | /// Add arbitrary "pre-link" args defined by the target spec or from command line. | |
1563 | /// FIXME: Determine where exactly these args need to be inserted. | |
f035d41b | 1564 | fn add_pre_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) { |
29967ef6 | 1565 | if let Some(args) = sess.target.pre_link_args.get(&flavor) { |
ba9703b0 XL |
1566 | cmd.args(args); |
1567 | } | |
ba9703b0 XL |
1568 | cmd.args(&sess.opts.debugging_opts.pre_link_args); |
1569 | } | |
48663c56 | 1570 | |
f9f354fc XL |
1571 | /// Add a link script embedded in the target, if applicable. |
1572 | fn add_link_script(cmd: &mut dyn Linker, sess: &Session, tmpdir: &Path, crate_type: CrateType) { | |
29967ef6 | 1573 | match (crate_type, &sess.target.link_script) { |
f9f354fc | 1574 | (CrateType::Cdylib | CrateType::Executable, Some(script)) => { |
29967ef6 | 1575 | if !sess.target.linker_is_gnu { |
f9f354fc XL |
1576 | sess.fatal("can only use link script when linking with GNU-like linker"); |
1577 | } | |
1578 | ||
29967ef6 | 1579 | let file_name = ["rustc", &sess.target.llvm_target, "linkfile.ld"].join("-"); |
f9f354fc XL |
1580 | |
1581 | let path = tmpdir.join(file_name); | |
1582 | if let Err(e) = fs::write(&path, script) { | |
1583 | sess.fatal(&format!("failed to write link script to {}: {}", path.display(), e)); | |
1584 | } | |
1585 | ||
1586 | cmd.arg("--script"); | |
1587 | cmd.arg(path); | |
1588 | } | |
1589 | _ => {} | |
1590 | } | |
1591 | } | |
1592 | ||
cdc7bbd5 | 1593 | /// Add arbitrary "user defined" args defined from command line. |
ba9703b0 | 1594 | /// FIXME: Determine where exactly these args need to be inserted. |
cdc7bbd5 | 1595 | fn add_user_defined_link_args(cmd: &mut dyn Linker, sess: &Session) { |
ba9703b0 | 1596 | cmd.args(&sess.opts.cg.link_args); |
ba9703b0 | 1597 | } |
48663c56 | 1598 | |
ba9703b0 XL |
1599 | /// Add arbitrary "late link" args defined by the target spec. |
1600 | /// FIXME: Determine where exactly these args need to be inserted. | |
1601 | fn add_late_link_args( | |
1602 | cmd: &mut dyn Linker, | |
1603 | sess: &Session, | |
1604 | flavor: LinkerFlavor, | |
f9f354fc | 1605 | crate_type: CrateType, |
ba9703b0 XL |
1606 | codegen_results: &CodegenResults, |
1607 | ) { | |
f9f354fc | 1608 | let any_dynamic_crate = crate_type == CrateType::Dylib |
ba9703b0 XL |
1609 | || codegen_results.crate_info.dependency_formats.iter().any(|(ty, list)| { |
1610 | *ty == crate_type && list.iter().any(|&linkage| linkage == Linkage::Dynamic) | |
1611 | }); | |
1612 | if any_dynamic_crate { | |
29967ef6 | 1613 | if let Some(args) = sess.target.late_link_args_dynamic.get(&flavor) { |
ba9703b0 XL |
1614 | cmd.args(args); |
1615 | } | |
1616 | } else { | |
29967ef6 | 1617 | if let Some(args) = sess.target.late_link_args_static.get(&flavor) { |
ba9703b0 | 1618 | cmd.args(args); |
74b04a01 XL |
1619 | } |
1620 | } | |
29967ef6 | 1621 | if let Some(args) = sess.target.late_link_args.get(&flavor) { |
1b1a35ee XL |
1622 | cmd.args(args); |
1623 | } | |
ba9703b0 | 1624 | } |
74b04a01 | 1625 | |
ba9703b0 XL |
1626 | /// Add arbitrary "post-link" args defined by the target spec. |
1627 | /// FIXME: Determine where exactly these args need to be inserted. | |
1628 | fn add_post_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) { | |
29967ef6 | 1629 | if let Some(args) = sess.target.post_link_args.get(&flavor) { |
ba9703b0 XL |
1630 | cmd.args(args); |
1631 | } | |
1632 | } | |
e1599b0c | 1633 | |
ba9703b0 XL |
1634 | /// Add object files containing code from the current crate. |
1635 | fn add_local_crate_regular_objects(cmd: &mut dyn Linker, codegen_results: &CodegenResults) { | |
48663c56 XL |
1636 | for obj in codegen_results.modules.iter().filter_map(|m| m.object.as_ref()) { |
1637 | cmd.add_object(obj); | |
1638 | } | |
ba9703b0 | 1639 | } |
48663c56 | 1640 | |
ba9703b0 XL |
1641 | /// Add object files for allocator code linked once for the whole crate tree. |
1642 | fn add_local_crate_allocator_objects(cmd: &mut dyn Linker, codegen_results: &CodegenResults) { | |
1643 | if let Some(obj) = codegen_results.allocator_module.as_ref().and_then(|m| m.object.as_ref()) { | |
1644 | cmd.add_object(obj); | |
48663c56 | 1645 | } |
ba9703b0 | 1646 | } |
48663c56 | 1647 | |
ba9703b0 XL |
1648 | /// Add object files containing metadata for the current crate. |
1649 | fn add_local_crate_metadata_objects( | |
1650 | cmd: &mut dyn Linker, | |
f9f354fc | 1651 | crate_type: CrateType, |
ba9703b0 XL |
1652 | codegen_results: &CodegenResults, |
1653 | ) { | |
48663c56 XL |
1654 | // When linking a dynamic library, we put the metadata into a section of the |
1655 | // executable. This metadata is in a separate object file from the main | |
1656 | // object file, so we link that in here. | |
f9f354fc | 1657 | if crate_type == CrateType::Dylib || crate_type == CrateType::ProcMacro { |
ba9703b0 XL |
1658 | if let Some(obj) = codegen_results.metadata_module.as_ref().and_then(|m| m.object.as_ref()) |
1659 | { | |
48663c56 XL |
1660 | cmd.add_object(obj); |
1661 | } | |
1662 | } | |
ba9703b0 | 1663 | } |
48663c56 | 1664 | |
ba9703b0 | 1665 | /// Add sysroot and other globally set directories to the directory search list. |
f035d41b | 1666 | fn add_library_search_dirs(cmd: &mut dyn Linker, sess: &Session, self_contained: bool) { |
ba9703b0 XL |
1667 | // The default library location, we need this to find the runtime. |
1668 | // The location of crates will be determined as needed. | |
1669 | let lib_path = sess.target_filesearch(PathKind::All).get_lib_path(); | |
1670 | cmd.include_path(&fix_windows_verbatim_for_gcc(&lib_path)); | |
f035d41b XL |
1671 | |
1672 | // Special directory with libraries used only in self-contained linkage mode | |
1673 | if self_contained { | |
1674 | let lib_path = sess.target_filesearch(PathKind::All).get_self_contained_lib_path(); | |
1675 | cmd.include_path(&fix_windows_verbatim_for_gcc(&lib_path)); | |
1676 | } | |
ba9703b0 XL |
1677 | } |
1678 | ||
ba9703b0 XL |
1679 | /// Add options making relocation sections in the produced ELF files read-only |
1680 | /// and suppressing lazy binding. | |
1681 | fn add_relro_args(cmd: &mut dyn Linker, sess: &Session) { | |
29967ef6 | 1682 | match sess.opts.debugging_opts.relro_level.unwrap_or(sess.target.relro_level) { |
ba9703b0 XL |
1683 | RelroLevel::Full => cmd.full_relro(), |
1684 | RelroLevel::Partial => cmd.partial_relro(), | |
1685 | RelroLevel::Off => cmd.no_relro(), | |
1686 | RelroLevel::None => {} | |
74b04a01 | 1687 | } |
ba9703b0 | 1688 | } |
74b04a01 | 1689 | |
ba9703b0 XL |
1690 | /// Add library search paths used at runtime by dynamic linkers. |
1691 | fn add_rpath_args( | |
1692 | cmd: &mut dyn Linker, | |
1693 | sess: &Session, | |
1694 | codegen_results: &CodegenResults, | |
1695 | out_filename: &Path, | |
1696 | ) { | |
48663c56 XL |
1697 | // FIXME (#2397): At some point we want to rpath our guesses as to |
1698 | // where extern libraries might live, based on the | |
17df50a5 | 1699 | // add_lib_search_paths |
48663c56 | 1700 | if sess.opts.cg.rpath { |
136023e0 XL |
1701 | let libs = codegen_results |
1702 | .crate_info | |
1703 | .used_crates | |
1704 | .iter() | |
1705 | .filter_map(|cnum| { | |
1706 | codegen_results.crate_info.used_crate_source[cnum] | |
1707 | .dylib | |
1708 | .as_ref() | |
1709 | .map(|(path, _)| &**path) | |
1710 | }) | |
1711 | .collect::<Vec<_>>(); | |
48663c56 | 1712 | let mut rpath_config = RPathConfig { |
136023e0 | 1713 | libs: &*libs, |
48663c56 | 1714 | out_filename: out_filename.to_path_buf(), |
29967ef6 XL |
1715 | has_rpath: sess.target.has_rpath, |
1716 | is_like_osx: sess.target.is_like_osx, | |
1717 | linker_is_gnu: sess.target.linker_is_gnu, | |
48663c56 XL |
1718 | }; |
1719 | cmd.args(&rpath::get_rpath_flags(&mut rpath_config)); | |
1720 | } | |
ba9703b0 | 1721 | } |
48663c56 | 1722 | |
ba9703b0 | 1723 | /// Produce the linker command line containing linker path and arguments. |
17df50a5 XL |
1724 | /// |
1725 | /// When comments in the function say "order-(in)dependent" they mean order-dependence between | |
1726 | /// options and libraries/object files. For example `--whole-archive` (order-dependent) applies | |
1727 | /// to specific libraries passed after it, and `-o` (output file, order-independent) applies | |
1728 | /// to the linking process as a whole. | |
1729 | /// Order-independent options may still override each other in order-dependent fashion, | |
1730 | /// e.g `--foo=yes --foo=no` may be equivalent to `--foo=no`. | |
ba9703b0 XL |
1731 | fn linker_with_args<'a, B: ArchiveBuilder<'a>>( |
1732 | path: &Path, | |
1733 | flavor: LinkerFlavor, | |
1734 | sess: &'a Session, | |
f9f354fc | 1735 | crate_type: CrateType, |
ba9703b0 XL |
1736 | tmpdir: &Path, |
1737 | out_filename: &Path, | |
1738 | codegen_results: &CodegenResults, | |
ba9703b0 | 1739 | ) -> Command { |
f035d41b | 1740 | let crt_objects_fallback = crt_objects_fallback(sess, crate_type); |
136023e0 XL |
1741 | let cmd = &mut *super::linker::get_linker( |
1742 | sess, | |
1743 | path, | |
1744 | flavor, | |
1745 | crt_objects_fallback, | |
1746 | &codegen_results.crate_info.target_cpu, | |
1747 | ); | |
f9f354fc | 1748 | let link_output_kind = link_output_kind(sess, crate_type); |
ba9703b0 | 1749 | |
17df50a5 XL |
1750 | // ------------ Early order-dependent options ------------ |
1751 | ||
1752 | // If we're building something like a dynamic library then some platforms | |
1753 | // need to make sure that all symbols are exported correctly from the | |
1754 | // dynamic library. | |
1755 | // Must be passed before any libraries to prevent the symbols to export from being thrown away, | |
1756 | // at least on some platforms (e.g. windows-gnu). | |
136023e0 XL |
1757 | cmd.export_symbols( |
1758 | tmpdir, | |
1759 | crate_type, | |
1760 | &codegen_results.crate_info.exported_symbols[&crate_type], | |
1761 | ); | |
17df50a5 XL |
1762 | |
1763 | // Can be used for adding custom CRT objects or overriding order-dependent options above. | |
1764 | // FIXME: In practice built-in target specs use this for arbitrary order-independent options, | |
1765 | // introduce a target spec option for order-independent linker options and migrate built-in | |
1766 | // specs to it. | |
f035d41b | 1767 | add_pre_link_args(cmd, sess, flavor); |
ba9703b0 | 1768 | |
17df50a5 XL |
1769 | // ------------ Object code and libraries, order-dependent ------------ |
1770 | ||
1771 | // Pre-link CRT objects. | |
1772 | add_pre_link_objects(cmd, sess, link_output_kind, crt_objects_fallback); | |
1773 | ||
1774 | // Sanitizer libraries. | |
1775 | add_sanitizer_libraries(sess, crate_type, cmd); | |
1776 | ||
1777 | // Object code from the current crate. | |
1778 | // Take careful note of the ordering of the arguments we pass to the linker | |
1779 | // here. Linkers will assume that things on the left depend on things to the | |
1780 | // right. Things on the right cannot depend on things on the left. This is | |
1781 | // all formally implemented in terms of resolving symbols (libs on the right | |
1782 | // resolve unknown symbols of libs on the left, but not vice versa). | |
1783 | // | |
1784 | // For this reason, we have organized the arguments we pass to the linker as | |
1785 | // such: | |
1786 | // | |
1787 | // 1. The local object that LLVM just generated | |
1788 | // 2. Local native libraries | |
1789 | // 3. Upstream rust libraries | |
1790 | // 4. Upstream native libraries | |
1791 | // | |
1792 | // The rationale behind this ordering is that those items lower down in the | |
1793 | // list can't depend on items higher up in the list. For example nothing can | |
1794 | // depend on what we just generated (e.g., that'd be a circular dependency). | |
1795 | // Upstream rust libraries are not supposed to depend on our local native | |
1796 | // libraries as that would violate the structure of the DAG, in that | |
1797 | // scenario they are required to link to them as well in a shared fashion. | |
1798 | // (The current implementation still doesn't prevent it though, see the FIXME below.) | |
1799 | // | |
1800 | // Note that upstream rust libraries may contain native dependencies as | |
1801 | // well, but they also can't depend on what we just started to add to the | |
1802 | // link line. And finally upstream native libraries can't depend on anything | |
1803 | // in this DAG so far because they can only depend on other native libraries | |
1804 | // and such dependencies are also required to be specified. | |
1805 | add_local_crate_regular_objects(cmd, codegen_results); | |
1806 | add_local_crate_metadata_objects(cmd, crate_type, codegen_results); | |
1807 | add_local_crate_allocator_objects(cmd, codegen_results); | |
1808 | ||
1809 | // Avoid linking to dynamic libraries unless they satisfy some undefined symbols | |
1810 | // at the point at which they are specified on the command line. | |
1811 | // Must be passed before any (dynamic) libraries to have effect on them. | |
1812 | // On Solaris-like systems, `-z ignore` acts as both `--as-needed` and `--gc-sections` | |
1813 | // so it will ignore unreferenced ELF sections from relocatable objects. | |
1814 | // For that reason, we put this flag after metadata objects as they would otherwise be removed. | |
1815 | // FIXME: Support more fine-grained dead code removal on Solaris/illumos | |
1816 | // and move this option back to the top. | |
1817 | cmd.add_as_needed(); | |
1818 | ||
1819 | // FIXME: Move this below to other native libraries | |
1820 | // (or alternatively link all native libraries after their respective crates). | |
1821 | // This change is somewhat breaking in practice due to local static libraries being linked | |
1822 | // as whole-archive (#85144), so removing whole-archive may be a pre-requisite. | |
1823 | if sess.opts.debugging_opts.link_native_libraries { | |
1824 | add_local_native_libraries(cmd, sess, codegen_results); | |
1825 | } | |
1826 | ||
94222f64 | 1827 | // Upstream rust libraries and their nobundle static libraries |
17df50a5 XL |
1828 | add_upstream_rust_crates::<B>(cmd, sess, codegen_results, crate_type, tmpdir); |
1829 | ||
94222f64 XL |
1830 | // Upstream dymamic native libraries linked with `#[link]` attributes at and `-l` |
1831 | // command line options. | |
17df50a5 XL |
1832 | // If -Zlink-native-libraries=false is set, then the assumption is that an |
1833 | // external build system already has the native dependencies defined, and it | |
1834 | // will provide them to the linker itself. | |
1835 | if sess.opts.debugging_opts.link_native_libraries { | |
94222f64 | 1836 | add_upstream_native_libraries(cmd, sess, codegen_results); |
17df50a5 XL |
1837 | } |
1838 | ||
1839 | // Library linking above uses some global state for things like `-Bstatic`/`-Bdynamic` to make | |
1840 | // command line shorter, reset it to default here before adding more libraries. | |
1841 | cmd.reset_per_library_state(); | |
1842 | ||
1843 | // FIXME: Built-in target specs occasionally use this for linking system libraries, | |
1844 | // eliminate all such uses by migrating them to `#[link]` attributes in `lib(std,c,unwind)` | |
1845 | // and remove the option. | |
1846 | add_late_link_args(cmd, sess, flavor, crate_type, codegen_results); | |
1847 | ||
1848 | // ------------ Arbitrary order-independent options ------------ | |
1849 | ||
1850 | // Add order-independent options determined by rustc from its compiler options, | |
1851 | // target properties and source code. | |
1852 | add_order_independent_options( | |
1853 | cmd, | |
1854 | sess, | |
1855 | link_output_kind, | |
1856 | crt_objects_fallback, | |
1857 | flavor, | |
1858 | crate_type, | |
1859 | codegen_results, | |
1860 | out_filename, | |
1861 | tmpdir, | |
1862 | ); | |
1863 | ||
1864 | // Can be used for arbitrary order-independent options. | |
1865 | // In practice may also be occasionally used for linking native libraries. | |
1866 | // Passed after compiler-generated options to support manual overriding when necessary. | |
1867 | add_user_defined_link_args(cmd, sess); | |
1868 | ||
1869 | // ------------ Object code and libraries, order-dependent ------------ | |
1870 | ||
1871 | // Post-link CRT objects. | |
1872 | add_post_link_objects(cmd, sess, link_output_kind, crt_objects_fallback); | |
1873 | ||
1874 | // ------------ Late order-dependent options ------------ | |
1875 | ||
1876 | // Doesn't really make sense. | |
1877 | // FIXME: In practice built-in target specs use this for arbitrary order-independent options, | |
1878 | // introduce a target spec option for order-independent linker options, migrate built-in specs | |
1879 | // to it and remove the option. | |
1880 | add_post_link_args(cmd, sess, flavor); | |
1881 | ||
1882 | cmd.take_cmd() | |
1883 | } | |
1884 | ||
1885 | fn add_order_independent_options( | |
1886 | cmd: &mut dyn Linker, | |
1887 | sess: &Session, | |
1888 | link_output_kind: LinkOutputKind, | |
1889 | crt_objects_fallback: bool, | |
1890 | flavor: LinkerFlavor, | |
1891 | crate_type: CrateType, | |
1892 | codegen_results: &CodegenResults, | |
1893 | out_filename: &Path, | |
1894 | tmpdir: &Path, | |
1895 | ) { | |
1896 | add_gcc_ld_path(cmd, sess, flavor); | |
1897 | ||
1b1a35ee XL |
1898 | add_apple_sdk(cmd, sess, flavor); |
1899 | ||
f9f354fc XL |
1900 | add_link_script(cmd, sess, tmpdir, crate_type); |
1901 | ||
29967ef6 | 1902 | if sess.target.is_like_fuchsia && crate_type == CrateType::Executable { |
f035d41b XL |
1903 | let prefix = if sess.opts.debugging_opts.sanitizer.contains(SanitizerSet::ADDRESS) { |
1904 | "asan/" | |
1905 | } else { | |
1906 | "" | |
ba9703b0 XL |
1907 | }; |
1908 | cmd.arg(format!("--dynamic-linker={}ld.so.1", prefix)); | |
1909 | } | |
1910 | ||
29967ef6 | 1911 | if sess.target.eh_frame_header { |
f9652781 XL |
1912 | cmd.add_eh_frame_header(); |
1913 | } | |
f035d41b | 1914 | |
cdc7bbd5 XL |
1915 | // Make the binary compatible with data execution prevention schemes. |
1916 | cmd.add_no_exec(); | |
1917 | ||
f9f354fc XL |
1918 | if crt_objects_fallback { |
1919 | cmd.no_crt_objects(); | |
1920 | } | |
1921 | ||
29967ef6 | 1922 | if sess.target.is_like_emscripten { |
ba9703b0 XL |
1923 | cmd.arg("-s"); |
1924 | cmd.arg(if sess.panic_strategy() == PanicStrategy::Abort { | |
1925 | "DISABLE_EXCEPTION_CATCHING=1" | |
1926 | } else { | |
1927 | "DISABLE_EXCEPTION_CATCHING=0" | |
1928 | }); | |
1929 | } | |
1930 | ||
17df50a5 XL |
1931 | if flavor == LinkerFlavor::PtxLinker { |
1932 | // Provide the linker with fallback to internal `target-cpu`. | |
1933 | cmd.arg("--fallback-arch"); | |
136023e0 | 1934 | cmd.arg(&codegen_results.crate_info.target_cpu); |
17df50a5 XL |
1935 | } else if flavor == LinkerFlavor::BpfLinker { |
1936 | cmd.arg("--cpu"); | |
136023e0 | 1937 | cmd.arg(&codegen_results.crate_info.target_cpu); |
17df50a5 XL |
1938 | cmd.arg("--cpu-features"); |
1939 | cmd.arg(match &sess.opts.cg.target_feature { | |
1940 | feat if !feat.is_empty() => feat, | |
1941 | _ => &sess.target.options.features, | |
1942 | }); | |
1943 | } | |
ba9703b0 | 1944 | |
ba9703b0 XL |
1945 | cmd.linker_plugin_lto(); |
1946 | ||
f035d41b | 1947 | add_library_search_dirs(cmd, sess, crt_objects_fallback); |
ba9703b0 | 1948 | |
ba9703b0 XL |
1949 | cmd.output_filename(out_filename); |
1950 | ||
29967ef6 | 1951 | if crate_type == CrateType::Executable && sess.target.is_like_windows { |
17df50a5 | 1952 | if let Some(ref s) = codegen_results.crate_info.windows_subsystem { |
ba9703b0 XL |
1953 | cmd.subsystem(s); |
1954 | } | |
1955 | } | |
1956 | ||
ba9703b0 XL |
1957 | // Try to strip as much out of the generated object by removing unused |
1958 | // sections if possible. See more comments in linker.rs | |
1b1a35ee | 1959 | if !sess.link_dead_code() { |
136023e0 XL |
1960 | // If PGO is enabled sometimes gc_sections will remove the profile data section |
1961 | // as it appears to be unused. This can then cause the PGO profile file to lose | |
1962 | // some functions. If we are generating a profile we shouldn't strip those metadata | |
1963 | // sections to ensure we have all the data for PGO. | |
1964 | let keep_metadata = | |
1965 | crate_type == CrateType::Dylib || sess.opts.cg.profile_generate.enabled(); | |
ba9703b0 XL |
1966 | cmd.gc_sections(keep_metadata); |
1967 | } | |
1968 | ||
f9f354fc | 1969 | cmd.set_output_kind(link_output_kind, out_filename); |
ba9703b0 | 1970 | |
ba9703b0 XL |
1971 | add_relro_args(cmd, sess); |
1972 | ||
ba9703b0 XL |
1973 | // Pass optimization flags down to the linker. |
1974 | cmd.optimize(); | |
1975 | ||
f9f354fc | 1976 | // Pass debuginfo and strip flags down to the linker. |
3c0e092e | 1977 | cmd.debuginfo(strip_value(sess)); |
ba9703b0 | 1978 | |
ba9703b0 XL |
1979 | // We want to prevent the compiler from accidentally leaking in any system libraries, |
1980 | // so by default we tell linkers not to link to any default libraries. | |
29967ef6 | 1981 | if !sess.opts.cg.default_linker_libraries && sess.target.no_default_libraries { |
ba9703b0 XL |
1982 | cmd.no_default_libraries(); |
1983 | } | |
1984 | ||
cdc7bbd5 | 1985 | if sess.opts.cg.profile_generate.enabled() || sess.instrument_coverage() { |
ba9703b0 XL |
1986 | cmd.pgo_gen(); |
1987 | } | |
1988 | ||
3dfed10e | 1989 | if sess.opts.cg.control_flow_guard != CFGuard::Disabled { |
ba9703b0 XL |
1990 | cmd.control_flow_guard(); |
1991 | } | |
1992 | ||
ba9703b0 | 1993 | add_rpath_args(cmd, sess, codegen_results, out_filename); |
48663c56 XL |
1994 | } |
1995 | ||
fc512014 XL |
1996 | /// # Native library linking |
1997 | /// | |
1998 | /// User-supplied library search paths (-L on the command line). These are the same paths used to | |
1999 | /// find Rust crates, so some of them may have been added already by the previous crate linking | |
2000 | /// code. This only allows them to be found at compile time so it is still entirely up to outside | |
2001 | /// forces to make sure that library can be found at runtime. | |
2002 | /// | |
2003 | /// Also note that the native libraries linked here are only the ones located in the current crate. | |
2004 | /// Upstream crates with native library dependencies may have their native library pulled in above. | |
ba9703b0 | 2005 | fn add_local_native_libraries( |
dfeec247 XL |
2006 | cmd: &mut dyn Linker, |
2007 | sess: &Session, | |
2008 | codegen_results: &CodegenResults, | |
2009 | ) { | |
48663c56 XL |
2010 | let filesearch = sess.target_filesearch(PathKind::All); |
2011 | for search_path in filesearch.search_paths() { | |
2012 | match search_path.kind { | |
dfeec247 XL |
2013 | PathKind::Framework => { |
2014 | cmd.framework_path(&search_path.dir); | |
2015 | } | |
2016 | _ => { | |
2017 | cmd.include_path(&fix_windows_verbatim_for_gcc(&search_path.dir)); | |
2018 | } | |
48663c56 XL |
2019 | } |
2020 | } | |
2021 | ||
dfeec247 XL |
2022 | let relevant_libs = |
2023 | codegen_results.crate_info.used_libraries.iter().filter(|l| relevant_lib(sess, l)); | |
48663c56 | 2024 | |
c295e0f8 | 2025 | let search_path = OnceCell::new(); |
17df50a5 | 2026 | let mut last = (NativeLibKind::Unspecified, None); |
48663c56 XL |
2027 | for lib in relevant_libs { |
2028 | let name = match lib.name { | |
e1599b0c | 2029 | Some(l) => l, |
48663c56 XL |
2030 | None => continue, |
2031 | }; | |
17df50a5 XL |
2032 | |
2033 | // Skip if this library is the same as the last. | |
2034 | last = if (lib.kind, lib.name) == last { continue } else { (lib.kind, lib.name) }; | |
2035 | ||
2036 | let verbatim = lib.verbatim.unwrap_or(false); | |
48663c56 | 2037 | match lib.kind { |
17df50a5 XL |
2038 | NativeLibKind::Dylib { as_needed } => { |
2039 | cmd.link_dylib(name, verbatim, as_needed.unwrap_or(true)) | |
2040 | } | |
2041 | NativeLibKind::Unspecified => cmd.link_dylib(name, verbatim, true), | |
2042 | NativeLibKind::Framework { as_needed } => { | |
2043 | cmd.link_framework(name, as_needed.unwrap_or(true)) | |
2044 | } | |
2045 | NativeLibKind::Static { bundle: None | Some(true), .. } | |
2046 | | NativeLibKind::Static { whole_archive: Some(true), .. } => { | |
c295e0f8 XL |
2047 | cmd.link_whole_staticlib( |
2048 | name, | |
2049 | verbatim, | |
2050 | &search_path.get_or_init(|| archive_search_paths(sess)), | |
2051 | ); | |
17df50a5 XL |
2052 | } |
2053 | NativeLibKind::Static { .. } => cmd.link_staticlib(name, verbatim), | |
f9f354fc | 2054 | NativeLibKind::RawDylib => { |
e74abb32 XL |
2055 | // FIXME(#58713): Proper handling for raw dylibs. |
2056 | bug!("raw_dylib feature not yet implemented"); | |
dfeec247 | 2057 | } |
48663c56 XL |
2058 | } |
2059 | } | |
2060 | } | |
2061 | ||
94222f64 | 2062 | /// # Linking Rust crates and their nobundle static libraries |
fc512014 XL |
2063 | /// |
2064 | /// Rust crates are not considered at all when creating an rlib output. All dependencies will be | |
2065 | /// linked when producing the final output (instead of the intermediate rlib version). | |
e74abb32 XL |
2066 | fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>( |
2067 | cmd: &mut dyn Linker, | |
2068 | sess: &'a Session, | |
2069 | codegen_results: &CodegenResults, | |
f9f354fc | 2070 | crate_type: CrateType, |
e74abb32 XL |
2071 | tmpdir: &Path, |
2072 | ) { | |
48663c56 XL |
2073 | // All of the heavy lifting has previously been accomplished by the |
2074 | // dependency_format module of the compiler. This is just crawling the | |
2075 | // output of that module, adding crates as necessary. | |
2076 | // | |
2077 | // Linking to a rlib involves just passing it to the linker (the linker | |
2078 | // will slurp up the object files inside), and linking to a dynamic library | |
2079 | // involves just passing the right -l flag. | |
2080 | ||
dfeec247 XL |
2081 | let (_, data) = codegen_results |
2082 | .crate_info | |
2083 | .dependency_formats | |
e74abb32 XL |
2084 | .iter() |
2085 | .find(|(ty, _)| *ty == crate_type) | |
2086 | .expect("failed to find crate type in dependency format list"); | |
48663c56 XL |
2087 | |
2088 | // Invoke get_used_crates to ensure that we get a topological sorting of | |
2089 | // crates. | |
136023e0 | 2090 | let deps = &codegen_results.crate_info.used_crates; |
48663c56 XL |
2091 | |
2092 | // There's a few internal crates in the standard library (aka libcore and | |
2093 | // libstd) which actually have a circular dependence upon one another. This | |
2094 | // currently arises through "weak lang items" where libcore requires things | |
2095 | // like `rust_begin_unwind` but libstd ends up defining it. To get this | |
2096 | // circular dependence to work correctly in all situations we'll need to be | |
2097 | // sure to correctly apply the `--start-group` and `--end-group` options to | |
2098 | // GNU linkers, otherwise if we don't use any other symbol from the standard | |
2099 | // library it'll get discarded and the whole application won't link. | |
2100 | // | |
2101 | // In this loop we're calculating the `group_end`, after which crate to | |
2102 | // pass `--end-group` and `group_start`, before which crate to pass | |
2103 | // `--start-group`. We currently do this by passing `--end-group` after | |
2104 | // the first crate (when iterating backwards) that requires a lang item | |
2105 | // defined somewhere else. Once that's set then when we've defined all the | |
2106 | // necessary lang items we'll pass `--start-group`. | |
2107 | // | |
2108 | // Note that this isn't amazing logic for now but it should do the trick | |
2109 | // for the current implementation of the standard library. | |
2110 | let mut group_end = None; | |
2111 | let mut group_start = None; | |
74b04a01 XL |
2112 | // Crates available for linking thus far. |
2113 | let mut available = FxHashSet::default(); | |
2114 | // Crates required to satisfy dependencies discovered so far. | |
2115 | let mut required = FxHashSet::default(); | |
2116 | ||
48663c56 | 2117 | let info = &codegen_results.crate_info; |
136023e0 | 2118 | for &cnum in deps.iter().rev() { |
48663c56 | 2119 | if let Some(missing) = info.missing_lang_items.get(&cnum) { |
74b04a01 XL |
2120 | let missing_crates = missing.iter().map(|i| info.lang_item_to_crate.get(i).copied()); |
2121 | required.extend(missing_crates); | |
2122 | } | |
2123 | ||
2124 | required.insert(Some(cnum)); | |
2125 | available.insert(Some(cnum)); | |
2126 | ||
2127 | if required.len() > available.len() && group_end.is_none() { | |
2128 | group_end = Some(cnum); | |
48663c56 | 2129 | } |
74b04a01 | 2130 | if required.len() == available.len() && group_end.is_some() { |
48663c56 | 2131 | group_start = Some(cnum); |
dfeec247 | 2132 | break; |
48663c56 XL |
2133 | } |
2134 | } | |
2135 | ||
2136 | // If we didn't end up filling in all lang items from upstream crates then | |
2137 | // we'll be filling it in with our crate. This probably means we're the | |
2138 | // standard library itself, so skip this for now. | |
2139 | if group_end.is_some() && group_start.is_none() { | |
2140 | group_end = None; | |
2141 | } | |
2142 | ||
2143 | let mut compiler_builtins = None; | |
c295e0f8 | 2144 | let search_path = OnceCell::new(); |
48663c56 | 2145 | |
136023e0 | 2146 | for &cnum in deps.iter() { |
48663c56 XL |
2147 | if group_start == Some(cnum) { |
2148 | cmd.group_start(); | |
2149 | } | |
2150 | ||
2151 | // We may not pass all crates through to the linker. Some crates may | |
2152 | // appear statically in an existing dylib, meaning we'll pick up all the | |
2153 | // symbols from the dylib. | |
2154 | let src = &codegen_results.crate_info.used_crate_source[&cnum]; | |
2155 | match data[cnum.as_usize() - 1] { | |
2156 | _ if codegen_results.crate_info.profiler_runtime == Some(cnum) => { | |
2157 | add_static_crate::<B>(cmd, sess, codegen_results, tmpdir, crate_type, cnum); | |
2158 | } | |
48663c56 XL |
2159 | // compiler-builtins are always placed last to ensure that they're |
2160 | // linked correctly. | |
2161 | _ if codegen_results.crate_info.compiler_builtins == Some(cnum) => { | |
2162 | assert!(compiler_builtins.is_none()); | |
2163 | compiler_builtins = Some(cnum); | |
2164 | } | |
dfeec247 | 2165 | Linkage::NotLinked | Linkage::IncludedFromDylib => {} |
48663c56 XL |
2166 | Linkage::Static => { |
2167 | add_static_crate::<B>(cmd, sess, codegen_results, tmpdir, crate_type, cnum); | |
94222f64 XL |
2168 | |
2169 | // Link static native libs with "-bundle" modifier only if the crate they originate from | |
2170 | // is being linked statically to the current crate. If it's linked dynamically | |
2171 | // or is an rlib already included via some other dylib crate, the symbols from | |
2172 | // native libs will have already been included in that dylib. | |
2173 | // | |
2174 | // If -Zlink-native-libraries=false is set, then the assumption is that an | |
2175 | // external build system already has the native dependencies defined, and it | |
2176 | // will provide them to the linker itself. | |
2177 | if sess.opts.debugging_opts.link_native_libraries { | |
94222f64 XL |
2178 | let mut last = None; |
2179 | for lib in &codegen_results.crate_info.native_libraries[&cnum] { | |
c295e0f8 XL |
2180 | if !relevant_lib(sess, lib) { |
2181 | // Skip libraries if they are disabled by `#[link(cfg=...)]` | |
2182 | continue; | |
2183 | } | |
2184 | ||
2185 | // Skip if this library is the same as the last. | |
2186 | if last == lib.name { | |
2187 | continue; | |
2188 | } | |
2189 | ||
2190 | if let Some(static_lib_name) = lib.name { | |
2191 | if let NativeLibKind::Static { bundle: Some(false), whole_archive } = | |
2192 | lib.kind | |
2193 | { | |
2194 | let verbatim = lib.verbatim.unwrap_or(false); | |
2195 | if whole_archive == Some(true) { | |
2196 | cmd.link_whole_staticlib( | |
2197 | static_lib_name, | |
2198 | verbatim, | |
2199 | search_path.get_or_init(|| archive_search_paths(sess)), | |
2200 | ); | |
2201 | } else { | |
2202 | cmd.link_staticlib(static_lib_name, verbatim); | |
2203 | } | |
2204 | ||
2205 | last = lib.name; | |
2206 | } | |
94222f64 XL |
2207 | } |
2208 | } | |
2209 | } | |
48663c56 | 2210 | } |
dfeec247 | 2211 | Linkage::Dynamic => add_dynamic_crate(cmd, sess, &src.dylib.as_ref().unwrap().0), |
48663c56 XL |
2212 | } |
2213 | ||
2214 | if group_end == Some(cnum) { | |
2215 | cmd.group_end(); | |
2216 | } | |
2217 | } | |
2218 | ||
2219 | // compiler-builtins are always placed last to ensure that they're | |
2220 | // linked correctly. | |
2221 | // We must always link the `compiler_builtins` crate statically. Even if it | |
2222 | // was already "included" in a dylib (e.g., `libstd` when `-C prefer-dynamic` | |
2223 | // is used) | |
2224 | if let Some(cnum) = compiler_builtins { | |
2225 | add_static_crate::<B>(cmd, sess, codegen_results, tmpdir, crate_type, cnum); | |
2226 | } | |
2227 | ||
2228 | // Converts a library file-stem into a cc -l argument | |
29967ef6 XL |
2229 | fn unlib<'a>(target: &Target, stem: &'a str) -> &'a str { |
2230 | if stem.starts_with("lib") && !target.is_like_windows { &stem[3..] } else { stem } | |
48663c56 XL |
2231 | } |
2232 | ||
48663c56 | 2233 | // Adds the static "rlib" versions of all crates to the command line. |
17df50a5 XL |
2234 | // There's a bit of magic which happens here specifically related to LTO, |
2235 | // namely that we remove upstream object files. | |
48663c56 XL |
2236 | // |
2237 | // When performing LTO, almost(*) all of the bytecode from the upstream | |
2238 | // libraries has already been included in our object file output. As a | |
2239 | // result we need to remove the object files in the upstream libraries so | |
2240 | // the linker doesn't try to include them twice (or whine about duplicate | |
2241 | // symbols). We must continue to include the rest of the rlib, however, as | |
2242 | // it may contain static native libraries which must be linked in. | |
2243 | // | |
2244 | // (*) Crates marked with `#![no_builtins]` don't participate in LTO and | |
2245 | // their bytecode wasn't included. The object files in those libraries must | |
2246 | // still be passed to the linker. | |
2247 | // | |
17df50a5 XL |
2248 | // Note, however, that if we're not doing LTO we can just pass the rlib |
2249 | // blindly to the linker (fast) because it's fine if it's not actually | |
2250 | // included as we're at the end of the dependency chain. | |
dfeec247 XL |
2251 | fn add_static_crate<'a, B: ArchiveBuilder<'a>>( |
2252 | cmd: &mut dyn Linker, | |
2253 | sess: &'a Session, | |
2254 | codegen_results: &CodegenResults, | |
2255 | tmpdir: &Path, | |
f9f354fc | 2256 | crate_type: CrateType, |
dfeec247 XL |
2257 | cnum: CrateNum, |
2258 | ) { | |
48663c56 XL |
2259 | let src = &codegen_results.crate_info.used_crate_source[&cnum]; |
2260 | let cratepath = &src.rlib.as_ref().unwrap().0; | |
2261 | ||
17df50a5 XL |
2262 | let mut link_upstream = |path: &Path| { |
2263 | // If we're creating a dylib, then we need to include the | |
2264 | // whole of each object in our archive into that artifact. This is | |
2265 | // because a `dylib` can be reused as an intermediate artifact. | |
2266 | // | |
2267 | // Note, though, that we don't want to include the whole of a | |
2268 | // compiler-builtins crate (e.g., compiler-rt) because it'll get | |
2269 | // repeatedly linked anyway. | |
2270 | let path = fix_windows_verbatim_for_gcc(path); | |
2271 | if crate_type == CrateType::Dylib | |
2272 | && codegen_results.crate_info.compiler_builtins != Some(cnum) | |
2273 | { | |
2274 | cmd.link_whole_rlib(&path); | |
2275 | } else { | |
2276 | cmd.link_rlib(&path); | |
2277 | } | |
2278 | }; | |
2279 | ||
48663c56 XL |
2280 | // See the comment above in `link_staticlib` and `link_rlib` for why if |
2281 | // there's a static library that's not relevant we skip all object | |
2282 | // files. | |
2283 | let native_libs = &codegen_results.crate_info.native_libraries[&cnum]; | |
17df50a5 XL |
2284 | let skip_native = native_libs.iter().any(|lib| { |
2285 | matches!(lib.kind, NativeLibKind::Static { bundle: None | Some(true), .. }) | |
2286 | && !relevant_lib(sess, lib) | |
2287 | }); | |
dfeec247 XL |
2288 | |
2289 | if (!are_upstream_rust_objects_already_included(sess) | |
2290 | || ignored_for_lto(sess, &codegen_results.crate_info, cnum)) | |
dfeec247 XL |
2291 | && !skip_native |
2292 | { | |
17df50a5 | 2293 | link_upstream(cratepath); |
dfeec247 | 2294 | return; |
48663c56 XL |
2295 | } |
2296 | ||
2297 | let dst = tmpdir.join(cratepath.file_name().unwrap()); | |
2298 | let name = cratepath.file_name().unwrap().to_str().unwrap(); | |
2299 | let name = &name[3..name.len() - 5]; // chop off lib/.rlib | |
2300 | ||
74b04a01 | 2301 | sess.prof.generic_activity_with_arg("link_altering_rlib", name).run(|| { |
48663c56 | 2302 | let mut archive = <B as ArchiveBuilder>::new(sess, &dst, Some(cratepath)); |
48663c56 XL |
2303 | |
2304 | let mut any_objects = false; | |
2305 | for f in archive.src_files() { | |
f9f354fc | 2306 | if f == METADATA_FILENAME { |
48663c56 | 2307 | archive.remove_file(&f); |
dfeec247 | 2308 | continue; |
48663c56 XL |
2309 | } |
2310 | ||
a2a8927a XL |
2311 | let canonical = f.replace('-', "_"); |
2312 | let canonical_name = name.replace('-', "_"); | |
48663c56 | 2313 | |
48663c56 | 2314 | let is_rust_object = |
dfeec247 | 2315 | canonical.starts_with(&canonical_name) && looks_like_rust_object_file(&f); |
48663c56 XL |
2316 | |
2317 | // If we've been requested to skip all native object files | |
2318 | // (those not generated by the rust compiler) then we can skip | |
2319 | // this file. See above for why we may want to do this. | |
2320 | let skip_because_cfg_say_so = skip_native && !is_rust_object; | |
2321 | ||
2322 | // If we're performing LTO and this is a rust-generated object | |
2323 | // file, then we don't need the object file as it's part of the | |
2324 | // LTO module. Note that `#![no_builtins]` is excluded from LTO, | |
2325 | // though, so we let that object file slide. | |
dfeec247 XL |
2326 | let skip_because_lto = are_upstream_rust_objects_already_included(sess) |
2327 | && is_rust_object | |
29967ef6 | 2328 | && (sess.target.no_builtins |
dfeec247 | 2329 | || !codegen_results.crate_info.is_no_builtins.contains(&cnum)); |
48663c56 XL |
2330 | |
2331 | if skip_because_cfg_say_so || skip_because_lto { | |
2332 | archive.remove_file(&f); | |
2333 | } else { | |
2334 | any_objects = true; | |
2335 | } | |
2336 | } | |
2337 | ||
2338 | if !any_objects { | |
dfeec247 | 2339 | return; |
48663c56 XL |
2340 | } |
2341 | archive.build(); | |
17df50a5 | 2342 | link_upstream(&dst); |
48663c56 XL |
2343 | }); |
2344 | } | |
2345 | ||
2346 | // Same thing as above, but for dynamic crates instead of static crates. | |
2347 | fn add_dynamic_crate(cmd: &mut dyn Linker, sess: &Session, cratepath: &Path) { | |
2348 | // Just need to tell the linker about where the library lives and | |
2349 | // what its name is | |
2350 | let parent = cratepath.parent(); | |
2351 | if let Some(dir) = parent { | |
2352 | cmd.include_path(&fix_windows_verbatim_for_gcc(dir)); | |
2353 | } | |
2354 | let filestem = cratepath.file_stem().unwrap().to_str().unwrap(); | |
dfeec247 XL |
2355 | cmd.link_rust_dylib( |
2356 | Symbol::intern(&unlib(&sess.target, filestem)), | |
6a06907d | 2357 | parent.unwrap_or_else(|| Path::new("")), |
dfeec247 | 2358 | ); |
48663c56 XL |
2359 | } |
2360 | } | |
2361 | ||
fc512014 XL |
2362 | /// Link in all of our upstream crates' native dependencies. Remember that all of these upstream |
2363 | /// native dependencies are all non-static dependencies. We've got two cases then: | |
2364 | /// | |
2365 | /// 1. The upstream crate is an rlib. In this case we *must* link in the native dependency because | |
2366 | /// the rlib is just an archive. | |
2367 | /// | |
2368 | /// 2. The upstream crate is a dylib. In order to use the dylib, we have to have the dependency | |
2369 | /// present on the system somewhere. Thus, we don't gain a whole lot from not linking in the | |
2370 | /// dynamic dependency to this crate as well. | |
2371 | /// | |
2372 | /// The use case for this is a little subtle. In theory the native dependencies of a crate are | |
2373 | /// purely an implementation detail of the crate itself, but the problem arises with generic and | |
2374 | /// inlined functions. If a generic function calls a native function, then the generic function | |
2375 | /// must be instantiated in the target crate, meaning that the native symbol must also be resolved | |
2376 | /// in the target crate. | |
ba9703b0 | 2377 | fn add_upstream_native_libraries( |
e74abb32 XL |
2378 | cmd: &mut dyn Linker, |
2379 | sess: &Session, | |
2380 | codegen_results: &CodegenResults, | |
e74abb32 | 2381 | ) { |
17df50a5 | 2382 | let mut last = (NativeLibKind::Unspecified, None); |
94222f64 | 2383 | for &cnum in &codegen_results.crate_info.used_crates { |
48663c56 XL |
2384 | for lib in codegen_results.crate_info.native_libraries[&cnum].iter() { |
2385 | let name = match lib.name { | |
e1599b0c | 2386 | Some(l) => l, |
48663c56 XL |
2387 | None => continue, |
2388 | }; | |
2389 | if !relevant_lib(sess, &lib) { | |
dfeec247 | 2390 | continue; |
48663c56 | 2391 | } |
17df50a5 XL |
2392 | |
2393 | // Skip if this library is the same as the last. | |
2394 | last = if (lib.kind, lib.name) == last { continue } else { (lib.kind, lib.name) }; | |
2395 | ||
2396 | let verbatim = lib.verbatim.unwrap_or(false); | |
48663c56 | 2397 | match lib.kind { |
17df50a5 XL |
2398 | NativeLibKind::Dylib { as_needed } => { |
2399 | cmd.link_dylib(name, verbatim, as_needed.unwrap_or(true)) | |
2400 | } | |
2401 | NativeLibKind::Unspecified => cmd.link_dylib(name, verbatim, true), | |
2402 | NativeLibKind::Framework { as_needed } => { | |
2403 | cmd.link_framework(name, as_needed.unwrap_or(true)) | |
2404 | } | |
94222f64 XL |
2405 | // ignore static native libraries here as we've |
2406 | // already included them in add_local_native_libraries and | |
2407 | // add_upstream_rust_crates | |
2408 | NativeLibKind::Static { .. } => {} | |
17df50a5 | 2409 | NativeLibKind::RawDylib => {} |
48663c56 XL |
2410 | } |
2411 | } | |
2412 | } | |
2413 | } | |
2414 | ||
f9f354fc | 2415 | fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool { |
48663c56 | 2416 | match lib.cfg { |
74b04a01 | 2417 | Some(ref cfg) => rustc_attr::cfg_matches(cfg, &sess.parse_sess, None), |
48663c56 XL |
2418 | None => true, |
2419 | } | |
2420 | } | |
2421 | ||
ba9703b0 | 2422 | fn are_upstream_rust_objects_already_included(sess: &Session) -> bool { |
48663c56 XL |
2423 | match sess.lto() { |
2424 | config::Lto::Fat => true, | |
2425 | config::Lto::Thin => { | |
2426 | // If we defer LTO to the linker, we haven't run LTO ourselves, so | |
2427 | // any upstream object files have not been copied yet. | |
2428 | !sess.opts.cg.linker_plugin_lto.enabled() | |
2429 | } | |
dfeec247 | 2430 | config::Lto::No | config::Lto::ThinLocal => false, |
48663c56 XL |
2431 | } |
2432 | } | |
1b1a35ee XL |
2433 | |
2434 | fn add_apple_sdk(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) { | |
29967ef6 XL |
2435 | let arch = &sess.target.arch; |
2436 | let os = &sess.target.os; | |
2437 | let llvm_target = &sess.target.llvm_target; | |
2438 | if sess.target.vendor != "apple" | |
1b1a35ee XL |
2439 | || !matches!(os.as_str(), "ios" | "tvos") |
2440 | || flavor != LinkerFlavor::Gcc | |
2441 | { | |
2442 | return; | |
2443 | } | |
2444 | let sdk_name = match (arch.as_str(), os.as_str()) { | |
2445 | ("aarch64", "tvos") => "appletvos", | |
2446 | ("x86_64", "tvos") => "appletvsimulator", | |
2447 | ("arm", "ios") => "iphoneos", | |
fc512014 | 2448 | ("aarch64", "ios") if llvm_target.contains("macabi") => "macosx", |
6a06907d | 2449 | ("aarch64", "ios") if llvm_target.contains("sim") => "iphonesimulator", |
1b1a35ee XL |
2450 | ("aarch64", "ios") => "iphoneos", |
2451 | ("x86", "ios") => "iphonesimulator", | |
fc512014 | 2452 | ("x86_64", "ios") if llvm_target.contains("macabi") => "macosx", |
1b1a35ee XL |
2453 | ("x86_64", "ios") => "iphonesimulator", |
2454 | _ => { | |
2455 | sess.err(&format!("unsupported arch `{}` for os `{}`", arch, os)); | |
2456 | return; | |
2457 | } | |
2458 | }; | |
2459 | let sdk_root = match get_apple_sdk_root(sdk_name) { | |
2460 | Ok(s) => s, | |
2461 | Err(e) => { | |
2462 | sess.err(&e); | |
2463 | return; | |
2464 | } | |
2465 | }; | |
5869c6ff XL |
2466 | if llvm_target.contains("macabi") { |
2467 | cmd.args(&["-target", llvm_target]) | |
2468 | } else { | |
2469 | let arch_name = llvm_target.split('-').next().expect("LLVM target must have a hyphen"); | |
2470 | cmd.args(&["-arch", arch_name]) | |
2471 | } | |
2472 | cmd.args(&["-isysroot", &sdk_root, "-Wl,-syslibroot", &sdk_root]); | |
1b1a35ee XL |
2473 | } |
2474 | ||
2475 | fn get_apple_sdk_root(sdk_name: &str) -> Result<String, String> { | |
2476 | // Following what clang does | |
2477 | // (https://github.com/llvm/llvm-project/blob/ | |
2478 | // 296a80102a9b72c3eda80558fb78a3ed8849b341/clang/lib/Driver/ToolChains/Darwin.cpp#L1661-L1678) | |
2479 | // to allow the SDK path to be set. (For clang, xcrun sets | |
2480 | // SDKROOT; for rustc, the user or build system can set it, or we | |
2481 | // can fall back to checking for xcrun on PATH.) | |
2482 | if let Ok(sdkroot) = env::var("SDKROOT") { | |
2483 | let p = Path::new(&sdkroot); | |
2484 | match sdk_name { | |
2485 | // Ignore `SDKROOT` if it's clearly set for the wrong platform. | |
2486 | "appletvos" | |
2487 | if sdkroot.contains("TVSimulator.platform") | |
2488 | || sdkroot.contains("MacOSX.platform") => {} | |
2489 | "appletvsimulator" | |
2490 | if sdkroot.contains("TVOS.platform") || sdkroot.contains("MacOSX.platform") => {} | |
2491 | "iphoneos" | |
2492 | if sdkroot.contains("iPhoneSimulator.platform") | |
2493 | || sdkroot.contains("MacOSX.platform") => {} | |
2494 | "iphonesimulator" | |
2495 | if sdkroot.contains("iPhoneOS.platform") || sdkroot.contains("MacOSX.platform") => { | |
2496 | } | |
2497 | "macosx10.15" | |
2498 | if sdkroot.contains("iPhoneOS.platform") | |
2499 | || sdkroot.contains("iPhoneSimulator.platform") => {} | |
2500 | // Ignore `SDKROOT` if it's not a valid path. | |
2501 | _ if !p.is_absolute() || p == Path::new("/") || !p.exists() => {} | |
2502 | _ => return Ok(sdkroot), | |
2503 | } | |
2504 | } | |
2505 | let res = | |
2506 | Command::new("xcrun").arg("--show-sdk-path").arg("-sdk").arg(sdk_name).output().and_then( | |
2507 | |output| { | |
2508 | if output.status.success() { | |
2509 | Ok(String::from_utf8(output.stdout).unwrap()) | |
2510 | } else { | |
2511 | let error = String::from_utf8(output.stderr); | |
2512 | let error = format!("process exit with error: {}", error.unwrap()); | |
2513 | Err(io::Error::new(io::ErrorKind::Other, &error[..])) | |
2514 | } | |
2515 | }, | |
2516 | ); | |
2517 | ||
2518 | match res { | |
2519 | Ok(output) => Ok(output.trim().to_string()), | |
2520 | Err(e) => Err(format!("failed to get {} SDK path: {}", sdk_name, e)), | |
2521 | } | |
2522 | } | |
17df50a5 XL |
2523 | |
2524 | fn add_gcc_ld_path(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) { | |
2525 | if let Some(ld_impl) = sess.opts.debugging_opts.gcc_ld { | |
2526 | if let LinkerFlavor::Gcc = flavor { | |
2527 | match ld_impl { | |
2528 | LdImpl::Lld => { | |
94222f64 | 2529 | if sess.target.lld_flavor == LldFlavor::Ld64 { |
c295e0f8 | 2530 | let tools_path = sess.get_tools_search_paths(false); |
94222f64 XL |
2531 | let ld64_exe = tools_path |
2532 | .into_iter() | |
2533 | .map(|p| p.join("gcc-ld")) | |
2534 | .map(|p| { | |
2535 | p.join(if sess.host.is_like_windows { "ld64.exe" } else { "ld64" }) | |
2536 | }) | |
2537 | .find(|p| p.exists()) | |
2538 | .unwrap_or_else(|| sess.fatal("rust-lld (as ld64) not found")); | |
2539 | cmd.cmd().arg({ | |
2540 | let mut arg = OsString::from("-fuse-ld="); | |
2541 | arg.push(ld64_exe); | |
2542 | arg | |
2543 | }); | |
2544 | } else { | |
c295e0f8 | 2545 | let tools_path = sess.get_tools_search_paths(false); |
94222f64 XL |
2546 | let lld_path = tools_path |
2547 | .into_iter() | |
2548 | .map(|p| p.join("gcc-ld")) | |
2549 | .find(|p| { | |
2550 | p.join(if sess.host.is_like_windows { "ld.exe" } else { "ld" }) | |
2551 | .exists() | |
2552 | }) | |
2553 | .unwrap_or_else(|| sess.fatal("rust-lld (as ld) not found")); | |
2554 | cmd.cmd().arg({ | |
2555 | let mut arg = OsString::from("-B"); | |
2556 | arg.push(lld_path); | |
2557 | arg | |
2558 | }); | |
2559 | } | |
17df50a5 XL |
2560 | } |
2561 | } | |
2562 | } else { | |
2563 | sess.fatal("option `-Z gcc-ld` is used even though linker flavor is not gcc"); | |
2564 | } | |
2565 | } | |
2566 | } |