]>
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 | ||
e74abb32 XL |
56 | use rustc::hir::def_id::CrateNum; |
57 | use rustc::middle::cstore::LinkagePreference::{self, RequireStatic, RequireDynamic}; | |
58 | use rustc::middle::cstore::{self, DepKind}; | |
59 | use rustc::middle::dependency_format::{DependencyList, Dependencies, Linkage}; | |
60 | use rustc::session::config; | |
61 | use rustc::ty::TyCtxt; | |
62 | use rustc::util::nodemap::FxHashMap; | |
63 | use rustc_target::spec::PanicStrategy; | |
64 | ||
65 | crate fn calculate(tcx: TyCtxt<'_>) -> Dependencies { | |
66 | tcx.sess.crate_types.borrow().iter().map(|&ty| { | |
67 | let linkage = calculate_type(tcx, ty); | |
68 | verify_ok(tcx, &linkage); | |
69 | (ty, linkage) | |
70 | }).collect::<Vec<_>>() | |
71 | } | |
72 | ||
73 | fn calculate_type(tcx: TyCtxt<'_>, ty: config::CrateType) -> DependencyList { | |
74 | let sess = &tcx.sess; | |
75 | ||
76 | if !sess.opts.output_types.should_codegen() { | |
77 | return Vec::new(); | |
78 | } | |
79 | ||
80 | let preferred_linkage = match ty { | |
81 | // cdylibs must have all static dependencies. | |
82 | config::CrateType::Cdylib => Linkage::Static, | |
83 | ||
84 | // Generating a dylib without `-C prefer-dynamic` means that we're going | |
85 | // to try to eagerly statically link all dependencies. This is normally | |
86 | // done for end-product dylibs, not intermediate products. | |
87 | config::CrateType::Dylib if !sess.opts.cg.prefer_dynamic => Linkage::Static, | |
88 | config::CrateType::Dylib => Linkage::Dynamic, | |
89 | ||
90 | // If the global prefer_dynamic switch is turned off, or the final | |
91 | // executable will be statically linked, prefer static crate linkage. | |
92 | config::CrateType::Executable if !sess.opts.cg.prefer_dynamic || | |
93 | sess.crt_static() => Linkage::Static, | |
94 | config::CrateType::Executable => Linkage::Dynamic, | |
95 | ||
96 | // proc-macro crates are mostly cdylibs, but we also need metadata. | |
97 | config::CrateType::ProcMacro => Linkage::Static, | |
98 | ||
99 | // No linkage happens with rlibs, we just needed the metadata (which we | |
100 | // got long ago), so don't bother with anything. | |
101 | config::CrateType::Rlib => Linkage::NotLinked, | |
102 | ||
103 | // staticlibs must have all static dependencies. | |
104 | config::CrateType::Staticlib => Linkage::Static, | |
105 | }; | |
106 | ||
107 | if preferred_linkage == Linkage::NotLinked { | |
108 | // If the crate is not linked, there are no link-time dependencies. | |
109 | return Vec::new(); | |
110 | } | |
111 | ||
112 | if preferred_linkage == Linkage::Static { | |
113 | // Attempt static linkage first. For dylibs and executables, we may be | |
114 | // able to retry below with dynamic linkage. | |
115 | if let Some(v) = attempt_static(tcx) { | |
116 | return v; | |
117 | } | |
118 | ||
119 | // Staticlibs, cdylibs, and static executables must have all static | |
120 | // dependencies. If any are not found, generate some nice pretty errors. | |
121 | if ty == config::CrateType::Cdylib || ty == config::CrateType::Staticlib || | |
122 | (ty == config::CrateType::Executable && sess.crt_static() && | |
123 | !sess.target.target.options.crt_static_allows_dylibs) { | |
124 | for &cnum in tcx.crates().iter() { | |
125 | if tcx.dep_kind(cnum).macros_only() { continue } | |
126 | let src = tcx.used_crate_source(cnum); | |
127 | if src.rlib.is_some() { continue } | |
128 | sess.err(&format!("crate `{}` required to be available in rlib format, \ | |
129 | but was not found in this form", | |
130 | tcx.crate_name(cnum))); | |
131 | } | |
132 | return Vec::new(); | |
133 | } | |
134 | } | |
135 | ||
136 | let mut formats = FxHashMap::default(); | |
137 | ||
138 | // Sweep all crates for found dylibs. Add all dylibs, as well as their | |
139 | // dependencies, ensuring there are no conflicts. The only valid case for a | |
140 | // dependency to be relied upon twice is for both cases to rely on a dylib. | |
141 | for &cnum in tcx.crates().iter() { | |
142 | if tcx.dep_kind(cnum).macros_only() { continue } | |
143 | let name = tcx.crate_name(cnum); | |
144 | let src = tcx.used_crate_source(cnum); | |
145 | if src.dylib.is_some() { | |
146 | log::info!("adding dylib: {}", name); | |
147 | add_library(tcx, cnum, RequireDynamic, &mut formats); | |
148 | let deps = tcx.dylib_dependency_formats(cnum); | |
149 | for &(depnum, style) in deps.iter() { | |
150 | log::info!("adding {:?}: {}", style, tcx.crate_name(depnum)); | |
151 | add_library(tcx, depnum, style, &mut formats); | |
152 | } | |
153 | } | |
154 | } | |
155 | ||
156 | // Collect what we've got so far in the return vector. | |
157 | let last_crate = tcx.crates().len(); | |
158 | let mut ret = (1..last_crate+1).map(|cnum| { | |
159 | match formats.get(&CrateNum::new(cnum)) { | |
160 | Some(&RequireDynamic) => Linkage::Dynamic, | |
161 | Some(&RequireStatic) => Linkage::IncludedFromDylib, | |
162 | None => Linkage::NotLinked, | |
163 | } | |
164 | }).collect::<Vec<_>>(); | |
165 | ||
166 | // Run through the dependency list again, and add any missing libraries as | |
167 | // static libraries. | |
168 | // | |
169 | // If the crate hasn't been included yet and it's not actually required | |
170 | // (e.g., it's an allocator) then we skip it here as well. | |
171 | for &cnum in tcx.crates().iter() { | |
172 | let src = tcx.used_crate_source(cnum); | |
173 | if src.dylib.is_none() && | |
174 | !formats.contains_key(&cnum) && | |
175 | tcx.dep_kind(cnum) == DepKind::Explicit { | |
176 | assert!(src.rlib.is_some() || src.rmeta.is_some()); | |
177 | log::info!("adding staticlib: {}", tcx.crate_name(cnum)); | |
178 | add_library(tcx, cnum, RequireStatic, &mut formats); | |
179 | ret[cnum.as_usize() - 1] = Linkage::Static; | |
180 | } | |
181 | } | |
182 | ||
183 | // We've gotten this far because we're emitting some form of a final | |
184 | // artifact which means that we may need to inject dependencies of some | |
185 | // form. | |
186 | // | |
187 | // Things like allocators and panic runtimes may not have been activated | |
188 | // quite yet, so do so here. | |
60c5eb7d | 189 | activate_injected_dep(CStore::from_tcx(tcx).injected_panic_runtime(), &mut ret, |
e74abb32 XL |
190 | &|cnum| tcx.is_panic_runtime(cnum)); |
191 | ||
192 | // When dylib B links to dylib A, then when using B we must also link to A. | |
193 | // It could be the case, however, that the rlib for A is present (hence we | |
194 | // found metadata), but the dylib for A has since been removed. | |
195 | // | |
196 | // For situations like this, we perform one last pass over the dependencies, | |
197 | // making sure that everything is available in the requested format. | |
198 | for (cnum, kind) in ret.iter().enumerate() { | |
199 | let cnum = CrateNum::new(cnum + 1); | |
200 | let src = tcx.used_crate_source(cnum); | |
201 | match *kind { | |
202 | Linkage::NotLinked | | |
203 | Linkage::IncludedFromDylib => {} | |
204 | Linkage::Static if src.rlib.is_some() => continue, | |
205 | Linkage::Dynamic if src.dylib.is_some() => continue, | |
206 | kind => { | |
207 | let kind = match kind { | |
208 | Linkage::Static => "rlib", | |
209 | _ => "dylib", | |
210 | }; | |
211 | sess.err(&format!("crate `{}` required to be available in {} format, \ | |
212 | but was not found in this form", | |
213 | tcx.crate_name(cnum), kind)); | |
214 | } | |
215 | } | |
216 | } | |
217 | ||
218 | ret | |
219 | } | |
220 | ||
221 | fn add_library( | |
222 | tcx: TyCtxt<'_>, | |
223 | cnum: CrateNum, | |
224 | link: LinkagePreference, | |
225 | m: &mut FxHashMap<CrateNum, LinkagePreference>, | |
226 | ) { | |
227 | match m.get(&cnum) { | |
228 | Some(&link2) => { | |
229 | // If the linkages differ, then we'd have two copies of the library | |
230 | // if we continued linking. If the linkages are both static, then we | |
231 | // would also have two copies of the library (static from two | |
232 | // different locations). | |
233 | // | |
234 | // This error is probably a little obscure, but I imagine that it | |
235 | // can be refined over time. | |
236 | if link2 != link || link == RequireStatic { | |
237 | tcx.sess.struct_err(&format!("cannot satisfy dependencies so `{}` only \ | |
238 | shows up once", tcx.crate_name(cnum))) | |
239 | .help("having upstream crates all available in one format \ | |
240 | will likely make this go away") | |
241 | .emit(); | |
242 | } | |
243 | } | |
244 | None => { m.insert(cnum, link); } | |
245 | } | |
246 | } | |
247 | ||
248 | fn attempt_static(tcx: TyCtxt<'_>) -> Option<DependencyList> { | |
e74abb32 XL |
249 | let crates = cstore::used_crates(tcx, RequireStatic); |
250 | if !crates.iter().by_ref().all(|&(_, ref p)| p.is_some()) { | |
251 | return None | |
252 | } | |
253 | ||
254 | // All crates are available in an rlib format, so we're just going to link | |
255 | // everything in explicitly so long as it's actually required. | |
256 | let last_crate = tcx.crates().len(); | |
257 | let mut ret = (1..last_crate+1).map(|cnum| { | |
258 | if tcx.dep_kind(CrateNum::new(cnum)) == DepKind::Explicit { | |
259 | Linkage::Static | |
260 | } else { | |
261 | Linkage::NotLinked | |
262 | } | |
263 | }).collect::<Vec<_>>(); | |
264 | ||
265 | // Our allocator/panic runtime may not have been linked above if it wasn't | |
266 | // explicitly linked, which is the case for any injected dependency. Handle | |
267 | // that here and activate them. | |
60c5eb7d | 268 | activate_injected_dep(CStore::from_tcx(tcx).injected_panic_runtime(), &mut ret, |
e74abb32 XL |
269 | &|cnum| tcx.is_panic_runtime(cnum)); |
270 | ||
271 | Some(ret) | |
272 | } | |
273 | ||
274 | // Given a list of how to link upstream dependencies so far, ensure that an | |
275 | // injected dependency is activated. This will not do anything if one was | |
276 | // transitively included already (e.g., via a dylib or explicitly so). | |
277 | // | |
278 | // If an injected dependency was not found then we're guaranteed the | |
279 | // metadata::creader module has injected that dependency (not listed as | |
280 | // a required dependency) in one of the session's field. If this field is not | |
281 | // set then this compilation doesn't actually need the dependency and we can | |
282 | // also skip this step entirely. | |
283 | fn activate_injected_dep(injected: Option<CrateNum>, | |
284 | list: &mut DependencyList, | |
285 | replaces_injected: &dyn Fn(CrateNum) -> bool) { | |
286 | for (i, slot) in list.iter().enumerate() { | |
287 | let cnum = CrateNum::new(i + 1); | |
288 | if !replaces_injected(cnum) { | |
289 | continue | |
290 | } | |
291 | if *slot != Linkage::NotLinked { | |
292 | return | |
293 | } | |
294 | } | |
295 | if let Some(injected) = injected { | |
296 | let idx = injected.as_usize() - 1; | |
297 | assert_eq!(list[idx], Linkage::NotLinked); | |
298 | list[idx] = Linkage::Static; | |
299 | } | |
300 | } | |
301 | ||
302 | // After the linkage for a crate has been determined we need to verify that | |
303 | // there's only going to be one allocator in the output. | |
304 | fn verify_ok(tcx: TyCtxt<'_>, list: &[Linkage]) { | |
305 | let sess = &tcx.sess; | |
306 | if list.len() == 0 { | |
307 | return | |
308 | } | |
309 | let mut panic_runtime = None; | |
310 | for (i, linkage) in list.iter().enumerate() { | |
311 | if let Linkage::NotLinked = *linkage { | |
312 | continue | |
313 | } | |
314 | let cnum = CrateNum::new(i + 1); | |
315 | ||
316 | if tcx.is_panic_runtime(cnum) { | |
317 | if let Some((prev, _)) = panic_runtime { | |
318 | let prev_name = tcx.crate_name(prev); | |
319 | let cur_name = tcx.crate_name(cnum); | |
320 | sess.err(&format!("cannot link together two \ | |
321 | panic runtimes: {} and {}", | |
322 | prev_name, cur_name)); | |
323 | } | |
324 | panic_runtime = Some((cnum, tcx.panic_strategy(cnum))); | |
325 | } | |
326 | } | |
327 | ||
328 | // If we found a panic runtime, then we know by this point that it's the | |
329 | // only one, but we perform validation here that all the panic strategy | |
330 | // compilation modes for the whole DAG are valid. | |
331 | if let Some((cnum, found_strategy)) = panic_runtime { | |
332 | let desired_strategy = sess.panic_strategy(); | |
333 | ||
334 | // First up, validate that our selected panic runtime is indeed exactly | |
335 | // our same strategy. | |
336 | if found_strategy != desired_strategy { | |
337 | sess.err(&format!("the linked panic runtime `{}` is \ | |
338 | not compiled with this crate's \ | |
339 | panic strategy `{}`", | |
340 | tcx.crate_name(cnum), | |
341 | desired_strategy.desc())); | |
342 | } | |
343 | ||
344 | // Next up, verify that all other crates are compatible with this panic | |
345 | // strategy. If the dep isn't linked, we ignore it, and if our strategy | |
346 | // is abort then it's compatible with everything. Otherwise all crates' | |
347 | // panic strategy must match our own. | |
348 | for (i, linkage) in list.iter().enumerate() { | |
349 | if let Linkage::NotLinked = *linkage { | |
350 | continue | |
351 | } | |
352 | if desired_strategy == PanicStrategy::Abort { | |
353 | continue | |
354 | } | |
355 | let cnum = CrateNum::new(i + 1); | |
356 | let found_strategy = tcx.panic_strategy(cnum); | |
357 | let is_compiler_builtins = tcx.is_compiler_builtins(cnum); | |
358 | if is_compiler_builtins || desired_strategy == found_strategy { | |
359 | continue | |
360 | } | |
361 | ||
362 | sess.err(&format!("the crate `{}` is compiled with the \ | |
363 | panic strategy `{}` which is \ | |
364 | incompatible with this crate's \ | |
365 | strategy of `{}`", | |
366 | tcx.crate_name(cnum), | |
367 | found_strategy.desc(), | |
368 | desired_strategy.desc())); | |
369 | } | |
370 | } | |
371 | } |