Skip to content
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
61 changes: 61 additions & 0 deletions onnxruntime/core/platform/windows/dll_load_error.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

#include <windows.h>
#include <dbghelp.h>
#pragma comment(lib, "dbghelp.lib")
#include <iostream>
#include <memory>
#include <string>
#include "dll_load_error.h"

Check warning on line 10 in onnxruntime/core/platform/windows/dll_load_error.cc

View workflow job for this annotation

GitHub Actions / Optional Lint C++

[cpplint] reported by reviewdog 🐶 Include the directory when naming header files [build/include_subdir] [4] Raw Output: onnxruntime/core/platform/windows/dll_load_error.cc:10: Include the directory when naming header files [build/include_subdir] [4]

struct HMODULE_Deleter {
typedef HMODULE pointer;
void operator()(HMODULE h) { FreeLibrary(h); }
};

using ModulePtr = std::unique_ptr<HMODULE, HMODULE_Deleter>;

// If a DLL fails to load, this will try loading the DLL and then its dependencies recursively
// until it finds a missing file, then will report which file is missing and what the dependency
// chain is.
std::wstring DetermineLoadLibraryError(const wchar_t* filename_in, DWORD flags) {
std::wstring error(L"Error loading");

std::wstring filename{filename_in};
while (filename.size()) {
error += std::wstring(L" \"") + filename + L"\"";

// We use DONT_RESOLVE_DLL_REFERENCES instead of LOAD_LIBRARY_AS_DATAFILE because the latter will not process the import table
// and will result in the IMAGE_IMPORT_DESCRIPTOR table names being uninitialized.
ModulePtr hModule = ModulePtr{LoadLibraryExW(filename.c_str(), NULL, flags | DONT_RESOLVE_DLL_REFERENCES)};
if (!hModule) {
error += L" which is missing.";
return error;
}

// Get the address of the Import Directory
ULONG size{};
IMAGE_IMPORT_DESCRIPTOR* import_desc = reinterpret_cast<IMAGE_IMPORT_DESCRIPTOR*>(ImageDirectoryEntryToData(hModule.get(), TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &size));
if (!import_desc) {
error += L" No import directory found."; // This is unexpected, and I'm not sure how it could happen but we handle it just in case.
return error;
}

// Iterate through the import descriptors to see which dependent DLL can't load
filename.clear();
flags = 0; // Dependent libraries are relative, and flags like LOAD_WITH_ALTERED_SEARCH_PATH is undefined for those.
for (; import_desc->Characteristics; import_desc++) {
const char* dll_name = reinterpret_cast<const char*>(reinterpret_cast<const BYTE*>(hModule.get()) + import_desc->Name);
// Try to load the dependent DLL, and if it fails, we loop again with this as the DLL and we'll be one step closer to the missing file.
ModulePtr hDepModule{LoadLibrary(dll_name)};
if (!hDepModule) {
filename = std::wstring(dll_name, dll_name + strlen(dll_name));
error += L" which depends on";
break;
}
}
}
error += L" But no dependency issue could be determined.";
return error;
}
1 change: 1 addition & 0 deletions onnxruntime/core/platform/windows/dll_load_error.h
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
std::wstring DetermineLoadLibraryError(const wchar_t* filename, DWORD flags);
12 changes: 7 additions & 5 deletions onnxruntime/core/platform/windows/env.cc
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ limitations under the License.
#include <wil/Resource.h>

#include "core/platform/path_lib.h" // for LoopDir()
#include "core/platform/windows/dll_load_error.h"

EXTERN_C IMAGE_DOS_HEADER __ImageBase;

Expand Down Expand Up @@ -704,17 +705,18 @@ Status WindowsEnv::LoadDynamicLibrary(const PathString& wlibrary_filename, bool
static constexpr DWORD bufferLength = 64 * 1024;
std::wstring s(bufferLength, '\0');
FormatMessageW(
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
error_code,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPWSTR)s.data(),
0, NULL);
bufferLength, NULL);
s.erase(std::remove(s.begin(), s.end(), L'\r'), s.end());
s.erase(std::remove(s.begin(), s.end(), L'\n'), s.end());
std::wostringstream oss;
oss << L"LoadLibrary failed with error " << error_code << L" \"" << s.c_str() << L"\" when trying to load \"" << wlibrary_filename << L"\"";
oss << DetermineLoadLibraryError(wlibrary_filename.c_str(), LOAD_WITH_ALTERED_SEARCH_PATH)
<< L" (Error " << error_code << ": \"" << s.c_str() << "\")";
std::wstring errmsg = oss.str();
// TODO: trim the ending '\r' and/or '\n'
common::Status status(common::ONNXRUNTIME, common::FAIL, ToUTF8String(errmsg));
return status;
}
Expand Down
Loading