Skip to content

Support weak definitions #4414

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 59 additions & 27 deletions src/shims/foreign_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use rustc_hir::def::DefKind;
use rustc_hir::def_id::CrateNum;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::mir::interpret::AllocInit;
use rustc_middle::mir::mono::Linkage;
use rustc_middle::ty::{Instance, Ty};
use rustc_middle::{mir, ty};
use rustc_span::Symbol;
Expand Down Expand Up @@ -138,7 +139,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
Entry::Occupied(e) => e.into_mut(),
Entry::Vacant(e) => {
// Find it if it was not cached.
let mut instance_and_crate: Option<(ty::Instance<'_>, CrateNum)> = None;
let mut instance_and_crate: Option<(ty::Instance<'_>, CrateNum, bool)> = None;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please explain what the bool means, or use a self-explaining type. It might be worth using a struct instead of a tuple.

helpers::iter_exported_symbols(tcx, |cnum, def_id| {
let attrs = tcx.codegen_fn_attrs(def_id);
// Skip over imports of items.
Expand All @@ -155,39 +156,70 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {

let instance = Instance::mono(tcx, def_id);
let symbol_name = tcx.symbol_name(instance).name;
let is_weak = attrs.linkage == Some(Linkage::WeakAny);
if symbol_name == link_name.as_str() {
if let Some((original_instance, original_cnum)) = instance_and_crate {
// Make sure we are consistent wrt what is 'first' and 'second'.
let original_span = tcx.def_span(original_instance.def_id()).data();
let span = tcx.def_span(def_id).data();
if original_span < span {
throw_machine_stop!(TerminationInfo::MultipleSymbolDefinitions {
link_name,
first: original_span,
first_crate: tcx.crate_name(original_cnum),
second: span,
second_crate: tcx.crate_name(cnum),
});
} else {
throw_machine_stop!(TerminationInfo::MultipleSymbolDefinitions {
link_name,
first: span,
first_crate: tcx.crate_name(cnum),
second: original_span,
second_crate: tcx.crate_name(original_cnum),
});
if let Some((original_instance, original_cnum, original_is_weak)) =
instance_and_crate
{
match (is_weak, original_is_weak) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
match (is_weak, original_is_weak) {
// There is more than one definition with this name. What we do now
// depends on whether one or both definitions are weak.
match (is_weak, original_is_weak) {

(false, true) => {
// Original definition is a weak definition. Override it.

instance_and_crate =
Some((ty::Instance::mono(tcx, def_id), cnum, is_weak));
}
(true, false) => {
// Current definition is a weak definition. Keep the original one.
}
(true, true) | (false, false) => {
// Either both definitions are non-weak or both are weak. In
// either case return an error. For weak definitions we error
// because it is undefined which definition would have been
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// because it is undefined which definition would have been
// because it is unspecified which definition would have been

// picked by the linker.

// Make sure we are consistent wrt what is 'first' and 'second'.
let original_span =
tcx.def_span(original_instance.def_id()).data();
let span = tcx.def_span(def_id).data();
if original_span < span {
throw_machine_stop!(
TerminationInfo::MultipleSymbolDefinitions {
link_name,
first: original_span,
first_crate: tcx.crate_name(original_cnum),
second: span,
second_crate: tcx.crate_name(cnum),
}
);
} else {
throw_machine_stop!(
TerminationInfo::MultipleSymbolDefinitions {
link_name,
first: span,
first_crate: tcx.crate_name(cnum),
second: original_span,
second_crate: tcx.crate_name(original_cnum),
}
);
}
}
}
} else {
instance_and_crate =
Some((ty::Instance::mono(tcx, def_id), cnum, is_weak));
}
if !matches!(tcx.def_kind(def_id), DefKind::Fn | DefKind::AssocFn) {
throw_ub_format!(
"attempt to call an exported symbol that is not defined as a function"
);
}
instance_and_crate = Some((ty::Instance::mono(tcx, def_id), cnum));
}
interp_ok(())
})?;

if let Some((instance, _, _)) = instance_and_crate {
if !matches!(tcx.def_kind(instance.def_id()), DefKind::Fn | DefKind::AssocFn) {
throw_ub_format!(
"attempt to call an exported symbol that is not defined as a function"
);
}
}

Comment on lines +215 to +222
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this logic can live here as well, but is there a specific reason you moved it?

e.insert(instance_and_crate.map(|ic| ic.0))
}
};
Expand Down
39 changes: 39 additions & 0 deletions tests/pass/function_calls/weak_definition.rs
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please rename this to exported_symbol_weak for better test grouping.

Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#![feature(linkage)]

// FIXME move this module to a separate crate once aux-build is allowed
// This currently depends on the fact that miri skips the codegen check
// that denies multiple symbols with the same name.
mod first {
#[no_mangle]
#[linkage = "weak"]
extern "C" fn foo() -> i32 {
1
}

#[no_mangle]
#[linkage = "weak"]
extern "C" fn bar() -> i32 {
2
}
}

mod second {
#[no_mangle]
extern "C" fn bar() -> i32 {
3
}
}

extern "C" {
fn foo() -> i32;
fn bar() -> i32;
}

fn main() {
unsafe {
// If there is no non-weak definition, the weak definition will be used.
assert_eq!(foo(), 1);
// Non-weak definition takes presedence.
assert_eq!(bar(), 3);
}
}