From: Mikhail Kurinnoi Date: Wed, 2 Feb 2022 10:56:46 +0000 (-0800) Subject: Fix MCD. X-Git-Tag: submit/tizen/20220315.024618~1 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=02e22e0705cedf68f6a24d461dd1b0ca913c1a5b;p=sdk%2Ftools%2Fnetcoredbg.git Fix MCD. --- diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b62617c..fe87a52 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -109,6 +109,7 @@ set(netcoredbg_SRC metadata/modules.cpp metadata/typeprinter.cpp protocols/cliprotocol.cpp + protocols/escaped_string.cpp protocols/protocol_utils.cpp protocols/miprotocol.cpp protocols/tokenizer.cpp @@ -118,20 +119,19 @@ set(netcoredbg_SRC errormessage.cpp main.cpp buildinfo.cpp - utils/escaped_string.cpp + utils/dynlibs_unix.cpp + utils/dynlibs_win32.cpp utils/filesystem.cpp + utils/filesystem_unix.cpp + utils/filesystem_win32.cpp utils/ioredirect.cpp + utils/iosystem_unix.cpp + utils/iosystem_win32.cpp + utils/interop_unix.cpp + utils/interop_win32.cpp + utils/platform_unix.cpp + utils/platform_win32.cpp utils/streams.cpp - unix/interop_unix.cpp - unix/dynlibs_unix.cpp - unix/filesystem_unix.cpp - unix/iosystem_unix.cpp - unix/platform_unix.cpp - windows/interop_win32.cpp - windows/dynlibs_win32.cpp - windows/filesystem_win32.cpp - windows/iosystem_win32.cpp - windows/platform_win32.cpp ) set(CMAKE_INCLUDE_CURRENT_DIR OFF) diff --git a/src/managed/interop.cpp b/src/managed/interop.cpp index b07e4cb..e12387b 100644 --- a/src/managed/interop.cpp +++ b/src/managed/interop.cpp @@ -114,6 +114,8 @@ int ReadMemoryForSymbols(uint64_t address, char *buffer, int cb) return 0; } +} // unnamed namespace + HRESULT LoadSymbolsForPortablePDB(const std::string &modulePath, BOOL isInMemory, BOOL isFileLayout, ULONG64 peAddress, ULONG64 peSize, ULONG64 inMemoryPdbAddress, ULONG64 inMemoryPdbSize, VOID **ppSymbolReaderHandle) { @@ -138,42 +140,11 @@ HRESULT LoadSymbolsForPortablePDB(const std::string &modulePath, BOOL isInMemory return S_OK; } -} // unnamed namespace - - SequencePoint::~SequencePoint() noexcept { Interop::SysFreeString(document); } -HRESULT LoadSymbols(IMetaDataImport *pMD, ICorDebugModule *pModule, VOID **ppSymbolReaderHandle) -{ - HRESULT Status = S_OK; - BOOL isDynamic = FALSE; - BOOL isInMemory = FALSE; - IfFailRet(pModule->IsDynamic(&isDynamic)); - IfFailRet(pModule->IsInMemory(&isInMemory)); - - if (isDynamic) - return E_FAIL; // Dynamic and in memory assemblies are a special case which we will ignore for now - - ULONG64 peAddress = 0; - ULONG32 peSize = 0; - IfFailRet(pModule->GetBaseAddress(&peAddress)); - IfFailRet(pModule->GetSize(&peSize)); - - return LoadSymbolsForPortablePDB( - GetModuleFileName(pModule), - isInMemory, - isInMemory, // isFileLayout - peAddress, - peSize, - 0, // inMemoryPdbAddress - 0, // inMemoryPdbSize - ppSymbolReaderHandle - ); -} - void DisposeSymbols(PVOID pSymbolReaderHandle) { std::unique_lock read_lock(CLRrwlock.reader); diff --git a/src/managed/interop.h b/src/managed/interop.h index cceb764..20cd602 100644 --- a/src/managed/interop.h +++ b/src/managed/interop.h @@ -83,7 +83,8 @@ namespace Interop // WARNING! Due to CoreCLR limitations, Shutdown() can't be called out of the Main() scope, for example, from global object destructor. void Shutdown(); - HRESULT LoadSymbols(IMetaDataImport *pMD, ICorDebugModule *pModule, VOID **ppSymbolReaderHandle); + HRESULT LoadSymbolsForPortablePDB(const std::string &modulePath, BOOL isInMemory, BOOL isFileLayout, ULONG64 peAddress, ULONG64 peSize, + ULONG64 inMemoryPdbAddress, ULONG64 inMemoryPdbSize, VOID **ppSymbolReaderHandle); void DisposeSymbols(PVOID pSymbolReaderHandle); HRESULT GetSequencePointByILOffset(PVOID pSymbolReaderHandle, mdMethodDef MethodToken, ULONG32 IlOffset, SequencePoint *sequencePoint); HRESULT GetNextSequencePointByILOffset(PVOID pSymbolReaderHandle, mdMethodDef MethodToken, ULONG32 IlOffset, ULONG32 &ilCloseOffset, bool *noUserCodeFound); diff --git a/src/metadata/modules.cpp b/src/metadata/modules.cpp index 0943233..176a600 100644 --- a/src/metadata/modules.cpp +++ b/src/metadata/modules.cpp @@ -717,6 +717,34 @@ bool Modules::FindLastIlOffsetAwaitInfo(CORDB_ADDRESS modAddress, mdMethodDef me return true; } +static HRESULT LoadSymbols(IMetaDataImport *pMD, ICorDebugModule *pModule, VOID **ppSymbolReaderHandle) +{ + HRESULT Status = S_OK; + BOOL isDynamic = FALSE; + BOOL isInMemory = FALSE; + IfFailRet(pModule->IsDynamic(&isDynamic)); + IfFailRet(pModule->IsInMemory(&isInMemory)); + + if (isDynamic) + return E_FAIL; // Dynamic and in memory assemblies are a special case which we will ignore for now + + ULONG64 peAddress = 0; + ULONG32 peSize = 0; + IfFailRet(pModule->GetBaseAddress(&peAddress)); + IfFailRet(pModule->GetSize(&peSize)); + + return Interop::LoadSymbolsForPortablePDB( + GetModuleFileName(pModule), + isInMemory, + isInMemory, // isFileLayout + peAddress, + peSize, + 0, // inMemoryPdbAddress + 0, // inMemoryPdbSize + ppSymbolReaderHandle + ); +} + HRESULT Modules::TryLoadModuleSymbols(ICorDebugModule *pModule, Module &module, bool needJMC, std::string &outputText) { HRESULT Status; @@ -730,7 +758,7 @@ HRESULT Modules::TryLoadModuleSymbols(ICorDebugModule *pModule, Module &module, module.name = GetFileName(module.path); PVOID pSymbolReaderHandle = nullptr; - Interop::LoadSymbols(pMDImport, pModule, &pSymbolReaderHandle); + LoadSymbols(pMDImport, pModule, &pSymbolReaderHandle); module.symbolStatus = pSymbolReaderHandle != nullptr ? SymbolsLoaded : SymbolsNotFound; if (module.symbolStatus == SymbolsLoaded) diff --git a/src/protocols/completions.h b/src/protocols/completions.h index ebd8485..20a1eae 100644 --- a/src/protocols/completions.h +++ b/src/protocols/completions.h @@ -5,7 +5,6 @@ #include #include #include -#include "utility.h" #include "utils/string_view.h" #include "utils/limits.h" diff --git a/src/protocols/escaped_string.cpp b/src/protocols/escaped_string.cpp new file mode 100644 index 0000000..523e3c7 --- /dev/null +++ b/src/protocols/escaped_string.cpp @@ -0,0 +1,130 @@ +// Copyright (C) 2020 Samsung Electronics Co., Ltd. +// See the LICENSE file in the project root for more information. + +#include +#include +#include "escaped_string.h" +#include "assert.h" + +namespace netcoredbg +{ + +EscapedStringInternal::EscapedStringImpl::EscapedStringImpl(const EscapedStringInternal::EscapedStringImpl::Params& params, Utility::string_view str, const TempRef& ref, bool isstring) +: + m_ref(&ref), m_params(params), m_input(str), m_result(), m_size(UndefinedSize), m_isstring(isstring), m_isresult(false) +{ + ref.set(this, &EscapedStringImpl::transform); +} + +// This function performs input string transformation according to specified (via `m_params) rules. +// Result will be passed to supplied callback function. +void EscapedStringInternal::EscapedStringImpl::operator()(void *thiz, void (*func)(void*, Utility::string_view)) +{ + // always have transformed result + if (m_isresult) + return func(thiz, {&m_result[0], m_result.size()}); + + // case, when no conversion needed + if (m_size == m_input.size()) + return func(thiz, m_input); + + // perform transformation and compute result size + size_t size = 0; + string_view src = m_input; + while (!src.empty()) + { + // try to find first forbidden character + auto it = std::find_first_of(src.begin(), src.end(), m_params.forbidden.begin(), m_params.forbidden.end()); + size_t prefix_size = it - src.begin(); + if (prefix_size) + { + // output any other charactes that preceede first forbidden character + func(thiz, src.substr(0, prefix_size)); + size += prefix_size; + } + + if (it != src.end()) + { + // find right substitution for forbidden character and output substituting pair of characters + auto ir = std::find(m_params.forbidden.begin(), m_params.forbidden.end(), *it); + string_view subst = m_params.subst[ir - m_params.forbidden.begin()]; + func(thiz, subst); + size += subst.size(); + prefix_size++; + } + + src.remove_prefix(prefix_size); + } + + // remember output size (to avoid computations in future) + if (m_size == UndefinedSize) + m_size = size; +} + +// function computes output size (but not produces the output) +size_t EscapedStringInternal::EscapedStringImpl::size() noexcept +{ + if (m_size == UndefinedSize) + (*this)(nullptr, [](void *, string_view){}); + + return m_size; +} + +// function allocates memory and transforms string in all cases, +// except of case, when transformation isn't required at all and +// input arguments still not destroyed. +EscapedStringInternal::EscapedStringImpl::operator Utility::string_view() noexcept +{ + if (! m_isresult) + { + if (size() == m_input.size()) + return m_input; + + transform(); + } + return {&m_result[0], m_result.size()}; +} + +// function allocates memory and transforms string. +EscapedStringInternal::EscapedStringImpl::operator const std::string&() +{ + if (!m_isresult) + transform(); + + return m_result; +} + +// function allocates memory and transforms string in all cases, +// except of case, when transformation isn't required at all, and +// input arguments still not destroyed, and input argument contains +// terminating zero. +const char* EscapedStringInternal::EscapedStringImpl::c_str() +{ + if (m_isstring && !m_isresult && size() == m_input.size()) + return m_input.data(); + else + return static_cast(*this).c_str(); +} + +// function performs string transformation to allocated memory +void EscapedStringInternal::EscapedStringImpl::transform() +{ + m_ref->reset(); + m_ref = nullptr; + + m_result.resize(size(), 0); + auto it = m_result.begin(); + + auto func = [&](string_view str) + { + m_result.replace(it, it + str.size(), str.begin(), str.end()); + it += str.size(); + }; + + (*this)(&func, [](void *fp, string_view str) { (*static_cast(fp))(str); }); + + m_isresult = true; + m_isstring = true; +} + +} // ::netcoredbg::Utility diff --git a/src/protocols/escaped_string.h b/src/protocols/escaped_string.h new file mode 100644 index 0000000..3a9a48e --- /dev/null +++ b/src/protocols/escaped_string.h @@ -0,0 +1,222 @@ +// Copyright (C) 2021 Samsung Electronics Co., Ltd. +// See the LICENSE file in the project root for more information. + +#pragma once +#include +#include +#include "utils/utility.h" +#include "utils/span.h" +#include "utils/string_view.h" + +namespace netcoredbg +{ + +// This namespace contains implementation details for few following classes, +// contents of this namespace isn't designated for direct use. +namespace EscapedStringInternal +{ + using Utility::string_view; + + // This class allows to perform some predefined actions in moment of time, + // when temporary object of type TempReference is destroyed. Typically it might + // be used to perform some sort of lazy evaluation, when some other class accepts + // it's arguments and temporary TempReference object. And it's expected, that + // later some evaluation on arguments might be performed. But arguments might + // be temporary objects, which will be deleted at end of full expression. + // So arguments should be copied, or evaluation performed, prior deleting + // the arguments. + // + // When const TempReference& used as last (default) argument to + // class constructor, it is guaranteed, that ~TempReference will be called + // exactly prior to deleting other temporary arguments, so some required actions + // might be performed before deleting other arguments. + // + // CAUTION: + // When TempReference is passed to ordinary function (not a class constructor), + // additionaly cautions is needed: in this case construction order of function + // arguments isn't predefined, as destruction order (which is reverse). In such + // situation TempReference might be used only and only in case, when no other + // arguments is strictly not a temporary variables. To ensure this, you should + // delete functions accepting r-values: void f(const Type&&) = delete. + // + template + struct TempReference + { + // This function should be called from constructor of T, and should pass + // reference to T's method, which be called on TempReference destruction. + void set(T* ptr, void (T::*func)()) const noexcept { m_ptr = ptr, m_func = func; } + + // This function might be called from T to cancel callback which is set with `set` + // function. Especially, `reset` might be called from T's destructor. + void reset() const noexcept { m_ptr = nullptr; } + + ~TempReference() { if (m_ptr) (m_ptr->*m_func)(); } + + private: + mutable T* m_ptr; + mutable void (T::*m_func)(); + }; + + + // This is actual implementation of `EscapedString` class. + struct EscapedStringImpl + { + // EscapeStrinct class performs type erasure, and information on + // it's template parameters should be stored in `Params` class. + struct Params + { + string_view forbidden; // characters which must be replaced + Utility::span subst; // strings to which `forbidden` characters must be replaced + char escape; // character, which preceedes each substitution + }; + + using TempRef = TempReference; + + // `str` is the source string, in which all `forbidden` characters must be replaced, + // `isstring` must be set to true only in case, when `str` contains terminating zero + // (to which `str->end()` points). + EscapedStringImpl(const Params& params, string_view str, const TempRef& ref, bool isstring); + + ~EscapedStringImpl() { if (m_ref) m_ref->reset(); } + + // see comments in `EscapeString` class below + void operator()(void *thiz, void (*func)(void*, string_view)); + size_t size() noexcept; + explicit operator const std::string&(); + operator string_view() noexcept; + const char* c_str(); + + private: + // This function performs input string (`m_input`) transformation and stores the + // result in (`m_result`). This function might be called in two cases: from + // destructor of `TempRef` object (before deleting function argument `str`, + // to which `m_input` currently points), or in case when result is needed + // in form of data (on call of any getter function, except of operator()). + void transform(); + + static const size_t UndefinedSize = size_t(0)-1; + + // This TempReference structure was passed as arument to EscapedString class + // constructor and continue to exist until end of full expression (till ';'). + // At end of full expression all temporary variables destroyed and this structure too. + // When this happens, TempReference calls transform() method from it's destructor. + // In transform() method the strings, which was passed as arguments to EscapedString + // constructor, is transformed and copied in allocated memory, so arguments might + // be safely deleted. In case, if such transformation and copying occurs before + // calling of temporary destructors, EscapedStringImpl::transform() calls m_ref->reset() + // to cancel callback from TempReference class. + const TempReference* m_ref; + + const Params& m_params; // character substitution rules + const string_view m_input; // points to input string + std::string m_result; // might contain result string (lazy evaluated) + size_t m_size; // size of result stirng (lazy evaluated) + bool m_isstring; // true if m_input->end() points to terminating zero + bool m_isresult; // true if m_result contain result string + }; +} // namespace EscapedStringInternal + + +/// This class allows lazy transformation of the given source string by substituting +/// set of "forbidden" characters with escape symbol and other substituted character. +/// The main idea is to avoid unwanted memory allocation: when it is possible no +/// memory allocation performed. To achieve this lazy evaluation is used: string +/// transformation is perfomed only if it is needed. +/// +/// This class should be used in two steps: first -- creation of class instance from given +/// arguments, at this time neither memory allocation, nor string transformation is +/// performed. Only arguments is remembered. And second step -- calling one of the +/// member functions (see below). At this time string transformation is performed +/// and memory might be allocated. The latter depends on string content, which +/// function and when was called... Memory allocation avoided in cases when no +/// transformation is required (string doesn't contain forbidden characters) and +/// when arguments, given to class constructor, still not destroyed. Also memory +/// allocation always avoided in case of call to `operator()`: in this case +/// output generated on the fly, but again only if constructor arguments still +/// exists. String is always transformed and copy saved to allocated memory in +/// moment, when constructor arguments destroyed (if `EscapedString` class instance +/// is not temporary variable itself, but continue to exist after end of full expression). +/// +template class EscapedString +{ + using string_view = Utility::string_view; + using EscapedStringImpl = EscapedStringInternal::EscapedStringImpl; + static EscapedStringImpl::Params params; + +public: + /// Construct `EscapedString` from c-strings (via implicit conversion) or string_view. + /// Second parameter must have default value (shouldn't be assigned explicitly). + EscapedString(string_view str, + const EscapedStringImpl::TempRef& ref = EscapedStringImpl::TempRef()) + : + impl(params, str, ref, false) + {} + + // Note: there is no reasons to disable temporaries of type std::string, because + // temporary objects construction and destruction ordef for constructors are + // predefined, and TempRef always destructed prior to temporary string. + + /// This function allows to avoid memory allocation: functor `func` given as argument + /// will consume parts of result (transformed string) which will be generated on the fly. + /// Functor `func` must accept `string_view` as argument. + template ()(std::declval()))> + void operator()(Func&& func) const + { + impl.operator()(&func, [](void *thiz, string_view str) { (*static_cast(thiz))(str); }); + } + + /// Function returns size of transformed string (no actual transformation performed). + size_t size() const noexcept { return impl.size(); } + + /// Function transforms string (if it was not transformed earlier) and allocates memory. + explicit operator const std::string&() const& { return static_cast(impl); } + + /// Function transforms string and allocates memory. + operator string_view() const noexcept { return static_cast(impl); } + + /// Function transforms string to allocated memory. + const char* c_str() const { return impl.c_str(); } + +private: + mutable EscapedStringImpl impl; +}; + + +// instantiation of Params structure for particular Traits template parameter +template EscapedStringInternal::EscapedStringImpl::Params EscapedString::params = +{ + { ((void)([]() -> void {static_assert(sizeof(Traits::forbidden_chars)-1 == Utility::Size(Traits::subst_chars), + "forbidden_chars and subst_chars must have same size!");}), + string_view(Traits::forbidden_chars)) }, + { Traits::subst_chars }, + Traits::escape_char +}; + + +/// Implementation of `operator<<`, which allows write `EscapedString` contents to +/// the supplied `std::ostream` avoiding memory allocations. +template +std::ostream& operator<<(std::ostream& os, const EscapedString& estr) +{ + using string_view = Utility::string_view; + estr([&](string_view str) -> void { os.write(str.data(), str.size()); }); + return os; +} + +/// Overloading of `operator+` for `EscapedString`, which allows concatenation +/// of `EscapedString` instances with ordinary strings. +template +std::string operator+(const EscapedString& left, T&& right) +{ + return static_cast(left) + std::forward(right); +} + +/// Overloading of `operator+` for `EscapedString`, which allows concatenation +/// of `EscapedString` instances with ordinary strings. +template +std::string operator+(T&& left, const EscapedString& right) +{ + return std::forward(left) + static_cast(right); +} + +} // ::netcoredbg diff --git a/src/protocols/miprotocol.h b/src/protocols/miprotocol.h index bdfd4d2..b5719bc 100644 --- a/src/protocols/miprotocol.h +++ b/src/protocols/miprotocol.h @@ -11,7 +11,7 @@ #include #include #include "utils/string_view.h" -#include "utils/escaped_string.h" +#include "protocols/escaped_string.h" #include "interfaces/idebugger.h" #include "interfaces/iprotocol.h" diff --git a/src/protocols/vscodeprotocol.cpp b/src/protocols/vscodeprotocol.cpp index 5098a49..4a71713 100644 --- a/src/protocols/vscodeprotocol.cpp +++ b/src/protocols/vscodeprotocol.cpp @@ -23,7 +23,7 @@ #include "utils/torelease.h" #include "utils/utf.h" #include "utils/logger.h" -#include "utils/escaped_string.h" +#include "protocols/escaped_string.h" // for convenience using json = nlohmann::json; diff --git a/src/unittests/CMakeLists.txt b/src/unittests/CMakeLists.txt index 3556199..44ee84b 100644 --- a/src/unittests/CMakeLists.txt +++ b/src/unittests/CMakeLists.txt @@ -29,26 +29,26 @@ find_package (Threads) # currently defined unit tests deftest(string_view string_view_test.cpp) deftest(span span_test.cpp) -deftest(escaped_string ../utils/escaped_string.cpp escaped_string_test.cpp) +deftest(escaped_string ../protocols/escaped_string.cpp escaped_string_test.cpp) deftest(iosystem iosystem_test.cpp - ${PROJECT_SOURCE_DIR}/src/windows/iosystem_win32.cpp - ${PROJECT_SOURCE_DIR}/src/unix/iosystem_unix.cpp + ${PROJECT_SOURCE_DIR}/src/utils/iosystem_win32.cpp + ${PROJECT_SOURCE_DIR}/src/utils/iosystem_unix.cpp ) deftest(streams streams_test.cpp ${PROJECT_SOURCE_DIR}/src/utils/streams.cpp - ${PROJECT_SOURCE_DIR}/src/windows/iosystem_win32.cpp - ${PROJECT_SOURCE_DIR}/src/unix/iosystem_unix.cpp + ${PROJECT_SOURCE_DIR}/src/utils/iosystem_win32.cpp + ${PROJECT_SOURCE_DIR}/src/utils/iosystem_unix.cpp ) deftest(ioredirect ioredirect_test.cpp ${PROJECT_SOURCE_DIR}/src/utils/ioredirect.cpp ${PROJECT_SOURCE_DIR}/src/utils/streams.cpp - ${PROJECT_SOURCE_DIR}/src/windows/iosystem_win32.cpp - ${PROJECT_SOURCE_DIR}/src/unix/iosystem_unix.cpp + ${PROJECT_SOURCE_DIR}/src/utils/iosystem_win32.cpp + ${PROJECT_SOURCE_DIR}/src/utils/iosystem_unix.cpp ${PROJECT_SOURCE_DIR}/src/utils/logger.cpp ) diff --git a/src/unittests/escaped_string_test.cpp b/src/unittests/escaped_string_test.cpp index f5672f5..9923c9e 100644 --- a/src/unittests/escaped_string_test.cpp +++ b/src/unittests/escaped_string_test.cpp @@ -4,7 +4,7 @@ #include #include #include "utils/string_view.h" -#include "utils/escaped_string.h" +#include "protocols/escaped_string.h" #include "compile_test.h" using namespace netcoredbg; diff --git a/src/unix/dynlibs_unix.cpp b/src/unix/dynlibs_unix.cpp deleted file mode 100644 index e7f745a..0000000 --- a/src/unix/dynlibs_unix.cpp +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (C) 2020 Samsung Electronics Co., Ltd. -// See the LICENSE file in the project root for more information. - -/// \file dynlibsi_unix.h This file contains unix-specific function definitions -/// required to work with dynamically loading libraries. - -#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) - -#if defined(__APPLE__) -#include -#include -#endif - -#include -#include "utils/limits.h" -#include "utils/dynlibs.h" - -namespace netcoredbg -{ - -// This functon load specified library and returns handle (which then -// can be passed to DLSym and DLCLose functions). -// In case of error function returns NULL. -DLHandle DLOpen(const std::string &path) -{ - return reinterpret_cast(::dlopen(path.c_str(), RTLD_GLOBAL | RTLD_NOW)); -} - -// This function resolves symbol address within library specified by handle, -// and returns it's address, in case of error function returns NULL. -void* DLSym(DLHandle handle, string_view name) -{ - char str[LINE_MAX]; - if (name.size() >= sizeof(str)) - return {}; - - name.copy(str, name.size()); - str[name.size()] = 0; - return ::dlsym(handle, str); -} - -/// This function unloads previously loadded library, specified by handle. -/// In case of error this function returns `false'. -bool DLClose(DLHandle handle) -{ - return ::dlclose(handle); -} - -} // ::netcoredbg -#endif // __unix__ diff --git a/src/unix/filesystem_unix.cpp b/src/unix/filesystem_unix.cpp deleted file mode 100644 index 2006e7b..0000000 --- a/src/unix/filesystem_unix.cpp +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (C) 2020 Samsung Electronics Co., Ltd. -// See the LICENSE file in the project root for more information. - -/// \file filesystem_unix.cpp -/// This file contains definitions of unix-specific functions related to file system. - -#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) -#ifdef __APPLE__ -#include -#endif -#include -#include -#include -#include -#include "utils/filesystem.h" -#include "utils/string_view.h" -#include "utils/span.h" -#include "utils/limits.h" - -namespace netcoredbg -{ - -using Utility::string_view; -template using span = Utility::span; - -const char* FileSystemTraits::PathSeparatorSymbols = "/"; - -namespace -{ -#ifdef __linux__ - std::string get_exe_path() - { - static const char self_link[] = "/proc/self/exe"; - char buffer[PATH_MAX]; - ssize_t r = readlink(self_link, buffer, PATH_MAX); - return std::string(buffer, r < 0 ? 0 : r); - } -#elif defined(__APPLE__) - std::string get_exe_path() - { - uint32_t lenActualPath = 0; - if (_NSGetExecutablePath(nullptr, &lenActualPath) == -1) - { - // OSX has placed the actual path length in lenActualPath, - // so re-attempt the operation - std::string resizedPath(lenActualPath, '\0'); - char *pResizedPath = const_cast(resizedPath.data()); - if (_NSGetExecutablePath(pResizedPath, &lenActualPath) == 0) - return pResizedPath; - } - return std::string(); - } -#endif -} - -// Function returns absolute path to currently running executable. -std::string GetExeAbsPath() -{ - static const std::string result(get_exe_path()); - return result; -} - -// Function returns path to directory, which should be used for creation of -// temporary files. Typically this is `/tmp` on Unix and something like -// `C:\Users\localuser\Appdata\Local\Temp` on Windows. -string_view GetTempDir() -{ - auto get_tmpdir = []() - { - const char *pPath = getenv("TMPDIR"); - if (pPath != nullptr) - return pPath; - else - return P_tmpdir; - }; - - static const std::string result {get_tmpdir()}; - return result; -} - - -// Function changes current working directory. Return value is `false` in case of error. -bool SetWorkDir(const std::string &path) -{ - return chdir(path.c_str()) == 0; -} - -} // ::netcoredbg -#endif __unix__ diff --git a/src/unix/filesystem_unix.h b/src/unix/filesystem_unix.h deleted file mode 100644 index b27882e..0000000 --- a/src/unix/filesystem_unix.h +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (C) 2020 Samsung Electronics Co., Ltd. -// See the LICENSE file in the project root for more information. - -/// \file filesystem_unix.h This file contains unix-specific details to FileSystem class. - -#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) -#pragma once -#include -#include "utils/limits.h" -#include "utils/platform.h" - -namespace netcoredbg -{ - template <> struct FileSystemTraits - { - const static size_t PathMax = PATH_MAX; - const static size_t NameMax = NAME_MAX; - const static char PathSeparator = '/'; - const static char* PathSeparatorSymbols; - }; -} -#endif diff --git a/src/unix/interop_unix.cpp b/src/unix/interop_unix.cpp deleted file mode 100644 index 1a22e57..0000000 --- a/src/unix/interop_unix.cpp +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright (C) 2020 Samsung Electronics Co., Ltd. -// See the LICENSE file in the project root for more information. - -/// \file interop_unix.h This file contains unix-specific functions for Interop class defined in interop.h - -#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "managed/interop.h" -#include "utils/filesystem.h" - -namespace netcoredbg -{ - -// This function searches *.dll files in specified directory and adds full paths to files -// to colon-separated list `tpaList'. -template <> -void InteropTraits::AddFilesFromDirectoryToTpaList(const std::string &directory, std::string& tpaList) -{ - const char * const tpaExtensions[] = { - ".ni.dll", // Probe for .ni.dll first so that it's preferred if ni and il coexist in the same dir - ".dll", - ".ni.exe", - ".exe", - }; - - DIR* dir = opendir(directory.c_str()); - if (dir == nullptr) - return; - - std::set addedAssemblies; - - // Walk the directory for each extension separately so that we first get files with .ni.dll extension, - // then files with .dll extension, etc. - for (size_t extIndex = 0; extIndex < sizeof(tpaExtensions) / sizeof(tpaExtensions[0]); extIndex++) - { - const char* ext = tpaExtensions[extIndex]; - int extLength = strlen(ext); - - struct dirent* entry; - - // For all entries in the directory - while ((entry = readdir(dir)) != nullptr) - { - // We are interested in files only - switch (entry->d_type) - { - case DT_REG: - break; - - // Handle symlinks and file systems that do not support d_type - case DT_LNK: - case DT_UNKNOWN: - { - std::string fullFilename; - - fullFilename.append(directory); - fullFilename += FileSystem::PathSeparator; - fullFilename.append(entry->d_name); - - struct stat sb; - if (stat(fullFilename.c_str(), &sb) == -1) - continue; - - if (!S_ISREG(sb.st_mode)) - continue; - } - break; - - default: - continue; - } - - std::string filename(entry->d_name); - - // Check if the extension matches the one we are looking for - int extPos = filename.length() - extLength; - if ((extPos <= 0) || (filename.compare(extPos, extLength, ext) != 0)) - { - continue; - } - - std::string filenameWithoutExt(filename.substr(0, extPos)); - - // Make sure if we have an assembly with multiple extensions present, - // we insert only one version of it. - if (addedAssemblies.find(filenameWithoutExt) == addedAssemblies.end()) - { - addedAssemblies.insert(filenameWithoutExt); - - tpaList.append(directory); - tpaList += FileSystem::PathSeparator; - tpaList.append(filename); - tpaList.append(":"); - } - } - - // Rewind the directory stream to be able to iterate over it for the next extension - rewinddir(dir); - } - - closedir(dir); -} - -// This function unsets `CORECLR_ENABLE_PROFILING' environment variable. -template <> -void InteropTraits::UnsetCoreCLREnv() -{ - unsetenv("CORECLR_ENABLE_PROFILING"); -} - -// Returns the length of a BSTR. -template <> -UINT InteropTraits::SysStringLen(BSTR bstrString) -{ - if (bstrString == NULL) - return 0; - return (unsigned int)((((DWORD FAR*)bstrString)[-1]) / sizeof(OLECHAR)); -} - -} // ::netcoredbg -#endif // __unix__ diff --git a/src/unix/iosystem_unix.cpp b/src/unix/iosystem_unix.cpp deleted file mode 100644 index 04831cc..0000000 --- a/src/unix/iosystem_unix.cpp +++ /dev/null @@ -1,405 +0,0 @@ -// Copyright (C) 2020 Samsung Electronics Co., Ltd. -// See the LICENSE file in the project root for more information. - -/// \file iosystem_unix.cpp This file contains unix-specific definitions of -/// IOSystem class members (see iosystem.h). - -#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "iosystem_unix.h" - -namespace -{ - // short alias for full class name - typedef netcoredbg::IOSystemTraits Class; - - - struct AsyncRead - { - int fd; - void* buffer; - size_t size; - - AsyncRead(int fd, void *buf, size_t size) : fd(fd), buffer(buf), size(size) {} - - Class::IOResult operator()() - { - // TODO need to optimize code to left only one syscall. - fd_set set; - FD_ZERO(&set); - FD_SET(fd, &set); - struct timeval tv = {0, 0}; - ssize_t result = ::select(fd + 1, &set, NULL, &set, &tv); - if (result == 0) - return {Class::IOResult::Pending, 0}; - - if (result >= 0) - result = read(fd, buffer, size); - - if (result < 0) - { - if (errno == EAGAIN) - return {Class::IOResult::Pending, 0}; - - // TODO make exception class - char msg[256]; - snprintf(msg, sizeof(msg), "select: %s", strerror(errno)); - throw std::runtime_error(msg); - } - - return {result == 0 ? Class::IOResult::Eof : Class::IOResult::Success, size_t(result)}; - } - - int poll(fd_set* read, fd_set *, fd_set* except) const - { - FD_SET(fd, read); - FD_SET(fd, except); - return fd; - } - }; - - struct AsyncWrite - { - int fd; - void const* buffer; - size_t size; - - AsyncWrite(int fd, const void *buf, size_t size) : fd(fd), buffer(buf), size(size) {} - - Class::IOResult operator()() - { - fd_set set; - FD_ZERO(&set); - FD_SET(fd, &set); - struct timeval tv = {0, 0}; - ssize_t result = select(fd + 1, NULL, &set, NULL, &tv); - if (result == 0) - return {Class::IOResult::Pending, 0}; - - if (result >= 0) - result = write(fd, buffer, size); - - if (result < 0) - { - if (errno == EAGAIN) - return {Class::IOResult::Pending, 0}; - - char msg[256]; - snprintf(msg, sizeof(msg), "select: %s", strerror(errno)); - throw std::runtime_error(msg); - } - - return {Class::IOResult::Success, size_t(result)}; - } - - - int poll(fd_set *, fd_set *write, fd_set *) const - { - FD_SET(fd, write); - return fd; - } - }; -} - - -template Class::AsyncHandle::Traits Class::AsyncHandle::TraitsImpl::traits = -{ - [](void *thiz) - -> Class::IOResult { return reinterpret_cast(thiz)->operator()(); }, - - [](void *thiz, fd_set* read, fd_set* write, fd_set* except) - -> int { return reinterpret_cast(thiz)->poll(read, write, except); }, - - [](void *src, void *dst) - -> void { *reinterpret_cast(dst) = *reinterpret_cast(src); }, - - [](void *thiz) - -> void { reinterpret_cast(thiz)->~T(); } -}; - - -// Function should create unnamed pipe and return two file handles -// (reading and writing pipe ends) or return empty file handles if pipe can't be created. -std::pair Class::unnamed_pipe() -{ - int fds[2]; - if (::pipe(fds) < 0) - { - perror("pipe"); - return {}; - } - - // TODO what to do with this? - signal(SIGPIPE, SIG_IGN); - - return { fds[0], fds[1] }; -} - - -// Function creates listening TCP socket on given port, waits, accepts single -// connection, and return file descriptor related to the accepted connection. -// In case of error, empty file handle will be returned. -Class::FileHandle Class::listen_socket(unsigned port) -{ - assert(port > 0 && port < 65536); - - int newsockfd; - socklen_t clilen; - struct sockaddr_in serv_addr, cli_addr; - - int sockFd = ::socket(AF_INET, SOCK_STREAM, 0); - if (sockFd < 0) - { - perror("can't create socket"); - return {}; - } - - int enable = 1; - if (setsockopt(sockFd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) - { - ::close(sockFd); - perror("can't set socket options"); - return {}; - } - memset(&serv_addr, 0, sizeof(serv_addr)); - - serv_addr.sin_family = AF_INET; - serv_addr.sin_addr.s_addr = INADDR_ANY; - serv_addr.sin_port = htons(port); - - if (::bind(sockFd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) - { - ::close(sockFd); - perror("can't bind to specified port"); - return {}; - } - - ::listen(sockFd, 1); - -#ifdef DEBUGGER_FOR_TIZEN - // On Tizen, launch_app won't terminate until stdin, stdout and stderr are closed. - // But Visual Studio initiates the connection only after launch_app termination, - // therefore we need to close the descriptors before the call to accept(). - int fd_null = open("/dev/null", O_WRONLY | O_APPEND); - if (fd_null < 0) - { - ::close(sockFd); - perror("can't open /dev/null"); - return {}; - } - - // Silently close previous stdin/stdout/stderr and reuse fds. - // Don't allow stdin read (EOF), but allow stdout/stderr write. - if (dup2(fd_null, STDIN_FILENO) == -1 || - dup2(fd_null, STDOUT_FILENO) == -1 || - dup2(fd_null, STDERR_FILENO) == -1) - { - ::close(sockFd); - perror("can't dup2"); - return {}; - } - - close(fd_null); - - //TODO on Tizen redirect stderr/stdout output into dlog -#endif - - clilen = sizeof(cli_addr); - newsockfd = ::accept(sockFd, (struct sockaddr *) &cli_addr, &clilen); - ::close(sockFd); - if (newsockfd < 0) - { - perror("accept"); - return {}; - } - - return newsockfd; -} - -// Enable/disable handle inheritance for child processes. -Class::IOResult Class::set_inherit(const FileHandle &fh, bool inherit) -{ - int flags = fcntl(fh.fd, F_GETFD); - if (flags < 0) - return {IOResult::Error, 0}; - - if (inherit) - flags &= ~FD_CLOEXEC; - else - flags |= FD_CLOEXEC; - - if (fcntl(fh.fd, F_SETFD, flags) < 0) - return {IOResult::Error, 0}; - - return {IOResult::Success, 0}; -} - -// Function perform reading from the file: it may read up to `count' bytes to `buf'. -Class::IOResult Class::read(const FileHandle &fh, void *buf, size_t count) -{ - ssize_t rsize = ::read(fh.fd, buf, count); - if (rsize < 0) - return { (errno == EAGAIN ? IOResult::Pending : IOResult::Error), 0 }; - else - return { (rsize == 0 ? IOResult::Eof : IOResult::Success), size_t(rsize) }; -} - - -// Function perform writing to the file: it may write up to `count' byte from `buf'. -Class::IOResult Class::write(const FileHandle &fh, const void *buf, size_t count) -{ - ssize_t wsize = ::write(fh.fd, buf, count); - if (wsize < 0) - return { (errno == EAGAIN ? IOResult::Pending : IOResult::Error), 0 }; - else - return { IOResult::Success, size_t(wsize) }; -} - - -Class::AsyncHandle Class::async_read(const FileHandle& fh, void *buf, size_t count) -{ - return fh.fd == -1 ? AsyncHandle() : AsyncHandle::create(fh.fd, buf, count); -} - -Class::AsyncHandle Class::async_write(const FileHandle& fh, const void *buf, size_t count) -{ - return fh.fd == -1 ? AsyncHandle() : AsyncHandle::create(fh.fd, buf, count); -} - - -bool Class::async_wait(IOSystem::AsyncHandleIterator begin, IOSystem::AsyncHandleIterator end, std::chrono::milliseconds timeout) -{ - fd_set read_set, write_set, except_set; - FD_ZERO(&read_set); - FD_ZERO(&write_set); - FD_ZERO(&except_set); - - int maxfd = -1; - for (IOSystem::AsyncHandleIterator it = begin; it != end; ++it) - { - if (*it) - maxfd = std::max(it->handle.poll(&read_set, &write_set, &except_set), maxfd); - } - - struct timeval tv; - std::chrono::microseconds us = std::chrono::duration_cast(timeout); - tv.tv_sec = us.count() / 1000000, tv.tv_usec = us.count() % 1000000; - - int result; - do result = ::select(maxfd + 1, &read_set, &write_set, &except_set, &tv); - while (result < 0 && errno == EINTR); - - if (result < 0) - { - char msg[256]; - snprintf(msg, sizeof(msg), "select: %s", strerror(errno)); - throw std::runtime_error(msg); - } - - return result > 0; -} - -Class::IOResult Class::async_cancel(Class::AsyncHandle& handle) -{ - if (!handle) - return {Class::IOResult::Error, 0}; - - handle = {}; - return {Class::IOResult::Success, 0}; -} - -Class::IOResult Class::async_result(Class::AsyncHandle& handle) -{ - if (!handle) - return {Class::IOResult::Error, 0}; - - auto result = handle(); - if (result.status != Class::IOResult::Pending) - handle = {}; - - return result; -} - - -// Function closes the file represented by file handle. -Class::IOResult Class::close(const FileHandle &fh) -{ - return { (::close(fh.fd) == 0 ? IOResult::Success : IOResult::Error), 0 }; -} - - -// This function returns triplet of currently selected standard files. -netcoredbg::IOSystem::StdFiles Class::get_std_files() -{ - static const IOSystem::FileHandle handles[std::tuple_size::value] = { - FileHandle(STDIN_FILENO), FileHandle(STDOUT_FILENO), FileHandle(STDERR_FILENO) - }; - return {handles[0], handles[1], handles[2]}; -} - - -// StdIOSwap class allows to substitute set of standard IO files with one provided to constructor. -// Substitution exists only during life time of StsIOSwap instance. -Class::StdIOSwap::StdIOSwap(const StdFiles& files) : m_valid(true) -{ - const static unsigned NFD = std::tuple_size::value; - static const int oldfd[NFD] = { StdFileType::Stdin, StdFileType::Stdout, StdFileType::Stderr }; - - const int newfd[NFD] = { - std::get(files).handle.fd, - std::get(files).handle.fd, - std::get(files).handle.fd }; - - for (unsigned n = 0; n < NFD; n++) - { - m_orig_fd[n] = ::dup(oldfd[n]); - if (m_orig_fd[n] == -1) - { - char msg[256]; - snprintf(msg, sizeof(msg), "dup(%d): %s", oldfd[n], strerror(errno)); - throw std::runtime_error(msg); - } - - if (::dup2(newfd[n], oldfd[n]) == -1) - { - char msg[256]; - snprintf(msg, sizeof(msg), "dup2(%d, %d): %s", newfd[n], oldfd[n], strerror(errno)); - throw std::runtime_error(msg); - } - } -} - - -Class::StdIOSwap::~StdIOSwap() -{ - if (!m_valid) - return; - - const static unsigned NFD = std::tuple_size::value; - static const int oldfd[NFD] = { StdFileType::Stdin, StdFileType::Stdout, StdFileType::Stderr }; - for (unsigned n = 0; n < NFD; n++) - { - if (::dup2(m_orig_fd[n], oldfd[n]) == -1) - { - abort(); - } - - ::close(m_orig_fd[n]); - } -} - -#endif // __unix__ diff --git a/src/unix/iosystem_unix.h b/src/unix/iosystem_unix.h deleted file mode 100644 index a8e4402..0000000 --- a/src/unix/iosystem_unix.h +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright (C) 2020 Samsung Electronics Co., Ltd. -// See the LICENSE file in the project root for more information. - -/// \file iosystem_unix.h This file contains unix-specific declaration of IOSystem class (see iosystem.h). - -#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) -#pragma once -#include -#include -#include -#include -#include - -#include "utils/platform.h" -#include "utils/iosystem.h" - -template <> struct netcoredbg::IOSystemTraits -{ - using IOSystem = typename netcoredbg::IOSystemImpl >; - using IOResult = IOSystem::IOResult; - - struct FileHandle - { - FileHandle() : fd(-1) {} - FileHandle(int n) : fd(n) {} - explicit operator bool() const { return fd != -1; } - - int fd; - }; - - struct AsyncHandle - { - struct Traits - { - IOResult (*oper)(void *thiz); - int (*poll)(void *thiz, fd_set *, fd_set *, fd_set *); - void (*move)(void* src, void *dst); - void (*destr)(void *thiz); - }; - - template struct TraitsImpl - { - static struct Traits traits; - }; - - const Traits *traits; - mutable char data alignas(__BIGGEST_ALIGNMENT__) [sizeof(void*) * 4]; - - explicit operator bool() const { return !!traits; } - - IOResult operator()() { assert(*this); return traits->oper(data); } - - int poll(fd_set* read, fd_set* write, fd_set* except) - { - assert(*this); - return traits->poll(data, read, write, except); - } - - AsyncHandle() : traits(nullptr) {} - - template - static AsyncHandle create(Args&&... args) - { - static_assert(sizeof(InstanceType) <= sizeof(data), "insufficiend data size"); - AsyncHandle result; - result.traits = &TraitsImpl::traits; - new (result.data) InstanceType(std::forward(args)...); - return result; - } - - AsyncHandle(AsyncHandle&& other) : traits(other.traits) - { - if (other) traits->move(other.data, data); - other.traits = nullptr; - } - - AsyncHandle& operator=(AsyncHandle&& other) - { - this->~AsyncHandle(); - return *new (this) AsyncHandle(std::move(other)); - } - - ~AsyncHandle() { if (*this) traits->destr(data); } - }; - - static std::pair unnamed_pipe(); - static FileHandle listen_socket(unsigned tcp_port); - static IOResult set_inherit(const FileHandle&, bool); - static IOResult read(const FileHandle&, void *buf, size_t count); - static IOResult write(const FileHandle&, const void *buf, size_t count); - static AsyncHandle async_read(const FileHandle&, void *buf, size_t count); - static AsyncHandle async_write(const FileHandle&, const void *buf, size_t count); - static bool async_wait(IOSystem::AsyncHandleIterator begin, IOSystem::AsyncHandleIterator end, std::chrono::milliseconds); - static IOResult async_cancel(AsyncHandle&); - static IOResult async_result(AsyncHandle&); - static IOResult close(const FileHandle&); - - struct StdIOSwap - { - using StdFiles = IOSystem::StdFiles; - using StdFileType = IOSystem::StdFileType; - StdIOSwap(const StdFiles &); - ~StdIOSwap(); - - StdIOSwap(StdIOSwap&& other) - { - m_valid = other.m_valid; - if (!m_valid) - return; - - other.m_valid = false; - for (unsigned n = 0; n < std::tuple_size::value; n++) - m_orig_fd[n] = other.m_orig_fd[n]; - } - - bool m_valid; - int m_orig_fd[std::tuple_size::value]; - }; - - static IOSystem::StdFiles get_std_files(); -}; - -#endif // __unix__ diff --git a/src/unix/platform_unix.cpp b/src/unix/platform_unix.cpp deleted file mode 100644 index 9d799c5..0000000 --- a/src/unix/platform_unix.cpp +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (C) 2020 Samsung Electronics Co., Ltd. -// See the LICENSE file in the project root for more information. - -/// \file platform_unix.cpp This file contains unix-specific function definitions, -/// for functions defined in platform.h - -#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) -#ifdef __APPLE__ -#include -#endif -#include -#include "utils/platform.h" - -extern char** environ; - -namespace netcoredbg -{ - -// Function returns memory mapping page size (like sysconf(_SC_PAGESIZE) on Unix). -unsigned long OSPageSize() -{ - static unsigned long pageSize = sysconf(_SC_PAGESIZE); - return pageSize; -} - - -// Function suspends process execution for specified amount of time (in microseconds) -void USleep(unsigned long usec) -{ - usleep(usec); -} - - -// Function returns list of environment variables (like char **environ). -char** GetSystemEnvironment() -{ -#if __APPLE__ - return *(_NSGetEnviron()); -#else // __APPLE__ - return environ; -#endif // __APPLE__ -} - -} // ::netcoredbg -#endif // __unix__ diff --git a/src/utility.h b/src/utility.h deleted file mode 100644 index 6dd50dc..0000000 --- a/src/utility.h +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (c) 2020 Samsung Electronics Co., LTD -// Distributed under the MIT License. -// See the LICENSE file in the project root for more information. - -// \file utility.h This file contains few supplimentar classes and functions residing in Utility namespace. - -#pragma once -#include - -namespace netcoredbg -{ - -namespace Utility -{ - -/// @{ This function is similar to `std::size()` from C++17 and allows to determine -/// the object size as number of elements, which might be stored within object -/// (opposed to sizeof(), which returns object sizes in bytes). Typically these -/// functions applicable to arrays and to classes like std::array. -template constexpr auto Size(const T& v) -> decltype(v.size()) { return v.size(); } -template constexpr size_t Size(const T (&)[N]) noexcept { return N; } -/// @} - - -/// This type is similar to `std::index_sequence` from C++14 and typically -/// should be used in pair with `MakeSequence` type to create sequence of -/// indices usable in template metaprogramming techniques. -template struct Sequence {}; - -// Actual implementation of MakeSequence type. -namespace Internals -{ - template struct MakeSequence : MakeSequence {}; - template struct MakeSequence<0, Index...> { typedef Sequence type; }; -} - -/// MakeSequence type is similar to `std::make_index_sequence` from C++14. -/// Instantination of this type exapands to `Sequence` type, where -/// `N` have values from 0 to `Size-1`. -template using MakeSequence = typename Internals::MakeSequence::type; - - -/// @{ This is similar to std:void_t which is defined in c++17. -template struct MakeVoid { typedef void type; }; -template using Void = typename MakeVoid::type; -/// @} - - -/// This template is similar to `offsetof` macros in plain C. It allows -/// to get offset of specified member in some particular class. -template -static inline constexpr size_t offset_of(const Member Owner::*mem) -{ - return reinterpret_cast(&(reinterpret_cast(0)->*mem)); -} - -/// This template is similar to well known `container_of` macros. It allows -/// to get pointer to owner class from pointer to member. -template -static inline constexpr Owner *container_of(Member *ptr, const Member Owner::*mem) -{ - return reinterpret_cast(reinterpret_cast(ptr) - offset_of(mem)); -} - - -// This is helper class which simplifies implementation of singleton classes. -// -// Usage example: -// 1) define dictinct type of singleton: typedef Singleton YourSingleton; -// 2) to access your singleton use expression: YourSingleton::instance().operations... -// -template struct Singleton -{ - static T& instance() - { - static T val; - return val; - } -}; - -} // Utility namespace -} // namespace netcoredbg diff --git a/src/utils/dynlibs_unix.cpp b/src/utils/dynlibs_unix.cpp new file mode 100644 index 0000000..e7f745a --- /dev/null +++ b/src/utils/dynlibs_unix.cpp @@ -0,0 +1,50 @@ +// Copyright (C) 2020 Samsung Electronics Co., Ltd. +// See the LICENSE file in the project root for more information. + +/// \file dynlibsi_unix.h This file contains unix-specific function definitions +/// required to work with dynamically loading libraries. + +#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) + +#if defined(__APPLE__) +#include +#include +#endif + +#include +#include "utils/limits.h" +#include "utils/dynlibs.h" + +namespace netcoredbg +{ + +// This functon load specified library and returns handle (which then +// can be passed to DLSym and DLCLose functions). +// In case of error function returns NULL. +DLHandle DLOpen(const std::string &path) +{ + return reinterpret_cast(::dlopen(path.c_str(), RTLD_GLOBAL | RTLD_NOW)); +} + +// This function resolves symbol address within library specified by handle, +// and returns it's address, in case of error function returns NULL. +void* DLSym(DLHandle handle, string_view name) +{ + char str[LINE_MAX]; + if (name.size() >= sizeof(str)) + return {}; + + name.copy(str, name.size()); + str[name.size()] = 0; + return ::dlsym(handle, str); +} + +/// This function unloads previously loadded library, specified by handle. +/// In case of error this function returns `false'. +bool DLClose(DLHandle handle) +{ + return ::dlclose(handle); +} + +} // ::netcoredbg +#endif // __unix__ diff --git a/src/utils/dynlibs_win32.cpp b/src/utils/dynlibs_win32.cpp new file mode 100644 index 0000000..7a1fcb0 --- /dev/null +++ b/src/utils/dynlibs_win32.cpp @@ -0,0 +1,44 @@ +// Copyright (C) 2020 Samsung Electronics Co., Ltd. +// See the LICENSE file in the project root for more information. + +/// \file dynlibsi_win32.h This file contains windows-specific function definitions +/// required to work with dynamically loading libraries. + +#ifdef WIN32 +#include +#include "utils/dynlibs.h" +#include "utils/limits.h" + +namespace netcoredbg +{ + +// This functon load specified library and returns handle (which then +// can be passed to DLSym and DLCLose functions). +// In case of error function returns NULL. +DLHandle DLOpen(const std::string &path) +{ + return reinterpret_cast(::LoadLibraryExA(path.c_str(), NULL, 0)); +} + +// This function resolves symbol address within library specified by handle, +// and returns it's address, in case of error function returns NULL. +void* DLSym(DLHandle handle, string_view name) +{ + char str[LINE_MAX]; + if (name.size() >= sizeof(str)) + return {}; + + name.copy(str, name.size()); + str[name.size()] = 0; + return ::GetProcAddress((HMODULE)handle, str); +} + +/// This function unloads previously loadded library, specified by handle. +/// In case of error this function returns `false'. +bool DLClose(DLHandle handle) +{ + return ::FreeLibrary(reinterpret_cast(handle)); +} + +} // ::netcoredbg +#endif diff --git a/src/utils/escaped_string.cpp b/src/utils/escaped_string.cpp deleted file mode 100644 index 523e3c7..0000000 --- a/src/utils/escaped_string.cpp +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright (C) 2020 Samsung Electronics Co., Ltd. -// See the LICENSE file in the project root for more information. - -#include -#include -#include "escaped_string.h" -#include "assert.h" - -namespace netcoredbg -{ - -EscapedStringInternal::EscapedStringImpl::EscapedStringImpl(const EscapedStringInternal::EscapedStringImpl::Params& params, Utility::string_view str, const TempRef& ref, bool isstring) -: - m_ref(&ref), m_params(params), m_input(str), m_result(), m_size(UndefinedSize), m_isstring(isstring), m_isresult(false) -{ - ref.set(this, &EscapedStringImpl::transform); -} - -// This function performs input string transformation according to specified (via `m_params) rules. -// Result will be passed to supplied callback function. -void EscapedStringInternal::EscapedStringImpl::operator()(void *thiz, void (*func)(void*, Utility::string_view)) -{ - // always have transformed result - if (m_isresult) - return func(thiz, {&m_result[0], m_result.size()}); - - // case, when no conversion needed - if (m_size == m_input.size()) - return func(thiz, m_input); - - // perform transformation and compute result size - size_t size = 0; - string_view src = m_input; - while (!src.empty()) - { - // try to find first forbidden character - auto it = std::find_first_of(src.begin(), src.end(), m_params.forbidden.begin(), m_params.forbidden.end()); - size_t prefix_size = it - src.begin(); - if (prefix_size) - { - // output any other charactes that preceede first forbidden character - func(thiz, src.substr(0, prefix_size)); - size += prefix_size; - } - - if (it != src.end()) - { - // find right substitution for forbidden character and output substituting pair of characters - auto ir = std::find(m_params.forbidden.begin(), m_params.forbidden.end(), *it); - string_view subst = m_params.subst[ir - m_params.forbidden.begin()]; - func(thiz, subst); - size += subst.size(); - prefix_size++; - } - - src.remove_prefix(prefix_size); - } - - // remember output size (to avoid computations in future) - if (m_size == UndefinedSize) - m_size = size; -} - -// function computes output size (but not produces the output) -size_t EscapedStringInternal::EscapedStringImpl::size() noexcept -{ - if (m_size == UndefinedSize) - (*this)(nullptr, [](void *, string_view){}); - - return m_size; -} - -// function allocates memory and transforms string in all cases, -// except of case, when transformation isn't required at all and -// input arguments still not destroyed. -EscapedStringInternal::EscapedStringImpl::operator Utility::string_view() noexcept -{ - if (! m_isresult) - { - if (size() == m_input.size()) - return m_input; - - transform(); - } - return {&m_result[0], m_result.size()}; -} - -// function allocates memory and transforms string. -EscapedStringInternal::EscapedStringImpl::operator const std::string&() -{ - if (!m_isresult) - transform(); - - return m_result; -} - -// function allocates memory and transforms string in all cases, -// except of case, when transformation isn't required at all, and -// input arguments still not destroyed, and input argument contains -// terminating zero. -const char* EscapedStringInternal::EscapedStringImpl::c_str() -{ - if (m_isstring && !m_isresult && size() == m_input.size()) - return m_input.data(); - else - return static_cast(*this).c_str(); -} - -// function performs string transformation to allocated memory -void EscapedStringInternal::EscapedStringImpl::transform() -{ - m_ref->reset(); - m_ref = nullptr; - - m_result.resize(size(), 0); - auto it = m_result.begin(); - - auto func = [&](string_view str) - { - m_result.replace(it, it + str.size(), str.begin(), str.end()); - it += str.size(); - }; - - (*this)(&func, [](void *fp, string_view str) { (*static_cast(fp))(str); }); - - m_isresult = true; - m_isstring = true; -} - -} // ::netcoredbg::Utility diff --git a/src/utils/escaped_string.h b/src/utils/escaped_string.h deleted file mode 100644 index e8f8466..0000000 --- a/src/utils/escaped_string.h +++ /dev/null @@ -1,222 +0,0 @@ -// Copyright (C) 2021 Samsung Electronics Co., Ltd. -// See the LICENSE file in the project root for more information. - -#pragma once -#include -#include -#include "utility.h" -#include "utils/span.h" -#include "utils/string_view.h" - -namespace netcoredbg -{ - -// This namespace contains implementation details for few following classes, -// contents of this namespace isn't designated for direct use. -namespace EscapedStringInternal -{ - using Utility::string_view; - - // This class allows to perform some predefined actions in moment of time, - // when temporary object of type TempReference is destroyed. Typically it might - // be used to perform some sort of lazy evaluation, when some other class accepts - // it's arguments and temporary TempReference object. And it's expected, that - // later some evaluation on arguments might be performed. But arguments might - // be temporary objects, which will be deleted at end of full expression. - // So arguments should be copied, or evaluation performed, prior deleting - // the arguments. - // - // When const TempReference& used as last (default) argument to - // class constructor, it is guaranteed, that ~TempReference will be called - // exactly prior to deleting other temporary arguments, so some required actions - // might be performed before deleting other arguments. - // - // CAUTION: - // When TempReference is passed to ordinary function (not a class constructor), - // additionaly cautions is needed: in this case construction order of function - // arguments isn't predefined, as destruction order (which is reverse). In such - // situation TempReference might be used only and only in case, when no other - // arguments is strictly not a temporary variables. To ensure this, you should - // delete functions accepting r-values: void f(const Type&&) = delete. - // - template - struct TempReference - { - // This function should be called from constructor of T, and should pass - // reference to T's method, which be called on TempReference destruction. - void set(T* ptr, void (T::*func)()) const noexcept { m_ptr = ptr, m_func = func; } - - // This function might be called from T to cancel callback which is set with `set` - // function. Especially, `reset` might be called from T's destructor. - void reset() const noexcept { m_ptr = nullptr; } - - ~TempReference() { if (m_ptr) (m_ptr->*m_func)(); } - - private: - mutable T* m_ptr; - mutable void (T::*m_func)(); - }; - - - // This is actual implementation of `EscapedString` class. - struct EscapedStringImpl - { - // EscapeStrinct class performs type erasure, and information on - // it's template parameters should be stored in `Params` class. - struct Params - { - string_view forbidden; // characters which must be replaced - Utility::span subst; // strings to which `forbidden` characters must be replaced - char escape; // character, which preceedes each substitution - }; - - using TempRef = TempReference; - - // `str` is the source string, in which all `forbidden` characters must be replaced, - // `isstring` must be set to true only in case, when `str` contains terminating zero - // (to which `str->end()` points). - EscapedStringImpl(const Params& params, string_view str, const TempRef& ref, bool isstring); - - ~EscapedStringImpl() { if (m_ref) m_ref->reset(); } - - // see comments in `EscapeString` class below - void operator()(void *thiz, void (*func)(void*, string_view)); - size_t size() noexcept; - explicit operator const std::string&(); - operator string_view() noexcept; - const char* c_str(); - - private: - // This function performs input string (`m_input`) transformation and stores the - // result in (`m_result`). This function might be called in two cases: from - // destructor of `TempRef` object (before deleting function argument `str`, - // to which `m_input` currently points), or in case when result is needed - // in form of data (on call of any getter function, except of operator()). - void transform(); - - static const size_t UndefinedSize = size_t(0)-1; - - // This TempReference structure was passed as arument to EscapedString class - // constructor and continue to exist until end of full expression (till ';'). - // At end of full expression all temporary variables destroyed and this structure too. - // When this happens, TempReference calls transform() method from it's destructor. - // In transform() method the strings, which was passed as arguments to EscapedString - // constructor, is transformed and copied in allocated memory, so arguments might - // be safely deleted. In case, if such transformation and copying occurs before - // calling of temporary destructors, EscapedStringImpl::transform() calls m_ref->reset() - // to cancel callback from TempReference class. - const TempReference* m_ref; - - const Params& m_params; // character substitution rules - const string_view m_input; // points to input string - std::string m_result; // might contain result string (lazy evaluated) - size_t m_size; // size of result stirng (lazy evaluated) - bool m_isstring; // true if m_input->end() points to terminating zero - bool m_isresult; // true if m_result contain result string - }; -} // namespace EscapedStringInternal - - -/// This class allows lazy transformation of the given source string by substituting -/// set of "forbidden" characters with escape symbol and other substituted character. -/// The main idea is to avoid unwanted memory allocation: when it is possible no -/// memory allocation performed. To achieve this lazy evaluation is used: string -/// transformation is perfomed only if it is needed. -/// -/// This class should be used in two steps: first -- creation of class instance from given -/// arguments, at this time neither memory allocation, nor string transformation is -/// performed. Only arguments is remembered. And second step -- calling one of the -/// member functions (see below). At this time string transformation is performed -/// and memory might be allocated. The latter depends on string content, which -/// function and when was called... Memory allocation avoided in cases when no -/// transformation is required (string doesn't contain forbidden characters) and -/// when arguments, given to class constructor, still not destroyed. Also memory -/// allocation always avoided in case of call to `operator()`: in this case -/// output generated on the fly, but again only if constructor arguments still -/// exists. String is always transformed and copy saved to allocated memory in -/// moment, when constructor arguments destroyed (if `EscapedString` class instance -/// is not temporary variable itself, but continue to exist after end of full expression). -/// -template class EscapedString -{ - using string_view = Utility::string_view; - using EscapedStringImpl = EscapedStringInternal::EscapedStringImpl; - static EscapedStringImpl::Params params; - -public: - /// Construct `EscapedString` from c-strings (via implicit conversion) or string_view. - /// Second parameter must have default value (shouldn't be assigned explicitly). - EscapedString(string_view str, - const EscapedStringImpl::TempRef& ref = EscapedStringImpl::TempRef()) - : - impl(params, str, ref, false) - {} - - // Note: there is no reasons to disable temporaries of type std::string, because - // temporary objects construction and destruction ordef for constructors are - // predefined, and TempRef always destructed prior to temporary string. - - /// This function allows to avoid memory allocation: functor `func` given as argument - /// will consume parts of result (transformed string) which will be generated on the fly. - /// Functor `func` must accept `string_view` as argument. - template ()(std::declval()))> - void operator()(Func&& func) const - { - impl.operator()(&func, [](void *thiz, string_view str) { (*static_cast(thiz))(str); }); - } - - /// Function returns size of transformed string (no actual transformation performed). - size_t size() const noexcept { return impl.size(); } - - /// Function transforms string (if it was not transformed earlier) and allocates memory. - explicit operator const std::string&() const& { return static_cast(impl); } - - /// Function transforms string and allocates memory. - operator string_view() const noexcept { return static_cast(impl); } - - /// Function transforms string to allocated memory. - const char* c_str() const { return impl.c_str(); } - -private: - mutable EscapedStringImpl impl; -}; - - -// instantiation of Params structure for particular Traits template parameter -template EscapedStringInternal::EscapedStringImpl::Params EscapedString::params = -{ - { ((void)([]() -> void {static_assert(sizeof(Traits::forbidden_chars)-1 == Utility::Size(Traits::subst_chars), - "forbidden_chars and subst_chars must have same size!");}), - string_view(Traits::forbidden_chars)) }, - { Traits::subst_chars }, - Traits::escape_char -}; - - -/// Implementation of `operator<<`, which allows write `EscapedString` contents to -/// the supplied `std::ostream` avoiding memory allocations. -template -std::ostream& operator<<(std::ostream& os, const EscapedString& estr) -{ - using string_view = Utility::string_view; - estr([&](string_view str) -> void { os.write(str.data(), str.size()); }); - return os; -} - -/// Overloading of `operator+` for `EscapedString`, which allows concatenation -/// of `EscapedString` instances with ordinary strings. -template -std::string operator+(const EscapedString& left, T&& right) -{ - return static_cast(left) + std::forward(right); -} - -/// Overloading of `operator+` for `EscapedString`, which allows concatenation -/// of `EscapedString` instances with ordinary strings. -template -std::string operator+(T&& left, const EscapedString& right) -{ - return std::forward(left) + static_cast(right); -} - -} // ::netcoredbg diff --git a/src/utils/filesystem_unix.cpp b/src/utils/filesystem_unix.cpp new file mode 100644 index 0000000..2006e7b --- /dev/null +++ b/src/utils/filesystem_unix.cpp @@ -0,0 +1,89 @@ +// Copyright (C) 2020 Samsung Electronics Co., Ltd. +// See the LICENSE file in the project root for more information. + +/// \file filesystem_unix.cpp +/// This file contains definitions of unix-specific functions related to file system. + +#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) +#ifdef __APPLE__ +#include +#endif +#include +#include +#include +#include +#include "utils/filesystem.h" +#include "utils/string_view.h" +#include "utils/span.h" +#include "utils/limits.h" + +namespace netcoredbg +{ + +using Utility::string_view; +template using span = Utility::span; + +const char* FileSystemTraits::PathSeparatorSymbols = "/"; + +namespace +{ +#ifdef __linux__ + std::string get_exe_path() + { + static const char self_link[] = "/proc/self/exe"; + char buffer[PATH_MAX]; + ssize_t r = readlink(self_link, buffer, PATH_MAX); + return std::string(buffer, r < 0 ? 0 : r); + } +#elif defined(__APPLE__) + std::string get_exe_path() + { + uint32_t lenActualPath = 0; + if (_NSGetExecutablePath(nullptr, &lenActualPath) == -1) + { + // OSX has placed the actual path length in lenActualPath, + // so re-attempt the operation + std::string resizedPath(lenActualPath, '\0'); + char *pResizedPath = const_cast(resizedPath.data()); + if (_NSGetExecutablePath(pResizedPath, &lenActualPath) == 0) + return pResizedPath; + } + return std::string(); + } +#endif +} + +// Function returns absolute path to currently running executable. +std::string GetExeAbsPath() +{ + static const std::string result(get_exe_path()); + return result; +} + +// Function returns path to directory, which should be used for creation of +// temporary files. Typically this is `/tmp` on Unix and something like +// `C:\Users\localuser\Appdata\Local\Temp` on Windows. +string_view GetTempDir() +{ + auto get_tmpdir = []() + { + const char *pPath = getenv("TMPDIR"); + if (pPath != nullptr) + return pPath; + else + return P_tmpdir; + }; + + static const std::string result {get_tmpdir()}; + return result; +} + + +// Function changes current working directory. Return value is `false` in case of error. +bool SetWorkDir(const std::string &path) +{ + return chdir(path.c_str()) == 0; +} + +} // ::netcoredbg +#endif __unix__ diff --git a/src/utils/filesystem_unix.h b/src/utils/filesystem_unix.h new file mode 100644 index 0000000..b27882e --- /dev/null +++ b/src/utils/filesystem_unix.h @@ -0,0 +1,22 @@ +// Copyright (C) 2020 Samsung Electronics Co., Ltd. +// See the LICENSE file in the project root for more information. + +/// \file filesystem_unix.h This file contains unix-specific details to FileSystem class. + +#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) +#pragma once +#include +#include "utils/limits.h" +#include "utils/platform.h" + +namespace netcoredbg +{ + template <> struct FileSystemTraits + { + const static size_t PathMax = PATH_MAX; + const static size_t NameMax = NAME_MAX; + const static char PathSeparator = '/'; + const static char* PathSeparatorSymbols; + }; +} +#endif diff --git a/src/utils/filesystem_win32.cpp b/src/utils/filesystem_win32.cpp new file mode 100644 index 0000000..c5380c4 --- /dev/null +++ b/src/utils/filesystem_win32.cpp @@ -0,0 +1,52 @@ +// Copyright (C) 2020 Samsung Electronics Co., Ltd. +// See the LICENSE file in the project root for more information. + +/// \file filesystem_win32.cpp +/// This file contains definitions of windows-specific functions related to file system. + +#ifdef WIN32 +#include +#include +#include "utils/filesystem.h" +#include "utils/limits.h" + + +namespace netcoredbg +{ + +const char* FileSystemTraits::PathSeparatorSymbols = "/\\"; + +// Function returns absolute path to currently running executable. +std::string GetExeAbsPath() +{ + const size_t MAX_LONGPATH = 1024; + char hostPath[MAX_LONGPATH + 1]; + static const std::string result(hostPath, ::GetModuleFileNameA(NULL, hostPath, MAX_LONGPATH)); + return result; +} + + +// Function returns path to directory, which should be used for creation of +// temporary files. Typically this is `/tmp` on Unix and something like +// `C:\Users\localuser\Appdata\Local\Temp` on Windows. +string_view GetTempDir() +{ + CHAR path[MAX_PATH + 1]; + static const std::string result(path, GetTempPathA(MAX_PATH, path)); + return result; +} + + +// Function changes current working directory. Return value is `false` in case of error. +bool SetWorkDir(const std::string &path) +{ + // In the ANSI version of this function, the name is limited to MAX_PATH characters. + // https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setcurrentdirectory + if (path.size() >= MAX_PATH) + return false; + + return SetCurrentDirectoryA(path.c_str()); +} + +} // ::netcoredbg +#endif diff --git a/src/utils/filesystem_win32.h b/src/utils/filesystem_win32.h new file mode 100644 index 0000000..ac14fcf --- /dev/null +++ b/src/utils/filesystem_win32.h @@ -0,0 +1,21 @@ +// Copyright (C) 2020 Samsung Electronics Co., Ltd. +// See the LICENSE file in the project root for more information. + +/// \file filesystem_win32.h This file contains windows-specific details to FileSystem class. + +#ifdef WIN32 +#pragma once +#include +#include + +namespace netcoredbg +{ + template <> struct FileSystemTraits + { + const static size_t PathMax = MAX_PATH; + const static size_t NameMax = MAX_PATH - 1; // not include terminal null. + const static char PathSeparator = '\\'; + const static char* PathSeparatorSymbols; + }; +} +#endif diff --git a/src/utils/interop_unix.cpp b/src/utils/interop_unix.cpp new file mode 100644 index 0000000..1a22e57 --- /dev/null +++ b/src/utils/interop_unix.cpp @@ -0,0 +1,131 @@ +// Copyright (C) 2020 Samsung Electronics Co., Ltd. +// See the LICENSE file in the project root for more information. + +/// \file interop_unix.h This file contains unix-specific functions for Interop class defined in interop.h + +#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "managed/interop.h" +#include "utils/filesystem.h" + +namespace netcoredbg +{ + +// This function searches *.dll files in specified directory and adds full paths to files +// to colon-separated list `tpaList'. +template <> +void InteropTraits::AddFilesFromDirectoryToTpaList(const std::string &directory, std::string& tpaList) +{ + const char * const tpaExtensions[] = { + ".ni.dll", // Probe for .ni.dll first so that it's preferred if ni and il coexist in the same dir + ".dll", + ".ni.exe", + ".exe", + }; + + DIR* dir = opendir(directory.c_str()); + if (dir == nullptr) + return; + + std::set addedAssemblies; + + // Walk the directory for each extension separately so that we first get files with .ni.dll extension, + // then files with .dll extension, etc. + for (size_t extIndex = 0; extIndex < sizeof(tpaExtensions) / sizeof(tpaExtensions[0]); extIndex++) + { + const char* ext = tpaExtensions[extIndex]; + int extLength = strlen(ext); + + struct dirent* entry; + + // For all entries in the directory + while ((entry = readdir(dir)) != nullptr) + { + // We are interested in files only + switch (entry->d_type) + { + case DT_REG: + break; + + // Handle symlinks and file systems that do not support d_type + case DT_LNK: + case DT_UNKNOWN: + { + std::string fullFilename; + + fullFilename.append(directory); + fullFilename += FileSystem::PathSeparator; + fullFilename.append(entry->d_name); + + struct stat sb; + if (stat(fullFilename.c_str(), &sb) == -1) + continue; + + if (!S_ISREG(sb.st_mode)) + continue; + } + break; + + default: + continue; + } + + std::string filename(entry->d_name); + + // Check if the extension matches the one we are looking for + int extPos = filename.length() - extLength; + if ((extPos <= 0) || (filename.compare(extPos, extLength, ext) != 0)) + { + continue; + } + + std::string filenameWithoutExt(filename.substr(0, extPos)); + + // Make sure if we have an assembly with multiple extensions present, + // we insert only one version of it. + if (addedAssemblies.find(filenameWithoutExt) == addedAssemblies.end()) + { + addedAssemblies.insert(filenameWithoutExt); + + tpaList.append(directory); + tpaList += FileSystem::PathSeparator; + tpaList.append(filename); + tpaList.append(":"); + } + } + + // Rewind the directory stream to be able to iterate over it for the next extension + rewinddir(dir); + } + + closedir(dir); +} + +// This function unsets `CORECLR_ENABLE_PROFILING' environment variable. +template <> +void InteropTraits::UnsetCoreCLREnv() +{ + unsetenv("CORECLR_ENABLE_PROFILING"); +} + +// Returns the length of a BSTR. +template <> +UINT InteropTraits::SysStringLen(BSTR bstrString) +{ + if (bstrString == NULL) + return 0; + return (unsigned int)((((DWORD FAR*)bstrString)[-1]) / sizeof(OLECHAR)); +} + +} // ::netcoredbg +#endif // __unix__ diff --git a/src/utils/interop_win32.cpp b/src/utils/interop_win32.cpp new file mode 100644 index 0000000..a0d06f8 --- /dev/null +++ b/src/utils/interop_win32.cpp @@ -0,0 +1,95 @@ +// Copyright (C) 2020 Samsung Electronics Co., Ltd. +// See the LICENSE file in the project root for more information. + +/// \file interop_win32.h This file contains windows-specific functions for Interop class defined in interop.h + +#ifdef WIN32 +#include +#include +#include +#include +#include + +#include "managed/interop.h" +#include "utils/filesystem.h" +#include "utils/limits.h" + + +namespace netcoredbg +{ + +// This function searches *.dll files in specified directory and adds full paths to files +// to semicolon-separated list `tpaList'. +template <> +void InteropTraits::AddFilesFromDirectoryToTpaList(const std::string &directory, std::string& tpaList) +{ + const char * const tpaExtensions[] = { + "*.ni.dll", // Probe for .ni.dll first so that it's preferred if ni and il coexist in the same dir + "*.dll", + "*.ni.exe", + "*.exe", + }; + + std::set addedAssemblies; + + // Walk the directory for each extension separately so that we first get files with .ni.dll extension, + // then files with .dll extension, etc. + for (int extIndex = 0; extIndex < sizeof(tpaExtensions) / sizeof(tpaExtensions[0]); extIndex++) + { + const char* ext = tpaExtensions[extIndex]; + size_t extLength = strlen(ext); + + std::string assemblyPath(directory); + assemblyPath += FileSystem::PathSeparator; + assemblyPath.append(tpaExtensions[extIndex]); + + WIN32_FIND_DATAA data; + HANDLE findHandle = FindFirstFileA(assemblyPath.c_str(), &data); + + if (findHandle != INVALID_HANDLE_VALUE) + { + do + { + if (!(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + { + + std::string filename(data.cFileName); + size_t extPos = filename.length() - extLength; + std::string filenameWithoutExt(filename.substr(0, extPos)); + + // Make sure if we have an assembly with multiple extensions present, + // we insert only one version of it. + if (addedAssemblies.find(filenameWithoutExt) == addedAssemblies.end()) + { + addedAssemblies.insert(filenameWithoutExt); + + tpaList.append(directory); + tpaList += FileSystem::PathSeparator; + tpaList.append(filename); + tpaList.append(";"); + } + } + } + while (0 != FindNextFileA(findHandle, &data)); + + FindClose(findHandle); + } + } +} + +// This function unsets `CORECLR_ENABLE_PROFILING' environment variable. +template <> +void InteropTraits::UnsetCoreCLREnv() +{ + _putenv("CORECLR_ENABLE_PROFILING="); +} + +// Returns the length of a BSTR. +template <> +UINT InteropTraits::SysStringLen(BSTR bstrString) +{ + return ::SysStringLen(bstrString); +} + +} // ::netcoredbg +#endif // WIN32 diff --git a/src/utils/iosystem_unix.cpp b/src/utils/iosystem_unix.cpp new file mode 100644 index 0000000..04831cc --- /dev/null +++ b/src/utils/iosystem_unix.cpp @@ -0,0 +1,405 @@ +// Copyright (C) 2020 Samsung Electronics Co., Ltd. +// See the LICENSE file in the project root for more information. + +/// \file iosystem_unix.cpp This file contains unix-specific definitions of +/// IOSystem class members (see iosystem.h). + +#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "iosystem_unix.h" + +namespace +{ + // short alias for full class name + typedef netcoredbg::IOSystemTraits Class; + + + struct AsyncRead + { + int fd; + void* buffer; + size_t size; + + AsyncRead(int fd, void *buf, size_t size) : fd(fd), buffer(buf), size(size) {} + + Class::IOResult operator()() + { + // TODO need to optimize code to left only one syscall. + fd_set set; + FD_ZERO(&set); + FD_SET(fd, &set); + struct timeval tv = {0, 0}; + ssize_t result = ::select(fd + 1, &set, NULL, &set, &tv); + if (result == 0) + return {Class::IOResult::Pending, 0}; + + if (result >= 0) + result = read(fd, buffer, size); + + if (result < 0) + { + if (errno == EAGAIN) + return {Class::IOResult::Pending, 0}; + + // TODO make exception class + char msg[256]; + snprintf(msg, sizeof(msg), "select: %s", strerror(errno)); + throw std::runtime_error(msg); + } + + return {result == 0 ? Class::IOResult::Eof : Class::IOResult::Success, size_t(result)}; + } + + int poll(fd_set* read, fd_set *, fd_set* except) const + { + FD_SET(fd, read); + FD_SET(fd, except); + return fd; + } + }; + + struct AsyncWrite + { + int fd; + void const* buffer; + size_t size; + + AsyncWrite(int fd, const void *buf, size_t size) : fd(fd), buffer(buf), size(size) {} + + Class::IOResult operator()() + { + fd_set set; + FD_ZERO(&set); + FD_SET(fd, &set); + struct timeval tv = {0, 0}; + ssize_t result = select(fd + 1, NULL, &set, NULL, &tv); + if (result == 0) + return {Class::IOResult::Pending, 0}; + + if (result >= 0) + result = write(fd, buffer, size); + + if (result < 0) + { + if (errno == EAGAIN) + return {Class::IOResult::Pending, 0}; + + char msg[256]; + snprintf(msg, sizeof(msg), "select: %s", strerror(errno)); + throw std::runtime_error(msg); + } + + return {Class::IOResult::Success, size_t(result)}; + } + + + int poll(fd_set *, fd_set *write, fd_set *) const + { + FD_SET(fd, write); + return fd; + } + }; +} + + +template Class::AsyncHandle::Traits Class::AsyncHandle::TraitsImpl::traits = +{ + [](void *thiz) + -> Class::IOResult { return reinterpret_cast(thiz)->operator()(); }, + + [](void *thiz, fd_set* read, fd_set* write, fd_set* except) + -> int { return reinterpret_cast(thiz)->poll(read, write, except); }, + + [](void *src, void *dst) + -> void { *reinterpret_cast(dst) = *reinterpret_cast(src); }, + + [](void *thiz) + -> void { reinterpret_cast(thiz)->~T(); } +}; + + +// Function should create unnamed pipe and return two file handles +// (reading and writing pipe ends) or return empty file handles if pipe can't be created. +std::pair Class::unnamed_pipe() +{ + int fds[2]; + if (::pipe(fds) < 0) + { + perror("pipe"); + return {}; + } + + // TODO what to do with this? + signal(SIGPIPE, SIG_IGN); + + return { fds[0], fds[1] }; +} + + +// Function creates listening TCP socket on given port, waits, accepts single +// connection, and return file descriptor related to the accepted connection. +// In case of error, empty file handle will be returned. +Class::FileHandle Class::listen_socket(unsigned port) +{ + assert(port > 0 && port < 65536); + + int newsockfd; + socklen_t clilen; + struct sockaddr_in serv_addr, cli_addr; + + int sockFd = ::socket(AF_INET, SOCK_STREAM, 0); + if (sockFd < 0) + { + perror("can't create socket"); + return {}; + } + + int enable = 1; + if (setsockopt(sockFd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) + { + ::close(sockFd); + perror("can't set socket options"); + return {}; + } + memset(&serv_addr, 0, sizeof(serv_addr)); + + serv_addr.sin_family = AF_INET; + serv_addr.sin_addr.s_addr = INADDR_ANY; + serv_addr.sin_port = htons(port); + + if (::bind(sockFd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) + { + ::close(sockFd); + perror("can't bind to specified port"); + return {}; + } + + ::listen(sockFd, 1); + +#ifdef DEBUGGER_FOR_TIZEN + // On Tizen, launch_app won't terminate until stdin, stdout and stderr are closed. + // But Visual Studio initiates the connection only after launch_app termination, + // therefore we need to close the descriptors before the call to accept(). + int fd_null = open("/dev/null", O_WRONLY | O_APPEND); + if (fd_null < 0) + { + ::close(sockFd); + perror("can't open /dev/null"); + return {}; + } + + // Silently close previous stdin/stdout/stderr and reuse fds. + // Don't allow stdin read (EOF), but allow stdout/stderr write. + if (dup2(fd_null, STDIN_FILENO) == -1 || + dup2(fd_null, STDOUT_FILENO) == -1 || + dup2(fd_null, STDERR_FILENO) == -1) + { + ::close(sockFd); + perror("can't dup2"); + return {}; + } + + close(fd_null); + + //TODO on Tizen redirect stderr/stdout output into dlog +#endif + + clilen = sizeof(cli_addr); + newsockfd = ::accept(sockFd, (struct sockaddr *) &cli_addr, &clilen); + ::close(sockFd); + if (newsockfd < 0) + { + perror("accept"); + return {}; + } + + return newsockfd; +} + +// Enable/disable handle inheritance for child processes. +Class::IOResult Class::set_inherit(const FileHandle &fh, bool inherit) +{ + int flags = fcntl(fh.fd, F_GETFD); + if (flags < 0) + return {IOResult::Error, 0}; + + if (inherit) + flags &= ~FD_CLOEXEC; + else + flags |= FD_CLOEXEC; + + if (fcntl(fh.fd, F_SETFD, flags) < 0) + return {IOResult::Error, 0}; + + return {IOResult::Success, 0}; +} + +// Function perform reading from the file: it may read up to `count' bytes to `buf'. +Class::IOResult Class::read(const FileHandle &fh, void *buf, size_t count) +{ + ssize_t rsize = ::read(fh.fd, buf, count); + if (rsize < 0) + return { (errno == EAGAIN ? IOResult::Pending : IOResult::Error), 0 }; + else + return { (rsize == 0 ? IOResult::Eof : IOResult::Success), size_t(rsize) }; +} + + +// Function perform writing to the file: it may write up to `count' byte from `buf'. +Class::IOResult Class::write(const FileHandle &fh, const void *buf, size_t count) +{ + ssize_t wsize = ::write(fh.fd, buf, count); + if (wsize < 0) + return { (errno == EAGAIN ? IOResult::Pending : IOResult::Error), 0 }; + else + return { IOResult::Success, size_t(wsize) }; +} + + +Class::AsyncHandle Class::async_read(const FileHandle& fh, void *buf, size_t count) +{ + return fh.fd == -1 ? AsyncHandle() : AsyncHandle::create(fh.fd, buf, count); +} + +Class::AsyncHandle Class::async_write(const FileHandle& fh, const void *buf, size_t count) +{ + return fh.fd == -1 ? AsyncHandle() : AsyncHandle::create(fh.fd, buf, count); +} + + +bool Class::async_wait(IOSystem::AsyncHandleIterator begin, IOSystem::AsyncHandleIterator end, std::chrono::milliseconds timeout) +{ + fd_set read_set, write_set, except_set; + FD_ZERO(&read_set); + FD_ZERO(&write_set); + FD_ZERO(&except_set); + + int maxfd = -1; + for (IOSystem::AsyncHandleIterator it = begin; it != end; ++it) + { + if (*it) + maxfd = std::max(it->handle.poll(&read_set, &write_set, &except_set), maxfd); + } + + struct timeval tv; + std::chrono::microseconds us = std::chrono::duration_cast(timeout); + tv.tv_sec = us.count() / 1000000, tv.tv_usec = us.count() % 1000000; + + int result; + do result = ::select(maxfd + 1, &read_set, &write_set, &except_set, &tv); + while (result < 0 && errno == EINTR); + + if (result < 0) + { + char msg[256]; + snprintf(msg, sizeof(msg), "select: %s", strerror(errno)); + throw std::runtime_error(msg); + } + + return result > 0; +} + +Class::IOResult Class::async_cancel(Class::AsyncHandle& handle) +{ + if (!handle) + return {Class::IOResult::Error, 0}; + + handle = {}; + return {Class::IOResult::Success, 0}; +} + +Class::IOResult Class::async_result(Class::AsyncHandle& handle) +{ + if (!handle) + return {Class::IOResult::Error, 0}; + + auto result = handle(); + if (result.status != Class::IOResult::Pending) + handle = {}; + + return result; +} + + +// Function closes the file represented by file handle. +Class::IOResult Class::close(const FileHandle &fh) +{ + return { (::close(fh.fd) == 0 ? IOResult::Success : IOResult::Error), 0 }; +} + + +// This function returns triplet of currently selected standard files. +netcoredbg::IOSystem::StdFiles Class::get_std_files() +{ + static const IOSystem::FileHandle handles[std::tuple_size::value] = { + FileHandle(STDIN_FILENO), FileHandle(STDOUT_FILENO), FileHandle(STDERR_FILENO) + }; + return {handles[0], handles[1], handles[2]}; +} + + +// StdIOSwap class allows to substitute set of standard IO files with one provided to constructor. +// Substitution exists only during life time of StsIOSwap instance. +Class::StdIOSwap::StdIOSwap(const StdFiles& files) : m_valid(true) +{ + const static unsigned NFD = std::tuple_size::value; + static const int oldfd[NFD] = { StdFileType::Stdin, StdFileType::Stdout, StdFileType::Stderr }; + + const int newfd[NFD] = { + std::get(files).handle.fd, + std::get(files).handle.fd, + std::get(files).handle.fd }; + + for (unsigned n = 0; n < NFD; n++) + { + m_orig_fd[n] = ::dup(oldfd[n]); + if (m_orig_fd[n] == -1) + { + char msg[256]; + snprintf(msg, sizeof(msg), "dup(%d): %s", oldfd[n], strerror(errno)); + throw std::runtime_error(msg); + } + + if (::dup2(newfd[n], oldfd[n]) == -1) + { + char msg[256]; + snprintf(msg, sizeof(msg), "dup2(%d, %d): %s", newfd[n], oldfd[n], strerror(errno)); + throw std::runtime_error(msg); + } + } +} + + +Class::StdIOSwap::~StdIOSwap() +{ + if (!m_valid) + return; + + const static unsigned NFD = std::tuple_size::value; + static const int oldfd[NFD] = { StdFileType::Stdin, StdFileType::Stdout, StdFileType::Stderr }; + for (unsigned n = 0; n < NFD; n++) + { + if (::dup2(m_orig_fd[n], oldfd[n]) == -1) + { + abort(); + } + + ::close(m_orig_fd[n]); + } +} + +#endif // __unix__ diff --git a/src/utils/iosystem_unix.h b/src/utils/iosystem_unix.h new file mode 100644 index 0000000..a8e4402 --- /dev/null +++ b/src/utils/iosystem_unix.h @@ -0,0 +1,123 @@ +// Copyright (C) 2020 Samsung Electronics Co., Ltd. +// See the LICENSE file in the project root for more information. + +/// \file iosystem_unix.h This file contains unix-specific declaration of IOSystem class (see iosystem.h). + +#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) +#pragma once +#include +#include +#include +#include +#include + +#include "utils/platform.h" +#include "utils/iosystem.h" + +template <> struct netcoredbg::IOSystemTraits +{ + using IOSystem = typename netcoredbg::IOSystemImpl >; + using IOResult = IOSystem::IOResult; + + struct FileHandle + { + FileHandle() : fd(-1) {} + FileHandle(int n) : fd(n) {} + explicit operator bool() const { return fd != -1; } + + int fd; + }; + + struct AsyncHandle + { + struct Traits + { + IOResult (*oper)(void *thiz); + int (*poll)(void *thiz, fd_set *, fd_set *, fd_set *); + void (*move)(void* src, void *dst); + void (*destr)(void *thiz); + }; + + template struct TraitsImpl + { + static struct Traits traits; + }; + + const Traits *traits; + mutable char data alignas(__BIGGEST_ALIGNMENT__) [sizeof(void*) * 4]; + + explicit operator bool() const { return !!traits; } + + IOResult operator()() { assert(*this); return traits->oper(data); } + + int poll(fd_set* read, fd_set* write, fd_set* except) + { + assert(*this); + return traits->poll(data, read, write, except); + } + + AsyncHandle() : traits(nullptr) {} + + template + static AsyncHandle create(Args&&... args) + { + static_assert(sizeof(InstanceType) <= sizeof(data), "insufficiend data size"); + AsyncHandle result; + result.traits = &TraitsImpl::traits; + new (result.data) InstanceType(std::forward(args)...); + return result; + } + + AsyncHandle(AsyncHandle&& other) : traits(other.traits) + { + if (other) traits->move(other.data, data); + other.traits = nullptr; + } + + AsyncHandle& operator=(AsyncHandle&& other) + { + this->~AsyncHandle(); + return *new (this) AsyncHandle(std::move(other)); + } + + ~AsyncHandle() { if (*this) traits->destr(data); } + }; + + static std::pair unnamed_pipe(); + static FileHandle listen_socket(unsigned tcp_port); + static IOResult set_inherit(const FileHandle&, bool); + static IOResult read(const FileHandle&, void *buf, size_t count); + static IOResult write(const FileHandle&, const void *buf, size_t count); + static AsyncHandle async_read(const FileHandle&, void *buf, size_t count); + static AsyncHandle async_write(const FileHandle&, const void *buf, size_t count); + static bool async_wait(IOSystem::AsyncHandleIterator begin, IOSystem::AsyncHandleIterator end, std::chrono::milliseconds); + static IOResult async_cancel(AsyncHandle&); + static IOResult async_result(AsyncHandle&); + static IOResult close(const FileHandle&); + + struct StdIOSwap + { + using StdFiles = IOSystem::StdFiles; + using StdFileType = IOSystem::StdFileType; + StdIOSwap(const StdFiles &); + ~StdIOSwap(); + + StdIOSwap(StdIOSwap&& other) + { + m_valid = other.m_valid; + if (!m_valid) + return; + + other.m_valid = false; + for (unsigned n = 0; n < std::tuple_size::value; n++) + m_orig_fd[n] = other.m_orig_fd[n]; + } + + bool m_valid; + int m_orig_fd[std::tuple_size::value]; + }; + + static IOSystem::StdFiles get_std_files(); +}; + +#endif // __unix__ diff --git a/src/utils/iosystem_win32.cpp b/src/utils/iosystem_win32.cpp new file mode 100644 index 0000000..861cb1e --- /dev/null +++ b/src/utils/iosystem_win32.cpp @@ -0,0 +1,615 @@ +// Copyright (C) 2020 Samsung Electronics Co., Ltd. +// See the LICENSE file in the project root for more information. + +/// \file iosystem_win32.cpp This file contains windows-specific definitions of +/// IOSystem class members (see iosystem.h). + +#ifdef WIN32 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "utils/iosystem.h" +#include "utils/limits.h" + +// short alias for full class name +namespace { typedef netcoredbg::IOSystemTraits Class; } + +namespace +{ + class Win32Exception : public std::runtime_error + { + struct Msg + { + mutable char buf[2 * LINE_MAX]; + }; + + static const char* getmsg(const char *prefix, DWORD error, const Msg& msg = Msg()) + { + int len = prefix ? snprintf(msg.buf, sizeof(msg.buf), "%s: ", prefix) : 0; + + if (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + msg.buf + len, sizeof(msg.buf) - len, NULL)) + { + return msg.buf; + } + + snprintf(msg.buf + len, sizeof(msg.buf) - len, "error %#x", error); + return msg.buf; + } + + public: + /// Specify Win32 error code and, optionally, error message prefix. + Win32Exception(DWORD error, const char* prefix = nullptr) : std::runtime_error(getmsg(prefix, error)) {} + + /// Specify error message prefix (optionally). Win32 error code will be obtained via call to GetLastError(). + Win32Exception(const char *prefix = nullptr) : Win32Exception(prefix, GetLastError()) {} + + /// Specify explicitly error message prefix and error code. + Win32Exception(const char *prefix, DWORD error) : Win32Exception(error, prefix) {} + }; + + struct Initializer + { + Initializer() + { + WSADATA wsa; + int wsa_error = WSAStartup(MAKEWORD(2, 2), &wsa); + if (wsa_error != 0) + throw Win32Exception("WSAStartup failed", wsa_error); + } + + ~Initializer() + { + WSACleanup(); + } + }; + + static Initializer initializer; + +#if 0 + // assuming domain=AF_UNIX, type=SOCK_STREAM, protocol=0 + int wsa_socketpair(int domain, int type, int protocol, SOCKET sv[2]) + { + + SOCKET serv = ::socket(domain, type, protocol); + if (serv == INVALID_SOCKET) + throw Win32Exception("can't create socket", WSAGetLastError()); + + // TODO + char name[] = "netcoredbg"; + size_t namelen = sizeof(name)-1; + + SOCKADDR_UN sa; + sa.sun_family = domain; + assert(namelen <= sizeof(sa.sun_path)); + memcpy(sa.sun_path, name, namelen); + if (::bind(serv, (struct sockaddr*)&sa, sizeof(sa)) == SOCKET_ERROR) + { + auto err = WSAGetLastError(); + ::closesocket(serv); + throw Win32Exception("can't bind socket", err); + } + + u_long mode = 1; + if (::ioctlsocket(serv, FIONBIO, &mode) == SOCKET_ERROR) + { + auto err = WSAGetLastError(); + ::closesocket(serv); + throw Win32Exception("ioctlsocket(FIONBIO)", err); + } + + if (::listen(serv, 1) == SOCKET_ERROR && WSAGetLastError() != WSAEINPROGRESS) + { + auto err = WSAGetLastError(); + ::closesocket(serv); + throw Win32Exception("ioctlsocket(FIONBIO)", err); + } + + SOCKET conn = ::socket(domain, type, protocol); + if (conn == INVALID_SOCKET) + { + auto err = WSAGetLastError(); + ::closesocket(serv); + throw Win32Exception("can't create socket", err); + } + + sa.sun_family = domain; + memcpy(sa.sun_path, name, namelen); + if (::connect(conn, (struct sockaddr*)&sa, sizeof(sa)) == SOCKET_ERROR) + { + auto err = WSAGetLastError(); + ::closesocket(serv); + ::closesocket(conn); + throw Win32Exception("can't bind socket", err); + } + + mode = 0; + if (::ioctlsocket(serv, FIONBIO, &mode) == SOCKET_ERROR) + { + auto err = WSAGetLastError(); + ::closesocket(serv); + ::closesocket(conn); + throw Win32Exception("ioctlsocket(FIONBIO)", err); + } + + SOCKET newsock = ::accept(serv, NULL, NULL); + if (newsock == INVALID_SOCKET) + { + auto err = WSAGetLastError(); + ::closesocket(serv); + ::closesocket(conn); + throw Win32Exception("accept on socket", err); + } + + ::closesocket(serv); + + sv[0] = newsock, sv[1] = conn; + return 0; + } +#endif +} + + +// Function should create unnamed pipe and return two file handles +// (reading and writing pipe ends) or return empty file handles if pipe can't be created. +std::pair Class::unnamed_pipe() +{ +#if 0 + SOCKET sv[2]; + if (wsa_socketpair(AF_UNIX, SOCK_STREAM, 0, sv) != 0) + return {FileHandle(), FileHandle()}; +#endif + + static const size_t PipeSize = 32 * LINE_MAX; + + SECURITY_ATTRIBUTES saAttr; + saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); + saAttr.bInheritHandle = TRUE; + saAttr.lpSecurityDescriptor = NULL; + + HANDLE reading_fd, writing_fd; + + static std::atomic pipe_num; + char pipe_name[MAX_PATH + 1]; + snprintf(pipe_name, sizeof(pipe_name), "\\\\.\\Pipe\\Win32Pipes.%08x.%08x", + GetCurrentProcessId(), pipe_num++); + + reading_fd = CreateNamedPipeA(pipe_name, + PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_BYTE | PIPE_WAIT, + 1, // number of pipes + PipeSize, PipeSize, + 0, // 50ms default timeout + &saAttr); + + if (reading_fd == INVALID_HANDLE_VALUE) + { + perror("CreateNamedPipeA"); + return { FileHandle(), FileHandle() }; + } + + writing_fd = CreateFileA(pipe_name, + GENERIC_WRITE, + 0, // no sharing + &saAttr, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, + NULL); + + if (writing_fd == INVALID_HANDLE_VALUE) + { + auto err = GetLastError(); + ::CloseHandle(writing_fd); + fprintf(stderr, "CreateFile pipe error: %#x\n", err); + return { FileHandle(), FileHandle() }; + } + + if (!SetHandleInformation(writing_fd, HANDLE_FLAG_INHERIT, 0)) + { + fprintf(stderr, "SetHandleInformation failed!\n"); + return { FileHandle(), FileHandle() }; + } + + if (!SetHandleInformation(reading_fd, HANDLE_FLAG_INHERIT, 0)) + { + fprintf(stderr, "SetHandleInformation failed!\n"); + return { FileHandle(), FileHandle() }; + } + + return { FileHandle(reading_fd), FileHandle(writing_fd) }; +} + + +// Function creates listening TCP socket on given port, waits, accepts single +// connection, and return file descriptor related to the accepted connection. +// In case of error, empty file handle will be returned. +Class::FileHandle Class::listen_socket(unsigned port) +{ + assert(port > 0 && port < 65536); + + SOCKET newsockfd; + int clilen; + struct sockaddr_in serv_addr, cli_addr; + + SOCKET sockFd = ::socket(AF_INET, SOCK_STREAM, 0); + if (sockFd == INVALID_SOCKET) + { + fprintf(stderr, "can't create socket: %#x\n", WSAGetLastError()); + return {}; + } + + BOOL enable = 1; + if (::setsockopt(sockFd, SOL_SOCKET, SO_REUSEADDR, (const char *)&enable, sizeof(BOOL)) == SOCKET_ERROR) + { + ::closesocket(sockFd); + fprintf(stderr, "setsockopt failed\n"); + return {}; + } + memset(&serv_addr, 0, sizeof(serv_addr)); + + serv_addr.sin_family = AF_INET; + serv_addr.sin_addr.s_addr = INADDR_ANY; + serv_addr.sin_port = htons(port); + + if (::bind(sockFd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == SOCKET_ERROR) + { + ::closesocket(sockFd); + fprintf(stderr, "can't bind to specified port!\n"); + return {}; + } + + ::listen(sockFd, 1); + + clilen = sizeof(cli_addr); + newsockfd = ::accept(sockFd, (struct sockaddr*)&cli_addr, &clilen); + ::closesocket(sockFd); + if (newsockfd == INVALID_SOCKET) + { + fprintf(stderr, "can't accept connection\n"); + return {}; + } + + return FileHandle(newsockfd); +} + +// Function enables or disables inheritance of file handle for child processes. +Class::IOResult Class::set_inherit(const FileHandle& fh, bool inherit) +{ + DWORD flags; + if (!GetHandleInformation(fh.handle, &flags)) + return {IOResult::Error}; + + if (inherit) + flags |= HANDLE_FLAG_INHERIT; + else + flags &= ~HANDLE_FLAG_INHERIT; + + if (!SetHandleInformation(fh.handle, HANDLE_FLAG_INHERIT, flags)) + return {IOResult::Error}; + + return {IOResult::Success}; +} + +// Function perform reading from the file: it may read up to `count' bytes to `buf'. +Class::IOResult Class::read(const FileHandle& fh, void *buf, size_t count) +{ + DWORD dwRead = 0; + OVERLAPPED ov = {}; + if (! ReadFile(fh.handle, buf, (DWORD)count, &dwRead, &ov)) + return { (GetLastError() == ERROR_IO_PENDING ? IOResult::Pending : IOResult::Error), dwRead }; + else + return { (dwRead == 0 ? IOResult::Eof : IOResult::Success), dwRead }; +} + + +// Function perform writing to the file: it may write up to `count' byte from `buf'. +Class::IOResult Class::write(const FileHandle& fh, const void *buf, size_t count) +{ + // see https://stackoverflow.com/questions/43939424/writefile-with-windows-sockets-returns-invalid-parameter-error + DWORD dwWritten = 0; + OVERLAPPED ov = {}; + if (! WriteFile(fh.handle, buf, (DWORD)count, &dwWritten, &ov)) + return { (GetLastError() == ERROR_IO_PENDING ? IOResult::Pending : IOResult::Error), dwWritten }; + else + return { IOResult::Success, dwWritten }; +} + + +Class::AsyncHandle Class::async_read(const FileHandle& fh, void *buf, size_t count) +{ + if (fh.handle == INVALID_HANDLE_VALUE) + return {}; + + AsyncHandle result; + result.check_eof = true; + result.handle = fh.handle; + result.overlapped.reset(new OVERLAPPED); + memset(result.overlapped.get(), 0, sizeof(OVERLAPPED)); + result.overlapped->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + if (result.overlapped->hEvent == INVALID_HANDLE_VALUE) + return {}; + + DWORD val; + DWORD bytesRead; + + if (GetConsoleMode(fh.handle, &val)) + { // file handle is the console + // first, remove all events before the first key event, if exists + while (GetNumberOfConsoleInputEvents(fh.handle, &val) && val) + { + INPUT_RECORD event; + if (!PeekConsoleInput(fh.handle, &event, 1, &bytesRead)) + return {}; + if (event.EventType != KEY_EVENT || (event.EventType == KEY_EVENT && !event.Event.KeyEvent.bKeyDown)) + { + if (!ReadConsoleInput(fh.handle, &event, 1, &bytesRead)) + return {}; + } + else + break; + } + if (!val) + { + // nothing to read from the console -- defer call to ReadFile + result.buf = buf, result.count = count; + return result; + } + } + + if (! ReadFile(fh.handle, buf, (DWORD)count, nullptr, result.overlapped.get())) + { + if (GetLastError() != ERROR_IO_PENDING) + return {}; + } + + return result; +} + +Class::AsyncHandle Class::async_write(const FileHandle& fh, const void *buf, size_t count) +{ + if (fh.handle == INVALID_HANDLE_VALUE) + return {}; + + AsyncHandle result; + result.handle = fh.handle; + result.overlapped.reset(new OVERLAPPED); + memset(result.overlapped.get(), 0, sizeof(OVERLAPPED)); + result.overlapped->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + if (result.overlapped->hEvent == INVALID_HANDLE_VALUE) + return {}; + + if (! WriteFile(fh.handle, buf, (DWORD)count, nullptr, result.overlapped.get())) + { + if (GetLastError() != ERROR_IO_PENDING) + return {}; + } + + return result; +} + +bool Class::async_wait(IOSystem::AsyncHandleIterator begin, IOSystem::AsyncHandleIterator end, std::chrono::milliseconds timeout) +{ + // console workaround + for (auto it = begin; it != end; ++it) + { + if (it->handle.buf) + { + DWORD val; + if (GetNumberOfConsoleInputEvents(it->handle.handle, &val) && val) + SetEvent(it->handle.overlapped->hEvent); + } + } + + // count number of active handles + unsigned count = 0; + for (auto it = begin; it != end; ++it) + if (*it) ++count; + + // allocate memory for events array + HANDLE *events = static_cast(alloca(count * sizeof(HANDLE))); + unsigned n = 0; + for (auto it = begin; it != end; ++it) + { + if (*it) + events[n++] = it->handle.overlapped->hEvent; + } + + assert(n == count); + DWORD result = WaitForMultipleObjects(count, events, FALSE, DWORD(timeout.count())); + return result != WAIT_FAILED && result != WAIT_TIMEOUT; +} + +Class::IOResult Class::async_cancel(AsyncHandle& h) +{ + if (!h) + return {IOResult::Error}; + + if (!CloseHandle(h.overlapped->hEvent)) + perror("CloseHandle(event) error"); + + // console workaround -- canceling deffered operation + if (h.buf) + { + h = AsyncHandle(); + return {IOResult::Success}; + } + + IOResult result; + if (!CancelIoEx(h.handle, h.overlapped.get())) + result = {IOResult::Error}; + else + result = {IOResult::Success}; + + h = AsyncHandle(); + return result; +} + +Class::IOResult Class::async_result(AsyncHandle& h) +{ + if (!h) + return {IOResult::Error}; + + DWORD bytes = 1; + bool finished; + + if (h.buf) + { + // workaround for the console + finished = true; + } + else + { + // pipes, normal files, etc... + finished = GetOverlappedResult(h.handle, h.overlapped.get(), &bytes, FALSE); + if (!finished) + { + DWORD error = GetLastError(); + if (error == ERROR_IO_INCOMPLETE) + return {IOResult::Pending}; + } + } + + if (!CloseHandle(h.overlapped->hEvent)) + perror("CloseHandle(event) error"); + + bool check_eof = h.check_eof; + + h = AsyncHandle(); + + if (!finished) + return {IOResult::Error}; + + if (check_eof && bytes == 0) + return {IOResult::Eof, bytes}; + + return {IOResult::Success, bytes}; +} + +// Function closes the file represented by file handle. +Class::IOResult Class::close(const FileHandle& fh) +{ + assert(fh); + if (fh.type == FileHandle::Socket) + return { ::closesocket((SOCKET)fh.handle) == 0 ? IOResult::Success : IOResult::Error }; + else + return { ::CloseHandle(fh.handle) ? IOResult::Success : IOResult::Error }; +} + + +// Function allows non-blocking IO on files, it is similar with select(2) system call on Unix. +// Arguments includes: pointers to three sets of file handles (for reading, for writing, and for +// exceptions), and timeout value, in milliseconds. Any pointer might have NULL value if some set +// isn't specified. +// Function returns -1 on error, 0 on timeout or number of ready to read/write file handles. +// If function returns value greater than zero, at least one of the sets, passed in arguments, +// is not empty and contains file handles ready to read/write/etc... + + +// This function returns triplet of currently selected standard files. +Class::IOSystem::StdFiles Class::get_std_files() +{ + using Handles = std::tuple; + /*thread_local*/ static alignas(alignof(Handles)) char mem[sizeof(Handles)]; // TODO + Handles& handles = *new (mem) Handles { + FileHandle(GetStdHandle(STD_INPUT_HANDLE)), + FileHandle(GetStdHandle(STD_OUTPUT_HANDLE)), + FileHandle(GetStdHandle(STD_ERROR_HANDLE)) + }; + return { std::get(handles), + std::get(handles), + std::get(handles) }; +} + + +// StdIOSwap class allows to substitute set of standard IO files with one provided to constructor. +// Substitution exists only during life time of StsIOSwap instance. +Class::StdIOSwap::StdIOSwap(const StdFiles& files) : m_valid(true) +{ + const static unsigned NFD = std::tuple_size::value; + static const DWORD std_handles[NFD] = {STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, STD_ERROR_HANDLE}; + static const int open_flags[NFD] = {_O_RDONLY | _O_BINARY, _O_BINARY, _O_BINARY}; + const int open_fds[NFD] = {_fileno(stdin), _fileno(stdout), _fileno(stderr)}; + + const FileHandle new_handles[NFD] = { + std::get(files).handle, + std::get(files).handle, + std::get(files).handle }; + + fflush(stdout); + fflush(stderr); + + for (unsigned n = 0; n < NFD; n++) + { + if (new_handles[n].type != FileHandle::FileOrPipe) + throw std::runtime_error("can't use socket handle for stdin/stdout/stderr"); + } + + for (unsigned n = 0; n < NFD; n++) + { + m_orig_handle[n] = GetStdHandle(std_handles[n]); + if (m_orig_handle[n] == INVALID_HANDLE_VALUE) + { + char msg[256]; + snprintf(msg, sizeof(msg), "GetStdHandle(%#x): error", std_handles[n]); + throw std::runtime_error(msg); + } + + if (!SetHandleInformation(new_handles[n].handle, HANDLE_FLAG_INHERIT, 1)) + fprintf(stderr, "SetHandleInformation failed!\n"); + + if (!SetStdHandle(std_handles[n], new_handles[n].handle)) + { + char msg[256]; + snprintf(msg, sizeof(msg), "SetStdHandle(%#x, %p): error", std_handles[n], new_handles[n].handle); + throw std::runtime_error(msg); + } + + + int fd = _open_osfhandle(reinterpret_cast(new_handles[n].handle), open_flags[n]); + if (fd == -1) + throw Win32Exception("_open_osfhandle"); + + m_orig_fd[n] = _dup(open_fds[n]); + if (m_orig_fd[n] == -1) + throw Win32Exception("_dup"); + + if (_dup2(fd, open_fds[n]) == -1) + throw Win32Exception("_dup2"); + + close(fd); + } +} + +Class::StdIOSwap::~StdIOSwap() +{ + if (!m_valid) + return; + + const static unsigned NFD = std::tuple_size::value; + static const DWORD std_handles[NFD] = {STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, STD_ERROR_HANDLE}; + const int open_fds[NFD] = {_fileno(stdin), _fileno(stdout), _fileno(stderr)}; + + fflush(stdout); + fflush(stderr); + + for (unsigned n = 0; n < NFD; n++) + { + if (!SetStdHandle(std_handles[n], m_orig_handle[n])) + { + abort(); + } + + _dup2(m_orig_fd[n], open_fds[n]); + close(m_orig_fd[n]); + } +} + +#endif // WIN32 diff --git a/src/utils/iosystem_win32.h b/src/utils/iosystem_win32.h new file mode 100644 index 0000000..424d367 --- /dev/null +++ b/src/utils/iosystem_win32.h @@ -0,0 +1,111 @@ +// Copyright (C) 2020 Samsung Electronics Co., Ltd. +// See the LICENSE file in the project root for more information. + +/// \file iosystem_win32.h This file contains windows-specific declaration of IOSystem class (see iosystem.h). +// +#ifdef _WIN32 +#pragma once +#include +#include // TODO +#include +#include + +namespace netcoredbg +{ + +template <> struct IOSystemTraits +{ + struct FileHandle + { + FileHandle() : handle(INVALID_HANDLE_VALUE), type(FileOrPipe) {} + + explicit operator bool() const { return handle != INVALID_HANDLE_VALUE; } + + enum FileType + { + FileOrPipe, + Socket + }; + + FileHandle(HANDLE filefd) : handle(filefd), type(FileOrPipe) {} + FileHandle(SOCKET sockfd) : handle((HANDLE)sockfd), type(Socket) {} + + HANDLE handle; + enum FileType type; + }; + + struct AsyncHandle + { + HANDLE handle; + std::unique_ptr overlapped; + bool check_eof; + + // workaround: non-blocking reading from console + void *buf; + size_t count; + + AsyncHandle() + : handle(INVALID_HANDLE_VALUE), overlapped(), check_eof(false), buf(nullptr), count(0) + {} + + AsyncHandle(AsyncHandle&& other) noexcept + : handle(other.handle), overlapped(std::move(other.overlapped)), check_eof(other.check_eof), + buf(other.buf), count(other.count) + { + other.handle = INVALID_HANDLE_VALUE; + } + + AsyncHandle& operator=(AsyncHandle&& other) noexcept + { + return this->~AsyncHandle(), *new (this) AsyncHandle(std::move(other)); + } + + explicit operator bool() const { return handle != INVALID_HANDLE_VALUE; } + }; + + using IOSystem = typename IOSystemImpl >; + using IOResult = IOSystem::IOResult; + + static std::pair unnamed_pipe(); + static FileHandle listen_socket(unsigned tcp_port); + static IOResult set_inherit(const FileHandle &, bool); + static IOResult read(const FileHandle &, void *buf, size_t count); + static IOResult write(const FileHandle &, const void *buf, size_t count); + static AsyncHandle async_read(const FileHandle &, void *buf, size_t count); + static AsyncHandle async_write(const FileHandle &, const void *buf, size_t count); + static bool async_wait(IOSystem::AsyncHandleIterator begin, IOSystem::AsyncHandleIterator end, std::chrono::milliseconds); + static IOResult async_cancel(AsyncHandle &); + static IOResult async_result(AsyncHandle &); + static IOResult close(const FileHandle &); + + static IOSystem::StdFiles get_std_files(); + + struct StdIOSwap + { + using StdFiles = IOSystem::StdFiles; + using StdFileType = IOSystem::StdFileType; + StdIOSwap(const StdFiles &); + ~StdIOSwap(); + + StdIOSwap(StdIOSwap&& other) + { + m_valid = other.m_valid; + if (!m_valid) + return; + + other.m_valid = false; + for (unsigned n = 0; n < std::tuple_size::value; n++) + { + m_orig_handle[n] = other.m_orig_handle[n]; + m_orig_fd[n] = other.m_orig_fd[n]; + } + } + + bool m_valid; + HANDLE m_orig_handle[std::tuple_size::value]; + int m_orig_fd[std::tuple_size::value]; + }; +}; + +} // ::netcoredbg +#endif // WIN32 diff --git a/src/utils/literal_string.h b/src/utils/literal_string.h index 1aebd99..e0b7965 100644 --- a/src/utils/literal_string.h +++ b/src/utils/literal_string.h @@ -2,7 +2,7 @@ // See the LICENSE file in the project root for more information. #pragma once -#include "utility.h" +#include "utils/utility.h" #include "utils/string_view.h" namespace netcoredbg diff --git a/src/utils/platform_unix.cpp b/src/utils/platform_unix.cpp new file mode 100644 index 0000000..9d799c5 --- /dev/null +++ b/src/utils/platform_unix.cpp @@ -0,0 +1,45 @@ +// Copyright (C) 2020 Samsung Electronics Co., Ltd. +// See the LICENSE file in the project root for more information. + +/// \file platform_unix.cpp This file contains unix-specific function definitions, +/// for functions defined in platform.h + +#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) +#ifdef __APPLE__ +#include +#endif +#include +#include "utils/platform.h" + +extern char** environ; + +namespace netcoredbg +{ + +// Function returns memory mapping page size (like sysconf(_SC_PAGESIZE) on Unix). +unsigned long OSPageSize() +{ + static unsigned long pageSize = sysconf(_SC_PAGESIZE); + return pageSize; +} + + +// Function suspends process execution for specified amount of time (in microseconds) +void USleep(unsigned long usec) +{ + usleep(usec); +} + + +// Function returns list of environment variables (like char **environ). +char** GetSystemEnvironment() +{ +#if __APPLE__ + return *(_NSGetEnviron()); +#else // __APPLE__ + return environ; +#endif // __APPLE__ +} + +} // ::netcoredbg +#endif // __unix__ diff --git a/src/utils/platform_win32.cpp b/src/utils/platform_win32.cpp new file mode 100644 index 0000000..6e08f7b --- /dev/null +++ b/src/utils/platform_win32.cpp @@ -0,0 +1,51 @@ +// Copyright (C) 2020 Samsung Electronics Co., Ltd. +// See the LICENSE file in the project root for more information. + +/// \file platform_win32.cpp This file contains windows-specific function definitions, +/// for functions defined in platform.h + +#ifdef WIN32 +#include +#include // char **environ +#include "utils/platform.h" +#include "utils/limits.h" + +namespace netcoredbg +{ + +// Function returns memory mapping page size (like sysconf(_SC_PAGESIZE) on Unix). +unsigned long OSPageSize() +{ + static unsigned long pageSize = []{ + SYSTEM_INFO si; + GetSystemInfo(&si); + return si.dwPageSize; + }(); + + return pageSize; +} + + +// Function suspends process execution for specified amount of time (in microseconds) +void USleep(unsigned long usec) +{ + HANDLE timer; + LARGE_INTEGER ft; + + ft.QuadPart = -(10*(long)usec); // Convert to 100 nanosecond interval, negative value indicates relative time + + timer = CreateWaitableTimer(NULL, TRUE, NULL); + SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0); + WaitForSingleObject(timer, INFINITE); + CloseHandle(timer); +} + + +// Function returns list of environment variables (like char **environ). +char** GetSystemEnvironment() +{ + return environ; +} + +} // ::netcoredbg +#endif diff --git a/src/utils/rwlock.h b/src/utils/rwlock.h index 27973a4..da6bfa0 100644 --- a/src/utils/rwlock.h +++ b/src/utils/rwlock.h @@ -2,7 +2,7 @@ // See the LICENSE file in the project root for more information. #pragma once -#include "utility.h" +#include "utils/utility.h" #include #include diff --git a/src/utils/utility.h b/src/utils/utility.h new file mode 100644 index 0000000..6dd50dc --- /dev/null +++ b/src/utils/utility.h @@ -0,0 +1,82 @@ +// Copyright (c) 2020 Samsung Electronics Co., LTD +// Distributed under the MIT License. +// See the LICENSE file in the project root for more information. + +// \file utility.h This file contains few supplimentar classes and functions residing in Utility namespace. + +#pragma once +#include + +namespace netcoredbg +{ + +namespace Utility +{ + +/// @{ This function is similar to `std::size()` from C++17 and allows to determine +/// the object size as number of elements, which might be stored within object +/// (opposed to sizeof(), which returns object sizes in bytes). Typically these +/// functions applicable to arrays and to classes like std::array. +template constexpr auto Size(const T& v) -> decltype(v.size()) { return v.size(); } +template constexpr size_t Size(const T (&)[N]) noexcept { return N; } +/// @} + + +/// This type is similar to `std::index_sequence` from C++14 and typically +/// should be used in pair with `MakeSequence` type to create sequence of +/// indices usable in template metaprogramming techniques. +template struct Sequence {}; + +// Actual implementation of MakeSequence type. +namespace Internals +{ + template struct MakeSequence : MakeSequence {}; + template struct MakeSequence<0, Index...> { typedef Sequence type; }; +} + +/// MakeSequence type is similar to `std::make_index_sequence` from C++14. +/// Instantination of this type exapands to `Sequence` type, where +/// `N` have values from 0 to `Size-1`. +template using MakeSequence = typename Internals::MakeSequence::type; + + +/// @{ This is similar to std:void_t which is defined in c++17. +template struct MakeVoid { typedef void type; }; +template using Void = typename MakeVoid::type; +/// @} + + +/// This template is similar to `offsetof` macros in plain C. It allows +/// to get offset of specified member in some particular class. +template +static inline constexpr size_t offset_of(const Member Owner::*mem) +{ + return reinterpret_cast(&(reinterpret_cast(0)->*mem)); +} + +/// This template is similar to well known `container_of` macros. It allows +/// to get pointer to owner class from pointer to member. +template +static inline constexpr Owner *container_of(Member *ptr, const Member Owner::*mem) +{ + return reinterpret_cast(reinterpret_cast(ptr) - offset_of(mem)); +} + + +// This is helper class which simplifies implementation of singleton classes. +// +// Usage example: +// 1) define dictinct type of singleton: typedef Singleton YourSingleton; +// 2) to access your singleton use expression: YourSingleton::instance().operations... +// +template struct Singleton +{ + static T& instance() + { + static T val; + return val; + } +}; + +} // Utility namespace +} // namespace netcoredbg diff --git a/src/windows/dynlibs_win32.cpp b/src/windows/dynlibs_win32.cpp deleted file mode 100644 index 7a1fcb0..0000000 --- a/src/windows/dynlibs_win32.cpp +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (C) 2020 Samsung Electronics Co., Ltd. -// See the LICENSE file in the project root for more information. - -/// \file dynlibsi_win32.h This file contains windows-specific function definitions -/// required to work with dynamically loading libraries. - -#ifdef WIN32 -#include -#include "utils/dynlibs.h" -#include "utils/limits.h" - -namespace netcoredbg -{ - -// This functon load specified library and returns handle (which then -// can be passed to DLSym and DLCLose functions). -// In case of error function returns NULL. -DLHandle DLOpen(const std::string &path) -{ - return reinterpret_cast(::LoadLibraryExA(path.c_str(), NULL, 0)); -} - -// This function resolves symbol address within library specified by handle, -// and returns it's address, in case of error function returns NULL. -void* DLSym(DLHandle handle, string_view name) -{ - char str[LINE_MAX]; - if (name.size() >= sizeof(str)) - return {}; - - name.copy(str, name.size()); - str[name.size()] = 0; - return ::GetProcAddress((HMODULE)handle, str); -} - -/// This function unloads previously loadded library, specified by handle. -/// In case of error this function returns `false'. -bool DLClose(DLHandle handle) -{ - return ::FreeLibrary(reinterpret_cast(handle)); -} - -} // ::netcoredbg -#endif diff --git a/src/windows/filesystem_win32.cpp b/src/windows/filesystem_win32.cpp deleted file mode 100644 index c5380c4..0000000 --- a/src/windows/filesystem_win32.cpp +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (C) 2020 Samsung Electronics Co., Ltd. -// See the LICENSE file in the project root for more information. - -/// \file filesystem_win32.cpp -/// This file contains definitions of windows-specific functions related to file system. - -#ifdef WIN32 -#include -#include -#include "utils/filesystem.h" -#include "utils/limits.h" - - -namespace netcoredbg -{ - -const char* FileSystemTraits::PathSeparatorSymbols = "/\\"; - -// Function returns absolute path to currently running executable. -std::string GetExeAbsPath() -{ - const size_t MAX_LONGPATH = 1024; - char hostPath[MAX_LONGPATH + 1]; - static const std::string result(hostPath, ::GetModuleFileNameA(NULL, hostPath, MAX_LONGPATH)); - return result; -} - - -// Function returns path to directory, which should be used for creation of -// temporary files. Typically this is `/tmp` on Unix and something like -// `C:\Users\localuser\Appdata\Local\Temp` on Windows. -string_view GetTempDir() -{ - CHAR path[MAX_PATH + 1]; - static const std::string result(path, GetTempPathA(MAX_PATH, path)); - return result; -} - - -// Function changes current working directory. Return value is `false` in case of error. -bool SetWorkDir(const std::string &path) -{ - // In the ANSI version of this function, the name is limited to MAX_PATH characters. - // https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setcurrentdirectory - if (path.size() >= MAX_PATH) - return false; - - return SetCurrentDirectoryA(path.c_str()); -} - -} // ::netcoredbg -#endif diff --git a/src/windows/filesystem_win32.h b/src/windows/filesystem_win32.h deleted file mode 100644 index ac14fcf..0000000 --- a/src/windows/filesystem_win32.h +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (C) 2020 Samsung Electronics Co., Ltd. -// See the LICENSE file in the project root for more information. - -/// \file filesystem_win32.h This file contains windows-specific details to FileSystem class. - -#ifdef WIN32 -#pragma once -#include -#include - -namespace netcoredbg -{ - template <> struct FileSystemTraits - { - const static size_t PathMax = MAX_PATH; - const static size_t NameMax = MAX_PATH - 1; // not include terminal null. - const static char PathSeparator = '\\'; - const static char* PathSeparatorSymbols; - }; -} -#endif diff --git a/src/windows/interop_win32.cpp b/src/windows/interop_win32.cpp deleted file mode 100644 index a0d06f8..0000000 --- a/src/windows/interop_win32.cpp +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright (C) 2020 Samsung Electronics Co., Ltd. -// See the LICENSE file in the project root for more information. - -/// \file interop_win32.h This file contains windows-specific functions for Interop class defined in interop.h - -#ifdef WIN32 -#include -#include -#include -#include -#include - -#include "managed/interop.h" -#include "utils/filesystem.h" -#include "utils/limits.h" - - -namespace netcoredbg -{ - -// This function searches *.dll files in specified directory and adds full paths to files -// to semicolon-separated list `tpaList'. -template <> -void InteropTraits::AddFilesFromDirectoryToTpaList(const std::string &directory, std::string& tpaList) -{ - const char * const tpaExtensions[] = { - "*.ni.dll", // Probe for .ni.dll first so that it's preferred if ni and il coexist in the same dir - "*.dll", - "*.ni.exe", - "*.exe", - }; - - std::set addedAssemblies; - - // Walk the directory for each extension separately so that we first get files with .ni.dll extension, - // then files with .dll extension, etc. - for (int extIndex = 0; extIndex < sizeof(tpaExtensions) / sizeof(tpaExtensions[0]); extIndex++) - { - const char* ext = tpaExtensions[extIndex]; - size_t extLength = strlen(ext); - - std::string assemblyPath(directory); - assemblyPath += FileSystem::PathSeparator; - assemblyPath.append(tpaExtensions[extIndex]); - - WIN32_FIND_DATAA data; - HANDLE findHandle = FindFirstFileA(assemblyPath.c_str(), &data); - - if (findHandle != INVALID_HANDLE_VALUE) - { - do - { - if (!(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) - { - - std::string filename(data.cFileName); - size_t extPos = filename.length() - extLength; - std::string filenameWithoutExt(filename.substr(0, extPos)); - - // Make sure if we have an assembly with multiple extensions present, - // we insert only one version of it. - if (addedAssemblies.find(filenameWithoutExt) == addedAssemblies.end()) - { - addedAssemblies.insert(filenameWithoutExt); - - tpaList.append(directory); - tpaList += FileSystem::PathSeparator; - tpaList.append(filename); - tpaList.append(";"); - } - } - } - while (0 != FindNextFileA(findHandle, &data)); - - FindClose(findHandle); - } - } -} - -// This function unsets `CORECLR_ENABLE_PROFILING' environment variable. -template <> -void InteropTraits::UnsetCoreCLREnv() -{ - _putenv("CORECLR_ENABLE_PROFILING="); -} - -// Returns the length of a BSTR. -template <> -UINT InteropTraits::SysStringLen(BSTR bstrString) -{ - return ::SysStringLen(bstrString); -} - -} // ::netcoredbg -#endif // WIN32 diff --git a/src/windows/iosystem_win32.cpp b/src/windows/iosystem_win32.cpp deleted file mode 100644 index 861cb1e..0000000 --- a/src/windows/iosystem_win32.cpp +++ /dev/null @@ -1,615 +0,0 @@ -// Copyright (C) 2020 Samsung Electronics Co., Ltd. -// See the LICENSE file in the project root for more information. - -/// \file iosystem_win32.cpp This file contains windows-specific definitions of -/// IOSystem class members (see iosystem.h). - -#ifdef WIN32 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "utils/iosystem.h" -#include "utils/limits.h" - -// short alias for full class name -namespace { typedef netcoredbg::IOSystemTraits Class; } - -namespace -{ - class Win32Exception : public std::runtime_error - { - struct Msg - { - mutable char buf[2 * LINE_MAX]; - }; - - static const char* getmsg(const char *prefix, DWORD error, const Msg& msg = Msg()) - { - int len = prefix ? snprintf(msg.buf, sizeof(msg.buf), "%s: ", prefix) : 0; - - if (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - msg.buf + len, sizeof(msg.buf) - len, NULL)) - { - return msg.buf; - } - - snprintf(msg.buf + len, sizeof(msg.buf) - len, "error %#x", error); - return msg.buf; - } - - public: - /// Specify Win32 error code and, optionally, error message prefix. - Win32Exception(DWORD error, const char* prefix = nullptr) : std::runtime_error(getmsg(prefix, error)) {} - - /// Specify error message prefix (optionally). Win32 error code will be obtained via call to GetLastError(). - Win32Exception(const char *prefix = nullptr) : Win32Exception(prefix, GetLastError()) {} - - /// Specify explicitly error message prefix and error code. - Win32Exception(const char *prefix, DWORD error) : Win32Exception(error, prefix) {} - }; - - struct Initializer - { - Initializer() - { - WSADATA wsa; - int wsa_error = WSAStartup(MAKEWORD(2, 2), &wsa); - if (wsa_error != 0) - throw Win32Exception("WSAStartup failed", wsa_error); - } - - ~Initializer() - { - WSACleanup(); - } - }; - - static Initializer initializer; - -#if 0 - // assuming domain=AF_UNIX, type=SOCK_STREAM, protocol=0 - int wsa_socketpair(int domain, int type, int protocol, SOCKET sv[2]) - { - - SOCKET serv = ::socket(domain, type, protocol); - if (serv == INVALID_SOCKET) - throw Win32Exception("can't create socket", WSAGetLastError()); - - // TODO - char name[] = "netcoredbg"; - size_t namelen = sizeof(name)-1; - - SOCKADDR_UN sa; - sa.sun_family = domain; - assert(namelen <= sizeof(sa.sun_path)); - memcpy(sa.sun_path, name, namelen); - if (::bind(serv, (struct sockaddr*)&sa, sizeof(sa)) == SOCKET_ERROR) - { - auto err = WSAGetLastError(); - ::closesocket(serv); - throw Win32Exception("can't bind socket", err); - } - - u_long mode = 1; - if (::ioctlsocket(serv, FIONBIO, &mode) == SOCKET_ERROR) - { - auto err = WSAGetLastError(); - ::closesocket(serv); - throw Win32Exception("ioctlsocket(FIONBIO)", err); - } - - if (::listen(serv, 1) == SOCKET_ERROR && WSAGetLastError() != WSAEINPROGRESS) - { - auto err = WSAGetLastError(); - ::closesocket(serv); - throw Win32Exception("ioctlsocket(FIONBIO)", err); - } - - SOCKET conn = ::socket(domain, type, protocol); - if (conn == INVALID_SOCKET) - { - auto err = WSAGetLastError(); - ::closesocket(serv); - throw Win32Exception("can't create socket", err); - } - - sa.sun_family = domain; - memcpy(sa.sun_path, name, namelen); - if (::connect(conn, (struct sockaddr*)&sa, sizeof(sa)) == SOCKET_ERROR) - { - auto err = WSAGetLastError(); - ::closesocket(serv); - ::closesocket(conn); - throw Win32Exception("can't bind socket", err); - } - - mode = 0; - if (::ioctlsocket(serv, FIONBIO, &mode) == SOCKET_ERROR) - { - auto err = WSAGetLastError(); - ::closesocket(serv); - ::closesocket(conn); - throw Win32Exception("ioctlsocket(FIONBIO)", err); - } - - SOCKET newsock = ::accept(serv, NULL, NULL); - if (newsock == INVALID_SOCKET) - { - auto err = WSAGetLastError(); - ::closesocket(serv); - ::closesocket(conn); - throw Win32Exception("accept on socket", err); - } - - ::closesocket(serv); - - sv[0] = newsock, sv[1] = conn; - return 0; - } -#endif -} - - -// Function should create unnamed pipe and return two file handles -// (reading and writing pipe ends) or return empty file handles if pipe can't be created. -std::pair Class::unnamed_pipe() -{ -#if 0 - SOCKET sv[2]; - if (wsa_socketpair(AF_UNIX, SOCK_STREAM, 0, sv) != 0) - return {FileHandle(), FileHandle()}; -#endif - - static const size_t PipeSize = 32 * LINE_MAX; - - SECURITY_ATTRIBUTES saAttr; - saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); - saAttr.bInheritHandle = TRUE; - saAttr.lpSecurityDescriptor = NULL; - - HANDLE reading_fd, writing_fd; - - static std::atomic pipe_num; - char pipe_name[MAX_PATH + 1]; - snprintf(pipe_name, sizeof(pipe_name), "\\\\.\\Pipe\\Win32Pipes.%08x.%08x", - GetCurrentProcessId(), pipe_num++); - - reading_fd = CreateNamedPipeA(pipe_name, - PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, - PIPE_TYPE_BYTE | PIPE_WAIT, - 1, // number of pipes - PipeSize, PipeSize, - 0, // 50ms default timeout - &saAttr); - - if (reading_fd == INVALID_HANDLE_VALUE) - { - perror("CreateNamedPipeA"); - return { FileHandle(), FileHandle() }; - } - - writing_fd = CreateFileA(pipe_name, - GENERIC_WRITE, - 0, // no sharing - &saAttr, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, - NULL); - - if (writing_fd == INVALID_HANDLE_VALUE) - { - auto err = GetLastError(); - ::CloseHandle(writing_fd); - fprintf(stderr, "CreateFile pipe error: %#x\n", err); - return { FileHandle(), FileHandle() }; - } - - if (!SetHandleInformation(writing_fd, HANDLE_FLAG_INHERIT, 0)) - { - fprintf(stderr, "SetHandleInformation failed!\n"); - return { FileHandle(), FileHandle() }; - } - - if (!SetHandleInformation(reading_fd, HANDLE_FLAG_INHERIT, 0)) - { - fprintf(stderr, "SetHandleInformation failed!\n"); - return { FileHandle(), FileHandle() }; - } - - return { FileHandle(reading_fd), FileHandle(writing_fd) }; -} - - -// Function creates listening TCP socket on given port, waits, accepts single -// connection, and return file descriptor related to the accepted connection. -// In case of error, empty file handle will be returned. -Class::FileHandle Class::listen_socket(unsigned port) -{ - assert(port > 0 && port < 65536); - - SOCKET newsockfd; - int clilen; - struct sockaddr_in serv_addr, cli_addr; - - SOCKET sockFd = ::socket(AF_INET, SOCK_STREAM, 0); - if (sockFd == INVALID_SOCKET) - { - fprintf(stderr, "can't create socket: %#x\n", WSAGetLastError()); - return {}; - } - - BOOL enable = 1; - if (::setsockopt(sockFd, SOL_SOCKET, SO_REUSEADDR, (const char *)&enable, sizeof(BOOL)) == SOCKET_ERROR) - { - ::closesocket(sockFd); - fprintf(stderr, "setsockopt failed\n"); - return {}; - } - memset(&serv_addr, 0, sizeof(serv_addr)); - - serv_addr.sin_family = AF_INET; - serv_addr.sin_addr.s_addr = INADDR_ANY; - serv_addr.sin_port = htons(port); - - if (::bind(sockFd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == SOCKET_ERROR) - { - ::closesocket(sockFd); - fprintf(stderr, "can't bind to specified port!\n"); - return {}; - } - - ::listen(sockFd, 1); - - clilen = sizeof(cli_addr); - newsockfd = ::accept(sockFd, (struct sockaddr*)&cli_addr, &clilen); - ::closesocket(sockFd); - if (newsockfd == INVALID_SOCKET) - { - fprintf(stderr, "can't accept connection\n"); - return {}; - } - - return FileHandle(newsockfd); -} - -// Function enables or disables inheritance of file handle for child processes. -Class::IOResult Class::set_inherit(const FileHandle& fh, bool inherit) -{ - DWORD flags; - if (!GetHandleInformation(fh.handle, &flags)) - return {IOResult::Error}; - - if (inherit) - flags |= HANDLE_FLAG_INHERIT; - else - flags &= ~HANDLE_FLAG_INHERIT; - - if (!SetHandleInformation(fh.handle, HANDLE_FLAG_INHERIT, flags)) - return {IOResult::Error}; - - return {IOResult::Success}; -} - -// Function perform reading from the file: it may read up to `count' bytes to `buf'. -Class::IOResult Class::read(const FileHandle& fh, void *buf, size_t count) -{ - DWORD dwRead = 0; - OVERLAPPED ov = {}; - if (! ReadFile(fh.handle, buf, (DWORD)count, &dwRead, &ov)) - return { (GetLastError() == ERROR_IO_PENDING ? IOResult::Pending : IOResult::Error), dwRead }; - else - return { (dwRead == 0 ? IOResult::Eof : IOResult::Success), dwRead }; -} - - -// Function perform writing to the file: it may write up to `count' byte from `buf'. -Class::IOResult Class::write(const FileHandle& fh, const void *buf, size_t count) -{ - // see https://stackoverflow.com/questions/43939424/writefile-with-windows-sockets-returns-invalid-parameter-error - DWORD dwWritten = 0; - OVERLAPPED ov = {}; - if (! WriteFile(fh.handle, buf, (DWORD)count, &dwWritten, &ov)) - return { (GetLastError() == ERROR_IO_PENDING ? IOResult::Pending : IOResult::Error), dwWritten }; - else - return { IOResult::Success, dwWritten }; -} - - -Class::AsyncHandle Class::async_read(const FileHandle& fh, void *buf, size_t count) -{ - if (fh.handle == INVALID_HANDLE_VALUE) - return {}; - - AsyncHandle result; - result.check_eof = true; - result.handle = fh.handle; - result.overlapped.reset(new OVERLAPPED); - memset(result.overlapped.get(), 0, sizeof(OVERLAPPED)); - result.overlapped->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - if (result.overlapped->hEvent == INVALID_HANDLE_VALUE) - return {}; - - DWORD val; - DWORD bytesRead; - - if (GetConsoleMode(fh.handle, &val)) - { // file handle is the console - // first, remove all events before the first key event, if exists - while (GetNumberOfConsoleInputEvents(fh.handle, &val) && val) - { - INPUT_RECORD event; - if (!PeekConsoleInput(fh.handle, &event, 1, &bytesRead)) - return {}; - if (event.EventType != KEY_EVENT || (event.EventType == KEY_EVENT && !event.Event.KeyEvent.bKeyDown)) - { - if (!ReadConsoleInput(fh.handle, &event, 1, &bytesRead)) - return {}; - } - else - break; - } - if (!val) - { - // nothing to read from the console -- defer call to ReadFile - result.buf = buf, result.count = count; - return result; - } - } - - if (! ReadFile(fh.handle, buf, (DWORD)count, nullptr, result.overlapped.get())) - { - if (GetLastError() != ERROR_IO_PENDING) - return {}; - } - - return result; -} - -Class::AsyncHandle Class::async_write(const FileHandle& fh, const void *buf, size_t count) -{ - if (fh.handle == INVALID_HANDLE_VALUE) - return {}; - - AsyncHandle result; - result.handle = fh.handle; - result.overlapped.reset(new OVERLAPPED); - memset(result.overlapped.get(), 0, sizeof(OVERLAPPED)); - result.overlapped->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - if (result.overlapped->hEvent == INVALID_HANDLE_VALUE) - return {}; - - if (! WriteFile(fh.handle, buf, (DWORD)count, nullptr, result.overlapped.get())) - { - if (GetLastError() != ERROR_IO_PENDING) - return {}; - } - - return result; -} - -bool Class::async_wait(IOSystem::AsyncHandleIterator begin, IOSystem::AsyncHandleIterator end, std::chrono::milliseconds timeout) -{ - // console workaround - for (auto it = begin; it != end; ++it) - { - if (it->handle.buf) - { - DWORD val; - if (GetNumberOfConsoleInputEvents(it->handle.handle, &val) && val) - SetEvent(it->handle.overlapped->hEvent); - } - } - - // count number of active handles - unsigned count = 0; - for (auto it = begin; it != end; ++it) - if (*it) ++count; - - // allocate memory for events array - HANDLE *events = static_cast(alloca(count * sizeof(HANDLE))); - unsigned n = 0; - for (auto it = begin; it != end; ++it) - { - if (*it) - events[n++] = it->handle.overlapped->hEvent; - } - - assert(n == count); - DWORD result = WaitForMultipleObjects(count, events, FALSE, DWORD(timeout.count())); - return result != WAIT_FAILED && result != WAIT_TIMEOUT; -} - -Class::IOResult Class::async_cancel(AsyncHandle& h) -{ - if (!h) - return {IOResult::Error}; - - if (!CloseHandle(h.overlapped->hEvent)) - perror("CloseHandle(event) error"); - - // console workaround -- canceling deffered operation - if (h.buf) - { - h = AsyncHandle(); - return {IOResult::Success}; - } - - IOResult result; - if (!CancelIoEx(h.handle, h.overlapped.get())) - result = {IOResult::Error}; - else - result = {IOResult::Success}; - - h = AsyncHandle(); - return result; -} - -Class::IOResult Class::async_result(AsyncHandle& h) -{ - if (!h) - return {IOResult::Error}; - - DWORD bytes = 1; - bool finished; - - if (h.buf) - { - // workaround for the console - finished = true; - } - else - { - // pipes, normal files, etc... - finished = GetOverlappedResult(h.handle, h.overlapped.get(), &bytes, FALSE); - if (!finished) - { - DWORD error = GetLastError(); - if (error == ERROR_IO_INCOMPLETE) - return {IOResult::Pending}; - } - } - - if (!CloseHandle(h.overlapped->hEvent)) - perror("CloseHandle(event) error"); - - bool check_eof = h.check_eof; - - h = AsyncHandle(); - - if (!finished) - return {IOResult::Error}; - - if (check_eof && bytes == 0) - return {IOResult::Eof, bytes}; - - return {IOResult::Success, bytes}; -} - -// Function closes the file represented by file handle. -Class::IOResult Class::close(const FileHandle& fh) -{ - assert(fh); - if (fh.type == FileHandle::Socket) - return { ::closesocket((SOCKET)fh.handle) == 0 ? IOResult::Success : IOResult::Error }; - else - return { ::CloseHandle(fh.handle) ? IOResult::Success : IOResult::Error }; -} - - -// Function allows non-blocking IO on files, it is similar with select(2) system call on Unix. -// Arguments includes: pointers to three sets of file handles (for reading, for writing, and for -// exceptions), and timeout value, in milliseconds. Any pointer might have NULL value if some set -// isn't specified. -// Function returns -1 on error, 0 on timeout or number of ready to read/write file handles. -// If function returns value greater than zero, at least one of the sets, passed in arguments, -// is not empty and contains file handles ready to read/write/etc... - - -// This function returns triplet of currently selected standard files. -Class::IOSystem::StdFiles Class::get_std_files() -{ - using Handles = std::tuple; - /*thread_local*/ static alignas(alignof(Handles)) char mem[sizeof(Handles)]; // TODO - Handles& handles = *new (mem) Handles { - FileHandle(GetStdHandle(STD_INPUT_HANDLE)), - FileHandle(GetStdHandle(STD_OUTPUT_HANDLE)), - FileHandle(GetStdHandle(STD_ERROR_HANDLE)) - }; - return { std::get(handles), - std::get(handles), - std::get(handles) }; -} - - -// StdIOSwap class allows to substitute set of standard IO files with one provided to constructor. -// Substitution exists only during life time of StsIOSwap instance. -Class::StdIOSwap::StdIOSwap(const StdFiles& files) : m_valid(true) -{ - const static unsigned NFD = std::tuple_size::value; - static const DWORD std_handles[NFD] = {STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, STD_ERROR_HANDLE}; - static const int open_flags[NFD] = {_O_RDONLY | _O_BINARY, _O_BINARY, _O_BINARY}; - const int open_fds[NFD] = {_fileno(stdin), _fileno(stdout), _fileno(stderr)}; - - const FileHandle new_handles[NFD] = { - std::get(files).handle, - std::get(files).handle, - std::get(files).handle }; - - fflush(stdout); - fflush(stderr); - - for (unsigned n = 0; n < NFD; n++) - { - if (new_handles[n].type != FileHandle::FileOrPipe) - throw std::runtime_error("can't use socket handle for stdin/stdout/stderr"); - } - - for (unsigned n = 0; n < NFD; n++) - { - m_orig_handle[n] = GetStdHandle(std_handles[n]); - if (m_orig_handle[n] == INVALID_HANDLE_VALUE) - { - char msg[256]; - snprintf(msg, sizeof(msg), "GetStdHandle(%#x): error", std_handles[n]); - throw std::runtime_error(msg); - } - - if (!SetHandleInformation(new_handles[n].handle, HANDLE_FLAG_INHERIT, 1)) - fprintf(stderr, "SetHandleInformation failed!\n"); - - if (!SetStdHandle(std_handles[n], new_handles[n].handle)) - { - char msg[256]; - snprintf(msg, sizeof(msg), "SetStdHandle(%#x, %p): error", std_handles[n], new_handles[n].handle); - throw std::runtime_error(msg); - } - - - int fd = _open_osfhandle(reinterpret_cast(new_handles[n].handle), open_flags[n]); - if (fd == -1) - throw Win32Exception("_open_osfhandle"); - - m_orig_fd[n] = _dup(open_fds[n]); - if (m_orig_fd[n] == -1) - throw Win32Exception("_dup"); - - if (_dup2(fd, open_fds[n]) == -1) - throw Win32Exception("_dup2"); - - close(fd); - } -} - -Class::StdIOSwap::~StdIOSwap() -{ - if (!m_valid) - return; - - const static unsigned NFD = std::tuple_size::value; - static const DWORD std_handles[NFD] = {STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, STD_ERROR_HANDLE}; - const int open_fds[NFD] = {_fileno(stdin), _fileno(stdout), _fileno(stderr)}; - - fflush(stdout); - fflush(stderr); - - for (unsigned n = 0; n < NFD; n++) - { - if (!SetStdHandle(std_handles[n], m_orig_handle[n])) - { - abort(); - } - - _dup2(m_orig_fd[n], open_fds[n]); - close(m_orig_fd[n]); - } -} - -#endif // WIN32 diff --git a/src/windows/iosystem_win32.h b/src/windows/iosystem_win32.h deleted file mode 100644 index 424d367..0000000 --- a/src/windows/iosystem_win32.h +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (C) 2020 Samsung Electronics Co., Ltd. -// See the LICENSE file in the project root for more information. - -/// \file iosystem_win32.h This file contains windows-specific declaration of IOSystem class (see iosystem.h). -// -#ifdef _WIN32 -#pragma once -#include -#include // TODO -#include -#include - -namespace netcoredbg -{ - -template <> struct IOSystemTraits -{ - struct FileHandle - { - FileHandle() : handle(INVALID_HANDLE_VALUE), type(FileOrPipe) {} - - explicit operator bool() const { return handle != INVALID_HANDLE_VALUE; } - - enum FileType - { - FileOrPipe, - Socket - }; - - FileHandle(HANDLE filefd) : handle(filefd), type(FileOrPipe) {} - FileHandle(SOCKET sockfd) : handle((HANDLE)sockfd), type(Socket) {} - - HANDLE handle; - enum FileType type; - }; - - struct AsyncHandle - { - HANDLE handle; - std::unique_ptr overlapped; - bool check_eof; - - // workaround: non-blocking reading from console - void *buf; - size_t count; - - AsyncHandle() - : handle(INVALID_HANDLE_VALUE), overlapped(), check_eof(false), buf(nullptr), count(0) - {} - - AsyncHandle(AsyncHandle&& other) noexcept - : handle(other.handle), overlapped(std::move(other.overlapped)), check_eof(other.check_eof), - buf(other.buf), count(other.count) - { - other.handle = INVALID_HANDLE_VALUE; - } - - AsyncHandle& operator=(AsyncHandle&& other) noexcept - { - return this->~AsyncHandle(), *new (this) AsyncHandle(std::move(other)); - } - - explicit operator bool() const { return handle != INVALID_HANDLE_VALUE; } - }; - - using IOSystem = typename IOSystemImpl >; - using IOResult = IOSystem::IOResult; - - static std::pair unnamed_pipe(); - static FileHandle listen_socket(unsigned tcp_port); - static IOResult set_inherit(const FileHandle &, bool); - static IOResult read(const FileHandle &, void *buf, size_t count); - static IOResult write(const FileHandle &, const void *buf, size_t count); - static AsyncHandle async_read(const FileHandle &, void *buf, size_t count); - static AsyncHandle async_write(const FileHandle &, const void *buf, size_t count); - static bool async_wait(IOSystem::AsyncHandleIterator begin, IOSystem::AsyncHandleIterator end, std::chrono::milliseconds); - static IOResult async_cancel(AsyncHandle &); - static IOResult async_result(AsyncHandle &); - static IOResult close(const FileHandle &); - - static IOSystem::StdFiles get_std_files(); - - struct StdIOSwap - { - using StdFiles = IOSystem::StdFiles; - using StdFileType = IOSystem::StdFileType; - StdIOSwap(const StdFiles &); - ~StdIOSwap(); - - StdIOSwap(StdIOSwap&& other) - { - m_valid = other.m_valid; - if (!m_valid) - return; - - other.m_valid = false; - for (unsigned n = 0; n < std::tuple_size::value; n++) - { - m_orig_handle[n] = other.m_orig_handle[n]; - m_orig_fd[n] = other.m_orig_fd[n]; - } - } - - bool m_valid; - HANDLE m_orig_handle[std::tuple_size::value]; - int m_orig_fd[std::tuple_size::value]; - }; -}; - -} // ::netcoredbg -#endif // WIN32 diff --git a/src/windows/platform_win32.cpp b/src/windows/platform_win32.cpp deleted file mode 100644 index 6e08f7b..0000000 --- a/src/windows/platform_win32.cpp +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (C) 2020 Samsung Electronics Co., Ltd. -// See the LICENSE file in the project root for more information. - -/// \file platform_win32.cpp This file contains windows-specific function definitions, -/// for functions defined in platform.h - -#ifdef WIN32 -#include -#include // char **environ -#include "utils/platform.h" -#include "utils/limits.h" - -namespace netcoredbg -{ - -// Function returns memory mapping page size (like sysconf(_SC_PAGESIZE) on Unix). -unsigned long OSPageSize() -{ - static unsigned long pageSize = []{ - SYSTEM_INFO si; - GetSystemInfo(&si); - return si.dwPageSize; - }(); - - return pageSize; -} - - -// Function suspends process execution for specified amount of time (in microseconds) -void USleep(unsigned long usec) -{ - HANDLE timer; - LARGE_INTEGER ft; - - ft.QuadPart = -(10*(long)usec); // Convert to 100 nanosecond interval, negative value indicates relative time - - timer = CreateWaitableTimer(NULL, TRUE, NULL); - SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0); - WaitForSingleObject(timer, INFINITE); - CloseHandle(timer); -} - - -// Function returns list of environment variables (like char **environ). -char** GetSystemEnvironment() -{ - return environ; -} - -} // ::netcoredbg -#endif