[Tizen] Add coreclr_preload_assembly to CoreCLR host API
authorKonstantin Baladurin <k.baladurin@samsung.com>
Fri, 5 Jul 2019 16:36:28 +0000 (19:36 +0300)
committerGleb Balykov <g.balykov@samsung.com>
Wed, 25 Mar 2020 12:29:41 +0000 (15:29 +0300)
14 files changed:
src/coreclr/hosts/inc/coreclrhost.h
src/dlls/mscoree/mscorwks_ntdef.src
src/dlls/mscoree/mscorwks_unixexports.src
src/dlls/mscoree/unixinterface.cpp
src/inc/corhost.h
src/pal/inc/pal.h
src/pal/src/include/pal/dbgmsg.h
src/pal/src/include/pal/map.hpp
src/pal/src/include/pal/module.h
src/pal/src/loader/module.cpp
src/pal/src/map/map.cpp
src/vm/corhost.cpp
src/vm/peimagelayout.cpp
src/vm/peimagelayout.h

index d1ec724..7058492 100644 (file)
@@ -22,6 +22,9 @@
     extern "C" int CORECLR_CALLING_CONVENTION function(__VA_ARGS__); \
     typedef int (CORECLR_CALLING_CONVENTION *function##_ptr)(__VA_ARGS__)
     
+CORECLR_HOSTING_API(coreclr_preload_assembly,
+            const char* assemblyPath);
+
 //
 // Initialize the CoreCLR. Creates and starts CoreCLR host and creates an app domain
 //
index bdbce4f..3611e0c 100644 (file)
@@ -20,6 +20,7 @@ EXPORTS
         CLRJitAttachState                                   @3 data
         
         ; Unix hosting API
+        coreclr_preload_assembly
         coreclr_create_delegate
         coreclr_execute_assembly
         coreclr_initialize
index 6ba08c9..ddbe76b 100644 (file)
@@ -1,4 +1,5 @@
 ; Unix hosting API
+coreclr_preload_assembly
 coreclr_create_delegate
 coreclr_execute_assembly
 coreclr_initialize
index 67bd444..7cbfad0 100644 (file)
@@ -146,6 +146,34 @@ extern "C" int coreclr_create_delegate(void*, unsigned int, const char*, const c
 #endif //FEATURE_GDBJIT
 
 //
+// Preload native assembly.
+//
+// This method allows to preload assembly to memory and apply relocations before initialization of CoreCLR.
+// Assemblies are stored in the list, which is scanned during general loading after CoreCLR initialization.
+// If path is found in the list, preloaded memory is used.
+// If CoreCLR is already initialized, it returns E_FAIL.
+//
+// Parameters:
+//  assemblyPath            - Absolute path of the assembly to preload
+//
+// Returns:
+//  HRESULT indicating status of the operation. S_OK if the assembly was successfully preloaded
+//
+extern "C"
+DLLEXPORT
+int coreclr_preload_assembly(
+            const char *assemblyPath
+)
+{
+    if (assemblyPath == NULL)
+    {
+        return E_FAIL;
+    }
+
+    return CorHost2::PreloadAssembly(assemblyPath);
+}
+
+//
 // Initialize the CoreCLR. Creates and starts CoreCLR host and creates an app domain
 //
 // Parameters:
@@ -288,7 +316,12 @@ int coreclr_shutdown(
 {
     ReleaseHolder<ICLRRuntimeHost4> host(reinterpret_cast<ICLRRuntimeHost4*>(hostHandle));
 
-    HRESULT hr = host->UnloadAppDomain(domainId, true); // Wait until done
+    HRESULT hr;
+
+    hr = CorHost2::UnloadPreloadedAssemblies();
+    IfFailRet(hr);
+
+    hr = host->UnloadAppDomain(domainId, true); // Wait until done
     IfFailRet(hr);
 
     hr = host->Stop();
@@ -320,7 +353,12 @@ int coreclr_shutdown_2(
 {
     ReleaseHolder<ICLRRuntimeHost4> host(reinterpret_cast<ICLRRuntimeHost4*>(hostHandle));
 
-    HRESULT hr = host->UnloadAppDomain2(domainId, true, latchedExitCode); // Wait until done
+    HRESULT hr;
+
+    hr = CorHost2::UnloadPreloadedAssemblies();
+    IfFailRet(hr);
+
+    hr = host->UnloadAppDomain2(domainId, true, latchedExitCode); // Wait until done
     IfFailRet(hr);
 
     hr = host->Stop();
index 09ea994..1ff6caf 100644 (file)
@@ -218,6 +218,11 @@ public:
         LPCWSTR* argv,
         DWORD* pReturnValue);
 
+    static HRESULT PreloadAssembly(
+        LPCSTR szPath);
+
+    static HRESULT UnloadPreloadedAssemblies();
+
     static STARTUP_FLAGS GetStartupFlags();
 
     static BOOL HasStarted()
index abb25e8..11b434d 100644 (file)
@@ -2696,6 +2696,8 @@ Abstract
 
 Parameters:
     IN hFile    - The file to load
+    IN wszPath  - File path
+    OUT isPreloaded - Flag whether pefile was preloaded
 
 Return value:
     A valid base address if successful.
@@ -2704,7 +2706,7 @@ Return value:
 PALIMPORT
 PVOID
 PALAPI
-PAL_LOADLoadPEFile(HANDLE hFile);
+PAL_LOADLoadPEFile(HANDLE hFile, LPCWSTR wszPath, BOOL *isPreloaded);
 
 /*++
     PAL_LOADUnloadPEFile
@@ -2723,6 +2725,36 @@ BOOL
 PALAPI
 PAL_LOADUnloadPEFile(PVOID ptr);
 
+/*++
+Function:
+  PAL_LOADPreloadPEFile
+
+Abstract
+  Preloads a PE file into memory.  Properly maps all of the sections in the PE file.  Returns a pointer to the
+  loaded base.
+
+Parameters:
+    IN szPath    - path of file to load
+
+Return value:
+    A valid base address if successful.
+    0 if failure
+--*/
+void *
+PAL_LOADPreloadPEFile(LPCSTR szPath);
+
+/*++
+    PAL_LOADUnloadPreloadedPEFiles
+
+    Unload all PE files that were loaded by PAL_LOADPreloadPEFile().
+
+Return value:
+    TRUE - success
+    FALSE - failure
+--*/
+BOOL
+PAL_LOADUnloadPreloadedPEFiles();
+
 #ifdef UNICODE
 #define LoadLibrary LoadLibraryW
 #define LoadLibraryEx LoadLibraryExW
index d7219b2..58ab25b 100644 (file)
@@ -363,14 +363,17 @@ inline void ANALYZER_NORETURN AssertBreak()
     }
 }
 
-#define ASSERT(...)                                                     \
-{                                                                       \
-    __ASSERT_ENTER();                                                   \
-    if (output_file && dbg_master_switch)                               \
-    {                                                                   \
-        DBG_printf(defdbgchan,DLI_ASSERT,TRUE,__FUNCTION__,__FILE__,__LINE__,__VA_ARGS__); \
-    }                                                                   \
-    AssertBreak();                                                     \
+#define ASSERT(...)                                                         \
+{                                                                           \
+    if (PALIsInitialized())                                                 \
+    {                                                                       \
+        __ASSERT_ENTER();                                                   \
+        if (output_file && dbg_master_switch)                               \
+        {                                                                   \
+            DBG_printf(defdbgchan,DLI_ASSERT,TRUE,__FUNCTION__,__FILE__,__LINE__,__VA_ARGS__); \
+        }                                                                   \
+        AssertBreak();                                                      \
+    }                                                                       \
 }
     
 #define _ASSERT(expr) do { if (!(expr)) { ASSERT(""); } } while(0)
index 7bcb20a..16ee58e 100644 (file)
@@ -77,15 +77,59 @@ extern "C"
         Map a PE format file into memory like Windows LoadLibrary() would do.
         Doesn't apply base relocations if the function is relocated.
 
+        There are two scenarios:
+
+        - image could be preloaded and then MAPMapPEFile is called for it
+        - image is loaded with MAPMapPEFile
+
+        In the first scenario, hFile and lpPreloadedBase are supposed to be NULL, and szPath and size - non-NULL.
+        In the second scenario, hFile and lpPreloadedBase are supposed to be non-NULL, and szPath and size - NULL.
+
+        See coreclr_preload_assembly for further details.
+
     Parameters:
         IN hFile - file to map
+        IN szPath - path to mapped file
+        OUT size - mapped virtual size
+        IN lpPreloadedBase - previously loaded base
 
     Return value:
         non-NULL - the base address of the mapped image
         NULL - error, with last error set.
     --*/
 
-    void * MAPMapPEFile(HANDLE hFile);
+    void * MAPMapPEFile(HANDLE hFile, LPCSTR szPath, SIZE_T *size, LPVOID lpPreloadedBase);
+
+    /*++
+        MAPApplyBaseRelocationsPreloadedPEFile -
+
+        Apply base relocations to preloaded image
+
+    Parameters:
+        IN lpMappedImage - base address of preloaded image
+        IN virtualSize - virtual size of preloaded image
+
+    Return value:
+        true - if relocations were applied successfully
+        false - otherwise
+    --*/
+
+    bool MAPApplyBaseRelocationsPreloadedPEFile(LPVOID lpMappedImage, SIZE_T virtualSize);
+
+    /*++
+        MAPUnmapPreloadedPEFile -
+
+        Unmap a PE file
+
+    Parameters:
+        IN lpMappedImage - address of mapped file
+        IN size - virtual size
+
+    Return value:
+        returns TRUE if successful, FALSE otherwise
+    --*/
+
+    BOOL MAPUnmapPreloadedPEFile(LPVOID lpMappedImage, SIZE_T size);
 
     /*++
     Function :
index 66ac238..ee5d000 100644 (file)
@@ -51,6 +51,20 @@ typedef struct _MODSTRUCT
     struct _MODSTRUCT *prev;
 } MODSTRUCT;
 
+/*++
+    LOADFindPreloadedPEFile -
+
+    Find image in list of preloaded
+
+Parameters:
+    IN wszPath - path to mapped file
+
+Return value:
+    non-NULL - the base address of the mapped image
+    NULL - image is not in the list of preloaded.
+--*/
+
+void * LOADFindPreloadedPEFile(LPCWSTR wszPath);
 
 /*++
 Function :
@@ -145,12 +159,14 @@ Abstract
 
 Parameters:
     IN hFile    - The file to load
+    IN wszPath  - File path
+    OUT isPreloaded - Flag whether pefile was preloaded
 
 Return value:
     A valid base address if successful.
     0 if failure
 --*/
-void * PAL_LOADLoadPEFile(HANDLE hFile);
+void * PAL_LOADLoadPEFile(HANDLE hFile, LPCWSTR wszPath, BOOL *isPreloaded);
 
 /*++
     PAL_LOADUnloadPEFile
@@ -167,6 +183,36 @@ Return value:
 BOOL PAL_LOADUnloadPEFile(void * ptr);
 
 /*++
+Function:
+  PAL_LOADPreloadPEFile
+
+Abstract
+  Preloads a PE file into memory.  Properly maps all of the sections in the PE file.  Returns a pointer to the
+  loaded base.
+
+Parameters:
+    IN szPath    - path of file to load
+
+Return value:
+    A valid base address if successful.
+    0 if failure
+--*/
+void *
+PAL_LOADPreloadPEFile(LPCSTR szPath);
+
+/*++
+    PAL_LOADUnloadPreloadedPEFiles
+
+    Unload all PE files that were loaded by PAL_LOADPreloadPEFile().
+
+Return value:
+    TRUE - success
+    FALSE - failure
+--*/
+BOOL
+PAL_LOADUnloadPreloadedPEFiles();
+
+/*++
     LOADInitializeCoreCLRModule
 
     Run the initialization methods for CoreCLR module.
index 74156fd..78069b8 100644 (file)
@@ -85,8 +85,98 @@ using namespace CorUnix;
 
 #define LIBC_NAME_WITHOUT_EXTENSION "libc"
 
+struct PreloadedImageListNode
+{
+    PreloadedImageListNode *next;
+    LPSTR szPath;
+    LPWSTR wszPath;
+    LPVOID lpMappedImage;
+    SIZE_T virtualSize;
+    bool isLoaded;
+};
+
+class PreloadedImageList
+{
+    PreloadedImageListNode *m_start;
+
+public:
+
+    PreloadedImageList()
+      : m_start(NULL) {}
+
+    void addNew(LPCSTR szPath, LPVOID lpImage, SIZE_T size)
+    {
+        PreloadedImageListNode *node = (PreloadedImageListNode *) InternalMalloc(sizeof(PreloadedImageListNode));
+        node->szPath = (LPSTR) InternalMalloc(strlen(szPath) + 1);
+        strcpy(node->szPath, szPath);
+        node->wszPath = NULL;
+        node->lpMappedImage = lpImage;
+        node->virtualSize = size;
+        node->isLoaded = false;
+        node->next = m_start;
+        m_start = node;
+    }
+
+    bool freeAll()
+    {
+        PreloadedImageListNode *node = m_start;
+        while (node != NULL)
+        {
+            PreloadedImageListNode *nextNode = node->next;
+            if (!node->isLoaded)
+            {
+                MAPUnmapPreloadedPEFile(node->lpMappedImage, node->virtualSize);
+            }
+
+            if (node->wszPath != NULL)
+            {
+                free(node->wszPath);
+            }
+            free(node->szPath);
+            free(node);
+            node = nextNode;
+        }
+
+        return true;
+    }
+
+    PreloadedImageListNode *find(LPCWSTR wszPath)
+    {
+        PreloadedImageListNode *node = m_start;
+        while (node != NULL)
+        {
+            if (node->wszPath == NULL)
+            {
+                // MultiByteToWideChar requires PAL initialization
+                if (!PALIsInitialized())
+                    break;
+
+                int length = MultiByteToWideChar(CP_UTF8, 0, node->szPath, -1, NULL, 0);
+                _ASSERTE(length != 0);
+
+                node->wszPath = (LPWSTR) InternalMalloc(length * sizeof(WCHAR));
+                _ASSERTE(node->wszPath != NULL);
+
+                length = MultiByteToWideChar(CP_UTF8, 0, node->szPath, -1, node->wszPath, length);
+                _ASSERTE(length != 0);
+            }
+
+            if (PAL_wcscmp(node->wszPath, wszPath) == 0)
+            {
+                return node;
+            }
+
+            node = node->next;
+        }
+
+        return NULL;
+    }
+};
+
 /* static variables ***********************************************************/
 
+PreloadedImageList preloadedAssemblies;
+
 /* critical section that regulates access to the module list */
 CRITICAL_SECTION module_critsec;
 
@@ -779,6 +869,8 @@ PAL_UnregisterModule(
 
 Parameters:
     IN hFile - file to map
+    IN wszPath  - File path
+    OUT isPreloaded - Flag whether pefile was preloaded
 
 Return value:
     non-NULL - the base address of the mapped image
@@ -786,11 +878,14 @@ Return value:
 --*/
 PVOID
 PALAPI
-PAL_LOADLoadPEFile(HANDLE hFile)
+PAL_LOADLoadPEFile(HANDLE hFile, LPCWSTR wszPath, BOOL *isPreloaded)
 {
     ENTRY("PAL_LOADLoadPEFile (hFile=%p)\n", hFile);
 
-    void * loadedBase = MAPMapPEFile(hFile);
+    void * preloadedBase = LOADFindPreloadedPEFile(wszPath);
+    *isPreloaded = preloadedBase != NULL;
+
+    void * loadedBase = MAPMapPEFile(hFile, NULL, 0, preloadedBase);
 
 #ifdef _DEBUG
     if (loadedBase != nullptr)
@@ -801,8 +896,11 @@ PAL_LOADLoadPEFile(HANDLE hFile)
             if (strlen(envVar) > 0)
             {
                 TRACE("Forcing failure of PE file map, and retry\n");
-                PAL_LOADUnloadPEFile(loadedBase); // unload it
-                loadedBase = MAPMapPEFile(hFile); // load it again
+                if (preloadedBase == NULL)
+                {
+                    PAL_LOADUnloadPEFile(loadedBase); // unload it
+                }
+                loadedBase = MAPMapPEFile(hFile, NULL, 0, preloadedBase); // load it again
             }
 
             free(envVar);
@@ -815,6 +913,60 @@ PAL_LOADLoadPEFile(HANDLE hFile)
 }
 
 /*++
+Function:
+  PAL_LOADPreloadPEFile
+
+Abstract
+  Preloads a PE file into memory.  Properly maps all of the sections in the PE file.  Returns a pointer to the
+  loaded base.
+
+Parameters:
+    IN szPath    - path of file to load
+
+Return value:
+    A valid base address if successful.
+    0 if failure
+--*/
+void *
+PALAPI
+PAL_LOADPreloadPEFile(LPCSTR szPath)
+{
+    size_t size;
+    void *addr = MAPMapPEFile(NULL, szPath, &size, NULL);
+
+    if (addr == NULL)
+    {
+        return NULL;
+    }
+
+    if (!MAPApplyBaseRelocationsPreloadedPEFile(addr, size))
+    {
+        MAPUnmapPreloadedPEFile(addr, size);
+        return NULL;
+    }
+
+    preloadedAssemblies.addNew(szPath, addr, size);
+
+    return addr;
+}
+
+/*++
+    PAL_LOADUnloadPreloadedPEFiles
+
+    Unload all PE files that were loaded by PAL_LOADPreloadPEFile().
+
+Return value:
+    TRUE - success
+    FALSE - failure
+--*/
+BOOL
+PALAPI
+PAL_LOADUnloadPreloadedPEFiles()
+{
+    return preloadedAssemblies.freeAll();
+}
+
+/*++
     PAL_LOADUnloadPEFile
 
     Unload a PE file that was loaded by PAL_LOADLoadPEFile().
@@ -921,6 +1073,34 @@ PAL_GetLoadLibraryError()
 /* Internal PAL functions *****************************************************/
 
 /*++
+    LOADFindPreloadedPEFile -
+
+    Find image in list of preloaded
+
+Parameters:
+    IN wszPath - path to mapped file
+
+Return value:
+    non-NULL - the base address of the mapped image
+    NULL - image is not in the list of preloaded.
+--*/
+
+void * LOADFindPreloadedPEFile(LPCWSTR wszPath)
+{
+    void * preloadedBase = NULL;
+
+    PreloadedImageListNode *node = preloadedAssemblies.find(wszPath);
+    if (node != NULL)
+    {
+        preloadedBase = node->lpMappedImage;
+        _ASSERTE(!node->isLoaded);
+        node->isLoaded = true;
+    }
+
+    return preloadedBase;
+}
+
+/*++
 Function :
     LOADInitializeModules
 
index a8d8af2..6caf5e1 100644 (file)
@@ -2139,6 +2139,25 @@ MAPRecordMapping(
     return palError;
 }
 
+// calculate offset adjustment
+static off_t MAPcalcAdj(off_t offset)
+{
+    return offset & (GetVirtualPageSize() - 1);
+}
+
+static PAL_ERROR
+MAPRecordMapping(
+    IPalObject *pMappingObject,
+    void *pPEBaseAddress,
+    void *addr,
+    size_t len,
+    int prot,
+    off_t offset
+    )
+{
+    return MAPRecordMapping(pMappingObject, pPEBaseAddress, static_cast<char *>(addr) - MAPcalcAdj(offset), len, prot);
+}
+
 // 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
@@ -2206,27 +2225,374 @@ MAPmmapAndRecord(
 }
 
 /*++
+    MAPUnmapPreloadedPEFile -
+
+    Unmap a PE file
+
+Parameters:
+    IN addr - address of mapped file
+    IN size - virtual size
+
+Return value:
+    returns TRUE if successful, FALSE otherwise
+--*/
+
+BOOL MAPUnmapPreloadedPEFile(void *addr, size_t size)
+{
+    if (munmap(addr, size) == -1)
+    {
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+// Do the actual mprotect call with aligned lpAddress
+static bool MAPProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, SIZE_T dwPageSize)
+{
+    UINT_PTR StartBoundary = (UINT_PTR)ALIGN_DOWN(lpAddress, dwPageSize);
+    SIZE_T MemSize = ALIGN_UP((UINT_PTR)lpAddress + dwSize, dwPageSize) - StartBoundary;
+    INT nProtect = W32toUnixAccessControl(flNewProtect);
+    if (nProtect == 0)
+    {
+        return false;
+    }
+
+    return mprotect((void *)StartBoundary, MemSize, nProtect) == 0;
+}
+
+static IMAGE_SECTION_HEADER *RvaToSection(void *base, DWORD rva)
+{
+    IMAGE_SECTION_HEADER *pSection = NULL;
+    IMAGE_NT_HEADERS *imageNTHeaders = (IMAGE_NT_HEADERS *) ((uintptr_t)base + VAL32(((IMAGE_DOS_HEADER *) base)->e_lfanew));
+    IMAGE_SECTION_HEADER *firstSection = (IMAGE_SECTION_HEADER *)
+        ((uintptr_t)(imageNTHeaders) +
+        offsetof(IMAGE_NT_HEADERS, OptionalHeader) +
+        VAL16(imageNTHeaders->FileHeader.SizeOfOptionalHeader));
+
+    IMAGE_SECTION_HEADER *section = firstSection;
+    IMAGE_SECTION_HEADER *sectionEnd = section + VAL16(imageNTHeaders->FileHeader.NumberOfSections);
+
+    while (section < sectionEnd)
+    {
+        UINT value = (UINT)VAL32(section->Misc.VirtualSize);
+        UINT alignment = (UINT)VAL32(imageNTHeaders->OptionalHeader.SectionAlignment);
+        if (rva < (VAL32(section->VirtualAddress) + ALIGN_UP(value, alignment)))
+        {
+            if (rva < VAL32(section->VirtualAddress))
+            {
+                break;
+            }
+            else
+            {
+                pSection = section;
+                break;
+            }
+        }
+
+        section++;
+    }
+
+    return pSection;
+}
+
+static DWORD SectionCharacteristicsToPageProtection(UINT characteristics)
+{
+    DWORD pageProtection;
+
+    if ((characteristics & VAL32(IMAGE_SCN_MEM_WRITE)) != 0)
+    {
+        if ((characteristics & VAL32(IMAGE_SCN_MEM_EXECUTE)) != 0)
+        {
+            pageProtection = PAGE_EXECUTE_READWRITE;
+        }
+        else
+        {
+            pageProtection = PAGE_READWRITE;
+        }
+    }
+    else
+    {
+        if ((characteristics & VAL32(IMAGE_SCN_MEM_EXECUTE)) != 0)
+        {
+            pageProtection = PAGE_EXECUTE_READ;
+        }
+        else
+        {
+            pageProtection = PAGE_READONLY;
+        }
+    }
+
+    return pageProtection;
+}
+
+/*++
+    MAPApplyBaseRelocationsPreloadedPEFile -
+
+    Apply base relocations to preloaded image
+
+Parameters:
+    IN mappedImage - base address of preloaded image
+    IN virtualSize - virtual size of preloaded image
+
+Return value:
+    true - if relocations were applied successfully
+    false - otherwise
+--*/
+
+bool
+MAPApplyBaseRelocationsPreloadedPEFile(void *mappedImage, size_t virtualSize)
+{
+    void *base = mappedImage;
+    void *preferredBase;
+    IMAGE_NT_HEADERS *imageNTHeaders = (IMAGE_NT_HEADERS *) ((uintptr_t)base + VAL32(((IMAGE_DOS_HEADER *) base)->e_lfanew));
+    bool has32BitNTHeaders = imageNTHeaders->OptionalHeader.Magic == VAL16(IMAGE_NT_OPTIONAL_HDR32_MAGIC);
+    if (has32BitNTHeaders)
+    {
+        preferredBase = (void *) (SIZE_T) VAL32(((IMAGE_NT_HEADERS32 *)imageNTHeaders)->OptionalHeader.ImageBase);
+    }
+    else
+    {
+        preferredBase = (void *) (SIZE_T) VAL64(((IMAGE_NT_HEADERS64 *)imageNTHeaders)->OptionalHeader.ImageBase);
+    }
+
+    SSIZE_T delta = (SIZE_T) base - (SIZE_T) preferredBase;
+
+    // Nothing to do - image is loaded at preferred base
+    if (delta == 0)
+        return true;
+
+    IMAGE_DATA_DIRECTORY *pDir;
+    if (has32BitNTHeaders)
+    {
+        pDir = (IMAGE_DATA_DIRECTORY *) ((uintptr_t)((IMAGE_NT_HEADERS32 *)imageNTHeaders) +
+            offsetof(IMAGE_NT_HEADERS32, OptionalHeader.DataDirectory) +
+            IMAGE_DIRECTORY_ENTRY_BASERELOC * sizeof(IMAGE_DATA_DIRECTORY));
+    }
+    else
+    {
+        pDir = (IMAGE_DATA_DIRECTORY *) ((uintptr_t)((IMAGE_NT_HEADERS64 *)imageNTHeaders) +
+            offsetof(IMAGE_NT_HEADERS64, OptionalHeader.DataDirectory) +
+            IMAGE_DIRECTORY_ENTRY_BASERELOC * sizeof(IMAGE_DATA_DIRECTORY));
+    }
+
+    UINT32 dirSize = VAL32(pDir->Size);
+    DWORD rva = VAL32(pDir->VirtualAddress);
+    uintptr_t dir = (uintptr_t) (rva == 0 ? NULL : (uintptr_t)base + rva);
+
+    // Minimize number of calls to VirtualProtect by keeping a whole section unprotected at a time.
+    BYTE * pWriteableRegion = NULL;
+    SIZE_T cbWriteableRegion = 0;
+    DWORD dwOldProtection = 0;
+
+    const SIZE_T cbPageSize = 4096;
+
+    UINT32 dirPos = 0;
+
+    while (dirPos < dirSize)
+    {
+        IMAGE_BASE_RELOCATION *r = (IMAGE_BASE_RELOCATION *)(dir + dirPos);
+
+        UINT32 fixupsSize = VAL32(r->SizeOfBlock);
+
+        USHORT *fixups = (USHORT *) (r + 1);
+
+        if (fixupsSize <= sizeof(IMAGE_BASE_RELOCATION)
+            || (fixupsSize - sizeof(IMAGE_BASE_RELOCATION)) % 2 != 0)
+        {
+            return false;
+        }
+
+        UINT32 fixupsCount = (fixupsSize - sizeof(IMAGE_BASE_RELOCATION)) / 2;
+
+        if ((BYTE *)(fixups + fixupsCount) > (BYTE *)(dir + dirSize))
+        {
+            return false;
+        }
+
+        DWORD rva = VAL32(r->VirtualAddress);
+
+        BYTE * pageAddress = (BYTE *) base + rva;
+
+        // Check whether the page is outside the unprotected region
+        if ((SIZE_T)(pageAddress - pWriteableRegion) >= cbWriteableRegion)
+        {
+            // Restore the protection
+            if (dwOldProtection != 0)
+            {
+                BOOL bExecRegion = (dwOldProtection & (PAGE_EXECUTE | PAGE_EXECUTE_READ |
+                    PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY)) != 0;
+
+                if (!MAPProtect(pWriteableRegion, cbWriteableRegion, dwOldProtection, cbPageSize))
+                {
+                    return false;
+                }
+
+                dwOldProtection = 0;
+            }
+
+            USHORT fixup = VAL16(fixups[0]);
+
+            IMAGE_SECTION_HEADER *pSection = RvaToSection(base, rva + (fixup & 0xfff));
+            if (pSection == NULL)
+            {
+                return false;
+            }
+
+            DWORD rvaSect = VAL32(pSection->VirtualAddress);
+            pWriteableRegion = (BYTE*) (rvaSect == 0 ? NULL : (uintptr_t)base + rvaSect);
+            cbWriteableRegion = VAL32(pSection->SizeOfRawData);
+
+            // Unprotect the section if it is not writable
+            if (((pSection->Characteristics & VAL32(IMAGE_SCN_MEM_WRITE)) == 0))
+            {
+                DWORD dwNewProtection = PAGE_READWRITE;
+                if (((pSection->Characteristics & VAL32(IMAGE_SCN_MEM_EXECUTE)) != 0))
+                {
+                    // On SELinux, we cannot change protection that doesn't have execute access rights
+                    // to one that has it, so we need to set the protection to RWX instead of RW
+                    dwNewProtection = PAGE_EXECUTE_READWRITE;
+                }
+
+                if (!MAPProtect(pWriteableRegion, cbWriteableRegion, dwNewProtection, cbPageSize))
+                {
+                    return false;
+                }
+
+                DWORD pageProtection;
+                if ((pSection->Characteristics & VAL32(IMAGE_SCN_MEM_READ)) == 0)
+                {
+                    return false;
+                }
+
+                dwOldProtection = SectionCharacteristicsToPageProtection(pSection->Characteristics);
+            }
+        }
+
+        for (UINT32 fixupIndex = 0; fixupIndex < fixupsCount; fixupIndex++)
+        {
+            USHORT fixup = VAL16(fixups[fixupIndex]);
+
+            BYTE * address = pageAddress + (fixup & 0xfff);
+
+#ifdef BIT64
+#define IMAGE_REL_BASED_PTR IMAGE_REL_BASED_DIR64
+#else
+#define IMAGE_REL_BASED_PTR IMAGE_REL_BASED_HIGHLOW
+#endif
+            switch (fixup>>12)
+            {
+                case IMAGE_REL_BASED_PTR:
+                {
+                    *(uintptr_t *)address += delta;
+                    break;
+                }
+                case IMAGE_REL_BASED_THUMB_MOV32:
+                {
+                    // Make sure we are decoding movw/movt sequence
+                    UINT16 *p = (UINT16 *)address;
+                    if ((*(p+0) & 0xFBF0) != 0xF240 || (*(p+2) & 0xFBF0) != 0xF2C0)
+                    {
+                        return false;
+                    }
+
+#define GET_THUMB2_IMM16(p) ((((p)[0] << 12) & 0xf000) | \
+                           (((p)[0] <<  1) & 0x0800) | \
+                           (((p)[1] >>  4) & 0x0700) | \
+                           (((p)[1] >>  0) & 0x00ff))
+#define PUT_THUMB2_IMM16(p,imm16) \
+    { \
+        USHORT Opcode0 = (p)[0]; \
+        USHORT Opcode1 = (p)[1]; \
+        Opcode0 &= ~((0xf000 >> 12) | (0x0800 >> 1)); \
+        Opcode1 &= ~((0x0700 <<  4) | (0x00ff << 0)); \
+        Opcode0 |= ((imm16) & 0xf000) >> 12; \
+        Opcode0 |= ((imm16) & 0x0800) >>  1; \
+        Opcode1 |= ((imm16) & 0x0700) <<  4; \
+        Opcode1 |= ((imm16) & 0x00ff) <<  0; \
+        (p)[0] = Opcode0; \
+        (p)[1] = Opcode1; \
+    }
+
+                    UINT32 imm32 = (UINT32)GET_THUMB2_IMM16(p) + ((UINT32)GET_THUMB2_IMM16(p + 2) << 16) + delta;
+
+                    PUT_THUMB2_IMM16(p, (UINT16)imm32);
+                    PUT_THUMB2_IMM16(p + 2, (UINT16)(imm32 >> 16));
+
+#undef GET_THUMB2_IMM16
+#undef PUT_THUMB2_IMM16
+
+                    break;
+                }
+                case IMAGE_REL_BASED_ABSOLUTE:
+                {
+                    //no adjustment
+                    break;
+                }
+
+                default:
+                {
+                    return false;
+                }
+            }
+#undef IMAGE_REL_BASED_PTR
+        }
+
+        dirPos += fixupsSize;
+    }
+    if (dirSize != dirPos)
+    {
+        return false;
+    }
+
+    if (dwOldProtection != 0)
+    {
+        if (!MAPProtect(pWriteableRegion, cbWriteableRegion, dwOldProtection, cbPageSize))
+        {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+/*++
     MAPMapPEFile -
 
     Map a PE format file into memory like Windows LoadLibrary() would do.
     Doesn't apply base relocations if the function is relocated.
 
+    There are two scenarios:
+
+    - image could be preloaded and then MAPMapPEFile is called for it
+    - image is loaded with MAPMapPEFile
+
+    In the first scenario, hFile and lpPreloadedBase are supposed to be NULL, and szPath and size - non-NULL.
+    In the second scenario, hFile and lpPreloadedBase are supposed to be non-NULL, and szPath and size - NULL.
+
+    See coreclr_preload_assembly for further details.
+
 Parameters:
     IN hFile - file to map
+    IN szPath - path to mapped file
+    OUT size - mapped virtual size
+    IN lpPreloadedBase - previously loaded base
 
 Return value:
     non-NULL - the base address of the mapped image
     NULL - error, with last error set.
 --*/
 
-void * MAPMapPEFile(HANDLE hFile)
+void * MAPMapPEFile(HANDLE hFile, LPCSTR szPath, SIZE_T *size, LPVOID lpPreloadedBase)
 {
     PAL_ERROR palError = 0;
     IPalObject *pFileObject = NULL;
     IDataLock *pLocalDataLock = NULL;
     CFileProcessLocalData *pLocalData = NULL;
-    CPalThread *pThread = InternalGetCurrentThread();
-    void * loadedBase = NULL;
+    CPalThread *pThread;
+
+    bool doPreload = hFile == NULL;
+
     IMAGE_DOS_HEADER * loadedHeader = NULL;
     void * retval;
 #if _DEBUG
@@ -2234,43 +2600,73 @@ void * MAPMapPEFile(HANDLE hFile)
     char* envVar;
 #endif
 
-    ENTRY("MAPMapPEFile (hFile=%p)\n", hFile);
+    size_t headerSize;
 
-    //Step 0: Verify values, find internal pal data structures.
-    if (INVALID_HANDLE_VALUE == hFile)
+    void * loadedBase = lpPreloadedBase;
+    bool isPreloaded = loadedBase != NULL;
+    bool arePreloadParametersCorrect = lpPreloadedBase == NULL && hFile == NULL && szPath != NULL && size != NULL;
+    bool areNonPreloadParametersCorrect = hFile != NULL && szPath == NULL && size == NULL;
+    bool areParametersCorrect = (doPreload && arePreloadParametersCorrect)
+                                || (!doPreload && areNonPreloadParametersCorrect);
+    if (!areParametersCorrect)
     {
-        ERROR_(LOADER)( "Invalid file handle\n" );
-        palError = ERROR_INVALID_HANDLE;
+        palError = ERROR_INVALID_PARAMETER;
         goto done;
     }
 
-    palError = g_pObjectManager->ReferenceObjectByHandle(
-            pThread,
-            hFile,
-            &aotFile,
-            GENERIC_READ,
-            &pFileObject
-            );
-    if (NO_ERROR != palError)
+    int fd;
+
+    if (doPreload)
     {
-        ERROR_(LOADER)( "ReferenceObjectByHandle failed\n" );
-        goto done;
-    }
+        fd = InternalOpen(szPath, O_RDONLY);
 
-    palError = pFileObject->GetProcessLocalData(
-            pThread,
-            ReadLock,
-            &pLocalDataLock,
-            reinterpret_cast<void**>(&pLocalData)
-            );
-    if (NO_ERROR != palError)
+        if (fd == -1)
+        {
+            palError = ERROR_INVALID_NAME;
+            goto done;
+        }
+    }
+    else
     {
-        ERROR_(LOADER)( "GetProcessLocalData failed\n" );
-        goto done;
+        pThread = InternalGetCurrentThread();
+
+        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;
+        }
+
+        fd = pLocalData->unix_fd;
     }
 
-    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;
@@ -2317,21 +2713,24 @@ void * MAPMapPEFile(HANDLE hFile)
     }
 
 #if _DEBUG
-    envVar = EnvironGetenv("PAL_ForceRelocs");
-    if (envVar)
+    if (!doPreload && !isPreloaded)
     {
-        if (strlen(envVar) > 0)
+        envVar = EnvironGetenv("PAL_ForceRelocs");
+        if (envVar)
         {
-            forceRelocs = true;
-            TRACE_(LOADER)("Forcing rebase of image\n");
-        }
+            if (strlen(envVar) > 0)
+            {
+                forceRelocs = true;
+                TRACE_(LOADER)("Forcing rebase of image\n");
+            }
 
-        free(envVar);
+            free(envVar);
+        }
     }
 
     void * pForceRelocBase;
     pForceRelocBase = NULL;
-    if (forceRelocs)
+    if (!doPreload && !isPreloaded && 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.
@@ -2356,7 +2755,10 @@ void * MAPMapPEFile(HANDLE hFile)
     // 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 (!doPreload)
+    {
+        InternalEnterCriticalSection(pThread, &mapping_critsec);
+    }
 
 #ifdef BIT64
     // First try to reserve virtual memory using ExecutableAllocator. This allows all PE images to be
@@ -2364,10 +2766,13 @@ void * MAPMapPEFile(HANDLE hFile)
     // more efficient code (by avoiding usage of jump stubs). Alignment to a 64 KB granularity should
     // not be necessary (alignment to page size should be sufficient), but see
     // ExecutableMemoryAllocator::AllocateMemory() for the reason why it is done.
-    loadedBase = ReserveMemoryFromExecutableAllocator(pThread, ALIGN_UP(virtualSize, VIRTUAL_64KB));
+    if (!doPreload && loadedBase == NULL)
+    {
+        loadedBase = ReserveMemoryFromExecutableAllocator(pThread, ALIGN_UP(virtualSize, VIRTUAL_64KB));
+    }
 #endif // BIT64
 
-    if (loadedBase == NULL)
+    if (doPreload || loadedBase == NULL)
     {
         void *usedBaseAddr = NULL;
 #ifdef FEATURE_ENABLE_NO_ADDRESS_SPACE_RANDOMIZATION
@@ -2399,39 +2804,77 @@ void * MAPMapPEFile(HANDLE hFile)
     // 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, GetVirtualPageSize()); // 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
+    if (!doPreload && !isPreloaded)
     {
-        TRACE_(LOADER)("Image loaded at preferred base %p\n", loadedBase);
+        if (forceRelocs)
+        {
+            _ASSERTE(((SIZE_T)loadedBase) != preferredBase);
+            munmap(pForceRelocBase, GetVirtualPageSize()); // 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 = GetVirtualPageSize(); // if there are lots of sections, this could be wrong
+    // use here getpagesize because GetVirtualPageSize needs PAL
+    headerSize = getpagesize(); // if there are lots of sections, this could be wrong
+    _ASSERTE(GetVirtualPageSize() == getpagesize());
 
     //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)
+    if (!doPreload)
     {
-        ERROR_(LOADER)( "mmap of PE header failed\n" );
-        goto doneReleaseMappingCriticalSection;
+        if (!isPreloaded)
+        {
+            palError = MAPmmapAndRecord(pFileObject, loadedBase,
+                            loadedBase, headerSize, PROT_READ, MAP_FILE|MAP_PRIVATE|MAP_FIXED, fd, 0,
+                            (void**)&loadedHeader);
+        }
+        else
+        {
+            palError = MAPRecordMapping(pFileObject,
+                            loadedBase,
+                            loadedBase,
+                            headerSize,
+                            PROT_READ,
+                            0);
+            loadedHeader = (IMAGE_DOS_HEADER *)(((char *)loadedBase) - MAPcalcAdj(0));
+        }
+
+        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
+    }
+    else
+    {
+        loadedHeader = (IMAGE_DOS_HEADER*)mmap(loadedBase, headerSize, PROT_READ, MAP_FILE|MAP_PRIVATE|MAP_FIXED, fd, 0);
+
+        if (loadedHeader == MAP_FAILED)
+        {
+            palError = ERROR_INVALID_PARAMETER;
+            goto doneReleaseMappingCriticalSection;
+        }
+        if (loadedHeader != loadedBase)
+        {
+            munmap(loadedHeader, headerSize);
+            palError = ERROR_INVALID_PARAMETER;
+            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
@@ -2464,7 +2907,9 @@ void * MAPMapPEFile(HANDLE hFile)
         IMAGE_SECTION_HEADER &currentHeader = firstSection[i];
 
         void* sectionBase = (char*)loadedBase + currentHeader.VirtualAddress;
-        void* sectionBaseAligned = ALIGN_DOWN(sectionBase, GetVirtualPageSize());
+        void* sectionBaseAligned;
+
+        sectionBaseAligned = (void*) ALIGN_DOWN((size_t)sectionBase, getpagesize());
 
         // Validate the section header
         if (   (sectionBase < loadedBase)                                                           // Did computing the section base overflow?
@@ -2484,18 +2929,21 @@ void * MAPMapPEFile(HANDLE hFile)
             goto doneReleaseMappingCriticalSection;
         }
 
-        // Is there space between the previous section and this one? If so, add a PROT_NONE mapping to cover it.
-        if (prevSectionEnd < sectionBaseAligned)
+        if (!doPreload)
         {
-            palError = MAPRecordMapping(pFileObject,
-                            loadedBase,
-                            prevSectionEnd,
-                            (char*)sectionBaseAligned - (char*)prevSectionEnd,
-                            PROT_NONE);
-            if (NO_ERROR != palError)
+            // Is there space between the previous section and this one? If so, add a PROT_NONE mapping to cover it.
+            if (prevSectionEnd < sectionBaseAligned)
             {
-                ERROR_(LOADER)( "recording gap section before section %d failed\n", i );
-                goto doneReleaseMappingCriticalSection;
+                palError = MAPRecordMapping(pFileObject,
+                                loadedBase,
+                                prevSectionEnd,
+                                (char*)sectionBaseAligned - (char*)prevSectionEnd,
+                                PROT_NONE);
+                if (NO_ERROR != palError)
+                {
+                    ERROR_(LOADER)( "recording gap section before section %d failed\n", i );
+                    goto doneReleaseMappingCriticalSection;
+                }
             }
         }
 
@@ -2509,21 +2957,55 @@ void * MAPMapPEFile(HANDLE hFile)
         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)
+        if (!doPreload)
         {
-            ERROR_(LOADER)( "mmap of section %d failed\n", i );
-            goto doneReleaseMappingCriticalSection;
+            if (!isPreloaded)
+            {
+                palError = MAPmmapAndRecord(pFileObject, loadedBase,
+                                sectionBase,
+                                currentHeader.SizeOfRawData,
+                                prot,
+                                MAP_FILE|MAP_PRIVATE|MAP_FIXED,
+                                fd,
+                                currentHeader.PointerToRawData,
+                                &sectionData);
+            }
+            else
+            {
+                palError = MAPRecordMapping(pFileObject,
+                                loadedBase,
+                                sectionBase,
+                                currentHeader.SizeOfRawData,
+                                prot,
+                                currentHeader.PointerToRawData);
+                sectionData = static_cast<char *>(sectionBase) - MAPcalcAdj(currentHeader.PointerToRawData);
+            }
+            if (NO_ERROR != palError)
+            {
+                ERROR_(LOADER)( "mmap of section %d failed\n", i );
+                goto doneReleaseMappingCriticalSection;
+            }
+        }
+        else
+        {
+            off_t adjust = currentHeader.PointerToRawData & (getpagesize() - 1);
+            sectionData = mmap(static_cast<char *>(sectionBase) - adjust, currentHeader.SizeOfRawData + adjust,
+                               prot, MAP_FILE|MAP_PRIVATE|MAP_FIXED, fd, currentHeader.PointerToRawData - adjust);
+            if (sectionData == MAP_FAILED)
+            {
+                palError = ERROR_INVALID_PARAMETER;
+                goto doneReleaseMappingCriticalSection;
+            }
+            if (sectionData != (static_cast<char *>(sectionBase) - adjust))
+            {
+                munmap(sectionData, currentHeader.SizeOfRawData + adjust);
+                palError = ERROR_INVALID_PARAMETER;
+                goto doneReleaseMappingCriticalSection;
+            }
         }
 
 #if _DEBUG
+        if (!doPreload)
         {
             // Ensure null termination of section name (which is allowed to not be null terminated if exactly 8 characters long)
             char sectionName[9];
@@ -2534,31 +3016,41 @@ void * MAPMapPEFile(HANDLE hFile)
         }
 #endif // _DEBUG
 
-        prevSectionEnd = ALIGN_UP((char*)sectionBase + currentHeader.SizeOfRawData, GetVirtualPageSize()); // round up to page boundary
+        prevSectionEnd = (void*) ALIGN_UP((size_t)((char*)sectionBase + currentHeader.SizeOfRawData), getpagesize()); // 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 (prevSectionEnd < imageEnd)
+    if (!doPreload)
     {
-        palError = MAPRecordMapping(pFileObject,
-                        loadedBase,
-                        prevSectionEnd,
-                        (char*)imageEnd - (char*)prevSectionEnd,
-                        PROT_NONE);
-        if (NO_ERROR != palError)
+        // 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 (prevSectionEnd < imageEnd)
         {
-            ERROR_(LOADER)( "recording end of image gap section failed\n" );
-            goto doneReleaseMappingCriticalSection;
+            palError = MAPRecordMapping(pFileObject,
+                            loadedBase,
+                            prevSectionEnd,
+                            (char*)imageEnd - (char*)prevSectionEnd,
+                            PROT_NONE);
+            if (NO_ERROR != palError)
+            {
+                ERROR_(LOADER)( "recording end of image gap section failed\n" );
+                goto doneReleaseMappingCriticalSection;
+            }
         }
     }
+    else
+    {
+        *size = virtualSize;
+    }
 
     palError = ERROR_SUCCESS;
 
 doneReleaseMappingCriticalSection:
 
-    InternalLeaveCriticalSection(pThread, &mapping_critsec);
+    if (!doPreload)
+    {
+        InternalLeaveCriticalSection(pThread, &mapping_critsec);
+    }
 
 done:
 
@@ -2583,10 +3075,14 @@ done:
         LOGEXIT("MAPMapPEFile error: %d\n", palError);
 
         // If we had an error, and had mapped anything, we need to unmap it
-        if (loadedBase != NULL)
+        if (!doPreload && loadedBase != NULL && !isPreloaded)
         {
             MAPUnmapPEFile(loadedBase);
         }
+        else if (doPreload && loadedBase != NULL)
+        {
+            munmap(loadedBase, virtualSize);
+        }
     }
     return retval;
 }
index be91820..96ade5c 100644 (file)
@@ -368,6 +368,47 @@ void SetCommandLineArgs(LPCWSTR pwzAssemblyPath, int argc, LPCWSTR* argv)
     GCPROTECT_END();
 }
 
+/*
+ * This method allows to preload assembly to memory and apply relocations before initialization of CoreCLR.
+ * Assemblies are stored in the list, which is scanned during general loading after CoreCLR initialization.
+ * If path is found in the list, preloaded memory is used.
+ * If CoreCLR is already initialized, it returns E_FAIL.
+ */
+HRESULT CorHost2::PreloadAssembly(LPCSTR szPath)
+{
+#ifndef FEATURE_PAL
+    // Preloading is not available without FEATURE_PAL, as it requires some PAL changes
+    return E_FAIL;
+#else
+    if (g_fEEStarted)
+    {
+        return HOST_E_INVALIDOPERATION;
+    }
+
+    void *preloadedImage = PAL_LOADPreloadPEFile(szPath);
+    if (preloadedImage == NULL)
+    {
+        return E_FAIL;
+    }
+
+    return S_OK;
+#endif
+}
+
+HRESULT CorHost2::UnloadPreloadedAssemblies()
+{
+#ifndef FEATURE_PAL
+    // Preloading is not available without FEATURE_PAL, as it requires some PAL changes
+    return E_FAIL;
+#else
+    if (!PAL_LOADUnloadPreloadedPEFiles())
+    {
+        return E_FAIL;
+    }
+    return S_OK;
+#endif
+}
+
 HRESULT CorHost2::ExecuteAssembly(DWORD dwAppDomainId,
                                       LPCWSTR pwzAssemblyPath,
                                       int argc,
index 906e443..1d08558 100644 (file)
@@ -124,7 +124,9 @@ DWORD SectionCharacteristicsToPageProtection(UINT characteristics)
 
 //To force base relocation on Vista (which uses ASLR), unmask IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
 //(0x40) for OptionalHeader.DllCharacteristics
-void PEImageLayout::ApplyBaseRelocations()
+//
+//NOTE: MAPApplyBaseRelocationsPreloadedPEFile should match this function exactly!
+void PEImageLayout::ApplyBaseRelocations(BOOL isRelocated)
 {
     STANDARD_VM_CONTRACT;
 
@@ -177,48 +179,51 @@ void PEImageLayout::ApplyBaseRelocations()
 
         BYTE * pageAddress = (BYTE *)GetBase() + rva;
 
-        // Check whether the page is outside the unprotected region
-        if ((SIZE_T)(pageAddress - pWriteableRegion) >= cbWriteableRegion)
+        if (!isRelocated)
         {
-            // Restore the protection
-            if (dwOldProtection != 0)
+            // Check whether the page is outside the unprotected region
+            if ((SIZE_T)(pageAddress - pWriteableRegion) >= cbWriteableRegion)
             {
-                BOOL bExecRegion = (dwOldProtection & (PAGE_EXECUTE | PAGE_EXECUTE_READ |
-                    PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY)) != 0;
+                // Restore the protection
+                if (dwOldProtection != 0)
+                {
+                    BOOL bExecRegion = (dwOldProtection & (PAGE_EXECUTE | PAGE_EXECUTE_READ |
+                        PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY)) != 0;
 
-                if (!ClrVirtualProtect(pWriteableRegion, cbWriteableRegion,
-                                       dwOldProtection, &dwOldProtection))
-                    ThrowLastError();
+                    if (!ClrVirtualProtect(pWriteableRegion, cbWriteableRegion,
+                                           dwOldProtection, &dwOldProtection))
+                        ThrowLastError();
 
-                dwOldProtection = 0;
-            }
+                    dwOldProtection = 0;
+                }
 
-            USHORT fixup = VAL16(fixups[0]);
+                USHORT fixup = VAL16(fixups[0]);
 
-            IMAGE_SECTION_HEADER *pSection = RvaToSection(rva + (fixup & 0xfff));
-            PREFIX_ASSUME(pSection != NULL);
+                IMAGE_SECTION_HEADER *pSection = RvaToSection(rva + (fixup & 0xfff));
+                PREFIX_ASSUME(pSection != NULL);
 
-            pWriteableRegion = (BYTE*)GetRvaData(VAL32(pSection->VirtualAddress));
-            cbWriteableRegion = VAL32(pSection->SizeOfRawData);
+                pWriteableRegion = (BYTE*)GetRvaData(VAL32(pSection->VirtualAddress));
+                cbWriteableRegion = VAL32(pSection->SizeOfRawData);
 
-            // Unprotect the section if it is not writable
-            if (((pSection->Characteristics & VAL32(IMAGE_SCN_MEM_WRITE)) == 0))
-            {
-                DWORD dwNewProtection = PAGE_READWRITE;
-#if defined(FEATURE_PAL) && !defined(CROSSGEN_COMPILE)
-                if (((pSection->Characteristics & VAL32(IMAGE_SCN_MEM_EXECUTE)) != 0))
+                // Unprotect the section if it is not writable
+                if (((pSection->Characteristics & VAL32(IMAGE_SCN_MEM_WRITE)) == 0))
                 {
-                    // On SELinux, we cannot change protection that doesn't have execute access rights
-                    // to one that has it, so we need to set the protection to RWX instead of RW
-                    dwNewProtection = PAGE_EXECUTE_READWRITE;
-                }
+                    DWORD dwNewProtection = PAGE_READWRITE;
+#if defined(FEATURE_PAL) && !defined(CROSSGEN_COMPILE)
+                    if (((pSection->Characteristics & VAL32(IMAGE_SCN_MEM_EXECUTE)) != 0))
+                    {
+                        // On SELinux, we cannot change protection that doesn't have execute access rights
+                        // to one that has it, so we need to set the protection to RWX instead of RW
+                        dwNewProtection = PAGE_EXECUTE_READWRITE;
+                    }
 #endif // FEATURE_PAL && !CROSSGEN_COMPILE
-                if (!ClrVirtualProtect(pWriteableRegion, cbWriteableRegion,
-                                       dwNewProtection, &dwOldProtection))
-                    ThrowLastError();
+                    if (!ClrVirtualProtect(pWriteableRegion, cbWriteableRegion,
+                                           dwNewProtection, &dwOldProtection))
+                        ThrowLastError();
 #ifdef FEATURE_PAL
-                dwOldProtection = SectionCharacteristicsToPageProtection(pSection->Characteristics);
+                    dwOldProtection = SectionCharacteristicsToPageProtection(pSection->Characteristics);
 #endif // FEATURE_PAL
+                }
             }
         }
 
@@ -232,13 +237,19 @@ void PEImageLayout::ApplyBaseRelocations()
             switch (fixup>>12)
             {
             case IMAGE_REL_BASED_PTR:
-                *(TADDR *)address += delta;
+                if (!isRelocated)
+                {
+                    *(TADDR *)address += delta;
+                }
                 pEndAddressToFlush = max(pEndAddressToFlush, address + sizeof(TADDR));
                 break;
 
 #ifdef _TARGET_ARM_
             case IMAGE_REL_BASED_THUMB_MOV32:
-                PutThumb2Mov32((UINT16 *)address, GetThumb2Mov32((UINT16 *)address) + (INT32)delta);
+                if (!isRelocated)
+                {
+                    PutThumb2Mov32((UINT16 *)address, GetThumb2Mov32((UINT16 *)address) + (INT32)delta);
+                }
                 pEndAddressToFlush = max(pEndAddressToFlush, address + 8);
                 break;
 #endif
@@ -275,15 +286,15 @@ void PEImageLayout::ApplyBaseRelocations()
     _ASSERTE(dirSize == dirPos);
 
 #ifndef CROSSGEN_COMPILE
-    if (dwOldProtection != 0)
+    if (!isRelocated)
     {
-        BOOL bExecRegion = (dwOldProtection & (PAGE_EXECUTE | PAGE_EXECUTE_READ |
-            PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY)) != 0;
-
-        // Restore the protection
-        if (!ClrVirtualProtect(pWriteableRegion, cbWriteableRegion,
-                               dwOldProtection, &dwOldProtection))
-            ThrowLastError();
+        if (dwOldProtection != 0)
+        {
+            // Restore the protection
+            if (!ClrVirtualProtect(pWriteableRegion, cbWriteableRegion,
+                                   dwOldProtection, &dwOldProtection))
+                ThrowLastError();
+        }
     }
 #endif // CROSSGEN_COMPILE
 
@@ -403,7 +414,7 @@ ConvertedImageLayout::ConvertedImageLayout(PEImageLayout* source)
 
 #ifdef CROSSGEN_COMPILE
     if (HasNativeHeader())
-        ApplyBaseRelocations();
+        ApplyBaseRelocations(FALSE);
 #endif
 }
 
@@ -487,7 +498,7 @@ MappedImageLayout::MappedImageLayout(HANDLE hFile, PEImage* pOwner)
             if (!IsNativeMachineFormat())
                 ThrowHR(COR_E_BADIMAGEFORMAT);
 
-            ApplyBaseRelocations();
+            ApplyBaseRelocations(FALSE);
         }
     }
     else
@@ -518,7 +529,8 @@ MappedImageLayout::MappedImageLayout(HANDLE hFile, PEImage* pOwner)
 #else //!FEATURE_PAL
 
 #ifndef CROSSGEN_COMPILE
-    m_LoadedFile = PAL_LOADLoadPEFile(hFile);
+    BOOL isPreloaded;
+    m_LoadedFile = PAL_LOADLoadPEFile(hFile, (LPCWSTR) GetPath(), &isPreloaded);
 
     if (m_LoadedFile == NULL)
     {
@@ -543,7 +555,7 @@ MappedImageLayout::MappedImageLayout(HANDLE hFile, PEImage* pOwner)
         if (!IsNativeMachineFormat())
             ThrowHR(COR_E_BADIMAGEFORMAT);
 
-        ApplyBaseRelocations();
+        ApplyBaseRelocations(isPreloaded);
         SetRelocated();
     }
 
index 17254bc..4e358b3 100644 (file)
@@ -68,7 +68,7 @@ public:
     ULONG Release();
     const SString& GetPath();
 
-    void ApplyBaseRelocations();
+    void ApplyBaseRelocations(BOOL isRelocated);
 
 public:
 #ifdef DACCESS_COMPILE