Skip to content
This repository has been archived by the owner. It is now read-only.
Permalink
858d034a3b
Go to file
 
 
Cannot retrieve contributors at this time
914 lines (771 sloc) 29.2 KB
/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <windows.h>
#include "MMgc.h"
#include "../other-licenses/wtf/AddressSpaceRandomization.h"
#ifdef MMGC_MEMORY_PROFILER
#include <malloc.h>
#include <strsafe.h>
#ifndef UNDER_CE // Not available on WinMo builds
#include <DbgHelp.h>
#include <io.h>
#endif
#endif
#ifdef NANOJIT_WIN_CFG
#include <stdlib.h>
#include <stdio.h>
#ifndef INTERNAL_BUILD
typedef struct _CFG_CALL_TARGET_INFO {
ULONG_PTR Offset;
ULONG_PTR Flags;
} CFG_CALL_TARGET_INFO, *PCFG_CALL_TARGET_INFO;
#endif
#define FILE_MAP_TARGETS_INVALID 0x40000000
#define PAGE_TARGETS_NO_UPDATE 0x40000000
#define PAGE_TARGETS_INVALID 0x40000000
#define CFG_CALL_TARGET_VALID (0x1)
#pragma section(".00cfg", read)
#define DECLSPEC_CFG_SECTION __declspec(allocate(".00cfg"))
BOOL WINAPI SetProcessValidCallTargetsStub(_In_ HANDLE,
_In_ PVOID,
_In_ SIZE_T,
_In_ ULONG,
_In_reads_(0) PCFG_CALL_TARGET_INFO)
{
return FALSE;
}
// SetProcessValidCallTargets function pointer type.
typedef BOOL (WINAPI *FNCSetProcessValidCallTargets)(HANDLE, PVOID, SIZE_T, ULONG, PCFG_CALL_TARGET_INFO);
//
// Allocate SetProcessValidCallTargetsPtr in the .00cfg so that it is implicitly merged into the
// import section of the binary (which is read-only).
// Indirect calls using this pointer do not have the guard check function,
// and so will not fail the CFG check.
//
EXTERN_C DECLSPEC_CFG_SECTION FNCSetProcessValidCallTargets SetProcessValidCallTargetsPtr = SetProcessValidCallTargetsStub;
EXTERN_C DECLSPEC_CFG_SECTION LONG gIsSupportedSetICallTarget = false;
EXTERN_C DECLSPEC_CFG_SECTION LONG gIsSetICallTargetResolved = false;
DECLSPEC_GUARDNOCF BOOL CallSetProcessValidCallTargets( _In_ HANDLE hProcess,
_In_ PVOID pBuffer,
_In_ SIZE_T nLength,
_In_ ULONG nInfoArgs,
_In_reads_(0) PCFG_CALL_TARGET_INFO InfoArgs)
{
// Read into local to avoid compiler optimizing away a read-only global variable.
FNCSetProcessValidCallTargets pfn;
pfn = (FNCSetProcessValidCallTargets)ReadPointerNoFence((volatile PVOID *)&SetProcessValidCallTargetsPtr);
return pfn(hProcess, pBuffer, nLength, nInfoArgs, InfoArgs);
}
DECLSPEC_GUARDNOCF BOOL IsSupportedSetICallTarget()
{
// Read into local to avoid compiler optimizing away a read-only global variable.
BOOL ret;
ret = (BOOL)ReadNoFence((volatile LONG *)&gIsSupportedSetICallTarget);
return ret;
}
DECLSPEC_GUARDNOCF BOOL IsSetICallTargetResolved()
{
// Read into local to avoid compiler optimizing away a read-only global variable.
BOOL ret;
ret = (BOOL)ReadNoFence((volatile LONG *)&gIsSetICallTargetResolved);
return ret;
}
VOID ResolveSetProcessValidCallTargets()
{
ULONG dwOldProtect;
FNCSetProcessValidCallTargets funcPtr;
if (IsSetICallTargetResolved())
return;
if (!VirtualProtect(&gIsSetICallTargetResolved, sizeof(BOOL), PAGE_READWRITE, &dwOldProtect))
return;
gIsSetICallTargetResolved = (LONG)true;
if (!VirtualProtect(&gIsSetICallTargetResolved, sizeof(BOOL), dwOldProtect, &dwOldProtect))
return;
funcPtr = (FNCSetProcessValidCallTargets)GetProcAddress(GetModuleHandleW(L"api-ms-win-core-memory-l1-1-3.dll"), "SetProcessValidCallTargets");
if (funcPtr == nullptr)
return;
if (!VirtualProtect(&SetProcessValidCallTargetsPtr, sizeof(PVOID), PAGE_READWRITE, &dwOldProtect))
return;
SetProcessValidCallTargetsPtr = funcPtr;
if (!VirtualProtect(&SetProcessValidCallTargetsPtr, sizeof(PVOID), dwOldProtect, &dwOldProtect))
return;
if (!VirtualProtect(&gIsSupportedSetICallTarget, sizeof(BOOL), PAGE_READWRITE, &dwOldProtect))
return;
gIsSupportedSetICallTarget = (LONG)true;
if (!VirtualProtect(&gIsSupportedSetICallTarget, sizeof(BOOL), dwOldProtect, &dwOldProtect))
return;
}
#endif //NANOJIT_WIN_CFG
bool AVMPI_canMergeContiguousRegions()
{
return false;
}
bool AVMPI_canCommitAlreadyCommittedMemory()
{
return true;
}
bool AVMPI_useVirtualMemory()
{
return true;
}
bool AVMPI_areNewPagesDirty()
{
return false;
}
void* AVMPI_reserveMemoryRegion(void* address, size_t size)
{
#ifdef NANOJIT_WIN_CFG
// Resolve SetProcessValidCallTargets() function pointer if OS supports it.
ResolveSetProcessValidCallTargets();
#endif
#ifdef AVMPLUS_64BIT
if (!address) {
address = WTF::getRandomPageBase();
}
#endif // AVMPLUS_64BIT
return VirtualAlloc(address,
size,
MEM_RESERVE,
PAGE_NOACCESS);
}
bool AVMPI_releaseMemoryRegion(void* address, size_t /*size*/)
{
return VirtualFree(address, 0, MEM_RELEASE) == TRUE;
}
static bool CommitMemory(void* address, size_t size)
{
address = VirtualAlloc(address,
size,
MEM_COMMIT,
PAGE_READWRITE);
return address != NULL;
}
bool AVMPI_commitMemory(void* address, size_t size)
{
bool success = false;
MEMORY_BASIC_INFORMATION mbi;
do {
VirtualQuery(address, &mbi, sizeof(MEMORY_BASIC_INFORMATION));
size_t commitSize = size > mbi.RegionSize ? mbi.RegionSize : size;
success = CommitMemory(address, commitSize);
address = (char*)address + commitSize;
size -= commitSize;
} while(size > 0 && success);
return success;
}
bool AVMPI_decommitMemory(char *address, size_t size)
{
bool success = false;
MEMORY_BASIC_INFORMATION mbi;
do {
VirtualQuery(address, &mbi, sizeof(MEMORY_BASIC_INFORMATION));
size_t commitSize = size > mbi.RegionSize ? mbi.RegionSize : size;
success = VirtualFree(address, commitSize, MEM_DECOMMIT) == TRUE;
address += commitSize;
size -= commitSize;
} while(size > 0 && success);
return success;
}
void* AVMPI_allocateAlignedMemory(size_t size)
{
return VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE);
}
void AVMPI_releaseAlignedMemory(void* address)
{
AVMPI_releaseMemoryRegion(address, 0);
}
#ifdef UNDER_CE
typedef DWORD (*pTGetProcessIndexFromID)(HANDLE hProc);
static pTGetProcessIndexFromID gGetID=NULL;
#if _WIN32_WCE>=0x600
THIS WILL NOT WORK ON WINCE 6.0 AND ABOVE
#endif
#endif
#ifdef UNDER_CE
// The WinCE version of getPrivateResidentPageCount must do some specific things to get
// an accurate picture of private bytes due to how WinCE lays out memory for the process.
// see http://msdn.microsoft.com/en-us/library/bb331824.aspx for a desccription of how the memory is laid out.
// Note that we are running on Windows Mobile 6.0, but that is based on WinCE 5.0.
// Basically, first we walk the memory for the process slot, from 0x10000 to 0x2000000. Then we walk the memory
// in the large memory area (0x42000000 - 0x80000000), as this is where gcheap allocates memory from.
size_t AVMPI_getPrivateResidentPageCount()
{
void *addr = (void*)(0x00010000);
void *endAddr = (void*)(0x02000000);
size_t bytes=0;
MEMORY_BASIC_INFORMATION mib;
while(true)
{
size_t ret = VirtualQuery(addr, &mib, sizeof(MEMORY_BASIC_INFORMATION));
if(ret == 0)
break;
if((mib.State & MEM_COMMIT))
if ((DWORD)mib.BaseAddress + mib.RegionSize > (DWORD)endAddr)
bytes += (DWORD)endAddr - (DWORD)mib.BaseAddress;
else
bytes += mib.RegionSize;
addr = (void*) ((intptr_t)mib.BaseAddress + mib.RegionSize);
if (addr>=endAddr)
break;
}
MMgc::GCHeap* heap = MMgc::GCHeap::GetGCHeap();
// We need to also walk the shared memory regions to make sure we
// count the blocks we've allocated there
MMgc::GCHeap::Region* curRegion = heap->lastRegion;
if (curRegion)
addr = curRegion->baseAddr;
else
addr = NULL;
while (curRegion!=NULL)
{
addr = curRegion->baseAddr;
if (addr < (void*)0x42000000)
{
// Not in the shared regions
curRegion = curRegion->prev;
continue;
}
while(true)
{
size_t ret = VirtualQuery(addr, &mib, sizeof(MEMORY_BASIC_INFORMATION));
if(ret == 0)
break;
if((mib.State & MEM_COMMIT)) // && (mib.Type & MEM_PRIVATE))
{
if ((DWORD)mib.BaseAddress + mib.RegionSize > (DWORD)curRegion->reserveTop)
bytes += (DWORD)curRegion->reserveTop - (DWORD)mib.BaseAddress;
else
bytes += mib.RegionSize;
}
addr = (void*) ((intptr_t)mib.BaseAddress + mib.RegionSize);
if (addr>=curRegion->reserveTop)
break;
}
curRegion = curRegion->prev;
}
return bytes / VMPI_getVMPageSize();
}
#else // UNDER_CE
size_t AVMPI_getPrivateResidentPageCount()
{
void *addr = 0;
size_t bytes=0;
MEMORY_BASIC_INFORMATION mib;
while(true)
{
size_t ret = VirtualQuery(addr, &mib, sizeof(MEMORY_BASIC_INFORMATION));
if(ret == 0)
break;
if((mib.State & MEM_COMMIT) && (mib.Type & MEM_PRIVATE))
bytes += mib.RegionSize;
addr = (void*) ((intptr_t)mib.BaseAddress + mib.RegionSize);
}
return bytes / VMPI_getVMPageSize();
}
#endif //UNDER_CE
void AVMPI_cleanStack(size_t amt)
{
void *space = alloca(amt);
if(space)
{
VMPI_memset(space, 0, amt);
}
}
uintptr_t AVMPI_getThreadStackBase()
{
MEMORY_BASIC_INFORMATION __mib;
VirtualQuery(&__mib, &__mib, sizeof(MEMORY_BASIC_INFORMATION));
return (uintptr_t)__mib.BaseAddress + __mib.RegionSize;
}
#ifdef MMGC_MEMORY_PROFILER
#ifndef UNDER_CE
namespace MMgc
{
// This relies on headers that don't exist in winmo builds
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
// helper snarfed and simplified from main flash player code.
// since we only need it here and only for debug, I didn't bother
// migrating the whole thing.
class DynamicLoadLibraryHelper
{
protected:
DynamicLoadLibraryHelper(const char* p_dllName, bool p_required = true);
virtual ~DynamicLoadLibraryHelper();
FARPROC GetProc(const char* p_funcName, bool p_required = true);
public:
// note that this is only if any of the *required* ones failed;
// some "optional" ones may be missing and still have this return true.
bool AllRequiredItemsPresent() const { return m_allRequiredItemsPresent; }
private:
HMODULE m_lib;
bool m_allRequiredItemsPresent;
};
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
#define GETPROC(n) do { m_##n = (n##ProcPtr)GetProc(#n); } while (0)
#define GETPROC_OPTIONAL(n) do { m_##n = (n##ProcPtr)GetProc(#n, false); } while (0)
// --------------------------------------------------------------------------
DynamicLoadLibraryHelper::DynamicLoadLibraryHelper(const char* p_dllName, bool p_required) :
m_lib(NULL),
m_allRequiredItemsPresent(true) // assume the best
{
m_lib = ::LoadLibraryA(p_dllName);
if (p_required && (m_lib == NULL || m_lib == INVALID_HANDLE_VALUE))
{
// don't assert here... it will trigger a DebugBreak(), which will crash
// systems not running a debugger... and QE insists that they be able
// to run Debug builds on debuggerless Win98 systems... (sigh)
//GCAssertMsg(0, p_dllName);
m_allRequiredItemsPresent = false;
}
}
// --------------------------------------------------------------------------
FARPROC DynamicLoadLibraryHelper::GetProc(const char* p_funcName, bool p_required)
{
FARPROC a_proc = NULL;
if (m_lib != NULL && m_lib != INVALID_HANDLE_VALUE)
{
a_proc = ::GetProcAddress(m_lib, p_funcName);
}
if (p_required && a_proc == NULL)
{
// don't assert here... it will trigger a DebugBreak(), which will crash
// systems not running a debugger... and QE insists that they be able
// to run Debug builds on debuggerless Win98 systems... (sigh)
//GCAssertMsg(0, p_funcName);
m_allRequiredItemsPresent = false;
}
return a_proc;
}
// --------------------------------------------------------------------------
DynamicLoadLibraryHelper::~DynamicLoadLibraryHelper()
{
if (m_lib != NULL && m_lib != INVALID_HANDLE_VALUE)
{
::FreeLibrary(m_lib);
}
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
class DbgHelpDllHelper : public DynamicLoadLibraryHelper
{
public:
DbgHelpDllHelper();
public:
typedef BOOL (__stdcall *StackWalk64ProcPtr)(
DWORD MachineType,
HANDLE hProcess,
HANDLE hThread,
LPSTACKFRAME64 StackFrame,
PVOID ContextRecord,
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,
PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,
PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress
);
typedef PVOID (__stdcall *SymFunctionTableAccess64ProcPtr)(
HANDLE hProcess,
DWORD64 AddrBase
);
typedef DWORD64 (__stdcall *SymGetModuleBase64ProcPtr)(
HANDLE hProcess,
DWORD64 qwAddr
);
typedef BOOL (__stdcall *SymGetLineFromAddr64ProcPtr)(
HANDLE hProcess,
DWORD64 qwAddr,
PDWORD pdwDisplacement,
PIMAGEHLP_LINE64 Line64
);
typedef BOOL (__stdcall *SymGetSymFromAddr64ProcPtr)(
HANDLE hProcess,
DWORD64 qwAddr,
PDWORD64 pdwDisplacement,
PIMAGEHLP_SYMBOL64 Symbol
);
typedef BOOL (__stdcall *SymInitializeProcPtr)(
HANDLE hProcess,
PSTR UserSearchPath,
BOOL fInvadeProcess
);
public:
StackWalk64ProcPtr m_StackWalk64;
SymFunctionTableAccess64ProcPtr m_SymFunctionTableAccess64;
SymGetModuleBase64ProcPtr m_SymGetModuleBase64;
SymGetLineFromAddr64ProcPtr m_SymGetLineFromAddr64;
SymGetSymFromAddr64ProcPtr m_SymGetSymFromAddr64;
SymInitializeProcPtr m_SymInitialize;
};
// --------------------------------------------------------------------------
DbgHelpDllHelper::DbgHelpDllHelper() :
DynamicLoadLibraryHelper("dbghelp.dll"),
m_StackWalk64(NULL),
m_SymFunctionTableAccess64(NULL),
m_SymGetModuleBase64(NULL),
m_SymGetLineFromAddr64(NULL),
m_SymGetSymFromAddr64(NULL),
m_SymInitialize(NULL)
{
GETPROC(StackWalk64);
GETPROC(SymFunctionTableAccess64);
GETPROC(SymGetModuleBase64);
GETPROC(SymGetLineFromAddr64);
GETPROC(SymGetSymFromAddr64);
GETPROC(SymInitialize);
}
}
// declaring this statically will dynamically load the dll and procs
// at startup, and never ever release them... if this ever becomes NON-debug
// code, you might want to have a way to toss all this... but for _DEBUG
// only, it should be fine
static MMgc::DbgHelpDllHelper g_DbgHelpDll;
#endif
static const int MaxNameLength = 256;
#ifdef _WIN64
#define MACHINETYPE IMAGE_FILE_MACHINE_AMD64
#else
#define MACHINETYPE IMAGE_FILE_MACHINE_I386
#endif
bool InitDbgHelp()
{
static vmpi_spin_lock_t lock;
static bool inited = false;
// We must hold the lock for the entire initialization process:
// - if we set inited to true and Release the lock then other
// threads may forge ahead without initialization having occured
// - if we leave it false and Release then other threads
// may try to perform initialization as well.
MMGC_LOCK(lock);
if(!inited) {
#ifndef UNDER_CE
if(!g_DbgHelpDll.m_SymInitialize ||
!(*g_DbgHelpDll.m_SymInitialize)(GetCurrentProcess(), NULL, true)) {
LPVOID lpMsgBuf;
if(FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR) &lpMsgBuf, 0, NULL ))
{
GCAssertMsg(false, "See lpMsgBuf");
LocalFree(lpMsgBuf);
}
return false;
}
#endif // ifn UNDER_CE
inited = true;
}
return true;
}
#ifdef UNDER_CE
typedef ULONG NTAPI RtlCaptureStackBackTrace_Function(
IN HANDLE hThrd,
IN ULONG dwMaxFrames,
OUT void* lpFrames,
IN DWORD dwFlags,
IN DWORD dwSkip);
static RtlCaptureStackBackTrace_Function* const RtlCaptureStackBackTrace_fn =
(RtlCaptureStackBackTrace_Function*)
GetProcAddress(LoadLibrary(_T("coredll.dll")), _T("GetThreadCallStack"));
typedef struct _CallSnapshot {
DWORD dwReturnAddr;
} CallSnapshot;
#define STACKSNAP_EXTENDED_INFO 2
#else // UNDER_CE
typedef USHORT NTAPI RtlCaptureStackBackTrace_Function(
IN ULONG frames_to_skip,
IN ULONG frames_to_capture,
OUT PVOID *backtrace,
OUT PULONG backtrace_hash);
static RtlCaptureStackBackTrace_Function* const RtlCaptureStackBackTrace_fn =
(RtlCaptureStackBackTrace_Function*)
GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlCaptureStackBackTrace");
#endif // UNDER_CE
#ifdef UNDER_CE
#include <cmnintrin.h>
extern "C" unsigned long* get_frame_pointer();
#endif
bool AVMPI_captureStackTrace(uintptr_t *buffer, size_t bufferSize, uint32_t framesToSkip)
{
if(RtlCaptureStackBackTrace_fn == NULL)
return false;
#ifdef UNDER_CE
CallSnapshot lpFrames[32];
DWORD dwCnt = RtlCaptureStackBackTrace_fn(GetCurrentThread(), bufferSize >= 32 ? 32 : bufferSize, (void*)&lpFrames,0, framesToSkip);
size_t i = 0;
for( ; i < dwCnt && i < bufferSize ; ++i )
{
buffer[i] = lpFrames[i].dwReturnAddr;
}
#else
int num = RtlCaptureStackBackTrace_fn(framesToSkip, (uint32_t)(bufferSize - 1), (PVOID*)buffer, NULL);
buffer[num] = 0;
#endif //UNDER_CE
return true;
}
bool AVMPI_getFileAndLineInfoFromPC(uintptr_t pc, char *filenameBuffer, size_t bufferSize, uint32_t* lineNumber)
{
#ifdef UNDER_CE
(void)pc;
(void)lineNumber;
(void)filenameBuffer;
(void)bufferSize;
return false;
#else
if(!InitDbgHelp())
return false;
// gleaned from IMAGEHLP_SYMBOL64 docs
IMAGEHLP_SYMBOL64 *pSym = (IMAGEHLP_SYMBOL64 *) alloca(sizeof(IMAGEHLP_SYMBOL64) + MaxNameLength);
pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
pSym->MaxNameLength = MaxNameLength;
DWORD64 offsetFromSymbol;
if(!g_DbgHelpDll.m_SymGetSymFromAddr64 ||
!(*g_DbgHelpDll.m_SymGetSymFromAddr64)(GetCurrentProcess(), pc, &offsetFromSymbol, pSym)) {
return false;
}
// get line
IMAGEHLP_LINE64 line;
VMPI_memset(&line, 0, sizeof(IMAGEHLP_LINE64));
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
DWORD offsetFromLine;
if(!g_DbgHelpDll.m_SymGetLineFromAddr64 ||
!(*g_DbgHelpDll.m_SymGetLineFromAddr64)(GetCurrentProcess(), pc, &offsetFromLine, &line)) {
return false;
}
// success!
char *fileName = line.FileName + VMPI_strlen(line.FileName);
// skip everything up to last slash
while(fileName > line.FileName && *fileName != '\\')
fileName--;
fileName++;
StringCchPrintfA(filenameBuffer, bufferSize, "%s", fileName);
*lineNumber = line.LineNumber;
return true;
#endif // UNDER_CE
}
bool AVMPI_getFunctionNameFromPC(uintptr_t pc, char *buffer, size_t bufferSize)
{
#ifdef UNDER_CE
(void)pc;
(void)buffer;
(void)bufferSize;
return false;
#else
if(!InitDbgHelp())
return false;
// gleaned from IMAGEHLP_SYMBOL64 docs
IMAGEHLP_SYMBOL64 *pSym = (IMAGEHLP_SYMBOL64 *) alloca(sizeof(IMAGEHLP_SYMBOL64) + MaxNameLength);
pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
pSym->MaxNameLength = MaxNameLength;
DWORD64 offsetFromSymbol;
if(!g_DbgHelpDll.m_SymGetSymFromAddr64 ||
!(*g_DbgHelpDll.m_SymGetSymFromAddr64)(GetCurrentProcess(), pc, &offsetFromSymbol, pSym)) {
return false;
}
StringCchPrintfA(buffer, bufferSize, "%s", pSym->Name);
//printf("%s\n", pSym->Name);
return true;
#endif //UNDER_CE
}
void AVMPI_setupPCResolution() { }
void AVMPI_desetupPCResolution() { }
bool AVMPI_isMemoryProfilingEnabled()
{
#if defined (UNDER_CE) && defined(MMGC_MEMORY_PROFILER)
return true;
#else
//read the mmgc profiling option switch
const char *env = VMPI_getenv("MMGC_PROFILE");
return (env && (VMPI_strncmp(env, "1", 1) == 0));
#endif
}
#endif // MMGC_MEMORY_PROFILER
// We believe that we may be failing to set code memory executable in some cases,
// resulting in crashes. Let's instead crash immediately upon such failures in a
// way that the crash dump will tell us what happened.
VMPI_DEFINE_FAILFAST(CodeMemoryProtectionError)
// Constraint: nbytes must be a multiple of the VM page size.
//
// The returned memory will be aligned on a VM page boundary and cover
// an integral number of VM pages. This is necessary in order for
// AVMPI_makeCodeMemoryExecutable to work properly - it too operates
// on entire VM pages.
//
// This function should be the same as for generic Posix platforms, if you
// fix a bug here be sure to fix the bug there.
void *AVMPI_allocateCodeMemory(size_t nbytes)
{
MMgc::GCHeap* heap = MMgc::GCHeap::GetGCHeap();
size_t pagesize = VMPI_getVMPageSize();
if (nbytes % pagesize != 0) {
#ifdef DEBUG
char buf[256];
VMPI_snprintf(buf,
sizeof(buf),
"AVMPI_allocateCodeMemory invariants violated: request=%lu pagesize=%lu\nAborting.\n",
(unsigned long)nbytes,
(unsigned long)pagesize);
VMPI_log(buf);
#endif
VMPI_abort();
}
size_t nblocks = nbytes / MMgc::GCHeap::kBlockSize;
heap->SignalCodeMemoryAllocation(nblocks, true);
void *codePage = heap->GetPartition(MMgc::kCodePartition)->Alloc(nblocks, MMgc::GCHeap::flags_Alloc, pagesize / MMgc::GCHeap::kBlockSize);
#if defined(NANOJIT_WIN_CFG)
if (IsSupportedSetICallTarget())
{
if (!codePage)
{
// It should not happen!!
return NULL;
}
void *curPage = codePage;
do {
MEMORY_BASIC_INFORMATION mbi;
VirtualQuery(curPage, &mbi, sizeof(MEMORY_BASIC_INFORMATION));
size_t markSize = nbytes > mbi.RegionSize ? mbi.RegionSize : nbytes; // handle multiple adjoining regions
if (!VirtualAlloc(curPage,
markSize,
MEM_COMMIT,
(PAGE_EXECUTE_READ | PAGE_TARGETS_INVALID)))
{
// It should not happen!!
CodeMemoryProtectionError();
}
DWORD oldProtectFlags = 0;
// PAGE_TARGETS_NO_UPDATE should not be required, as we are not changing *to* an executable mode.
// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa366786%28v=vs.85%29.aspx .
if (!VirtualProtect(curPage, markSize, PAGE_READWRITE, &oldProtectFlags))
{
// It should not happen!!
CodeMemoryProtectionError();
}
curPage = (char*)curPage + markSize;
nbytes -= markSize;
} while (nbytes != 0);
}
#endif
return codePage;
}
// Constraint: address must have been returned from AVMPI_allocateCodeMemory
// and nbytes must be the size of the allocation. We can't quite check
// this, so we check that the address points to a page boundary and that
// the size is given as an integral number of VM pages and that the size
// corresponds to GCHeap's notion of the size.
//
// Usage note: on Posix, where the memory goes back into the common pool
// and isn't unmapped by the OS, it is very bad form for the client to
// free executable memory, we do not try to detect that (in DEBUG mode)
// but we probably should.
//
// This function should be the same as for generic Posix platforms, if you
// fix a bug here be sure to fix the bug there.
void AVMPI_freeCodeMemory(void* address, size_t nbytes)
{
MMgc::GCHeap* heap = MMgc::GCHeap::GetGCHeap();
size_t pagesize = VMPI_getVMPageSize();
size_t nblocks = heap->GetPartition(MMgc::kCodePartition)->Size(address);
size_t actualBytes = nblocks * MMgc::GCHeap::kBlockSize;
if ((uintptr_t)address % pagesize != 0 || nbytes % pagesize != 0 || nbytes != actualBytes) {
#ifdef DEBUG
char buf[256];
VMPI_snprintf(buf,
sizeof(buf),
"AVMPI_freeCodeMemory invariants violated: address=%llu provided=%llu actual=%llu\nAborting.\n",
(unsigned long long)(uintptr_t)address,
(unsigned long long)nbytes,
(unsigned long long)actualBytes);
VMPI_log(buf);
#endif
VMPI_abort();
}
heap->GetPartition(MMgc::kCodePartition)->Free(address);
heap->SignalCodeMemoryDeallocated(nblocks, true);
}
// Constraint: address must point into a block returned from AVMPI_allocateCodeMemory
// that has not been freed, it must point to a VM page boundary, and the number of
// bytes to protect must be an integral number of VM pages. We can't check that
// the memory was returned from AVMPI_allocateCodeMemory though and we don't check
// that the memory is currently allocated.
void AVMPI_makeCodeMemoryExecutable(void *address, size_t nbytes, bool makeItSo)
{
size_t pagesize = VMPI_getVMPageSize();
if ((uintptr_t)address % pagesize != 0 || nbytes % pagesize != 0) {
#ifdef DEBUG
char buf[256];
VMPI_snprintf(buf,
sizeof(buf),
"AVMPI_makeCodeMemoryExecutable invariants violated: address=%llu size=%llu pagesize=%llu\nAborting.\n",
(unsigned long long)(uintptr_t)address,
(unsigned long long)nbytes,
(unsigned long long)pagesize);
VMPI_log(buf);
#endif
VMPI_abort();
}
DWORD oldProtectFlags = 0;
DWORD newProtectFlags = 0;
#if defined(NANOJIT_WIN_CFG)
if (IsSupportedSetICallTarget())
{
if (makeItSo)
newProtectFlags = PAGE_EXECUTE_READ | PAGE_TARGETS_NO_UPDATE;
else
// According to MS docs, PAGE_TARGETS_NO_UPDATE is valid only when changing *to* an executable type.
newProtectFlags = PAGE_READWRITE;
}
else
#endif
{
if (makeItSo)
newProtectFlags = PAGE_EXECUTE_READ;
else
newProtectFlags = PAGE_READWRITE;
}
BOOL retval;
do {
MEMORY_BASIC_INFORMATION mbi;
VirtualQuery(address, &mbi, sizeof(MEMORY_BASIC_INFORMATION));
size_t markSize = nbytes > mbi.RegionSize ? mbi.RegionSize : nbytes; // handle multiple adjoining regions
retval = VirtualProtect(address, markSize, newProtectFlags, &oldProtectFlags);
AvmAssert(retval != 0);
if (retval == FALSE) CodeMemoryProtectionError();
address = (char*) address + markSize;
nbytes -= markSize;
} while(nbytes != 0 && retval != 0);
}
#if defined(NANOJIT_WIN_CFG)
void AVMPI_makeTargetValid(void *address, size_t pageSize, void *pFnc)
{
if (IsSupportedSetICallTarget())
{
do {
MEMORY_BASIC_INFORMATION mbi;
VirtualQuery(address, &mbi, sizeof(MEMORY_BASIC_INFORMATION));
size_t markSize = pageSize > mbi.RegionSize ? mbi.RegionSize : pageSize; // handle multiple adjoining regions
if (((char *)mbi.BaseAddress + mbi.RegionSize) > pFnc)
{
size_t offset = (size_t)((char *)pFnc - (char *)mbi.BaseAddress);
CFG_CALL_TARGET_INFO info;
info.Offset = offset;
info.Flags = CFG_CALL_TARGET_VALID;
if (!CallSetProcessValidCallTargets(GetCurrentProcess(), mbi.BaseAddress, markSize, 1, &info))
{
// TODO: error handler
CodeMemoryProtectionError();
}
return;
}
address = (char*)address + markSize;
pageSize -= markSize;
} while (pageSize != 0);
}
}
#endif
You can’t perform that action at this time.