@@ -13,7 +13,8 @@ namespace posix
1313// 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
1414// function points, but this would disallow existing correct code. Instead, only the array of pointers is noted as constant.
1515
16- #if defined(__DARWIN_C_LEVEL) || defined(__MSDOS__)
16+ #if (defined(__APPLE__) || defined(__DARWIN_C_LEVEL)) || (defined(__MSDOS__) || defined(__DJGPP__))
17+ extern int libc_execve (char const *pathname, char *const *argv, char *const *envp) noexcept __asm__(" _execve" );
1718extern int libc_fexecve (int fd, char *const *argv, char *const *envp) noexcept __asm__(" _fexecve" );
1819extern int libc_execveat (int dirfd, char const *pathname, char *const *argv, char *const *envp, int flags) noexcept __asm__(" _execveat" );
1920extern 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__(
2425[[noreturn]] extern void libc_exit (int status) noexcept __asm__(" __Exit" );
2526[[noreturn]] extern void libc_exit2 (int status) noexcept __asm__(" __exit" );
2627#else
28+ extern int libc_execve (char const *pathname, char *const *argv, char *const *envp) noexcept __asm__(" execve" );
2729extern int libc_fexecve (int fd, char *const *argv, char *const *envp) noexcept __asm__(" fexecve" );
2830extern int libc_execveat (int dirfd, char const *pathname, char *const *argv, char *const *envp, int flags) noexcept __asm__(" execveat" );
2931extern int libc_kill (pid_t pid, int sig) noexcept __asm__(" kill" );
@@ -403,20 +405,102 @@ inline void posix_waitpid_noexcept(pid_t pid) noexcept
403405
404406inline int posix_execveat (int dirfd, char const *cstr, char const *const *args, char const *const *envp, process_mode mode) noexcept
405407{
406- bool const follow{(mode & process_mode::follow) == process_mode::follow};
408+ [[maybe_unused]] bool const follow{(mode & process_mode::follow) == process_mode::follow};
407409
408410#if defined(__linux__) && defined(__NR_execveat)
411+ // linux execveat
412+
409413 int flags{};
410414 if (!follow)
411415 {
412416 flags |= AT_SYMLINK_NOFOLLOW;
413417 }
414418 return -(system_call<__NR_execveat, int >(dirfd, cstr, args, envp, flags));
419+
420+ #elif defined(__APPLE__) || defined(__DARWIN_C_LEVEL)
421+ // macOS / Darwin: no public execveat/fexecve
422+ // Fallback: emulate execveat(dirfd, path, ...) using execve() and F_GETPATH.
423+ // Limitations:
424+ // - AT_SYMLINK_NOFOLLOW is ignored (no equivalent execve flag).
425+ // - AT_EMPTY_PATH / other execveat-specific flags are not supported.
426+
427+ if (cstr == nullptr )
428+ {
429+ errno = EFAULT;
430+ return errno;
431+ }
432+
433+ #if defined(AT_FDCWD)
434+ constexpr int fdcwd_val{AT_FDCWD};
435+ #else
436+ constexpr int fdcwd_val{-2 };
437+ #endif
438+
439+ // Absolute path: ignore dirfd, behave like execve()
440+ if (cstr[0 ] == ' /' )
441+ {
442+ ::fast_io::posix::libc_execve (cstr, const_cast <char *const *>(args), const_cast<char *const *>(envp));
443+ return errno;
444+ }
445+
446+ // Relative path and "current working directory" dirfd: just use execve()
447+ if (dirfd == fdcwd_val)
448+ {
449+ ::fast_io::posix::libc_execve (cstr, const_cast <char *const *>(args), const_cast<char *const *>(envp));
450+ return errno;
451+ }
452+
453+ // dirfd refers to a directory: resolve its absolute path via F_GETPATH and
454+ // append the relative filename to form an absolute pathname.
455+ #if defined(PATH_MAX)
456+ constexpr ::std::size_t path_max{PATH_MAX};
457+ #elif defined(MAXPATHLEN)
458+ constexpr ::std::size_t path_max{MAXPATHLEN};
459+ #else
460+ constexpr ::std::size_t path_max{1024u };
461+ #endif
462+
463+ constexpr ::std::size_t dirbuf_sz{path_max + 1u };
464+
465+ char dirbuf[dirbuf_sz]{};
466+ auto ret_dir{::fast_io::details::posix::fcntl (dirfd, F_GETPATH, dirbuf)};
467+ if (ret_dir == -1 ) [[unlikely]]
468+ {
469+ return errno;
470+ }
471+
472+ auto dir_len{::fast_io::cstr_nlen (dirbuf, path_max)};
473+ auto name_len{::fast_io::cstr_len (cstr)};
474+
475+ // Ensure combined path fits into our fixed buffer
476+ if (dir_len + 1u + name_len >= path_max) [[unlikely]]
477+ {
478+ errno = ENAMETOOLONG;
479+ return errno;
480+ }
481+
482+ char fullpath[dirbuf_sz];
483+ auto it{fullpath};
484+ for (::std::size_t i{}; i < dir_len; ++i)
485+ {
486+ it[i] = dirbuf[i];
487+ }
488+ it[dir_len] = ' /' ;
489+ for (::std::size_t i{}; i < name_len; ++i)
490+ {
491+ it[dir_len + 1u + i] = cstr[i];
492+ }
493+ fullpath[dir_len + 1u + name_len] = ' \0 ' ;
494+
495+ ::fast_io::posix::libc_execve (fullpath, const_cast <char *const *>(args), const_cast<char *const *>(envp));
496+ return errno;
415497#else
498+ // POSIX 2008: openat + fexecve
499+
416500 int flags{O_RDONLY};
417501 if (!follow)
418502 {
419- flags |= AT_SYMLINK_NOFOLLOW ;
503+ flags |= O_NOFOLLOW ;
420504 }
421505 int fd{::fast_io::details::my_posix_openat_noexcept (dirfd, cstr, flags, 0644 )};
422506 if (fd != -1 ) [[likely]]
@@ -716,7 +800,7 @@ struct fd_remapper
716800};
717801
718802// only used in vfork_execveat_common_impl()
719- 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
803+ 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)
720804{
721805 // vfork can only be called through libc wrapper
722806 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
741825 }
742826#endif
743827
744- bool const follow{(mode & process_mode::follow) == process_mode::follow};
745-
746- #if defined(__linux__)
747-
748- #if defined(__NR_execveat)
749- int flags{};
750- if (!follow)
751- {
752- flags |= AT_SYMLINK_NOFOLLOW;
753- }
754-
755- auto ret{system_call<__NR_execveat, int >(dirfd, cstr, args, envp, flags)};
756- if (::fast_io::linux_system_call_fails (ret))
757- {
758- t_errno = -ret;
759- }
760- else
761- {
762- t_errno = 0 ;
763- }
764- #else
765- int flags{};
766- if (!follow)
767- {
768- flags |= AT_SYMLINK_NOFOLLOW;
769- }
770-
771- auto ret{::fast_io::posix::libc_execveat (dirfd, cstr, const_cast <char *const *>(args), const_cast <char *const *>(envp), flags)};
772- if (ret == -1 )
773- {
774- t_errno = errno;
775- }
776- else
777- {
778- t_errno = 0 ;
779- }
780- #endif
781-
782- #if defined(__NR_exit_group)
783- ::fast_io::system_call_no_return<__NR_exit_group>(127 );
784- #else
785- ::fast_io::system_call_no_return<__NR_exit>(127 );
786- #endif
787- #else
788- int flags{};
789- if (!follow)
790- {
791- flags |= AT_SYMLINK_NOFOLLOW;
792- }
793-
794- auto ret{::fast_io::posix::libc_execveat (dirfd, cstr, const_cast <char *const *>(args), const_cast <char *const *>(envp), flags)};
795- if (ret == -1 )
796- {
797- t_errno = errno;
798- }
799- else
800- {
801- t_errno = 0 ;
802- }
803-
828+ t_errno = posix_execveat (dirfd, cstr, args, envp, mode);
804829 ::fast_io::posix::libc_exit2 (127 );
805- #endif
806830
807831 __builtin_unreachable ();
808832}
0 commit comments