2022-03-14 10:28:34 +00:00
|
|
|
use rustc_ast::{NestedMetaItem, CRATE_NODE_ID};
|
2020-01-11 12:15:20 +00:00
|
|
|
use rustc_attr as attr;
|
2019-12-24 04:02:53 +00:00
|
|
|
use rustc_data_structures::fx::FxHashSet;
|
2020-01-09 10:18:47 +00:00
|
|
|
use rustc_errors::struct_span_err;
|
2020-01-05 01:37:57 +00:00
|
|
|
use rustc_hir as hir;
|
2022-04-07 16:29:57 +00:00
|
|
|
use rustc_hir::def::DefKind;
|
2021-06-08 20:56:06 +00:00
|
|
|
use rustc_middle::ty::{List, ParamEnv, ParamEnvAnd, Ty, TyCtxt};
|
2020-11-14 02:02:03 +00:00
|
|
|
use rustc_session::cstore::{DllCallingConvention, DllImport, NativeLib};
|
2020-03-11 11:49:08 +00:00
|
|
|
use rustc_session::parse::feature_err;
|
2020-05-17 21:18:50 +00:00
|
|
|
use rustc_session::utils::NativeLibKind;
|
2020-03-11 11:49:08 +00:00
|
|
|
use rustc_session::Session;
|
2022-03-14 10:28:34 +00:00
|
|
|
use rustc_span::symbol::{sym, Symbol};
|
2018-04-25 16:30:39 +00:00
|
|
|
use rustc_target::spec::abi::Abi;
|
2019-11-11 21:46:56 +00:00
|
|
|
|
2022-05-20 23:51:09 +00:00
|
|
|
pub(crate) fn collect(tcx: TyCtxt<'_>) -> Vec<NativeLib> {
|
2017-08-30 21:48:57 +00:00
|
|
|
let mut collector = Collector { tcx, libs: Vec::new() };
|
2022-04-06 23:04:15 +00:00
|
|
|
for id in tcx.hir().items() {
|
2022-04-07 16:29:57 +00:00
|
|
|
collector.process_item(id);
|
2022-04-06 23:04:15 +00:00
|
|
|
}
|
2017-08-30 21:48:57 +00:00
|
|
|
collector.process_command_line();
|
2020-03-20 14:03:11 +00:00
|
|
|
collector.libs
|
2017-08-30 21:48:57 +00:00
|
|
|
}
|
|
|
|
|
2022-05-20 23:51:09 +00:00
|
|
|
pub(crate) fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool {
|
2017-08-30 21:48:57 +00:00
|
|
|
match lib.cfg {
|
2022-02-27 21:26:24 +00:00
|
|
|
Some(ref cfg) => attr::cfg_matches(cfg, &sess.parse_sess, CRATE_NODE_ID, None),
|
2017-08-30 21:48:57 +00:00
|
|
|
None => true,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-11 19:03:44 +00:00
|
|
|
struct Collector<'tcx> {
|
2019-06-13 21:48:52 +00:00
|
|
|
tcx: TyCtxt<'tcx>,
|
2020-05-17 21:18:50 +00:00
|
|
|
libs: Vec<NativeLib>,
|
2017-08-30 21:48:57 +00:00
|
|
|
}
|
|
|
|
|
2022-04-07 16:29:57 +00:00
|
|
|
impl<'tcx> Collector<'tcx> {
|
|
|
|
fn process_item(&mut self, id: rustc_hir::ItemId) {
|
2022-04-29 17:11:22 +00:00
|
|
|
if !matches!(self.tcx.def_kind(id.def_id), DefKind::ForeignMod) {
|
2022-04-07 16:29:57 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let it = self.tcx.hir().item(id);
|
2022-02-18 23:48:49 +00:00
|
|
|
let hir::ItemKind::ForeignMod { abi, items: foreign_mod_items } = it.kind else {
|
|
|
|
return;
|
2017-08-30 21:48:57 +00:00
|
|
|
};
|
|
|
|
|
2020-11-11 21:40:09 +00:00
|
|
|
if abi == Abi::Rust || abi == Abi::RustIntrinsic || abi == Abi::PlatformIntrinsic {
|
2017-08-30 21:48:57 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Process all of the #[link(..)]-style arguments
|
2020-07-30 01:27:50 +00:00
|
|
|
let sess = &self.tcx.sess;
|
2022-03-14 10:28:34 +00:00
|
|
|
let features = self.tcx.features();
|
2021-07-29 17:00:41 +00:00
|
|
|
for m in self.tcx.hir().attrs(it.hir_id()).iter().filter(|a| a.has_name(sym::link)) {
|
2022-02-18 23:48:49 +00:00
|
|
|
let Some(items) = m.meta_item_list() else {
|
|
|
|
continue;
|
2017-08-30 21:48:57 +00:00
|
|
|
};
|
2018-07-16 18:31:14 +00:00
|
|
|
|
2022-03-14 10:28:34 +00:00
|
|
|
let mut name = None;
|
|
|
|
let mut kind = None;
|
|
|
|
let mut modifiers = None;
|
|
|
|
let mut cfg = None;
|
|
|
|
let mut wasm_import_module = None;
|
2018-07-16 18:31:14 +00:00
|
|
|
for item in items.iter() {
|
2022-03-14 10:28:34 +00:00
|
|
|
match item.name_or_empty() {
|
|
|
|
sym::name => {
|
|
|
|
if name.is_some() {
|
|
|
|
let msg = "multiple `name` arguments in a single `#[link]` attribute";
|
|
|
|
sess.span_err(item.span(), msg);
|
|
|
|
continue;
|
2021-03-25 04:45:09 +00:00
|
|
|
}
|
2022-03-14 10:28:34 +00:00
|
|
|
let Some(link_name) = item.value_str() else {
|
|
|
|
let msg = "link name must be of the form `name = \"string\"`";
|
|
|
|
sess.span_err(item.span(), msg);
|
|
|
|
continue;
|
|
|
|
};
|
|
|
|
let span = item.name_value_literal_span().unwrap();
|
|
|
|
if link_name.is_empty() {
|
|
|
|
struct_span_err!(sess, span, E0454, "link name must not be empty")
|
|
|
|
.span_label(span, "empty link name")
|
2020-07-30 01:27:50 +00:00
|
|
|
.emit();
|
2018-07-16 18:31:14 +00:00
|
|
|
}
|
2022-03-14 10:28:34 +00:00
|
|
|
name = Some((link_name, span));
|
2018-07-16 18:31:14 +00:00
|
|
|
}
|
2022-03-14 10:28:34 +00:00
|
|
|
sym::kind => {
|
|
|
|
if kind.is_some() {
|
|
|
|
let msg = "multiple `kind` arguments in a single `#[link]` attribute";
|
2020-07-30 01:27:50 +00:00
|
|
|
sess.span_err(item.span(), msg);
|
2022-03-14 10:28:34 +00:00
|
|
|
continue;
|
2018-07-16 18:31:14 +00:00
|
|
|
}
|
2022-03-14 10:28:34 +00:00
|
|
|
let Some(link_kind) = item.value_str() else {
|
|
|
|
let msg = "link kind must be of the form `kind = \"string\"`";
|
|
|
|
sess.span_err(item.span(), msg);
|
|
|
|
continue;
|
2021-03-25 04:45:09 +00:00
|
|
|
};
|
|
|
|
|
2022-03-14 10:28:34 +00:00
|
|
|
let span = item.name_value_literal_span().unwrap();
|
|
|
|
let link_kind = match link_kind.as_str() {
|
|
|
|
"static" => NativeLibKind::Static { bundle: None, whole_archive: None },
|
|
|
|
"dylib" => NativeLibKind::Dylib { as_needed: None },
|
|
|
|
"framework" => {
|
|
|
|
if !sess.target.is_like_osx {
|
|
|
|
struct_span_err!(
|
|
|
|
sess,
|
|
|
|
span,
|
|
|
|
E0455,
|
|
|
|
"link kind `framework` is only supported on Apple targets"
|
|
|
|
)
|
|
|
|
.emit();
|
2022-02-11 07:08:35 +00:00
|
|
|
}
|
2022-03-14 10:28:34 +00:00
|
|
|
NativeLibKind::Framework { as_needed: None }
|
2021-03-25 04:45:09 +00:00
|
|
|
}
|
2022-03-14 10:28:34 +00:00
|
|
|
"raw-dylib" => {
|
|
|
|
if !sess.target.is_like_windows {
|
|
|
|
struct_span_err!(
|
|
|
|
sess,
|
|
|
|
span,
|
|
|
|
E0455,
|
|
|
|
"link kind `raw-dylib` is only supported on Windows targets"
|
|
|
|
)
|
|
|
|
.emit();
|
|
|
|
} else if !features.raw_dylib {
|
|
|
|
feature_err(
|
|
|
|
&sess.parse_sess,
|
|
|
|
sym::raw_dylib,
|
|
|
|
span,
|
|
|
|
"link kind `raw-dylib` is unstable",
|
|
|
|
)
|
|
|
|
.emit();
|
2022-02-11 07:08:35 +00:00
|
|
|
}
|
2022-03-14 10:28:34 +00:00
|
|
|
NativeLibKind::RawDylib
|
2021-03-25 04:45:09 +00:00
|
|
|
}
|
2022-03-14 10:28:34 +00:00
|
|
|
kind => {
|
|
|
|
let msg = format!(
|
|
|
|
"unknown link kind `{kind}`, expected one of: \
|
|
|
|
static, dylib, framework, raw-dylib"
|
2022-01-23 00:49:12 +00:00
|
|
|
);
|
2022-03-14 10:28:34 +00:00
|
|
|
struct_span_err!(sess, span, E0458, "{}", msg)
|
|
|
|
.span_label(span, "unknown link kind")
|
|
|
|
.emit();
|
|
|
|
continue;
|
2022-01-23 00:49:12 +00:00
|
|
|
}
|
2022-03-14 10:28:34 +00:00
|
|
|
};
|
|
|
|
kind = Some(link_kind);
|
|
|
|
}
|
|
|
|
sym::modifiers => {
|
|
|
|
if modifiers.is_some() {
|
|
|
|
let msg =
|
|
|
|
"multiple `modifiers` arguments in a single `#[link]` attribute";
|
|
|
|
sess.span_err(item.span(), msg);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
let Some(link_modifiers) = item.value_str() else {
|
|
|
|
let msg = "link modifiers must be of the form `modifiers = \"string\"`";
|
|
|
|
sess.span_err(item.span(), msg);
|
|
|
|
continue;
|
|
|
|
};
|
|
|
|
modifiers = Some((link_modifiers, item.name_value_literal_span().unwrap()));
|
|
|
|
}
|
|
|
|
sym::cfg => {
|
|
|
|
if cfg.is_some() {
|
|
|
|
let msg = "multiple `cfg` arguments in a single `#[link]` attribute";
|
|
|
|
sess.span_err(item.span(), msg);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
let Some(link_cfg) = item.meta_item_list() else {
|
|
|
|
let msg = "link cfg must be of the form `cfg(/* predicate */)`";
|
|
|
|
sess.span_err(item.span(), msg);
|
|
|
|
continue;
|
|
|
|
};
|
|
|
|
let [NestedMetaItem::MetaItem(link_cfg)] = link_cfg else {
|
|
|
|
let msg = "link cfg must have a single predicate argument";
|
|
|
|
sess.span_err(item.span(), msg);
|
|
|
|
continue;
|
|
|
|
};
|
|
|
|
if !features.link_cfg {
|
|
|
|
feature_err(
|
|
|
|
&sess.parse_sess,
|
|
|
|
sym::link_cfg,
|
|
|
|
item.span(),
|
|
|
|
"link cfg is unstable",
|
|
|
|
)
|
|
|
|
.emit();
|
|
|
|
}
|
|
|
|
cfg = Some(link_cfg.clone());
|
|
|
|
}
|
|
|
|
sym::wasm_import_module => {
|
|
|
|
if wasm_import_module.is_some() {
|
|
|
|
let msg = "multiple `wasm_import_module` arguments \
|
|
|
|
in a single `#[link]` attribute";
|
|
|
|
sess.span_err(item.span(), msg);
|
|
|
|
continue;
|
2021-03-25 04:45:09 +00:00
|
|
|
}
|
2022-03-14 10:28:34 +00:00
|
|
|
let Some(link_wasm_import_module) = item.value_str() else {
|
|
|
|
let msg = "wasm import module must be of the form \
|
|
|
|
`wasm_import_module = \"string\"`";
|
|
|
|
sess.span_err(item.span(), msg);
|
|
|
|
continue;
|
|
|
|
};
|
|
|
|
wasm_import_module = Some((link_wasm_import_module, item.span()));
|
2021-03-25 04:45:09 +00:00
|
|
|
}
|
2022-03-14 10:28:34 +00:00
|
|
|
_ => {
|
|
|
|
let msg = "unexpected `#[link]` argument, expected one of: \
|
|
|
|
name, kind, modifiers, cfg, wasm_import_module";
|
2022-02-11 07:08:35 +00:00
|
|
|
sess.span_err(item.span(), msg);
|
|
|
|
}
|
2021-03-25 04:45:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-14 10:28:34 +00:00
|
|
|
// Do this outside the above loop so we don't depend on modifiers coming after kinds
|
|
|
|
let mut verbatim = None;
|
|
|
|
if let Some((modifiers, span)) = modifiers {
|
|
|
|
for modifier in modifiers.as_str().split(',') {
|
|
|
|
let (modifier, value) = match modifier.strip_prefix(&['+', '-']) {
|
|
|
|
Some(m) => (m, modifier.starts_with('+')),
|
|
|
|
None => {
|
|
|
|
sess.span_err(
|
|
|
|
span,
|
|
|
|
"invalid linking modifier syntax, expected '+' or '-' prefix \
|
|
|
|
before one of: bundle, verbatim, whole-archive, as-needed",
|
|
|
|
);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
macro report_unstable_modifier($feature: ident) {
|
|
|
|
if !features.$feature {
|
|
|
|
feature_err(
|
|
|
|
&sess.parse_sess,
|
|
|
|
sym::$feature,
|
|
|
|
span,
|
|
|
|
&format!("linking modifier `{modifier}` is unstable"),
|
|
|
|
)
|
|
|
|
.emit();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
let assign_modifier = |dst: &mut Option<bool>| {
|
|
|
|
if dst.is_some() {
|
|
|
|
let msg = format!(
|
|
|
|
"multiple `{modifier}` modifiers in a single `modifiers` argument"
|
|
|
|
);
|
|
|
|
sess.span_err(span, &msg);
|
|
|
|
} else {
|
|
|
|
*dst = Some(value);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
match (modifier, &mut kind) {
|
|
|
|
("bundle", Some(NativeLibKind::Static { bundle, .. })) => {
|
|
|
|
assign_modifier(bundle)
|
|
|
|
}
|
|
|
|
("bundle", _) => {
|
|
|
|
sess.span_err(
|
|
|
|
span,
|
|
|
|
"linking modifier `bundle` is only compatible with \
|
|
|
|
`static` linking kind",
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
("verbatim", _) => {
|
|
|
|
report_unstable_modifier!(native_link_modifiers_verbatim);
|
|
|
|
assign_modifier(&mut verbatim)
|
|
|
|
}
|
|
|
|
|
|
|
|
("whole-archive", Some(NativeLibKind::Static { whole_archive, .. })) => {
|
|
|
|
assign_modifier(whole_archive)
|
|
|
|
}
|
|
|
|
("whole-archive", _) => {
|
|
|
|
sess.span_err(
|
|
|
|
span,
|
|
|
|
"linking modifier `whole-archive` is only compatible with \
|
|
|
|
`static` linking kind",
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
("as-needed", Some(NativeLibKind::Dylib { as_needed }))
|
|
|
|
| ("as-needed", Some(NativeLibKind::Framework { as_needed })) => {
|
|
|
|
report_unstable_modifier!(native_link_modifiers_as_needed);
|
|
|
|
assign_modifier(as_needed)
|
|
|
|
}
|
|
|
|
("as-needed", _) => {
|
|
|
|
sess.span_err(
|
|
|
|
span,
|
|
|
|
"linking modifier `as-needed` is only compatible with \
|
|
|
|
`dylib` and `framework` linking kinds",
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
_ => {
|
|
|
|
sess.span_err(
|
|
|
|
span,
|
|
|
|
format!(
|
|
|
|
"unknown linking modifier `{modifier}`, expected one of: \
|
|
|
|
bundle, verbatim, whole-archive, as-needed"
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-02-11 07:08:35 +00:00
|
|
|
}
|
|
|
|
|
2022-03-14 10:28:34 +00:00
|
|
|
if let Some((_, span)) = wasm_import_module {
|
|
|
|
if name.is_some() || kind.is_some() || modifiers.is_some() || cfg.is_some() {
|
|
|
|
let msg = "`wasm_import_module` is incompatible with \
|
|
|
|
other arguments in `#[link]` attributes";
|
|
|
|
sess.span_err(span, msg);
|
|
|
|
}
|
|
|
|
} else if name.is_none() {
|
2018-07-16 18:31:14 +00:00
|
|
|
struct_span_err!(
|
2020-07-30 01:27:50 +00:00
|
|
|
sess,
|
2018-07-16 18:31:14 +00:00
|
|
|
m.span,
|
|
|
|
E0459,
|
2022-03-14 10:28:34 +00:00
|
|
|
"`#[link]` attribute requires a `name = \"string\"` argument"
|
2018-07-16 18:31:14 +00:00
|
|
|
)
|
|
|
|
.span_label(m.span, "missing `name` argument")
|
|
|
|
.emit();
|
|
|
|
}
|
2021-03-08 20:42:54 +00:00
|
|
|
|
2022-03-14 10:28:34 +00:00
|
|
|
let dll_imports = match kind {
|
|
|
|
Some(NativeLibKind::RawDylib) => {
|
|
|
|
if let Some((name, span)) = name && name.as_str().contains('\0') {
|
|
|
|
sess.span_err(
|
|
|
|
span,
|
|
|
|
"link name must not contain NUL characters if link kind is `raw-dylib`",
|
|
|
|
);
|
|
|
|
}
|
2021-03-08 20:42:54 +00:00
|
|
|
foreign_mod_items
|
|
|
|
.iter()
|
2022-03-14 10:28:34 +00:00
|
|
|
.map(|child_item| self.build_dll_import(abi, child_item))
|
|
|
|
.collect()
|
2022-01-23 00:49:12 +00:00
|
|
|
}
|
2022-03-14 10:28:34 +00:00
|
|
|
_ => Vec::new(),
|
2021-03-08 20:42:54 +00:00
|
|
|
};
|
2022-03-14 10:28:34 +00:00
|
|
|
self.libs.push(NativeLib {
|
|
|
|
name: name.map(|(name, _)| name),
|
|
|
|
kind: kind.unwrap_or(NativeLibKind::Unspecified),
|
|
|
|
cfg,
|
|
|
|
foreign_module: Some(it.def_id.to_def_id()),
|
|
|
|
wasm_import_module: wasm_import_module.map(|(name, _)| name),
|
|
|
|
verbatim,
|
|
|
|
dll_imports,
|
|
|
|
});
|
2019-08-27 14:42:44 +00:00
|
|
|
}
|
2017-08-30 21:48:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Process libs passed on the command line
|
|
|
|
fn process_command_line(&mut self) {
|
|
|
|
// First, check for errors
|
2018-10-16 08:44:26 +00:00
|
|
|
let mut renames = FxHashSet::default();
|
2021-03-25 04:45:09 +00:00
|
|
|
for lib in &self.tcx.sess.opts.libs {
|
2022-03-14 10:28:34 +00:00
|
|
|
if let NativeLibKind::Framework { .. } = lib.kind && !self.tcx.sess.target.is_like_osx {
|
|
|
|
// Cannot check this when parsing options because the target is not yet available.
|
|
|
|
self.tcx.sess.err("library kind `framework` is only supported on Apple targets");
|
|
|
|
}
|
2021-03-25 04:45:09 +00:00
|
|
|
if let Some(ref new_name) = lib.new_name {
|
2018-07-16 18:31:14 +00:00
|
|
|
let any_duplicate = self
|
|
|
|
.libs
|
|
|
|
.iter()
|
|
|
|
.filter_map(|lib| lib.name.as_ref())
|
2021-11-07 09:33:27 +00:00
|
|
|
.any(|n| n.as_str() == lib.name);
|
2017-08-30 21:48:57 +00:00
|
|
|
if new_name.is_empty() {
|
2022-03-14 10:28:34 +00:00
|
|
|
self.tcx.sess.err(format!(
|
2017-08-30 21:48:57 +00:00
|
|
|
"an empty renaming target was specified for library `{}`",
|
2021-03-25 04:45:09 +00:00
|
|
|
lib.name
|
2017-08-30 21:48:57 +00:00
|
|
|
));
|
2018-07-16 18:31:14 +00:00
|
|
|
} else if !any_duplicate {
|
2022-03-14 10:28:34 +00:00
|
|
|
self.tcx.sess.err(format!(
|
2017-08-30 21:48:57 +00:00
|
|
|
"renaming of the library `{}` was specified, \
|
2019-07-19 18:08:21 +00:00
|
|
|
however this crate contains no `#[link(...)]` \
|
2021-10-03 06:53:02 +00:00
|
|
|
attributes referencing this library",
|
2021-03-25 04:45:09 +00:00
|
|
|
lib.name
|
2017-08-30 21:48:57 +00:00
|
|
|
));
|
2021-03-25 04:45:09 +00:00
|
|
|
} else if !renames.insert(&lib.name) {
|
2022-03-14 10:28:34 +00:00
|
|
|
self.tcx.sess.err(format!(
|
2017-08-30 21:48:57 +00:00
|
|
|
"multiple renamings were \
|
2021-10-03 06:53:02 +00:00
|
|
|
specified for library `{}`",
|
2021-03-25 04:45:09 +00:00
|
|
|
lib.name
|
2017-08-30 21:48:57 +00:00
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-19 13:30:23 +00:00
|
|
|
// Update kind and, optionally, the name of all native libraries
|
Keep last redundant linker flag, not first
When a library (L1) is passed to the linker multiple times, this is
sometimes purposeful: there might be several other libraries in the
linker command (L2 and L3) that all depend on L1. You'd end up with a
(simplified) linker command that looks like:
-l2 -l1 -l3 -l1
With the previous behavior, when rustc encountered a redundant library,
it would keep the first instance, and remove the later ones, resulting
in:
-l2 -l1 -l3
This can cause a linker error, because on some platforms (e.g. Linux),
the linker will only include symbols from L1 that are needed *at the
point it's referenced in the command line*. So if L3 depends on
additional symbols from L1, which aren't needed by L2, the linker won't
know to include them, and you'll end up with "undefined symbols" errors.
A better behavior is to keep the *last* instance of the library:
-l2 -l3 -l1
This ensures that all "downstream" libraries have been included in the
linker command before the "upstream" library is referenced.
Fixes rust-lang#47989
2018-12-20 20:46:42 +00:00
|
|
|
// (there may be more than one) with the specified name. If any
|
|
|
|
// library is mentioned more than once, keep the latest mention
|
|
|
|
// of it, so that any possible dependent libraries appear before
|
|
|
|
// it. (This ensures that the linker is able to see symbols from
|
|
|
|
// all possible dependent libraries before linking in the library
|
|
|
|
// in question.)
|
2021-03-25 04:45:09 +00:00
|
|
|
for passed_lib in &self.tcx.sess.opts.libs {
|
Keep last redundant linker flag, not first
When a library (L1) is passed to the linker multiple times, this is
sometimes purposeful: there might be several other libraries in the
linker command (L2 and L3) that all depend on L1. You'd end up with a
(simplified) linker command that looks like:
-l2 -l1 -l3 -l1
With the previous behavior, when rustc encountered a redundant library,
it would keep the first instance, and remove the later ones, resulting
in:
-l2 -l1 -l3
This can cause a linker error, because on some platforms (e.g. Linux),
the linker will only include symbols from L1 that are needed *at the
point it's referenced in the command line*. So if L3 depends on
additional symbols from L1, which aren't needed by L2, the linker won't
know to include them, and you'll end up with "undefined symbols" errors.
A better behavior is to keep the *last* instance of the library:
-l2 -l3 -l1
This ensures that all "downstream" libraries have been included in the
linker command before the "upstream" library is referenced.
Fixes rust-lang#47989
2018-12-20 20:46:42 +00:00
|
|
|
// If we've already added any native libraries with the same
|
2018-12-20 21:19:55 +00:00
|
|
|
// name, they will be pulled out into `existing`, so that we
|
|
|
|
// can move them to the end of the list below.
|
Keep last redundant linker flag, not first
When a library (L1) is passed to the linker multiple times, this is
sometimes purposeful: there might be several other libraries in the
linker command (L2 and L3) that all depend on L1. You'd end up with a
(simplified) linker command that looks like:
-l2 -l1 -l3 -l1
With the previous behavior, when rustc encountered a redundant library,
it would keep the first instance, and remove the later ones, resulting
in:
-l2 -l1 -l3
This can cause a linker error, because on some platforms (e.g. Linux),
the linker will only include symbols from L1 that are needed *at the
point it's referenced in the command line*. So if L3 depends on
additional symbols from L1, which aren't needed by L2, the linker won't
know to include them, and you'll end up with "undefined symbols" errors.
A better behavior is to keep the *last* instance of the library:
-l2 -l3 -l1
This ensures that all "downstream" libraries have been included in the
linker command before the "upstream" library is referenced.
Fixes rust-lang#47989
2018-12-20 20:46:42 +00:00
|
|
|
let mut existing = self
|
|
|
|
.libs
|
|
|
|
.drain_filter(|lib| {
|
|
|
|
if let Some(lib_name) = lib.name {
|
2021-03-25 04:45:09 +00:00
|
|
|
if lib_name.as_str() == passed_lib.name {
|
2022-02-11 07:08:35 +00:00
|
|
|
// FIXME: This whole logic is questionable, whether modifiers are
|
|
|
|
// involved or not, library reordering and kind overriding without
|
|
|
|
// explicit `:rename` in particular.
|
|
|
|
if lib.has_modifiers() || passed_lib.has_modifiers() {
|
2022-05-23 17:27:06 +00:00
|
|
|
let msg = "overriding linking modifiers from command line is not supported";
|
|
|
|
match lib.foreign_module {
|
|
|
|
Some(def_id) => self.tcx.sess.span_err(self.tcx.def_span(def_id), msg),
|
|
|
|
None => self.tcx.sess.err(msg),
|
|
|
|
};
|
2022-02-11 07:08:35 +00:00
|
|
|
}
|
2021-03-25 04:45:09 +00:00
|
|
|
if passed_lib.kind != NativeLibKind::Unspecified {
|
|
|
|
lib.kind = passed_lib.kind;
|
2019-12-22 22:42:04 +00:00
|
|
|
}
|
2021-03-25 04:45:09 +00:00
|
|
|
if let Some(new_name) = &passed_lib.new_name {
|
Keep last redundant linker flag, not first
When a library (L1) is passed to the linker multiple times, this is
sometimes purposeful: there might be several other libraries in the
linker command (L2 and L3) that all depend on L1. You'd end up with a
(simplified) linker command that looks like:
-l2 -l1 -l3 -l1
With the previous behavior, when rustc encountered a redundant library,
it would keep the first instance, and remove the later ones, resulting
in:
-l2 -l1 -l3
This can cause a linker error, because on some platforms (e.g. Linux),
the linker will only include symbols from L1 that are needed *at the
point it's referenced in the command line*. So if L3 depends on
additional symbols from L1, which aren't needed by L2, the linker won't
know to include them, and you'll end up with "undefined symbols" errors.
A better behavior is to keep the *last* instance of the library:
-l2 -l3 -l1
This ensures that all "downstream" libraries have been included in the
linker command before the "upstream" library is referenced.
Fixes rust-lang#47989
2018-12-20 20:46:42 +00:00
|
|
|
lib.name = Some(Symbol::intern(new_name));
|
2019-12-22 22:42:04 +00:00
|
|
|
}
|
2021-03-25 04:45:09 +00:00
|
|
|
lib.verbatim = passed_lib.verbatim;
|
Keep last redundant linker flag, not first
When a library (L1) is passed to the linker multiple times, this is
sometimes purposeful: there might be several other libraries in the
linker command (L2 and L3) that all depend on L1. You'd end up with a
(simplified) linker command that looks like:
-l2 -l1 -l3 -l1
With the previous behavior, when rustc encountered a redundant library,
it would keep the first instance, and remove the later ones, resulting
in:
-l2 -l1 -l3
This can cause a linker error, because on some platforms (e.g. Linux),
the linker will only include symbols from L1 that are needed *at the
point it's referenced in the command line*. So if L3 depends on
additional symbols from L1, which aren't needed by L2, the linker won't
know to include them, and you'll end up with "undefined symbols" errors.
A better behavior is to keep the *last* instance of the library:
-l2 -l3 -l1
This ensures that all "downstream" libraries have been included in the
linker command before the "upstream" library is referenced.
Fixes rust-lang#47989
2018-12-20 20:46:42 +00:00
|
|
|
return true;
|
|
|
|
}
|
2017-08-30 21:48:57 +00:00
|
|
|
}
|
Keep last redundant linker flag, not first
When a library (L1) is passed to the linker multiple times, this is
sometimes purposeful: there might be several other libraries in the
linker command (L2 and L3) that all depend on L1. You'd end up with a
(simplified) linker command that looks like:
-l2 -l1 -l3 -l1
With the previous behavior, when rustc encountered a redundant library,
it would keep the first instance, and remove the later ones, resulting
in:
-l2 -l1 -l3
This can cause a linker error, because on some platforms (e.g. Linux),
the linker will only include symbols from L1 that are needed *at the
point it's referenced in the command line*. So if L3 depends on
additional symbols from L1, which aren't needed by L2, the linker won't
know to include them, and you'll end up with "undefined symbols" errors.
A better behavior is to keep the *last* instance of the library:
-l2 -l3 -l1
This ensures that all "downstream" libraries have been included in the
linker command before the "upstream" library is referenced.
Fixes rust-lang#47989
2018-12-20 20:46:42 +00:00
|
|
|
false
|
|
|
|
})
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
if existing.is_empty() {
|
2017-08-30 21:48:57 +00:00
|
|
|
// Add if not found
|
2021-10-07 21:18:39 +00:00
|
|
|
let new_name: Option<&str> = passed_lib.new_name.as_deref();
|
2022-03-14 10:28:34 +00:00
|
|
|
self.libs.push(NativeLib {
|
2021-03-25 04:45:09 +00:00
|
|
|
name: Some(Symbol::intern(new_name.unwrap_or(&passed_lib.name))),
|
|
|
|
kind: passed_lib.kind,
|
2017-08-30 21:48:57 +00:00
|
|
|
cfg: None,
|
2018-02-10 22:28:17 +00:00
|
|
|
foreign_module: None,
|
2018-07-16 18:31:14 +00:00
|
|
|
wasm_import_module: None,
|
2021-03-25 04:45:09 +00:00
|
|
|
verbatim: passed_lib.verbatim,
|
2021-03-08 20:42:54 +00:00
|
|
|
dll_imports: Vec::new(),
|
2022-03-14 10:28:34 +00:00
|
|
|
});
|
Keep last redundant linker flag, not first
When a library (L1) is passed to the linker multiple times, this is
sometimes purposeful: there might be several other libraries in the
linker command (L2 and L3) that all depend on L1. You'd end up with a
(simplified) linker command that looks like:
-l2 -l1 -l3 -l1
With the previous behavior, when rustc encountered a redundant library,
it would keep the first instance, and remove the later ones, resulting
in:
-l2 -l1 -l3
This can cause a linker error, because on some platforms (e.g. Linux),
the linker will only include symbols from L1 that are needed *at the
point it's referenced in the command line*. So if L3 depends on
additional symbols from L1, which aren't needed by L2, the linker won't
know to include them, and you'll end up with "undefined symbols" errors.
A better behavior is to keep the *last* instance of the library:
-l2 -l3 -l1
This ensures that all "downstream" libraries have been included in the
linker command before the "upstream" library is referenced.
Fixes rust-lang#47989
2018-12-20 20:46:42 +00:00
|
|
|
} else {
|
|
|
|
// Move all existing libraries with the same name to the
|
|
|
|
// end of the command line.
|
|
|
|
self.libs.append(&mut existing);
|
2017-08-30 21:48:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-06-08 20:56:06 +00:00
|
|
|
|
2021-07-15 20:19:39 +00:00
|
|
|
fn i686_arg_list_size(&self, item: &hir::ForeignItemRef) -> usize {
|
2021-06-08 20:56:06 +00:00
|
|
|
let argument_types: &List<Ty<'_>> = self.tcx.erase_late_bound_regions(
|
|
|
|
self.tcx
|
|
|
|
.type_of(item.id.def_id)
|
|
|
|
.fn_sig(self.tcx)
|
|
|
|
.inputs()
|
|
|
|
.map_bound(|slice| self.tcx.mk_type_list(slice.iter())),
|
|
|
|
);
|
|
|
|
|
|
|
|
argument_types
|
|
|
|
.iter()
|
|
|
|
.map(|ty| {
|
|
|
|
let layout = self
|
|
|
|
.tcx
|
|
|
|
.layout_of(ParamEnvAnd { param_env: ParamEnv::empty(), value: ty })
|
|
|
|
.expect("layout")
|
|
|
|
.layout;
|
|
|
|
// In both stdcall and fastcall, we always round up the argument size to the
|
|
|
|
// nearest multiple of 4 bytes.
|
2022-03-04 02:46:56 +00:00
|
|
|
(layout.size().bytes_usize() + 3) & !3
|
2021-06-08 20:56:06 +00:00
|
|
|
})
|
|
|
|
.sum()
|
|
|
|
}
|
|
|
|
|
2021-07-15 20:19:39 +00:00
|
|
|
fn build_dll_import(&self, abi: Abi, item: &hir::ForeignItemRef) -> DllImport {
|
2021-06-08 20:56:06 +00:00
|
|
|
let calling_convention = if self.tcx.sess.target.arch == "x86" {
|
|
|
|
match abi {
|
2022-02-01 17:53:45 +00:00
|
|
|
Abi::C { .. } | Abi::Cdecl { .. } => DllCallingConvention::C,
|
2021-06-08 20:56:06 +00:00
|
|
|
Abi::Stdcall { .. } | Abi::System { .. } => {
|
|
|
|
DllCallingConvention::Stdcall(self.i686_arg_list_size(item))
|
|
|
|
}
|
2022-02-01 17:53:45 +00:00
|
|
|
Abi::Fastcall { .. } => {
|
|
|
|
DllCallingConvention::Fastcall(self.i686_arg_list_size(item))
|
|
|
|
}
|
2022-07-19 17:40:26 +00:00
|
|
|
Abi::Vectorcall { .. } => {
|
|
|
|
DllCallingConvention::Vectorcall(self.i686_arg_list_size(item))
|
|
|
|
}
|
2021-06-08 20:56:06 +00:00
|
|
|
_ => {
|
|
|
|
self.tcx.sess.span_fatal(
|
|
|
|
item.span,
|
|
|
|
r#"ABI not supported by `#[link(kind = "raw-dylib")]` on i686"#,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
match abi {
|
2022-02-01 17:53:45 +00:00
|
|
|
Abi::C { .. } | Abi::Win64 { .. } | Abi::System { .. } => DllCallingConvention::C,
|
2021-06-08 20:56:06 +00:00
|
|
|
_ => {
|
|
|
|
self.tcx.sess.span_fatal(
|
|
|
|
item.span,
|
|
|
|
r#"ABI not supported by `#[link(kind = "raw-dylib")]` on this architecture"#,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2021-09-11 00:34:09 +00:00
|
|
|
|
|
|
|
DllImport {
|
|
|
|
name: item.ident.name,
|
|
|
|
ordinal: self.tcx.codegen_fn_attrs(item.id.def_id).link_ordinal,
|
|
|
|
calling_convention,
|
|
|
|
span: item.span,
|
|
|
|
}
|
2021-06-08 20:56:06 +00:00
|
|
|
}
|
2017-08-30 21:48:57 +00:00
|
|
|
}
|