summaryrefslogtreecommitdiff
path: root/src/pal/src/map/map.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/pal/src/map/map.cpp')
-rw-r--r--src/pal/src/map/map.cpp2749
1 files changed, 2749 insertions, 0 deletions
diff --git a/src/pal/src/map/map.cpp b/src/pal/src/map/map.cpp
new file mode 100644
index 0000000000..f3ec47b846
--- /dev/null
+++ b/src/pal/src/map/map.cpp
@@ -0,0 +1,2749 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*++
+
+
+
+Module Name:
+
+ map.cpp
+
+Abstract:
+
+ Implementation of file mapping API.
+
+
+
+--*/
+
+
+#include "pal/palinternal.h"
+#include "pal/dbgmsg.h"
+#include "pal/init.h"
+#include "pal/critsect.h"
+#include "pal/virtual.h"
+#include "pal/environ.h"
+#include "common.h"
+#include "pal/map.hpp"
+#include "pal/thread.hpp"
+#include "pal/file.hpp"
+#include "pal/malloc.hpp"
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "rt/ntimage.h"
+#include <pal_endian.h>
+
+using namespace CorUnix;
+
+SET_DEFAULT_DEBUG_CHANNEL(VIRTUAL);
+
+//
+// The mapping critical section guards access to the list
+// of currently mapped views. If a thread needs to access
+// both this critical section and the data for an object
+// it must acquire the object data first. That is, a thread
+// cannot acquire any other locks after taking hold of
+// this critical section.
+//
+
+CRITICAL_SECTION mapping_critsec;
+LIST_ENTRY MappedViewList;
+
+#ifndef CORECLR
+static PAL_ERROR MAPCreateTempFile(CPalThread *, PINT, PSZ);
+#endif // !CORECLR
+static PAL_ERROR MAPGrowLocalFile(INT, UINT);
+static PMAPPED_VIEW_LIST MAPGetViewForAddress( LPCVOID );
+static PAL_ERROR MAPDesiredAccessAllowed( DWORD, DWORD, DWORD );
+
+static INT MAPProtectionToFileOpenFlags( DWORD );
+static BOOL MAPIsRequestPermissible( DWORD, CFileProcessLocalData * );
+static BOOL MAPContainsInvalidFlags( DWORD );
+static DWORD MAPConvertProtectToAccess( DWORD );
+static INT MAPFileMapToMmapFlags( DWORD );
+static DWORD MAPMmapProtToAccessFlags( int prot );
+#if ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
+static NativeMapHolder * NewNativeMapHolder(CPalThread *pThread, LPVOID address, SIZE_T size,
+ SIZE_T offset, long init_ref_count);
+static LONG NativeMapHolderAddRef(NativeMapHolder * thisPMH);
+static LONG NativeMapHolderRelease(CPalThread *pThread, NativeMapHolder * thisPMH);
+static PMAPPED_VIEW_LIST FindSharedMappingReplacement(CPalThread *pThread, dev_t deviceNum, ino_t inodeNum,
+ SIZE_T size, SIZE_T offset);
+#endif
+
+static PAL_ERROR
+MAPRecordMapping(
+ IPalObject *pMappingObject,
+ void *pPEBaseAddress,
+ void *addr,
+ size_t len,
+ int prot
+ );
+
+static PAL_ERROR
+MAPmmapAndRecord(
+ IPalObject *pMappingObject,
+ void *pPEBaseAddress,
+ void *addr,
+ size_t len,
+ int prot,
+ int flags,
+ int fd,
+ off_t offset,
+ LPVOID *ppvBaseAddress
+ );
+
+#if !HAVE_MMAP_DEV_ZERO
+/* We need MAP_ANON. However on some platforms like HP-UX, it is defined as MAP_ANONYMOUS */
+#if !defined(MAP_ANON) && defined(MAP_ANONYMOUS)
+#define MAP_ANON MAP_ANONYMOUS
+#endif
+#endif
+
+void
+FileMappingCleanupRoutine(
+ CPalThread *pThread,
+ IPalObject *pObjectToCleanup,
+ bool fShutdown,
+ bool fCleanupSharedState
+ );
+
+PAL_ERROR
+FileMappingInitializationRoutine(
+ CPalThread *pThread,
+ CObjectType *pObjectType,
+ void *pImmutableData,
+ void *pSharedData,
+ void *pProcessLocalData
+ );
+
+CObjectType CorUnix::otFileMapping(
+ otiFileMapping,
+ FileMappingCleanupRoutine,
+ FileMappingInitializationRoutine,
+ sizeof(CFileMappingImmutableData),
+ sizeof(CFileMappingProcessLocalData),
+ 0,
+ PAGE_READWRITE | PAGE_READONLY | PAGE_WRITECOPY,
+ CObjectType::SecuritySupported,
+ CObjectType::SecurityInfoNotPersisted,
+ CObjectType::UnnamedObject,
+ CObjectType::LocalDuplicationOnly,
+ CObjectType::UnwaitableObject,
+ CObjectType::SignalingNotApplicable,
+ CObjectType::ThreadReleaseNotApplicable,
+ CObjectType::OwnershipNotApplicable
+ );
+
+CAllowedObjectTypes aotFileMapping(otiFileMapping);
+
+void
+FileMappingCleanupRoutine(
+ CPalThread *pThread,
+ IPalObject *pObjectToCleanup,
+ bool fShutdown,
+ bool fCleanupSharedState
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ CFileMappingImmutableData *pImmutableData = NULL;
+ CFileMappingProcessLocalData *pLocalData = NULL;
+ IDataLock *pLocalDataLock = NULL;
+ bool fDataChanged = FALSE;
+
+ if (TRUE == fCleanupSharedState)
+ {
+ //
+ // If we created a temporary file to back this mapping we need
+ // to unlink it now
+ //
+
+ palError = pObjectToCleanup->GetImmutableData(
+ reinterpret_cast<void**>(&pImmutableData)
+ );
+
+ if (NO_ERROR != palError)
+ {
+ ASSERT("Unable to obtain immutable data for object to be reclaimed");
+ return;
+ }
+
+ if (pImmutableData->bPALCreatedTempFile)
+ {
+ unlink(pImmutableData->szFileName);
+ }
+ }
+
+ if (FALSE == fShutdown)
+ {
+ //
+ // We only need to close the object's descriptor if we're not
+ // shutting down
+ //
+
+ palError = pObjectToCleanup->GetProcessLocalData(
+ pThread,
+ WriteLock,
+ &pLocalDataLock,
+ reinterpret_cast<void**>(&pLocalData)
+ );
+
+ if (NO_ERROR != palError)
+ {
+ ASSERT("Unable to obtain process local data for object to be reclaimed");
+ return;
+ }
+
+ if (-1 != pLocalData->UnixFd)
+ {
+ close(pLocalData->UnixFd);
+ pLocalData->UnixFd = -1;
+ fDataChanged = TRUE;
+ }
+
+ pLocalDataLock->ReleaseLock(pThread, fDataChanged);
+ }
+
+ //
+ // Why don't we need to deal with any views that may have been created
+ // from this mapping? If the process is shutting down then there's nothing
+ // that we need to take care of, as the OS will remove the underlying
+ // mappings when the process goes away. If we're not shutting down then
+ // there's no way for a view to exist against this mapping, since each
+ // view holds a reference against the mapping object.
+ //
+}
+
+PAL_ERROR
+FileMappingInitializationRoutine(
+ CPalThread *pThread,
+ CObjectType *pObjectType,
+ void *pvImmutableData,
+ void *pvSharedData,
+ void *pvProcessLocalData
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+
+ CFileMappingImmutableData *pImmutableData =
+ reinterpret_cast<CFileMappingImmutableData *>(pvImmutableData);
+ CFileMappingProcessLocalData *pProcessLocalData =
+ reinterpret_cast<CFileMappingProcessLocalData *>(pvProcessLocalData);
+
+ pProcessLocalData->UnixFd = InternalOpen(
+ pImmutableData->szFileName,
+ MAPProtectionToFileOpenFlags(pImmutableData->flProtect)
+ );
+
+ if (-1 == pProcessLocalData->UnixFd)
+ {
+ palError = ERROR_INTERNAL_ERROR;
+ goto ExitFileMappingInitializationRoutine;
+ }
+
+#if ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
+ struct stat st;
+
+ if (0 == fstat(pProcessLocalData->UnixFd, &st))
+ {
+ pProcessLocalData->MappedFileDevNum = st.st_dev;
+ pProcessLocalData->MappedFileInodeNum = st.st_ino;
+ }
+ else
+ {
+ ERROR("Couldn't get inode info for fd=%d to be stored in mapping object\n", pProcessLocalData->UnixFd);
+ }
+#endif
+
+ExitFileMappingInitializationRoutine:
+
+ return palError;
+}
+
+/*++
+Function:
+ CreateFileMappingA
+
+Note:
+ File mapping are used to do inter-process communication.
+
+See MSDN doc.
+--*/
+HANDLE
+PALAPI
+CreateFileMappingA(
+ IN HANDLE hFile,
+ IN LPSECURITY_ATTRIBUTES lpFileMappingAttributes,
+ IN DWORD flProtect,
+ IN DWORD dwMaximumSizeHigh,
+ IN DWORD dwMaximumSizeLow,
+ IN LPCSTR lpName)
+{
+ HANDLE hFileMapping = NULL;
+ CPalThread *pThread = NULL;
+ PAL_ERROR palError = NO_ERROR;
+
+ PERF_ENTRY(CreateFileMappingA);
+ ENTRY("CreateFileMappingA(hFile=%p, lpAttributes=%p, flProtect=%#x, "
+ "dwMaxSizeH=%d, dwMaxSizeL=%d, lpName=%p (%s))\n",
+ hFile, lpFileMappingAttributes, flProtect,
+ dwMaximumSizeHigh, dwMaximumSizeLow,
+ lpName?lpName:"NULL",
+ lpName?lpName:"NULL");
+
+ pThread = InternalGetCurrentThread();
+
+ if (lpName != nullptr)
+ {
+ ASSERT("lpName: Cross-process named objects are not supported in PAL");
+ palError = ERROR_NOT_SUPPORTED;
+ }
+ else
+ {
+ palError = InternalCreateFileMapping(
+ pThread,
+ hFile,
+ lpFileMappingAttributes,
+ flProtect,
+ dwMaximumSizeHigh,
+ dwMaximumSizeLow,
+ NULL,
+ &hFileMapping
+ );
+ }
+
+
+ //
+ // We always need to set last error, even on success:
+ // we need to protect ourselves from the situation
+ // where last error is set to ERROR_ALREADY_EXISTS on
+ // entry to the function
+ //
+
+ pThread->SetLastError(palError);
+
+ LOGEXIT( "CreateFileMappingA returns HANDLE %p. \n", hFileMapping );
+ PERF_EXIT(CreateFileMappingA);
+ return hFileMapping;
+}
+
+/*++
+Function:
+ CreateFileMappingW
+
+Note:
+ File mapping are used to do inter-process communication.
+
+See MSDN doc.
+--*/
+HANDLE
+PALAPI
+CreateFileMappingW(
+ IN HANDLE hFile,
+ IN LPSECURITY_ATTRIBUTES lpFileMappingAttributes,
+ IN DWORD flProtect,
+ IN DWORD dwMaximumSizeHigh,
+ IN DWORD dwMaximumSizeLow,
+ IN LPCWSTR lpName)
+{
+ HANDLE hFileMapping = NULL;
+ CPalThread *pThread = NULL;
+ PAL_ERROR palError = NO_ERROR;
+
+ PERF_ENTRY(CreateFileMappingW);
+ ENTRY("CreateFileMappingW(hFile=%p, lpAttributes=%p, flProtect=%#x, "
+ "dwMaxSizeH=%u, dwMaxSizeL=%u, lpName=%p (%S))\n",
+ hFile, lpFileMappingAttributes, flProtect, dwMaximumSizeHigh,
+ dwMaximumSizeLow, lpName?lpName:W16_NULLSTRING, lpName?lpName:W16_NULLSTRING);
+
+ pThread = InternalGetCurrentThread();
+
+ palError = InternalCreateFileMapping(
+ pThread,
+ hFile,
+ lpFileMappingAttributes,
+ flProtect,
+ dwMaximumSizeHigh,
+ dwMaximumSizeLow,
+ lpName,
+ &hFileMapping
+ );
+
+ //
+ // We always need to set last error, even on success:
+ // we need to protect ourselves from the situation
+ // where last error is set to ERROR_ALREADY_EXISTS on
+ // entry to the function
+ //
+
+ pThread->SetLastError(palError);
+
+ LOGEXIT( "CreateFileMappingW returning %p .\n", hFileMapping );
+ PERF_EXIT(CreateFileMappingW);
+ return hFileMapping;
+}
+
+PAL_ERROR
+CorUnix::InternalCreateFileMapping(
+ CPalThread *pThread,
+ HANDLE hFile,
+ LPSECURITY_ATTRIBUTES lpFileMappingAttributes,
+ DWORD flProtect,
+ DWORD dwMaximumSizeHigh,
+ DWORD dwMaximumSizeLow,
+ LPCWSTR lpName,
+ HANDLE *phMapping
+ )
+{
+ CObjectAttributes objectAttributes(lpName, lpFileMappingAttributes);
+ PAL_ERROR palError = NO_ERROR;
+ IPalObject *pMapping = NULL;
+ IPalObject *pRegisteredMapping = NULL;
+ CFileMappingProcessLocalData *pLocalData = NULL;
+ IDataLock *pLocalDataLock = NULL;
+ CFileMappingImmutableData *pImmutableData = NULL;
+ IPalObject *pFileObject = NULL;
+ CFileProcessLocalData *pFileLocalData = NULL;
+ IDataLock *pFileLocalDataLock = NULL;
+
+ struct stat UnixFileInformation;
+ INT UnixFd = -1;
+ BOOL bPALCreatedTempFile = FALSE;
+ UINT nFileSize = 0;
+
+ //
+ // Validate parameters
+ //
+
+ if (lpName != nullptr)
+ {
+ ASSERT("lpName: Cross-process named objects are not supported in PAL");
+ palError = ERROR_NOT_SUPPORTED;
+ goto ExitInternalCreateFileMapping;
+ }
+
+ if (0 != dwMaximumSizeHigh)
+ {
+ ASSERT("dwMaximumSizeHigh is always 0.\n");
+ palError = ERROR_INVALID_PARAMETER;
+ goto ExitInternalCreateFileMapping;
+ }
+
+ if (PAGE_READWRITE != flProtect
+ && PAGE_READONLY != flProtect
+ && PAGE_WRITECOPY != flProtect)
+ {
+ ASSERT( "invalid flProtect %#x, acceptable values are PAGE_READONLY "
+ "(%#x), PAGE_READWRITE (%#x) and PAGE_WRITECOPY (%#x).\n",
+ flProtect, PAGE_READONLY, PAGE_READWRITE, PAGE_WRITECOPY );
+ palError = ERROR_INVALID_PARAMETER;
+ goto ExitInternalCreateFileMapping;
+ }
+
+ if (hFile == INVALID_HANDLE_VALUE && 0 == dwMaximumSizeLow)
+ {
+ ERROR( "If hFile is INVALID_HANDLE_VALUE, then you must specify a size.\n" );
+ palError = ERROR_INVALID_PARAMETER;
+ goto ExitInternalCreateFileMapping;
+ }
+
+ if (hFile != INVALID_HANDLE_VALUE && NULL != lpName)
+ {
+ ASSERT( "If hFile is not -1, then lpName must be NULL.\n" );
+ palError = ERROR_INVALID_PARAMETER;
+ goto ExitInternalCreateFileMapping;
+ }
+
+ palError = g_pObjectManager->AllocateObject(
+ pThread,
+ &otFileMapping,
+ &objectAttributes,
+ &pMapping
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto ExitInternalCreateFileMapping;
+ }
+
+ palError = pMapping->GetImmutableData(reinterpret_cast<void**>(&pImmutableData));
+ if (NO_ERROR != palError)
+ {
+ goto ExitInternalCreateFileMapping;
+ }
+
+ if (hFile == INVALID_HANDLE_VALUE
+#ifndef CORECLR
+ && NULL == lpName
+#endif // !CORECLR
+ )
+ {
+ //
+ // Note: this path is what prevents us supporting the
+ // duplication of file mapping objects across processes, since
+ // there is no backing file that the other process can open. We can
+ // avoid this restriction by always using a temp backing file for
+ // anonymous mappings.
+ //
+
+ /* Anonymous mapped files. */
+ if (strcpy_s(pImmutableData->szFileName, sizeof(pImmutableData->szFileName), "/dev/zero") != SAFECRT_SUCCESS)
+ {
+ ERROR( "strcpy_s failed!\n" );
+ palError = ERROR_INTERNAL_ERROR;
+ goto ExitInternalCreateFileMapping;
+ }
+
+#if HAVE_MMAP_DEV_ZERO
+
+ UnixFd = InternalOpen(pImmutableData->szFileName, O_RDWR);
+ if ( -1 == UnixFd )
+ {
+ ERROR( "Unable to open the file.\n");
+ palError = ERROR_INTERNAL_ERROR;
+ goto ExitInternalCreateFileMapping;
+ }
+
+#else //!HAVE_MMAP_DEV_ZERO
+
+ UnixFd = -1; /* will pass MAP_ANON to mmap() instead */
+
+#endif //!HAVE_MMAP_DEV_ZERO
+
+ }
+ else
+ {
+ if ( hFile != INVALID_HANDLE_VALUE )
+ {
+ palError = g_pObjectManager->ReferenceObjectByHandle(
+ pThread,
+ hFile,
+ &aotFile,
+ GENERIC_READ,
+ &pFileObject
+ );
+
+ if (NO_ERROR != palError)
+ {
+ ERROR("Unable to obtain file data.\n");
+ palError = ERROR_INVALID_PARAMETER;
+ goto ExitInternalCreateFileMapping;
+ }
+
+ palError = pFileObject->GetProcessLocalData(
+ pThread,
+ ReadLock,
+ &pFileLocalDataLock,
+ reinterpret_cast<void**>(&pFileLocalData)
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto ExitInternalCreateFileMapping;
+ }
+
+ /* We need to check to ensure flProtect jives with
+ the permission on the file handle */
+ if (!MAPIsRequestPermissible(flProtect, pFileLocalData))
+ {
+ ERROR("File handle does not have the correct "
+ "permissions to create mapping\n" );
+ palError = ERROR_ACCESS_DENIED;
+ if (NULL != pFileLocalDataLock)
+ {
+ pFileLocalDataLock->ReleaseLock(pThread, FALSE);
+ }
+ goto ExitInternalCreateFileMapping;
+ }
+
+ //
+ // TODO: technically, the file mapping object should hold
+ // a reference to the passed in file object. This implementation
+ // only keeps the underlying native file structure (i.e., what
+ // the duplicated descriptors point to) open. There may be a risk
+ // here pertaining to the file lock information that the PAL must
+ // maintain (e.g,. if the passed in handle is closed immediately
+ // after the file mapping is opened then the lock information will
+ // be released, since we're not doing anything to keep it alive
+ // here).
+ //
+ // Having a direct reference to the underlying file object adds
+ // some complication, especially in cross-process cases. We may
+ // want to consider adding a reference to the PAL's file lock
+ // information, though...
+ //
+
+ UnixFd = dup(pFileLocalData->unix_fd);
+ if (-1 == UnixFd)
+ {
+ ERROR( "Unable to duplicate the Unix file descriptor!\n" );
+ palError = ERROR_INTERNAL_ERROR;
+ if (NULL != pFileLocalDataLock)
+ {
+ pFileLocalDataLock->ReleaseLock(pThread, FALSE);
+ }
+ goto ExitInternalCreateFileMapping;
+ }
+
+ if (strcpy_s(pImmutableData->szFileName, sizeof(pImmutableData->szFileName), pFileLocalData->unix_filename) != SAFECRT_SUCCESS)
+ {
+ ERROR( "strcpy_s failed!\n" );
+ palError = ERROR_INTERNAL_ERROR;
+ if (NULL != pFileLocalDataLock)
+ {
+ pFileLocalDataLock->ReleaseLock(pThread, FALSE);
+ }
+ goto ExitInternalCreateFileMapping;
+ }
+
+ if (NULL != pFileLocalDataLock)
+ {
+ pFileLocalDataLock->ReleaseLock(pThread, FALSE);
+ }
+ }
+ else
+ {
+#ifndef CORECLR
+ TRACE( "INVALID_HANDLE_VALUE was the hFile, time to try to create a "
+ "temporary file" );
+
+ /* Create a temporary file on the filesystem in order to be
+ shared across processes. */
+ palError = MAPCreateTempFile(pThread, &UnixFd, pImmutableData->szFileName);
+ if (NO_ERROR != palError)
+ {
+ ERROR("Unable to create the temporary file.\n");
+ goto ExitInternalCreateFileMapping;
+ }
+ bPALCreatedTempFile = TRUE;
+#else // !CORECLR
+ ASSERT("should not get here\n");
+ palError = ERROR_INTERNAL_ERROR;
+ goto ExitInternalCreateFileMapping;
+#endif // !CORECLR
+ }
+
+ if (-1 == fstat(UnixFd, &UnixFileInformation))
+ {
+ ASSERT("fstat() failed for this reason %s.\n", strerror(errno));
+ palError = ERROR_INTERNAL_ERROR;
+ goto ExitInternalCreateFileMapping;
+ }
+
+ if ( 0 == UnixFileInformation.st_size &&
+ 0 == dwMaximumSizeHigh && 0 == dwMaximumSizeLow )
+ {
+ ERROR( "The file cannot be a zero length file.\n" );
+ palError = ERROR_FILE_INVALID;
+ goto ExitInternalCreateFileMapping;
+ }
+
+ if ( INVALID_HANDLE_VALUE != hFile &&
+ dwMaximumSizeLow > (DWORD) UnixFileInformation.st_size &&
+ ( PAGE_READONLY == flProtect || PAGE_WRITECOPY == flProtect ) )
+ {
+ /* In this situation, Windows returns an error, because the
+ permissions requested do not allow growing the file */
+ ERROR( "The file cannot be grown do to the map's permissions.\n" );
+ palError = ERROR_NOT_ENOUGH_MEMORY;
+ goto ExitInternalCreateFileMapping;
+ }
+
+ if ( (DWORD) UnixFileInformation.st_size < dwMaximumSizeLow )
+ {
+ TRACE( "Growing the size of file on disk to match requested size.\n" );
+
+ /* Need to grow the file on disk to match size. */
+ palError = MAPGrowLocalFile(UnixFd, dwMaximumSizeLow);
+ if (NO_ERROR != palError)
+ {
+ ERROR( "Unable to grow the file on disk.\n" );
+ goto ExitInternalCreateFileMapping;
+ }
+ }
+ }
+
+ nFileSize = ( 0 == dwMaximumSizeLow && 0 == dwMaximumSizeHigh ) ?
+ UnixFileInformation.st_size : dwMaximumSizeLow;
+
+ pImmutableData->MaxSize = nFileSize;
+ pImmutableData->flProtect = flProtect;
+ pImmutableData->bPALCreatedTempFile = bPALCreatedTempFile;
+ pImmutableData->dwDesiredAccessWhenOpened = MAPConvertProtectToAccess(flProtect);
+
+
+ //
+ // The local data isn't grabbed / modified until here so that we don't
+ // need to worry ourselves with locking issues with the passed in
+ // file handle -- all operations concerning the file handle are completed
+ // before we deal with the lock for the new object.
+ //
+
+ palError = pMapping->GetProcessLocalData(
+ pThread,
+ WriteLock,
+ &pLocalDataLock,
+ reinterpret_cast<void**>(&pLocalData)
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto ExitInternalCreateFileMapping;
+ }
+
+ pLocalData->UnixFd = UnixFd;
+
+#if ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
+ if (-1 == UnixFd)
+ {
+ pLocalData->MappedFileDevNum = (dev_t)-1; /* there is no standard NO_DEV */
+ pLocalData->MappedFileInodeNum = NO_INO;
+ }
+ else
+ {
+ struct stat st;
+
+ if (0 == fstat(UnixFd, &st))
+ {
+ pLocalData->MappedFileDevNum = st.st_dev;
+ pLocalData->MappedFileInodeNum = st.st_ino;
+ }
+ else
+ {
+ ERROR("Couldn't get inode info for fd=%d to be stored in mapping object\n", UnixFd);
+ palError = ERROR_INTERNAL_ERROR;
+ goto ExitInternalCreateFileMapping;
+ }
+ }
+#endif
+
+ pLocalDataLock->ReleaseLock(pThread, TRUE);
+ pLocalDataLock = NULL;
+
+ palError = g_pObjectManager->RegisterObject(
+ pThread,
+ pMapping,
+ &aotFileMapping,
+ flProtect, // TODO: is flProtect really an access right?
+ phMapping,
+ &pRegisteredMapping
+ );
+
+ //
+ // pMapping is invalidated by the call to RegisterObject, so NULL it
+ // out here to ensure that we don't try to release a reference on
+ // it down the line. This also ensures that we won't attempt to release
+ // any data associated with the mapping object here, as if any cleanup is
+ // necessary due to a failure in RegisterObject (which includes another
+ // object by the same name already existing) the cleanup will take place
+ // when that routine releases the reference to pMapping.
+ //
+
+ pMapping = NULL;
+
+ExitInternalCreateFileMapping:
+
+ if (NULL != pLocalDataLock)
+ {
+ pLocalDataLock->ReleaseLock(
+ pThread,
+ TRUE
+ );
+ }
+
+ if (NULL != pMapping)
+ {
+ pMapping->ReleaseReference(pThread);
+
+ if (bPALCreatedTempFile)
+ {
+ unlink(pImmutableData->szFileName);
+ }
+
+ if (-1 != UnixFd)
+ {
+ close(UnixFd);
+ }
+ }
+
+ if (NULL != pRegisteredMapping)
+ {
+ pRegisteredMapping->ReleaseReference(pThread);
+ }
+
+ if (NULL != pFileObject)
+ {
+ pFileObject->ReleaseReference(pThread);
+ }
+
+ return palError;
+}
+
+/*++
+Function:
+ OpenFileMappingA
+
+See MSDN doc.
+--*/
+HANDLE
+PALAPI
+OpenFileMappingA(
+ IN DWORD dwDesiredAccess,
+ IN BOOL bInheritHandle,
+ IN LPCSTR lpName)
+{
+ HANDLE hFileMapping = NULL;
+ CPalThread *pThread = NULL;
+ PAL_ERROR palError = NO_ERROR;
+
+ PERF_ENTRY(OpenFileMappingA);
+ ENTRY("OpenFileMappingA(dwDesiredAccess=%u, bInheritHandle=%d, lpName=%p (%s)\n",
+ dwDesiredAccess, bInheritHandle, lpName?lpName:"NULL", lpName?lpName:"NULL");
+
+ pThread = InternalGetCurrentThread();
+
+ if (lpName == nullptr)
+ {
+ ERROR("name is NULL\n");
+ palError = ERROR_INVALID_PARAMETER;
+ }
+ else
+ {
+ ASSERT("lpName: Cross-process named objects are not supported in PAL");
+ palError = ERROR_NOT_SUPPORTED;
+ }
+
+ if (NO_ERROR != palError)
+ {
+ pThread->SetLastError(palError);
+ }
+ LOGEXIT( "OpenFileMappingA returning %p\n", hFileMapping );
+ PERF_EXIT(OpenFileMappingA);
+ return hFileMapping;
+}
+
+
+/*++
+Function:
+ OpenFileMappingW
+
+See MSDN doc.
+--*/
+HANDLE
+PALAPI
+OpenFileMappingW(
+ IN DWORD dwDesiredAccess,
+ IN BOOL bInheritHandle,
+ IN LPCWSTR lpName)
+{
+ HANDLE hFileMapping = NULL;
+ PAL_ERROR palError = NO_ERROR;
+ CPalThread *pThread = NULL;
+
+ PERF_ENTRY(OpenFileMappingW);
+ ENTRY("OpenFileMappingW(dwDesiredAccess=%#x, bInheritHandle=%d, lpName=%p (%S)\n",
+ dwDesiredAccess, bInheritHandle, lpName?lpName:W16_NULLSTRING, lpName?lpName:W16_NULLSTRING);
+
+ pThread = InternalGetCurrentThread();
+
+ /* validate parameters */
+ if (lpName == nullptr)
+ {
+ ERROR("name is NULL\n");
+ palError = ERROR_INVALID_PARAMETER;
+ }
+ else
+ {
+ ASSERT("lpName: Cross-process named objects are not supported in PAL");
+ palError = ERROR_NOT_SUPPORTED;
+ }
+
+ if (NO_ERROR != palError)
+ {
+ pThread->SetLastError(palError);
+ }
+ LOGEXIT("OpenFileMappingW returning %p.\n", hFileMapping);
+ PERF_EXIT(OpenFileMappingW);
+ return hFileMapping;
+}
+
+PAL_ERROR
+CorUnix::InternalOpenFileMapping(
+ CPalThread *pThread,
+ DWORD dwDesiredAccess,
+ BOOL bInheritHandle,
+ LPCWSTR lpName,
+ HANDLE *phMapping
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ IPalObject *pFileMapping = NULL;
+ CPalString sObjectName(lpName);
+
+ if ( MAPContainsInvalidFlags( dwDesiredAccess ) )
+ {
+ ASSERT( "dwDesiredAccess can be one or more of FILE_MAP_READ, "
+ "FILE_MAP_WRITE, FILE_MAP_COPY or FILE_MAP_ALL_ACCESS.\n" );
+ palError = ERROR_INVALID_PARAMETER;
+ goto ExitInternalOpenFileMapping;
+ }
+
+ palError = g_pObjectManager->LocateObject(
+ pThread,
+ &sObjectName,
+ &aotFileMapping,
+ &pFileMapping
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto ExitInternalOpenFileMapping;
+ }
+
+ palError = g_pObjectManager->ObtainHandleForObject(
+ pThread,
+ pFileMapping,
+ dwDesiredAccess,
+ bInheritHandle,
+ NULL,
+ phMapping
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto ExitInternalOpenFileMapping;
+ }
+
+ExitInternalOpenFileMapping:
+
+ if (NULL != pFileMapping)
+ {
+ pFileMapping->ReleaseReference(pThread);
+ }
+
+ return palError;
+}
+
+/*++
+Function:
+ MapViewOfFile
+
+ Limitations: 1) Currently file mappings are supported only at file
+ offset 0.
+ 2) Some platforms (specifically HP-UX) do not support
+ multiple simultaneous shared mapping of the same file
+ region in the same process. On these platforms, in case
+ we are asked for a new view completely contained in an
+ existing one, we return an address within the existing
+ mapping. In case the new requested view is overlapping
+ with the existing one, but not contained in it, the
+ mapping is impossible, and MapViewOfFile will fail.
+ Since currently the mappings are supported only at file
+ offset 0, MapViewOfFile will succeed if the new view
+ is equal or smaller of the existing one, and the address
+ returned will be the same address of the existing
+ mapping.
+ Since the underlying mapping is always the same, all
+ the shared views of the same file region will share the
+ same protection, i.e. they will have the largest
+ protection requested. If any mapping asked for a
+ read-write access, all the read-only mappings of the
+ same region will silently get a read-write access to
+ it.
+
+See MSDN doc.
+--*/
+LPVOID
+PALAPI
+MapViewOfFile(
+ IN HANDLE hFileMappingObject,
+ IN DWORD dwDesiredAccess,
+ IN DWORD dwFileOffsetHigh,
+ IN DWORD dwFileOffsetLow,
+ IN SIZE_T dwNumberOfBytesToMap)
+{
+ PAL_ERROR palError = NO_ERROR;
+ CPalThread *pThread = NULL;
+ LPVOID pvMappedBaseAddress = NULL;
+
+ PERF_ENTRY(MapViewOfFile);
+ ENTRY("MapViewOfFile(hFileMapping=%p, dwDesiredAccess=%u, "
+ "dwFileOffsetH=%u, dwFileOffsetL=%u, dwNumberOfBytes=%u)\n",
+ hFileMappingObject, dwDesiredAccess, dwFileOffsetHigh,
+ dwFileOffsetLow, dwNumberOfBytesToMap);
+
+ pThread = InternalGetCurrentThread();
+
+ palError = InternalMapViewOfFile(
+ pThread,
+ hFileMappingObject,
+ dwDesiredAccess,
+ dwFileOffsetHigh,
+ dwFileOffsetLow,
+ dwNumberOfBytesToMap,
+ &pvMappedBaseAddress
+ );
+
+ if (NO_ERROR != palError)
+ {
+ pThread->SetLastError(palError);
+ }
+
+ LOGEXIT( "MapViewOfFile returning %p.\n", pvMappedBaseAddress );
+ PERF_EXIT(MapViewOfFile);
+ return pvMappedBaseAddress;
+}
+
+LPVOID
+PALAPI
+MapViewOfFileEx(
+ IN HANDLE hFileMappingObject,
+ IN DWORD dwDesiredAccess,
+ IN DWORD dwFileOffsetHigh,
+ IN DWORD dwFileOffsetLow,
+ IN SIZE_T dwNumberOfBytesToMap,
+ IN LPVOID lpBaseAddress)
+{
+ PAL_ERROR palError = NO_ERROR;
+ CPalThread *pThread = NULL;
+ LPVOID pvMappedBaseAddress = NULL;
+
+ PERF_ENTRY(MapViewOfFileEx);
+ ENTRY("MapViewOfFileEx(hFileMapping=%p, dwDesiredAccess=%u, "
+ "dwFileOffsetH=%u, dwFileOffsetL=%u, dwNumberOfBytes=%u, lpBaseAddress=%p)\n",
+ hFileMappingObject, dwDesiredAccess, dwFileOffsetHigh,
+ dwFileOffsetLow, dwNumberOfBytesToMap, lpBaseAddress);
+
+ pThread = InternalGetCurrentThread();
+
+ if (lpBaseAddress == NULL)
+ {
+ palError = InternalMapViewOfFile(
+ pThread,
+ hFileMappingObject,
+ dwDesiredAccess,
+ dwFileOffsetHigh,
+ dwFileOffsetLow,
+ dwNumberOfBytesToMap,
+ &pvMappedBaseAddress
+ );
+
+ if (NO_ERROR != palError)
+ {
+ pThread->SetLastError(palError);
+ }
+ }
+ else
+ {
+ // TODO: Figure out if we can support mapping at a specific address on Linux.
+ pThread->SetLastError(ERROR_INVALID_PARAMETER);
+ }
+
+ LOGEXIT( "MapViewOfFileEx returning %p.\n", pvMappedBaseAddress );
+ PERF_EXIT(MapViewOfFileEx);
+ return pvMappedBaseAddress;
+}
+
+/*++
+Function:
+ FlushViewOfFile
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+FlushViewOfFile(
+ IN LPVOID lpBaseAddress,
+ IN SIZE_T dwNumberOfBytesToFlush)
+{
+ PAL_ERROR palError = NO_ERROR;
+ CPalThread *pThread = NULL;
+ PMAPPED_VIEW_LIST pView = NULL;
+ BOOL fResult = TRUE;
+
+ PERF_ENTRY(FlushViewOfFile);
+ ENTRY("FlushViewOfFile(lpBaseAddress=%p, dwNumberOfBytesToFlush=%u)\n",
+ lpBaseAddress, dwNumberOfBytesToFlush);
+
+ pThread = InternalGetCurrentThread();
+
+ InternalEnterCriticalSection(pThread, &mapping_critsec);
+
+ pView = MAPGetViewForAddress(lpBaseAddress);
+ if (NULL == pView)
+ {
+ ERROR("lpBaseAddress has to be the address returned by MapViewOfFile[Ex]");
+ palError = ERROR_INVALID_HANDLE;
+ goto Exit;
+ }
+
+ if (dwNumberOfBytesToFlush == 0)
+ {
+ dwNumberOfBytesToFlush = pView->NumberOfBytesToMap;
+ }
+
+ // <ROTORTODO>we should only use MS_SYNC if the file has been opened
+ // with FILE_FLAG_WRITE_THROUGH
+ if (msync(lpBaseAddress, dwNumberOfBytesToFlush, MS_SYNC) == -1)
+ {
+ if (errno == EINVAL)
+ {
+ WARN("msync failed; %s\n", strerror(errno));
+ palError = ERROR_INVALID_PARAMETER;
+ }
+ else if (errno == EIO)
+ {
+ WARN("msync failed; %s\n", strerror(errno));
+ palError = ERROR_WRITE_FAULT;
+ }
+ else
+ {
+ ERROR("msync failed; %s\n", strerror(errno));
+ palError = ERROR_INTERNAL_ERROR;
+ }
+ }
+
+Exit:
+ InternalLeaveCriticalSection(pThread, &mapping_critsec);
+
+ if (NO_ERROR != palError)
+ {
+ fResult = FALSE;
+ pThread->SetLastError(palError);
+ }
+
+ LOGEXIT("FlushViewOfFile returning %d.\n", fResult);
+ PERF_EXIT(FlushViewOfFile);
+ return fResult;
+}
+
+
+/*++
+Function:
+ UnmapViewOfFile
+
+See MSDN doc.
+--*/
+BOOL
+PALAPI
+UnmapViewOfFile(
+ IN LPCVOID lpBaseAddress)
+{
+ PAL_ERROR palError;
+ CPalThread *pThread;
+
+ PERF_ENTRY(UnmapViewOfFile);
+ ENTRY("UnmapViewOfFile(lpBaseAddress=%p)\n", lpBaseAddress);
+
+ pThread = InternalGetCurrentThread();
+
+ palError = InternalUnmapViewOfFile(pThread, lpBaseAddress);
+
+ if (NO_ERROR != palError)
+ {
+ pThread->SetLastError(palError);
+ }
+
+ LOGEXIT( "UnmapViewOfFile returning %s.\n", (NO_ERROR == palError) ? "TRUE" : "FALSE" );
+ PERF_EXIT(UnmapViewOfFile);
+ return (NO_ERROR == palError);
+}
+
+PAL_ERROR
+CorUnix::InternalMapViewOfFile(
+ CPalThread *pThread,
+ HANDLE hFileMappingObject,
+ DWORD dwDesiredAccess,
+ DWORD dwFileOffsetHigh,
+ DWORD dwFileOffsetLow,
+ SIZE_T dwNumberOfBytesToMap,
+ LPVOID *ppvBaseAddress
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ IPalObject *pMappingObject = NULL;
+ CFileMappingImmutableData *pImmutableData = NULL;
+ CFileMappingProcessLocalData *pProcessLocalData = NULL;
+ IDataLock *pProcessLocalDataLock = NULL;
+#if ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
+ PMAPPED_VIEW_LIST pReusedMapping = NULL;
+#endif
+ LPVOID pvBaseAddress = NULL;
+
+ /* Sanity checks */
+ if ( MAPContainsInvalidFlags( dwDesiredAccess ) )
+ {
+ ASSERT( "dwDesiredAccess can be one of FILE_MAP_WRITE, FILE_MAP_READ,"
+ " FILE_MAP_COPY or FILE_MAP_ALL_ACCESS.\n" );
+ palError = ERROR_INVALID_PARAMETER;
+ goto InternalMapViewOfFileExit;
+ }
+
+ if ( 0 != dwFileOffsetHigh || 0 != dwFileOffsetLow )
+ {
+ ASSERT( "dwFileOffsetHigh and dwFileOffsetLow are always 0.\n" );
+ palError = ERROR_INVALID_PARAMETER;
+ goto InternalMapViewOfFileExit;
+ }
+
+ palError = g_pObjectManager->ReferenceObjectByHandle(
+ pThread,
+ hFileMappingObject,
+ &aotFileMapping,
+ dwDesiredAccess,
+ &pMappingObject
+ );
+
+ if (NO_ERROR != palError)
+ {
+ ERROR( "Unable to reference handle %p.\n",hFileMappingObject );
+ goto InternalMapViewOfFileExit;
+ }
+
+ palError = pMappingObject->GetImmutableData(
+ reinterpret_cast<void**>(&pImmutableData)
+ );
+
+ if (NO_ERROR != palError)
+ {
+ ERROR( "Unable to obtain object immutable data");
+ goto InternalMapViewOfFileExit;
+ }
+
+ palError = pMappingObject->GetProcessLocalData(
+ pThread,
+ ReadLock,
+ &pProcessLocalDataLock,
+ reinterpret_cast<void**>(&pProcessLocalData)
+ );
+
+ if (NO_ERROR != palError)
+ {
+ ERROR( "Unable to obtain object process local data");
+ goto InternalMapViewOfFileExit;
+ }
+
+ /* If dwNumberOfBytesToMap is 0, we need to map the entire file.
+ * mmap doesn't do the same thing as Windows in that case, though,
+ * so we use the file size instead. */
+ if (0 == dwNumberOfBytesToMap)
+ {
+ dwNumberOfBytesToMap = pImmutableData->MaxSize;
+ }
+
+ palError = MAPDesiredAccessAllowed(
+ pImmutableData->flProtect,
+ dwDesiredAccess,
+ pImmutableData->dwDesiredAccessWhenOpened
+ );
+
+ if (NO_ERROR != palError)
+ {
+ goto InternalMapViewOfFileExit;
+ }
+
+ InternalEnterCriticalSection(pThread, &mapping_critsec);
+
+ if (FILE_MAP_COPY == dwDesiredAccess)
+ {
+ int flags = MAP_PRIVATE;
+
+#if !HAVE_MMAP_DEV_ZERO
+ if (pProcessLocalData->UnixFd == -1)
+ {
+ flags |= MAP_ANON;
+ }
+#endif
+ pvBaseAddress = mmap(
+ NULL,
+ dwNumberOfBytesToMap,
+ PROT_READ|PROT_WRITE,
+ flags,
+ pProcessLocalData->UnixFd,
+ 0
+ );
+ }
+ else
+ {
+ INT prot = MAPFileMapToMmapFlags(dwDesiredAccess);
+ if (prot != -1)
+ {
+ int flags = MAP_SHARED;
+
+#if !HAVE_MMAP_DEV_ZERO
+ if (pProcessLocalData->UnixFd == -1)
+ {
+ flags |= MAP_ANON;
+ }
+#endif
+
+ pvBaseAddress = mmap(
+ NULL,
+ dwNumberOfBytesToMap,
+ prot,
+ flags,
+ pProcessLocalData->UnixFd,
+ 0
+ );
+
+#if ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
+ if ((MAP_FAILED == pvBaseAddress) && (ENOMEM == errno))
+ {
+ /* Search in list of MAPPED_MEMORY_INFO for a shared mapping
+ with the same inode number
+ */
+ TRACE("Mmap() failed with errno=ENOMEM probably for multiple mapping "
+ "limitation. Searching for a replacement among existing mappings\n");
+
+ pReusedMapping = FindSharedMappingReplacement(
+ pThread,
+ pProcessLocalData->MappedFileDevNum,
+ pProcessLocalData->MappedFileInodeNum,
+ dwNumberOfBytesToMap,
+ 0
+ );
+
+ if (pReusedMapping)
+ {
+ int ret;
+
+ TRACE("Mapping @ %p {sz=%d offs=%d} fully "
+ "contains the requested one {sz=%d offs=%d}: reusing it\n",
+ pReusedMapping->pNMHolder->address,
+ (int)pReusedMapping->pNMHolder->size,
+ (int)pReusedMapping->pNMHolder->offset,
+ dwNumberOfBytesToMap, 0);
+
+ /* Let's check the mapping's current protection */
+ ret = mprotect(pReusedMapping->pNMHolder->address,
+ pReusedMapping->pNMHolder->size,
+ prot | PROT_CHECK);
+ if (0 != ret)
+ {
+ /* We need to raise the protection to the desired
+ one. That will give write access to any read-only
+ mapping sharing this native mapping, but there is
+ no way around this problem on systems that do not
+ allow more than one mapping per file region, per
+ process */
+ TRACE("Raising protections on mapping @ %p to 0x%x\n",
+ pReusedMapping->pNMHolder->address, prot);
+ ret = mprotect(pReusedMapping->pNMHolder->address,
+ pReusedMapping->pNMHolder->size,
+ prot);
+ }
+
+ if (ret != 0)
+ {
+ ERROR( "Failed setting protections on reused mapping\n");
+
+ NativeMapHolderRelease(pThread, pReusedMapping->pNMHolder);
+ free(pReusedMapping);
+ pReusedMapping = NULL;
+ }
+ }
+ }
+#endif // ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
+ }
+ else
+ {
+ ASSERT( "MapFileMapToMmapFlags failed!\n" );
+ palError = ERROR_INTERNAL_ERROR;
+ goto InternalMapViewOfFileLeaveCriticalSection;
+ }
+ }
+
+ if (MAP_FAILED == pvBaseAddress
+#if ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
+ && (pReusedMapping == NULL)
+#endif // ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
+ )
+ {
+ ERROR( "mmap failed with code %s.\n", strerror( errno ) );
+ palError = ERROR_NOT_ENOUGH_MEMORY;
+ goto InternalMapViewOfFileLeaveCriticalSection;
+
+ }
+
+#if ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
+ if (pReusedMapping != NULL)
+ {
+ //
+ // Add a reference to the file mapping object the reused mapping
+ // points to (note that it may be different than the object this
+ // call was actually made against) and add the view to the global
+ // list. All other initialization took place in
+ // FindSharedMappingReplacement
+ //
+
+ pvBaseAddress = pReusedMapping->lpAddress;
+ pReusedMapping->pFileMapping->AddReference();
+ InsertTailList(&MappedViewList, &pReusedMapping->Link);
+ }
+ else
+#endif // ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
+ {
+ //
+ // Allocate and fill out a new view structure, and add it to
+ // the global list.
+ //
+
+ PMAPPED_VIEW_LIST pNewView = (PMAPPED_VIEW_LIST)InternalMalloc(sizeof(*pNewView));
+ if (NULL != pNewView)
+ {
+ pNewView->lpAddress = pvBaseAddress;
+ pNewView->NumberOfBytesToMap = dwNumberOfBytesToMap;
+ pNewView->dwDesiredAccess = dwDesiredAccess;
+ pNewView->pFileMapping = pMappingObject;
+ pNewView->pFileMapping->AddReference();
+ pNewView->lpPEBaseAddress = 0;
+ InsertTailList(&MappedViewList, &pNewView->Link);
+
+#if ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
+ pNewView->MappedFileDevNum = pProcessLocalData->MappedFileDevNum;
+ pNewView->MappedFileInodeNum = pProcessLocalData->MappedFileInodeNum;
+
+ pNewView->pNMHolder = NewNativeMapHolder(
+ pThread,
+ pvBaseAddress,
+ dwNumberOfBytesToMap,
+ 0,
+ 1
+ );
+
+ if (NULL == pNewView->pNMHolder)
+ {
+ pNewView->pFileMapping->ReleaseReference(pThread);
+ RemoveEntryList(&pNewView->Link);
+ free(pNewView);
+ palError = ERROR_INTERNAL_ERROR;
+ }
+#endif // ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
+ }
+ else
+ {
+ palError = ERROR_INTERNAL_ERROR;
+ }
+
+ if (NO_ERROR != palError)
+ {
+ if (-1 == munmap(pvBaseAddress, dwNumberOfBytesToMap))
+ {
+ ERROR("Unable to unmap the file. Expect trouble.\n");
+ goto InternalMapViewOfFileLeaveCriticalSection;
+ }
+ }
+ }
+
+ if (NO_ERROR == palError)
+ {
+ TRACE( "Added %p to the list.\n", pvBaseAddress );
+ *ppvBaseAddress = pvBaseAddress;
+ }
+
+InternalMapViewOfFileLeaveCriticalSection:
+
+ InternalLeaveCriticalSection(pThread, &mapping_critsec);
+
+InternalMapViewOfFileExit:
+
+ if (NULL != pProcessLocalDataLock)
+ {
+ pProcessLocalDataLock->ReleaseLock(pThread, FALSE);
+ }
+
+ if (NULL != pMappingObject)
+ {
+ pMappingObject->ReleaseReference(pThread);
+ }
+
+ return palError;
+}
+
+
+PAL_ERROR
+CorUnix::InternalUnmapViewOfFile(
+ CPalThread *pThread,
+ LPCVOID lpBaseAddress
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ PMAPPED_VIEW_LIST pView = NULL;
+ IPalObject *pMappingObject = NULL;
+
+ InternalEnterCriticalSection(pThread, &mapping_critsec);
+
+ pView = MAPGetViewForAddress(lpBaseAddress);
+ if (NULL == pView)
+ {
+ ERROR("lpBaseAddress has to be the address returned by MapViewOfFile[Ex]");
+ palError = ERROR_INVALID_HANDLE;
+ goto InternalUnmapViewOfFileExit;
+ }
+
+#if ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
+ NativeMapHolderRelease(pThread, pView->pNMHolder);
+ pView->pNMHolder = NULL;
+#else
+ if (-1 == munmap((LPVOID)lpBaseAddress, pView->NumberOfBytesToMap))
+ {
+ ASSERT( "Unable to unmap the memory. Error=%s.\n",
+ strerror( errno ) );
+ palError = ERROR_INTERNAL_ERROR;
+
+ //
+ // Even if the unmap fails we want to continue removing the
+ // info for this view
+ //
+ }
+#endif
+
+ RemoveEntryList(&pView->Link);
+ pMappingObject = pView->pFileMapping;
+ free(pView);
+
+InternalUnmapViewOfFileExit:
+
+ InternalLeaveCriticalSection(pThread, &mapping_critsec);
+
+ //
+ // We can't dereference the file mapping object until after
+ // we've released the mapping critical section, since it may
+ // start going down its cleanup path and we don't want to make
+ // any assumptions as to what locks that might grab...
+ //
+
+ if (NULL != pMappingObject)
+ {
+ pMappingObject->ReleaseReference(pThread);
+ }
+
+ return palError;
+}
+
+/*++
+Function :
+ MAPInitialize
+
+ Initialize the critical sections.
+
+Return value:
+ TRUE if initialization succeeded
+ FALSE otherwise
+--*/
+BOOL
+MAPInitialize( void )
+{
+ TRACE( "Initialising the critical section.\n" );
+
+ InternalInitializeCriticalSection(&mapping_critsec);
+
+ InitializeListHead(&MappedViewList);
+
+ return TRUE;
+}
+
+/*++
+Function :
+ MAPCleanup
+
+ Deletes the critical sections. And all other necessary cleanup.
+
+Note:
+ This function is called after the handle manager is stopped. So
+ there shouldn't be any call that will cause an access to the handle
+ manager.
+
+--*/
+void MAPCleanup( void )
+{
+ TRACE( "Deleting the critical section.\n" );
+ InternalDeleteCriticalSection(&mapping_critsec);
+}
+
+/*++
+Function :
+ MAPGetViewForAddress
+
+ Returns the mapped view (if any) that is based at the passed in address.
+
+ Callers to this function must hold mapping_critsec
+--*/
+static PMAPPED_VIEW_LIST MAPGetViewForAddress( LPCVOID lpAddress )
+{
+ if ( NULL == lpAddress )
+ {
+ ERROR( "lpAddress cannot be NULL\n" );
+ return NULL;
+ }
+
+ for(LIST_ENTRY *pLink = MappedViewList.Flink;
+ pLink != &MappedViewList;
+ pLink = pLink->Flink)
+ {
+ PMAPPED_VIEW_LIST pView = CONTAINING_RECORD(pLink, MAPPED_VIEW_LIST, Link);
+
+ if (pView->lpAddress == lpAddress)
+ {
+ return pView;
+ }
+ }
+
+ WARN( "No match found.\n" );
+
+ return NULL;
+}
+
+/*++
+Function :
+
+ MAPDesiredAccessAllowed
+
+ Determines if desired access is allowed based on the protection state.
+
+ if dwDesiredAccess conflicts with flProtect then the error is
+ ERROR_INVALID_PARAMETER, if the dwDesiredAccess conflicts with
+ dwDesiredAccessWhenOpened, then the error code is ERROR_ACCESS_DENIED
+--*/
+static PAL_ERROR MAPDesiredAccessAllowed( DWORD flProtect,
+ DWORD dwUserDesiredAccess,
+ DWORD dwDesiredAccessWhenOpened )
+{
+ TRACE( "flProtect=%d, dwUserDesiredAccess=%d, dwDesiredAccessWhenOpened=%d\n",
+ flProtect, dwUserDesiredAccess, dwDesiredAccessWhenOpened );
+
+ /* check flProtect parameters*/
+ if ( FILE_MAP_READ!= dwUserDesiredAccess && PAGE_READONLY == flProtect )
+ {
+ ERROR( "map object is read-only, can't map a view with write access\n");
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ if ( FILE_MAP_WRITE == dwUserDesiredAccess && PAGE_READWRITE != flProtect )
+ {
+ ERROR( "map object not open read-write, can't map a view with write "
+ "access.\n" );
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ if ( FILE_MAP_COPY == dwUserDesiredAccess && PAGE_WRITECOPY != flProtect )
+ {
+ ERROR( "map object not open for copy-on-write, can't map copy-on-write "
+ "view.\n" );
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ /* Check to see we don't confict with the desired access we
+ opened the mapping object with. */
+ if ( ( dwUserDesiredAccess == FILE_MAP_READ ) &&
+ !( ( dwDesiredAccessWhenOpened == FILE_MAP_READ ) ||
+ ( dwDesiredAccessWhenOpened == FILE_MAP_ALL_ACCESS ) ) )
+ {
+ ERROR( "dwDesiredAccess conflict : read access requested, object not "
+ "opened with read access.\n" );
+ return ERROR_ACCESS_DENIED;
+ }
+ if ( ( dwUserDesiredAccess & FILE_MAP_WRITE ) &&
+ !( ( dwDesiredAccessWhenOpened == FILE_MAP_WRITE ) ||
+ ( dwDesiredAccessWhenOpened == FILE_MAP_ALL_ACCESS ) ) )
+ {
+ ERROR( "dwDesiredAccess conflict : write access requested, object not "
+ "opened with write access.\n" );
+ return ERROR_ACCESS_DENIED;
+ }
+ if ( ( dwUserDesiredAccess == FILE_MAP_COPY ) &&
+ !( dwDesiredAccessWhenOpened == FILE_MAP_COPY ) )
+ {
+ ERROR( "dwDesiredAccess conflict : copy-on-write access requested, "
+ "object not opened with copy-on-write access.\n" );
+ return ERROR_ACCESS_DENIED;
+ }
+
+ return NO_ERROR;
+}
+
+/*++
+Function :
+ MAPConvertProtectToAccess
+
+ Converts the PAGE_READONLY type flags to FILE_MAP_READ flags.
+
+--*/
+static DWORD MAPConvertProtectToAccess( DWORD flProtect )
+{
+ if ( PAGE_READONLY == flProtect )
+ {
+ return FILE_MAP_READ;
+ }
+ if ( PAGE_READWRITE == flProtect )
+ {
+ return FILE_MAP_ALL_ACCESS;
+ }
+ if ( PAGE_WRITECOPY == flProtect )
+ {
+ return FILE_MAP_COPY;
+ }
+
+ ASSERT( "Unknown flag for flProtect. This line "
+ "should not have been executed.\n " );
+ return (DWORD) -1;
+}
+
+/*++
+Function :
+ MAPConvertAccessToProtect
+
+ Converts the FILE_MAP_READ type flags to PAGE_READONLY flags.
+ Currently, this function only deals with the access flags recognized as valid
+ by MAPContainsInvalidFlags().
+
+--*/
+static DWORD MAPConvertAccessToProtect(DWORD flAccess)
+{
+ if (flAccess == FILE_MAP_ALL_ACCESS)
+ {
+ return PAGE_READWRITE;
+ }
+ else if ((flAccess == FILE_MAP_COPY) || (flAccess == FILE_MAP_WRITE))
+ {
+ return PAGE_WRITECOPY;
+ }
+ else if (flAccess == FILE_MAP_READ)
+ {
+ return PAGE_READONLY;
+ }
+ else if (flAccess == 0)
+ {
+ return PAGE_NOACCESS;
+ }
+
+ ASSERT("Unknown flag for flAccess.\n");
+ return (DWORD) -1;
+}
+
+/*++
+Function :
+ MAPFileMapToMmapFlags
+
+ Converts the mapping flags to unix protection flags.
+--*/
+static INT MAPFileMapToMmapFlags( DWORD flags )
+{
+ if ( FILE_MAP_READ == flags )
+ {
+ TRACE( "FILE_MAP_READ\n" );
+ return PROT_READ;
+ }
+ else if ( FILE_MAP_WRITE == flags )
+ {
+ TRACE( "FILE_MAP_WRITE\n" );
+ /* The limitation of x86 archetecture
+ means you cant have writable but not readable
+ page. In Windows maps of FILE_MAP_WRITE can still be
+ read from. */
+ return PROT_WRITE | PROT_READ;
+ }
+ else if ( (FILE_MAP_READ|FILE_MAP_WRITE) == flags )
+ {
+ TRACE( "FILE_MAP_READ|FILE_MAP_WRITE\n" );
+ return PROT_READ | PROT_WRITE;
+ }
+ else if( FILE_MAP_COPY == flags)
+ {
+ TRACE( "FILE_MAP_COPY\n");
+ return PROT_READ | PROT_WRITE;
+ }
+
+ ASSERT( "Unknown flag. This line should not have been executed.\n" );
+ return -1;
+}
+
+/*++
+Function :
+ MAPMmapProtToAccessFlags
+
+ Converts unix protection flags to file access flags.
+ We ignore PROT_EXEC.
+--*/
+static DWORD MAPMmapProtToAccessFlags( int prot )
+{
+ DWORD flAccess = 0; // default: no access
+
+ if (PROT_NONE == prot)
+ {
+ flAccess = 0;
+ }
+ else if ( ((PROT_READ | PROT_WRITE) & prot) == (PROT_READ | PROT_WRITE) )
+ {
+ flAccess = FILE_MAP_ALL_ACCESS;
+ }
+ else if ( (PROT_WRITE & prot) == PROT_WRITE )
+ {
+ flAccess = FILE_MAP_WRITE;
+ }
+ else if ( (PROT_READ & prot) == PROT_READ )
+ {
+ flAccess = FILE_MAP_READ;
+ }
+ else
+ {
+ ASSERT( "Unknown Unix protection flag\n" );
+ }
+
+ return flAccess;
+}
+
+/*++
+Function :
+
+ MAPGrowLocalFile
+
+ Grows the file on disk to match the specified size.
+
+--*/
+static PAL_ERROR MAPGrowLocalFile( INT UnixFD, UINT NewSize )
+{
+ PAL_ERROR palError = NO_ERROR;
+ INT TruncateRetVal = -1;
+ struct stat FileInfo;
+ TRACE( "Entered MapGrowLocalFile (UnixFD=%d,NewSize%d)\n", UnixFD, NewSize );
+
+ //
+ // TODO: can we add configure flags to model the behavior of ftruncate
+ // among our various target platforms? How much would that actually gain
+ // us?
+ //
+
+ /* ftruncate is a standard function, but the behavior of enlarging files is
+ non-standard. So I will try to enlarge a file, and if that fails try the
+ less efficent way.*/
+ TruncateRetVal = ftruncate( UnixFD, NewSize );
+ fstat( UnixFD, &FileInfo );
+
+ if ( TruncateRetVal != 0 || FileInfo.st_size != (int) NewSize )
+ {
+ INT OrigSize;
+ CONST UINT BUFFER_SIZE = 128;
+ BYTE buf[BUFFER_SIZE];
+ UINT x = 0;
+ UINT CurrentPosition = 0;
+
+ TRACE( "Trying the less efficent way.\n" );
+
+ CurrentPosition = lseek( UnixFD, 0, SEEK_CUR );
+ OrigSize = lseek( UnixFD, 0, SEEK_END );
+ if ( OrigSize == -1 )
+ {
+ ERROR( "Unable to locate the EOF marker. Reason=%s\n",
+ strerror( errno ) );
+ palError = ERROR_INTERNAL_ERROR;
+ goto done;
+ }
+
+ if (NewSize <= (UINT) OrigSize)
+ {
+ return TRUE;
+ }
+
+ memset( buf, 0, BUFFER_SIZE );
+
+ for ( x = 0; x < NewSize - OrigSize - BUFFER_SIZE; x += BUFFER_SIZE )
+ {
+ if ( write( UnixFD, (LPVOID)buf, BUFFER_SIZE ) == -1 )
+ {
+ ERROR( "Unable to grow the file. Reason=%s\n", strerror( errno ) );
+ if((errno == ENOSPC) || (errno == EDQUOT))
+ {
+ palError = ERROR_DISK_FULL;
+ }
+ else
+ {
+ palError = ERROR_INTERNAL_ERROR;
+ }
+ goto done;
+ }
+ }
+ /* Catch any left overs. */
+ if ( x != NewSize )
+ {
+ if ( write( UnixFD, (LPVOID)buf, NewSize - OrigSize - x) == -1 )
+ {
+ ERROR( "Unable to grow the file. Reason=%s\n", strerror( errno ) );
+ if((errno == ENOSPC) || (errno == EDQUOT))
+ {
+ palError = ERROR_DISK_FULL;
+ }
+ else
+ {
+ palError = ERROR_INTERNAL_ERROR;
+ }
+ goto done;
+ }
+ }
+
+ /* restore the file pointer position */
+ lseek( UnixFD, CurrentPosition, SEEK_SET );
+ }
+
+done:
+ return palError;
+}
+
+/*++
+Function :
+ MAPContainsInvalidFlags
+
+ Checks that only valid flags are in the parameter.
+
+--*/
+static BOOL MAPContainsInvalidFlags( DWORD flags )
+{
+
+ if ( (flags == FILE_MAP_READ) ||
+ (flags == FILE_MAP_WRITE) ||
+ (flags == FILE_MAP_ALL_ACCESS) ||
+ (flags == FILE_MAP_COPY) )
+ {
+ return FALSE;
+ }
+ else
+ {
+ return TRUE;
+ }
+}
+
+/*++
+Function :
+ MAPProtectionToFileOpenFlags
+
+ Converts the PAGE_* flags to the O_* flags.
+
+ Returns the file open flags.
+--*/
+static INT MAPProtectionToFileOpenFlags( DWORD flProtect )
+{
+ INT retVal = 0;
+ switch(flProtect)
+ {
+ case PAGE_READONLY:
+ retVal = O_RDONLY;
+ break;
+ case PAGE_READWRITE:
+ retVal = O_RDWR;
+ break;
+ case PAGE_WRITECOPY:
+ retVal = O_RDONLY;
+ break;
+ default:
+ ASSERT("unexpected flProtect value %#x\n", flProtect);
+ retVal = 0;
+ break;
+ }
+ return retVal;
+}
+
+/*++
+Function :
+
+ MAPIsRequestPermissible
+
+ DWORD flProtect - The requested file mapping protection .
+ file * pFileStruct - The file structure containing all the information.
+
+--*/
+static BOOL MAPIsRequestPermissible( DWORD flProtect, CFileProcessLocalData * pFileLocalData )
+{
+ if ( ( (flProtect == PAGE_READONLY || flProtect == PAGE_WRITECOPY) &&
+ (pFileLocalData->open_flags_deviceaccessonly == TRUE ||
+ pFileLocalData->open_flags & O_WRONLY) )
+ )
+ {
+ /*
+ * PAGE_READONLY or PAGE_WRITECOPY access to a file must at least be
+ * readable. Contrary to what MSDN says, PAGE_WRITECOPY
+ * only needs to be readable.
+ */
+ return FALSE;
+ }
+ else if ( flProtect == PAGE_READWRITE && !(pFileLocalData->open_flags & O_RDWR) )
+ {
+ /*
+ * PAGE_READWRITE access to a file needs to be readable and writable
+ */
+ return FALSE;
+ }
+ else
+ {
+ /* Action is permissible */
+ return TRUE;
+ }
+}
+
+// returns TRUE if we have information about the specified address
+BOOL MAPGetRegionInfo(LPVOID lpAddress,
+ PMEMORY_BASIC_INFORMATION lpBuffer)
+{
+ BOOL fFound = FALSE;
+ CPalThread * pThread = InternalGetCurrentThread();
+
+ InternalEnterCriticalSection(pThread, &mapping_critsec);
+
+ for(LIST_ENTRY *pLink = MappedViewList.Flink;
+ pLink != &MappedViewList;
+ pLink = pLink->Flink)
+ {
+ UINT MappedSize;
+ VOID * real_map_addr;
+ SIZE_T real_map_sz;
+ PMAPPED_VIEW_LIST pView = CONTAINING_RECORD(pLink, MAPPED_VIEW_LIST, Link);
+
+#if ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
+ real_map_addr = pView->pNMHolder->address;
+ real_map_sz = pView->pNMHolder->size;
+#else
+ real_map_addr = pView->lpAddress;
+ real_map_sz = pView->NumberOfBytesToMap;
+#endif
+
+ MappedSize = ((real_map_sz-1) & ~VIRTUAL_PAGE_MASK) + VIRTUAL_PAGE_SIZE;
+ if ( real_map_addr <= lpAddress &&
+ (VOID *)((UINT_PTR)real_map_addr+MappedSize) > lpAddress )
+ {
+ if (lpBuffer)
+ {
+ SIZE_T regionSize = MappedSize + (UINT_PTR) real_map_addr -
+ ((UINT_PTR) lpAddress & ~VIRTUAL_PAGE_MASK);
+
+ lpBuffer->BaseAddress = lpAddress;
+ lpBuffer->AllocationProtect = 0;
+ lpBuffer->RegionSize = regionSize;
+ lpBuffer->State = MEM_COMMIT;
+ lpBuffer->Protect = MAPConvertAccessToProtect(pView->dwDesiredAccess);
+ lpBuffer->Type = MEM_MAPPED;
+ }
+
+ fFound = TRUE;
+ break;
+ }
+ }
+
+ InternalLeaveCriticalSection(pThread, &mapping_critsec);
+
+ return fFound;
+}
+
+#if ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
+
+//
+// Callers of FindSharedMappingReplacement must hold mapping_critsec
+//
+
+static PMAPPED_VIEW_LIST FindSharedMappingReplacement(
+ CPalThread *pThread,
+ dev_t deviceNum,
+ ino_t inodeNum,
+ SIZE_T size,
+ SIZE_T offset)
+{
+ PMAPPED_VIEW_LIST pNewView = NULL;
+
+ if (size == 0)
+ {
+ ERROR("Mapping size cannot be NULL\n");
+ return NULL;
+ }
+
+ for (LIST_ENTRY *pLink = MappedViewList.Flink;
+ pLink != &MappedViewList;
+ pLink = pLink->Flink)
+ {
+ PMAPPED_VIEW_LIST pView = CONTAINING_RECORD(pLink, MAPPED_VIEW_LIST, Link);
+
+ if (pView->MappedFileDevNum != deviceNum
+ || pView->MappedFileInodeNum != inodeNum
+ || pView->dwDesiredAccess == FILE_MAP_COPY)
+ {
+ continue;
+ }
+
+ //
+ // This is a shared mapping for the same indoe / device. Now, check
+ // to see if it overlaps with the range for the new view
+ //
+
+ SIZE_T real_map_offs = pView->pNMHolder->offset;
+ SIZE_T real_map_sz = pView->pNMHolder->size;
+
+ if (real_map_offs <= offset
+ && real_map_offs+real_map_sz >= offset)
+ {
+ //
+ // The views overlap. Even if this view is not reusable for the
+ // new once the search is over, as on
+ // ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS systems there
+ // cannot be shared mappings of two overlapping regions of the
+ // same file, in the same process. Therefore, whether this view
+ // is reusable or not we cannot mmap the requested region of
+ // the specified file.
+ //
+
+ if (real_map_offs+real_map_sz >= offset+size)
+ {
+ /* The new desired mapping is fully contained in the
+ one just found: we can reuse this one */
+
+ pNewView = (PMAPPED_VIEW_LIST)InternalMalloc(sizeof(MAPPED_VIEW_LIST));
+ if (pNewView)
+ {
+ memcpy(pNewView, pView, sizeof(*pNewView));
+ NativeMapHolderAddRef(pNewView->pNMHolder);
+ pNewView->lpAddress = (VOID*)((CHAR*)pNewView->pNMHolder->address +
+ offset - pNewView->pNMHolder->offset);
+ pNewView->NumberOfBytesToMap = size;
+ }
+ else
+ {
+ ERROR("No memory for new MAPPED_VIEW_LIST node\n");
+ }
+ }
+
+ break;
+ }
+ }
+
+ TRACE ("FindSharedMappingReplacement returning %p\n", pNewView);
+ return pNewView;
+}
+
+static NativeMapHolder * NewNativeMapHolder(CPalThread *pThread, LPVOID address, SIZE_T size,
+ SIZE_T offset, long init_ref_count)
+{
+ NativeMapHolder * pThisMapHolder;
+
+ if (init_ref_count < 0)
+ {
+ ASSERT("Negative initial reference count for new map holder\n");
+ return NULL;
+ }
+
+ pThisMapHolder =
+ (NativeMapHolder *)InternalMalloc(sizeof(NativeMapHolder));
+
+ if (pThisMapHolder)
+ {
+ pThisMapHolder->ref_count = init_ref_count;
+ pThisMapHolder->address = address;
+ pThisMapHolder->size = size;
+ pThisMapHolder->offset = offset;
+ }
+
+ return pThisMapHolder;
+}
+
+static LONG NativeMapHolderAddRef(NativeMapHolder * thisNMH)
+{
+ LONG ret = InterlockedIncrement(&thisNMH->ref_count);
+ return ret;
+}
+
+static LONG NativeMapHolderRelease(CPalThread *pThread, NativeMapHolder * thisNMH)
+{
+ LONG ret = InterlockedDecrement(&thisNMH->ref_count);
+ if (ret == 0)
+ {
+ if (-1 == munmap(thisNMH->address, thisNMH->size))
+ {
+ ASSERT( "Unable to unmap memory. Error=%s.\n",
+ strerror( errno ) );
+ }
+ else
+ {
+ TRACE( "Successfully unmapped %p (size=%lu)\n",
+ thisNMH->address, (unsigned long)thisNMH->size);
+ }
+ free (thisNMH);
+ }
+ else if (ret < 0)
+ {
+ ASSERT( "Negative reference count for map holder %p"
+ " {address=%p, size=%lu}\n", thisNMH->address,
+ (unsigned long)thisNMH->size);
+ }
+
+ return ret;
+}
+
+#endif // ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
+
+// Record a mapping in the MappedViewList list.
+// This call assumes the mapping_critsec has already been taken.
+static PAL_ERROR
+MAPRecordMapping(
+ IPalObject *pMappingObject,
+ void *pPEBaseAddress,
+ void *addr,
+ size_t len,
+ int prot
+ )
+{
+ if (pPEBaseAddress == NULL)
+ {
+ return ERROR_INTERNAL_ERROR;
+ }
+
+ PAL_ERROR palError = NO_ERROR;
+ PMAPPED_VIEW_LIST pNewView;
+ pNewView = (PMAPPED_VIEW_LIST)InternalMalloc(sizeof(*pNewView));
+ if (NULL != pNewView)
+ {
+ pNewView->lpAddress = addr;
+ pNewView->NumberOfBytesToMap = len;
+ pNewView->dwDesiredAccess = MAPMmapProtToAccessFlags(prot);
+ pMappingObject->AddReference();
+ pNewView->pFileMapping = pMappingObject;
+ pNewView->lpPEBaseAddress = pPEBaseAddress;
+ InsertTailList(&MappedViewList, &pNewView->Link);
+
+ TRACE_(LOADER)("Added address %p, size 0x%x, to the mapped file list.\n", addr, len);
+ }
+ else
+ {
+ palError = ERROR_INTERNAL_ERROR;
+ }
+
+ return palError;
+}
+
+// Do the actual mmap() call, and record the mapping in the MappedViewList list.
+// This call assumes the mapping_critsec has already been taken.
+static PAL_ERROR
+MAPmmapAndRecord(
+ IPalObject *pMappingObject,
+ void *pPEBaseAddress,
+ void *addr,
+ size_t len,
+ int prot,
+ int flags,
+ int fd,
+ off_t offset,
+ LPVOID *ppvBaseAddress
+ )
+{
+ _ASSERTE(pPEBaseAddress != NULL);
+
+ PAL_ERROR palError = NO_ERROR;
+ LPVOID pvBaseAddress = NULL;
+
+ pvBaseAddress = mmap(addr, len, prot, flags, fd, offset);
+ if (MAP_FAILED == pvBaseAddress)
+ {
+ ERROR_(LOADER)( "mmap failed with code %d: %s.\n", errno, strerror( errno ) );
+ palError = FILEGetLastErrorFromErrno();
+ }
+ else
+ {
+ palError = MAPRecordMapping(pMappingObject, pPEBaseAddress, pvBaseAddress, len, prot);
+ if (NO_ERROR != palError)
+ {
+ if (-1 == munmap(pvBaseAddress, len))
+ {
+ ERROR_(LOADER)("Unable to unmap the file. Expect trouble.\n");
+ }
+ }
+ else
+ {
+ *ppvBaseAddress = pvBaseAddress;
+ }
+ }
+
+ return palError;
+}
+
+/*++
+ MAPMapPEFile -
+
+ Map a PE format file into memory like Windows LoadLibrary() would do.
+ Doesn't apply base relocations if the function is relocated.
+
+Parameters:
+ IN hFile - file to map
+
+Return value:
+ non-NULL - the base address of the mapped image
+ NULL - error, with last error set.
+--*/
+
+void * MAPMapPEFile(HANDLE hFile)
+{
+ PAL_ERROR palError = 0;
+ IPalObject *pFileObject = NULL;
+ IDataLock *pLocalDataLock = NULL;
+ CFileProcessLocalData *pLocalData = NULL;
+ CPalThread *pThread = InternalGetCurrentThread();
+ void * loadedBase = NULL;
+ IMAGE_DOS_HEADER * loadedHeader = NULL;
+ void * retval;
+#if _DEBUG
+ bool forceRelocs = false;
+ char* envVar;
+#endif
+
+ ENTRY("MAPMapPEFile (hFile=%p)\n", hFile);
+
+ //Step 0: Verify values, find internal pal data structures.
+ if (INVALID_HANDLE_VALUE == hFile)
+ {
+ ERROR_(LOADER)( "Invalid file handle\n" );
+ palError = ERROR_INVALID_HANDLE;
+ goto done;
+ }
+
+ palError = g_pObjectManager->ReferenceObjectByHandle(
+ pThread,
+ hFile,
+ &aotFile,
+ GENERIC_READ,
+ &pFileObject
+ );
+ if (NO_ERROR != palError)
+ {
+ ERROR_(LOADER)( "ReferenceObjectByHandle failed\n" );
+ goto done;
+ }
+
+ palError = pFileObject->GetProcessLocalData(
+ pThread,
+ ReadLock,
+ &pLocalDataLock,
+ reinterpret_cast<void**>(&pLocalData)
+ );
+ if (NO_ERROR != palError)
+ {
+ ERROR_(LOADER)( "GetProcessLocalData failed\n" );
+ goto done;
+ }
+
+ int fd;
+ fd = pLocalData->unix_fd;
+ //Step 1: Read the PE headers and reserve enough space for the whole image somewhere.
+ IMAGE_DOS_HEADER dosHeader;
+ IMAGE_NT_HEADERS ntHeader;
+ errno = 0;
+ if (0 != lseek(fd, 0, SEEK_SET))
+ {
+ palError = FILEGetLastErrorFromErrno();
+ ERROR_(LOADER)( "lseek failed\n" );
+ goto done;
+ }
+ if (sizeof(dosHeader) != read(fd, &dosHeader, sizeof(dosHeader)))
+ {
+ palError = FILEGetLastErrorFromErrno();
+ ERROR_(LOADER)( "reading dos header failed\n" );
+ goto done;
+ }
+ if (dosHeader.e_lfanew != lseek(fd, dosHeader.e_lfanew, SEEK_SET))
+ {
+ palError = FILEGetLastErrorFromErrno();
+ goto done;
+ }
+ if (sizeof(ntHeader) != read(fd, &ntHeader, sizeof(ntHeader)))
+ {
+ palError = FILEGetLastErrorFromErrno();
+ goto done;
+ }
+
+ if ((VAL16(IMAGE_DOS_SIGNATURE) != VAL16(dosHeader.e_magic))
+ || (VAL32(IMAGE_NT_SIGNATURE) != VAL32(ntHeader.Signature))
+ || (VAL16(IMAGE_NT_OPTIONAL_HDR_MAGIC) != VAL16(ntHeader.OptionalHeader.Magic) ) )
+ {
+ ERROR_(LOADER)( "Magic number mismatch\n" );
+ palError = ERROR_INVALID_PARAMETER;
+ goto done;
+ }
+
+ //this code requires that the file alignment be the same as the page alignment
+ if (ntHeader.OptionalHeader.FileAlignment < VIRTUAL_PAGE_SIZE)
+ {
+ ERROR_(LOADER)( "Optional header file alignment is bad\n" );
+ palError = ERROR_INVALID_PARAMETER;
+ goto done;
+ }
+
+ //This doesn't read the entire NT header (the optional header technically has a variable length. But I
+ //don't need more directories.
+
+ //I now know how big the file is. Reserve enough address space for the whole thing. Try to get the
+ //preferred base. Create the intial mapping as "no access". We'll use that for the guard pages in the
+ //"holes" between sections.
+ SIZE_T preferredBase, virtualSize;
+ preferredBase = ntHeader.OptionalHeader.ImageBase;
+ virtualSize = ntHeader.OptionalHeader.SizeOfImage;
+
+ // Validate the image header
+ if ( (preferredBase == 0)
+ || (virtualSize == 0)
+ || (preferredBase + virtualSize < preferredBase) // Does the image overflow?
+ )
+ {
+ ERROR_(LOADER)( "image is corrupt\n" );
+ palError = ERROR_INVALID_PARAMETER;
+ goto done;
+ }
+
+#if _DEBUG
+ envVar = EnvironGetenv("PAL_ForceRelocs");
+ if (envVar)
+ {
+ if (strlen(envVar) > 0)
+ {
+ forceRelocs = true;
+ TRACE_(LOADER)("Forcing rebase of image\n");
+ }
+
+ free(envVar);
+ }
+
+ void * pForceRelocBase;
+ pForceRelocBase = NULL;
+ if (forceRelocs)
+ {
+ //if we're forcing relocs, create an anonymous mapping at the preferred base. Only create the
+ //mapping if we can create it at the specified address.
+ pForceRelocBase = mmap( (void*)preferredBase, VIRTUAL_PAGE_SIZE, PROT_NONE, MAP_ANON|MAP_FIXED|MAP_PRIVATE, -1, 0 );
+ if (pForceRelocBase == MAP_FAILED)
+ {
+ TRACE_(LOADER)("Attempt to take preferred base of %p to force relocation failed\n", (void*)preferredBase);
+ forceRelocs = false;
+ }
+ else if ((void*)preferredBase != pForceRelocBase)
+ {
+ TRACE_(LOADER)("Attempt to take preferred base of %p to force relocation failed; actually got %p\n", (void*)preferredBase, pForceRelocBase);
+ }
+ }
+#endif // _DEBUG
+
+ // The first mmap mapping covers the entire file but just reserves space. Subsequent mappings cover
+ // individual parts of the file, and actually map pages in. Note that according to the mmap() man page, "A
+ // successful mmap deletes any previous mapping in the allocated address range." Also, "If a MAP_FIXED
+ // request is successful, the mapping established by mmap() replaces any previous mappings for the process' pages
+ // in the range from addr to addr + len." Thus, we will record a series of mappings here, one for the header
+ // and each of the sections, as well as all the space between them that we give PROT_NONE protections.
+
+ // We're going to start adding mappings to the mapping list, so take the critical section
+ InternalEnterCriticalSection(pThread, &mapping_critsec);
+
+#if !defined(_AMD64_)
+ loadedBase = mmap((void*)preferredBase, virtualSize, PROT_NONE, MAP_ANON|MAP_PRIVATE, -1, 0);
+#else // defined(_AMD64_)
+ // First try to reserve virtual memory using ExecutableAllcator. This allows all PE images to be
+ // near each other and close to the coreclr library which also allows the runtime to generate
+ // more efficient code (by avoiding usage of jump stubs).
+ loadedBase = ReserveMemoryFromExecutableAllocator(pThread, virtualSize);
+ if (loadedBase == NULL)
+ {
+ // MAC64 requires we pass MAP_SHARED (or MAP_PRIVATE) flags - otherwise, the call is failed.
+ // Refer to mmap documentation at http://www.manpagez.com/man/2/mmap/ for details.
+ loadedBase = mmap((void*)preferredBase, virtualSize, PROT_NONE, MAP_ANON|MAP_PRIVATE, -1, 0);
+ }
+#endif // !defined(_AMD64_)
+
+ if (MAP_FAILED == loadedBase)
+ {
+ ERROR_(LOADER)( "mmap failed with code %d: %s.\n", errno, strerror( errno ) );
+ palError = FILEGetLastErrorFromErrno();
+ loadedBase = NULL; // clear it so we don't try to use it during clean-up
+ goto doneReleaseMappingCriticalSection;
+ }
+
+ // All subsequent mappings of the PE file will be in the range [loadedBase, loadedBase + virtualSize)
+
+#if _DEBUG
+ if (forceRelocs)
+ {
+ _ASSERTE(((SIZE_T)loadedBase) != preferredBase);
+ munmap(pForceRelocBase, VIRTUAL_PAGE_SIZE); // now that we've forced relocation, let the original address mapping go
+ }
+ if (((SIZE_T)loadedBase) != preferredBase)
+ {
+ TRACE_(LOADER)("Image rebased from preferredBase of %p to loadedBase of %p\n", preferredBase, loadedBase);
+ }
+ else
+ {
+ TRACE_(LOADER)("Image loaded at preferred base %p\n", loadedBase);
+ }
+#endif // _DEBUG
+
+ //we have now reserved memory (potentially we got rebased). Walk the PE sections and map each part
+ //separately.
+
+ size_t headerSize;
+ headerSize = VIRTUAL_PAGE_SIZE; // if there are lots of sections, this could be wrong
+
+ //first, map the PE header to the first page in the image. Get pointers to the section headers
+ palError = MAPmmapAndRecord(pFileObject, loadedBase,
+ loadedBase, headerSize, PROT_READ, MAP_FILE|MAP_PRIVATE|MAP_FIXED, fd, 0,
+ (void**)&loadedHeader);
+ if (NO_ERROR != palError)
+ {
+ ERROR_(LOADER)( "mmap of PE header failed\n" );
+ goto doneReleaseMappingCriticalSection;
+ }
+
+ TRACE_(LOADER)("PE header loaded @ %p\n", loadedHeader);
+ _ASSERTE(loadedHeader == loadedBase); // we already preallocated the space, and we used MAP_FIXED, so we should have gotten this address
+ IMAGE_SECTION_HEADER * firstSection;
+ firstSection = (IMAGE_SECTION_HEADER*)(((char *)loadedHeader)
+ + loadedHeader->e_lfanew
+ + offsetof(IMAGE_NT_HEADERS, OptionalHeader)
+ + VAL16(ntHeader.FileHeader.SizeOfOptionalHeader));
+ unsigned numSections;
+ numSections = ntHeader.FileHeader.NumberOfSections;
+
+ // Validation
+ char* sectionHeaderEnd;
+ sectionHeaderEnd = (char*)firstSection + numSections * sizeof(IMAGE_SECTION_HEADER);
+ if ( ((void*)firstSection < loadedBase)
+ || ((char*)firstSection > sectionHeaderEnd)
+ || (sectionHeaderEnd > (char*)loadedBase + virtualSize)
+ )
+ {
+ ERROR_(LOADER)( "image is corrupt\n" );
+ palError = ERROR_INVALID_PARAMETER;
+ goto doneReleaseMappingCriticalSection;
+ }
+
+ void* prevSectionBase;
+ prevSectionBase = loadedBase; // the first "section" for our purposes is the header
+ size_t prevSectionSizeInMemory;
+ prevSectionSizeInMemory = headerSize;
+ for (unsigned i = 0; i < numSections; ++i)
+ {
+ //for each section, map the section of the file to the correct virtual offset. Gather the
+ //protection bits from the PE file and convert them to the correct mmap PROT_* flags.
+ void * sectionData;
+ int prot = 0;
+ IMAGE_SECTION_HEADER &currentHeader = firstSection[i];
+
+ void* sectionBase = (char*)loadedBase + currentHeader.VirtualAddress;
+
+ // Validate the section header
+ if ( (sectionBase < loadedBase) // Did computing the section base overflow?
+ || ((char*)sectionBase + currentHeader.SizeOfRawData < (char*)sectionBase) // Does the section overflow?
+ || ((char*)sectionBase + currentHeader.SizeOfRawData > (char*)loadedBase + virtualSize) // Does the section extend past the end of the image as the header stated?
+ || ((char*)prevSectionBase + prevSectionSizeInMemory > sectionBase) // Does this section overlap the previous one?
+ )
+ {
+ ERROR_(LOADER)( "section %d is corrupt\n", i );
+ palError = ERROR_INVALID_PARAMETER;
+ goto doneReleaseMappingCriticalSection;
+ }
+ if (currentHeader.Misc.VirtualSize > currentHeader.SizeOfRawData)
+ {
+ ERROR_(LOADER)( "no support for zero-padded sections, section %d\n", i );
+ palError = ERROR_INVALID_PARAMETER;
+ goto doneReleaseMappingCriticalSection;
+ }
+
+ // Is there space between the previous section and this one? If so, add a PROT_NONE mapping to cover it.
+ if ((char*)prevSectionBase + prevSectionSizeInMemory < sectionBase)
+ {
+ char* gapBase = (char*)prevSectionBase + prevSectionSizeInMemory;
+ palError = MAPRecordMapping(pFileObject,
+ loadedBase,
+ (void*)gapBase,
+ (char*)sectionBase - gapBase,
+ PROT_NONE);
+ if (NO_ERROR != palError)
+ {
+ ERROR_(LOADER)( "recording gap section before section %d failed\n", i );
+ goto doneReleaseMappingCriticalSection;
+ }
+ }
+
+ //Don't discard these sections. We need them to verify PE files
+ //if (currentHeader.Characteristics & IMAGE_SCN_MEM_DISCARDABLE)
+ // continue;
+ if (currentHeader.Characteristics & IMAGE_SCN_MEM_EXECUTE)
+ prot |= PROT_EXEC;
+ if (currentHeader.Characteristics & IMAGE_SCN_MEM_READ)
+ prot |= PROT_READ;
+ if (currentHeader.Characteristics & IMAGE_SCN_MEM_WRITE)
+ prot |= PROT_WRITE;
+
+ palError = MAPmmapAndRecord(pFileObject, loadedBase,
+ sectionBase,
+ currentHeader.SizeOfRawData,
+ prot,
+ MAP_FILE|MAP_PRIVATE|MAP_FIXED,
+ fd,
+ currentHeader.PointerToRawData,
+ &sectionData);
+ if (NO_ERROR != palError)
+ {
+ ERROR_(LOADER)( "mmap of section %d failed\n", i );
+ goto doneReleaseMappingCriticalSection;
+ }
+
+#if _DEBUG
+ {
+ // Ensure null termination of section name (which is allowed to not be null terminated if exactly 8 characters long)
+ char sectionName[9];
+ sectionName[8] = '\0';
+ memcpy(sectionName, currentHeader.Name, sizeof(currentHeader.Name));
+ TRACE_(LOADER)("Section %d '%s' (header @ %p) loaded @ %p (RVA: 0x%x, SizeOfRawData: 0x%x, PointerToRawData: 0x%x)\n",
+ i, sectionName, &currentHeader, sectionData, currentHeader.VirtualAddress, currentHeader.SizeOfRawData, currentHeader.PointerToRawData);
+ }
+#endif // _DEBUG
+
+ prevSectionBase = sectionBase;
+ prevSectionSizeInMemory = (currentHeader.SizeOfRawData + VIRTUAL_PAGE_MASK) & ~VIRTUAL_PAGE_MASK; // round up to page boundary
+ }
+
+ // Is there space after the last section and before the end of the mapped image? If so, add a PROT_NONE mapping to cover it.
+ char* imageEnd;
+ imageEnd = (char*)loadedBase + virtualSize; // actually, points just after the mapped end
+ if ((char*)prevSectionBase + prevSectionSizeInMemory < imageEnd)
+ {
+ char* gapBase = (char*)prevSectionBase + prevSectionSizeInMemory;
+ palError = MAPRecordMapping(pFileObject,
+ loadedBase,
+ (void*)gapBase,
+ imageEnd - gapBase,
+ PROT_NONE);
+ if (NO_ERROR != palError)
+ {
+ ERROR_(LOADER)( "recording end of image gap section failed\n" );
+ goto doneReleaseMappingCriticalSection;
+ }
+ }
+
+ palError = ERROR_SUCCESS;
+
+doneReleaseMappingCriticalSection:
+
+ InternalLeaveCriticalSection(pThread, &mapping_critsec);
+
+done:
+
+ if (NULL != pLocalDataLock)
+ {
+ pLocalDataLock->ReleaseLock(pThread, FALSE);
+ }
+
+ if (NULL != pFileObject)
+ {
+ pFileObject->ReleaseReference(pThread);
+ }
+
+ if (palError == ERROR_SUCCESS)
+ {
+ retval = loadedBase;
+ LOGEXIT("MAPMapPEFile returns %p\n", retval);
+ }
+ else
+ {
+ retval = NULL;
+ LOGEXIT("MAPMapPEFile error: %d\n", palError);
+
+ // If we had an error, and had mapped anything, we need to unmap it
+ if (loadedBase != NULL)
+ {
+ MAPUnmapPEFile(loadedBase);
+ }
+ }
+ return retval;
+}
+
+/*++
+Function :
+ MAPUnmapPEFile - unmap a PE file, and remove it from the recorded list of PE files mapped
+
+ returns TRUE if successful, FALSE otherwise
+--*/
+BOOL MAPUnmapPEFile(LPCVOID lpAddress)
+{
+ TRACE_(LOADER)("MAPUnmapPEFile(lpAddress=%p)\n", lpAddress);
+
+ if ( NULL == lpAddress )
+ {
+ ERROR_(LOADER)( "lpAddress cannot be NULL\n" );
+ return FALSE;
+ }
+
+ BOOL retval = TRUE;
+ CPalThread * pThread = InternalGetCurrentThread();
+ InternalEnterCriticalSection(pThread, &mapping_critsec);
+ PLIST_ENTRY pLink, pLinkNext, pLinkLocal = NULL;
+ unsigned nPESections = 0;
+
+ // Look through the entire MappedViewList for all mappings associated with the
+ // PE file with base address 'lpAddress'. We want to unmap all the memory
+ // and then release the file mapping object. Unfortunately, based on the comment
+ // in CorUnix::InternalUnmapViewOfFile(), we can't release the file mapping object
+ // while within the mapping critical section. So, we unlink all the elements from the
+ // main list while in the critical section, and then run through this local list
+ // doing the real work after releasing the main list critical section. The order
+ // of the unmapping doesn't matter, so we don't fully set all the list link pointers,
+ // only a minimal set.
+
+ for(pLink = MappedViewList.Flink;
+ pLink != &MappedViewList;
+ pLink = pLinkNext)
+ {
+ pLinkNext = pLink->Flink;
+ PMAPPED_VIEW_LIST pView = CONTAINING_RECORD(pLink, MAPPED_VIEW_LIST, Link);
+
+ if (pView->lpPEBaseAddress == lpAddress) // this entry is associated with the PE file
+ {
+ ++nPESections; // for debugging, check that we see at least one
+
+ RemoveEntryList(&pView->Link);
+ pView->Link.Flink = pLinkLocal; // the local list is singly-linked, NULL terminated
+ pLinkLocal = &pView->Link;
+ }
+ }
+
+#if _DEBUG
+ if (nPESections == 0)
+ {
+ ERROR_(LOADER)( "MAPUnmapPEFile called to unmap a file that was not in the PE file mapping list\n" );
+ }
+#endif // _DEBUG
+
+ InternalLeaveCriticalSection(pThread, &mapping_critsec);
+
+ // Now, outside the critical section, do the actual unmapping work
+
+ for(pLink = pLinkLocal;
+ pLink != NULL;
+ pLink = pLinkNext)
+ {
+ pLinkNext = pLink->Flink;
+ PMAPPED_VIEW_LIST pView = CONTAINING_RECORD(pLink, MAPPED_VIEW_LIST, Link);
+
+ // remove pView mapping from the list
+ if (-1 == munmap(pView->lpAddress, pView->NumberOfBytesToMap))
+ {
+ // Emit an error message in a trace, but continue trying to do the rest
+ ERROR_(LOADER)("Unable to unmap the file. Expect trouble.\n");
+ retval = FALSE;
+ }
+
+ IPalObject* pFileObject = pView->pFileMapping;
+ if (NULL != pFileObject)
+ {
+ pFileObject->ReleaseReference(pThread);
+ }
+ free(pView); // this leaves pLink dangling
+ }
+
+ TRACE_(LOADER)("MAPUnmapPEFile returning %d\n", retval);
+ return retval;
+}