+void *ndrc_try_restore_block(u_int vaddr)
+{
+ u_char *source_start = NULL, *source_end = NULL;
+ void *found_stub = NULL, *found_clean = NULL;
+ u_int len, page = get_page(vaddr);
+ const struct ll_entry *head;
+ int ep_count = 0;
+
+ stat_inc(stat_restore_tries);
+ for (head = jump_dirty[page]; head != NULL; head = head->next)
+ {
+ if (head->vaddr != vaddr)
+ continue;
+ // don't restore blocks which are about to expire from the cache
+ if (!doesnt_expire_soon(head->addr))
+ continue;
+ stat_inc(stat_restore_compares);
+ if (!verify_dirty(head->addr))
+ continue;
+
+ found_stub = head->addr;
+ break;
+ }
+ if (!found_stub)
+ return NULL;
+
+ found_clean = get_clean_addr(found_stub);
+ get_bounds(found_stub, &source_start, &source_end);
+ assert(source_start < source_end);
+ len = source_end - source_start;
+ mark_valid_code(vaddr, len);
+
+ // restore all entry points
+ for (head = jump_dirty[page]; head != NULL; head = head->next)
+ {
+ if (head->vaddr < vaddr || head->vaddr >= vaddr + len)
+ continue;
+
+ u_char *start = NULL, *end = NULL;
+ get_bounds(head->addr, &start, &end);
+ if (start != source_start || end != source_end)
+ continue;
+
+ void *clean_addr = get_clean_addr(head->addr);
+ ll_add_flags(jump_in + page, head->vaddr, head->reg_sv_flags, clean_addr);
+
+ int in_ht = 0;
+ struct ht_entry *ht_bin = hash_table_get(head->vaddr);
+ if (ht_bin->vaddr[0] == head->vaddr) {
+ ht_bin->tcaddr[0] = clean_addr; // Replace existing entry
+ in_ht = 1;
+ }
+ if (ht_bin->vaddr[1] == head->vaddr) {
+ ht_bin->tcaddr[1] = clean_addr; // Replace existing entry
+ in_ht = 1;
+ }
+ if (!in_ht)
+ hash_table_add(ht_bin, head->vaddr, clean_addr);
+ ep_count++;
+ }
+ inv_debug("INV: Restored %08x %p (%d)\n", vaddr, found_stub, ep_count);
+ stat_inc(stat_bc_restore);
+ return found_clean;
+}
+