From e2dd6e0c2661f05075870a7955b233173fac592c Mon Sep 17 00:00:00 2001 From: MacroModel Date: Mon, 8 Dec 2025 15:11:25 +0800 Subject: [PATCH] Enhance socket and process handling in Windows platform - Added noexcept specifier to constructors in `ip` struct for improved exception safety. - Updated function signatures in `apis.h` for better clarity and consistency. - Introduced new exception handling structures in `win32_definitions.h` to support enhanced error management. - Improved DNS iterator functionality in `win32_9xa_dns.h` by adding a new method to convert to `ip_address`. - Refactored `posix.h` to improve execveat handling, including better path resolution and error management. - Cleaned up thread handling in `wasi.h` by ensuring proper pragma usage. --- include/fast_io_core_impl/socket/ip.h | 6 +- include/fast_io_hosted/platforms/win32/apis.h | 21 +-- .../platforms/win32/win32_definitions.h | 42 +++++ .../platforms/win32_network/win32_9xa_dns.h | 9 +- .../fast_io_hosted/process/process/posix.h | 154 ++++++++++-------- include/fast_io_hosted/threads/thread/wasi.h | 2 +- 6 files changed, 153 insertions(+), 81 deletions(-) diff --git a/include/fast_io_core_impl/socket/ip.h b/include/fast_io_core_impl/socket/ip.h index e511d1d69..9cc5fcce9 100644 --- a/include/fast_io_core_impl/socket/ip.h +++ b/include/fast_io_core_impl/socket/ip.h @@ -40,13 +40,13 @@ struct ip } inline explicit constexpr ip() noexcept = default; - inline explicit constexpr ip(ipv4 add) + inline explicit constexpr ip(ipv4 add) noexcept : address{.address = {.v4 = add.address}, .isv4 = true}, port(add.port) {} - inline explicit constexpr ip(ipv6 add) + inline explicit constexpr ip(ipv6 add) noexcept : address{.address = {.v6 = add.address}}, port(add.port) {} - inline explicit constexpr ip(ip_address addr, ::std::uint_least16_t prt) + inline explicit constexpr ip(ip_address addr, ::std::uint_least16_t prt) noexcept : address{addr}, port{prt} {} }; diff --git a/include/fast_io_hosted/platforms/win32/apis.h b/include/fast_io_hosted/platforms/win32/apis.h index cc94d1b5f..658ed8fde 100644 --- a/include/fast_io_hosted/platforms/win32/apis.h +++ b/include/fast_io_hosted/platforms/win32/apis.h @@ -172,7 +172,7 @@ FAST_IO_DLLIMPORT int FAST_IO_WINSTDCALL SetWaitableTimer(void *, ::std::int_lea FAST_IO_DLLIMPORT int FAST_IO_WINSTDCALL CancelWaitableTimer(void *) noexcept FAST_IO_WINSTDCALL_RENAME(CancelWaitableTimer, 4); FAST_IO_DLLIMPORT int FAST_IO_WINSTDCALL LookupPrivilegeValueA(char const *__restrict, char const *__restrict, ::std::int_least64_t *) noexcept FAST_IO_WINSTDCALL_RENAME(LookupPrivilegeValueA, 12); FAST_IO_DLLIMPORT int FAST_IO_WINSTDCALL LookupPrivilegeValueW(char16_t const *__restrict, char16_t const *__restrict, ::std::int_least64_t *) noexcept FAST_IO_WINSTDCALL_RENAME(LookupPrivilegeValueW, 12); -FAST_IO_DLLIMPORT void *FAST_IO_WINSTDCALL CreateThread(security_attributes *, ::std::size_t, ::std::uint_least32_t (FAST_IO_WINSTDCALL*)(void*), void*, ::std::uint_least32_t, ::std::uint_least32_t*) noexcept FAST_IO_WINSTDCALL_RENAME(CreateThread, 24); +FAST_IO_DLLIMPORT void *FAST_IO_WINSTDCALL CreateThread(security_attributes *, ::std::size_t, ::std::uint_least32_t(FAST_IO_WINSTDCALL *)(void *), void *, ::std::uint_least32_t, ::std::uint_least32_t *) noexcept FAST_IO_WINSTDCALL_RENAME(CreateThread, 24); FAST_IO_DLLIMPORT char16_t **FAST_IO_WINSTDCALL CommandLineToArgvW(char16_t const *, int *) noexcept FAST_IO_WINSTDCALL_RENAME(CommandLineToArgvW, 8); FAST_IO_DLLIMPORT void *FAST_IO_WINSTDCALL LocalFree(void *) noexcept FAST_IO_WINSTDCALL_RENAME(LocalFree, 4); FAST_IO_DLLIMPORT int FAST_IO_WINSTDCALL PrefetchVirtualMemory(void *, ::std::size_t, ::fast_io::win32::win32_memory_range_entry *, ::std::uint_least32_t) noexcept FAST_IO_WINSTDCALL_RENAME(PrefetchVirtualMemory, 16); @@ -182,16 +182,17 @@ FAST_IO_DLLIMPORT void FAST_IO_WINSTDCALL Sleep(::std::uint_least32_t) noexcept FAST_IO_DLLIMPORT int FAST_IO_WINSTDCALL SwitchToThread() noexcept FAST_IO_WINSTDCALL_RENAME(SwitchToThread, 0); FAST_IO_DLLIMPORT char16_t *FAST_IO_WINSTDCALL GetEnvironmentStringsW() noexcept FAST_IO_WINSTDCALL_RENAME(GetEnvironmentStringsW, 0); FAST_IO_DLLIMPORT char *FAST_IO_WINSTDCALL GetEnvironmentStringsA() noexcept FAST_IO_WINSTDCALL_RENAME(GetEnvironmentStringsA, 0); -FAST_IO_DLLIMPORT int FAST_IO_WINSTDCALL FreeEnvironmentStringsW(char16_t*) noexcept FAST_IO_WINSTDCALL_RENAME(FreeEnvironmentStringsW, 4); -FAST_IO_DLLIMPORT int FAST_IO_WINSTDCALL FreeEnvironmentStringsA(char*) noexcept FAST_IO_WINSTDCALL_RENAME(FreeEnvironmentStringsA, 4); -FAST_IO_DLLIMPORT int FAST_IO_WINSTDCALL getsockopt(::std::size_t, int, int, char*, int*) noexcept FAST_IO_WINSTDCALL_RENAME(getsockopt, 20); -FAST_IO_DLLIMPORT ::std::uint_least32_t FAST_IO_WINSTDCALL GetFullPathNameW(char16_t const*, ::std::uint_least32_t, char16_t*, char16_t**) noexcept FAST_IO_WINSTDCALL_RENAME(GetFullPathNameW, 16); -FAST_IO_DLLIMPORT ::std::uint_least32_t FAST_IO_WINSTDCALL GetFullPathNameA(char const*, ::std::uint_least32_t, char*, char**) noexcept FAST_IO_WINSTDCALL_RENAME(GetFullPathNameA, 16); -FAST_IO_DLLIMPORT int FAST_IO_WINSTDCALL GetVolumeInformationW(char16_t const*, char16_t*, ::std::uint_least32_t, ::std::uint_least32_t*, ::std::uint_least32_t*, ::std::uint_least32_t*, char16_t*, ::std::uint_least32_t) noexcept FAST_IO_WINSTDCALL_RENAME(GetVolumeInformationW, 32); -FAST_IO_DLLIMPORT int FAST_IO_WINSTDCALL GetVolumeInformationA(char const*, char*, ::std::uint_least32_t, ::std::uint_least32_t*, ::std::uint_least32_t*, ::std::uint_least32_t*, char*, ::std::uint_least32_t) noexcept FAST_IO_WINSTDCALL_RENAME(GetVolumeInformationA, 32); -FAST_IO_DLLIMPORT int FAST_IO_WINSTDCALL GetDiskFreeSpaceW(char16_t const*, ::std::uint_least32_t*, ::std::uint_least32_t*, ::std::uint_least32_t*, ::std::uint_least32_t*) noexcept FAST_IO_WINSTDCALL_RENAME(GetDiskFreeSpaceW, 20); -FAST_IO_DLLIMPORT int FAST_IO_WINSTDCALL GetDiskFreeSpaceA(char const*, ::std::uint_least32_t*, ::std::uint_least32_t*, ::std::uint_least32_t*, ::std::uint_least32_t*) noexcept FAST_IO_WINSTDCALL_RENAME(GetDiskFreeSpaceA, 20); +FAST_IO_DLLIMPORT int FAST_IO_WINSTDCALL FreeEnvironmentStringsW(char16_t *) noexcept FAST_IO_WINSTDCALL_RENAME(FreeEnvironmentStringsW, 4); +FAST_IO_DLLIMPORT int FAST_IO_WINSTDCALL FreeEnvironmentStringsA(char *) noexcept FAST_IO_WINSTDCALL_RENAME(FreeEnvironmentStringsA, 4); +FAST_IO_DLLIMPORT int FAST_IO_WINSTDCALL getsockopt(::std::size_t, int, int, char *, int *) noexcept FAST_IO_WINSTDCALL_RENAME(getsockopt, 20); +FAST_IO_DLLIMPORT ::std::uint_least32_t FAST_IO_WINSTDCALL GetFullPathNameW(char16_t const *, ::std::uint_least32_t, char16_t *, char16_t **) noexcept FAST_IO_WINSTDCALL_RENAME(GetFullPathNameW, 16); +FAST_IO_DLLIMPORT ::std::uint_least32_t FAST_IO_WINSTDCALL GetFullPathNameA(char const *, ::std::uint_least32_t, char *, char **) noexcept FAST_IO_WINSTDCALL_RENAME(GetFullPathNameA, 16); +FAST_IO_DLLIMPORT int FAST_IO_WINSTDCALL GetVolumeInformationW(char16_t const *, char16_t *, ::std::uint_least32_t, ::std::uint_least32_t *, ::std::uint_least32_t *, ::std::uint_least32_t *, char16_t *, ::std::uint_least32_t) noexcept FAST_IO_WINSTDCALL_RENAME(GetVolumeInformationW, 32); +FAST_IO_DLLIMPORT int FAST_IO_WINSTDCALL GetVolumeInformationA(char const *, char *, ::std::uint_least32_t, ::std::uint_least32_t *, ::std::uint_least32_t *, ::std::uint_least32_t *, char *, ::std::uint_least32_t) noexcept FAST_IO_WINSTDCALL_RENAME(GetVolumeInformationA, 32); +FAST_IO_DLLIMPORT int FAST_IO_WINSTDCALL GetDiskFreeSpaceW(char16_t const *, ::std::uint_least32_t *, ::std::uint_least32_t *, ::std::uint_least32_t *, ::std::uint_least32_t *) noexcept FAST_IO_WINSTDCALL_RENAME(GetDiskFreeSpaceW, 20); +FAST_IO_DLLIMPORT int FAST_IO_WINSTDCALL GetDiskFreeSpaceA(char const *, ::std::uint_least32_t *, ::std::uint_least32_t *, ::std::uint_least32_t *, ::std::uint_least32_t *) noexcept FAST_IO_WINSTDCALL_RENAME(GetDiskFreeSpaceA, 20); FAST_IO_DLLIMPORT void FAST_IO_WINSTDCALL RaiseException(::std::uint_least32_t, ::std::uint_least32_t, ::std::uint_least32_t, ::std::size_t const *) noexcept FAST_IO_WINSTDCALL_RENAME(RaiseException, 16); FAST_IO_DLLIMPORT void FAST_IO_WINSTDCALL ExitProcess(::std::uint_least32_t) noexcept FAST_IO_WINSTDCALL_RENAME(ExitProcess, 4); +FAST_IO_DLLIMPORT void *FAST_IO_WINSTDCALL AddVectoredExceptionHandler(::std::uint_least32_t, pvectored_exception_handler) noexcept FAST_IO_WINSTDCALL_RENAME(AddVectoredExceptionHandler, 8); } // namespace fast_io::win32 diff --git a/include/fast_io_hosted/platforms/win32/win32_definitions.h b/include/fast_io_hosted/platforms/win32/win32_definitions.h index 639329cba..280365686 100644 --- a/include/fast_io_hosted/platforms/win32/win32_definitions.h +++ b/include/fast_io_hosted/platforms/win32/win32_definitions.h @@ -459,4 +459,46 @@ struct win32_memory_range_entry void* VirtualAddress; ::std::size_t NumberOfBytes; }; + +inline constexpr ::std::size_t exception_maximum_parameters{15u}; + +struct exception_record +{ + ::std::uint_least32_t ExceptionCode; + ::std::uint_least32_t ExceptionFlags; + exception_record* ExceptionRecord; + void* ExceptionAddress; + ::std::uint_least32_t NumberParameters; + ::std::size_t ExceptionInformation[exception_maximum_parameters]; +}; + +struct exception_record32 +{ + ::std::uint_least32_t ExceptionCode; + ::std::uint_least32_t ExceptionFlags; + ::std::uint_least32_t ExceptionRecord; + ::std::uint_least32_t ExceptionAddress; + ::std::uint_least32_t NumberParameters; + ::std::uint_least32_t ExceptionInformation[exception_maximum_parameters]; +}; + +struct exception_record64 +{ + ::std::uint_least32_t ExceptionCode; + ::std::uint_least32_t ExceptionFlags; + ::std::uint_least64_t ExceptionRecord; + ::std::uint_least64_t ExceptionAddress; + ::std::uint_least32_t NumberParameters; + ::std::uint_least32_t UnusedAlignment; + ::std::uint_least64_t ExceptionInformation[exception_maximum_parameters]; +}; + +struct exception_pointers +{ + exception_record* ExceptionRecord; + void* ContextRecord; +}; + +using pvectored_exception_handler = ::std::int_least32_t(FAST_IO_WINSTDCALL *)(exception_pointers *) noexcept; + } // namespace fast_io::win32 diff --git a/include/fast_io_hosted/platforms/win32_network/win32_9xa_dns.h b/include/fast_io_hosted/platforms/win32_network/win32_9xa_dns.h index ec045698e..0b4a197ff 100644 --- a/include/fast_io_hosted/platforms/win32_network/win32_9xa_dns.h +++ b/include/fast_io_hosted/platforms/win32_network/win32_9xa_dns.h @@ -114,7 +114,7 @@ struct win32_9xa_dns_iterator inline constexpr win32_9xa_dns_iterator operator*(win32_9xa_dns_iterator d) noexcept { - return {d.res}; + return {d.res, d.pos}; } inline constexpr win32_9xa_dns_iterator &operator++(win32_9xa_dns_iterator &d) noexcept @@ -159,11 +159,16 @@ inline constexpr bool operator!=(win32_9xa_dns_iterator a, ::std::default_sentin return !(a == b); } -inline constexpr ::fast_io::ip to_ip(win32_9xa_dns_iterator d, ::std::uint_least16_t port) +inline constexpr ::fast_io::ip to_ip(win32_9xa_dns_iterator d, ::std::uint_least16_t port) noexcept { return ::fast_io::details::hostent_to_ip_impl(d.res, d.pos, port); } +inline constexpr ::fast_io::ip_address to_ip_address(win32_9xa_dns_iterator d) noexcept +{ + return ::fast_io::details::hostent_to_ip_address_impl(d.res, d.pos); +} + namespace details { inline ::fast_io::win32::hostent *win32_9xa_gethostbyname_impl(char const *name) diff --git a/include/fast_io_hosted/process/process/posix.h b/include/fast_io_hosted/process/process/posix.h index e8f633f65..97af7f8bb 100644 --- a/include/fast_io_hosted/process/process/posix.h +++ b/include/fast_io_hosted/process/process/posix.h @@ -13,7 +13,8 @@ namespace posix // parameters for the exec functions may seem to be the natural choice, given that these functions do not modify either the array of pointers or the characters to which the // function points, but this would disallow existing correct code. Instead, only the array of pointers is noted as constant. -#if defined(__DARWIN_C_LEVEL) || defined(__MSDOS__) +#if (defined(__APPLE__) || defined(__DARWIN_C_LEVEL)) || (defined(__MSDOS__) || defined(__DJGPP__)) +extern int libc_execve(char const *pathname, char *const *argv, char *const *envp) noexcept __asm__("_execve"); extern int libc_fexecve(int fd, char *const *argv, char *const *envp) noexcept __asm__("_fexecve"); extern int libc_execveat(int dirfd, char const *pathname, char *const *argv, char *const *envp, int flags) noexcept __asm__("_execveat"); extern int libc_kill(pid_t pid, int sig) noexcept __asm__("_kill"); @@ -24,6 +25,7 @@ extern pid_t libc_waitpid(pid_t pid, int *status, int options) noexcept __asm__( [[noreturn]] extern void libc_exit(int status) noexcept __asm__("__Exit"); [[noreturn]] extern void libc_exit2(int status) noexcept __asm__("__exit"); #else +extern int libc_execve(char const *pathname, char *const *argv, char *const *envp) noexcept __asm__("execve"); extern int libc_fexecve(int fd, char *const *argv, char *const *envp) noexcept __asm__("fexecve"); extern int libc_execveat(int dirfd, char const *pathname, char *const *argv, char *const *envp, int flags) noexcept __asm__("execveat"); extern int libc_kill(pid_t pid, int sig) noexcept __asm__("kill"); @@ -403,20 +405,102 @@ inline void posix_waitpid_noexcept(pid_t pid) noexcept inline int posix_execveat(int dirfd, char const *cstr, char const *const *args, char const *const *envp, process_mode mode) noexcept { - bool const follow{(mode & process_mode::follow) == process_mode::follow}; + [[maybe_unused]] bool const follow{(mode & process_mode::follow) == process_mode::follow}; #if defined(__linux__) && defined(__NR_execveat) + // linux execveat + int flags{}; if (!follow) { flags |= AT_SYMLINK_NOFOLLOW; } return -(system_call<__NR_execveat, int>(dirfd, cstr, args, envp, flags)); + +#elif defined(__APPLE__) || defined(__DARWIN_C_LEVEL) + // macOS / Darwin: no public execveat/fexecve + // Fallback: emulate execveat(dirfd, path, ...) using execve() and F_GETPATH. + // Limitations: + // - AT_SYMLINK_NOFOLLOW is ignored (no equivalent execve flag). + // - AT_EMPTY_PATH / other execveat-specific flags are not supported. + + if (cstr == nullptr) + { + errno = EFAULT; + return errno; + } + +#if defined(AT_FDCWD) + constexpr int fdcwd_val{AT_FDCWD}; +#else + constexpr int fdcwd_val{-2}; +#endif + + // Absolute path: ignore dirfd, behave like execve() + if (cstr[0] == '/') + { + ::fast_io::posix::libc_execve(cstr, const_cast(args), const_cast(envp)); + return errno; + } + + // Relative path and "current working directory" dirfd: just use execve() + if (dirfd == fdcwd_val) + { + ::fast_io::posix::libc_execve(cstr, const_cast(args), const_cast(envp)); + return errno; + } + + // dirfd refers to a directory: resolve its absolute path via F_GETPATH and + // append the relative filename to form an absolute pathname. +#if defined(PATH_MAX) + constexpr ::std::size_t path_max{PATH_MAX}; +#elif defined(MAXPATHLEN) + constexpr ::std::size_t path_max{MAXPATHLEN}; +#else + constexpr ::std::size_t path_max{1024u}; +#endif + + constexpr ::std::size_t dirbuf_sz{path_max + 1u}; + + char dirbuf[dirbuf_sz]{}; + auto ret_dir{::fast_io::details::posix::fcntl(dirfd, F_GETPATH, dirbuf)}; + if (ret_dir == -1) [[unlikely]] + { + return errno; + } + + auto dir_len{::fast_io::cstr_nlen(dirbuf, path_max)}; + auto name_len{::fast_io::cstr_len(cstr)}; + + // Ensure combined path fits into our fixed buffer + if (dir_len + 1u + name_len >= path_max) [[unlikely]] + { + errno = ENAMETOOLONG; + return errno; + } + + char fullpath[dirbuf_sz]; + auto it{fullpath}; + for (::std::size_t i{}; i < dir_len; ++i) + { + it[i] = dirbuf[i]; + } + it[dir_len] = '/'; + for (::std::size_t i{}; i < name_len; ++i) + { + it[dir_len + 1u + i] = cstr[i]; + } + fullpath[dir_len + 1u + name_len] = '\0'; + + ::fast_io::posix::libc_execve(fullpath, const_cast(args), const_cast(envp)); + return errno; #else + // POSIX 2008: openat + fexecve + int flags{O_RDONLY}; if (!follow) { - flags |= AT_SYMLINK_NOFOLLOW; + flags |= O_NOFOLLOW; } int fd{::fast_io::details::my_posix_openat_noexcept(dirfd, cstr, flags, 0644)}; if (fd != -1) [[likely]] @@ -716,7 +800,7 @@ struct fd_remapper }; // only used in vfork_execveat_common_impl() -inline void vfork_and_execveat(pid_t &pid, int dirfd, char const *cstr, char const *const *args, char const *const *envp, unsigned volatile &t_errno, process_mode mode) noexcept +inline void vfork_and_execveat(pid_t &pid, int dirfd, char const *cstr, char const *const *args, char const *const *envp, unsigned volatile &t_errno, process_mode mode) { // vfork can only be called through libc wrapper pid = ::fast_io::posix::libc_vfork(); @@ -741,68 +825,8 @@ inline void vfork_and_execveat(pid_t &pid, int dirfd, char const *cstr, char con } #endif - bool const follow{(mode & process_mode::follow) == process_mode::follow}; - -#if defined(__linux__) - -#if defined(__NR_execveat) - int flags{}; - if (!follow) - { - flags |= AT_SYMLINK_NOFOLLOW; - } - - auto ret{system_call<__NR_execveat, int>(dirfd, cstr, args, envp, flags)}; - if (::fast_io::linux_system_call_fails(ret)) - { - t_errno = -ret; - } - else - { - t_errno = 0; - } -#else - int flags{}; - if (!follow) - { - flags |= AT_SYMLINK_NOFOLLOW; - } - - auto ret{::fast_io::posix::libc_execveat(dirfd, cstr, const_cast(args), const_cast(envp), flags)}; - if (ret == -1) - { - t_errno = errno; - } - else - { - t_errno = 0; - } -#endif - -#if defined(__NR_exit_group) - ::fast_io::system_call_no_return<__NR_exit_group>(127); -#else - ::fast_io::system_call_no_return<__NR_exit>(127); -#endif -#else - int flags{}; - if (!follow) - { - flags |= AT_SYMLINK_NOFOLLOW; - } - - auto ret{::fast_io::posix::libc_execveat(dirfd, cstr, const_cast(args), const_cast(envp), flags)}; - if (ret == -1) - { - t_errno = errno; - } - else - { - t_errno = 0; - } - + t_errno = posix_execveat(dirfd, cstr, args, envp, mode); ::fast_io::posix::libc_exit2(127); -#endif __builtin_unreachable(); } diff --git a/include/fast_io_hosted/threads/thread/wasi.h b/include/fast_io_hosted/threads/thread/wasi.h index 2af0274c0..29ec2ea26 100644 --- a/include/fast_io_hosted/threads/thread/wasi.h +++ b/include/fast_io_hosted/threads/thread/wasi.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once #if defined(__wasi__)