Skip to content

Commit 058bec8

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 058bec8

File tree

2 files changed

+114
-28
lines changed

2 files changed

+114
-28
lines changed

src/shims/foreign_items.rs

Lines changed: 75 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,80 @@ 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+
// There is more than one definition with this name. What we do now
169+
// depends on whether one or both definitions are weak.
170+
match (is_weak, original.is_weak) {
171+
(false, true) => {
172+
// Original definition is a weak definition. Override it.
173+
174+
symbol_target = Some(SymbolTarget {
175+
instance: ty::Instance::mono(tcx, def_id),
176+
cnum,
177+
is_weak,
178+
});
179+
}
180+
(true, false) => {
181+
// Current definition is a weak definition. Keep the original one.
182+
}
183+
(true, true) | (false, false) => {
184+
// Either both definitions are non-weak or both are weak. In
185+
// either case return an error. For weak definitions we error
186+
// because it is unspecified which definition would have been
187+
// picked by the linker.
188+
189+
// Make sure we are consistent wrt what is 'first' and 'second'.
190+
let original_span =
191+
tcx.def_span(original.instance.def_id()).data();
192+
let span = tcx.def_span(def_id).data();
193+
if original_span < span {
194+
throw_machine_stop!(
195+
TerminationInfo::MultipleSymbolDefinitions {
196+
link_name,
197+
first: original_span,
198+
first_crate: tcx.crate_name(original.cnum),
199+
second: span,
200+
second_crate: tcx.crate_name(cnum),
201+
}
202+
);
203+
} else {
204+
throw_machine_stop!(
205+
TerminationInfo::MultipleSymbolDefinitions {
206+
link_name,
207+
first: span,
208+
first_crate: tcx.crate_name(cnum),
209+
second: original_span,
210+
second_crate: tcx.crate_name(original.cnum),
211+
}
212+
);
213+
}
214+
}
179215
}
216+
} else {
217+
symbol_target = Some(SymbolTarget {
218+
instance: ty::Instance::mono(tcx, def_id),
219+
cnum,
220+
is_weak,
221+
});
180222
}
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));
187223
}
188224
interp_ok(())
189225
})?;
190226

191-
e.insert(instance_and_crate.map(|ic| ic.0))
227+
// Once we identified the instance corresponding to the symbol, ensure
228+
// it is a function. It is okay to encounter non-functions in the search above
229+
// as long as the final instance we arrive at is a function.
230+
if let Some(SymbolTarget { instance, .. }) = symbol_target {
231+
if !matches!(tcx.def_kind(instance.def_id()), DefKind::Fn | DefKind::AssocFn) {
232+
throw_ub_format!(
233+
"attempt to call an exported symbol that is not defined as a function"
234+
);
235+
}
236+
}
237+
238+
e.insert(symbol_target.map(|SymbolTarget { instance, .. }| instance))
192239
}
193240
};
194241
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)