From 18ace528472bacb103f91dd9edac70defce0875b Mon Sep 17 00:00:00 2001 From: DaLynX Date: Sun, 17 Feb 2019 22:47:19 +0100 Subject: [PATCH 1/4] Added x64 exception handling --- MemoryModule.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/MemoryModule.c b/MemoryModule.c index 9f95a70..2fb311b 100644 --- a/MemoryModule.c +++ b/MemoryModule.c @@ -435,6 +435,15 @@ PerformBaseRelocation(PMEMORYMODULE module, ptrdiff_t delta) return TRUE; } +static BOOL +RegisterExceptionHandling(PMEMORYMODULE module) +{ + PIMAGE_DATA_DIRECTORY pDir = GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_EXCEPTION); + PIMAGE_RUNTIME_FUNCTION_ENTRY pEntry = (PIMAGE_RUNTIME_FUNCTION_ENTRY)(module->codeBase + pDir->VirtualAddress); + UINT count = (pDir->Size / sizeof(IMAGE_RUNTIME_FUNCTION_ENTRY)) - 1; + return RtlAddFunctionTable(pEntry, count, (DWORD64)module->codeBase); +} + static BOOL BuildImportTable(PMEMORYMODULE module) { @@ -725,6 +734,10 @@ HMEMORYMODULE MemoryLoadLibraryEx(const void *data, size_t size, goto error; } + if (!RegisterExceptionHandling(result)) { + goto error; + } + // mark memory pages depending on section headers and release // sections that are marked as "discardable" if (!FinalizeSections(result)) { From f89837636a37340770dfa768f577060d79ace37f Mon Sep 17 00:00:00 2001 From: Joachim Bauch Date: Sun, 24 Feb 2019 14:41:35 +0100 Subject: [PATCH 2/4] Fix exception handling registration. - Only compile on 64bit, 32bit exception handling works differently. - Unregister table when module is unloaded. - Handle empty tables. - Fix wrong number of entries being passed to "RtlAddFunctionTable". --- MemoryModule.c | 40 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/MemoryModule.c b/MemoryModule.c index 2fb311b..bc5124a 100644 --- a/MemoryModule.c +++ b/MemoryModule.c @@ -438,10 +438,39 @@ PerformBaseRelocation(PMEMORYMODULE module, ptrdiff_t delta) static BOOL RegisterExceptionHandling(PMEMORYMODULE module) { - PIMAGE_DATA_DIRECTORY pDir = GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_EXCEPTION); - PIMAGE_RUNTIME_FUNCTION_ENTRY pEntry = (PIMAGE_RUNTIME_FUNCTION_ENTRY)(module->codeBase + pDir->VirtualAddress); - UINT count = (pDir->Size / sizeof(IMAGE_RUNTIME_FUNCTION_ENTRY)) - 1; - return RtlAddFunctionTable(pEntry, count, (DWORD64)module->codeBase); +#ifdef _WIN64 + PRUNTIME_FUNCTION pEntry; + DWORD count; + PIMAGE_DATA_DIRECTORY pDir = GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_EXCEPTION); + if (pDir->Size == 0) { + return TRUE; + } + + pEntry = (PRUNTIME_FUNCTION)(module->codeBase + pDir->VirtualAddress); + count = (pDir->Size / sizeof(RUNTIME_FUNCTION)); + return RtlAddFunctionTable(pEntry, count, (DWORD64) module->codeBase); +#else + // TODO(fancycode): Support 32bit exception handling. + return TRUE; +#endif +} + +static BOOL +UnregisterExceptionHandling(PMEMORYMODULE module) +{ +#ifdef _WIN64 + PRUNTIME_FUNCTION pEntry; + PIMAGE_DATA_DIRECTORY pDir = GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_EXCEPTION); + if (pDir->Size == 0) { + return TRUE; + } + + pEntry = (PRUNTIME_FUNCTION)(module->codeBase + pDir->VirtualAddress); + return RtlDeleteFunctionTable(pEntry); +#else + // TODO(fancycode): Support 32bit exception handling. + return TRUE; +#endif } static BOOL @@ -734,6 +763,7 @@ HMEMORYMODULE MemoryLoadLibraryEx(const void *data, size_t size, goto error; } + // register exception handling table so "try { } catch ( ) { }"" works if (!RegisterExceptionHandling(result)) { goto error; } @@ -880,6 +910,8 @@ void MemoryFreeLibrary(HMEMORYMODULE mod) (*DllEntry)((HINSTANCE)module->codeBase, DLL_PROCESS_DETACH, 0); } + UnregisterExceptionHandling(module); + free(module->nameExportsTable); if (module->modules != NULL) { // free previously opened libraries From bf23ebf03ccd0affa1cdc772d4d98ea5c71ccdb9 Mon Sep 17 00:00:00 2001 From: Joachim Bauch Date: Sun, 24 Feb 2019 14:42:48 +0100 Subject: [PATCH 3/4] Add testcase for exception handling code. --- tests/CMakeLists.txt | 2 ++ tests/LoadDll.cpp | 72 +++++++++++++++++++++++++++++++++++++++ tests/Makefile | 2 +- tests/generate-exports.sh | 6 ++++ 4 files changed, 81 insertions(+), 1 deletion(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 8b1007f..e968369 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -5,6 +5,8 @@ set (sources_testsuite if (NOT MSVC) set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "-static") set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "-static") +else () + set (CMAKE_CXX_FLAGS "/EHsc") endif () add_executable (TestSuite ${sources_testsuite}) diff --git a/tests/LoadDll.cpp b/tests/LoadDll.cpp index 8cb215b..15e9f93 100644 --- a/tests/LoadDll.cpp +++ b/tests/LoadDll.cpp @@ -13,6 +13,9 @@ typedef int (*addProc)(int); typedef int (*addNumberProc)(int, int); +#ifdef _WIN64 +typedef void (*throwExceptionProc)(void); +#endif // Thanks to Tim Cooper (from http://stackoverflow.com/a/8584708) const char *sstrstr(const char *haystack, const char *needle, size_t length) { @@ -283,6 +286,70 @@ BOOL LoadExportsFromMemory(char *filename) return result; } +#ifdef _WIN64 +BOOL LoadExceptionsFromMemory(char *filename) +{ + FILE *fp; + unsigned char *data=NULL; + long size; + size_t read; + HMEMORYMODULE handle = NULL; + throwExceptionProc throwException; + BOOL result = TRUE; + + fp = fopen(filename, "rb"); + if (fp == NULL) + { + printf("Can't open DLL file \"%s\".", filename); + result = FALSE; + goto exit; + } + + fseek(fp, 0, SEEK_END); + size = ftell(fp); + assert(size > 0); + data = (unsigned char *)malloc(size); + assert(data != NULL); + fseek(fp, 0, SEEK_SET); + read = fread(data, 1, size, fp); + assert(read == static_cast(size)); + fclose(fp); + + handle = MemoryLoadLibrary(data, size); + if (handle == NULL) + { + _tprintf(_T("Can't load library from memory.\n")); + result = FALSE; + goto exit; + } + + throwException = (throwExceptionProc)MemoryGetProcAddress(handle, "throwException"); + if (!throwException) { + _tprintf(_T("MemoryGetProcAddress(\"throwException\") returned NULL\n")); + result = FALSE; + goto exit; + } + + try { + throwException(); + _tprintf(_T("Should have caught exception.\n")); + result = FALSE; + goto exit; + } catch (int e) { + if (e != 42) { + _tprintf(_T("Should have caught exception 42, got %d instead\n"), e); + result = FALSE; + goto exit; + } + } + +exit: + MemoryFreeLibrary(handle); + free(data); + return result; +} +#endif + int main(int argc, char* argv[]) { if (argc < 2) { @@ -298,6 +365,11 @@ int main(int argc, char* argv[]) if (!LoadExportsFromMemory(argv[1])) { return 2; } +#ifdef _WIN64 + if (!LoadExceptionsFromMemory(argv[1])) { + return 2; + } +#endif } return 0; diff --git a/tests/Makefile b/tests/Makefile index 028bcad..532a1b7 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -74,7 +74,7 @@ test-relocate.dll: $(DLL_OBJ) $(CXX) $(LDFLAGS_DLL) $(LDFLAGS) -Wl,--image-base -Wl,0x20000000 -o $@ $(DLL_OBJ) test-exports.dll: SampleExports.o - $(CXX) $(LDFLAGS_DLL) $(LDFLAGS) -o $@ SampleExports.o + $(CXX) $(LDFLAGS_DLL) $(LDFLAGS) -static -lstdc++ -dynamic -o $@ SampleExports.o SampleExports.cpp: generate-exports.sh ./generate-exports.sh diff --git a/tests/generate-exports.sh b/tests/generate-exports.sh index 974b2a6..ece6d45 100755 --- a/tests/generate-exports.sh +++ b/tests/generate-exports.sh @@ -48,5 +48,11 @@ EOF done cat >> SampleExports.cpp << EOF +#ifdef _WIN64 +SAMPLEDLL_API void throwException(void) +{ + throw 42; +} +#endif } EOF From 1adaf0ef668f60e9757768b458d727a738932fea Mon Sep 17 00:00:00 2001 From: Joachim Bauch Date: Sun, 24 Feb 2019 14:54:07 +0100 Subject: [PATCH 4/4] Add exception handling code to example. --- example/DllLoader/DllLoader.cpp | 45 ++++++++++++++++++++++++++++++++ example/SampleDLL/CMakeLists.txt | 3 +++ example/SampleDLL/Makefile | 2 +- example/SampleDLL/SampleDLL.cpp | 7 +++++ 4 files changed, 56 insertions(+), 1 deletion(-) diff --git a/example/DllLoader/DllLoader.cpp b/example/DllLoader/DllLoader.cpp index 4355ea8..abaaaac 100644 --- a/example/DllLoader/DllLoader.cpp +++ b/example/DllLoader/DllLoader.cpp @@ -12,12 +12,18 @@ #include "../../MemoryModule.h" typedef int (*addNumberProc)(int, int); +#ifdef _WIN64 +typedef void (*throwExceptionProc)(void); +#endif #define DLL_FILE TEXT("..\\SampleDLL\\SampleDLL.dll") void LoadFromFile(void) { addNumberProc addNumber; +#ifdef _WIN64 + throwExceptionProc throwException; +#endif HRSRC resourceInfo; DWORD resourceSize; LPVOID resourceData; @@ -43,6 +49,24 @@ void LoadFromFile(void) LoadString(handle, 20, buffer, sizeof(buffer)); _tprintf(_T("String2: %s\n"), buffer); +#ifdef _WIN64 + throwException = (throwExceptionProc)GetProcAddress(handle, "throwException"); + if (!throwException) { + _tprintf(_T("GetProcAddress(\"throwException\") returned NULL\n")); + } else { + try { + throwException(); + _tprintf(_T("Should have caught exception.\n")); + } catch (int e) { + if (e != 42) { + _tprintf(_T("Should have caught exception 42, got %d instead\n"), e); + } else { + _tprintf(_T("Caught exception %d\n"), e); + } + } + } +#endif + FreeLibrary(handle); } @@ -90,6 +114,9 @@ void LoadFromMemory(void) size_t size; HMEMORYMODULE handle; addNumberProc addNumber; +#ifdef _WIN64 + throwExceptionProc throwException; +#endif HMEMORYRSRC resourceInfo; DWORD resourceSize; LPVOID resourceData; @@ -124,6 +151,24 @@ void LoadFromMemory(void) MemoryLoadString(handle, 20, buffer, sizeof(buffer)); _tprintf(_T("String2: %s\n"), buffer); +#ifdef _WIN64 + throwException = (throwExceptionProc)MemoryGetProcAddress(handle, "throwException"); + if (!throwException) { + _tprintf(_T("MemoryGetProcAddress(\"throwException\") returned NULL\n")); + } else { + try { + throwException(); + _tprintf(_T("Should have caught exception.\n")); + } catch (int e) { + if (e != 42) { + _tprintf(_T("Should have caught exception 42, got %d instead\n"), e); + } else { + _tprintf(_T("Caught exception %d\n"), e); + } + } + } +#endif + MemoryFreeLibrary(handle); exit: diff --git a/example/SampleDLL/CMakeLists.txt b/example/SampleDLL/CMakeLists.txt index e3a3af3..381e719 100644 --- a/example/SampleDLL/CMakeLists.txt +++ b/example/SampleDLL/CMakeLists.txt @@ -7,6 +7,9 @@ set (sources add_definitions (-DSAMPLEDLL_EXPORTS) add_library (SampleDLL MODULE ${sources}) if (NOT MSVC) + set_target_properties ("SampleDLL" PROPERTIES LINK_FLAGS "-static -lstdc++ -dynamic") set_target_properties ("SampleDLL" PROPERTIES PREFIX "") set_target_properties ("SampleDLL" PROPERTIES SUFFIX ".dll") +else () + set (CMAKE_CXX_FLAGS "/EHs") endif () diff --git a/example/SampleDLL/Makefile b/example/SampleDLL/Makefile index ce3f65b..ce6aaee 100644 --- a/example/SampleDLL/Makefile +++ b/example/SampleDLL/Makefile @@ -27,7 +27,7 @@ endif OBJ = SampleDLL.o SampleDLL.res SampleDLL.dll: $(OBJ) - $(LINK) $(LDFLAGS) -o SampleDLL.dll $(OBJ) + $(LINK) $(LDFLAGS) -static -lstdc++ -dynamic -o SampleDLL.dll $(OBJ) %.o: %.cpp $(CXX) $(CFLAGS) -c $< diff --git a/example/SampleDLL/SampleDLL.cpp b/example/SampleDLL/SampleDLL.cpp index 7bf03ef..ec73b0e 100644 --- a/example/SampleDLL/SampleDLL.cpp +++ b/example/SampleDLL/SampleDLL.cpp @@ -7,4 +7,11 @@ SAMPLEDLL_API int addNumbers(int a, int b) return a + b; } +#ifdef _WIN64 +SAMPLEDLL_API void throwException(void) +{ + throw 42; +} +#endif + }