Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Windows] Detect missing DLL dependencies and list them in the open_dynamic_library error message. #75383

Merged
merged 1 commit into from
Apr 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions platform/windows/crash_handler_windows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,6 @@

#include <psapi.h>

#pragma comment(lib, "psapi.lib")
#pragma comment(lib, "dbghelp.lib")

// Some versions of imagehlp.dll lack the proper packing directives themselves
// so we need to do it.
#pragma pack(push, before_imagehlp, 8)
Expand Down
6 changes: 6 additions & 0 deletions platform/windows/detect.py
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,9 @@ def configure_msvc(env, vcvars_msvc_config):
"wbemuuid",
]

if env.debug_features:
LIBS += ["psapi", "dbghelp"]

if env["vulkan"]:
env.AppendUnique(CPPDEFINES=["VULKAN_ENABLED"])
if not env["use_volk"]:
Expand Down Expand Up @@ -587,6 +590,9 @@ def configure_mingw(env):
]
)

if env.debug_features:
env.Append(LIBS=["psapi", "dbghelp"])

if env["vulkan"]:
env.Append(CPPDEFINES=["VULKAN_ENABLED"])
if not env["use_volk"]:
Expand Down
101 changes: 98 additions & 3 deletions platform/windows/os_windows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@
#include <shlobj.h>
#include <wbemcli.h>

#ifdef DEBUG_ENABLED
#pragma pack(push, before_imagehlp, 8)
#include <imagehlp.h>
#pragma pack(pop, before_imagehlp)
#endif

extern "C" {
__declspec(dllexport) DWORD NvOptimusEnablement = 1;
__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
Expand Down Expand Up @@ -88,7 +94,7 @@ static String format_error_message(DWORD id) {

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) {
Expand Down Expand Up @@ -277,6 +283,73 @@ Error OS_Windows::get_entropy(uint8_t *r_buffer, int p_bytes) {
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) {
String path = p_path.replace("/", "\\");

Expand All @@ -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);
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) {
remove_dll_directory(cookie);
Expand All @@ -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());
if (!p_symbol_handle) {
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 {
return ERR_CANT_RESOLVE;
}
Expand Down