@@ -44,10 +44,17 @@ impl Drop for CodeMemory {
44
44
// If there is a custom code memory handler, restore the
45
45
// original (non-executable) state of the memory.
46
46
if let Some ( mem) = self . custom_code_memory . as_ref ( ) {
47
- if self . published && self . needs_executable {
48
- let text = self . text ( ) ;
49
- mem. unpublish_executable ( text. as_ptr ( ) , text. len ( ) )
50
- . expect ( "Executable memory unpublish failed" ) ;
47
+ if self . published {
48
+ mem. unpublish_image (
49
+ self . mmap . as_ptr ( ) ,
50
+ self . mmap . len ( ) ,
51
+ if self . needs_executable {
52
+ Some ( self . text . clone ( ) )
53
+ } else {
54
+ None
55
+ } ,
56
+ )
57
+ . expect ( "Executable memory unpublish failed" ) ;
51
58
}
52
59
}
53
60
@@ -64,6 +71,23 @@ fn _assert() {
64
71
_assert_send_sync :: < CodeMemory > ( ) ;
65
72
}
66
73
74
+ #[ derive( PartialEq , PartialOrd ) ]
75
+ /// Actions that were performed by `CustomCodeMemory::publish_image` hook.
76
+ pub enum HandledByCustomCodeMemory {
77
+ /// Nothing was performed, publishing will be done by the
78
+ /// default Wasmtime implementation.
79
+ Nothing ,
80
+
81
+ /// Only memory mapping was performed by the hook,
82
+ /// registering unwind and debug information will
83
+ /// be done by default Wasmtime implementation.
84
+ Mapping ,
85
+
86
+ /// Everything was performed by the hook and no further
87
+ /// publishing actions are required.
88
+ MappingAndRegistration ,
89
+ }
90
+
67
91
/// Interface implemented by an embedder to provide custom
68
92
/// implementations of code-memory protection and execute permissions.
69
93
pub trait CustomCodeMemory : Send + Sync {
@@ -75,30 +99,50 @@ pub trait CustomCodeMemory: Send + Sync {
75
99
/// of virtual memory are disabled.
76
100
fn required_alignment ( & self ) -> usize ;
77
101
78
- /// Publish a region of memory as executable .
102
+ /// Publish an image .
79
103
///
80
- /// This should update permissions from the default RW
81
- /// (readable/writable but not executable) to RX
82
- /// (readable/executable but not writable), enforcing W^X
83
- /// discipline.
104
+ /// This should ensure that the provided image region is mapped
105
+ /// read-only, with the exception of `code` area that (if provided)
106
+ /// should be mapped as readable and executable.
107
+ ///
108
+ /// Return value describes which actions were completed, while other
109
+ /// steps will be handled by Wasmtime default implementation.
84
110
///
85
111
/// If the platform requires any data/instruction coherence
86
112
/// action, that should be performed as part of this hook as well.
87
113
///
88
- /// `ptr` and `ptr.offset(len)` are guaranteed to be aligned as
89
- /// per `required_alignment()`.
90
- fn publish_executable ( & self , ptr : * const u8 , len : usize ) -> anyhow:: Result < ( ) > ;
91
-
92
- /// Unpublish a region of memory.
114
+ /// If `image` is the image provided to `Module::deserialize_raw`
115
+ /// which was already prepared for execution, this hook might
116
+ /// be a no-op reporting all actions as completed.
93
117
///
94
- /// This should perform the opposite effect of `make_executable`,
95
- /// switching a range of memory back from RX (readable/executable)
96
- /// to RW (readable/writable). It is guaranteed that no code is
118
+ /// Values in `code` range are relative to `image` pointer.
119
+ /// `code` is guaranteed to be aligned as per `required_alignment()`.
120
+ fn publish_image (
121
+ & self ,
122
+ image : * const u8 ,
123
+ len : usize ,
124
+ code : Option < Range < usize > > ,
125
+ ) -> anyhow:: Result < HandledByCustomCodeMemory > ;
126
+
127
+ /// Unpublish an image.
128
+ ///
129
+ /// This should perform the opposite effect of `publish_image`.
130
+ ///
131
+ /// If `image` is the image provided to `Module::deserialize_raw`,
132
+ /// this hook might be used for unloading that image from address space.
133
+ ///
134
+ /// Otherwise it should switch a range of memory back to
135
+ /// readable and writable. It is guaranteed that no code is
97
136
/// running anymore from this region.
98
137
///
99
- /// `ptr` and `ptr.offset(len)` are guaranteed to be aligned as
100
- /// per `required_alignment()`.
101
- fn unpublish_executable ( & self , ptr : * const u8 , len : usize ) -> anyhow:: Result < ( ) > ;
138
+ /// Values in `code` range are relative to `image` pointer.
139
+ /// `code` is guaranteed to be aligned as per `required_alignment()`.
140
+ fn unpublish_image (
141
+ & self ,
142
+ image : * const u8 ,
143
+ len : usize ,
144
+ code : Option < Range < usize > > ,
145
+ ) -> anyhow:: Result < ( ) > ;
102
146
}
103
147
104
148
impl CodeMemory {
@@ -297,25 +341,29 @@ impl CodeMemory {
297
341
// both the actual unwinding tables as well as the validity of the
298
342
// pointers we pass in itself.
299
343
unsafe {
300
- // Next freeze the contents of this image by making all of the
301
- // memory readonly. Nothing after this point should ever be modified
302
- // so commit everything. For a compiled-in-memory image this will
303
- // mean IPIs to evict writable mappings from other cores. For
304
- // loaded-from-disk images this shouldn't result in IPIs so long as
305
- // there weren't any relocations because nothing should have
306
- // otherwise written to the image at any point either.
307
- //
308
- // Note that if virtual memory is disabled this is skipped because
309
- // we aren't able to make it readonly, but this is just a
310
- // defense-in-depth measure and isn't required for correctness.
311
- #[ cfg( has_virtual_memory) ]
312
- if self . mmap . supports_virtual_memory ( ) {
313
- self . mmap . make_readonly ( 0 ..self . mmap . len ( ) ) ?;
314
- }
344
+ // Potentially call embedder-provided custom publishing implementation,
345
+ // and only perform publishing steps that weren't done by it.
346
+ let handled = self . custom_publish ( ) ?;
347
+
348
+ if handled < HandledByCustomCodeMemory :: Mapping {
349
+ // Next freeze the contents of this image by making all of the
350
+ // memory readonly. Nothing after this point should ever be modified
351
+ // so commit everything. For a compiled-in-memory image this will
352
+ // mean IPIs to evict writable mappings from other cores. For
353
+ // loaded-from-disk images this shouldn't result in IPIs so long as
354
+ // there weren't any relocations because nothing should have
355
+ // otherwise written to the image at any point either.
356
+ //
357
+ // Note that if virtual memory is disabled this is skipped because
358
+ // we aren't able to make it readonly, but this is just a
359
+ // defense-in-depth measure and isn't required for correctness.
360
+ #[ cfg( has_virtual_memory) ]
361
+ if self . mmap . supports_virtual_memory ( ) {
362
+ self . mmap . make_readonly ( 0 ..self . mmap . len ( ) ) ?;
363
+ }
315
364
316
- // Switch the executable portion from readonly to read/execute.
317
- if self . needs_executable {
318
- if !self . custom_publish ( ) ? {
365
+ // Switch the executable portion from readonly to read/execute.
366
+ if self . needs_executable {
319
367
if !self . mmap . supports_virtual_memory ( ) {
320
368
bail ! ( "this target requires virtual memory to be enabled" ) ;
321
369
}
@@ -343,39 +391,45 @@ impl CodeMemory {
343
391
}
344
392
}
345
393
346
- // With all our memory set up use the platform-specific
347
- // `UnwindRegistration` implementation to inform the general
348
- // runtime that there's unwinding information available for all
349
- // our just-published JIT functions.
350
- self . register_unwind_info ( ) ?;
394
+ if handled < HandledByCustomCodeMemory :: MappingAndRegistration {
395
+ // With all our memory set up use the platform-specific
396
+ // `UnwindRegistration` implementation to inform the general
397
+ // runtime that there's unwinding information available for all
398
+ // our just-published JIT functions.
399
+ self . register_unwind_info ( ) ?;
351
400
352
- #[ cfg( feature = "debug-builtins" ) ]
353
- self . register_debug_image ( ) ?;
401
+ #[ cfg( feature = "debug-builtins" ) ]
402
+ self . register_debug_image ( ) ?;
403
+ }
354
404
}
355
405
356
406
Ok ( ( ) )
357
407
}
358
408
359
- fn custom_publish ( & mut self ) -> Result < bool > {
409
+ fn custom_publish ( & mut self ) -> Result < HandledByCustomCodeMemory > {
360
410
if let Some ( mem) = self . custom_code_memory . as_ref ( ) {
361
- let text = self . text ( ) ;
362
- // The text section should be aligned to
363
- // `custom_code_memory.required_alignment()` due to a
364
- // combination of two invariants:
365
- //
366
- // - MmapVec aligns its start address, even in owned-Vec mode; and
367
- // - The text segment inside the ELF image will be aligned according
368
- // to the platform's requirements.
369
- let text_addr = text. as_ptr ( ) as usize ;
370
- assert_eq ! ( text_addr & ( mem. required_alignment( ) - 1 ) , 0 ) ;
371
-
372
- // The custom code memory handler will ensure the
373
- // memory is executable and also handle icache
374
- // coherence.
375
- mem. publish_executable ( text. as_ptr ( ) , text. len ( ) ) ?;
376
- Ok ( true )
411
+ if self . needs_executable {
412
+ // The text section should be aligned to
413
+ // `custom_code_memory.required_alignment()` due to a
414
+ // combination of two invariants:
415
+ //
416
+ // - MmapVec aligns its start address, even in owned-Vec mode; and
417
+ // - The text segment inside the ELF image will be aligned according
418
+ // to the platform's requirements.
419
+ assert_eq ! (
420
+ ( self . text( ) . as_ptr( ) as usize ) & ( mem. required_alignment( ) - 1 ) ,
421
+ 0
422
+ ) ;
423
+
424
+ // The custom code memory handler will ensure the
425
+ // memory is executable and also handle icache
426
+ // coherence.
427
+ mem. publish_image ( self . mmap . as_ptr ( ) , self . mmap . len ( ) , Some ( self . text . clone ( ) ) )
428
+ } else {
429
+ mem. publish_image ( self . mmap . as_ptr ( ) , self . mmap . len ( ) , None )
430
+ }
377
431
} else {
378
- Ok ( false )
432
+ Ok ( HandledByCustomCodeMemory :: Nothing )
379
433
}
380
434
}
381
435
0 commit comments