]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_metadata/src/native_libs.rs
New upstream version 1.71.1+dfsg1
[rustc.git] / compiler / rustc_metadata / src / native_libs.rs
CommitLineData
923072b8 1use rustc_ast::{NestedMetaItem, CRATE_NODE_ID};
74b04a01 2use rustc_attr as attr;
dfeec247 3use rustc_data_structures::fx::FxHashSet;
dfeec247 4use rustc_hir as hir;
04454e1e 5use rustc_hir::def::DefKind;
136023e0 6use rustc_middle::ty::{List, ParamEnv, ParamEnvAnd, Ty, TyCtxt};
f2b60f7d
FG
7use rustc_session::config::CrateType;
8use rustc_session::cstore::{DllCallingConvention, DllImport, NativeLib, PeImportNameType};
ba9703b0 9use rustc_session::parse::feature_err;
f2b60f7d 10use rustc_session::search_paths::PathKind;
f9f354fc 11use rustc_session::utils::NativeLibKind;
ba9703b0 12use rustc_session::Session;
923072b8 13use rustc_span::symbol::{sym, Symbol};
83c7162d 14use rustc_target::spec::abi::Abi;
60c5eb7d 15
9ffffee4 16use crate::errors;
f2b60f7d
FG
17
18use std::path::PathBuf;
19
20pub fn find_native_static_library(
21 name: &str,
487cf647 22 verbatim: bool,
f2b60f7d
FG
23 search_paths: &[PathBuf],
24 sess: &Session,
25) -> PathBuf {
487cf647 26 let formats = if verbatim {
2b03887a 27 vec![("".into(), "".into())]
f2b60f7d 28 } else {
2b03887a
FG
29 let os = (sess.target.staticlib_prefix.clone(), sess.target.staticlib_suffix.clone());
30 // On Windows, static libraries sometimes show up as libfoo.a and other
31 // times show up as foo.lib
32 let unix = ("lib".into(), ".a".into());
33 if os == unix { vec![os] } else { vec![os, unix] }
f2b60f7d 34 };
f2b60f7d
FG
35
36 for path in search_paths {
2b03887a 37 for (prefix, suffix) in &formats {
9c376795 38 let test = path.join(format!("{prefix}{name}{suffix}"));
f2b60f7d
FG
39 if test.exists() {
40 return test;
41 }
42 }
43 }
2b03887a 44
9ffffee4 45 sess.emit_fatal(errors::MissingNativeLibrary::new(name, verbatim));
f2b60f7d
FG
46}
47
48fn find_bundled_library(
353b0b11 49 name: Symbol,
f2b60f7d
FG
50 verbatim: Option<bool>,
51 kind: NativeLibKind,
9ffffee4 52 has_cfg: bool,
f2b60f7d
FG
53 sess: &Session,
54) -> Option<Symbol> {
9ffffee4
FG
55 if let NativeLibKind::Static { bundle: Some(true) | None, whole_archive } = kind
56 && sess.crate_types().iter().any(|t| matches!(t, &CrateType::Rlib | CrateType::Staticlib))
57 && (sess.opts.unstable_opts.packed_bundled_libs || has_cfg || whole_archive == Some(true))
58 {
59 let verbatim = verbatim.unwrap_or(false);
60 let search_paths = &sess.target_filesearch(PathKind::Native).search_path_dirs();
353b0b11 61 return find_native_static_library(name.as_str(), verbatim, search_paths, sess)
9ffffee4
FG
62 .file_name()
63 .and_then(|s| s.to_str())
64 .map(Symbol::intern);
f2b60f7d 65 }
9ffffee4 66 None
f2b60f7d
FG
67}
68
923072b8 69pub(crate) fn collect(tcx: TyCtxt<'_>) -> Vec<NativeLib> {
dfeec247 70 let mut collector = Collector { tcx, libs: Vec::new() };
04454e1e
FG
71 for id in tcx.hir().items() {
72 collector.process_item(id);
73 }
ea8adc8c 74 collector.process_command_line();
ba9703b0 75 collector.libs
ea8adc8c
XL
76}
77
923072b8 78pub(crate) fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool {
ea8adc8c 79 match lib.cfg {
5e7ed085 80 Some(ref cfg) => attr::cfg_matches(cfg, &sess.parse_sess, CRATE_NODE_ID, None),
ea8adc8c
XL
81 None => true,
82 }
83}
84
dc9dc135
XL
85struct Collector<'tcx> {
86 tcx: TyCtxt<'tcx>,
f9f354fc 87 libs: Vec<NativeLib>,
ea8adc8c
XL
88}
89
04454e1e
FG
90impl<'tcx> Collector<'tcx> {
91 fn process_item(&mut self, id: rustc_hir::ItemId) {
2b03887a 92 if !matches!(self.tcx.def_kind(id.owner_id), DefKind::ForeignMod) {
04454e1e
FG
93 return;
94 }
95
96 let it = self.tcx.hir().item(id);
5e7ed085
FG
97 let hir::ItemKind::ForeignMod { abi, items: foreign_mod_items } = it.kind else {
98 return;
ea8adc8c
XL
99 };
100
9ffffee4 101 if matches!(abi, Abi::Rust | Abi::RustIntrinsic | Abi::PlatformIntrinsic) {
dfeec247 102 return;
ea8adc8c
XL
103 }
104
105 // Process all of the #[link(..)]-style arguments
9ffffee4 106 let sess = self.tcx.sess;
923072b8 107 let features = self.tcx.features();
9ffffee4
FG
108
109 if !sess.opts.unstable_opts.link_directives {
110 return;
111 }
112
94222f64 113 for m in self.tcx.hir().attrs(it.hir_id()).iter().filter(|a| a.has_name(sym::link)) {
5e7ed085
FG
114 let Some(items) = m.meta_item_list() else {
115 continue;
ea8adc8c 116 };
8faf50e0 117
923072b8
FG
118 let mut name = None;
119 let mut kind = None;
120 let mut modifiers = None;
121 let mut cfg = None;
122 let mut wasm_import_module = None;
f2b60f7d 123 let mut import_name_type = None;
8faf50e0 124 for item in items.iter() {
923072b8
FG
125 match item.name_or_empty() {
126 sym::name => {
127 if name.is_some() {
9ffffee4 128 sess.emit_err(errors::MultipleNamesInLink { span: item.span() });
923072b8 129 continue;
17df50a5 130 }
923072b8 131 let Some(link_name) = item.value_str() else {
9ffffee4 132 sess.emit_err(errors::LinkNameForm { span: item.span() });
923072b8
FG
133 continue;
134 };
135 let span = item.name_value_literal_span().unwrap();
136 if link_name.is_empty() {
9ffffee4 137 sess.emit_err(errors::EmptyLinkName { span });
8faf50e0 138 }
923072b8 139 name = Some((link_name, span));
8faf50e0 140 }
923072b8
FG
141 sym::kind => {
142 if kind.is_some() {
9ffffee4 143 sess.emit_err(errors::MultipleKindsInLink { span: item.span() });
923072b8 144 continue;
8faf50e0 145 }
923072b8 146 let Some(link_kind) = item.value_str() else {
9ffffee4 147 sess.emit_err(errors::LinkKindForm { span: item.span() });
923072b8 148 continue;
17df50a5
XL
149 };
150
923072b8
FG
151 let span = item.name_value_literal_span().unwrap();
152 let link_kind = match link_kind.as_str() {
153 "static" => NativeLibKind::Static { bundle: None, whole_archive: None },
154 "dylib" => NativeLibKind::Dylib { as_needed: None },
155 "framework" => {
156 if !sess.target.is_like_osx {
9ffffee4 157 sess.emit_err(errors::LinkFrameworkApple { span });
5e7ed085 158 }
923072b8 159 NativeLibKind::Framework { as_needed: None }
5e7ed085 160 }
923072b8
FG
161 "raw-dylib" => {
162 if !sess.target.is_like_windows {
9ffffee4 163 sess.emit_err(errors::FrameworkOnlyWindows { span });
5e7ed085 164 }
923072b8 165 NativeLibKind::RawDylib
17df50a5 166 }
923072b8 167 kind => {
9ffffee4 168 sess.emit_err(errors::UnknownLinkKind { span, kind });
923072b8 169 continue;
5e7ed085 170 }
923072b8
FG
171 };
172 kind = Some(link_kind);
173 }
174 sym::modifiers => {
175 if modifiers.is_some() {
9ffffee4 176 sess.emit_err(errors::MultipleLinkModifiers { span: item.span() });
923072b8 177 continue;
17df50a5 178 }
923072b8 179 let Some(link_modifiers) = item.value_str() else {
9ffffee4 180 sess.emit_err(errors::LinkModifiersForm { span: item.span() });
923072b8
FG
181 continue;
182 };
183 modifiers = Some((link_modifiers, item.name_value_literal_span().unwrap()));
17df50a5 184 }
923072b8
FG
185 sym::cfg => {
186 if cfg.is_some() {
9ffffee4 187 sess.emit_err(errors::MultipleCfgs { span: item.span() });
923072b8
FG
188 continue;
189 }
190 let Some(link_cfg) = item.meta_item_list() else {
9ffffee4 191 sess.emit_err(errors::LinkCfgForm { span: item.span() });
923072b8
FG
192 continue;
193 };
194 let [NestedMetaItem::MetaItem(link_cfg)] = link_cfg else {
9ffffee4 195 sess.emit_err(errors::LinkCfgSinglePredicate { span: item.span() });
923072b8
FG
196 continue;
197 };
198 if !features.link_cfg {
199 feature_err(
200 &sess.parse_sess,
201 sym::link_cfg,
202 item.span(),
203 "link cfg is unstable",
204 )
205 .emit();
206 }
207 cfg = Some(link_cfg.clone());
208 }
209 sym::wasm_import_module => {
210 if wasm_import_module.is_some() {
9ffffee4 211 sess.emit_err(errors::MultipleWasmImport { span: item.span() });
923072b8
FG
212 continue;
213 }
214 let Some(link_wasm_import_module) = item.value_str() else {
9ffffee4 215 sess.emit_err(errors::WasmImportForm { span: item.span() });
923072b8
FG
216 continue;
217 };
218 wasm_import_module = Some((link_wasm_import_module, item.span()));
219 }
f2b60f7d
FG
220 sym::import_name_type => {
221 if import_name_type.is_some() {
9ffffee4 222 sess.emit_err(errors::MultipleImportNameType { span: item.span() });
f2b60f7d
FG
223 continue;
224 }
225 let Some(link_import_name_type) = item.value_str() else {
9ffffee4 226 sess.emit_err(errors::ImportNameTypeForm { span: item.span() });
f2b60f7d
FG
227 continue;
228 };
229 if self.tcx.sess.target.arch != "x86" {
9ffffee4 230 sess.emit_err(errors::ImportNameTypeX86 { span: item.span() });
f2b60f7d
FG
231 continue;
232 }
233
234 let link_import_name_type = match link_import_name_type.as_str() {
235 "decorated" => PeImportNameType::Decorated,
236 "noprefix" => PeImportNameType::NoPrefix,
237 "undecorated" => PeImportNameType::Undecorated,
238 import_name_type => {
9ffffee4 239 sess.emit_err(errors::UnknownImportNameType {
f2b60f7d
FG
240 span: item.span(),
241 import_name_type,
242 });
243 continue;
244 }
245 };
f2b60f7d
FG
246 import_name_type = Some((link_import_name_type, item.span()));
247 }
923072b8 248 _ => {
9ffffee4 249 sess.emit_err(errors::UnexpectedLinkArg { span: item.span() });
5e7ed085 250 }
17df50a5
XL
251 }
252 }
253
923072b8
FG
254 // Do this outside the above loop so we don't depend on modifiers coming after kinds
255 let mut verbatim = None;
256 if let Some((modifiers, span)) = modifiers {
257 for modifier in modifiers.as_str().split(',') {
258 let (modifier, value) = match modifier.strip_prefix(&['+', '-']) {
259 Some(m) => (m, modifier.starts_with('+')),
260 None => {
9ffffee4 261 sess.emit_err(errors::InvalidLinkModifier { span });
923072b8
FG
262 continue;
263 }
264 };
265
266 macro report_unstable_modifier($feature: ident) {
267 if !features.$feature {
268 feature_err(
269 &sess.parse_sess,
270 sym::$feature,
271 span,
49aad941 272 format!("linking modifier `{modifier}` is unstable"),
923072b8
FG
273 )
274 .emit();
275 }
276 }
277 let assign_modifier = |dst: &mut Option<bool>| {
278 if dst.is_some() {
9ffffee4 279 sess.emit_err(errors::MultipleModifiers { span, modifier });
923072b8
FG
280 } else {
281 *dst = Some(value);
282 }
283 };
284 match (modifier, &mut kind) {
285 ("bundle", Some(NativeLibKind::Static { bundle, .. })) => {
286 assign_modifier(bundle)
287 }
288 ("bundle", _) => {
9ffffee4 289 sess.emit_err(errors::BundleNeedsStatic { span });
923072b8
FG
290 }
291
487cf647 292 ("verbatim", _) => assign_modifier(&mut verbatim),
923072b8
FG
293
294 ("whole-archive", Some(NativeLibKind::Static { whole_archive, .. })) => {
295 assign_modifier(whole_archive)
296 }
297 ("whole-archive", _) => {
9ffffee4 298 sess.emit_err(errors::WholeArchiveNeedsStatic { span });
923072b8
FG
299 }
300
301 ("as-needed", Some(NativeLibKind::Dylib { as_needed }))
302 | ("as-needed", Some(NativeLibKind::Framework { as_needed })) => {
303 report_unstable_modifier!(native_link_modifiers_as_needed);
304 assign_modifier(as_needed)
305 }
306 ("as-needed", _) => {
9ffffee4 307 sess.emit_err(errors::AsNeededCompatibility { span });
923072b8
FG
308 }
309
310 _ => {
9ffffee4 311 sess.emit_err(errors::UnknownLinkModifier { span, modifier });
923072b8
FG
312 }
313 }
314 }
5e7ed085
FG
315 }
316
923072b8
FG
317 if let Some((_, span)) = wasm_import_module {
318 if name.is_some() || kind.is_some() || modifiers.is_some() || cfg.is_some() {
9ffffee4 319 sess.emit_err(errors::IncompatibleWasmLink { span });
923072b8 320 }
f2b60f7d
FG
321 }
322
353b0b11
FG
323 if wasm_import_module.is_some() {
324 (name, kind) = (wasm_import_module, Some(NativeLibKind::WasmImportModule));
325 }
326 let Some((name, name_span)) = name else {
327 sess.emit_err(errors::LinkRequiresName { span: m.span });
328 continue;
329 };
330
f2b60f7d
FG
331 // Do this outside of the loop so that `import_name_type` can be specified before `kind`.
332 if let Some((_, span)) = import_name_type {
333 if kind != Some(NativeLibKind::RawDylib) {
9ffffee4 334 sess.emit_err(errors::ImportNameTypeRaw { span });
f2b60f7d 335 }
8faf50e0 336 }
17df50a5 337
923072b8
FG
338 let dll_imports = match kind {
339 Some(NativeLibKind::RawDylib) => {
353b0b11
FG
340 if name.as_str().contains('\0') {
341 sess.emit_err(errors::RawDylibNoNul { span: name_span });
923072b8 342 }
17df50a5
XL
343 foreign_mod_items
344 .iter()
f2b60f7d
FG
345 .map(|child_item| {
346 self.build_dll_import(
347 abi,
348 import_name_type.map(|(import_name_type, _)| import_name_type),
349 child_item,
350 )
351 })
923072b8 352 .collect()
ea8adc8c 353 }
f2b60f7d
FG
354 _ => {
355 for child_item in foreign_mod_items {
2b03887a 356 if self.tcx.def_kind(child_item.id.owner_id).has_codegen_attrs()
f2b60f7d
FG
357 && self
358 .tcx
2b03887a 359 .codegen_fn_attrs(child_item.id.owner_id)
f2b60f7d
FG
360 .link_ordinal
361 .is_some()
362 {
363 let link_ordinal_attr = self
364 .tcx
365 .hir()
2b03887a 366 .attrs(child_item.id.owner_id.into())
f2b60f7d
FG
367 .iter()
368 .find(|a| a.has_name(sym::link_ordinal))
369 .unwrap();
9ffffee4
FG
370 sess.emit_err(errors::LinkOrdinalRawDylib {
371 span: link_ordinal_attr.span,
372 });
f2b60f7d
FG
373 }
374 }
375
376 Vec::new()
377 }
17df50a5 378 };
f2b60f7d 379
f2b60f7d 380 let kind = kind.unwrap_or(NativeLibKind::Unspecified);
9ffffee4 381 let filename = find_bundled_library(name, verbatim, kind, cfg.is_some(), sess);
923072b8 382 self.libs.push(NativeLib {
f2b60f7d
FG
383 name,
384 filename,
385 kind,
923072b8 386 cfg,
2b03887a 387 foreign_module: Some(it.owner_id.to_def_id()),
923072b8
FG
388 verbatim,
389 dll_imports,
390 });
e74abb32 391 }
ea8adc8c
XL
392 }
393
394 // Process libs passed on the command line
395 fn process_command_line(&mut self) {
396 // First, check for errors
0bf4aa26 397 let mut renames = FxHashSet::default();
17df50a5 398 for lib in &self.tcx.sess.opts.libs {
923072b8
FG
399 if let NativeLibKind::Framework { .. } = lib.kind && !self.tcx.sess.target.is_like_osx {
400 // Cannot check this when parsing options because the target is not yet available.
9ffffee4 401 self.tcx.sess.emit_err(errors::LibFrameworkApple);
923072b8 402 }
17df50a5 403 if let Some(ref new_name) = lib.new_name {
353b0b11 404 let any_duplicate = self.libs.iter().any(|n| n.name.as_str() == lib.name);
ea8adc8c 405 if new_name.is_empty() {
9ffffee4 406 self.tcx.sess.emit_err(errors::EmptyRenamingTarget { lib_name: &lib.name });
8faf50e0 407 } else if !any_duplicate {
9ffffee4 408 self.tcx.sess.emit_err(errors::RenamingNoLink { lib_name: &lib.name });
17df50a5 409 } else if !renames.insert(&lib.name) {
9ffffee4 410 self.tcx.sess.emit_err(errors::MultipleRenamings { lib_name: &lib.name });
ea8adc8c
XL
411 }
412 }
413 }
414
b7449926 415 // Update kind and, optionally, the name of all native libraries
9c376795 416 // (there may be more than one) with the specified name. If any
532ac7d7
XL
417 // library is mentioned more than once, keep the latest mention
418 // of it, so that any possible dependent libraries appear before
9c376795 419 // it. (This ensures that the linker is able to see symbols from
532ac7d7
XL
420 // all possible dependent libraries before linking in the library
421 // in question.)
17df50a5 422 for passed_lib in &self.tcx.sess.opts.libs {
532ac7d7
XL
423 // If we've already added any native libraries with the same
424 // name, they will be pulled out into `existing`, so that we
425 // can move them to the end of the list below.
dfeec247
XL
426 let mut existing = self
427 .libs
428 .drain_filter(|lib| {
353b0b11
FG
429 if lib.name.as_str() == passed_lib.name {
430 // FIXME: This whole logic is questionable, whether modifiers are
431 // involved or not, library reordering and kind overriding without
432 // explicit `:rename` in particular.
433 if lib.has_modifiers() || passed_lib.has_modifiers() {
434 match lib.foreign_module {
435 Some(def_id) => self.tcx.sess.emit_err(errors::NoLinkModOverride {
436 span: Some(self.tcx.def_span(def_id)),
437 }),
438 None => {
439 self.tcx.sess.emit_err(errors::NoLinkModOverride { span: None })
440 }
441 };
442 }
443 if passed_lib.kind != NativeLibKind::Unspecified {
444 lib.kind = passed_lib.kind;
445 }
446 if let Some(new_name) = &passed_lib.new_name {
447 lib.name = Symbol::intern(new_name);
532ac7d7 448 }
353b0b11
FG
449 lib.verbatim = passed_lib.verbatim;
450 return true;
ea8adc8c 451 }
dfeec247
XL
452 false
453 })
454 .collect::<Vec<_>>();
532ac7d7 455 if existing.is_empty() {
ea8adc8c 456 // Add if not found
c295e0f8 457 let new_name: Option<&str> = passed_lib.new_name.as_deref();
353b0b11 458 let name = Symbol::intern(new_name.unwrap_or(&passed_lib.name));
f2b60f7d
FG
459 let sess = self.tcx.sess;
460 let filename =
9ffffee4 461 find_bundled_library(name, passed_lib.verbatim, passed_lib.kind, false, sess);
923072b8 462 self.libs.push(NativeLib {
f2b60f7d
FG
463 name,
464 filename,
17df50a5 465 kind: passed_lib.kind,
ea8adc8c 466 cfg: None,
0531ce1d 467 foreign_module: None,
17df50a5
XL
468 verbatim: passed_lib.verbatim,
469 dll_imports: Vec::new(),
923072b8 470 });
532ac7d7
XL
471 } else {
472 // Move all existing libraries with the same name to the
473 // end of the command line.
474 self.libs.append(&mut existing);
ea8adc8c
XL
475 }
476 }
477 }
136023e0 478
c295e0f8 479 fn i686_arg_list_size(&self, item: &hir::ForeignItemRef) -> usize {
136023e0
XL
480 let argument_types: &List<Ty<'_>> = self.tcx.erase_late_bound_regions(
481 self.tcx
2b03887a 482 .type_of(item.id.owner_id)
9ffffee4 483 .subst_identity()
136023e0
XL
484 .fn_sig(self.tcx)
485 .inputs()
9ffffee4 486 .map_bound(|slice| self.tcx.mk_type_list(slice)),
136023e0
XL
487 );
488
489 argument_types
490 .iter()
491 .map(|ty| {
492 let layout = self
493 .tcx
494 .layout_of(ParamEnvAnd { param_env: ParamEnv::empty(), value: ty })
495 .expect("layout")
496 .layout;
497 // In both stdcall and fastcall, we always round up the argument size to the
498 // nearest multiple of 4 bytes.
5e7ed085 499 (layout.size().bytes_usize() + 3) & !3
136023e0
XL
500 })
501 .sum()
502 }
503
f2b60f7d
FG
504 fn build_dll_import(
505 &self,
506 abi: Abi,
507 import_name_type: Option<PeImportNameType>,
508 item: &hir::ForeignItemRef,
509 ) -> DllImport {
136023e0
XL
510 let calling_convention = if self.tcx.sess.target.arch == "x86" {
511 match abi {
5099ac24 512 Abi::C { .. } | Abi::Cdecl { .. } => DllCallingConvention::C,
136023e0
XL
513 Abi::Stdcall { .. } | Abi::System { .. } => {
514 DllCallingConvention::Stdcall(self.i686_arg_list_size(item))
515 }
5099ac24
FG
516 Abi::Fastcall { .. } => {
517 DllCallingConvention::Fastcall(self.i686_arg_list_size(item))
518 }
064997fb
FG
519 Abi::Vectorcall { .. } => {
520 DllCallingConvention::Vectorcall(self.i686_arg_list_size(item))
521 }
136023e0 522 _ => {
9ffffee4 523 self.tcx.sess.emit_fatal(errors::UnsupportedAbiI686 { span: item.span });
136023e0
XL
524 }
525 }
526 } else {
527 match abi {
5099ac24 528 Abi::C { .. } | Abi::Win64 { .. } | Abi::System { .. } => DllCallingConvention::C,
136023e0 529 _ => {
9ffffee4 530 self.tcx.sess.emit_fatal(errors::UnsupportedAbi { span: item.span });
136023e0
XL
531 }
532 }
533 };
c295e0f8 534
2b03887a 535 let codegen_fn_attrs = self.tcx.codegen_fn_attrs(item.id.owner_id);
f2b60f7d
FG
536 let import_name_type = codegen_fn_attrs
537 .link_ordinal
538 .map_or(import_name_type, |ord| Some(PeImportNameType::Ordinal(ord)));
539
c295e0f8 540 DllImport {
f2b60f7d
FG
541 name: codegen_fn_attrs.link_name.unwrap_or(item.ident.name),
542 import_name_type,
c295e0f8
XL
543 calling_convention,
544 span: item.span,
2b03887a 545 is_fn: self.tcx.def_kind(item.id.owner_id).is_fn_like(),
c295e0f8 546 }
136023e0 547 }
ea8adc8c 548}