@@ -5,6 +5,7 @@ use std::path::Path;
55use rustc_abi:: { Align , AlignFromBytesError , CanonAbi , Size } ;
66use rustc_apfloat:: Float ;
77use rustc_ast:: expand:: allocator:: alloc_error_handler_name;
8+ use rustc_hir:: attrs:: Linkage ;
89use rustc_hir:: def:: DefKind ;
910use rustc_hir:: def_id:: CrateNum ;
1011use 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 {
0 commit comments