wasmtime/runtime/code_memory.rs
1//! Memory management for executable code.
2
3use crate::Engine;
4use crate::prelude::*;
5use crate::runtime::vm::MmapVec;
6use alloc::sync::Arc;
7use core::ops::Range;
8use object::read::elf::SectionTable;
9use object::{LittleEndian, SectionIndex, U32};
10use object::{
11 elf::{FileHeader64, SectionHeader64},
12 endian::Endianness,
13 read::elf::{FileHeader as _, SectionHeader as _},
14};
15use wasmtime_environ::StaticModuleIndex;
16use wasmtime_environ::{CompiledTrap, lookup_trap_code, obj};
17use wasmtime_unwinder::ExceptionTable;
18
19/// Management of executable memory within a `MmapVec`
20///
21/// This type consumes ownership of a region of memory and will manage the
22/// executable permissions of the contained JIT code as necessary.
23pub struct CodeMemory {
24 mmap: MmapVec,
25 #[cfg(has_host_compiler_backend)]
26 unwind_registration: Option<crate::runtime::vm::UnwindRegistration>,
27 #[cfg(feature = "debug-builtins")]
28 debug_registration: Option<crate::runtime::vm::GdbJitImageRegistration>,
29 published: bool,
30 registered: bool,
31 enable_branch_protection: bool,
32 needs_executable: bool,
33 #[cfg(feature = "debug-builtins")]
34 has_native_debug_info: bool,
35 custom_code_memory: Option<Arc<dyn CustomCodeMemory>>,
36
37 // Ranges within `self.mmap` of where the particular sections lie.
38 text: Range<usize>,
39 unwind: Range<usize>,
40 trap_data: Range<usize>,
41 wasm_data: Range<usize>,
42 address_map_data: Range<usize>,
43 stack_map_data: Range<usize>,
44 exception_data: Range<usize>,
45 frame_tables_data: Range<usize>,
46 func_name_data: Range<usize>,
47 info_data: Range<usize>,
48 wasm_dwarf: Range<usize>,
49 wasm_bytecode: Range<usize>,
50 wasm_bytecode_ends: Range<usize>,
51}
52
53impl Drop for CodeMemory {
54 fn drop(&mut self) {
55 // If there is a custom code memory handler, restore the
56 // original (non-executable) state of the memory.
57 //
58 // We do this rather than invoking `unpublish()` because we
59 // want to skip the mprotect() if we natively own the mmap and
60 // are going to munmap soon anyway.
61 if let Some(mem) = self.custom_code_memory.as_ref() {
62 if self.published && self.needs_executable {
63 let text = self.text();
64 mem.unpublish_executable(text.as_ptr(), text.len())
65 .expect("Executable memory unpublish failed");
66 }
67 }
68
69 // Drop the registrations before `self.mmap` since they (implicitly) refer to it.
70 #[cfg(has_host_compiler_backend)]
71 let _ = self.unwind_registration.take();
72 #[cfg(feature = "debug-builtins")]
73 let _ = self.debug_registration.take();
74 }
75}
76
77fn _assert() {
78 fn _assert_send_sync<T: Send + Sync>() {}
79 _assert_send_sync::<CodeMemory>();
80}
81
82/// Interface implemented by an embedder to provide custom
83/// implementations of code-memory protection and execute permissions.
84pub trait CustomCodeMemory: Send + Sync {
85 /// The minimal alignment granularity for an address region that
86 /// can be made executable.
87 ///
88 /// Wasmtime does not assume the system page size for this because
89 /// custom code-memory protection can be used when all other uses
90 /// of virtual memory are disabled.
91 fn required_alignment(&self) -> usize;
92
93 /// Publish a region of memory as executable.
94 ///
95 /// This should update permissions from the default RW
96 /// (readable/writable but not executable) to RX
97 /// (readable/executable but not writable), enforcing W^X
98 /// discipline.
99 ///
100 /// If the platform requires any data/instruction coherence
101 /// action, that should be performed as part of this hook as well.
102 ///
103 /// `ptr` and `ptr.offset(len)` are guaranteed to be aligned as
104 /// per `required_alignment()`.
105 fn publish_executable(&self, ptr: *const u8, len: usize) -> crate::Result<()>;
106
107 /// Unpublish a region of memory.
108 ///
109 /// This should perform the opposite effect of `make_executable`,
110 /// switching a range of memory back from RX (readable/executable)
111 /// to RW (readable/writable). It is guaranteed that no code is
112 /// running anymore from this region.
113 ///
114 /// `ptr` and `ptr.offset(len)` are guaranteed to be aligned as
115 /// per `required_alignment()`.
116 fn unpublish_executable(&self, ptr: *const u8, len: usize) -> crate::Result<()>;
117}
118
119impl CodeMemory {
120 /// Creates a new `CodeMemory` by taking ownership of the provided
121 /// `MmapVec`.
122 ///
123 /// The returned `CodeMemory` manages the internal `MmapVec` and the
124 /// `publish` method is used to actually make the memory executable.
125 pub fn new(engine: &Engine, mmap: MmapVec) -> Result<Self> {
126 let mmap_data = &*mmap;
127 let header = FileHeader64::<Endianness>::parse(mmap_data)
128 .map_err(obj::ObjectCrateErrorWrapper)
129 .context("failed to parse precompiled artifact as an ELF")?;
130 let endian = header
131 .endian()
132 .context("failed to parse header endianness")?;
133
134 let section_headers = header
135 .section_headers(endian, mmap_data)
136 .context("failed to parse section headers")?;
137 let strings = header
138 .section_strings(endian, mmap_data, section_headers)
139 .context("failed to parse strings table")?;
140 let sections = header
141 .sections(endian, mmap_data)
142 .context("failed to parse sections table")?;
143
144 let mut text = 0..0;
145 let mut unwind = 0..0;
146 let mut enable_branch_protection = None;
147 let mut needs_executable = true;
148 #[cfg(feature = "debug-builtins")]
149 let mut has_native_debug_info = false;
150 let mut trap_data = 0..0;
151 let mut exception_data = 0..0;
152 let mut frame_tables_data = 0..0;
153 let mut wasm_data = 0..0;
154 let mut address_map_data = 0..0;
155 let mut stack_map_data = 0..0;
156 let mut func_name_data = 0..0;
157 let mut info_data = 0..0;
158 let mut wasm_dwarf = 0..0;
159 let mut wasm_bytecode = 0..0;
160 let mut wasm_bytecode_ends = 0..0;
161 for section_header in sections.iter() {
162 let data = section_header
163 .data(endian, mmap_data)
164 .map_err(obj::ObjectCrateErrorWrapper)?;
165 let name = section_name(endian, strings, section_header)?;
166 let range = subslice_range(data, &mmap);
167
168 // Double-check that sections are all aligned properly.
169 let section_align = usize::try_from(section_header.sh_addralign(endian))?;
170 if section_align != 0 && data.len() != 0 {
171 let section_offset = data.as_ptr().addr() - mmap.as_ptr().addr();
172 ensure!(
173 section_offset % section_align == 0,
174 "section {name:?} isn't aligned to {section_align:#x}",
175 );
176 }
177
178 // Check that we don't have any relocations, which would make
179 // loading precompiled Wasm modules slower and also force them to
180 // get paged into memory from disk.
181 //
182 // We avoid using things like Cranelift's `floor`, `ceil`,
183 // etc... operators in the Wasm-to-CLIF translator specifically to
184 // avoid having to do any relocations here. This also ensures that
185 // all builtins use the same trampoline mechanism.
186 //
187 // We do, however, allow relocations in `.debug_*` DWARF sections.
188 if let Some(target_section) = reloc_section_target(§ions, section_header, endian)? {
189 let target_name = section_name(endian, strings, target_section)?;
190 ensure!(
191 target_name.starts_with(".debug_"),
192 "section {target_name:?} has unexpected relocations \
193 (defined in section {name:?})",
194 );
195 }
196
197 match name {
198 obj::ELF_WASM_BTI => match data.len() {
199 1 => enable_branch_protection = Some(data[0] != 0),
200 _ => bail!("invalid {name:?} section"),
201 },
202 ".text" => {
203 text = range;
204
205 if section_header
206 .sh_flags(endian)
207 .contains(obj::SH_WASMTIME_NOT_EXECUTED)
208 {
209 needs_executable = false;
210 }
211 }
212 #[cfg(has_host_compiler_backend)]
213 crate::runtime::vm::UnwindRegistration::SECTION_NAME => unwind = range,
214 obj::ELF_WASM_DATA => wasm_data = range,
215 obj::ELF_WASMTIME_ADDRMAP => address_map_data = range,
216 obj::ELF_WASMTIME_STACK_MAP => stack_map_data = range,
217 obj::ELF_WASMTIME_TRAPS => trap_data = range,
218 obj::ELF_WASMTIME_EXCEPTIONS => exception_data = range,
219 obj::ELF_WASMTIME_FRAMES => frame_tables_data = range,
220 obj::ELF_NAME_DATA => func_name_data = range,
221 obj::ELF_WASMTIME_INFO => info_data = range,
222 obj::ELF_WASMTIME_DWARF => wasm_dwarf = range,
223 obj::ELF_WASMTIME_WASM_BYTECODE => wasm_bytecode = range,
224 obj::ELF_WASMTIME_WASM_BYTECODE_ENDS => wasm_bytecode_ends = range,
225
226 #[cfg(feature = "debug-builtins")]
227 ".debug_info" => has_native_debug_info = true,
228
229 // These sections are expected, but we do not need to retain any
230 // info about them.
231 "" | ".symtab" | ".strtab" | ".shstrtab" | ".xdata" | obj::ELF_WASM_ENGINE => {
232 log::debug!("ignoring section {name:?}")
233 }
234 _ if name.starts_with(".debug_") || name.starts_with(".rela.debug_") => {
235 log::debug!("ignoring debug section {name:?}")
236 }
237
238 _ => bail!("unexpected section {name:?} in Wasm compilation artifact"),
239 }
240 }
241
242 // Silence unused `mut` warning.
243 #[cfg(not(has_host_compiler_backend))]
244 let _ = &mut unwind;
245
246 // Ensure that the exception table is well-formed. This parser
247 // construction is cheap: it reads the header and validates
248 // ranges but nothing else. We do this only in debug-assertion
249 // builds because we otherwise require for safety that the
250 // compiled artifact is as-produced-by this version of
251 // Wasmtime, and we should always produce a correct exception
252 // table (i.e., we are not expecting untrusted data here).
253 if cfg!(debug_assertions) {
254 let _ = ExceptionTable::parse(&mmap[exception_data.clone()])?;
255 }
256
257 Ok(Self {
258 mmap,
259 #[cfg(has_host_compiler_backend)]
260 unwind_registration: None,
261 #[cfg(feature = "debug-builtins")]
262 debug_registration: None,
263 published: false,
264 registered: false,
265 enable_branch_protection: enable_branch_protection
266 .ok_or_else(|| format_err!("missing `{}` section", obj::ELF_WASM_BTI))?,
267 needs_executable,
268 #[cfg(feature = "debug-builtins")]
269 has_native_debug_info,
270 custom_code_memory: engine.custom_code_memory().cloned(),
271 text,
272 unwind,
273 trap_data,
274 address_map_data,
275 stack_map_data,
276 exception_data,
277 frame_tables_data,
278 func_name_data,
279 wasm_dwarf,
280 info_data,
281 wasm_data,
282 wasm_bytecode,
283 wasm_bytecode_ends,
284 })
285 }
286
287 /// Returns a reference to the underlying `MmapVec` this memory owns.
288 #[inline]
289 pub fn mmap(&self) -> &MmapVec {
290 &self.mmap
291 }
292
293 /// Returns the contents of the text section of the ELF executable this
294 /// represents.
295 #[inline]
296 pub fn text(&self) -> &[u8] {
297 &self.mmap[self.text.clone()]
298 }
299
300 /// Returns the contents of the `ELF_WASMTIME_DWARF` section.
301 #[inline]
302 pub fn wasm_dwarf(&self) -> &[u8] {
303 &self.mmap[self.wasm_dwarf.clone()]
304 }
305
306 /// Returns the data in the `ELF_NAME_DATA` section.
307 #[inline]
308 pub fn func_name_data(&self) -> &[u8] {
309 &self.mmap[self.func_name_data.clone()]
310 }
311
312 /// Returns the concatenated list of all data associated with this wasm
313 /// module.
314 ///
315 /// This is used for initialization of memories and all data ranges stored
316 /// in a `Module` are relative to the slice returned here.
317 #[inline]
318 pub fn wasm_data(&self) -> &[u8] {
319 &self.mmap[self.wasm_data.clone()]
320 }
321
322 /// Returns the encoded address map section used to pass to
323 /// `wasmtime_environ::lookup_file_pos`.
324 #[inline]
325 pub fn address_map_data(&self) -> &[u8] {
326 &self.mmap[self.address_map_data.clone()]
327 }
328
329 /// Returns the encoded stack map section used to pass to
330 /// `wasmtime_environ::StackMap::lookup`.
331 pub fn stack_map_data(&self) -> &[u8] {
332 &self.mmap[self.stack_map_data.clone()]
333 }
334
335 /// Returns the encoded exception-tables section to pass to
336 /// `wasmtime_unwinder::ExceptionTable::parse`.
337 pub fn exception_tables(&self) -> &[u8] {
338 &self.mmap[self.exception_data.clone()]
339 }
340
341 /// Returns the encoded frame-tables section to pass to
342 /// `wasmtime_environ::FrameTable::parse`.
343 pub fn frame_tables(&self) -> &[u8] {
344 &self.mmap[self.frame_tables_data.clone()]
345 }
346
347 /// Returns the concatenated Wasm bytecode section, or an empty slice if
348 /// the artifact was not compiled with `guest-debug` enabled.
349 pub fn wasm_bytecode(&self) -> &[u8] {
350 &self.mmap[self.wasm_bytecode.clone()]
351 }
352
353 /// Returns the Wasm bytecode section end-offset array.
354 pub fn wasm_bytecode_ends(&self) -> &[u8] {
355 &self.mmap[self.wasm_bytecode_ends.clone()]
356 }
357
358 /// Returns the contents of the `ELF_WASMTIME_INFO` section, or an empty
359 /// slice if it wasn't found.
360 #[inline]
361 pub fn wasmtime_info(&self) -> &[u8] {
362 &self.mmap[self.info_data.clone()]
363 }
364
365 /// Returns the contents of the `ELF_WASMTIME_TRAPS` section, or an empty
366 /// slice if it wasn't found.
367 #[inline]
368 pub fn trap_data(&self) -> &[u8] {
369 &self.mmap[self.trap_data.clone()]
370 }
371
372 /// Returns the Wasm bytecode section end-offset for a given core
373 /// module, or `None` if no bytecode is present.
374 ///
375 /// # Panics
376 ///
377 /// Panics if index is out-of-range.
378 fn wasm_bytecode_end_for_module(&self, index: StaticModuleIndex) -> Option<usize> {
379 if self.wasm_bytecode_ends().is_empty() {
380 return None;
381 }
382 let ends = self.wasm_bytecode_ends();
383 let count = ends.len() / core::mem::size_of::<u32>();
384 let (ends, _) = object::slice_from_bytes::<U32<LittleEndian>>(ends, count)
385 .expect("Invalid alignment of `ends` section");
386 let index = usize::try_from(index.as_u32()).unwrap();
387 Some(usize::try_from(ends[index].get(LittleEndian)).unwrap())
388 }
389
390 /// Returns the Wasm bytecode for the a core module in this
391 /// artifact, or `None` if bytecode was not preserved.
392 pub(crate) fn wasm_bytecode_for_module(&self, index: StaticModuleIndex) -> Option<&[u8]> {
393 let start = if index.as_u32() == 0 {
394 0
395 } else {
396 self.wasm_bytecode_end_for_module(StaticModuleIndex::from_u32(index.as_u32() - 1))?
397 };
398 let end = self.wasm_bytecode_end_for_module(index)?;
399 Some(&self.wasm_bytecode()[start..end])
400 }
401
402 /// Publishes the internal ELF image to be ready for execution.
403 ///
404 /// This method can only be when the image is not published (its
405 /// default state) and will panic if called when already
406 /// published. This will parse the ELF image from the original
407 /// `MmapVec` and do everything necessary to get it ready for
408 /// execution, including:
409 ///
410 /// * Change page protections from read/write to read/execute.
411 /// * Register unwinding information with the OS
412 /// * Register this image with the debugger if native DWARF is present
413 ///
414 /// After this function executes all JIT code should be ready to execute.
415 ///
416 /// The action may be reversed by calling [`Self::unpublish`], as long
417 /// as that method's safety requirements are upheld.
418 pub fn publish(&mut self) -> Result<()> {
419 assert!(!self.published);
420 self.published = true;
421
422 if self.text().is_empty() {
423 return Ok(());
424 }
425
426 // The unsafety here comes from a few things:
427 //
428 // * We're actually updating some page protections to executable memory.
429 //
430 // * We're registering unwinding information which relies on the
431 // correctness of the information in the first place. This applies to
432 // both the actual unwinding tables as well as the validity of the
433 // pointers we pass in itself.
434 unsafe {
435 // Next freeze the contents of this image by making all of the
436 // memory readonly. Nothing after this point should ever be modified
437 // so commit everything. For a compiled-in-memory image this will
438 // mean IPIs to evict writable mappings from other cores. For
439 // loaded-from-disk images this shouldn't result in IPIs so long as
440 // there weren't any relocations because nothing should have
441 // otherwise written to the image at any point either.
442 //
443 // Note that if virtual memory is disabled this is skipped because
444 // we aren't able to make it readonly, but this is just a
445 // defense-in-depth measure and isn't required for correctness.
446 #[cfg(has_virtual_memory)]
447 if self.mmap.supports_virtual_memory() {
448 self.mmap.make_readonly(0..self.mmap.len())?;
449 }
450
451 // Switch the executable portion from readonly to read/execute.
452 if self.needs_executable {
453 if !self.custom_publish()? {
454 if !self.mmap.supports_virtual_memory() {
455 bail!("this target requires virtual memory to be enabled");
456 }
457 #[cfg(has_virtual_memory)]
458 self.mmap
459 .make_executable(self.text.clone(), self.enable_branch_protection)
460 .context("unable to make memory executable")?;
461 }
462 }
463
464 if !self.registered {
465 // With all our memory set up use the platform-specific
466 // `UnwindRegistration` implementation to inform the general
467 // runtime that there's unwinding information available for all
468 // our just-published JIT functions.
469 self.register_unwind_info()?;
470
471 #[cfg(feature = "debug-builtins")]
472 self.register_debug_image()?;
473 self.registered = true;
474 }
475 }
476
477 Ok(())
478 }
479
480 fn custom_publish(&mut self) -> Result<bool> {
481 if let Some(mem) = self.custom_code_memory.as_ref() {
482 let text = self.text();
483 // The text section should be aligned to
484 // `custom_code_memory.required_alignment()` due to a
485 // combination of two invariants:
486 //
487 // - MmapVec aligns its start address, even in owned-Vec mode; and
488 // - The text segment inside the ELF image will be aligned according
489 // to the platform's requirements.
490 let text_addr = text.as_ptr() as usize;
491 assert_eq!(text_addr & (mem.required_alignment() - 1), 0);
492
493 // The custom code memory handler will ensure the
494 // memory is executable and also handle icache
495 // coherence.
496 mem.publish_executable(text.as_ptr(), text.len())?;
497 Ok(true)
498 } else {
499 Ok(false)
500 }
501 }
502
503 /// "Unpublish" code memory (transition it from executable to read/writable).
504 ///
505 /// This may be used to edit the code image, as long as the
506 /// overall size of the memory remains the same. Note the hazards
507 /// inherent in editing code that may have been executed: any
508 /// stack frames with PC still active in this code must be
509 /// suspended (e.g., called into a hostcall that is then invoking
510 /// this method, or async-yielded) and any active PC values must
511 /// point to valid instructions. Thus this is mostly useful for
512 /// patching in-place at particular sites, such as by the use of
513 /// Cranelift's `patchable_call` instruction.
514 ///
515 /// If this fails, then the memory remains executable.
516 pub fn unpublish(&mut self) -> Result<()> {
517 assert!(self.published);
518 self.published = false;
519
520 if self.text().is_empty() {
521 return Ok(());
522 }
523
524 if self.custom_unpublish()? {
525 return Ok(());
526 }
527
528 if !self.mmap.supports_virtual_memory() {
529 bail!("this target requires virtual memory to be enabled");
530 }
531
532 // SAFETY: we are guaranteed by our own safety conditions that
533 // we have exclusive access to this code and can change its
534 // permissions (removing the execute bit) without causing
535 // problems.
536 #[cfg(has_virtual_memory)]
537 unsafe {
538 self.mmap.make_readwrite(0..self.mmap.len())?;
539 }
540
541 // Note that we do *not* unregister: we expect unpublish
542 // to be used for temporary edits, so we want the
543 // registration to "stick" after the initial publish and
544 // not toggle in subsequent unpublish/publish cycles.
545
546 Ok(())
547 }
548
549 fn custom_unpublish(&mut self) -> Result<bool> {
550 if let Some(mem) = self.custom_code_memory.as_ref() {
551 let text = self.text();
552 mem.unpublish_executable(text.as_ptr(), text.len())?;
553 Ok(true)
554 } else {
555 Ok(false)
556 }
557 }
558
559 /// Return a mutable borrow to the code, suitable for editing.
560 ///
561 /// Must not be published.
562 ///
563 /// # Panics
564 ///
565 /// This method panics if the code has been published (and not
566 /// subsequently unpublished).
567 pub fn text_mut(&mut self) -> &mut [u8] {
568 assert!(!self.published);
569 // SAFETY: we assert !published, which means we either have
570 // not yet applied readonly + execute permissions, or we have
571 // undone that and flipped back to read-write via unpublish.
572 unsafe { &mut self.mmap.as_mut_slice()[self.text.clone()] }
573 }
574
575 unsafe fn register_unwind_info(&mut self) -> Result<()> {
576 if self.unwind.len() == 0 {
577 return Ok(());
578 }
579 #[cfg(has_host_compiler_backend)]
580 {
581 let text = self.text();
582 let unwind_info = &self.mmap[self.unwind.clone()];
583 let registration = unsafe {
584 crate::runtime::vm::UnwindRegistration::new(
585 text.as_ptr(),
586 unwind_info.as_ptr(),
587 unwind_info.len(),
588 )
589 .context("failed to create unwind info registration")?
590 };
591 self.unwind_registration = Some(registration);
592 return Ok(());
593 }
594 #[cfg(not(has_host_compiler_backend))]
595 {
596 bail!("should not have unwind info for non-native backend")
597 }
598 }
599
600 #[cfg(feature = "debug-builtins")]
601 fn register_debug_image(&mut self) -> Result<()> {
602 if !self.has_native_debug_info {
603 return Ok(());
604 }
605
606 // TODO-DebugInfo: we're copying the whole image here, which is pretty wasteful.
607 // Use the existing memory by teaching code here about relocations in DWARF sections
608 // and anything else necessary that is done in "create_gdbjit_image" right now.
609 let image = self.mmap().to_vec();
610 let text: &[u8] = self.text();
611 let bytes = crate::native_debug::create_gdbjit_image(image, (text.as_ptr(), text.len()))?;
612 let reg = crate::runtime::vm::GdbJitImageRegistration::register(bytes);
613 self.debug_registration = Some(reg);
614 Ok(())
615 }
616
617 /// Looks up the given offset within this module's text section and returns
618 /// the trap code associated with that instruction, if there is one.
619 pub fn lookup_trap_code(&self, text_offset: usize) -> Option<CompiledTrap> {
620 lookup_trap_code(self.trap_data(), text_offset)
621 }
622
623 /// Get the raw address range of this CodeMemory.
624 pub(crate) fn raw_addr_range(&self) -> Range<usize> {
625 let start = self.text().as_ptr().addr();
626 let end = start + self.text().len();
627 start..end
628 }
629
630 /// Create a "deep clone": a separate CodeMemory for the same code
631 /// that can be patched or mutated independently. Also returns a
632 /// "metadata and location" handle that can be registered with the
633 /// global module registry and used for trap metadata lookups.
634 #[cfg(feature = "debug")]
635 pub(crate) fn deep_clone(self: &Arc<Self>, engine: &Engine) -> Result<CodeMemory> {
636 let mmap = self.mmap.deep_clone()?;
637 Self::new(engine, mmap)
638 }
639
640 /// Obtain a frame-table parser on this module's frame state slot
641 /// (debug instrumentation) metadata.
642 #[cfg(feature = "debug")]
643 pub(crate) fn frame_table(&self) -> Option<wasmtime_environ::FrameTable<'_>> {
644 let data = self.frame_tables();
645 if data.is_empty() {
646 None
647 } else {
648 let orig_text = self.text();
649 Some(
650 wasmtime_environ::FrameTable::parse(data, orig_text)
651 .expect("Frame tables were validated on module load"),
652 )
653 }
654 }
655}
656
657fn section_name<'a>(
658 endian: Endianness,
659 strings: object::StringTable<'a>,
660 section_header: &SectionHeader64<Endianness>,
661) -> Result<&'a str> {
662 let name = section_header
663 .name(endian, strings)
664 .map_err(obj::ObjectCrateErrorWrapper)?;
665 Ok(str::from_utf8(name).context("invalid section name in Wasm compilation artifact")?)
666}
667
668fn is_reloc_section(section_header: &SectionHeader64<Endianness>, endian: Endianness) -> bool {
669 let sh_type = section_header.sh_type(endian);
670 matches!(
671 sh_type,
672 object::elf::SHT_REL | object::elf::SHT_RELA | object::elf::SHT_CREL
673 )
674}
675
676fn reloc_section_target<'a>(
677 sections: &'a SectionTable<'a, FileHeader64<Endianness>, &'a [u8]>,
678 section: &'a SectionHeader64<Endianness>,
679 endian: Endianness,
680) -> Result<Option<&'a SectionHeader64<Endianness>>> {
681 if !is_reloc_section(§ion, endian) {
682 return Ok(None);
683 }
684
685 let sh_info = section.info_link(endian);
686
687 // Dynamic relocation.
688 if sh_info == SectionIndex(0) {
689 return Ok(None);
690 }
691
692 ensure!(
693 sh_info.0 < sections.len(),
694 "invalid ELF `sh_info` for relocation section",
695 );
696
697 Ok(Some(sections.section(sh_info)?))
698}
699
700/// Returns the range of `inner` within `outer`, such that `outer[range]` is the
701/// same as `inner`.
702///
703/// This method requires that `inner` is a sub-slice of `outer`, and if that
704/// isn't true then this method will panic.
705fn subslice_range(inner: &[u8], outer: &[u8]) -> Range<usize> {
706 if inner.len() == 0 {
707 return 0..0;
708 }
709
710 assert!(outer.as_ptr() <= inner.as_ptr());
711 assert!((&inner[inner.len() - 1] as *const _) <= (&outer[outer.len() - 1] as *const _));
712
713 let start = inner.as_ptr() as usize - outer.as_ptr() as usize;
714 start..start + inner.len()
715}