Skip to main content

wasmer_compiler/engine/
mapped_binary.rs

1use std::{
2    ffi::c_void,
3    fs::File,
4    sync::{Arc, Mutex},
5};
6#[cfg(unix)]
7use std::{os::fd::RawFd, ptr, slice};
8
9#[cfg(unix)]
10use itertools::Itertools;
11use object::{Object, ObjectSection, ReadRef};
12#[cfg(unix)]
13use object::{ObjectSegment, ObjectSymbol, ObjectSymbolTable, SegmentFlags, elf};
14use wasmer_vm::LibCall;
15#[cfg(unix)]
16use wasmer_vm::libcalls::function_pointer;
17
18use crate::GlobalFrameInfoRegistration;
19#[cfg(unix)]
20use crate::engine::unwind::UnwindRegistry;
21
22/// The `gimli` reader type used for DWARF sections loaded from an ELF image.
23///
24/// Each section's bytes are copied out of the source image into their own
25/// `Arc<[u8]>`, so the reader is independent of the lifetime of the
26/// `object::File` (or the buffer it was parsed from) used to load it.
27pub type DwarfReader = gimli::EndianArcSlice<gimli::RunTimeEndian>;
28
29/// Lazily-loaded DWARF debug info for an ELF-backed artifact.
30///
31/// Building an `addr2line::Context` parses the DWARF sections eagerly, which
32/// is wasted work for modules that are never symbolicated (e.g. no trap or
33/// backtrace ever occurs). This defers that work until the first lookup.
34#[derive(Clone)]
35pub(crate) enum DebugInfoSource {
36    Bytes(Arc<[u8]>),
37    // CAVEAT: The debug info parsing and trap resolution can happen simultaneously and the opened
38    // files will share e.g. seek position. Thus Mutex is used.
39    File(Arc<Mutex<File>>),
40}
41
42pub(crate) struct DebugInfo {
43    /// The ELF image, kept around (or reopened) so the DWARF sections can be
44    /// loaded on first use. `None` for non-ELF artifacts.
45    elf_data: Option<DebugInfoSource>,
46    /// `None` until first accessed; `Some(None)` once loading was attempted
47    /// and failed (or there was no ELF image to load from).
48    ///
49    /// `addr2line::Context` caches parsed DWARF units behind plain
50    /// `OnceCell`s internally, so it is `Send` but not `Sync` — a `Mutex`
51    /// serializes lookups from concurrent backtraces/traps instead of
52    /// exposing a `&Context` that could be read from multiple threads at
53    /// once.
54    context: Mutex<Option<Option<addr2line::Context<DwarfReader>>>>,
55}
56
57impl DebugInfo {
58    pub(crate) fn new(elf_data: Option<DebugInfoSource>) -> Self {
59        Self {
60            elf_data,
61            context: Mutex::new(None),
62        }
63    }
64
65    /// Runs `f` with the DWARF context, building it from the ELF image on
66    /// first access. `f` receives `None` if there is no ELF image, or the
67    /// image has no (or malformed) DWARF debug info.
68    pub(crate) fn with_context<T>(
69        &self,
70        f: impl FnOnce(Option<&addr2line::Context<DwarfReader>>) -> T,
71    ) -> T {
72        let mut context = self.context.lock().unwrap();
73        let context = context.get_or_insert_with(|| {
74            let elf_data = match self.elf_data.as_ref()? {
75                DebugInfoSource::Bytes(data) => data.clone(),
76                DebugInfoSource::File(file) => {
77                    let guard = file.lock().unwrap();
78                    let mut file = guard.try_clone().ok()?;
79                    use std::io::{Read as _, Seek as _};
80                    file.rewind().ok()?;
81                    let mut data = Vec::new();
82                    file.read_to_end(&mut data).ok()?;
83                    Arc::from(data)
84                }
85            };
86            let object_file = object::File::parse(&elf_data[..]).ok()?;
87            load_dwarf_context(&object_file).ok()
88        });
89        f(context.as_ref())
90    }
91}
92
93fn load_dwarf_context(
94    object_file: &object::File<'_>,
95) -> Result<addr2line::Context<DwarfReader>, gimli::Error> {
96    let endian = if object_file.is_little_endian() {
97        gimli::RunTimeEndian::Little
98    } else {
99        gimli::RunTimeEndian::Big
100    };
101
102    let load_section = |id: gimli::SectionId| -> Result<DwarfReader, gimli::Error> {
103        let data: Vec<u8> = object_file
104            .section_by_name(id.name())
105            .and_then(|section| section.uncompressed_data().ok())
106            .map(|data| data.into_owned())
107            .unwrap_or_default();
108        Ok(gimli::EndianReader::new(Arc::from(data), endian))
109    };
110
111    let dwarf = gimli::Dwarf::load(load_section)?;
112    addr2line::Context::from_dwarf(dwarf)
113}
114
115/// Maps an ELF dynamic-relocation symbol name to the `LibCall` it refers to.
116///
117/// Shared with `wasmer_compiler_llvm::object_file`, which resolves the same
118/// symbol names when linking an experimental artifact compilation into an object
119/// file in the first place.
120pub static LIBCALLS_ELF: phf::Map<&'static str, LibCall> = phf::phf_map! {
121    "ceilf" => LibCall::CeilF32,
122    "ceil" => LibCall::CeilF64,
123    "floorf" => LibCall::FloorF32,
124    "floor" => LibCall::FloorF64,
125    "nearbyintf" => LibCall::NearestF32,
126    "nearbyint" => LibCall::NearestF64,
127    "sqrtf" => LibCall::SqrtF32,
128    "sqrt" => LibCall::SqrtF64,
129    "truncf" => LibCall::TruncF32,
130    "trunc" => LibCall::TruncF64,
131    "__chkstk" => LibCall::Probestack,
132    "wasmer_vm_f32_ceil" => LibCall::CeilF32,
133    "wasmer_vm_f64_ceil" => LibCall::CeilF64,
134    "wasmer_vm_f32_floor" => LibCall::FloorF32,
135    "wasmer_vm_f64_floor" => LibCall::FloorF64,
136    "wasmer_vm_f32_nearest" => LibCall::NearestF32,
137    "wasmer_vm_f64_nearest" => LibCall::NearestF64,
138    "wasmer_vm_f32_sqrt" => LibCall::SqrtF32,
139    "wasmer_vm_f64_sqrt" => LibCall::SqrtF64,
140    "wasmer_vm_f32_trunc" => LibCall::TruncF32,
141    "wasmer_vm_f64_trunc" => LibCall::TruncF64,
142    "wasmer_vm_memory32_size" => LibCall::Memory32Size,
143    "wasmer_vm_imported_memory32_size" => LibCall::ImportedMemory32Size,
144    "wasmer_vm_table_copy" => LibCall::TableCopy,
145    "wasmer_vm_table_init" => LibCall::TableInit,
146    "wasmer_vm_table_fill" => LibCall::TableFill,
147    "wasmer_vm_table_size" => LibCall::TableSize,
148    "wasmer_vm_imported_table_size" => LibCall::ImportedTableSize,
149    "wasmer_vm_table_get" => LibCall::TableGet,
150    "wasmer_vm_imported_table_get" => LibCall::ImportedTableGet,
151    "wasmer_vm_table_set" => LibCall::TableSet,
152    "wasmer_vm_imported_table_set" => LibCall::ImportedTableSet,
153    "wasmer_vm_table_grow" => LibCall::TableGrow,
154    "wasmer_vm_imported_table_grow" => LibCall::ImportedTableGrow,
155    "wasmer_vm_func_ref" => LibCall::FuncRef,
156    "wasmer_vm_elem_drop" => LibCall::ElemDrop,
157    "wasmer_vm_memory32_copy" => LibCall::Memory32Copy,
158    "wasmer_vm_memory32_fill" => LibCall::Memory32Fill,
159    "wasmer_vm_imported_memory32_fill" => LibCall::ImportedMemory32Fill,
160    "wasmer_vm_memory32_init" => LibCall::Memory32Init,
161    "wasmer_vm_data_drop" => LibCall::DataDrop,
162    "wasmer_vm_raise_trap" => LibCall::RaiseTrap,
163    "wasmer_vm_memory32_atomic_wait32" => LibCall::Memory32AtomicWait32,
164    "wasmer_vm_imported_memory32_atomic_wait32" => LibCall::ImportedMemory32AtomicWait32,
165    "wasmer_vm_memory32_atomic_wait64" => LibCall::Memory32AtomicWait64,
166    "wasmer_vm_imported_memory32_atomic_wait64" => LibCall::ImportedMemory32AtomicWait64,
167    "wasmer_vm_memory32_atomic_notify" => LibCall::Memory32AtomicNotify,
168    "wasmer_vm_imported_memory32_atomic_notify" => LibCall::ImportedMemory32AtomicNotify,
169    "wasmer_vm_throw" => LibCall::Throw,
170    "wasmer_vm_alloc_exception" => LibCall::AllocException,
171    "wasmer_vm_read_exnref" => LibCall::ReadExnRef,
172    "wasmer_vm_exception_into_exnref" => LibCall::LibunwindExceptionIntoExnRef,
173    "wasmer_eh_personality" => LibCall::EHPersonality,
174    "wasmer_eh_personality2" => LibCall::EHPersonality2,
175    "wasmer_vm_dbg_usize" => LibCall::DebugUsize,
176    "wasmer_vm_dbg_str" => LibCall::DebugStr,
177};
178
179#[cfg(unix)]
180#[derive(Debug)]
181struct ImageSegment {
182    pub(crate) mem_address: usize,
183    pub(crate) mem_size: usize,
184    pub(crate) file_address: usize,
185    pub(crate) file_size: usize,
186    pub(crate) page_size: usize,
187    pub(crate) flags: SegmentFlags,
188}
189
190#[cfg(unix)]
191impl ImageSegment {
192    fn protection(&self) -> Result<i32, String> {
193        let (read, write, exec) = match self.flags {
194            SegmentFlags::Elf { p_flags, .. } => (
195                p_flags.contains(elf::PF_R),
196                p_flags.contains(elf::PF_W),
197                p_flags.contains(elf::PF_X),
198            ),
199            _ => return Err(format!("unsupported segment flags: {:?}", self.flags)),
200        };
201
202        let mut protection = 0;
203        if read {
204            protection |= libc::PROT_READ;
205        }
206        if write {
207            protection |= libc::PROT_WRITE;
208        }
209        if exec {
210            protection |= libc::PROT_EXEC;
211        }
212        Ok(protection)
213    }
214
215    fn mem_size_page_aligned(&self) -> usize {
216        (self.mem_size + (self.mem_address - self.mem_address_page_aligned()))
217            .next_multiple_of(self.page_size)
218    }
219
220    fn mem_address_page_aligned(&self) -> usize {
221        self.mem_address & !(self.page_size - 1)
222    }
223
224    fn file_size_page_aligned(&self) -> usize {
225        (self.file_size + (self.file_address - self.file_address_page_aligned()))
226            .next_multiple_of(self.page_size)
227    }
228
229    fn file_address_page_aligned(&self) -> usize {
230        self.file_address & !(self.page_size - 1)
231    }
232}
233
234// A data structure holding a memory map of a binary in the memory.
235pub(crate) struct MemoryMappedBinary {
236    #[cfg(unix)]
237    base: *mut c_void,
238    #[cfg(unix)]
239    size: usize,
240
241    // Unwind registry associated with the binary.
242    #[cfg(unix)]
243    unwind_registry: Option<UnwindRegistry>,
244
245    // Keeps the module's frame info alive in the global registry for exactly
246    // as long as this mapping (and thus the code it points at) is alive.
247    #[cfg(unix)]
248    frame_info_registration: Option<GlobalFrameInfoRegistration>,
249}
250
251// SAFETY: memory mapped base pointer does not escape the type.
252unsafe impl Send for MemoryMappedBinary {}
253unsafe impl Sync for MemoryMappedBinary {}
254
255#[cfg(unix)]
256impl MemoryMappedBinary {
257    /// Maps `object_file`'s load segments into a freshly allocated, private
258    /// virtual address range, copying segment bytes out of the in-memory
259    /// `data` buffer (rather than mapping a file directly).
260    pub(crate) fn try_from_bytes<'a, R: ReadRef<'a>>(
261        object_file: &object::File<'a, R>,
262        data: &[u8],
263    ) -> Result<Self, String> {
264        Self::try_from_source(object_file, Some(data), None)
265    }
266
267    /// Maps an ELF image's load segments directly from an open file.
268    pub(crate) fn try_from_file<'a, R: ReadRef<'a>>(
269        object_file: &object::File<'a, R>,
270        file: RawFd,
271    ) -> Result<Self, String> {
272        Self::try_from_source(object_file, None, Some(file))
273    }
274
275    fn try_from_source<'a, R: ReadRef<'a>>(
276        object_file: &object::File<'a, R>,
277        data: Option<&[u8]>,
278        file: Option<RawFd>,
279    ) -> Result<Self, String> {
280        let page_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) };
281        if page_size == -1 {
282            return Err("Cannot get page size".to_string());
283        }
284        let page_size = page_size as usize;
285
286        let segments = object_file
287            .segments()
288            .map(|segment| {
289                let mem_address = segment.address() as usize;
290                let mem_size = segment.size() as usize;
291                let (file_address, file_size) = segment.file_range();
292                let file_address = file_address as usize;
293                let file_size = file_size as usize;
294                ImageSegment {
295                    mem_address,
296                    mem_size,
297                    file_address,
298                    file_size,
299                    page_size,
300                    flags: segment.flags(),
301                }
302            })
303            .collect_vec();
304        let last_segment = segments
305            .last()
306            .ok_or("at least one segment is mandatory".to_string())?;
307        let total_memory_size =
308            last_segment.mem_address_page_aligned() + last_segment.mem_size_page_aligned();
309
310        // Create a contiguous virtual address memory map that will be populated
311        // per-partes with the individual protection flags.
312        let map = Self::new_mmap(total_memory_size)?;
313        let base = map.base();
314
315        // Mmap individual load segments
316        for load_segment in segments {
317            // The virtual offset does not need to start at a page boundary.
318            if load_segment.file_address % page_size != load_segment.mem_address % page_size {
319                return Err(format!(
320                    "Load segment file offset 0x{:x} and virtual address 0x{:x} have incompatible page alignment",
321                    load_segment.file_address, load_segment.mem_address
322                ));
323            }
324
325            let protection = load_segment.protection()?;
326
327            let offset = load_segment.mem_address_page_aligned();
328            let size = load_segment.file_size_page_aligned();
329            let file_offset = load_segment.file_address_page_aligned();
330            let result = if let Some(file) = file {
331                map.map_file(offset, size, protection, file, file_offset)
332            } else {
333                map.map_copy(
334                    offset,
335                    size,
336                    protection,
337                    data.expect("byte-backed mapping requires image data"),
338                    file_offset,
339                )
340            };
341            result.map_err(|error| {
342                format!(
343                    "Cannot map load segment at virtual address 0x{:x}: {error}",
344                    load_segment.mem_address_page_aligned()
345                )
346            })?;
347
348            if load_segment.mem_size_page_aligned() > load_segment.file_size_page_aligned() {
349                map.map_zero(
350                    load_segment.mem_address_page_aligned() + load_segment.file_size_page_aligned(),
351                    load_segment.mem_size_page_aligned() - load_segment.file_size_page_aligned(),
352                    protection,
353                )
354                .map_err(|error| format!("Cannot map zero-fill segment tail: {error}"))?;
355            }
356            if load_segment.mem_size_page_aligned() < load_segment.file_size_page_aligned() {
357                return Err("invalid memory segment with larger file representation".to_string());
358            }
359        }
360
361        // Apply dynamic relocations for the libcalls
362        if let Some(dynamic_relocations) = object_file.dynamic_relocations() {
363            let dynamic_symbols = object_file.dynamic_symbol_table().unwrap();
364            let architecture = object_file.architecture();
365
366            for (offset, relocation) in dynamic_relocations {
367                let rel_flags = relocation.flags();
368                if matches!(
369                    (architecture, rel_flags),
370                    (
371                        object::Architecture::X86_64,
372                        object::RelocationFlags::Elf {
373                            r_type: elf::R_X86_64_RELATIVE,
374                        },
375                    ) | (
376                        object::Architecture::Aarch64,
377                        object::RelocationFlags::Elf {
378                            r_type: elf::R_AARCH64_RELATIVE,
379                        },
380                    ) | (
381                        object::Architecture::Riscv64,
382                        object::RelocationFlags::Elf {
383                            r_type: elf::R_RISCV_RELATIVE,
384                        },
385                    ) | (
386                        object::Architecture::LoongArch64,
387                        object::RelocationFlags::Elf {
388                            r_type: elf::R_LARCH_RELATIVE,
389                        },
390                    )
391                ) {
392                    map.write_relocation(
393                        offset,
394                        (base as usize).wrapping_add(relocation.addend() as usize),
395                    )?;
396                    continue;
397                }
398
399                let object::RelocationTarget::Symbol(symbol_index) = relocation.target() else {
400                    return Err("unsupported dynamic relocation target".to_string());
401                };
402                let symbol = dynamic_symbols.symbol_by_index(symbol_index).unwrap();
403                let symbol_name = symbol.name().unwrap();
404                let Some(&libcall) = LIBCALLS_ELF.get(symbol_name) else {
405                    return Err(format!(
406                        "unsupported dynamic relocation symbol {symbol_name}"
407                    ));
408                };
409
410                let apply_absolute_relocation = || {
411                    map.write_relocation(
412                        offset,
413                        function_pointer(libcall).wrapping_add(relocation.addend() as usize),
414                    )
415                };
416                match (architecture, relocation.kind(), rel_flags) {
417                    (_, object::RelocationKind::Absolute, _) => apply_absolute_relocation()?,
418                    (
419                        object::Architecture::X86_64,
420                        object::RelocationKind::Unknown,
421                        object::RelocationFlags::Elf {
422                            r_type: elf::R_X86_64_GLOB_DAT | elf::R_X86_64_JUMP_SLOT,
423                        },
424                    ) => apply_absolute_relocation()?,
425                    (
426                        object::Architecture::Aarch64,
427                        object::RelocationKind::Unknown,
428                        object::RelocationFlags::Elf {
429                            r_type: elf::R_AARCH64_GLOB_DAT | elf::R_AARCH64_JUMP_SLOT,
430                        },
431                    ) => apply_absolute_relocation()?,
432                    (
433                        object::Architecture::Riscv64,
434                        object::RelocationKind::Unknown,
435                        object::RelocationFlags::Elf {
436                            r_type: elf::R_RISCV_64 | elf::R_RISCV_JUMP_SLOT,
437                        },
438                    ) => apply_absolute_relocation()?,
439                    (
440                        object::Architecture::LoongArch64,
441                        object::RelocationKind::Unknown,
442                        object::RelocationFlags::Elf {
443                            r_type: elf::R_LARCH_64 | elf::R_LARCH_JUMP_SLOT,
444                        },
445                    ) => apply_absolute_relocation()?,
446                    kind => return Err(format!("unsupported dynamic relocation kind {kind:?}")),
447                }
448            }
449        }
450
451        Ok(map)
452    }
453
454    fn write_relocation(&self, offset: u64, value: usize) -> Result<(), String> {
455        let err = || "Dynamic relocation exceeds allocated range".to_string();
456        let offset = usize::try_from(offset).map_err(|_| err())?;
457        let end = offset.checked_add(size_of::<usize>()).ok_or_else(err)?;
458        if end > self.size {
459            return Err(err());
460        }
461
462        // TODO: Replace this raw pointer write with a safer write operation.
463        unsafe {
464            ptr::write_unaligned(self.base.cast::<u8>().add(offset).cast::<usize>(), value);
465        }
466        Ok(())
467    }
468
469    fn new_mmap(size: usize) -> Result<Self, String> {
470        let base = unsafe {
471            libc::mmap(
472                ptr::null_mut(),
473                size,
474                libc::PROT_NONE,
475                libc::MAP_PRIVATE | libc::MAP_ANONYMOUS,
476                -1,
477                0,
478            )
479        };
480        if base == libc::MAP_FAILED {
481            return Err("Cannot create a memory map for built Artifact".to_string());
482        }
483
484        Ok(Self {
485            base,
486            size,
487            unwind_registry: Some(UnwindRegistry::new()),
488            frame_info_registration: None,
489        })
490    }
491
492    pub(crate) fn base(&self) -> *mut c_void {
493        self.base
494    }
495
496    pub(crate) fn register_frame_info(&mut self, frame_info: GlobalFrameInfoRegistration) {
497        self.frame_info_registration = Some(frame_info);
498    }
499
500    /// Returns the mapped memory as a byte slice tied to the lifetime of this map.
501    ///
502    /// # Safety
503    ///
504    /// The entire mapped range must be readable for the returned slice's lifetime.
505    #[allow(dead_code)]
506    unsafe fn as_slice(&self) -> &[u8] {
507        if self.base.is_null() || self.size == 0 {
508            return &[];
509        }
510
511        unsafe { slice::from_raw_parts(self.base.cast::<u8>(), self.size) }
512    }
513
514    #[cfg(not(target_os = "macos"))]
515    pub(crate) fn publish_eh_frame_section(
516        &mut self,
517        address: u64,
518        size: u64,
519    ) -> Result<(), String> {
520        if address.saturating_add(size) > self.size as u64 {
521            return Err("EH frame section exceeds allocated range".to_string());
522        }
523
524        let eh_frame = unsafe {
525            slice::from_raw_parts(self.base.cast::<u8>().add(address as usize), size as usize)
526        };
527        self.unwind_registry
528            .as_mut()
529            .expect("unwind registry should remain alive until MemoryMap::drop")
530            .publish_eh_frame(Some(eh_frame))
531    }
532
533    #[cfg(target_os = "macos")]
534    pub(crate) fn publish_eh_frame_section(
535        &mut self,
536        _address: u64,
537        _size: u64,
538    ) -> Result<(), String> {
539        Err("ELF artifacts are not supported on macOS".to_string())
540    }
541
542    /// Maps an anonymous zero-filled region at `offset` with the given
543    /// protection (used for a segment's BSS tail).
544    fn map_zero(&self, offset: usize, size: usize, protection: i32) -> Result<(), String> {
545        if offset + size > self.size {
546            return Err("Segment will overwrite allocated range".to_string());
547        }
548        let result = unsafe {
549            libc::mmap(
550                self.base.add(offset),
551                size,
552                protection,
553                libc::MAP_PRIVATE | libc::MAP_ANONYMOUS | libc::MAP_FIXED,
554                -1,
555                0,
556            )
557        };
558        if result == libc::MAP_FAILED {
559            return Err(std::io::Error::last_os_error().to_string());
560        }
561        Ok(())
562    }
563
564    /// Maps a region at `offset` directly from a file.
565    fn map_file(
566        &self,
567        offset: usize,
568        size: usize,
569        protection: i32,
570        file: RawFd,
571        file_offset: usize,
572    ) -> Result<(), String> {
573        if offset + size > self.size {
574            return Err("Segment will overwrite allocated range".to_string());
575        }
576        let result = unsafe {
577            libc::mmap(
578                self.base.add(offset),
579                size,
580                protection,
581                libc::MAP_PRIVATE | libc::MAP_FIXED,
582                file,
583                file_offset as libc::off_t,
584            )
585        };
586        if result == libc::MAP_FAILED {
587            return Err(std::io::Error::last_os_error().to_string());
588        }
589        Ok(())
590    }
591
592    /// Maps an anonymous region at `offset` and copies `size` bytes from
593    /// `data[file_offset..]` into it, then applies the final protection.
594    ///
595    /// Copying (rather than mapping the backing file directly) keeps this
596    /// portable: on macOS/Mach-O a file-backed `MAP_FIXED` mapping cannot be
597    /// created with executable protection, and here we don't need a real
598    /// file descriptor for the image at all.
599    fn map_copy(
600        &self,
601        offset: usize,
602        size: usize,
603        protection: i32,
604        data: &[u8],
605        file_offset: usize,
606    ) -> Result<(), String> {
607        if offset + size > self.size {
608            return Err("Segment will overwrite allocated range".to_string());
609        }
610        let dest = unsafe { self.base.add(offset) };
611        let result = unsafe {
612            libc::mmap(
613                dest,
614                size,
615                libc::PROT_READ | libc::PROT_WRITE,
616                libc::MAP_PRIVATE | libc::MAP_ANONYMOUS | libc::MAP_FIXED,
617                -1,
618                0,
619            )
620        };
621        if result == libc::MAP_FAILED {
622            return Err(std::io::Error::last_os_error().to_string());
623        }
624
625        let available = data.len().saturating_sub(file_offset).min(size);
626        unsafe {
627            ptr::copy_nonoverlapping(data.as_ptr().add(file_offset), dest as *mut u8, available);
628        }
629
630        if protection != (libc::PROT_READ | libc::PROT_WRITE)
631            && unsafe { libc::mprotect(dest, size, protection) } != 0
632        {
633            return Err(std::io::Error::last_os_error().to_string());
634        }
635        Ok(())
636    }
637}
638
639#[cfg(not(unix))]
640impl MemoryMappedBinary {
641    pub(crate) fn try_from_bytes<'a, R: ReadRef<'a>>(
642        _object_file: &object::File<'a, R>,
643        _data: &[u8],
644    ) -> Result<Self, String> {
645        Err("ELF memory mapping is only supported on Unix".to_string())
646    }
647
648    pub(crate) fn base(&self) -> *mut c_void {
649        std::ptr::null_mut()
650    }
651
652    pub(crate) fn publish_eh_frame_section(
653        &mut self,
654        _address: u64,
655        _size: u64,
656    ) -> Result<(), String> {
657        Err("ELF memory mapping is only supported on Unix".to_string())
658    }
659
660    pub(crate) fn register_frame_info(&mut self, _frame_info: GlobalFrameInfoRegistration) {}
661}
662
663#[cfg(unix)]
664impl Drop for MemoryMappedBinary {
665    fn drop(&mut self) {
666        // The registered `.eh_frame` records point into this mmap, so deregister
667        // them while the mapping is still live.
668        drop(self.unwind_registry.take());
669
670        if !self.base.is_null() && self.size != 0 {
671            unsafe {
672                libc::munmap(self.base, self.size);
673            }
674        }
675    }
676}