mirror of
https://github.com/kanjitalk755/macemu.git
synced 2024-10-16 17:24:14 +00:00
Wrap the Windows write-watch API.
This commit is contained in:
parent
ef39e46cb2
commit
69376f61ca
@ -188,6 +188,11 @@ void * vm_acquire(size_t size, int options)
|
|||||||
if (options & VM_MAP_FIXED)
|
if (options & VM_MAP_FIXED)
|
||||||
return VM_MAP_FAILED;
|
return VM_MAP_FAILED;
|
||||||
|
|
||||||
|
#ifndef HAVE_VM_WRITE_WATCH
|
||||||
|
if (options & VM_MAP_WRITE_WATCH)
|
||||||
|
return VM_MAP_FAILED;
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_MACH_VM
|
#ifdef HAVE_MACH_VM
|
||||||
// vm_allocate() returns a zero-filled memory region
|
// vm_allocate() returns a zero-filled memory region
|
||||||
if (vm_allocate(mach_task_self(), (vm_address_t *)&addr, size, TRUE) != KERN_SUCCESS)
|
if (vm_allocate(mach_task_self(), (vm_address_t *)&addr, size, TRUE) != KERN_SUCCESS)
|
||||||
@ -207,7 +212,11 @@ void * vm_acquire(size_t size, int options)
|
|||||||
next_address = (char *)addr + size;
|
next_address = (char *)addr + size;
|
||||||
#else
|
#else
|
||||||
#ifdef HAVE_WIN32_VM
|
#ifdef HAVE_WIN32_VM
|
||||||
if ((addr = VirtualAlloc(NULL, size, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE)) == NULL)
|
int alloc_type = MEM_RESERVE | MEM_COMMIT;
|
||||||
|
if (options & VM_MAP_WRITE_WATCH)
|
||||||
|
alloc_type |= MEM_WRITE_WATCH;
|
||||||
|
|
||||||
|
if ((addr = VirtualAlloc(NULL, size, alloc_type, PAGE_EXECUTE_READWRITE)) == NULL)
|
||||||
return VM_MAP_FAILED;
|
return VM_MAP_FAILED;
|
||||||
#else
|
#else
|
||||||
if ((addr = calloc(size, 1)) == 0)
|
if ((addr = calloc(size, 1)) == 0)
|
||||||
@ -236,6 +245,11 @@ int vm_acquire_fixed(void * addr, size_t size, int options)
|
|||||||
if (options & VM_MAP_SHARED)
|
if (options & VM_MAP_SHARED)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
#ifndef HAVE_VM_WRITE_WATCH
|
||||||
|
if (options & VM_MAP_WRITE_WATCH)
|
||||||
|
return -1;
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_MACH_VM
|
#ifdef HAVE_MACH_VM
|
||||||
// vm_allocate() returns a zero-filled memory region
|
// vm_allocate() returns a zero-filled memory region
|
||||||
if (vm_allocate(mach_task_self(), (vm_address_t *)&addr, size, 0) != KERN_SUCCESS)
|
if (vm_allocate(mach_task_self(), (vm_address_t *)&addr, size, 0) != KERN_SUCCESS)
|
||||||
@ -253,10 +267,14 @@ int vm_acquire_fixed(void * addr, size_t size, int options)
|
|||||||
if (addr == NULL)
|
if (addr == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
int alloc_type = MEM_RESERVE | MEM_COMMIT;
|
||||||
|
if (options & VM_MAP_WRITE_WATCH)
|
||||||
|
alloc_type |= MEM_WRITE_WATCH;
|
||||||
|
|
||||||
// Allocate a possibly offset region to align on 64K boundaries
|
// Allocate a possibly offset region to align on 64K boundaries
|
||||||
LPVOID req_addr = align_addr_segment(addr);
|
LPVOID req_addr = align_addr_segment(addr);
|
||||||
DWORD req_size = align_size_segment(addr, size);
|
DWORD req_size = align_size_segment(addr, size);
|
||||||
LPVOID ret_addr = VirtualAlloc(req_addr, req_size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
|
LPVOID ret_addr = VirtualAlloc(req_addr, req_size, alloc_type, PAGE_EXECUTE_READWRITE);
|
||||||
if (ret_addr != req_addr)
|
if (ret_addr != req_addr)
|
||||||
return -1;
|
return -1;
|
||||||
#else
|
#else
|
||||||
@ -328,6 +346,49 @@ int vm_protect(void * addr, size_t size, int prot)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Return the addresses of the pages that got modified in the
|
||||||
|
specified range [ ADDR, ADDR + SIZE [ since the last reset of the watch
|
||||||
|
bits. Returns 0 if successful, -1 for errors. */
|
||||||
|
|
||||||
|
int vm_get_write_watch(void * addr, size_t size,
|
||||||
|
void ** pages, unsigned int * n_pages,
|
||||||
|
int options)
|
||||||
|
{
|
||||||
|
#ifdef HAVE_VM_WRITE_WATCH
|
||||||
|
#ifdef HAVE_WIN32_VM
|
||||||
|
DWORD flags = 0;
|
||||||
|
if (options & VM_WRITE_WATCH_RESET)
|
||||||
|
flags |= WRITE_WATCH_FLAG_RESET;
|
||||||
|
|
||||||
|
ULONG page_size;
|
||||||
|
ULONG count = *n_pages;
|
||||||
|
int ret_code = GetWriteWatch(flags, addr, size, pages, &count, &page_size);
|
||||||
|
if (ret_code != 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
*n_pages = count;
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
// Unsupported
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reset the write-tracking state for the specified range [ ADDR, ADDR
|
||||||
|
+ SIZE [. Returns 0 if successful, -1 for errors. */
|
||||||
|
|
||||||
|
int vm_reset_write_watch(void * addr, size_t size)
|
||||||
|
{
|
||||||
|
#ifdef HAVE_VM_WRITE_WATCH
|
||||||
|
#ifdef HAVE_WIN32_VM
|
||||||
|
int ret_code = ResetWriteWatch(addr, size);
|
||||||
|
return ret_code == 0 ? 0 : -1;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
// Unsupported
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
/* Returns the size of a page. */
|
/* Returns the size of a page. */
|
||||||
|
|
||||||
int vm_get_page_size(void)
|
int vm_get_page_size(void)
|
||||||
@ -345,6 +406,53 @@ int vm_get_page_size(void)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIGURE_TEST_VM_WRITE_WATCH
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
vm_init();
|
||||||
|
|
||||||
|
vm_uintptr_t page_size = vm_get_page_size();
|
||||||
|
|
||||||
|
char *area;
|
||||||
|
const int n_pages = 7;
|
||||||
|
const int area_size = n_pages * page_size;
|
||||||
|
const int map_options = VM_MAP_DEFAULT | VM_MAP_WRITE_WATCH;
|
||||||
|
if ((area = (char *)vm_acquire(area_size, map_options)) == VM_MAP_FAILED)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
unsigned int n_modified_pages_expected = 0;
|
||||||
|
static const int touch_page[n_pages] = { 0, 1, 1, 0, 1, 0, 1 };
|
||||||
|
for (i = 0; i < n_pages; i++) {
|
||||||
|
if (touch_page[i]) {
|
||||||
|
area[i * page_size] = 1;
|
||||||
|
++n_modified_pages_expected;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char *modified_pages[n_pages];
|
||||||
|
unsigned int n_modified_pages = n_pages;
|
||||||
|
if (vm_get_write_watch(area, area_size, (void **)modified_pages, &n_modified_pages) < 0)
|
||||||
|
return 2;
|
||||||
|
if (n_modified_pages != n_modified_pages_expected)
|
||||||
|
return 3;
|
||||||
|
for (i = 0, j = 0; i < n_pages; i++) {
|
||||||
|
char v = area[i * page_size];
|
||||||
|
if ((touch_page[i] && !v) || (!touch_page[i] && v))
|
||||||
|
return 4;
|
||||||
|
if (!touch_page[i])
|
||||||
|
continue;
|
||||||
|
if (modified_pages[j] != (area + i * page_size))
|
||||||
|
return 5;
|
||||||
|
++j;
|
||||||
|
}
|
||||||
|
|
||||||
|
vm_release(area, area_size);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIGURE_TEST_VM_MAP
|
#ifdef CONFIGURE_TEST_VM_MAP
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
@ -46,11 +46,17 @@ extern "C" {
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Option to vm_get_write_watch() to reset the write-tracking state
|
||||||
|
once it was retrieved. Otherwise, you have to manually call
|
||||||
|
vm_reset_write_watch() and potentially lose some info. */
|
||||||
|
#define VM_WRITE_WATCH_RESET 0x01
|
||||||
|
|
||||||
/* Mapping options. */
|
/* Mapping options. */
|
||||||
#define VM_MAP_SHARED 0x01
|
#define VM_MAP_SHARED 0x01
|
||||||
#define VM_MAP_PRIVATE 0x02
|
#define VM_MAP_PRIVATE 0x02
|
||||||
#define VM_MAP_FIXED 0x04
|
#define VM_MAP_FIXED 0x04
|
||||||
#define VM_MAP_32BIT 0x08
|
#define VM_MAP_32BIT 0x08
|
||||||
|
#define VM_MAP_WRITE_WATCH 0x10
|
||||||
|
|
||||||
/* Default mapping options. */
|
/* Default mapping options. */
|
||||||
#define VM_MAP_DEFAULT (VM_MAP_PRIVATE)
|
#define VM_MAP_DEFAULT (VM_MAP_PRIVATE)
|
||||||
@ -103,10 +109,23 @@ extern int vm_acquire_fixed(void * addr, size_t size, int options = VM_MAP_DEFAU
|
|||||||
extern int vm_release(void * addr, size_t size);
|
extern int vm_release(void * addr, size_t size);
|
||||||
|
|
||||||
/* Change the memory protection of the region starting at ADDR and
|
/* Change the memory protection of the region starting at ADDR and
|
||||||
extending LEN bytes to PROT. Returns 0 if successful, -1 for errors. */
|
extending SIZE bytes to PROT. Returns 0 if successful, -1 for errors. */
|
||||||
|
|
||||||
extern int vm_protect(void * addr, size_t size, int prot);
|
extern int vm_protect(void * addr, size_t size, int prot);
|
||||||
|
|
||||||
|
/* Return the addresses of the pages that got modified since the last
|
||||||
|
reset of the write-tracking state for the specified range [ ADDR,
|
||||||
|
ADDR + SIZE [. Returns 0 if successful, -1 for errors. */
|
||||||
|
|
||||||
|
extern int vm_get_write_watch(void * addr, size_t size,
|
||||||
|
void ** pages, unsigned int * n_pages,
|
||||||
|
int options = 0);
|
||||||
|
|
||||||
|
/* Reset the write-tracking state for the specified range [ ADDR, ADDR
|
||||||
|
+ SIZE [. Returns 0 if successful, -1 for errors. */
|
||||||
|
|
||||||
|
extern int vm_reset_write_watch(void * addr, size_t size);
|
||||||
|
|
||||||
/* Returns the size of a page. */
|
/* Returns the size of a page. */
|
||||||
|
|
||||||
extern int vm_get_page_size(void);
|
extern int vm_get_page_size(void);
|
||||||
|
Loading…
Reference in New Issue
Block a user