1
1
use std:: {
2
2
convert:: { TryFrom , TryInto } ,
3
3
iter,
4
+ collections:: hash_map:: Entry ,
4
5
} ;
5
6
6
7
use log:: trace;
@@ -34,7 +35,7 @@ pub enum EmulateByNameResult<'mir, 'tcx> {
34
35
/// Jumping has already been taken care of.
35
36
AlreadyJumped ,
36
37
/// A MIR body has been found for the function
37
- MirBody ( & ' mir mir:: Body < ' tcx > ) ,
38
+ MirBody ( & ' mir mir:: Body < ' tcx > , ty :: Instance < ' tcx > ) ,
38
39
/// The item is not supported.
39
40
NotSupported ,
40
41
}
@@ -135,68 +136,74 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
135
136
fn lookup_exported_symbol (
136
137
& mut self ,
137
138
link_name : Symbol ,
138
- ) -> InterpResult < ' tcx , Option < & ' mir mir:: Body < ' tcx > > > {
139
+ ) -> InterpResult < ' tcx , Option < ( & ' mir mir:: Body < ' tcx > , ty :: Instance < ' tcx > ) > > {
139
140
let this = self . eval_context_mut ( ) ;
140
141
let tcx = this. tcx . tcx ;
141
142
142
143
// If the result was cached, just return it.
143
- if let Some ( instance) = this. machine . exported_symbols_cache . get ( & link_name) {
144
- return instance. map ( |instance| this. load_mir ( instance. def , None ) ) . transpose ( ) ;
145
- }
146
-
147
- // Find it if it was not cached.
148
- let mut instance_and_crate: Option < ( ty:: Instance < ' _ > , CrateNum ) > = None ;
149
- // `dependency_formats` includes all the transitive informations needed to link a crate,
150
- // which is what we need here since we need to dig out `exported_symbols` from all transitive
151
- // dependencies.
152
- let dependency_formats = tcx. dependency_formats ( ( ) ) ;
153
- let dependency_format = dependency_formats
154
- . iter ( )
155
- . find ( |( crate_type, _) | * crate_type == CrateType :: Executable )
156
- . expect ( "interpreting a non-executable crate" ) ;
157
- for cnum in
158
- iter:: once ( LOCAL_CRATE ) . chain ( dependency_format. 1 . iter ( ) . enumerate ( ) . filter_map (
159
- |( num, & linkage) | ( linkage != Linkage :: NotLinked ) . then_some ( CrateNum :: new ( num + 1 ) ) ,
160
- ) )
161
- {
162
- // We can ignore `_export_level` here: we are a Rust crate, and everything is exported
163
- // from a Rust crate.
164
- for & ( symbol, _export_level) in tcx. exported_symbols ( cnum) {
165
- if let ExportedSymbol :: NonGeneric ( def_id) = symbol {
166
- let attrs = tcx. codegen_fn_attrs ( def_id) ;
167
- let symbol_name = if let Some ( export_name) = attrs. export_name {
168
- export_name
169
- } else if attrs. flags . contains ( CodegenFnAttrFlags :: NO_MANGLE ) {
170
- tcx. item_name ( def_id)
171
- } else {
172
- // Skip over items without an explicitly defined symbol name.
173
- continue ;
174
- } ;
175
- if symbol_name == link_name {
176
- if let Some ( ( instance, original_cnum) ) = instance_and_crate {
177
- throw_machine_stop ! ( TerminationInfo :: MultipleSymbolDefinitions {
178
- link_name,
179
- first: tcx. def_span( instance. def_id( ) ) . data( ) ,
180
- first_crate: tcx. crate_name( original_cnum) ,
181
- second: tcx. def_span( def_id) . data( ) ,
182
- second_crate: tcx. crate_name( cnum) ,
183
- } ) ;
184
- }
185
- if !matches ! ( tcx. def_kind( def_id) , DefKind :: Fn | DefKind :: AssocFn ) {
186
- throw_ub_format ! (
187
- "attempt to call an exported symbol that is not defined as a function"
188
- ) ;
144
+ // (Cannot use `or_insert` since the code below might have to throw an error.)
145
+ let entry = this. machine . exported_symbols_cache . entry ( link_name) ;
146
+ let instance = * match entry {
147
+ Entry :: Occupied ( e) => e. into_mut ( ) ,
148
+ Entry :: Vacant ( e) => {
149
+ // Find it if it was not cached.
150
+ let mut instance_and_crate: Option < ( ty:: Instance < ' _ > , CrateNum ) > = None ;
151
+ // `dependency_formats` includes all the transitive informations needed to link a crate,
152
+ // which is what we need here since we need to dig out `exported_symbols` from all transitive
153
+ // dependencies.
154
+ let dependency_formats = tcx. dependency_formats ( ( ) ) ;
155
+ let dependency_format = dependency_formats
156
+ . iter ( )
157
+ . find ( |( crate_type, _) | * crate_type == CrateType :: Executable )
158
+ . expect ( "interpreting a non-executable crate" ) ;
159
+ for cnum in
160
+ iter:: once ( LOCAL_CRATE ) . chain ( dependency_format. 1 . iter ( ) . enumerate ( ) . filter_map (
161
+ |( num, & linkage) | ( linkage != Linkage :: NotLinked ) . then_some ( CrateNum :: new ( num + 1 ) ) ,
162
+ ) )
163
+ {
164
+ // We can ignore `_export_level` here: we are a Rust crate, and everything is exported
165
+ // from a Rust crate.
166
+ for & ( symbol, _export_level) in tcx. exported_symbols ( cnum) {
167
+ if let ExportedSymbol :: NonGeneric ( def_id) = symbol {
168
+ let attrs = tcx. codegen_fn_attrs ( def_id) ;
169
+ let symbol_name = if let Some ( export_name) = attrs. export_name {
170
+ export_name
171
+ } else if attrs. flags . contains ( CodegenFnAttrFlags :: NO_MANGLE ) {
172
+ tcx. item_name ( def_id)
173
+ } else {
174
+ // Skip over items without an explicitly defined symbol name.
175
+ continue ;
176
+ } ;
177
+ if symbol_name == link_name {
178
+ if let Some ( ( instance, original_cnum) ) = instance_and_crate {
179
+ throw_machine_stop ! ( TerminationInfo :: MultipleSymbolDefinitions {
180
+ link_name,
181
+ first: tcx. def_span( instance. def_id( ) ) . data( ) ,
182
+ first_crate: tcx. crate_name( original_cnum) ,
183
+ second: tcx. def_span( def_id) . data( ) ,
184
+ second_crate: tcx. crate_name( cnum) ,
185
+ } ) ;
186
+ }
187
+ if !matches ! ( tcx. def_kind( def_id) , DefKind :: Fn | DefKind :: AssocFn ) {
188
+ throw_ub_format ! (
189
+ "attempt to call an exported symbol that is not defined as a function"
190
+ ) ;
191
+ }
192
+ instance_and_crate = Some ( ( ty:: Instance :: mono ( tcx, def_id) , cnum) ) ;
193
+ }
189
194
}
190
- instance_and_crate = Some ( ( ty:: Instance :: mono ( tcx, def_id) , cnum) ) ;
191
195
}
192
196
}
197
+
198
+ e. insert ( instance_and_crate. map ( |ic| ic. 0 ) )
199
+ }
200
+ } ;
201
+ match instance {
202
+ None => Ok ( None ) , // no symbol with this name
203
+ Some ( instance) => {
204
+ Ok ( Some ( ( this. load_mir ( instance. def , None ) ?, instance) ) )
193
205
}
194
206
}
195
-
196
- let instance = instance_and_crate. map ( |ic| ic. 0 ) ;
197
- // Cache it and load its MIR, if found.
198
- this. machine . exported_symbols_cache . try_insert ( link_name, instance) . unwrap ( ) ;
199
- instance. map ( |instance| this. load_mir ( instance. def , None ) ) . transpose ( )
200
207
}
201
208
202
209
/// Emulates calling a foreign item, failing if the item is not supported.
@@ -212,7 +219,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
212
219
args : & [ OpTy < ' tcx , Tag > ] ,
213
220
ret : Option < ( & PlaceTy < ' tcx , Tag > , mir:: BasicBlock ) > ,
214
221
unwind : StackPopUnwind ,
215
- ) -> InterpResult < ' tcx , Option < & ' mir mir:: Body < ' tcx > > > {
222
+ ) -> InterpResult < ' tcx , Option < ( & ' mir mir:: Body < ' tcx > , ty :: Instance < ' tcx > ) > > {
216
223
let this = self . eval_context_mut ( ) ;
217
224
let attrs = this. tcx . get_attrs ( def_id) ;
218
225
let link_name = this
@@ -240,7 +247,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
240
247
this. check_abi_and_shim_symbol_clash ( abi, Abi :: Rust , link_name) ?;
241
248
let panic_impl_id = tcx. lang_items ( ) . panic_impl ( ) . unwrap ( ) ;
242
249
let panic_impl_instance = ty:: Instance :: mono ( tcx, panic_impl_id) ;
243
- return Ok ( Some ( & * this. load_mir ( panic_impl_instance. def , None ) ?) ) ;
250
+ return Ok ( Some ( ( & * this. load_mir ( panic_impl_instance. def , None ) ?, panic_impl_instance ) ) ) ;
244
251
}
245
252
#[ rustfmt:: skip]
246
253
| "exit"
@@ -284,7 +291,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
284
291
this. go_to_block ( ret) ;
285
292
}
286
293
EmulateByNameResult :: AlreadyJumped => ( ) ,
287
- EmulateByNameResult :: MirBody ( mir) => return Ok ( Some ( mir) ) ,
294
+ EmulateByNameResult :: MirBody ( mir, instance ) => return Ok ( Some ( ( mir, instance ) ) ) ,
288
295
EmulateByNameResult :: NotSupported => {
289
296
if let Some ( body) = this. lookup_exported_symbol ( link_name) ? {
290
297
return Ok ( Some ( body) ) ;
@@ -315,11 +322,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
315
322
316
323
match allocator_kind {
317
324
AllocatorKind :: Global => {
318
- let body = this
325
+ let ( body, instance ) = this
319
326
. lookup_exported_symbol ( symbol) ?
320
327
. expect ( "symbol should be present if there is a global allocator" ) ;
321
328
322
- Ok ( EmulateByNameResult :: MirBody ( body) )
329
+ Ok ( EmulateByNameResult :: MirBody ( body, instance ) )
323
330
}
324
331
AllocatorKind :: Default => {
325
332
default ( this) ?;
0 commit comments