Skip to content

Commit 6d39533

Browse files
committed
Support weak definitions
When a symbol only has a weak definition, this definition will be picked. When a symbol has both a weak and a regular definition, the regular definition will be picked instead.
1 parent f3328be commit 6d39533

File tree

2 files changed

+115
-28
lines changed

2 files changed

+115
-28
lines changed

src/shims/foreign_items.rs

Lines changed: 76 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use std::path::Path;
55
use rustc_abi::{Align, AlignFromBytesError, CanonAbi, Size};
66
use rustc_apfloat::Float;
77
use rustc_ast::expand::allocator::alloc_error_handler_name;
8+
use rustc_hir::attrs::Linkage;
89
use rustc_hir::def::DefKind;
910
use rustc_hir::def_id::CrateNum;
1011
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
@@ -138,7 +139,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
138139
Entry::Occupied(e) => e.into_mut(),
139140
Entry::Vacant(e) => {
140141
// Find it if it was not cached.
141-
let mut instance_and_crate: Option<(ty::Instance<'_>, CrateNum)> = None;
142+
143+
struct SymbolTarget<'tcx> {
144+
instance: ty::Instance<'tcx>,
145+
cnum: CrateNum,
146+
is_weak: bool,
147+
}
148+
let mut symbol_target: Option<SymbolTarget<'tcx>> = None;
142149
helpers::iter_exported_symbols(tcx, |cnum, def_id| {
143150
let attrs = tcx.codegen_fn_attrs(def_id);
144151
// Skip over imports of items.
@@ -155,40 +162,81 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
155162

156163
let instance = Instance::mono(tcx, def_id);
157164
let symbol_name = tcx.symbol_name(instance).name;
165+
let is_weak = attrs.linkage == Some(Linkage::WeakAny);
158166
if symbol_name == link_name.as_str() {
159-
if let Some((original_instance, original_cnum)) = instance_and_crate {
160-
// Make sure we are consistent wrt what is 'first' and 'second'.
161-
let original_span = tcx.def_span(original_instance.def_id()).data();
162-
let span = tcx.def_span(def_id).data();
163-
if original_span < span {
164-
throw_machine_stop!(TerminationInfo::MultipleSymbolDefinitions {
165-
link_name,
166-
first: original_span,
167-
first_crate: tcx.crate_name(original_cnum),
168-
second: span,
169-
second_crate: tcx.crate_name(cnum),
170-
});
171-
} else {
172-
throw_machine_stop!(TerminationInfo::MultipleSymbolDefinitions {
173-
link_name,
174-
first: span,
175-
first_crate: tcx.crate_name(cnum),
176-
second: original_span,
177-
second_crate: tcx.crate_name(original_cnum),
178-
});
167+
if let Some(original) = &symbol_target
168+
{
169+
// There is more than one definition with this name. What we do now
170+
// depends on whether one or both definitions are weak.
171+
match (is_weak, original.is_weak) {
172+
(false, true) => {
173+
// Original definition is a weak definition. Override it.
174+
175+
symbol_target = Some(SymbolTarget {
176+
instance: ty::Instance::mono(tcx, def_id),
177+
cnum,
178+
is_weak,
179+
});
180+
}
181+
(true, false) => {
182+
// Current definition is a weak definition. Keep the original one.
183+
}
184+
(true, true) | (false, false) => {
185+
// Either both definitions are non-weak or both are weak. In
186+
// either case return an error. For weak definitions we error
187+
// because it is unspecified which definition would have been
188+
// picked by the linker.
189+
190+
// Make sure we are consistent wrt what is 'first' and 'second'.
191+
let original_span =
192+
tcx.def_span(original.instance.def_id()).data();
193+
let span = tcx.def_span(def_id).data();
194+
if original_span < span {
195+
throw_machine_stop!(
196+
TerminationInfo::MultipleSymbolDefinitions {
197+
link_name,
198+
first: original_span,
199+
first_crate: tcx.crate_name(original.cnum),
200+
second: span,
201+
second_crate: tcx.crate_name(cnum),
202+
}
203+
);
204+
} else {
205+
throw_machine_stop!(
206+
TerminationInfo::MultipleSymbolDefinitions {
207+
link_name,
208+
first: span,
209+
first_crate: tcx.crate_name(cnum),
210+
second: original_span,
211+
second_crate: tcx.crate_name(original.cnum),
212+
}
213+
);
214+
}
215+
}
179216
}
217+
} else {
218+
symbol_target = Some(SymbolTarget {
219+
instance: ty::Instance::mono(tcx, def_id),
220+
cnum,
221+
is_weak,
222+
});
180223
}
181-
if !matches!(tcx.def_kind(def_id), DefKind::Fn | DefKind::AssocFn) {
182-
throw_ub_format!(
183-
"attempt to call an exported symbol that is not defined as a function"
184-
);
185-
}
186-
instance_and_crate = Some((ty::Instance::mono(tcx, def_id), cnum));
187224
}
188225
interp_ok(())
189226
})?;
190227

191-
e.insert(instance_and_crate.map(|ic| ic.0))
228+
// Once we identified the instance corresponding to the symbol, ensure
229+
// it is a function. It is okay to encounter non-functions in the search above
230+
// as long as the final instance we arrive at is a function.
231+
if let Some(SymbolTarget { instance, .. }) = symbol_target {
232+
if !matches!(tcx.def_kind(instance.def_id()), DefKind::Fn | DefKind::AssocFn) {
233+
throw_ub_format!(
234+
"attempt to call an exported symbol that is not defined as a function"
235+
);
236+
}
237+
}
238+
239+
e.insert(symbol_target.map(|SymbolTarget { instance, .. }| instance))
192240
}
193241
};
194242
match instance {
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#![feature(linkage)]
2+
3+
// FIXME move this module to a separate crate once aux-build is allowed
4+
// This currently depends on the fact that miri skips the codegen check
5+
// that denies multiple symbols with the same name.
6+
mod first {
7+
#[no_mangle]
8+
#[linkage = "weak"]
9+
extern "C" fn foo() -> i32 {
10+
1
11+
}
12+
13+
#[no_mangle]
14+
#[linkage = "weak"]
15+
extern "C" fn bar() -> i32 {
16+
2
17+
}
18+
}
19+
20+
mod second {
21+
#[no_mangle]
22+
extern "C" fn bar() -> i32 {
23+
3
24+
}
25+
}
26+
27+
extern "C" {
28+
fn foo() -> i32;
29+
fn bar() -> i32;
30+
}
31+
32+
fn main() {
33+
unsafe {
34+
// If there is no non-weak definition, the weak definition will be used.
35+
assert_eq!(foo(), 1);
36+
// Non-weak definition takes presedence.
37+
assert_eq!(bar(), 3);
38+
}
39+
}

0 commit comments

Comments
 (0)