summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKonstantin Baladurin <k.baladurin@samsung.com>2019-07-05 19:36:28 +0300
committer이형주/Common Platform Lab(SR)/Staff Engineer/삼성전자 <leee.lee@samsung.com>2019-10-15 06:54:39 +0900
commit5647236eac21fb0e01a55a12289a66bc9f874f19 (patch)
treef81eede622d29edc955cf9fe3d5dcb670ec95675
parentc4c19b84a37bbc750e2cc13570dd096b13abfff0 (diff)
downloadcoreclr-5647236eac21fb0e01a55a12289a66bc9f874f19.tar.gz
coreclr-5647236eac21fb0e01a55a12289a66bc9f874f19.tar.bz2
coreclr-5647236eac21fb0e01a55a12289a66bc9f874f19.zip
-rw-r--r--src/coreclr/hosts/inc/coreclrhost.h3
-rw-r--r--src/dlls/mscoree/mscorwks_ntdef.src1
-rw-r--r--src/dlls/mscoree/mscorwks_unixexports.src1
-rw-r--r--src/dlls/mscoree/unixinterface.cpp42
-rw-r--r--src/inc/corhost.h5
-rw-r--r--src/pal/inc/pal.h34
-rw-r--r--src/pal/src/include/pal/dbgmsg.h19
-rw-r--r--src/pal/src/include/pal/map.hpp46
-rw-r--r--src/pal/src/include/pal/module.h48
-rw-r--r--src/pal/src/loader/module.cpp188
-rw-r--r--src/pal/src/map/map.cpp694
-rw-r--r--src/vm/corhost.cpp41
-rw-r--r--src/vm/peimagelayout.cpp102
-rw-r--r--src/vm/peimagelayout.h2
14 files changed, 1064 insertions, 162 deletions
diff --git a/src/coreclr/hosts/inc/coreclrhost.h b/src/coreclr/hosts/inc/coreclrhost.h
index d1ec724974..7058492a9d 100644
--- a/src/coreclr/hosts/inc/coreclrhost.h
+++ b/src/coreclr/hosts/inc/coreclrhost.h
@@ -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
//
diff --git a/src/dlls/mscoree/mscorwks_ntdef.src b/src/dlls/mscoree/mscorwks_ntdef.src
index bdbce4f4c6..3611e0c140 100644
--- a/src/dlls/mscoree/mscorwks_ntdef.src
+++ b/src/dlls/mscoree/mscorwks_ntdef.src
@@ -20,6 +20,7 @@ EXPORTS
CLRJitAttachState @3 data
; Unix hosting API
+ coreclr_preload_assembly
coreclr_create_delegate
coreclr_execute_assembly
coreclr_initialize
diff --git a/src/dlls/mscoree/mscorwks_unixexports.src b/src/dlls/mscoree/mscorwks_unixexports.src
index 6ba08c9af8..ddbe76b3e9 100644
--- a/src/dlls/mscoree/mscorwks_unixexports.src
+++ b/src/dlls/mscoree/mscorwks_unixexports.src
@@ -1,4 +1,5 @@
; Unix hosting API
+coreclr_preload_assembly
coreclr_create_delegate
coreclr_execute_assembly
coreclr_initialize
diff --git a/src/dlls/mscoree/unixinterface.cpp b/src/dlls/mscoree/unixinterface.cpp
index 67bd444ba8..7cbfad048a 100644
--- a/src/dlls/mscoree/unixinterface.cpp
+++ b/src/dlls/mscoree/unixinterface.cpp
@@ -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();
diff --git a/src/inc/corhost.h b/src/inc/corhost.h
index 09ea994b59..1ff6cafe92 100644
--- a/src/inc/corhost.h
+++ b/src/inc/corhost.h
@@ -218,6 +218,11 @@ public:
LPCWSTR* argv,
DWORD* pReturnValue);
+ static HRESULT PreloadAssembly(
+ LPCSTR szPath);
+
+ static HRESULT UnloadPreloadedAssemblies();
+
static STARTUP_FLAGS GetStartupFlags();
static BOOL HasStarted()
diff --git a/src/pal/inc/pal.h b/src/pal/inc/pal.h
index abb25e889f..11b434dcd2 100644
--- a/src/pal/inc/pal.h
+++ b/src/pal/inc/pal.h
@@ -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
diff --git a/src/pal/src/include/pal/dbgmsg.h b/src/pal/src/include/pal/dbgmsg.h
index d7219b2bd4..58ab25b92d 100644
--- a/src/pal/src/include/pal/dbgmsg.h
+++ b/src/pal/src/include/pal/dbgmsg.h
@@ -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)
diff --git a/src/pal/src/include/pal/map.hpp b/src/pal/src/include/pal/map.hpp
index 7bcb20a404..16ee58ec43 100644
--- a/src/pal/src/include/pal/map.hpp
+++ b/src/pal/src/include/pal/map.hpp
@@ -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 :
diff --git a/src/pal/src/include/pal/module.h b/src/pal/src/include/pal/module.h
index 66ac23834a..ee5d000d63 100644
--- a/src/pal/src/include/pal/module.h
+++ b/src/pal/src/include/pal/module.h
@@ -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.
diff --git a/src/pal/src/loader/module.cpp b/src/pal/src/loader/module.cpp
index 74156fd215..78069b8875 100644
--- a/src/pal/src/loader/module.cpp
+++ b/src/pal/src/loader/module.cpp
@@ -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
diff --git a/src/pal/src/map/map.cpp b/src/pal/src/map/map.cpp
index f6a15f299d..05cfd611bb 100644
--- a/src/pal/src/map/map.cpp
+++ b/src/pal/src/map/map.cpp
@@ -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
@@ -2187,27 +2206,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
@@ -2215,43 +2581,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;
@@ -2298,21 +2694,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.
@@ -2337,7 +2736,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
@@ -2345,10 +2747,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
@@ -2373,39 +2778,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
@@ -2438,7 +2881,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?
@@ -2458,18 +2903,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;
+ }
}
}
@@ -2483,21 +2931,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];
@@ -2508,31 +2990,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:
@@ -2557,10 +3049,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;
}
diff --git a/src/vm/corhost.cpp b/src/vm/corhost.cpp
index be91820e6c..96ade5ca08 100644
--- a/src/vm/corhost.cpp
+++ b/src/vm/corhost.cpp
@@ -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,
diff --git a/src/vm/peimagelayout.cpp b/src/vm/peimagelayout.cpp
index 906e443af7..1d08558c70 100644
--- a/src/vm/peimagelayout.cpp
+++ b/src/vm/peimagelayout.cpp
@@ -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();
}
diff --git a/src/vm/peimagelayout.h b/src/vm/peimagelayout.h
index 17254bcb72..4e358b37e1 100644
--- a/src/vm/peimagelayout.h
+++ b/src/vm/peimagelayout.h
@@ -68,7 +68,7 @@ public:
ULONG Release();
const SString& GetPath();
- void ApplyBaseRelocations();
+ void ApplyBaseRelocations(BOOL isRelocated);
public:
#ifdef DACCESS_COMPILE