waterbox: add guard pages to libco stacks

This commit is contained in:
nattthebear 2017-06-11 20:05:35 -04:00
parent 8a24960cce
commit 626de2d7c8
8 changed files with 138 additions and 65 deletions

View File

@ -42,6 +42,8 @@ namespace BizHawk.Emulation.Cores.Waterbox
private class Bin private class Bin
{ {
public const MemoryBlock.Protection _FreeProt = (MemoryBlock.Protection)255;
/// <summary> /// <summary>
/// first page# in this bin, inclusive /// first page# in this bin, inclusive
/// </summary> /// </summary>
@ -62,11 +64,11 @@ namespace BizHawk.Emulation.Cores.Waterbox
{ {
get get
{ {
return (byte)Protection == 255; return Protection == _FreeProt;
} }
set set
{ {
Protection = value ? (MemoryBlock.Protection)255 : MemoryBlock.Protection.None; Protection = value ? _FreeProt : MemoryBlock.Protection.None;
} }
} }
@ -263,7 +265,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
var ds = Memory.GetStream(ret, oldSize, true); var ds = Memory.GetStream(ret, oldSize, true);
ss.CopyTo(ds); ss.CopyTo(ds);
Memory.Protect(ret, oldSize, oldStartBin.Protection); Memory.Protect(ret, oldSize, oldStartBin.Protection);
UnmapPagesInternal(oldStartPage, oldNumPages, oldStartBin); ProtectPagesInternal(oldStartPage, oldNumPages, oldStartBin, Bin._FreeProt);
//EnsureUsedInternal(); //EnsureUsedInternal();
return ret; return ret;
} }
@ -276,7 +278,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
{ {
// shrink in place // shrink in place
var s = GetBinForStartPage(newEndPage); var s = GetBinForStartPage(newEndPage);
UnmapPagesInternal(newEndPage, oldEndPage - newEndPage, s); ProtectPagesInternal(newEndPage, oldEndPage - newEndPage, s, Bin._FreeProt);
//EnsureUsedInternal(); //EnsureUsedInternal();
return start; return start;
} }
@ -302,14 +304,34 @@ namespace BizHawk.Emulation.Cores.Waterbox
if (GetBinForEndPageEnsureAllocated(endPage, startBin) == null) if (GetBinForEndPageEnsureAllocated(endPage, startBin) == null)
return false; return false;
UnmapPagesInternal(startPage, numPages, startBin); ProtectPagesInternal(startPage, numPages, startBin, Bin._FreeProt);
return true;
}
public bool Protect(ulong start, ulong size, MemoryBlock.Protection prot)
{
// TODO: lots of copy paste here
if (start < Memory.Start || start + size > Memory.End)
return false;
if (size == 0)
return true;
var startPage = GetPage(start);
var numPages = WaterboxUtils.PagesNeeded(size);
var endPage = startPage + numPages;
// to change protection, the entire area must currently be mapped
var startBin = GetBinForStartPage(startPage);
if (GetBinForEndPageEnsureAllocated(endPage, startBin) == null)
return false;
ProtectPagesInternal(startPage, numPages, startBin, prot);
return true; return true;
} }
/// <summary> /// <summary>
/// frees some pages. assumes they are all allocated /// frees or changes the protection on some pages. assumes they are all allocated
/// </summary> /// </summary>
private void UnmapPagesInternal(int startPage, int numPages, Bin startBin) private void ProtectPagesInternal(int startPage, int numPages, Bin startBin, MemoryBlock.Protection prot)
{ {
// from the various paths we took to get here, we must be unmapping at least one page // from the various paths we took to get here, we must be unmapping at least one page
@ -318,10 +340,10 @@ namespace BizHawk.Emulation.Cores.Waterbox
if (freeBin.StartPage != startPage) if (freeBin.StartPage != startPage)
{ {
freeBin.Cleave(startPage - freeBin.StartPage); freeBin.Cleave(startPage - freeBin.StartPage);
freeBin.Next.Protection = freeBin.Protection;
freeBin = freeBin.Next; freeBin = freeBin.Next;
} }
freeBin.Free = true; MemoryBlock.Protection lastEaten = freeBin.Protection;
MemoryBlock.Protection lastEaten = MemoryBlock.Protection.None;
while (freeBin.EndPage < endPage) while (freeBin.EndPage < endPage)
{ {
freeBin.PageCount += freeBin.Next.PageCount; freeBin.PageCount += freeBin.Next.PageCount;
@ -332,11 +354,19 @@ namespace BizHawk.Emulation.Cores.Waterbox
{ {
freeBin.Next.Protection = lastEaten; freeBin.Next.Protection = lastEaten;
} }
freeBin.Protection = prot;
freeBin.ApplyProtection(Memory); freeBin.ApplyProtection(Memory);
var totalSize = ((ulong)numPages) << WaterboxUtils.PageShift; var totalSize = ((ulong)numPages) << WaterboxUtils.PageShift;
Used -= totalSize; if (prot == Bin._FreeProt)
Console.WriteLine($"Freed {totalSize} bytes on {Name}, utilization {Used}/{Memory.Size} ({100.0 * Used / Memory.Size:0.#}%)"); {
Used -= totalSize;
Console.WriteLine($"Freed {totalSize} bytes on {Name}, utilization {Used}/{Memory.Size} ({100.0 * Used / Memory.Size:0.#}%)");
}
else
{
Console.WriteLine($"Set protection for {totalSize} bytes on {Name} to {prot}");
}
//EnsureUsedInternal(); //EnsureUsedInternal();
} }

View File

@ -456,6 +456,25 @@ namespace BizHawk.Emulation.Cores.Waterbox
{ {
return _parent._mmapheap.Unmap((ulong)address, (ulong)size) ? 0 : -1; return _parent._mmapheap.Unmap((ulong)address, (ulong)size) ? 0 : -1;
} }
[BizExport(CallingConvention.Cdecl, EntryPoint = "n10")]
public int MProtect(UIntPtr address, UIntPtr size, int prot)
{
MemoryBlock.Protection mprot;
switch (prot)
{
case 0: mprot = MemoryBlock.Protection.None; break;
default:
case 6: // W^X
case 7: // W^X
case 4: // exec only????
case 2: return -1; // write only????
case 3: mprot = MemoryBlock.Protection.RW; break;
case 1: mprot = MemoryBlock.Protection.R; break;
case 5: mprot = MemoryBlock.Protection.RX; break;
}
return _parent._mmapheap.Protect((ulong)address, (ulong)size, mprot) ? 0 : -1;
}
} }
/// <summary> /// <summary>

