]>
Commit | Line | Data |
---|---|---|
e74abb32 XL |
1 | //! Resolution of mixing rlibs and dylibs |
2 | //! | |
3 | //! When producing a final artifact, such as a dynamic library, the compiler has | |
4 | //! a choice between linking an rlib or linking a dylib of all upstream | |
5 | //! dependencies. The linking phase must guarantee, however, that a library only | |
6 | //! show up once in the object file. For example, it is illegal for library A to | |
7 | //! be statically linked to B and C in separate dylibs, and then link B and C | |
8 | //! into a crate D (because library A appears twice). | |
9 | //! | |
10 | //! The job of this module is to calculate what format each upstream crate | |
11 | //! should be used when linking each output type requested in this session. This | |
12 | //! generally follows this set of rules: | |
13 | //! | |
14 | //! 1. Each library must appear exactly once in the output. | |
15 | //! 2. Each rlib contains only one library (it's just an object file) | |
16 | //! 3. Each dylib can contain more than one library (due to static linking), | |
17 | //! and can also bring in many dynamic dependencies. | |
18 | //! | |
19 | //! With these constraints in mind, it's generally a very difficult problem to | |
20 | //! find a solution that's not "all rlibs" or "all dylibs". I have suspicions | |
21 | //! that NP-ness may come into the picture here... | |
22 | //! | |
23 | //! The current selection algorithm below looks mostly similar to: | |
24 | //! | |
25 | //! 1. If static linking is required, then require all upstream dependencies | |
26 | //! to be available as rlibs. If not, generate an error. | |
27 | //! 2. If static linking is requested (generating an executable), then | |
28 | //! attempt to use all upstream dependencies as rlibs. If any are not | |
29 | //! found, bail out and continue to step 3. | |
30 | //! 3. Static linking has failed, at least one library must be dynamically | |
31 | //! linked. Apply a heuristic by greedily maximizing the number of | |
32 | //! dynamically linked libraries. | |
33 | //! 4. Each upstream dependency available as a dynamic library is | |
34 | //! registered. The dependencies all propagate, adding to a map. It is | |
35 | //! possible for a dylib to add a static library as a dependency, but it | |
36 | //! is illegal for two dylibs to add the same static library as a | |
37 | //! dependency. The same dylib can be added twice. Additionally, it is | |
38 | //! illegal to add a static dependency when it was previously found as a | |
39 | //! dylib (and vice versa) | |
40 | //! 5. After all dynamic dependencies have been traversed, re-traverse the | |
41 | //! remaining dependencies and add them statically (if they haven't been | |
42 | //! added already). | |
43 | //! | |
44 | //! While not perfect, this algorithm should help support use-cases such as leaf | |
45 | //! dependencies being static while the larger tree of inner dependencies are | |
46 | //! all dynamic. This isn't currently very well battle tested, so it will likely | |
47 | //! fall short in some use cases. | |
48 | //! | |
49 | //! Currently, there is no way to specify the preference of linkage with a | |
50 | //! particular library (other than a global dynamic/static switch). | |
51 | //! Additionally, the algorithm is geared towards finding *any* solution rather | |
52 | //! than finding a number of solutions (there are normally quite a few). | |
53 | ||
60c5eb7d XL |
54 | use crate::creader::CStore; |
55 | ||
dfeec247 XL |
56 | use rustc_data_structures::fx::FxHashMap; |
57 | use rustc_hir::def_id::CrateNum; | |
136023e0 | 58 | use rustc_middle::middle::cstore::CrateDepKind; |
ba9703b0 | 59 | use rustc_middle::middle::cstore::LinkagePreference::{self, RequireDynamic, RequireStatic}; |
ba9703b0 XL |
60 | use rustc_middle::middle::dependency_format::{Dependencies, DependencyList, Linkage}; |
61 | use rustc_middle::ty::TyCtxt; | |
f9f354fc | 62 | use rustc_session::config::CrateType; |
e74abb32 XL |
63 | use rustc_target::spec::PanicStrategy; |
64 | ||
65 | crate fn calculate(tcx: TyCtxt<'_>) -> Dependencies { | |
dfeec247 | 66 | tcx.sess |
f9f354fc | 67 | .crate_types() |
dfeec247 XL |
68 | .iter() |
69 | .map(|&ty| { | |
70 | let linkage = calculate_type(tcx, ty); | |
71 | verify_ok(tcx, &linkage); | |
72 | (ty, linkage) | |
73 | }) | |
74 | .collect::<Vec<_>>() | |
e74abb32 XL |
75 | } |
76 | ||
f9f354fc | 77 | fn calculate_type(tcx: TyCtxt<'_>, ty: CrateType) -> DependencyList { |
e74abb32 XL |
78 | let sess = &tcx.sess; |
79 | ||
80 | if !sess.opts.output_types.should_codegen() { | |
81 | return Vec::new(); | |
82 | } | |
83 | ||
84 | let preferred_linkage = match ty { | |
e74abb32 XL |
85 | // Generating a dylib without `-C prefer-dynamic` means that we're going |
86 | // to try to eagerly statically link all dependencies. This is normally | |
87 | // done for end-product dylibs, not intermediate products. | |
dfeec247 XL |
88 | // |
89 | // Treat cdylibs similarly. If `-C prefer-dynamic` is set, the caller may | |
90 | // be code-size conscious, but without it, it makes sense to statically | |
91 | // link a cdylib. | |
f9f354fc XL |
92 | CrateType::Dylib | CrateType::Cdylib if !sess.opts.cg.prefer_dynamic => Linkage::Static, |
93 | CrateType::Dylib | CrateType::Cdylib => Linkage::Dynamic, | |
e74abb32 XL |
94 | |
95 | // If the global prefer_dynamic switch is turned off, or the final | |
96 | // executable will be statically linked, prefer static crate linkage. | |
f9f354fc | 97 | CrateType::Executable if !sess.opts.cg.prefer_dynamic || sess.crt_static(Some(ty)) => { |
dfeec247 XL |
98 | Linkage::Static |
99 | } | |
f9f354fc | 100 | CrateType::Executable => Linkage::Dynamic, |
e74abb32 XL |
101 | |
102 | // proc-macro crates are mostly cdylibs, but we also need metadata. | |
f9f354fc | 103 | CrateType::ProcMacro => Linkage::Static, |
e74abb32 XL |
104 | |
105 | // No linkage happens with rlibs, we just needed the metadata (which we | |
106 | // got long ago), so don't bother with anything. | |
f9f354fc | 107 | CrateType::Rlib => Linkage::NotLinked, |
e74abb32 XL |
108 | |
109 | // staticlibs must have all static dependencies. | |
f9f354fc | 110 | CrateType::Staticlib => Linkage::Static, |
e74abb32 XL |
111 | }; |
112 | ||
113 | if preferred_linkage == Linkage::NotLinked { | |
114 | // If the crate is not linked, there are no link-time dependencies. | |
115 | return Vec::new(); | |
116 | } | |
117 | ||
118 | if preferred_linkage == Linkage::Static { | |
119 | // Attempt static linkage first. For dylibs and executables, we may be | |
120 | // able to retry below with dynamic linkage. | |
121 | if let Some(v) = attempt_static(tcx) { | |
122 | return v; | |
123 | } | |
124 | ||
dfeec247 XL |
125 | // Staticlibs and static executables must have all static dependencies. |
126 | // If any are not found, generate some nice pretty errors. | |
f9f354fc XL |
127 | if ty == CrateType::Staticlib |
128 | || (ty == CrateType::Executable | |
ba9703b0 | 129 | && sess.crt_static(Some(ty)) |
29967ef6 | 130 | && !sess.target.crt_static_allows_dylibs) |
dfeec247 | 131 | { |
136023e0 | 132 | for &cnum in tcx.crates(()).iter() { |
dfeec247 XL |
133 | if tcx.dep_kind(cnum).macros_only() { |
134 | continue; | |
135 | } | |
e74abb32 | 136 | let src = tcx.used_crate_source(cnum); |
dfeec247 XL |
137 | if src.rlib.is_some() { |
138 | continue; | |
139 | } | |
140 | sess.err(&format!( | |
141 | "crate `{}` required to be available in rlib format, \ | |
e74abb32 | 142 | but was not found in this form", |
dfeec247 XL |
143 | tcx.crate_name(cnum) |
144 | )); | |
e74abb32 XL |
145 | } |
146 | return Vec::new(); | |
147 | } | |
148 | } | |
149 | ||
150 | let mut formats = FxHashMap::default(); | |
151 | ||
152 | // Sweep all crates for found dylibs. Add all dylibs, as well as their | |
153 | // dependencies, ensuring there are no conflicts. The only valid case for a | |
154 | // dependency to be relied upon twice is for both cases to rely on a dylib. | |
136023e0 | 155 | for &cnum in tcx.crates(()).iter() { |
dfeec247 XL |
156 | if tcx.dep_kind(cnum).macros_only() { |
157 | continue; | |
158 | } | |
e74abb32 XL |
159 | let name = tcx.crate_name(cnum); |
160 | let src = tcx.used_crate_source(cnum); | |
161 | if src.dylib.is_some() { | |
3dfed10e | 162 | tracing::info!("adding dylib: {}", name); |
e74abb32 XL |
163 | add_library(tcx, cnum, RequireDynamic, &mut formats); |
164 | let deps = tcx.dylib_dependency_formats(cnum); | |
165 | for &(depnum, style) in deps.iter() { | |
3dfed10e | 166 | tracing::info!("adding {:?}: {}", style, tcx.crate_name(depnum)); |
e74abb32 XL |
167 | add_library(tcx, depnum, style, &mut formats); |
168 | } | |
169 | } | |
170 | } | |
171 | ||
172 | // Collect what we've got so far in the return vector. | |
136023e0 | 173 | let last_crate = tcx.crates(()).len(); |
dfeec247 XL |
174 | let mut ret = (1..last_crate + 1) |
175 | .map(|cnum| match formats.get(&CrateNum::new(cnum)) { | |
e74abb32 XL |
176 | Some(&RequireDynamic) => Linkage::Dynamic, |
177 | Some(&RequireStatic) => Linkage::IncludedFromDylib, | |
178 | None => Linkage::NotLinked, | |
dfeec247 XL |
179 | }) |
180 | .collect::<Vec<_>>(); | |
e74abb32 XL |
181 | |
182 | // Run through the dependency list again, and add any missing libraries as | |
183 | // static libraries. | |
184 | // | |
185 | // If the crate hasn't been included yet and it's not actually required | |
186 | // (e.g., it's an allocator) then we skip it here as well. | |
136023e0 | 187 | for &cnum in tcx.crates(()).iter() { |
e74abb32 | 188 | let src = tcx.used_crate_source(cnum); |
dfeec247 XL |
189 | if src.dylib.is_none() |
190 | && !formats.contains_key(&cnum) | |
3dfed10e | 191 | && tcx.dep_kind(cnum) == CrateDepKind::Explicit |
dfeec247 | 192 | { |
e74abb32 | 193 | assert!(src.rlib.is_some() || src.rmeta.is_some()); |
3dfed10e | 194 | tracing::info!("adding staticlib: {}", tcx.crate_name(cnum)); |
e74abb32 XL |
195 | add_library(tcx, cnum, RequireStatic, &mut formats); |
196 | ret[cnum.as_usize() - 1] = Linkage::Static; | |
197 | } | |
198 | } | |
199 | ||
200 | // We've gotten this far because we're emitting some form of a final | |
201 | // artifact which means that we may need to inject dependencies of some | |
202 | // form. | |
203 | // | |
204 | // Things like allocators and panic runtimes may not have been activated | |
205 | // quite yet, so do so here. | |
dfeec247 XL |
206 | activate_injected_dep(CStore::from_tcx(tcx).injected_panic_runtime(), &mut ret, &|cnum| { |
207 | tcx.is_panic_runtime(cnum) | |
208 | }); | |
e74abb32 XL |
209 | |
210 | // When dylib B links to dylib A, then when using B we must also link to A. | |
211 | // It could be the case, however, that the rlib for A is present (hence we | |
212 | // found metadata), but the dylib for A has since been removed. | |
213 | // | |
214 | // For situations like this, we perform one last pass over the dependencies, | |
215 | // making sure that everything is available in the requested format. | |
216 | for (cnum, kind) in ret.iter().enumerate() { | |
217 | let cnum = CrateNum::new(cnum + 1); | |
218 | let src = tcx.used_crate_source(cnum); | |
219 | match *kind { | |
dfeec247 | 220 | Linkage::NotLinked | Linkage::IncludedFromDylib => {} |
e74abb32 XL |
221 | Linkage::Static if src.rlib.is_some() => continue, |
222 | Linkage::Dynamic if src.dylib.is_some() => continue, | |
223 | kind => { | |
224 | let kind = match kind { | |
225 | Linkage::Static => "rlib", | |
226 | _ => "dylib", | |
227 | }; | |
dfeec247 XL |
228 | sess.err(&format!( |
229 | "crate `{}` required to be available in {} format, \ | |
e74abb32 | 230 | but was not found in this form", |
dfeec247 XL |
231 | tcx.crate_name(cnum), |
232 | kind | |
233 | )); | |
e74abb32 XL |
234 | } |
235 | } | |
236 | } | |
237 | ||
238 | ret | |
239 | } | |
240 | ||
241 | fn add_library( | |
242 | tcx: TyCtxt<'_>, | |
243 | cnum: CrateNum, | |
244 | link: LinkagePreference, | |
245 | m: &mut FxHashMap<CrateNum, LinkagePreference>, | |
246 | ) { | |
247 | match m.get(&cnum) { | |
248 | Some(&link2) => { | |
249 | // If the linkages differ, then we'd have two copies of the library | |
250 | // if we continued linking. If the linkages are both static, then we | |
251 | // would also have two copies of the library (static from two | |
252 | // different locations). | |
253 | // | |
254 | // This error is probably a little obscure, but I imagine that it | |
255 | // can be refined over time. | |
256 | if link2 != link || link == RequireStatic { | |
dfeec247 XL |
257 | tcx.sess |
258 | .struct_err(&format!( | |
259 | "cannot satisfy dependencies so `{}` only \ | |
260 | shows up once", | |
261 | tcx.crate_name(cnum) | |
262 | )) | |
263 | .help( | |
264 | "having upstream crates all available in one format \ | |
265 | will likely make this go away", | |
266 | ) | |
e74abb32 XL |
267 | .emit(); |
268 | } | |
269 | } | |
dfeec247 XL |
270 | None => { |
271 | m.insert(cnum, link); | |
272 | } | |
e74abb32 XL |
273 | } |
274 | } | |
275 | ||
276 | fn attempt_static(tcx: TyCtxt<'_>) -> Option<DependencyList> { | |
136023e0 XL |
277 | let all_crates_available_as_rlib = tcx |
278 | .crates(()) | |
279 | .iter() | |
280 | .cloned() | |
281 | .filter_map(|cnum| { | |
282 | if tcx.dep_kind(cnum).macros_only() { | |
283 | return None; | |
284 | } | |
285 | Some(tcx.used_crate_source(cnum).rlib.is_some()) | |
286 | }) | |
287 | .all(|is_rlib| is_rlib); | |
288 | if !all_crates_available_as_rlib { | |
dfeec247 | 289 | return None; |
e74abb32 XL |
290 | } |
291 | ||
292 | // All crates are available in an rlib format, so we're just going to link | |
293 | // everything in explicitly so long as it's actually required. | |
136023e0 | 294 | let last_crate = tcx.crates(()).len(); |
dfeec247 XL |
295 | let mut ret = (1..last_crate + 1) |
296 | .map(|cnum| { | |
3dfed10e | 297 | if tcx.dep_kind(CrateNum::new(cnum)) == CrateDepKind::Explicit { |
dfeec247 XL |
298 | Linkage::Static |
299 | } else { | |
300 | Linkage::NotLinked | |
301 | } | |
302 | }) | |
303 | .collect::<Vec<_>>(); | |
e74abb32 XL |
304 | |
305 | // Our allocator/panic runtime may not have been linked above if it wasn't | |
306 | // explicitly linked, which is the case for any injected dependency. Handle | |
307 | // that here and activate them. | |
dfeec247 XL |
308 | activate_injected_dep(CStore::from_tcx(tcx).injected_panic_runtime(), &mut ret, &|cnum| { |
309 | tcx.is_panic_runtime(cnum) | |
310 | }); | |
e74abb32 XL |
311 | |
312 | Some(ret) | |
313 | } | |
314 | ||
315 | // Given a list of how to link upstream dependencies so far, ensure that an | |
316 | // injected dependency is activated. This will not do anything if one was | |
317 | // transitively included already (e.g., via a dylib or explicitly so). | |
318 | // | |
319 | // If an injected dependency was not found then we're guaranteed the | |
320 | // metadata::creader module has injected that dependency (not listed as | |
321 | // a required dependency) in one of the session's field. If this field is not | |
322 | // set then this compilation doesn't actually need the dependency and we can | |
323 | // also skip this step entirely. | |
dfeec247 XL |
324 | fn activate_injected_dep( |
325 | injected: Option<CrateNum>, | |
326 | list: &mut DependencyList, | |
327 | replaces_injected: &dyn Fn(CrateNum) -> bool, | |
328 | ) { | |
e74abb32 XL |
329 | for (i, slot) in list.iter().enumerate() { |
330 | let cnum = CrateNum::new(i + 1); | |
331 | if !replaces_injected(cnum) { | |
dfeec247 | 332 | continue; |
e74abb32 XL |
333 | } |
334 | if *slot != Linkage::NotLinked { | |
dfeec247 | 335 | return; |
e74abb32 XL |
336 | } |
337 | } | |
338 | if let Some(injected) = injected { | |
339 | let idx = injected.as_usize() - 1; | |
340 | assert_eq!(list[idx], Linkage::NotLinked); | |
341 | list[idx] = Linkage::Static; | |
342 | } | |
343 | } | |
344 | ||
345 | // After the linkage for a crate has been determined we need to verify that | |
346 | // there's only going to be one allocator in the output. | |
347 | fn verify_ok(tcx: TyCtxt<'_>, list: &[Linkage]) { | |
348 | let sess = &tcx.sess; | |
74b04a01 | 349 | if list.is_empty() { |
dfeec247 | 350 | return; |
e74abb32 XL |
351 | } |
352 | let mut panic_runtime = None; | |
353 | for (i, linkage) in list.iter().enumerate() { | |
354 | if let Linkage::NotLinked = *linkage { | |
dfeec247 | 355 | continue; |
e74abb32 XL |
356 | } |
357 | let cnum = CrateNum::new(i + 1); | |
358 | ||
359 | if tcx.is_panic_runtime(cnum) { | |
360 | if let Some((prev, _)) = panic_runtime { | |
361 | let prev_name = tcx.crate_name(prev); | |
362 | let cur_name = tcx.crate_name(cnum); | |
dfeec247 XL |
363 | sess.err(&format!( |
364 | "cannot link together two \ | |
e74abb32 | 365 | panic runtimes: {} and {}", |
dfeec247 XL |
366 | prev_name, cur_name |
367 | )); | |
e74abb32 XL |
368 | } |
369 | panic_runtime = Some((cnum, tcx.panic_strategy(cnum))); | |
370 | } | |
371 | } | |
372 | ||
373 | // If we found a panic runtime, then we know by this point that it's the | |
374 | // only one, but we perform validation here that all the panic strategy | |
375 | // compilation modes for the whole DAG are valid. | |
376 | if let Some((cnum, found_strategy)) = panic_runtime { | |
377 | let desired_strategy = sess.panic_strategy(); | |
378 | ||
379 | // First up, validate that our selected panic runtime is indeed exactly | |
380 | // our same strategy. | |
381 | if found_strategy != desired_strategy { | |
dfeec247 XL |
382 | sess.err(&format!( |
383 | "the linked panic runtime `{}` is \ | |
e74abb32 XL |
384 | not compiled with this crate's \ |
385 | panic strategy `{}`", | |
dfeec247 XL |
386 | tcx.crate_name(cnum), |
387 | desired_strategy.desc() | |
388 | )); | |
e74abb32 XL |
389 | } |
390 | ||
391 | // Next up, verify that all other crates are compatible with this panic | |
392 | // strategy. If the dep isn't linked, we ignore it, and if our strategy | |
393 | // is abort then it's compatible with everything. Otherwise all crates' | |
394 | // panic strategy must match our own. | |
395 | for (i, linkage) in list.iter().enumerate() { | |
396 | if let Linkage::NotLinked = *linkage { | |
dfeec247 | 397 | continue; |
e74abb32 XL |
398 | } |
399 | if desired_strategy == PanicStrategy::Abort { | |
dfeec247 | 400 | continue; |
e74abb32 XL |
401 | } |
402 | let cnum = CrateNum::new(i + 1); | |
403 | let found_strategy = tcx.panic_strategy(cnum); | |
404 | let is_compiler_builtins = tcx.is_compiler_builtins(cnum); | |
405 | if is_compiler_builtins || desired_strategy == found_strategy { | |
dfeec247 | 406 | continue; |
e74abb32 XL |
407 | } |
408 | ||
dfeec247 XL |
409 | sess.err(&format!( |
410 | "the crate `{}` is compiled with the \ | |
e74abb32 XL |
411 | panic strategy `{}` which is \ |
412 | incompatible with this crate's \ | |
413 | strategy of `{}`", | |
dfeec247 XL |
414 | tcx.crate_name(cnum), |
415 | found_strategy.desc(), | |
416 | desired_strategy.desc() | |
417 | )); | |
e74abb32 XL |
418 | } |
419 | } | |
420 | } |