Skip to main content

Module cache

Module cache 

Source
Expand description

Cached snapshot of the original binary’s symbol table.

Why a cache: the binary an app boots with is “hundreds of MB” on a real-world build (subsecond’s own comment). We pay that I/O + parse cost once at the start of a whisker run session and re-use it for every subsequent hot patch. Each save then only has to parse the small patch dylib and diff it against the cached original — closer to the sub-second budget.

Apart from the symbols, we also capture the static virtual address of whisker_aslr_anchor (emitted by #[whisker::main]) in the host binary. That becomes JumpTable::aslr_reference, and our vendored subsecond’s apply_patch uses it as

old_offset = aslr_reference()       // dlsym(RTLD_DEFAULT,
                                    //   "whisker_aslr_anchor")
           - table.aslr_reference    // static anchor addr (us)
           = runtime image base.

so the JumpTable’s static keys can be adjusted to live runtime addresses. Two earlier bugs led us here:

  1. Setting this to file.relative_address_base() (always 0 for ELF PIE) shifted the keys by runtime_main_addr rather than the image base; call_as_ptr’s map lookup always missed.
  2. Anchoring on main instead of whisker_aslr_anchor — on Android, dlsym(RTLD_DEFAULT, "main") resolves to app_process64’s main, not the user’s .so, so the slide math computed garbage. The unique anchor name fixes that.

Structs§

HotpatchModuleCache
Pre-parsed snapshot of the original (== “fat”) binary. Built once per dev-server run; subsequent JumpTable construction reads from here without touching the disk again.