/* $Id: kLdrHlpMem.c 2944 2007-01-13 15:55:40Z bird $ */ /** @file * * kLdr - The Dynamic Loader, Memory Helper Functions. * * Copyright (c) 2006-2007 knut st. osmundsen * * * This file is part of kLdr. * * kLdr is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * kLdr is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with kLdr; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ /******************************************************************************* * Header Files * *******************************************************************************/ #ifdef __OS2__ # define INCL_BASE # define INCL_ERRORS # include #elif defined(__WIN__) # include #else # error "port me" #endif #include #include "kLdrHlp.h" /******************************************************************************* * Global Variables * *******************************************************************************/ #ifdef __OS2__ /** The base of the stub object. * The OS/2 exe stub consists of a single data object. When allocating memory * for an executable, we'll have to reuse this. */ static void *g_pvStub = NULL; /** The size of the stub object - 0 if no stub. */ static size_t g_cbStub = 0; #elif defined(__WIN__) /** The system info. */ static SYSTEM_INFO g_SystemInfo; #else # error "port me" #endif #ifdef __OS2__ static ULONG kldrHlpPageProtToNative(KLDRPROT enmProt) { switch (enmProt) { case KLDRPROT_NOACCESS: return PAG_EXECUTE | PAG_READ | PAG_WRITE; case KLDRPROT_READONLY: return PAG_COMMIT | PAG_READ; case KLDRPROT_READWRITE: return PAG_COMMIT | PAG_READ | PAG_WRITE; case KLDRPROT_EXECUTE: return PAG_COMMIT | PAG_EXECUTE; case KLDRPROT_EXECUTE_READ: return PAG_COMMIT | PAG_EXECUTE | PAG_READ; case KLDRPROT_EXECUTE_READWRITE: return PAG_COMMIT | PAG_EXECUTE | PAG_READ | PAG_WRITE; default: kldrHlpAssert(0); return ~0U; } } #elif defined(__WIN__) static DWORD kldrHlpPageProtToNative(KLDRPROT enmProt) { switch (enmProt) { case KLDRPROT_NOACCESS: return PAGE_NOACCESS; case KLDRPROT_READONLY: return PAGE_READONLY; case KLDRPROT_READWRITE: return PAGE_READWRITE; case KLDRPROT_EXECUTE: return PAGE_EXECUTE; case KLDRPROT_EXECUTE_READ: return PAGE_EXECUTE_READ; case KLDRPROT_EXECUTE_READWRITE: return PAGE_EXECUTE_READWRITE; default: kldrHlpAssert(0); return ~0U; } } #endif /** * Allocate a chunk of memory with page granularity. * * @returns 0 on success, non-zero OS status code on failure. * @param ppv Where to store the address of the allocated memory. * If fFixed is set, *ppv will on entry contain the desired address (page aligned). * @param cb Number of bytes. Page aligned. * @param enmProt The new protection. Copy-on-write is invalid. */ int kldrHlpPageAlloc(void **ppv, size_t cb, KLDRPROT enmProt, unsigned fFixed) { #ifdef __OS2__ APIRET rc; ULONG fFlags = kldrHlpPageProtToNative(enmProt);; if (!fFixed) { /* simple */ rc = DosAllocMem(ppv, cb, fFlags | OBJ_ANY); if (rc == ERROR_INVALID_PARAMETER) rc = DosAllocMem(ppv, cb, fFlags); } else { /* not so simple. */ /** @todo I've got code for this in libc somewhere. */ rc = -1; } if (!rc) return 0; kldrHlpAssert(0); return rc; #elif defined(__WIN__) /* (We don't have to care about the stub here, because the stub will be unmapped before we get here.) */ int rc; DWORD fProt = kldrHlpPageProtToNative(enmProt); if (!g_SystemInfo.dwPageSize) GetSystemInfo(&g_SystemInfo); *ppv = VirtualAlloc(fFixed ? *ppv : NULL, cb, MEM_COMMIT, fProt); if (*ppv != NULL) return 0; rc = GetLastError(); kldrHlpAssert(0); return rc; #else # error "port me" #endif } /** * Change the protection of one or more pages in an allocation. * * (This will of course only work correctly on memory allocated by kldrHlpPageAlloc().) * * @returns 0 on success, non-zero OS status code on failure. * @param pv First page. Page aligned. * @param cb Number of bytes. Page aligned. * @param enmProt The new protection. Copy-on-write is invalid. */ int kldrHlpPageProtect(void *pv, size_t cb, KLDRPROT enmProt) { #ifdef __OS2__ APIRET rc; ULONG fFlags = kldrHlpPageProtToNative(enmProt);; /* * The non-stub pages. */ rc = DosSetMem(pv, cb, fFlags); if (rc && fFlags != PAG_DECOMMIT) rc = DosSetMem(pv, cb, fFlags | PAG_COMMIT); if (rc) { /* Try page by page. */ while (cb > 0) { rc = DosSetMem(pv, 0x1000, fFlags); if (rc && fFlags != PAG_DECOMMIT) rc = DosSetMem(pv, 0x1000, fFlags | PAG_COMMIT); if (rc) return rc; pv = (void *)((uintptr_t)pv + 0x1000); cb -= 0x1000; } } kldrHlpAssert(!rc); return rc; #elif defined(__WIN__) DWORD fOldProt = 0; DWORD fProt = kldrHlpPageProtToNative(enmProt); int rc = 0; if (!VirtualProtect(pv, cb, fProt, &fOldProt)) { rc = GetLastError(); kldrHlpAssert(0); } return rc; #else # error "port me" #endif } /** * Free memory allocated by kldrHlpPageAlloc(). * * @returns 0 on success, non-zero OS status code on failure. * @param pv The address returned by kldrHlpPageAlloc(). * @param cb The byte count requested from kldrHlpPageAlloc(). */ int kldrHlpPageFree(void *pv, size_t cb) { #ifdef __OS2__ APIRET rc; /* * Deal with any portion overlapping with the stub. */ uintptr_t offStub = (uintptr_t)pv - (uintptr_t)g_pvStub; if (offStub < g_cbStub) { /* decommit the pages in the stub. */ size_t cbStub = KLDR_MIN(g_cbStub - offStub, cb); rc = DosSetMem(pv, cbStub, PAG_DECOMMIT); if (rc) { /* Page by page, ignoring errors after the first success. */ while (cbStub > 0) { if (!DosSetMem(pv, 0x1000, PAG_DECOMMIT)) rc = 0; pv = (void *)((uintptr_t)pv + 0x1000); cbStub -= 0x1000; cb -= 0x1000; } if (rc) { kldrHlpAssert(!rc); return rc; } } else { cb -= cbStub; if (!cb) return 0; pv = (void *)((uintptr_t)pv + cbStub); } } /* * Free the object. */ rc = DosFreeMem(pv); kldrHlpAssert(!rc); return rc; #elif defined(__WIN__) /* * Free the object. */ int rc = 0; if (!VirtualFree(pv, 0 /*cb*/, MEM_RELEASE)) { rc = GetLastError(); kldrHlpAssert(0); } return rc; #else # error "port me" #endif }