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