]> git.lizzy.rs Git - rust.git/blob - src/shims/foreign_items.rs
Auto merge of #2062 - RalfJung:rustup, r=RalfJung
[rust.git] / src / shims / foreign_items.rs
1 use std::{collections::hash_map::Entry, iter};
2
3 use log::trace;
4
5 use rustc_apfloat::Float;
6 use rustc_ast::expand::allocator::AllocatorKind;
7 use rustc_hir::{
8     def::DefKind,
9     def_id::{CrateNum, DefId, LOCAL_CRATE},
10 };
11 use rustc_middle::middle::{
12     codegen_fn_attrs::CodegenFnAttrFlags, dependency_format::Linkage,
13     exported_symbols::ExportedSymbol,
14 };
15 use rustc_middle::mir;
16 use rustc_middle::ty;
17 use rustc_session::config::CrateType;
18 use rustc_span::{symbol::sym, Symbol};
19 use rustc_target::{
20     abi::{Align, Size},
21     spec::abi::Abi,
22 };
23
24 use super::backtrace::EvalContextExt as _;
25 use crate::*;
26
27 /// Returned by `emulate_foreign_item_by_name`.
28 pub enum EmulateByNameResult<'mir, 'tcx> {
29     /// The caller is expected to jump to the return block.
30     NeedsJumping,
31     /// Jumping has already been taken care of.
32     AlreadyJumped,
33     /// A MIR body has been found for the function
34     MirBody(&'mir mir::Body<'tcx>, ty::Instance<'tcx>),
35     /// The item is not supported.
36     NotSupported,
37 }
38
39 impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
40 pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
41     /// Returns the minimum alignment for the target architecture for allocations of the given size.
42     fn min_align(&self, size: u64, kind: MiriMemoryKind) -> Align {
43         let this = self.eval_context_ref();
44         // List taken from `libstd/sys_common/alloc.rs`.
45         let min_align = match this.tcx.sess.target.arch.as_ref() {
46             "x86" | "arm" | "mips" | "powerpc" | "powerpc64" | "asmjs" | "wasm32" => 8,
47             "x86_64" | "aarch64" | "mips64" | "s390x" | "sparc64" => 16,
48             arch => bug!("Unsupported target architecture: {}", arch),
49         };
50         // Windows always aligns, even small allocations.
51         // Source: <https://support.microsoft.com/en-us/help/286470/how-to-use-pageheap-exe-in-windows-xp-windows-2000-and-windows-server>
52         // But jemalloc does not, so for the C heap we only align if the allocation is sufficiently big.
53         if kind == MiriMemoryKind::WinHeap || size >= min_align {
54             return Align::from_bytes(min_align).unwrap();
55         }
56         // We have `size < min_align`. Round `size` *down* to the next power of two and use that.
57         fn prev_power_of_two(x: u64) -> u64 {
58             let next_pow2 = x.next_power_of_two();
59             if next_pow2 == x {
60                 // x *is* a power of two, just use that.
61                 x
62             } else {
63                 // x is between two powers, so next = 2*prev.
64                 next_pow2 / 2
65             }
66         }
67         Align::from_bytes(prev_power_of_two(size)).unwrap()
68     }
69
70     fn malloc(
71         &mut self,
72         size: u64,
73         zero_init: bool,
74         kind: MiriMemoryKind,
75     ) -> InterpResult<'tcx, Pointer<Option<Tag>>> {
76         let this = self.eval_context_mut();
77         if size == 0 {
78             Ok(Pointer::null())
79         } else {
80             let align = this.min_align(size, kind);
81             let ptr = this.allocate_ptr(Size::from_bytes(size), align, kind.into())?;
82             if zero_init {
83                 // We just allocated this, the access is definitely in-bounds.
84                 this.write_bytes_ptr(ptr.into(), iter::repeat(0u8).take(size as usize)).unwrap();
85             }
86             Ok(ptr.into())
87         }
88     }
89
90     fn free(&mut self, ptr: Pointer<Option<Tag>>, kind: MiriMemoryKind) -> InterpResult<'tcx> {
91         let this = self.eval_context_mut();
92         if !this.ptr_is_null(ptr)? {
93             this.deallocate_ptr(ptr, None, kind.into())?;
94         }
95         Ok(())
96     }
97
98     fn realloc(
99         &mut self,
100         old_ptr: Pointer<Option<Tag>>,
101         new_size: u64,
102         kind: MiriMemoryKind,
103     ) -> InterpResult<'tcx, Pointer<Option<Tag>>> {
104         let this = self.eval_context_mut();
105         let new_align = this.min_align(new_size, kind);
106         if this.ptr_is_null(old_ptr)? {
107             if new_size == 0 {
108                 Ok(Pointer::null())
109             } else {
110                 let new_ptr =
111                     this.allocate_ptr(Size::from_bytes(new_size), new_align, kind.into())?;
112                 Ok(new_ptr.into())
113             }
114         } else {
115             if new_size == 0 {
116                 this.deallocate_ptr(old_ptr, None, kind.into())?;
117                 Ok(Pointer::null())
118             } else {
119                 let new_ptr = this.reallocate_ptr(
120                     old_ptr,
121                     None,
122                     Size::from_bytes(new_size),
123                     new_align,
124                     kind.into(),
125                 )?;
126                 Ok(new_ptr.into())
127             }
128         }
129     }
130
131     /// Lookup the body of a function that has `link_name` as the symbol name.
132     fn lookup_exported_symbol(
133         &mut self,
134         link_name: Symbol,
135     ) -> InterpResult<'tcx, Option<(&'mir mir::Body<'tcx>, ty::Instance<'tcx>)>> {
136         let this = self.eval_context_mut();
137         let tcx = this.tcx.tcx;
138
139         // If the result was cached, just return it.
140         // (Cannot use `or_insert` since the code below might have to throw an error.)
141         let entry = this.machine.exported_symbols_cache.entry(link_name);
142         let instance = *match entry {
143             Entry::Occupied(e) => e.into_mut(),
144             Entry::Vacant(e) => {
145                 // Find it if it was not cached.
146                 let mut instance_and_crate: Option<(ty::Instance<'_>, CrateNum)> = None;
147                 // `dependency_formats` includes all the transitive informations needed to link a crate,
148                 // which is what we need here since we need to dig out `exported_symbols` from all transitive
149                 // dependencies.
150                 let dependency_formats = tcx.dependency_formats(());
151                 let dependency_format = dependency_formats
152                     .iter()
153                     .find(|(crate_type, _)| *crate_type == CrateType::Executable)
154                     .expect("interpreting a non-executable crate");
155                 for cnum in iter::once(LOCAL_CRATE).chain(
156                     dependency_format.1.iter().enumerate().filter_map(|(num, &linkage)| {
157                         (linkage != Linkage::NotLinked).then_some(CrateNum::new(num + 1))
158                     }),
159                 ) {
160                     // We can ignore `_export_level` here: we are a Rust crate, and everything is exported
161                     // from a Rust crate.
162                     for &(symbol, _export_level) in tcx.exported_symbols(cnum) {
163                         if let ExportedSymbol::NonGeneric(def_id) = symbol {
164                             let attrs = tcx.codegen_fn_attrs(def_id);
165                             let symbol_name = if let Some(export_name) = attrs.export_name {
166                                 export_name
167                             } else if attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE) {
168                                 tcx.item_name(def_id)
169                             } else {
170                                 // Skip over items without an explicitly defined symbol name.
171                                 continue;
172                             };
173                             if symbol_name == link_name {
174                                 if let Some((original_instance, original_cnum)) = instance_and_crate
175                                 {
176                                     // Make sure we are consistent wrt what is 'first' and 'second'.
177                                     let original_span =
178                                         tcx.def_span(original_instance.def_id()).data();
179                                     let span = tcx.def_span(def_id).data();
180                                     if original_span < span {
181                                         throw_machine_stop!(
182                                             TerminationInfo::MultipleSymbolDefinitions {
183                                                 link_name,
184                                                 first: original_span,
185                                                 first_crate: tcx.crate_name(original_cnum),
186                                                 second: span,
187                                                 second_crate: tcx.crate_name(cnum),
188                                             }
189                                         );
190                                     } else {
191                                         throw_machine_stop!(
192                                             TerminationInfo::MultipleSymbolDefinitions {
193                                                 link_name,
194                                                 first: span,
195                                                 first_crate: tcx.crate_name(cnum),
196                                                 second: original_span,
197                                                 second_crate: tcx.crate_name(original_cnum),
198                                             }
199                                         );
200                                     }
201                                 }
202                                 if !matches!(tcx.def_kind(def_id), DefKind::Fn | DefKind::AssocFn) {
203                                     throw_ub_format!(
204                                         "attempt to call an exported symbol that is not defined as a function"
205                                     );
206                                 }
207                                 instance_and_crate = Some((ty::Instance::mono(tcx, def_id), cnum));
208                             }
209                         }
210                     }
211                 }
212
213                 e.insert(instance_and_crate.map(|ic| ic.0))
214             }
215         };
216         match instance {
217             None => Ok(None), // no symbol with this name
218             Some(instance) => Ok(Some((this.load_mir(instance.def, None)?, instance))),
219         }
220     }
221
222     /// Emulates calling a foreign item, failing if the item is not supported.
223     /// This function will handle `goto_block` if needed.
224     /// Returns Ok(None) if the foreign item was completely handled
225     /// by this function.
226     /// Returns Ok(Some(body)) if processing the foreign item
227     /// is delegated to another function.
228     fn emulate_foreign_item(
229         &mut self,
230         def_id: DefId,
231         abi: Abi,
232         args: &[OpTy<'tcx, Tag>],
233         ret: Option<(&PlaceTy<'tcx, Tag>, mir::BasicBlock)>,
234         unwind: StackPopUnwind,
235     ) -> InterpResult<'tcx, Option<(&'mir mir::Body<'tcx>, ty::Instance<'tcx>)>> {
236         let this = self.eval_context_mut();
237         let attrs = this.tcx.get_attrs(def_id);
238         let link_name = this
239             .tcx
240             .sess
241             .first_attr_value_str_by_name(&attrs, sym::link_name)
242             .unwrap_or_else(|| this.tcx.item_name(def_id));
243         let tcx = this.tcx.tcx;
244
245         // First: functions that diverge.
246         let (dest, ret) = match ret {
247             None =>
248                 match &*link_name.as_str() {
249                     "miri_start_panic" => {
250                         // `check_shim` happens inside `handle_miri_start_panic`.
251                         this.handle_miri_start_panic(abi, link_name, args, unwind)?;
252                         return Ok(None);
253                     }
254                     // This matches calls to the foreign item `panic_impl`.
255                     // The implementation is provided by the function with the `#[panic_handler]` attribute.
256                     "panic_impl" => {
257                         // We don't use `check_shim` here because we are just forwarding to the lang
258                         // item. Argument count checking will be performed when the returned `Body` is
259                         // called.
260                         this.check_abi_and_shim_symbol_clash(abi, Abi::Rust, link_name)?;
261                         let panic_impl_id = tcx.lang_items().panic_impl().unwrap();
262                         let panic_impl_instance = ty::Instance::mono(tcx, panic_impl_id);
263                         return Ok(Some((
264                             &*this.load_mir(panic_impl_instance.def, None)?,
265                             panic_impl_instance,
266                         )));
267                     }
268                     #[rustfmt::skip]
269                     | "exit"
270                     | "ExitProcess"
271                     => {
272                         let exp_abi = if link_name.as_str() == "exit" {
273                             Abi::C { unwind: false }
274                         } else {
275                             Abi::System { unwind: false }
276                         };
277                         let &[ref code] = this.check_shim(abi, exp_abi, link_name, args)?;
278                         // it's really u32 for ExitProcess, but we have to put it into the `Exit` variant anyway
279                         let code = this.read_scalar(code)?.to_i32()?;
280                         throw_machine_stop!(TerminationInfo::Exit(code.into()));
281                     }
282                     "abort" => {
283                         let &[] =
284                             this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
285                         throw_machine_stop!(TerminationInfo::Abort(
286                             "the program aborted execution".to_owned()
287                         ))
288                     }
289                     _ => {
290                         if let Some(body) = this.lookup_exported_symbol(link_name)? {
291                             return Ok(Some(body));
292                         }
293                         this.handle_unsupported(format!(
294                             "can't call (diverging) foreign function: {}",
295                             link_name
296                         ))?;
297                         return Ok(None);
298                     }
299                 },
300             Some(p) => p,
301         };
302
303         // Second: functions that return.
304         match this.emulate_foreign_item_by_name(link_name, abi, args, dest, ret)? {
305             EmulateByNameResult::NeedsJumping => {
306                 trace!("{:?}", this.dump_place(**dest));
307                 this.go_to_block(ret);
308             }
309             EmulateByNameResult::AlreadyJumped => (),
310             EmulateByNameResult::MirBody(mir, instance) => return Ok(Some((mir, instance))),
311             EmulateByNameResult::NotSupported => {
312                 if let Some(body) = this.lookup_exported_symbol(link_name)? {
313                     return Ok(Some(body));
314                 }
315
316                 this.handle_unsupported(format!("can't call foreign function: {}", link_name))?;
317                 return Ok(None);
318             }
319         }
320
321         Ok(None)
322     }
323
324     /// Emulates calling the internal __rust_* allocator functions
325     fn emulate_allocator(
326         &mut self,
327         symbol: Symbol,
328         default: impl FnOnce(&mut MiriEvalContext<'mir, 'tcx>) -> InterpResult<'tcx>,
329     ) -> InterpResult<'tcx, EmulateByNameResult<'mir, 'tcx>> {
330         let this = self.eval_context_mut();
331
332         let allocator_kind = if let Some(allocator_kind) = this.tcx.allocator_kind(()) {
333             allocator_kind
334         } else {
335             // in real code, this symbol does not exist without an allocator
336             return Ok(EmulateByNameResult::NotSupported);
337         };
338
339         match allocator_kind {
340             AllocatorKind::Global => {
341                 let (body, instance) = this
342                     .lookup_exported_symbol(symbol)?
343                     .expect("symbol should be present if there is a global allocator");
344
345                 Ok(EmulateByNameResult::MirBody(body, instance))
346             }
347             AllocatorKind::Default => {
348                 default(this)?;
349                 Ok(EmulateByNameResult::NeedsJumping)
350             }
351         }
352     }
353
354     /// Emulates calling a foreign item using its name.
355     fn emulate_foreign_item_by_name(
356         &mut self,
357         link_name: Symbol,
358         abi: Abi,
359         args: &[OpTy<'tcx, Tag>],
360         dest: &PlaceTy<'tcx, Tag>,
361         ret: mir::BasicBlock,
362     ) -> InterpResult<'tcx, EmulateByNameResult<'mir, 'tcx>> {
363         let this = self.eval_context_mut();
364
365         // Here we dispatch all the shims for foreign functions. If you have a platform specific
366         // shim, add it to the corresponding submodule.
367         match &*link_name.as_str() {
368             // Miri-specific extern functions
369             "miri_static_root" => {
370                 let &[ref ptr] = this.check_shim(abi, Abi::Rust, link_name, args)?;
371                 let ptr = this.read_pointer(ptr)?;
372                 let (alloc_id, offset, _) = this.ptr_get_alloc_id(ptr)?;
373                 if offset != Size::ZERO {
374                     throw_unsup_format!("pointer passed to miri_static_root must point to beginning of an allocated block");
375                 }
376                 this.machine.static_roots.push(alloc_id);
377             }
378
379             // Obtains the size of a Miri backtrace. See the README for details.
380             "miri_backtrace_size" => {
381                 this.handle_miri_backtrace_size(abi, link_name, args, dest)?;
382             }
383
384             // Obtains a Miri backtrace. See the README for details.
385             "miri_get_backtrace" => {
386                 // `check_shim` happens inside `handle_miri_get_backtrace`.
387                 this.handle_miri_get_backtrace(abi, link_name, args, dest)?;
388             }
389
390             // Resolves a Miri backtrace frame. See the README for details.
391             "miri_resolve_frame" => {
392                 // `check_shim` happens inside `handle_miri_resolve_frame`.
393                 this.handle_miri_resolve_frame(abi, link_name, args, dest)?;
394             }
395
396             // Writes the function and file names of a Miri backtrace frame into a user provided buffer. See the README for details.
397             "miri_resolve_frame_names" => {
398                 this.handle_miri_resolve_frame_names(abi, link_name, args)?;
399             }
400
401             // Standard C allocation
402             "malloc" => {
403                 let &[ref size] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
404                 let size = this.read_scalar(size)?.to_machine_usize(this)?;
405                 let res = this.malloc(size, /*zero_init:*/ false, MiriMemoryKind::C)?;
406                 this.write_pointer(res, dest)?;
407             }
408             "calloc" => {
409                 let &[ref items, ref len] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
410                 let items = this.read_scalar(items)?.to_machine_usize(this)?;
411                 let len = this.read_scalar(len)?.to_machine_usize(this)?;
412                 let size =
413                     items.checked_mul(len).ok_or_else(|| err_ub_format!("overflow during calloc size computation"))?;
414                 let res = this.malloc(size, /*zero_init:*/ true, MiriMemoryKind::C)?;
415                 this.write_pointer(res, dest)?;
416             }
417             "free" => {
418                 let &[ref ptr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
419                 let ptr = this.read_pointer(ptr)?;
420                 this.free(ptr, MiriMemoryKind::C)?;
421             }
422             "realloc" => {
423                 let &[ref old_ptr, ref new_size] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
424                 let old_ptr = this.read_pointer(old_ptr)?;
425                 let new_size = this.read_scalar(new_size)?.to_machine_usize(this)?;
426                 let res = this.realloc(old_ptr, new_size, MiriMemoryKind::C)?;
427                 this.write_pointer(res, dest)?;
428             }
429
430             // Rust allocation
431             "__rust_alloc" => {
432                 let &[ref size, ref align] = this.check_shim(abi, Abi::Rust, link_name, args)?;
433                 let size = this.read_scalar(size)?.to_machine_usize(this)?;
434                 let align = this.read_scalar(align)?.to_machine_usize(this)?;
435
436                 return this.emulate_allocator(Symbol::intern("__rg_alloc"), |this| {
437                     Self::check_alloc_request(size, align)?;
438
439                     let ptr = this.allocate_ptr(
440                         Size::from_bytes(size),
441                         Align::from_bytes(align).unwrap(),
442                         MiriMemoryKind::Rust.into(),
443                     )?;
444
445                     this.write_pointer(ptr, dest)
446                 });
447             }
448             "__rust_alloc_zeroed" => {
449                 let &[ref size, ref align] = this.check_shim(abi, Abi::Rust, link_name, args)?;
450                 let size = this.read_scalar(size)?.to_machine_usize(this)?;
451                 let align = this.read_scalar(align)?.to_machine_usize(this)?;
452
453                 return this.emulate_allocator(Symbol::intern("__rg_alloc_zeroed"), |this| {
454                     Self::check_alloc_request(size, align)?;
455
456                     let ptr = this.allocate_ptr(
457                         Size::from_bytes(size),
458                         Align::from_bytes(align).unwrap(),
459                         MiriMemoryKind::Rust.into(),
460                     )?;
461
462                     // We just allocated this, the access is definitely in-bounds.
463                     this.write_bytes_ptr(ptr.into(), iter::repeat(0u8).take(usize::try_from(size).unwrap())).unwrap();
464                     this.write_pointer(ptr, dest)
465                 });
466             }
467             "__rust_dealloc" => {
468                 let &[ref ptr, ref old_size, ref align] = this.check_shim(abi, Abi::Rust, link_name, args)?;
469                 let ptr = this.read_pointer(ptr)?;
470                 let old_size = this.read_scalar(old_size)?.to_machine_usize(this)?;
471                 let align = this.read_scalar(align)?.to_machine_usize(this)?;
472
473                 return this.emulate_allocator(Symbol::intern("__rg_dealloc"), |this| {
474                     // No need to check old_size/align; we anyway check that they match the allocation.
475                     this.deallocate_ptr(
476                         ptr,
477                         Some((Size::from_bytes(old_size), Align::from_bytes(align).unwrap())),
478                         MiriMemoryKind::Rust.into(),
479                     )
480                 });
481             }
482             "__rust_realloc" => {
483                 let &[ref ptr, ref old_size, ref align, ref new_size] = this.check_shim(abi, Abi::Rust, link_name, args)?;
484                 let ptr = this.read_pointer(ptr)?;
485                 let old_size = this.read_scalar(old_size)?.to_machine_usize(this)?;
486                 let align = this.read_scalar(align)?.to_machine_usize(this)?;
487                 let new_size = this.read_scalar(new_size)?.to_machine_usize(this)?;
488                 // No need to check old_size; we anyway check that they match the allocation.
489
490                 return this.emulate_allocator(Symbol::intern("__rg_realloc"), |this| {
491                     Self::check_alloc_request(new_size, align)?;
492
493                     let align = Align::from_bytes(align).unwrap();
494                     let new_ptr = this.reallocate_ptr(
495                         ptr,
496                         Some((Size::from_bytes(old_size), align)),
497                         Size::from_bytes(new_size),
498                         align,
499                         MiriMemoryKind::Rust.into(),
500                     )?;
501                     this.write_pointer(new_ptr, dest)
502                 });
503             }
504
505             // C memory handling functions
506             "memcmp" => {
507                 let &[ref left, ref right, ref n] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
508                 let left = this.read_pointer(left)?;
509                 let right = this.read_pointer(right)?;
510                 let n = Size::from_bytes(this.read_scalar(n)?.to_machine_usize(this)?);
511
512                 let result = {
513                     let left_bytes = this.read_bytes_ptr(left, n)?;
514                     let right_bytes = this.read_bytes_ptr(right, n)?;
515
516                     use std::cmp::Ordering::*;
517                     match left_bytes.cmp(right_bytes) {
518                         Less => -1i32,
519                         Equal => 0,
520                         Greater => 1,
521                     }
522                 };
523
524                 this.write_scalar(Scalar::from_i32(result), dest)?;
525             }
526             "memrchr" => {
527                 let &[ref ptr, ref val, ref num] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
528                 let ptr = this.read_pointer(ptr)?;
529                 let val = this.read_scalar(val)?.to_i32()? as u8;
530                 let num = this.read_scalar(num)?.to_machine_usize(this)?;
531                 if let Some(idx) = this
532                     .read_bytes_ptr(ptr, Size::from_bytes(num))?
533                     .iter()
534                     .rev()
535                     .position(|&c| c == val)
536                 {
537                     let new_ptr = ptr.offset(Size::from_bytes(num - idx as u64 - 1), this)?;
538                     this.write_pointer(new_ptr, dest)?;
539                 } else {
540                     this.write_null(dest)?;
541                 }
542             }
543             "memchr" => {
544                 let &[ref ptr, ref val, ref num] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
545                 let ptr = this.read_pointer(ptr)?;
546                 let val = this.read_scalar(val)?.to_i32()? as u8;
547                 let num = this.read_scalar(num)?.to_machine_usize(this)?;
548                 let idx = this
549                     .read_bytes_ptr(ptr, Size::from_bytes(num))?
550                     .iter()
551                     .position(|&c| c == val);
552                 if let Some(idx) = idx {
553                     let new_ptr = ptr.offset(Size::from_bytes(idx as u64), this)?;
554                     this.write_pointer(new_ptr, dest)?;
555                 } else {
556                     this.write_null(dest)?;
557                 }
558             }
559             "strlen" => {
560                 let &[ref ptr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
561                 let ptr = this.read_pointer(ptr)?;
562                 let n = this.read_c_str(ptr)?.len();
563                 this.write_scalar(Scalar::from_machine_usize(u64::try_from(n).unwrap(), this), dest)?;
564             }
565
566             // math functions
567             #[rustfmt::skip]
568             | "cbrtf"
569             | "coshf"
570             | "sinhf"
571             | "tanf"
572             | "acosf"
573             | "asinf"
574             | "atanf"
575             => {
576                 let &[ref f] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
577                 // FIXME: Using host floats.
578                 let f = f32::from_bits(this.read_scalar(f)?.to_u32()?);
579                 let f = match &*link_name.as_str() {
580                     "cbrtf" => f.cbrt(),
581                     "coshf" => f.cosh(),
582                     "sinhf" => f.sinh(),
583                     "tanf" => f.tan(),
584                     "acosf" => f.acos(),
585                     "asinf" => f.asin(),
586                     "atanf" => f.atan(),
587                     _ => bug!(),
588                 };
589                 this.write_scalar(Scalar::from_u32(f.to_bits()), dest)?;
590             }
591             #[rustfmt::skip]
592             | "_hypotf"
593             | "hypotf"
594             | "atan2f"
595             => {
596                 let &[ref f1, ref f2] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
597                 // underscore case for windows, here and below
598                 // (see https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/floating-point-primitives?view=vs-2019)
599                 // FIXME: Using host floats.
600                 let f1 = f32::from_bits(this.read_scalar(f1)?.to_u32()?);
601                 let f2 = f32::from_bits(this.read_scalar(f2)?.to_u32()?);
602                 let n = match &*link_name.as_str() {
603                     "_hypotf" | "hypotf" => f1.hypot(f2),
604                     "atan2f" => f1.atan2(f2),
605                     _ => bug!(),
606                 };
607                 this.write_scalar(Scalar::from_u32(n.to_bits()), dest)?;
608             }
609             #[rustfmt::skip]
610             | "cbrt"
611             | "cosh"
612             | "sinh"
613             | "tan"
614             | "acos"
615             | "asin"
616             | "atan"
617             => {
618                 let &[ref f] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
619                 // FIXME: Using host floats.
620                 let f = f64::from_bits(this.read_scalar(f)?.to_u64()?);
621                 let f = match &*link_name.as_str() {
622                     "cbrt" => f.cbrt(),
623                     "cosh" => f.cosh(),
624                     "sinh" => f.sinh(),
625                     "tan" => f.tan(),
626                     "acos" => f.acos(),
627                     "asin" => f.asin(),
628                     "atan" => f.atan(),
629                     _ => bug!(),
630                 };
631                 this.write_scalar(Scalar::from_u64(f.to_bits()), dest)?;
632             }
633             #[rustfmt::skip]
634             | "_hypot"
635             | "hypot"
636             | "atan2"
637             => {
638                 let &[ref f1, ref f2] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
639                 // FIXME: Using host floats.
640                 let f1 = f64::from_bits(this.read_scalar(f1)?.to_u64()?);
641                 let f2 = f64::from_bits(this.read_scalar(f2)?.to_u64()?);
642                 let n = match &*link_name.as_str() {
643                     "_hypot" | "hypot" => f1.hypot(f2),
644                     "atan2" => f1.atan2(f2),
645                     _ => bug!(),
646                 };
647                 this.write_scalar(Scalar::from_u64(n.to_bits()), dest)?;
648             }
649             #[rustfmt::skip]
650             | "_ldexp"
651             | "ldexp"
652             | "scalbn"
653             => {
654                 let &[ref x, ref exp] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
655                 // For radix-2 (binary) systems, `ldexp` and `scalbn` are the same.
656                 let x = this.read_scalar(x)?.to_f64()?;
657                 let exp = this.read_scalar(exp)?.to_i32()?;
658
659                 // Saturating cast to i16. Even those are outside the valid exponent range to
660                 // `scalbn` below will do its over/underflow handling.
661                 let exp = if exp > i32::from(i16::MAX) {
662                     i16::MAX
663                 } else if exp < i32::from(i16::MIN) {
664                     i16::MIN
665                 } else {
666                     exp.try_into().unwrap()
667                 };
668
669                 let res = x.scalbn(exp);
670                 this.write_scalar(Scalar::from_f64(res), dest)?;
671             }
672
673             // Architecture-specific shims
674             "llvm.x86.sse2.pause" if this.tcx.sess.target.arch == "x86" || this.tcx.sess.target.arch == "x86_64" => {
675                 let &[] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
676                 this.yield_active_thread();
677             }
678             "llvm.aarch64.isb" if this.tcx.sess.target.arch == "aarch64" => {
679                 let &[ref arg] = this.check_shim(abi, Abi::Unadjusted, link_name, args)?;
680                 let arg = this.read_scalar(arg)?.to_i32()?;
681                 match arg {
682                     15 => { // SY ("full system scope")
683                         this.yield_active_thread();
684                     }
685                     _ => {
686                         throw_unsup_format!("unsupported llvm.aarch64.isb argument {}", arg);
687                     }
688                 }
689             }
690
691             // Platform-specific shims
692             _ => match this.tcx.sess.target.os.as_ref() {
693                 "linux" | "macos" => return shims::posix::foreign_items::EvalContextExt::emulate_foreign_item_by_name(this, link_name, abi, args, dest, ret),
694                 "windows" => return shims::windows::foreign_items::EvalContextExt::emulate_foreign_item_by_name(this, link_name, abi, args, dest, ret),
695                 target => throw_unsup_format!("the target `{}` is not supported", target),
696             }
697         };
698
699         // We only fall through to here if we did *not* hit the `_` arm above,
700         // i.e., if we actually emulated the function.
701         Ok(EmulateByNameResult::NeedsJumping)
702     }
703
704     /// Check some basic requirements for this allocation request:
705     /// non-zero size, power-of-two alignment.
706     fn check_alloc_request(size: u64, align: u64) -> InterpResult<'tcx> {
707         if size == 0 {
708             throw_ub_format!("creating allocation with size 0");
709         }
710         if !align.is_power_of_two() {
711             throw_ub_format!("creating allocation with non-power-of-two alignment {}", align);
712         }
713         Ok(())
714     }
715 }