Skip to content

Commit

Permalink
Conditionalize use of POSIX features missing on WASI/WebAssembly.
Browse files Browse the repository at this point in the history
This patch is the first in a series that makes it possible to build
LLVM, Clang, and LLD for WASI/WebAssembly. This patch does not introduce
conditionals of the form `defined(__wasi__)` or `defined(__wasm__)`;
instead it detects these APIs like any other platform features. While
some features are never present in WASI and the feature check is
functionally identical to a platform check, some may be conditionally
present if emulation is turned on, e.g. `getpid`.

The complete list of feature tests introduced is:
* `HAVE_ALARM`: WebAssembly does not support delivery of asynchronous
  signals.
* `HAVE_GETHOSTNAME`: WASI does not provide `gethostname`.
* `HAVE_GETPID`: WASI does not have process IDs. However, it ships
  with a polyfill (`-D_WASI_EMULATED_GETPID`). The final behavior is
  the same either way.
* `HAVE_FCHOWN`: WASI likely will never support UNIX ownership model.
* `HAVE_FLOCK`: WASI likely will never support POSIX advisory locking.
* `HAVE_PWD_H`: WASI likely will never support UNIX password databases.
* `HAVE_RAISE`: WASI does not support delivery of asynchronous signals.
  However, it ships with a polyfill (`-D_WASI_EMULATED_SIGNAL`).
  This polyfill implements `raise` but not `sigaction` (only `signal`)
  and as a result it is not currently useful for LLVM. If `sigaction`
  is implemented in wasi-libc then `raise(SIGABRT);` would be able to
  invoke the handler.
* `HAVE_SETJMP`: WebAssembly implements SjLj using exception handling.
  Exception handling has not been stabilized in Wasm yet. In addition,
  it significantly increases deployment complexity. Building with
  `-mllvm -wasm-enable-sjlj` enables the use of SjLj.
* `HAVE_SOCKET`: WASIp1 does not provide the Berkeley socket API.
  WASIp2 does provide it. It will likely remain desirable to target
  WASIp1 for a long time.
* `HAVE_SYS_WAIT_H`: WASI does not have subprocess management.
* `HAVE_UMASK`: WASI likely will never support UNIX permission model,
  but `umask` might eventually be added in a polyfill.

Of these, only `HAVE_FLOCK` requires a custom compile test:

    #include <sys/file.h>
    struct flock lk;
    int main(void) { return 0; }
  • Loading branch information
whitequark committed May 19, 2024
1 parent 577785c commit 0a86da8
Show file tree
Hide file tree
Showing 11 changed files with 116 additions and 7 deletions.
6 changes: 3 additions & 3 deletions clang/lib/Driver/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,8 @@
#include <optional>
#include <set>
#include <utility>
#if LLVM_ON_UNIX
#include <unistd.h> // getpid
#if HAVE_GETPID
#include <unistd.h>
#endif

