From 8c13cfa4dd35a79c983eb19b5ec2be7ffa220b69 Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Sun, 23 Feb 2025 19:13:53 -0800 Subject: [PATCH] ml/backend/ggml: fix crash on windows paths with wide characters (#9305) --- ...d-filesystem-path-instead-of-wstring.patch | 108 +++++++++++------- ml/backend/ggml/ggml/src/ggml-backend-reg.cpp | 56 +++++---- 2 files changed, 101 insertions(+), 63 deletions(-) diff --git a/llama/patches/0018-use-std-filesystem-path-instead-of-wstring.patch b/llama/patches/0018-use-std-filesystem-path-instead-of-wstring.patch index 749cfbbac..95144fb45 100644 --- a/llama/patches/0018-use-std-filesystem-path-instead-of-wstring.patch +++ b/llama/patches/0018-use-std-filesystem-path-instead-of-wstring.patch @@ -4,17 +4,23 @@ Date: Sun, 16 Feb 2025 20:00:22 -0500 Subject: [PATCH] use std::filesystem::path instead of wstring --- - ggml/src/ggml-backend-reg.cpp | 116 ++++++++++++---------------------- - 1 file changed, 40 insertions(+), 76 deletions(-) + ggml/src/ggml-backend-reg.cpp | 144 ++++++++++++++-------------------- + 1 file changed, 58 insertions(+), 86 deletions(-) diff --git a/ggml/src/ggml-backend-reg.cpp b/ggml/src/ggml-backend-reg.cpp -index 84b21dd8..de78feae 100644 +index 84b21dd8..e35a6936 100644 --- a/ggml/src/ggml-backend-reg.cpp +++ b/ggml/src/ggml-backend-reg.cpp -@@ -72,16 +72,6 @@ - # pragma clang diagnostic ignored "-Wdeprecated-declarations" +@@ -66,26 +66,6 @@ + #include "ggml-kompute.h" #endif +-// disable C++17 deprecation warning for std::codecvt_utf8 +-#if defined(__clang__) +-# pragma clang diagnostic push +-# pragma clang diagnostic ignored "-Wdeprecated-declarations" +-#endif +- -static std::wstring utf8_to_utf16(const std::string & str) { - std::wstring_convert> converter; - return converter.from_bytes(str); @@ -25,10 +31,14 @@ index 84b21dd8..de78feae 100644 - return converter.to_bytes(str); -} - - #if defined(__clang__) - # pragma clang diagnostic pop - #endif -@@ -96,12 +86,12 @@ struct dl_handle_deleter { +-#if defined(__clang__) +-# pragma clang diagnostic pop +-#endif +- + #ifdef _WIN32 + + using dl_handle = std::remove_pointer_t; +@@ -96,7 +76,7 @@ struct dl_handle_deleter { } }; @@ -37,24 +47,44 @@ index 84b21dd8..de78feae 100644 // suppress error dialogs for missing DLLs DWORD old_mode = SetErrorMode(SEM_FAILCRITICALERRORS); SetErrorMode(old_mode | SEM_FAILCRITICALERRORS); - -- HMODULE handle = LoadLibraryW(path.c_str()); -+ HMODULE handle = LoadLibraryW(path.wstring().c_str()); - - SetErrorMode(old_mode); - -@@ -129,8 +119,8 @@ struct dl_handle_deleter { +@@ -129,8 +109,8 @@ struct dl_handle_deleter { } }; -static void * dl_load_library(const std::wstring & path) { - dl_handle * handle = dlopen(utf16_to_utf8(path).c_str(), RTLD_NOW | RTLD_LOCAL); +static void * dl_load_library(const std::filesystem::path & path) { -+ dl_handle * handle = dlopen(path.string().c_str(), RTLD_NOW | RTLD_LOCAL); ++ dl_handle * handle = dlopen(path.c_str(), RTLD_NOW | RTLD_LOCAL); return handle; } -@@ -222,11 +212,11 @@ struct ggml_backend_registry { +@@ -141,6 +121,25 @@ static void * dl_get_sym(dl_handle * handle, const char * name) { + + #endif + ++static std::string path_to_string(const std::filesystem::path & path) ++{ ++#ifdef _WIN32 ++ const std::wstring wstr = path.wstring(); ++ const int size_needed = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, nullptr, 0, nullptr, nullptr); ++ if (size_needed <= 0) { ++ return std::string(); ++ } ++ ++ // size_needed includes the null terminator ++ std::string str(size_needed - 1, '\0'); ++ WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, str.data(), size_needed, nullptr, nullptr); ++ return str; ++#else ++ return path.string(); ++#endif ++} ++ ++ + using dl_handle_ptr = std::unique_ptr; + + struct ggml_backend_reg_entry { +@@ -222,11 +221,11 @@ struct ggml_backend_registry { ); } @@ -64,49 +94,49 @@ index 84b21dd8..de78feae 100644 if (!handle) { if (!silent) { - GGML_LOG_ERROR("%s: failed to load %s\n", __func__, utf16_to_utf8(path).c_str()); -+ GGML_LOG_ERROR("%s: failed to load %s\n", __func__, path.string().c_str()); ++ GGML_LOG_ERROR("%s: failed to load %s\n", __func__, path_to_string(path).c_str()); } return nullptr; } -@@ -234,7 +224,7 @@ struct ggml_backend_registry { +@@ -234,7 +233,7 @@ struct ggml_backend_registry { auto score_fn = (ggml_backend_score_t) dl_get_sym(handle.get(), "ggml_backend_score"); if (score_fn && score_fn() == 0) { if (!silent) { - GGML_LOG_INFO("%s: backend %s is not supported on this system\n", __func__, utf16_to_utf8(path).c_str()); -+ GGML_LOG_INFO("%s: backend %s is not supported on this system\n", __func__, path.string().c_str()); ++ GGML_LOG_INFO("%s: backend %s is not supported on this system\n", __func__, path_to_string(path).c_str()); } return nullptr; } -@@ -242,7 +232,7 @@ struct ggml_backend_registry { +@@ -242,7 +241,7 @@ struct ggml_backend_registry { auto backend_init_fn = (ggml_backend_init_t) dl_get_sym(handle.get(), "ggml_backend_init"); if (!backend_init_fn) { if (!silent) { - GGML_LOG_ERROR("%s: failed to find ggml_backend_init in %s\n", __func__, utf16_to_utf8(path).c_str()); -+ GGML_LOG_ERROR("%s: failed to find ggml_backend_init in %s\n", __func__, path.string().c_str()); ++ GGML_LOG_ERROR("%s: failed to find ggml_backend_init in %s\n", __func__, path_to_string(path).c_str()); } return nullptr; } -@@ -251,16 +241,16 @@ struct ggml_backend_registry { +@@ -251,16 +250,16 @@ struct ggml_backend_registry { if (!reg || reg->api_version != GGML_BACKEND_API_VERSION) { if (!silent) { if (!reg) { - GGML_LOG_ERROR("%s: failed to initialize backend from %s: ggml_backend_init returned NULL\n", __func__, utf16_to_utf8(path).c_str()); -+ GGML_LOG_ERROR("%s: failed to initialize backend from %s: ggml_backend_init returned NULL\n", __func__, path.string().c_str()); ++ GGML_LOG_ERROR("%s: failed to initialize backend from %s: ggml_backend_init returned NULL\n", __func__, path_to_string(path).c_str()); } else { GGML_LOG_ERROR("%s: failed to initialize backend from %s: incompatible API version (backend: %d, current: %d)\n", - __func__, utf16_to_utf8(path).c_str(), reg->api_version, GGML_BACKEND_API_VERSION); -+ __func__, path.string().c_str(), reg->api_version, GGML_BACKEND_API_VERSION); ++ __func__, path_to_string(path).c_str(), reg->api_version, GGML_BACKEND_API_VERSION); } } return nullptr; } - GGML_LOG_INFO("%s: loaded %s backend from %s\n", __func__, ggml_backend_reg_name(reg), utf16_to_utf8(path).c_str()); -+ GGML_LOG_INFO("%s: loaded %s backend from %s\n", __func__, ggml_backend_reg_name(reg), path.string().c_str()); ++ GGML_LOG_INFO("%s: loaded %s backend from %s\n", __func__, ggml_backend_reg_name(reg), path_to_string(path).c_str()); register_backend(reg, score_fn ? score_fn() : -1, std::move(handle)); -@@ -396,14 +386,14 @@ ggml_backend_t ggml_backend_init_best(void) { +@@ -396,14 +395,14 @@ ggml_backend_t ggml_backend_init_best(void) { // Dynamic loading ggml_backend_reg_t ggml_backend_load(const char * path) { @@ -123,7 +153,7 @@ index 84b21dd8..de78feae 100644 #if defined(__APPLE__) // get executable path std::vector path; -@@ -415,15 +405,9 @@ static std::wstring get_executable_path() { +@@ -415,15 +414,9 @@ static std::wstring get_executable_path() { } path.resize(size); } @@ -141,7 +171,7 @@ index 84b21dd8..de78feae 100644 std::vector path(1024); while (true) { // get executable path -@@ -436,76 +420,56 @@ static std::wstring get_executable_path() { +@@ -436,76 +429,55 @@ static std::wstring get_executable_path() { break; } if (len < (ssize_t) path.size()) { @@ -179,11 +209,11 @@ index 84b21dd8..de78feae 100644 -static std::wstring backend_filename_prefix() { -#ifdef _WIN32 - return L"ggml-"; -+ return std::filesystem::path(path.data()).parent_path(); - #else +-#else - return L"libggml-"; -+ return {}; ++ return std::filesystem::path(path.data()).parent_path(); #endif ++ return {}; } -static std::wstring backend_filename_suffix() { @@ -234,7 +264,7 @@ index 84b21dd8..de78feae 100644 for (const auto & search_path : search_paths) { if (!fs::exists(search_path)) { continue; -@@ -514,31 +478,31 @@ static ggml_backend_reg_t ggml_backend_load_best(const char * name, bool silent, +@@ -514,31 +486,31 @@ static ggml_backend_reg_t ggml_backend_load_best(const char * name, bool silent, for (const auto & entry : dir_it) { try { if (entry.is_regular_file()) { @@ -247,20 +277,20 @@ index 84b21dd8..de78feae 100644 + dl_handle_ptr handle { dl_load_library(entry.path()) }; if (!handle) { - GGML_LOG_ERROR("%s: failed to load %s\n", __func__, utf16_to_utf8(entry.path().wstring()).c_str()); -+ GGML_LOG_ERROR("%s: failed to load %s\n", __func__, entry.path().string().c_str()); ++ GGML_LOG_ERROR("%s: failed to load %s\n", __func__, path_to_string(entry.path()).c_str()); continue; } auto score_fn = (ggml_backend_score_t) dl_get_sym(handle.get(), "ggml_backend_score"); if (!score_fn) { - GGML_LOG_DEBUG("%s: failed to find ggml_backend_score in %s\n", __func__, utf16_to_utf8(entry.path().wstring()).c_str()); -+ GGML_LOG_DEBUG("%s: failed to find ggml_backend_score in %s\n", __func__, entry.path().string().c_str()); ++ GGML_LOG_DEBUG("%s: failed to find ggml_backend_score in %s\n", __func__, path_to_string(entry.path()).c_str()); continue; } int s = score_fn(); - GGML_LOG_DEBUG("%s: %s score: %d\n", __func__, utf16_to_utf8(entry.path().wstring()).c_str(), s); -+ GGML_LOG_DEBUG("%s: %s score: %d\n", __func__, entry.path().string().c_str(), s); ++ GGML_LOG_DEBUG("%s: %s score: %d\n", __func__, path_to_string(entry.path()).c_str(), s); if (s > best_score) { best_score = s; - best_path = entry.path().wstring(); @@ -270,11 +300,11 @@ index 84b21dd8..de78feae 100644 } } catch (const std::exception & e) { - GGML_LOG_ERROR("%s: failed to load %s: %s\n", __func__, utf16_to_utf8(entry.path().wstring()).c_str(), e.what()); -+ GGML_LOG_ERROR("%s: failed to load %s: %s\n", __func__, entry.path().string().c_str(), e.what()); ++ GGML_LOG_ERROR("%s: failed to load %s: %s\n", __func__, path_to_string(entry.path()).c_str(), e.what()); } } } -@@ -546,7 +510,7 @@ static ggml_backend_reg_t ggml_backend_load_best(const char * name, bool silent, +@@ -546,7 +518,7 @@ static ggml_backend_reg_t ggml_backend_load_best(const char * name, bool silent, if (best_score == 0) { // try to load the base backend for (const auto & search_path : search_paths) { diff --git a/ml/backend/ggml/ggml/src/ggml-backend-reg.cpp b/ml/backend/ggml/ggml/src/ggml-backend-reg.cpp index de78feae5..e35a69369 100644 --- a/ml/backend/ggml/ggml/src/ggml-backend-reg.cpp +++ b/ml/backend/ggml/ggml/src/ggml-backend-reg.cpp @@ -66,16 +66,6 @@ #include "ggml-kompute.h" #endif -// disable C++17 deprecation warning for std::codecvt_utf8 -#if defined(__clang__) -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wdeprecated-declarations" -#endif - -#if defined(__clang__) -# pragma clang diagnostic pop -#endif - #ifdef _WIN32 using dl_handle = std::remove_pointer_t; @@ -91,7 +81,7 @@ static dl_handle * dl_load_library(const std::filesystem::path & path) { DWORD old_mode = SetErrorMode(SEM_FAILCRITICALERRORS); SetErrorMode(old_mode | SEM_FAILCRITICALERRORS); - HMODULE handle = LoadLibraryW(path.wstring().c_str()); + HMODULE handle = LoadLibraryW(path.c_str()); SetErrorMode(old_mode); @@ -120,7 +110,7 @@ struct dl_handle_deleter { }; static void * dl_load_library(const std::filesystem::path & path) { - dl_handle * handle = dlopen(path.string().c_str(), RTLD_NOW | RTLD_LOCAL); + dl_handle * handle = dlopen(path.c_str(), RTLD_NOW | RTLD_LOCAL); return handle; } @@ -131,6 +121,25 @@ static void * dl_get_sym(dl_handle * handle, const char * name) { #endif +static std::string path_to_string(const std::filesystem::path & path) +{ +#ifdef _WIN32 + const std::wstring wstr = path.wstring(); + const int size_needed = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, nullptr, 0, nullptr, nullptr); + if (size_needed <= 0) { + return std::string(); + } + + // size_needed includes the null terminator + std::string str(size_needed - 1, '\0'); + WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, str.data(), size_needed, nullptr, nullptr); + return str; +#else + return path.string(); +#endif +} + + using dl_handle_ptr = std::unique_ptr; struct ggml_backend_reg_entry { @@ -216,7 +225,7 @@ struct ggml_backend_registry { dl_handle_ptr handle { dl_load_library(path) }; if (!handle) { if (!silent) { - GGML_LOG_ERROR("%s: failed to load %s\n", __func__, path.string().c_str()); + GGML_LOG_ERROR("%s: failed to load %s\n", __func__, path_to_string(path).c_str()); } return nullptr; } @@ -224,7 +233,7 @@ struct ggml_backend_registry { auto score_fn = (ggml_backend_score_t) dl_get_sym(handle.get(), "ggml_backend_score"); if (score_fn && score_fn() == 0) { if (!silent) { - GGML_LOG_INFO("%s: backend %s is not supported on this system\n", __func__, path.string().c_str()); + GGML_LOG_INFO("%s: backend %s is not supported on this system\n", __func__, path_to_string(path).c_str()); } return nullptr; } @@ -232,7 +241,7 @@ struct ggml_backend_registry { auto backend_init_fn = (ggml_backend_init_t) dl_get_sym(handle.get(), "ggml_backend_init"); if (!backend_init_fn) { if (!silent) { - GGML_LOG_ERROR("%s: failed to find ggml_backend_init in %s\n", __func__, path.string().c_str()); + GGML_LOG_ERROR("%s: failed to find ggml_backend_init in %s\n", __func__, path_to_string(path).c_str()); } return nullptr; } @@ -241,16 +250,16 @@ struct ggml_backend_registry { if (!reg || reg->api_version != GGML_BACKEND_API_VERSION) { if (!silent) { if (!reg) { - GGML_LOG_ERROR("%s: failed to initialize backend from %s: ggml_backend_init returned NULL\n", __func__, path.string().c_str()); + GGML_LOG_ERROR("%s: failed to initialize backend from %s: ggml_backend_init returned NULL\n", __func__, path_to_string(path).c_str()); } else { GGML_LOG_ERROR("%s: failed to initialize backend from %s: incompatible API version (backend: %d, current: %d)\n", - __func__, path.string().c_str(), reg->api_version, GGML_BACKEND_API_VERSION); + __func__, path_to_string(path).c_str(), reg->api_version, GGML_BACKEND_API_VERSION); } } return nullptr; } - GGML_LOG_INFO("%s: loaded %s backend from %s\n", __func__, ggml_backend_reg_name(reg), path.string().c_str()); + GGML_LOG_INFO("%s: loaded %s backend from %s\n", __func__, ggml_backend_reg_name(reg), path_to_string(path).c_str()); register_backend(reg, score_fn ? score_fn() : -1, std::move(handle)); @@ -432,9 +441,8 @@ static std::filesystem::path get_executable_path() { } return std::filesystem::path(path.data()).parent_path(); -#else - return {}; #endif + return {}; } static std::string backend_filename_prefix() { @@ -483,18 +491,18 @@ static ggml_backend_reg_t ggml_backend_load_best(const char * name, bool silent, if (filename.find(file_prefix) == 0 && ext == backend_filename_suffix()) { dl_handle_ptr handle { dl_load_library(entry.path()) }; if (!handle) { - GGML_LOG_ERROR("%s: failed to load %s\n", __func__, entry.path().string().c_str()); + GGML_LOG_ERROR("%s: failed to load %s\n", __func__, path_to_string(entry.path()).c_str()); continue; } auto score_fn = (ggml_backend_score_t) dl_get_sym(handle.get(), "ggml_backend_score"); if (!score_fn) { - GGML_LOG_DEBUG("%s: failed to find ggml_backend_score in %s\n", __func__, entry.path().string().c_str()); + GGML_LOG_DEBUG("%s: failed to find ggml_backend_score in %s\n", __func__, path_to_string(entry.path()).c_str()); continue; } int s = score_fn(); - GGML_LOG_DEBUG("%s: %s score: %d\n", __func__, entry.path().string().c_str(), s); + GGML_LOG_DEBUG("%s: %s score: %d\n", __func__, path_to_string(entry.path()).c_str(), s); if (s > best_score) { best_score = s; best_path = entry.path(); @@ -502,7 +510,7 @@ static ggml_backend_reg_t ggml_backend_load_best(const char * name, bool silent, } } } catch (const std::exception & e) { - GGML_LOG_ERROR("%s: failed to load %s: %s\n", __func__, entry.path().string().c_str(), e.what()); + GGML_LOG_ERROR("%s: failed to load %s: %s\n", __func__, path_to_string(entry.path()).c_str(), e.what()); } } }