Binary file not shown.

Binary file not shown.

6
waterbox/libco/.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,6 @@
// Place your settings in this file to overwrite default and user settings.
{
"editor.insertSpaces": false,
"editor.detectIndentation": false,
"editor.tabSize": 4
}

View File

@ -1,7 +1,7 @@
CC = x86_64-nt64-midipix-gcc CC = x86_64-nt64-midipix-gcc
AS = nasm AS = nasm
CCFLAGS:= -Wall -O3 CCFLAGS:= -Wall -O3 -std=c99
ASFLAGS:= -f elf64 ASFLAGS:= -f elf64
TARGET = libco.so TARGET = libco.so
@ -21,25 +21,25 @@ AOBJS:=$(patsubst $(ROOT_DIR)%,$(OBJ_DIR)%,$(_AOBJS))
$(OBJ_DIR)/%.c.o: %.c $(OBJ_DIR)/%.c.o: %.c
@mkdir -p $(@D) @mkdir -p $(@D)
$(CC) -c -o $@ $< $(CCFLAGS) @$(CC) -c -o $@ $< $(CCFLAGS)
$(OBJ_DIR)/%.s.o: %.s $(OBJ_DIR)/%.s.o: %.s
@mkdir -p $(@D) @mkdir -p $(@D)
$(AS) $(ASFLAGS) -o $@ $< @$(AS) $(ASFLAGS) -o $@ $<
all: $(TARGET) all: $(TARGET)
.PHONY: clean all .PHONY: clean all
$(TARGET): $(OBJS) $(AOBJS) $(TARGET): $(OBJS) $(AOBJS)
$(CC) -o $@ $(LDFLAGS) $(CCFLAGS) $(OBJS) $(AOBJS) @$(CC) -o $@ $(LDFLAGS) $(CCFLAGS) $(OBJS) $(AOBJS)
mv $(TARGET) $(TARGET).in @mv $(TARGET) $(TARGET).in
strip $(TARGET).in -o $(TARGET) -R /4 -R /14 -R /29 -R /41 -R /55 -R /67 -R /78 -R /89 -R /104 @strip $(TARGET).in -o $(TARGET) -R /4 -R /14 -R /29 -R /41 -R /55 -R /67 -R /78 -R /89 -R /104
clean: clean:
rm -rf $(OBJ_DIR) @rm -rf $(OBJ_DIR)
rm -f $(TARGET).in @rm -f $(TARGET).in
rm -f $(TARGET) @rm -f $(TARGET)
#install: #install:
# $(CP) $(TARGET) $(DEST_$(ARCH)) # $(CP) $(TARGET) $(DEST_$(ARCH))