using namespace clang::driver;
Expand Down Expand Up @@ -1577,7 +1577,7 @@ bool Driver::getCrashDiagnosticFile(StringRef ReproCrashFilename,
CrashDiagDir = "/";
path::append(CrashDiagDir, "Library/Logs/DiagnosticReports");
int PID =
#if LLVM_ON_UNIX
#if HAVE_GETPID
getpid();
#else
0;
Expand Down
16 changes: 16 additions & 0 deletions llvm/cmake/config-ix.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ check_include_file(malloc/malloc.h HAVE_MALLOC_MALLOC_H)
if( NOT PURE_WINDOWS )
check_include_file(pthread.h HAVE_PTHREAD_H)
endif()
check_include_file(pwd.h HAVE_PWD_H)
check_include_file(signal.h HAVE_SIGNAL_H)
check_include_file(sys/ioctl.h HAVE_SYS_IOCTL_H)
check_include_file(sys/mman.h HAVE_SYS_MMAN_H)
Expand All @@ -57,6 +58,7 @@ check_include_file(sys/resource.h HAVE_SYS_RESOURCE_H)
check_include_file(sys/stat.h HAVE_SYS_STAT_H)
check_include_file(sys/time.h HAVE_SYS_TIME_H)
check_include_file(sys/types.h HAVE_SYS_TYPES_H)
check_include_file(sys/wait.h HAVE_SYS_WAIT_H)
check_include_file(sysexits.h HAVE_SYSEXITS_H)
check_include_file(termios.h HAVE_TERMIOS_H)
check_include_file(unistd.h HAVE_UNISTD_H)
Expand Down Expand Up @@ -276,11 +278,15 @@ check_symbol_exists(__deregister_frame "${CMAKE_CURRENT_LIST_DIR}/unwind.h" HAVE
check_symbol_exists(__unw_add_dynamic_fde "${CMAKE_CURRENT_LIST_DIR}/unwind.h" HAVE_UNW_ADD_DYNAMIC_FDE)

check_symbol_exists(_Unwind_Backtrace "unwind.h" HAVE__UNWIND_BACKTRACE)
check_symbol_exists(alarm unistd.h HAVE_ALARM)
check_symbol_exists(gethostname unistd.h HAVE_GETHOSTNAME)
check_symbol_exists(getpagesize unistd.h HAVE_GETPAGESIZE)
check_symbol_exists(getpid unistd.h HAVE_GETPID)
check_symbol_exists(sysconf unistd.h HAVE_SYSCONF)
check_symbol_exists(getrusage sys/resource.h HAVE_GETRUSAGE)
check_symbol_exists(setrlimit sys/resource.h HAVE_SETRLIMIT)
check_symbol_exists(isatty unistd.h HAVE_ISATTY)
check_symbol_exists(fchown unistd.h HAVE_FCHOWN)
check_symbol_exists(futimens sys/stat.h HAVE_FUTIMENS)
check_symbol_exists(futimes sys/time.h HAVE_FUTIMES)
# AddressSanitizer conflicts with lib/Support/Unix/Signals.inc
Expand All @@ -298,10 +304,14 @@ check_symbol_exists(malloc_zone_statistics malloc/malloc.h
check_symbol_exists(getrlimit "sys/types.h;sys/time.h;sys/resource.h" HAVE_GETRLIMIT)
check_symbol_exists(posix_spawn spawn.h HAVE_POSIX_SPAWN)
check_symbol_exists(pread unistd.h HAVE_PREAD)
check_symbol_exists(raise signal.h HAVE_RAISE)
check_symbol_exists(sbrk unistd.h HAVE_SBRK)
check_symbol_exists(setjmp setjmp.h HAVE_SETJMP)
check_symbol_exists(socket sys/socket.h HAVE_SOCKET)
check_symbol_exists(strerror_r string.h HAVE_STRERROR_R)
check_symbol_exists(strerror_s string.h HAVE_DECL_STRERROR_S)
check_symbol_exists(setenv stdlib.h HAVE_SETENV)
check_symbol_exists(umask sys/stat.h HAVE_UMASK)
if( PURE_WINDOWS )
check_symbol_exists(_chsize_s io.h HAVE__CHSIZE_S)

Expand All @@ -327,6 +337,12 @@ if( PURE_WINDOWS )
check_function_exists(__cmpdi2 HAVE___CMPDI2)
endif()

check_c_source_compiles("
#include <sys/file.h>
struct flock lk;
int main(void) { return 0; }"
HAVE_FLOCK)

CHECK_STRUCT_HAS_MEMBER("struct stat" st_mtimespec.tv_nsec
"sys/types.h;sys/stat.h" HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC)
if (UNIX AND ${CMAKE_SYSTEM_NAME} MATCHES "AIX")
Expand Down
33 changes: 33 additions & 0 deletions llvm/include/llvm/Config/config.h.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
backslashes. */
#cmakedefine01 LLVM_WINDOWS_PREFER_FORWARD_SLASH

/* Define to 1 if you have the `alarm' function. */
#cmakedefine HAVE_ALARM ${HAVE_ALARM}

/* Define to 1 if you have the `backtrace' function. */
#cmakedefine HAVE_BACKTRACE ${HAVE_BACKTRACE}

Expand Down Expand Up @@ -71,6 +74,12 @@
/* Define to 1 if you have the <errno.h> header file. */
#cmakedefine HAVE_ERRNO_H ${HAVE_ERRNO_H}

/* Define to 1 if you have the `flock' structure. */
#cmakedefine HAVE_FLOCK ${HAVE_FLOCK}

/* Define to 1 if you have the `fchown' function. */
#cmakedefine HAVE_FCHOWN ${HAVE_FCHOWN}

/* Define to 1 if you have the <fcntl.h> header file. */
#cmakedefine HAVE_FCNTL_H ${HAVE_FCNTL_H}

Expand All @@ -92,9 +101,15 @@
/* Define to 1 if you have the `futimes' function. */
#cmakedefine HAVE_FUTIMES ${HAVE_FUTIMES}

/* Define to 1 if you have the `gethostname' function. */
#cmakedefine HAVE_GETHOSTNAME ${HAVE_GETHOSTNAME}

/* Define to 1 if you have the `getpagesize' function. */
#cmakedefine HAVE_GETPAGESIZE ${HAVE_GETPAGESIZE}

/* Define to 1 if you have the `getpid' function. */
#cmakedefine HAVE_GETPID ${HAVE_GETPID}

/* Define to 1 if you have the `getrlimit' function. */
#cmakedefine HAVE_GETRLIMIT ${HAVE_GETRLIMIT}

Expand Down Expand Up @@ -161,9 +176,21 @@
/* Have pthread_rwlock_init */
#cmakedefine HAVE_PTHREAD_RWLOCK_INIT ${HAVE_PTHREAD_RWLOCK_INIT}

/* Define to 1 if you have the <pwd.h> header file. */
#cmakedefine HAVE_PWD_H ${HAVE_PWD_H}

/* Define to 1 if you have the `raise' function. */
#cmakedefine HAVE_RAISE ${HAVE_RAISE}

/* Define to 1 if you have the `sbrk' function. */
#cmakedefine HAVE_SBRK ${HAVE_SBRK}

/* Define to 1 if you have the `setjmp' function. */
#cmakedefine HAVE_SETJMP ${HAVE_SETJMP}

/* Define to 1 if you have the `socket' function. */
#cmakedefine HAVE_SOCKET ${HAVE_SOCKET}

/* Define to 1 if you have the `setenv' function. */
#cmakedefine HAVE_SETENV ${HAVE_SETENV}

Expand Down Expand Up @@ -209,12 +236,18 @@
/* Define to 1 if you have the <sys/types.h> header file. */
#cmakedefine HAVE_SYS_TYPES_H ${HAVE_SYS_TYPES_H}

/* Define to 1 if you have the <sys/wait.h> header file. */
#cmakedefine HAVE_SYS_WAIT_H ${HAVE_SYS_WAIT_H}

/* Define if the setupterm() function is supported this platform. */
#cmakedefine LLVM_ENABLE_TERMINFO ${LLVM_ENABLE_TERMINFO}

/* Define to 1 if you have the <termios.h> header file. */
#cmakedefine HAVE_TERMIOS_H ${HAVE_TERMIOS_H}

/* Define to 1 if you have the `umask' function. */
#cmakedefine HAVE_UMASK ${HAVE_UMASK}

/* Define to 1 if you have the <unistd.h> header file. */
#cmakedefine HAVE_UNISTD_H ${HAVE_UNISTD_H}

Expand Down
6 changes: 6 additions & 0 deletions llvm/lib/ExecutionEngine/Interpreter/ExternalFunctions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@
#include "llvm/Support/raw_ostream.h"
#include <cassert>
#include <cmath>
#if HAVE_RAISE
#include <csignal>
#endif
#include <cstdint>
#include <cstdio>
#include <cstring>
Expand Down Expand Up @@ -340,7 +342,11 @@ static GenericValue lle_X_exit(FunctionType *FT, ArrayRef<GenericValue> Args) {
static GenericValue lle_X_abort(FunctionType *FT, ArrayRef<GenericValue> Args) {
//FIXME: should we report or raise here?
//report_fatal_error("Interpreted program raised SIGABRT");
#if HAVE_RAISE
raise (SIGABRT);
#else
abort();
#endif
return GenericValue();
}

Expand Down
15 changes: 14 additions & 1 deletion llvm/lib/Support/CrashRecoveryContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,13 @@
#include "llvm/Support/Signals.h"
#include "llvm/Support/thread.h"
#include <cassert>
#if HAVE_RAISE
#include <csignal>
#endif
#include <mutex>
#if HAVE_SETJMP
#include <setjmp.h>
#endif

using namespace llvm;

Expand All @@ -31,7 +36,9 @@ struct CrashRecoveryContextImpl {
const CrashRecoveryContextImpl *Next;

CrashRecoveryContext *CRC;
#ifdef HAVE_SETJMP
::jmp_buf JumpBuffer;
#endif
volatile unsigned Failed : 1;
unsigned SwitchedThread : 1;
unsigned ValidJumpBuffer : 1;
Expand Down Expand Up @@ -72,9 +79,11 @@ struct CrashRecoveryContextImpl {

CRC->RetCode = RetCode;

#if HAVE_SETJMP
// Jump back to the RunSafely we were called under.
if (ValidJumpBuffer)
longjmp(JumpBuffer, 1);
#endif

// Otherwise let the caller decide of the outcome of the crash. Currently
// this occurs when using SEH on Windows with MSVC or clang-cl.
Expand Down Expand Up @@ -417,10 +426,12 @@ bool CrashRecoveryContext::RunSafely(function_ref<void()> Fn) {
CrashRecoveryContextImpl *CRCI = new CrashRecoveryContextImpl(this);
Impl = CRCI;

#if HAVE_SETJMP
CRCI->ValidJumpBuffer = true;
if (setjmp(CRCI->JumpBuffer) != 0) {
return false;
}
#endif
}

Fn();
Expand Down Expand Up @@ -469,9 +480,11 @@ bool CrashRecoveryContext::throwIfCrash(int RetCode) {
return false;
#if defined(_WIN32)
::RaiseException(RetCode, 0, 0, NULL);
#else
#elif HAVE_RAISE
llvm::sys::unregisterHandlers();
raise(RetCode - 128);
#else
abort();
#endif
return true;
}
Expand Down
2 changes: 1 addition & 1 deletion llvm/lib/Support/LockFileManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ static std::error_code getHostID(SmallVectorImpl<char> &HostID) {
StringRef UUIDRef(UUIDStr);
HostID.append(UUIDRef.begin(), UUIDRef.end());

#elif LLVM_ON_UNIX
#elif HAVE_GETHOSTNAME
char HostName[256];
HostName[255] = 0;
HostName[0] = 0;
Expand Down
30 changes: 30 additions & 0 deletions llvm/lib/Support/Unix/Path.inc
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@
#endif

#include <dirent.h>
#ifdef HAVE_PWD_H
#include <pwd.h>
#endif
#include <sys/file.h>

#ifdef __APPLE__
Expand Down Expand Up @@ -673,6 +675,11 @@ static void expandTildeExpr(SmallVectorImpl<char> &Path) {
return;
}

#if !defined(HAVE_PWD_H)
// No access to password database, return back the original path.
(void)Remainder;
return;
#else
// This is a string of the form ~username/, look up this user's entry in the
// password database.
std::unique_ptr<char[]> Buf;
Expand All @@ -694,6 +701,7 @@ static void expandTildeExpr(SmallVectorImpl<char> &Path) {
Path.clear();
Path.append(Entry->pw_dir, Entry->pw_dir + strlen(Entry->pw_dir));
llvm::sys::path::append(Path, Storage);
#endif
}

void expand_tilde(const Twine &path, SmallVectorImpl<char> &dest) {
Expand Down Expand Up @@ -770,11 +778,15 @@ std::error_code status(int FD, file_status &Result) {
}

unsigned getUmask() {
#if HAVE_UMASK
// Chose arbitary new mask and reset the umask to the old mask.
// umask(2) never fails so ignore the return of the second call.
unsigned Mask = ::umask(0);
(void)::umask(Mask);
return Mask;
#else
return 0022;
#endif
}

std::error_code setPermissions(const Twine &Path, perms Permissions) {
Expand Down Expand Up @@ -1224,6 +1236,7 @@ Expected<size_t> readNativeFileSlice(file_t FD, MutableArrayRef<char> Buf,
}

std::error_code tryLockFile(int FD, std::chrono::milliseconds Timeout) {
#if HAVE_FLOCK
auto Start = std::chrono::steady_clock::now();
auto End = Start + Timeout;
do {
Expand All @@ -1241,9 +1254,13 @@ std::error_code tryLockFile(int FD, std::chrono::milliseconds Timeout) {
usleep(1000);
} while (std::chrono::steady_clock::now() < End);
return make_error_code(errc::no_lock_available);
#else
return std::error_code(ENOSYS, std::generic_category());
#endif
}

std::error_code lockFile(int FD) {
#if HAVE_FLOCK
struct flock Lock;
memset(&Lock, 0, sizeof(Lock));
Lock.l_type = F_WRLCK;
Expand All @@ -1253,9 +1270,13 @@ std::error_code lockFile(int FD) {
if (::fcntl(FD, F_SETLKW, &Lock) != -1)
return std::error_code();
return errnoAsErrorCode();
#else
return std::error_code(ENOSYS, std::generic_category());
#endif
}

std::error_code unlockFile(int FD) {
#if HAVE_FLOCK
struct flock Lock;
Lock.l_type = F_UNLCK;
Lock.l_whence = SEEK_SET;
Expand All @@ -1264,6 +1285,9 @@ std::error_code unlockFile(int FD) {
if (::fcntl(FD, F_SETLK, &Lock) != -1)
return std::error_code();
return errnoAsErrorCode();
#else
return std::error_code(ENOSYS, std::generic_category());
#endif
}

std::error_code closeFile(file_t &F) {
Expand Down Expand Up @@ -1335,11 +1359,15 @@ std::error_code real_path(const Twine &path, SmallVectorImpl<char> &dest,
}

std::error_code changeFileOwnership(int FD, uint32_t Owner, uint32_t Group) {
#if HAVE_FCHOWN
auto FChown = [&]() { return ::fchown(FD, Owner, Group); };
// Retry if fchown call fails due to interruption.
if ((sys::RetryAfterSignal(-1, FChown)) < 0)
return errnoAsErrorCode();
return std::error_code();
#else
return std::error_code(ENOSYS, std::generic_category());
#endif
}

} // end namespace fs
Expand All @@ -1349,6 +1377,7 @@ namespace path {
bool home_directory(SmallVectorImpl<char> &result) {
std::unique_ptr<char[]> Buf;
char *RequestedDir = getenv("HOME");
#if HAVE_PWD_H
if (!RequestedDir) {
long BufSize = sysconf(_SC_GETPW_R_SIZE_MAX);
if (BufSize <= 0)
Expand All @@ -1360,6 +1389,7 @@ bool home_directory(SmallVectorImpl<char> &result) {
if (pw && pw->pw_dir)
RequestedDir = pw->pw_dir;
}
#endif
if (!RequestedDir)
return false;

Expand Down
4 changes: 4 additions & 0 deletions llvm/lib/Support/Unix/Process.inc
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,11 @@ getRUsageTimes() {
Process::Pid Process::getProcessId() {
static_assert(sizeof(Pid) >= sizeof(pid_t),
"Process::Pid should be big enough to store pid_t");
#if HAVE_GETPID
return Pid(::getpid());
#else
return Pid(0);
#endif
}

// On Cygwin, getpagesize() returns 64k(AllocationGranularity) and
Expand Down
3 changes: 3 additions & 0 deletions llvm/lib/Support/Unix/Unix.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@
#include <cstring>
#include <string>
#include <sys/types.h>

#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif

#ifdef HAVE_UNISTD_H
#include <unistd.h>
Expand Down
Loading

0 comments on commit 0a86da8

Please sign in to comment.