diff options
Diffstat (limited to 'source/Host/windows')
-rw-r--r-- | source/Host/windows/ConnectionGenericFileWindows.cpp | 11 | ||||
-rw-r--r-- | source/Host/windows/FileSystem.cpp | 120 | ||||
-rw-r--r-- | source/Host/windows/Host.cpp | 48 | ||||
-rw-r--r-- | source/Host/windows/HostInfoWindows.cpp | 52 | ||||
-rw-r--r-- | source/Host/windows/HostProcessWindows.cpp | 21 | ||||
-rw-r--r-- | source/Host/windows/PipeWindows.cpp | 10 | ||||
-rw-r--r-- | source/Host/windows/ProcessLauncherWindows.cpp | 49 | ||||
-rw-r--r-- | source/Host/windows/Windows.cpp | 157 |
8 files changed, 348 insertions, 120 deletions
diff --git a/source/Host/windows/ConnectionGenericFileWindows.cpp b/source/Host/windows/ConnectionGenericFileWindows.cpp index eebf3d4f633c8..9743ed48b8efd 100644 --- a/source/Host/windows/ConnectionGenericFileWindows.cpp +++ b/source/Host/windows/ConnectionGenericFileWindows.cpp @@ -14,6 +14,7 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Support/ConvertUTF.h" using namespace lldb; using namespace lldb_private; @@ -138,7 +139,15 @@ ConnectionGenericFile::Connect(const char *s, Error *error_ptr) // Open the file for overlapped access. If it does not exist, create it. We open it overlapped // so that we can issue asynchronous reads and then use WaitForMultipleObjects to allow the read // to be interrupted by an event object. - m_file = ::CreateFile(path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_FLAG_OVERLAPPED, NULL); + std::wstring wpath; + if (!llvm::ConvertUTF8toWide(path, wpath)) + { + if (error_ptr) + error_ptr->SetError(1, eErrorTypeGeneric); + return eConnectionStatusError; + } + m_file = ::CreateFileW(wpath.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, + FILE_FLAG_OVERLAPPED, NULL); if (m_file == INVALID_HANDLE_VALUE) { if (error_ptr) diff --git a/source/Host/windows/FileSystem.cpp b/source/Host/windows/FileSystem.cpp index 2cca12b92711b..3e881f5d9b2be 100644 --- a/source/Host/windows/FileSystem.cpp +++ b/source/Host/windows/FileSystem.cpp @@ -15,6 +15,8 @@ #include "lldb/Host/FileSystem.h" #include "lldb/Host/windows/AutoHandle.h" + +#include "llvm/Support/ConvertUTF.h" #include "llvm/Support/FileSystem.h" using namespace lldb_private; @@ -22,6 +24,8 @@ using namespace lldb_private; const char * FileSystem::DEV_NULL = "nul"; +const char *FileSystem::PATH_CONVERSION_ERROR = "Error converting path between UTF-8 and native encoding"; + FileSpec::PathSyntax FileSystem::GetNativePathSyntax() { @@ -47,25 +51,32 @@ Error FileSystem::DeleteDirectory(const FileSpec &file_spec, bool recurse) { Error error; + std::wstring path_buffer; + if (!llvm::ConvertUTF8toWide(file_spec.GetPath(), path_buffer)) + { + error.SetErrorString(PATH_CONVERSION_ERROR); + return error; + } if (!recurse) { - BOOL result = ::RemoveDirectory(file_spec.GetCString()); + BOOL result = ::RemoveDirectoryW(path_buffer.c_str()); if (!result) error.SetError(::GetLastError(), lldb::eErrorTypeWin32); } else { // SHFileOperation() accepts a list of paths, and so must be double-null-terminated to - // indicate the end of the list. - std::string path_buffer{file_spec.GetPath()}; + // indicate the end of the list. The first null terminator is there only in the backing + // store but not the actual vector contents, and so we need to push twice. + path_buffer.push_back(0); path_buffer.push_back(0); - SHFILEOPSTRUCT shfos = {0}; + SHFILEOPSTRUCTW shfos = {0}; shfos.wFunc = FO_DELETE; - shfos.pFrom = path_buffer.c_str(); + shfos.pFrom = (LPCWSTR)path_buffer.data(); shfos.fFlags = FOF_NO_UI; - int result = ::SHFileOperation(&shfos); + int result = ::SHFileOperationW(&shfos); // TODO(zturner): Correctly handle the intricacies of SHFileOperation return values. if (result != 0) error.SetErrorStringWithFormat("SHFileOperation failed"); @@ -121,7 +132,10 @@ Error FileSystem::Hardlink(const FileSpec &src, const FileSpec &dst) { Error error; - if (!::CreateHardLink(src.GetCString(), dst.GetCString(), nullptr)) + std::wstring wsrc, wdst; + if (!llvm::ConvertUTF8toWide(src.GetCString(), wsrc) || !llvm::ConvertUTF8toWide(dst.GetCString(), wdst)) + error.SetErrorString(PATH_CONVERSION_ERROR); + else if (!::CreateHardLinkW(wsrc.c_str(), wdst.c_str(), nullptr)) error.SetError(::GetLastError(), lldb::eErrorTypeWin32); return error; } @@ -129,13 +143,12 @@ FileSystem::Hardlink(const FileSpec &src, const FileSpec &dst) int FileSystem::GetHardlinkCount(const FileSpec &file_spec) { - HANDLE file_handle = ::CreateFile(file_spec.GetCString(), - FILE_READ_ATTRIBUTES, - FILE_SHARE_READ, - nullptr, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - nullptr); + std::wstring path; + if (!llvm::ConvertUTF8toWide(file_spec.GetCString(), path)) + return -1; + + HANDLE file_handle = ::CreateFileW(path.c_str(), FILE_READ_ATTRIBUTES, FILE_SHARE_READ, nullptr, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, nullptr); if (file_handle == INVALID_HANDLE_VALUE) return -1; @@ -152,7 +165,12 @@ Error FileSystem::Symlink(const FileSpec &src, const FileSpec &dst) { Error error; - DWORD attrib = ::GetFileAttributes(dst.GetCString()); + std::wstring wsrc, wdst; + if (!llvm::ConvertUTF8toWide(src.GetCString(), wsrc) || !llvm::ConvertUTF8toWide(dst.GetCString(), wdst)) + error.SetErrorString(PATH_CONVERSION_ERROR); + if (error.Fail()) + return error; + DWORD attrib = ::GetFileAttributesW(wdst.c_str()); if (attrib == INVALID_FILE_ATTRIBUTES) { error.SetError(::GetLastError(), lldb::eErrorTypeWin32); @@ -160,7 +178,7 @@ FileSystem::Symlink(const FileSpec &src, const FileSpec &dst) } bool is_directory = !!(attrib & FILE_ATTRIBUTE_DIRECTORY); DWORD flag = is_directory ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0; - BOOL result = ::CreateSymbolicLink(src.GetCString(), dst.GetCString(), flag); + BOOL result = ::CreateSymbolicLinkW(wsrc.c_str(), wdst.c_str(), flag); if (!result) error.SetError(::GetLastError(), lldb::eErrorTypeWin32); return error; @@ -170,7 +188,13 @@ Error FileSystem::Unlink(const FileSpec &file_spec) { Error error; - BOOL result = ::DeleteFile(file_spec.GetCString()); + std::wstring path; + if (!llvm::ConvertUTF8toWide(file_spec.GetCString(), path)) + { + error.SetErrorString(PATH_CONVERSION_ERROR); + return error; + } + BOOL result = ::DeleteFileW(path.c_str()); if (!result) error.SetError(::GetLastError(), lldb::eErrorTypeWin32); return error; @@ -180,23 +204,31 @@ Error FileSystem::Readlink(const FileSpec &src, FileSpec &dst) { Error error; - HANDLE h = ::CreateFile(src.GetCString(), GENERIC_READ, - FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, - FILE_FLAG_OPEN_REPARSE_POINT, NULL); + std::wstring wsrc; + if (!llvm::ConvertUTF8toWide(src.GetCString(), wsrc)) + { + error.SetErrorString(PATH_CONVERSION_ERROR); + return error; + } + + HANDLE h = ::CreateFileW(wsrc.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, + FILE_FLAG_OPEN_REPARSE_POINT, NULL); if (h == INVALID_HANDLE_VALUE) { error.SetError(::GetLastError(), lldb::eErrorTypeWin32); return error; } - char buf[PATH_MAX]; + std::vector<wchar_t> buf(PATH_MAX + 1); // Subtract 1 from the path length since this function does not add a null terminator. - DWORD result = ::GetFinalPathNameByHandle(h, buf, sizeof(buf) - 1, - FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); + DWORD result = ::GetFinalPathNameByHandleW(h, buf.data(), buf.size() - 1, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); + std::string path; if (result == 0) error.SetError(::GetLastError(), lldb::eErrorTypeWin32); + else if (!llvm::convertWideToUTF8(buf.data(), path)) + error.SetErrorString(PATH_CONVERSION_ERROR); else - dst.SetFile(buf, false); + dst.SetFile(path, false); ::CloseHandle(h); return error; @@ -219,3 +251,43 @@ FileSystem::IsLocal(const FileSpec &spec) return false; } + +FILE * +FileSystem::Fopen(const char *path, const char *mode) +{ + std::wstring wpath, wmode; + if (!llvm::ConvertUTF8toWide(path, wpath)) + return nullptr; + if (!llvm::ConvertUTF8toWide(mode, wmode)) + return nullptr; + FILE *file; + if (_wfopen_s(&file, wpath.c_str(), wmode.c_str()) != 0) + return nullptr; + return file; +} + +int +FileSystem::Stat(const char *path, struct stat *stats) +{ + std::wstring wpath; + if (!llvm::ConvertUTF8toWide(path, wpath)) + { + errno = EINVAL; + return -EINVAL; + } + int stat_result; +#ifdef _USE_32BIT_TIME_T + struct _stat32 file_stats; + stat_result = ::_wstat32(wpath.c_str(), &file_stats); +#else + struct _stat64i32 file_stats; + stat_result = ::_wstat64i32(wpath.c_str(), &file_stats); +#endif + if (stat_result == 0) + { + static_assert(sizeof(struct stat) == sizeof(file_stats), + "stat and _stat32/_stat64i32 must have the same layout"); + *stats = *reinterpret_cast<struct stat *>(&file_stats); + } + return stat_result; +} diff --git a/source/Host/windows/Host.cpp b/source/Host/windows/Host.cpp index 2c9a139df256c..1b4eeb8d4f606 100644 --- a/source/Host/windows/Host.cpp +++ b/source/Host/windows/Host.cpp @@ -26,6 +26,8 @@ #include "lldb/Core/StreamFile.h" #include "lldb/Core/StructuredData.h" +#include "llvm/Support/ConvertUTF.h" + // Windows includes #include <TlHelp32.h> @@ -68,12 +70,11 @@ namespace bool GetExecutableForProcess(const AutoHandle &handle, std::string &path) { // Get the process image path. MAX_PATH isn't long enough, paths can actually be up to 32KB. - std::vector<char> buffer(32768); + std::vector<wchar_t> buffer(PATH_MAX); DWORD dwSize = buffer.size(); - if (!::QueryFullProcessImageNameA(handle.get(), 0, &buffer[0], &dwSize)) + if (!::QueryFullProcessImageNameW(handle.get(), 0, &buffer[0], &dwSize)) return false; - path.assign(&buffer[0]); - return true; + return llvm::convertWideToUTF8(buffer.data(), path); } void GetProcessExecutableAndTriple(const AutoHandle &handle, ProcessInstanceInfo &process) @@ -156,15 +157,17 @@ Host::GetModuleFileSpecForHostAddress (const void *host_addr) if (!::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCTSTR)host_addr, &hmodule)) return module_filespec; - std::vector<char> buffer(MAX_PATH); + std::vector<wchar_t> buffer(PATH_MAX); DWORD chars_copied = 0; do { - chars_copied = ::GetModuleFileName(hmodule, &buffer[0], buffer.size()); + chars_copied = ::GetModuleFileNameW(hmodule, &buffer[0], buffer.size()); if (chars_copied == buffer.size() && ::GetLastError() == ERROR_INSUFFICIENT_BUFFER) buffer.resize(buffer.size() * 2); } while (chars_copied >= buffer.size()); - - module_filespec.SetFile(&buffer[0], false); + std::string path; + if (!llvm::convertWideToUTF8(buffer.data(), path)) + return module_filespec; + module_filespec.SetFile(path, false); return module_filespec; } @@ -177,23 +180,25 @@ Host::FindProcesses (const ProcessInstanceInfoMatch &match_info, ProcessInstance if (!snapshot.IsValid()) return 0; - PROCESSENTRY32 pe = {0}; - pe.dwSize = sizeof(PROCESSENTRY32); - if (Process32First(snapshot.get(), &pe)) + PROCESSENTRY32W pe = {0}; + pe.dwSize = sizeof(PROCESSENTRY32W); + if (Process32FirstW(snapshot.get(), &pe)) { do { AutoHandle handle(::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pe.th32ProcessID), nullptr); ProcessInstanceInfo process; - process.SetExecutableFile(FileSpec(pe.szExeFile, false), true); + std::string exeFile; + llvm::convertWideToUTF8(pe.szExeFile, exeFile); + process.SetExecutableFile(FileSpec(exeFile, false), true); process.SetProcessID(pe.th32ProcessID); process.SetParentProcessID(pe.th32ParentProcessID); GetProcessExecutableAndTriple(handle, process); if (match_info.MatchAllProcesses() || match_info.Matches(process)) process_infos.Append(process); - } while (Process32Next(snapshot.get(), &pe)); + } while (Process32NextW(snapshot.get(), &pe)); } return process_infos.GetSize(); } @@ -216,7 +221,8 @@ Host::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info) } HostThread -Host::StartMonitoringChildProcess(Host::MonitorChildProcessCallback callback, void *callback_baton, lldb::pid_t pid, bool monitor_signals) +Host::StartMonitoringChildProcess(const Host::MonitorChildProcessCallback &callback, lldb::pid_t pid, + bool monitor_signals) { return HostThread(); } @@ -312,15 +318,21 @@ Host::GetEnvironment(StringList &env) { // The environment block on Windows is a contiguous buffer of NULL terminated strings, // where the end of the environment block is indicated by two consecutive NULLs. - LPCH environment_block = ::GetEnvironmentStrings(); + LPWCH environment_block = ::GetEnvironmentStringsW(); env.Clear(); - while (*environment_block != '\0') + while (*environment_block != L'\0') { - llvm::StringRef current_var(environment_block); + std::string current_var; + auto current_var_size = wcslen(environment_block) + 1; + if (!llvm::convertWideToUTF8(environment_block, current_var)) + { + environment_block += current_var_size; + continue; + } if (current_var[0] != '=') env.AppendString(current_var); - environment_block += current_var.size()+1; + environment_block += current_var_size; } return env.GetSize(); } diff --git a/source/Host/windows/HostInfoWindows.cpp b/source/Host/windows/HostInfoWindows.cpp index 6dce71d9172ad..6b2190294081f 100644 --- a/source/Host/windows/HostInfoWindows.cpp +++ b/source/Host/windows/HostInfoWindows.cpp @@ -9,18 +9,35 @@ #include "lldb/Host/windows/windows.h" +#include <objbase.h> + #include <mutex> // std::once #include "lldb/Host/windows/HostInfoWindows.h" #include "llvm/ADT/SmallString.h" +#include "llvm/Support/ConvertUTF.h" #include "llvm/Support/FileSystem.h" -#include "llvm/Support/raw_ostream.h" #include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" using namespace lldb_private; FileSpec HostInfoWindows::m_program_filespec; +void +HostInfoWindows::Initialize() +{ + ::CoInitializeEx(nullptr, COINIT_MULTITHREADED); + HostInfoBase::Initialize(); +} + +void +HostInfoWindows::Terminate() +{ + HostInfoBase::Terminate(); + ::CoUninitialize(); +} + size_t HostInfoWindows::GetPageSize() { @@ -76,23 +93,24 @@ HostInfoWindows::GetOSKernelDescription(std::string &s) bool HostInfoWindows::GetHostname(std::string &s) { - char buffer[MAX_COMPUTERNAME_LENGTH + 1]; + wchar_t buffer[MAX_COMPUTERNAME_LENGTH + 1]; DWORD dwSize = MAX_COMPUTERNAME_LENGTH + 1; - if (!::GetComputerName(buffer, &dwSize)) + if (!::GetComputerNameW(buffer, &dwSize)) return false; - s.assign(buffer, buffer + dwSize); - return true; + return llvm::convertWideToUTF8(buffer, s); } FileSpec HostInfoWindows::GetProgramFileSpec() { static std::once_flag g_once_flag; - std::call_once(g_once_flag, []() { - char buffer[PATH_MAX]; - ::GetModuleFileName(NULL, buffer, sizeof(buffer)); - m_program_filespec.SetFile(buffer, false); + std::call_once(g_once_flag, []() { + std::vector<wchar_t> buffer(PATH_MAX); + ::GetModuleFileNameW(NULL, buffer.data(), buffer.size()); + std::string path; + llvm::convertWideToUTF8(buffer.data(), path); + m_program_filespec.SetFile(path, false); }); return m_program_filespec; } @@ -100,7 +118,9 @@ HostInfoWindows::GetProgramFileSpec() FileSpec HostInfoWindows::GetDefaultShell() { - return FileSpec(::getenv("ComSpec"), false); + std::string shell; + GetEnvironmentVar("ComSpec", shell); + return FileSpec(shell, false); } bool @@ -116,3 +136,15 @@ HostInfoWindows::ComputePythonDirectory(FileSpec &file_spec) file_spec.GetDirectory().SetString(path.c_str()); return true; } + +bool +HostInfoWindows::GetEnvironmentVar(const std::string &var_name, std::string &var) +{ + std::wstring wvar_name; + if (!llvm::ConvertUTF8toWide(var_name, wvar_name)) + return false; + + if (const wchar_t *wvar = _wgetenv(wvar_name.c_str())) + return llvm::convertWideToUTF8(wvar, var); + return false; +} diff --git a/source/Host/windows/HostProcessWindows.cpp b/source/Host/windows/HostProcessWindows.cpp index 0f81c18d34afb..2878171cbe773 100644 --- a/source/Host/windows/HostProcessWindows.cpp +++ b/source/Host/windows/HostProcessWindows.cpp @@ -14,6 +14,7 @@ #include "lldb/Host/windows/HostProcessWindows.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/Support/ConvertUTF.h" #include <Psapi.h> @@ -23,8 +24,7 @@ namespace { struct MonitorInfo { - HostProcess::MonitorCallback callback; - void *baton; + Host::MonitorChildProcessCallback callback; HANDLE process_handle; }; } @@ -70,9 +70,15 @@ Error HostProcessWindows::GetMainModule(FileSpec &file_spec) const if (m_process == nullptr) error.SetError(ERROR_INVALID_HANDLE, lldb::eErrorTypeWin32); - char path[MAX_PATH] = { 0 }; - if (::GetProcessImageFileName(m_process, path, llvm::array_lengthof(path))) - file_spec.SetFile(path, false); + std::vector<wchar_t> wpath(PATH_MAX); + if (::GetProcessImageFileNameW(m_process, wpath.data(), wpath.size())) + { + std::string path; + if (llvm::convertWideToUTF8(wpath.data(), path)) + file_spec.SetFile(path, false); + else + error.SetErrorString("Error converting path to UTF-8"); + } else error.SetError(::GetLastError(), lldb::eErrorTypeWin32); @@ -97,12 +103,11 @@ bool HostProcessWindows::IsRunning() const } HostThread -HostProcessWindows::StartMonitoring(HostProcess::MonitorCallback callback, void *callback_baton, bool monitor_signals) +HostProcessWindows::StartMonitoring(const Host::MonitorChildProcessCallback &callback, bool monitor_signals) { HostThread monitor_thread; MonitorInfo *info = new MonitorInfo; info->callback = callback; - info->baton = callback_baton; // Since the life of this HostProcessWindows instance and the life of the process may be different, duplicate the handle so that // the monitor thread can have ownership over its own copy of the handle. @@ -122,7 +127,7 @@ HostProcessWindows::MonitorThread(void *thread_arg) { ::WaitForSingleObject(info->process_handle, INFINITE); ::GetExitCodeProcess(info->process_handle, &exit_code); - info->callback(info->baton, ::GetProcessId(info->process_handle), true, 0, exit_code); + info->callback(::GetProcessId(info->process_handle), true, 0, exit_code); ::CloseHandle(info->process_handle); delete (info); } diff --git a/source/Host/windows/PipeWindows.cpp b/source/Host/windows/PipeWindows.cpp index d4afd6e749436..6ed8d0e83f3ca 100644 --- a/source/Host/windows/PipeWindows.cpp +++ b/source/Host/windows/PipeWindows.cpp @@ -73,8 +73,8 @@ PipeWindows::CreateNew(llvm::StringRef name, bool child_process_inherit) // Always open for overlapped i/o. We implement blocking manually in Read and Write. DWORD read_mode = FILE_FLAG_OVERLAPPED; - m_read = - ::CreateNamedPipe(pipe_path.c_str(), PIPE_ACCESS_INBOUND | read_mode, PIPE_TYPE_BYTE | PIPE_WAIT, 1, 1024, 1024, 120 * 1000, NULL); + m_read = ::CreateNamedPipeA(pipe_path.c_str(), PIPE_ACCESS_INBOUND | read_mode, PIPE_TYPE_BYTE | PIPE_WAIT, 1, 1024, + 1024, 120 * 1000, NULL); if (INVALID_HANDLE_VALUE == m_read) return Error(::GetLastError(), eErrorTypeWin32); m_read_fd = _open_osfhandle((intptr_t)m_read, _O_RDONLY); @@ -153,7 +153,8 @@ PipeWindows::OpenNamedPipe(llvm::StringRef name, bool child_process_inherit, boo if (is_read) { - m_read = ::CreateFile(pipe_path.c_str(), GENERIC_READ, 0, &attributes, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + m_read = + ::CreateFileA(pipe_path.c_str(), GENERIC_READ, 0, &attributes, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); if (INVALID_HANDLE_VALUE == m_read) return Error(::GetLastError(), eErrorTypeWin32); @@ -164,7 +165,8 @@ PipeWindows::OpenNamedPipe(llvm::StringRef name, bool child_process_inherit, boo } else { - m_write = ::CreateFile(pipe_path.c_str(), GENERIC_WRITE, 0, &attributes, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + m_write = + ::CreateFileA(pipe_path.c_str(), GENERIC_WRITE, 0, &attributes, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); if (INVALID_HANDLE_VALUE == m_write) return Error(::GetLastError(), eErrorTypeWin32); diff --git a/source/Host/windows/ProcessLauncherWindows.cpp b/source/Host/windows/ProcessLauncherWindows.cpp index 355dd40544f9a..caf0fc71e1212 100644 --- a/source/Host/windows/ProcessLauncherWindows.cpp +++ b/source/Host/windows/ProcessLauncherWindows.cpp @@ -11,12 +11,38 @@ #include "lldb/Host/windows/ProcessLauncherWindows.h" #include "lldb/Target/ProcessLaunchInfo.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/ConvertUTF.h" + #include <string> #include <vector> using namespace lldb; using namespace lldb_private; +namespace +{ +void +CreateEnvironmentBuffer(const Args &env, std::vector<char> &buffer) +{ + if (env.GetArgumentCount() == 0) + return; + + // Environment buffer is a null terminated list of null terminated strings + for (int i = 0; i < env.GetArgumentCount(); ++i) + { + std::wstring warg; + if (llvm::ConvertUTF8toWide(env.GetArgumentAtIndex(i), warg)) + { + buffer.insert(buffer.end(), (char *)warg.c_str(), (char *)(warg.c_str() + warg.size() + 1)); + } + } + // One null wchar_t (to end the block) is two null bytes + buffer.push_back(0); + buffer.push_back(0); +} +} + HostProcess ProcessLauncherWindows::LaunchProcess(const ProcessLaunchInfo &launch_info, Error &error) { @@ -45,14 +71,27 @@ ProcessLauncherWindows::LaunchProcess(const ProcessLaunchInfo &launch_info, Erro startupinfo.wShowWindow = SW_HIDE; } - DWORD flags = CREATE_NEW_CONSOLE; + DWORD flags = CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT; if (launch_info.GetFlags().Test(eLaunchFlagDebug)) flags |= DEBUG_ONLY_THIS_PROCESS; + auto &env = const_cast<Args &>(launch_info.GetEnvironmentEntries()); + LPVOID env_block = nullptr; + ::CreateEnvironmentBuffer(env, environment); + if (!environment.empty()) + env_block = environment.data(); + executable = launch_info.GetExecutableFile().GetPath(); launch_info.GetArguments().GetQuotedCommandString(commandLine); - BOOL result = ::CreateProcessA(executable.c_str(), const_cast<char *>(commandLine.c_str()), NULL, NULL, TRUE, flags, NULL, - launch_info.GetWorkingDirectory().GetCString(), &startupinfo, &pi); + + std::wstring wexecutable, wcommandLine, wworkingDirectory; + llvm::ConvertUTF8toWide(executable, wexecutable); + llvm::ConvertUTF8toWide(commandLine, wcommandLine); + llvm::ConvertUTF8toWide(launch_info.GetWorkingDirectory().GetCString(), wworkingDirectory); + + wcommandLine.resize(PATH_MAX); // Needs to be over-allocated because CreateProcessW can modify it + BOOL result = ::CreateProcessW(wexecutable.c_str(), &wcommandLine[0], NULL, NULL, TRUE, flags, env_block, + wworkingDirectory.size() == 0 ? NULL : wworkingDirectory.c_str(), &startupinfo, &pi); if (result) { // Do not call CloseHandle on pi.hProcess, since we want to pass that back through the HostProcess. @@ -100,6 +139,8 @@ ProcessLauncherWindows::GetStdioHandle(const ProcessLaunchInfo &launch_info, int flags = FILE_FLAG_WRITE_THROUGH; } - HANDLE result = ::CreateFile(path, access, share, &secattr, create, flags, NULL); + std::wstring wpath; + llvm::ConvertUTF8toWide(path, wpath); + HANDLE result = ::CreateFileW(wpath.c_str(), access, share, &secattr, create, flags, NULL); return (result == INVALID_HANDLE_VALUE) ? NULL : result; } diff --git a/source/Host/windows/Windows.cpp b/source/Host/windows/Windows.cpp index 71bff7a9d2e2f..2aebb10bcdd92 100644 --- a/source/Host/windows/Windows.cpp +++ b/source/Host/windows/Windows.cpp @@ -12,6 +12,9 @@ #include "lldb/Host/windows/windows.h" #include "lldb/Host/windows/win32.h" +#include "llvm/Support/ConvertUTF.h" + +#include <assert.h> #include <stdio.h> #include <stdarg.h> #include <string.h> @@ -27,6 +30,29 @@ extern "C" int _chdir(const char *path); } +namespace +{ +bool +utf8ToWide(const char *utf8, wchar_t *buf, size_t bufSize) +{ + const UTF8 *sourceStart = reinterpret_cast<const UTF8 *>(utf8); + size_t sourceLen = strlen(utf8) + 1 /* convert null too */; + UTF16 *target = reinterpret_cast<UTF16 *>(buf); + ConversionFlags flags = strictConversion; + return ConvertUTF8toUTF16(&sourceStart, sourceStart + sourceLen, &target, target + bufSize, flags) == conversionOK; +} + +bool +wideToUtf8(const wchar_t *wide, char *buf, size_t bufSize) +{ + const UTF16 *sourceStart = reinterpret_cast<const UTF16 *>(wide); + size_t sourceLen = wcslen(wide) + 1 /* convert null too */; + UTF8 *target = reinterpret_cast<UTF8 *>(buf); + ConversionFlags flags = strictConversion; + return ConvertUTF16toUTF8(&sourceStart, sourceStart + sourceLen, &target, target + bufSize, flags) == conversionOK; +} +} + int vasprintf(char **ret, const char *fmt, va_list ap) { char *buf; @@ -75,81 +101,90 @@ char* strcasestr(const char *s, const char* find) char* realpath(const char * name, char * resolved) { - char *retname = NULL; /* we will return this, if we fail */ + char *retname = NULL; /* SUSv3 says we must set `errno = EINVAL', and return NULL, * if `name' is passed as a NULL pointer. */ - if (name == NULL) + { errno = EINVAL; + return NULL; + } /* Otherwise, `name' must refer to a readable filesystem object, * if we are going to resolve its absolute path name. */ - - else if (access(name, 4) == 0) + wchar_t wideNameBuffer[PATH_MAX]; + wchar_t *wideName = wideNameBuffer; + if (!utf8ToWide(name, wideName, PATH_MAX)) { - /* If `name' didn't point to an existing entity, - * then we don't get to here; we simply fall past this block, - * returning NULL, with `errno' appropriately set by `access'. - * - * When we _do_ get to here, then we can use `_fullpath' to - * resolve the full path for `name' into `resolved', but first, - * check that we have a suitable buffer, in which to return it. - */ + errno = EINVAL; + return NULL; + } - if ((retname = resolved) == NULL) - { - /* Caller didn't give us a buffer, so we'll exercise the - * option granted by SUSv3, and allocate one. - * - * `_fullpath' would do this for us, but it uses `malloc', and - * Microsoft's implementation doesn't set `errno' on failure. - * If we don't do this explicitly ourselves, then we will not - * know if `_fullpath' fails on `malloc' failure, or for some - * other reason, and we want to set `errno = ENOMEM' for the - * `malloc' failure case. - */ - - retname = (char*) malloc(_MAX_PATH); - } + if (_waccess(wideName, 4) != 0) + return NULL; + + /* If `name' didn't point to an existing entity, + * then we don't get to here; we simply fall past this block, + * returning NULL, with `errno' appropriately set by `access'. + * + * When we _do_ get to here, then we can use `_fullpath' to + * resolve the full path for `name' into `resolved', but first, + * check that we have a suitable buffer, in which to return it. + */ - /* By now, we should have a valid buffer. - * If we don't, then we know that `malloc' failed, - * so we can set `errno = ENOMEM' appropriately. + if ((retname = resolved) == NULL) + { + /* Caller didn't give us a buffer, so we'll exercise the + * option granted by SUSv3, and allocate one. + * + * `_fullpath' would do this for us, but it uses `malloc', and + * Microsoft's implementation doesn't set `errno' on failure. + * If we don't do this explicitly ourselves, then we will not + * know if `_fullpath' fails on `malloc' failure, or for some + * other reason, and we want to set `errno = ENOMEM' for the + * `malloc' failure case. */ + retname = (char *)malloc(PATH_MAX); if (retname == NULL) + { errno = ENOMEM; + return NULL; + } + } - /* Otherwise, when we do have a valid buffer, - * `_fullpath' should only fail if the path name is too long. - */ + /* Otherwise, when we do have a valid buffer, + * `_fullpath' should only fail if the path name is too long. + */ - else if ((retname = _fullpath(retname, name, _MAX_PATH)) == NULL) - errno = ENAMETOOLONG; + wchar_t wideFullPathBuffer[PATH_MAX]; + wchar_t *wideFullPath; + if ((wideFullPath = _wfullpath(wideFullPathBuffer, wideName, PATH_MAX)) == NULL) + { + errno = ENAMETOOLONG; + return NULL; } - /* By the time we get to here, - * `retname' either points to the required resolved path name, - * or it is NULL, with `errno' set appropriately, either of which - * is our required return condition. - */ + // Do a LongPath<->ShortPath roundtrip so that case is resolved by OS + // FIXME: Check for failure + size_t initialLength = wcslen(wideFullPath); + GetShortPathNameW(wideFullPath, wideNameBuffer, PATH_MAX); + GetLongPathNameW(wideNameBuffer, wideFullPathBuffer, initialLength + 1); - if (retname != NULL) + // Convert back to UTF-8 + if (!wideToUtf8(wideFullPathBuffer, retname, PATH_MAX)) { - // Do a LongPath<->ShortPath roundtrip so that case is resolved by OS - int initialLength = strlen(retname); - TCHAR buffer[MAX_PATH]; - GetShortPathName(retname, buffer, MAX_PATH); - GetLongPathName(buffer, retname, initialLength + 1); - - // Force drive to be upper case - if (retname[1] == ':') - retname[0] = toupper(retname[0]); + errno = EINVAL; + return NULL; } + // Force drive to be upper case + if (retname[1] == ':') + retname[0] = toupper(retname[0]); + return retname; } @@ -167,7 +202,27 @@ char* basename(char *path) // use _getcwd() instead of GetCurrentDirectory() because it updates errno char* getcwd(char* path, int max) { - return _getcwd(path, max); + assert(path == NULL || max <= PATH_MAX); + wchar_t wpath[PATH_MAX]; + if (wchar_t *wresult = _wgetcwd(wpath, PATH_MAX)) + { + // Caller is allowed to pass in NULL for `path`. + // In that case, we're supposed to allocate a + // buffer on the caller's behalf. + if (path == NULL) + { + max = UNI_MAX_UTF8_BYTES_PER_CODE_POINT * wcslen(wresult) + 1; + path = (char *)malloc(max); + if (path == NULL) + { + errno = ENOMEM; + return NULL; + } + } + if (wideToUtf8(wresult, path, max)) + return path; + } + return NULL; } // use _chdir() instead of SetCurrentDirectory() because it updates errno |