View File

@ -7,76 +7,94 @@
#define LIBCO_C #define LIBCO_C
#include "libco.h" #include "libco.h"
#include <stdint.h>
#include <assert.h> #include <assert.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/mman.h>
#ifdef __cplusplus
extern "C" {
#endif
static long long co_active_buffer[64]; static long long co_active_buffer[64];
static cothread_t co_active_handle = 0; static cothread_t co_active_handle = 0;
static void* smalloc(size_t size) // allocations are 16k larger than asked for,
// and include guard space between the stack and the storage
static void* alloc_thread(size_t* size)
{ {
char* ret = malloc(size + 16); // align up to 4k
if (ret) *size = (*size + 16384 + 4095) & ~4095;
{
*(size_t*)ret = size; uint64_t* ptr = mmap(NULL, *size,
return ret + 16; PROT_READ | PROT_WRITE, MAP_ANONYMOUS, -1, 0);
} if (ptr == (uint64_t*)(-1))
return NULL; return NULL;
ptr[512] = *size;
for (int i = 513; i < 2048; i++)
ptr[i] = 0xdeadbeefdeadbeef;
if (mprotect(ptr + 512, 512 * 3 * sizeof(uint64_t), PROT_NONE) != 0)
abort();
return ptr;
} }
static void sfree(void* ptr) static void free_thread(void* p)
{ {
char* original = (char*)ptr - 16; uint64_t* ptr = (uint64_t*)p;
size_t size = *(size_t*)original + 16; if (mprotect(ptr + 512, 512 * 3 * sizeof(uint64_t), PROT_READ | PROT_WRITE) != 0)
memset(original, 0, size); abort();
free(original); uint64_t size = ptr[512];
memset(p, 0, size);
if (munmap(ptr, size) != 0)
abort();
} }
extern void co_swap(cothread_t, cothread_t); extern void co_swap(cothread_t, cothread_t);
static void crash() { static void crash()
assert(0); /* called only if cothread_t entrypoint returns */ {
assert(0); /* called only if cothread_t entrypoint returns */
} }
void co_clean() { void co_clean()
memset(co_active_buffer, 0, sizeof(co_active_buffer)); {
memset(co_active_buffer, 0, sizeof(co_active_buffer));
} }
cothread_t co_active() { cothread_t co_active()
if(!co_active_handle) co_active_handle = &co_active_buffer; {
return co_active_handle; if (!co_active_handle)
co_active_handle = &co_active_buffer;
return co_active_handle;
} }
cothread_t co_create(unsigned int size, void (*entrypoint)(void)) { cothread_t co_create(unsigned int sz, void (*entrypoint)(void))
cothread_t handle; {
if(!co_active_handle) co_active_handle = &co_active_buffer; cothread_t handle;
size += 512; /* allocate additional space for storage */ if (!co_active_handle)
size &= ~15; /* align stack to 16-byte boundary */ co_active_handle = &co_active_buffer;
if(handle = (cothread_t)smalloc(size)) { uint64_t size = sz;
long long *p = (long long*)((char*)handle + size); /* seek to top of stack */
*--p = (long long)crash; /* crash if entrypoint returns */
*--p = (long long)entrypoint; /* start of function */
*(long long*)handle = (long long)p; /* stack pointer */
}
return handle; if (handle = (cothread_t)alloc_thread(&size))
{
uint64_t* p = (uint64_t*)((char*)handle + size); // seek to top of stack
*--p = (uint64_t)crash; /* crash if entrypoint returns */
*--p = (uint64_t)entrypoint; /* start of function */
*(uint64_t*)handle = (uint64_t)p; /* stack pointer */
}
return handle;
} }
void co_delete(cothread_t handle) { void co_delete(cothread_t handle)
sfree(handle); {
free_thread(handle);
} }
void co_switch(cothread_t handle) { void co_switch(cothread_t handle)
register cothread_t co_previous_handle = co_active_handle; {
co_swap(co_active_handle = handle, co_previous_handle); register cothread_t co_previous_handle = co_active_handle;
co_swap(co_active_handle = handle, co_previous_handle);
} }
#ifdef __cplusplus
}
#endif

Binary file not shown.