Browse Source

Merge pull request #75383 from bruvzg/dll_deps_detect

[Windows] Detect missing DLL dependencies and list them in the open_dynamic_library error message.
Rémi Verschelde 2 years ago
parent
commit
efa01d36b6

+ 0 - 3
platform/windows/crash_handler_windows.cpp

@@ -47,9 +47,6 @@
 
 
 #include <psapi.h>
 #include <psapi.h>
 
 
-#pragma comment(lib, "psapi.lib")
-#pragma comment(lib, "dbghelp.lib")
-
 // Some versions of imagehlp.dll lack the proper packing directives themselves
 // Some versions of imagehlp.dll lack the proper packing directives themselves
 // so we need to do it.
 // so we need to do it.
 #pragma pack(push, before_imagehlp, 8)
 #pragma pack(push, before_imagehlp, 8)

+ 6 - 0
platform/windows/detect.py

@@ -409,6 +409,9 @@ def configure_msvc(env, vcvars_msvc_config):
         "wbemuuid",
         "wbemuuid",
     ]
     ]
 
 
+    if env.debug_features:
+        LIBS += ["psapi", "dbghelp"]
+
     if env["vulkan"]:
     if env["vulkan"]:
         env.AppendUnique(CPPDEFINES=["VULKAN_ENABLED"])
         env.AppendUnique(CPPDEFINES=["VULKAN_ENABLED"])
         if not env["use_volk"]:
         if not env["use_volk"]:
@@ -587,6 +590,9 @@ def configure_mingw(env):
         ]
         ]
     )
     )
 
 
+    if env.debug_features:
+        env.Append(LIBS=["psapi", "dbghelp"])
+
     if env["vulkan"]:
     if env["vulkan"]:
         env.Append(CPPDEFINES=["VULKAN_ENABLED"])
         env.Append(CPPDEFINES=["VULKAN_ENABLED"])
         if not env["use_volk"]:
         if not env["use_volk"]:

+ 98 - 3
platform/windows/os_windows.cpp

@@ -55,6 +55,12 @@
 #include <shlobj.h>
 #include <shlobj.h>
 #include <wbemcli.h>
 #include <wbemcli.h>
 
 
