@@ -468,7 +468,7 @@ pub fn create_wasm_jump_table(patch: &Path, cache: &HotpatchModuleCache) -> Resu
468468 // `relocation-model=pic` synthesizes can reference the functions via the indirect function table
469469 // even if they are not normally synthesized in regular wasm code generation.
470470 //
471- // Normally, the dynaic linker setup would resolve GOT.func against the same GOT.func export in
471+ // Normally, the dynamic linker setup would resolve GOT.func against the same GOT.func export in
472472 // the main module, but we don't have that. Instead, we simply re-parse the main module, aggregate
473473 // its ifunc table, and then resolve directly to the index in that table.
474474 for ( import_id, ifunc_index) in got_funcs {
@@ -584,19 +584,23 @@ pub fn create_wasm_jump_table(patch: &Path, cache: &HotpatchModuleCache) -> Resu
584584
585585 if let Some ( table_idx) = name_to_ifunc_old. get ( import. name . as_str ( ) ) {
586586 new. imports . delete ( env_func_import) ;
587- convert_import_to_ifunc_call (
587+ convert_func_to_ifunc_call (
588588 & mut new,
589589 ifunc_table_initializer,
590590 func_id,
591591 * table_idx,
592592 name. clone ( ) ,
593593 ) ;
594+ continue ;
594595 }
595596
596597 if name_is_bindgen_symbol ( & name) {
597598 new. imports . delete ( env_func_import) ;
598- convert_import_to_ifunc_call ( & mut new, ifunc_table_initializer, func_id, 0 , name) ;
599+ convert_func_to_ifunc_call ( & mut new, ifunc_table_initializer, func_id, 0 , name) ;
600+ continue ;
599601 }
602+
603+ tracing:: warn!( "[hotpatching]: Symbol slipped through the cracks: {}" , name) ;
600604 }
601605
602606 // Wire up the preserved intrinsic functions that we saved before running wasm-bindgen to the expected
@@ -613,7 +617,34 @@ pub fn create_wasm_jump_table(patch: &Path, cache: &HotpatchModuleCache) -> Resu
613617 if name_is_bindgen_symbol ( & import. name ) {
614618 let name = import. name . as_str ( ) . to_string ( ) ;
615619 new. imports . delete ( import_id) ;
616- convert_import_to_ifunc_call ( & mut new, ifunc_table_initializer, func_id, 0 , name) ;
620+ convert_func_to_ifunc_call ( & mut new, ifunc_table_initializer, func_id, 0 , name) ;
621+ }
622+ }
623+
624+ // Rewrite the wbg_cast functions to call the indirect functions from the original module.
625+ // This is necessary because wasm-bindgen uses these calls to perform dynamic type casting through
626+ // the JS layer. If we don't rewrite these, they end up as calls to `breaks_if_inlined` functions
627+ // which are no-ops and get rewritten by the wbindgen post-processing step.
628+ //
629+ // Here, we find the corresponding wbg_cast function in the old module by name and then rewrite
630+ // the patch module's cast function to call the indirect function from the original module.
631+ //
632+ // See the wbg_cast implementation in wasm-bindgen for more details:
633+ // <https://github.com/wasm-bindgen/wasm-bindgen/blob/f61a588f674304964a2062b2307edb304aed4d16/src/rt/mod.rs#L30>
634+ let new_func_ids = new. funcs . iter ( ) . map ( |f| f. id ( ) ) . collect :: < Vec < _ > > ( ) ;
635+ for func_id in new_func_ids {
636+ let Some ( name) = new. funcs . get ( func_id) . name . as_deref ( ) else {
637+ continue ;
638+ } ;
639+
640+ if name. contains ( "wasm_bindgen4__rt8wbg_cast" ) && !name. contains ( "breaks_if_inline" ) {
641+ let name = name. to_string ( ) ;
642+ let old_idx = name_to_ifunc_old
643+ . get ( & name)
644+ . copied ( )
645+ . ok_or_else ( || anyhow:: anyhow!( "Could not find matching wbg_cast function for [{name}] - must generate new JS bindings." ) ) ?;
646+
647+ convert_func_to_ifunc_call ( & mut new, ifunc_table_initializer, func_id, old_idx, name) ;
617648 }
618649 }
619650
@@ -642,8 +673,18 @@ pub fn create_wasm_jump_table(patch: &Path, cache: &HotpatchModuleCache) -> Resu
642673 let ifunc_count = name_to_ifunc_new. len ( ) as u64 ;
643674 let mut map = AddressMap :: default ( ) ;
644675 for ( name, idx) in name_to_ifunc_new. iter ( ) {
676+ // Find the corresponding ifunc in the old module by name
645677 if let Some ( old_idx) = name_to_ifunc_old. get ( * name) {
646678 map. insert ( * old_idx as u64 , * idx as u64 ) ;
679+ continue ;
680+ }
681+
682+ // Warn if there might be a null entry
683+ if !name. contains ( "breaks_if_inlined" ) {
684+ tracing:: warn!(
685+ "[hotpatching]: Failed to find ifunc entry in old module for function: {}" ,
686+ name
687+ ) ;
647688 }
648689 }
649690
@@ -656,7 +697,7 @@ pub fn create_wasm_jump_table(patch: &Path, cache: &HotpatchModuleCache) -> Resu
656697 } )
657698}
658699
659- fn convert_import_to_ifunc_call (
700+ fn convert_func_to_ifunc_call (
660701 new : & mut Module ,
661702 ifunc_table_initializer : TableId ,
662703 func_id : FunctionId ,
@@ -1309,7 +1350,6 @@ fn name_is_bindgen_symbol(name: &str) -> bool {
13091350 || name. contains ( "wasm_bindgen..describe..WasmDescribe" )
13101351 || name. contains ( "wasm_bindgen..closure..WasmClosure$GT$8describe" )
13111352 || name. contains ( "wasm_bindgen7closure16Closure$LT$T$GT$4wrap8describe" )
1312- || name. contains ( "wasm_bindgen4__rt8wbg_" )
13131353}
13141354
13151355/// Manually parse the data section from a wasm module
0 commit comments