+#ifdef DEBUG_ENABLED
+#pragma pack(push, before_imagehlp, 8)
+#include <imagehlp.h>
+#pragma pack(pop, before_imagehlp)
+#endif
+
 extern "C" {
 extern "C" {
 __declspec(dllexport) DWORD NvOptimusEnablement = 1;
 __declspec(dllexport) DWORD NvOptimusEnablement = 1;
 __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
 __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
@@ -88,7 +94,7 @@ static String format_error_message(DWORD id) {
 
 
 	LocalFree(messageBuffer);
 	LocalFree(messageBuffer);
 
 
-	return msg;
+	return msg.replace("\r", "").replace("\n", "");
 }
 }
 
 
 void RedirectStream(const char *p_file_name, const char *p_mode, FILE *p_cpp_stream, const DWORD p_std_handle) {
 void RedirectStream(const char *p_file_name, const char *p_mode, FILE *p_cpp_stream, const DWORD p_std_handle) {
@@ -277,6 +283,73 @@ Error OS_Windows::get_entropy(uint8_t *r_buffer, int p_bytes) {
 	return OK;
 	return OK;
 }
 }
 
 
+#ifdef DEBUG_ENABLED
+void debug_dynamic_library_check_dependencies(const String &p_root_path, const String &p_path, HashSet<String> &r_checked, HashSet<String> &r_missing) {
+	if (r_checked.has(p_path)) {
+		return;
+	}
+	r_checked.insert(p_path);
+
+	LOADED_IMAGE loaded_image;
+	HANDLE file = CreateFileW((LPCWSTR)p_path.utf16().get_data(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr);
+	if (file != INVALID_HANDLE_VALUE) {
+		HANDLE file_mapping = CreateFileMappingW(file, nullptr, PAGE_READONLY | SEC_COMMIT, 0, 0, nullptr);
+		if (file_mapping != INVALID_HANDLE_VALUE) {
+			PVOID mapping = MapViewOfFile(file_mapping, FILE_MAP_READ, 0, 0, 0);
+			if (mapping) {
+				PIMAGE_DOS_HEADER dos_header = (PIMAGE_DOS_HEADER)mapping;
+				PIMAGE_NT_HEADERS nt_header = nullptr;
+				if (dos_header->e_magic == IMAGE_DOS_SIGNATURE) {
+					PCHAR nt_header_ptr;
+					nt_header_ptr = ((PCHAR)mapping) + dos_header->e_lfanew;
+					nt_header = (PIMAGE_NT_HEADERS)nt_header_ptr;
+					if (nt_header->Signature != IMAGE_NT_SIGNATURE) {
+						nt_header = nullptr;
+					}
+				}
+				if (nt_header) {
+					loaded_image.ModuleName = nullptr;
+					loaded_image.hFile = file;
+					loaded_image.MappedAddress = (PUCHAR)mapping;
+					loaded_image.FileHeader = nt_header;
+					loaded_image.Sections = (PIMAGE_SECTION_HEADER)((LPBYTE)&nt_header->OptionalHeader + nt_header->FileHeader.SizeOfOptionalHeader);
+					loaded_image.NumberOfSections = nt_header->FileHeader.NumberOfSections;
+					loaded_image.SizeOfImage = GetFileSize(file, nullptr);
+					loaded_image.Characteristics = nt_header->FileHeader.Characteristics;
+					loaded_image.LastRvaSection = loaded_image.Sections;
+					loaded_image.fSystemImage = false;
+					loaded_image.fDOSImage = false;
+					loaded_image.Links.Flink = &loaded_image.Links;
+					loaded_image.Links.Blink = &loaded_image.Links;
+
+					ULONG size = 0;
+					const IMAGE_IMPORT_DESCRIPTOR *import_desc = (const IMAGE_IMPORT_DESCRIPTOR *)ImageDirectoryEntryToData((HMODULE)loaded_image.MappedAddress, false, IMAGE_DIRECTORY_ENTRY_IMPORT, &size);
+					if (import_desc) {
+						for (; import_desc->Name && import_desc->FirstThunk; import_desc++) {
+							char16_t full_name_wc[MAX_PATH];
+							const char *name_cs = (const char *)ImageRvaToVa(loaded_image.FileHeader, loaded_image.MappedAddress, import_desc->Name, 0);
+							String name = String(name_cs);
+							if (name.begins_with("api-ms-win-")) {
+								r_checked.insert(name);
+							} else if (SearchPathW(nullptr, (LPCWSTR)name.utf16().get_data(), nullptr, MAX_PATH, (LPWSTR)full_name_wc, nullptr)) {
+								debug_dynamic_library_check_dependencies(p_root_path, String::utf16(full_name_wc), r_checked, r_missing);
+							} else if (SearchPathW((LPCWSTR)(p_path.get_base_dir().utf16().get_data()), (LPCWSTR)name.utf16().get_data(), nullptr, MAX_PATH, (LPWSTR)full_name_wc, nullptr)) {
+								debug_dynamic_library_check_dependencies(p_root_path, String::utf16(full_name_wc), r_checked, r_missing);
+							} else {
+								r_missing.insert(name);
+							}
+						}
+					}
+				}
+				UnmapViewOfFile(mapping);
+			}
+			CloseHandle(file_mapping);
+		}
+		CloseHandle(file);
+	}
+}
+#endif
+
 Error OS_Windows::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path, String *r_resolved_path) {
 Error OS_Windows::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path, String *r_resolved_path) {
 	String path = p_path.replace("/", "\\");
 	String path = p_path.replace("/", "\\");
 
 
@@ -299,7 +372,29 @@ Error OS_Windows::open_dynamic_library(const String p_path, void *&p_library_han
 	}
 	}
 
 
 	p_library_handle = (void *)LoadLibraryExW((LPCWSTR)(path.utf16().get_data()), nullptr, (p_also_set_library_path && has_dll_directory_api) ? LOAD_LIBRARY_SEARCH_DEFAULT_DIRS : 0);
 	p_library_handle = (void *)LoadLibraryExW((LPCWSTR)(path.utf16().get_data()), nullptr, (p_also_set_library_path && has_dll_directory_api) ? LOAD_LIBRARY_SEARCH_DEFAULT_DIRS : 0);
-	ERR_FAIL_COND_V_MSG(!p_library_handle, ERR_CANT_OPEN, "Can't open dynamic library: " + p_path + ", error: " + format_error_message(GetLastError()) + ".");
+#ifdef DEBUG_ENABLED
+	if (!p_library_handle) {
+		DWORD err_code = GetLastError();
+
+		HashSet<String> checekd_libs;
+		HashSet<String> missing_libs;
+		debug_dynamic_library_check_dependencies(path, path, checekd_libs, missing_libs);
+		if (!missing_libs.is_empty()) {
+			String missing;
+			for (const String &E : missing_libs) {
+				if (!missing.is_empty()) {
+					missing += ", ";
+				}
+				missing += E;
+			}
+			ERR_FAIL_V_MSG(ERR_CANT_OPEN, vformat("Can't open dynamic library: %s, missing dependencies: (%s), error: \"%s\".", p_path, missing, format_error_message(err_code)));
+		} else {
+			ERR_FAIL_V_MSG(ERR_CANT_OPEN, vformat("Can't open dynamic library: %s, error: \"%s\"." + p_path, format_error_message(err_code)));
+		}
+	}
+#else
+	ERR_FAIL_COND_V_MSG(!p_library_handle, ERR_CANT_OPEN, vformat("Can't open dynamic library: %s, error: \"%s\"." + p_path, format_error_message(GetLastError())));
+#endif
 
 
 	if (cookie) {
 	if (cookie) {
 		remove_dll_directory(cookie);
 		remove_dll_directory(cookie);
@@ -323,7 +418,7 @@ Error OS_Windows::get_dynamic_library_symbol_handle(void *p_library_handle, cons
 	p_symbol_handle = (void *)GetProcAddress((HMODULE)p_library_handle, p_name.utf8().get_data());
 	p_symbol_handle = (void *)GetProcAddress((HMODULE)p_library_handle, p_name.utf8().get_data());
 	if (!p_symbol_handle) {
 	if (!p_symbol_handle) {
 		if (!p_optional) {
 		if (!p_optional) {
-			ERR_FAIL_V_MSG(ERR_CANT_RESOLVE, "Can't resolve symbol " + p_name + ", error: " + String::num(GetLastError()) + ".");
+			ERR_FAIL_V_MSG(ERR_CANT_RESOLVE, vformat("Can't resolve symbol %s, error: \"%s\".", p_name, format_error_message(GetLastError())));
 		} else {
 		} else {
 			return ERR_CANT_RESOLVE;
 			return ERR_CANT_RESOLVE;
 		}
 		}