Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 13 additions & 6 deletions eng/native/configurecompiler.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,12 @@ elseif(CLR_CMAKE_HOST_OPENBSD)
# The PAL's hand-written asm lacks endbr64 landing pads, so disable branch-target CFI.
add_linker_flag("-Wl,-z,nobtcfi")
elseif(CLR_CMAKE_HOST_SUNOS)
add_compile_options($<$<COMPILE_LANGUAGE:ASM>:-Wa,--noexecstack>)
# llvm's assembler crashes with noexecstack with solaris triplet;
# bug report with minimal repro at: https://github.com/llvm/llvm-project/issues/202953
if (CMAKE_C_COMPILER_ID MATCHES "GNU")
add_compile_options($<$<COMPILE_LANGUAGE:ASM>:-Wa,--noexecstack>)
endif()

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fstack-protector")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fstack-protector")
add_definitions(-D__EXTENSIONS__ -D_XPG4_2 -D_POSIX_PTHREAD_SEMANTICS -D_REENTRANT)
Expand Down Expand Up @@ -382,15 +387,17 @@ elseif(CLR_CMAKE_HOST_HAIKU)
set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED TRUE)
set(CMAKE_LINK_LIBRARY_WHOLE_ARCHIVE_ATTRIBUTES LIBRARY_TYPE=STATIC DEDUPLICATION=YES OVERRIDE=DEFAULT)
endif()
# Haiku uses the GNU toolchain, which supports RESCAN, but only CMake 4.4.0+ recognizes this.
if(CMAKE_VERSION VERSION_LESS 4.4)
set(CMAKE_LINK_GROUP_USING_RESCAN "LINKER:--start-group" "LINKER:--end-group")
set(CMAKE_LINK_GROUP_USING_RESCAN_SUPPORTED TRUE)
endif()

add_compile_options($<$<COMPILE_LANGUAGE:ASM>:-Wa,--noexecstack>)
add_linker_flag("-Wl,--build-id=sha1")
endif()

if((CLR_CMAKE_HOST_HAIKU OR CLR_CMAKE_HOST_SUNOS) AND CMAKE_VERSION VERSION_LESS 4.4)
# Haiku and illumos uses the GNU toolchain, which supports RESCAN, but only CMake 4.4.0+ recognizes this.
set(CMAKE_LINK_GROUP_USING_RESCAN "LINKER:--start-group" "LINKER:--end-group")
set(CMAKE_LINK_GROUP_USING_RESCAN_SUPPORTED TRUE)
endif()

#------------------------------------
# Definitions (for platform)
#-----------------------------------
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/gc/unix/gcenv.unix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -568,7 +568,7 @@ bool GCToOSInterface::VirtualReset(void * address, size_t size, bool unlock)
st = madvise(address, size, MADV_DONTDUMP);
#endif

#ifdef MADV_FREE
#if defined(MADV_FREE) && !defined(TARGET_SUNOS)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this needed? It is strange that it would have MADV_FREE defined and it would fail in the code below.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mono has HAVE_MADVISE, coreclr doesn't. The error was about madvise not being defined:

 [117/162] Building CXX object nativeaot/Runtime/Full/CMakeFiles/Runtime.ServerGC.dir/__/__/__/gc/unix/gcenv.unix.cpp.o
  FAILED: nativeaot/Runtime/Full/CMakeFiles/Runtime.ServerGC.dir/__/__/__/gc/unix/gcenv.unix.cpp.o
  /crossrootfs/x64/bin/x86_64-illumos-g++ --sysroot=/crossrootfs/x64 -DDISABLE_CONTRACTS -DFEATURE_BASICFREEZE -DFEATURE_CONSERVATIVE_GC -DFEATURE_EVENT_TRACE -DFEATURE_HIJACK -DFEATURE_MANUALLY_MANAGED_CARD_BUNDLES -DFEATURE_NATIVEAOT -DFEATURE_PERFTRACING -DFEATURE_READONLY_GS_COOKIE -DFEATURE_RX_THUNKS -DFEATURE_SVR_GC -DFEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP -DGC_DESCRIPTOR -DHOST_64BIT -DHOST_AMD64 -DHOST_UNIX -DNATIVEAOT -DNDEBUG -DTARGET_64BIT -DTARGET_AMD64 -DTARGET_ILLUMOS -DTARGET_SUNOS -DTARGET_UNIX -DUNIX_AMD64_ABI -DURTBLDENV_FRIENDLY=Retail -DVERIFY_HEAP -D_FILE_OFFSET_BITS=64 -D_LIB -D_LIBUNWIND_DISABLE_ZERO_COST_APIS=1 -D_LIBUNWIND_IS_NATIVE_ONLY -D_POSIX_PTHREAD_SEMANTICS -D_REENTRANT -D_TIME_BITS=64 -D_XPG4_2 -D__EXTENSIONS__ -I/runtime/artifacts/obj/coreclr/illumos.x64.Release/nativeaot/Runtime/Full -I/runtime/src/coreclr/nativeaot/Runtime/Full -I/runtime/src/native/external/llvm-libunwind/include -I/runtime/src/native -I/runtime/src/native/inc -I/runtime/src/coreclr/pal/prebuilt/inc -I/runtime/artifacts/obj -I/runtime/src/coreclr/nativeaot/Runtime/inc -I/runtime/src/coreclr/nativeaot/Runtime/. -I/runtime/src/coreclr/nativeaot/Runtime/../../gc -I/runtime/src/coreclr/nativeaot/Runtime/../../gc/env -I/runtime/artifacts/obj/coreclr/illumos.x64.Release/nativeaot/Runtime/eventpipe/inc -I/runtime/src/coreclr/runtime -I/runtime/src/coreclr/nativeaot/Runtime/unix -I/runtime/src/coreclr/nativeaot/Runtime/../../pal/inc/rt -I/runtime/src/coreclr/nativeaot/Runtime/amd64 -I/runtime/artifacts/obj/coreclr/illumos.x64.Release/nativeaot/Runtime -I/runtime/src/coreclr/debug/datadescriptor-shared/inc -isystem /crossrootfs/x64/include -fstack-protector -O3 -DNDEBUG -std=gnu++17 -fPIC -O3 -Wall -g -fno-omit-frame-pointer -fno-strict-overflow -fno-strict-aliasing -fstack-protector-strong -Werror -Wno-unused-variable -Wno-unused-value -Wno-unused-function -Wno-tautological-compare -Wno-unknown-pragmas -Wimplicit-fallthrough -Wvla -Wno-invalid-offsetof -Wno-unused-but-set-variable -ffp-contract=off -fno-rtti -Wno-uninitialized -Wno-strict-aliasing -Wno-array-bounds -Wno-misleading-indentation -Wno-stringop-overflow -Wno-restrict -Wno-stringop-truncation -Wno-class-memaccess -faligned-new -fsigned-char -fvisibility=hidden -ffunction-sections -fdata-sections -fno-exceptions -fno-asynchronous-unwind-tables -nostdlib -MD -MT nativeaot/Runtime/Full/CMakeFiles/Runtime.ServerGC.dir/__/__/__/gc/unix/gcenv.unix.cpp.o -MF nativeaot/Runtime/Full/CMakeFiles/Runtime.ServerGC.dir/__/__/__/gc/unix/gcenv.unix.cpp.o.d -o nativeaot/Runtime/Full/CMakeFiles/Runtime.ServerGC.dir/__/__/__/gc/unix/gcenv.unix.cpp.o -c /runtime/src/coreclr/gc/unix/gcenv.unix.cpp
  /runtime/src/coreclr/gc/unix/gcenv.unix.cpp: In static member function 'static bool GCToOSInterface::VirtualReset(void*, size_t, bool)':
  /runtime/src/coreclr/gc/unix/gcenv.unix.cpp:574:10: error: 'madvise' was not declared in this scope; did you mean 'raise'?
    574 |     st = madvise(address, size, MADV_FREE);
        |          ^~~~~~~
        |          raise
  [118/162] Building C object /runtime/artifacts/obj/external/libunwind/CMakeFiles/libunwind.dir/runtime/src/native/external/libunwind/src/dwarf/Lparser.c.o
  [119/162] Building C object /runtime/artifacts/obj/external/libunwind/CMakeFiles/libunwind.dir/runtime/src/native/external/libunwind/src/dwarf/Gexpr.c.o
  [120/162] Building C object /runtime/artifacts/obj/external/libunwind/CMakeFiles/libunwind.dir/runtime/src/native/external/libunwind/src/dwarf/Gparser.c.o
  [121/162] Building CXX object Corehost.Static/hostmisc/CMakeFiles/hostmisc.dir/utils.cpp.o
  [122/162] Building CXX object Corehost.Static/hostmisc/CMakeFiles/hostmisc_public.dir/utils.cpp.o
  ninja: build stopped: subcommand failed.
  /runtime/src/coreclr
  Failed to build "CoreCLR component".
/runtime/src/coreclr/runtime.proj(120,5): error MSB3073: The command ""/runtime/src/coreclr/build-runtime.sh" -x64 -release gcc -cross -os illumos -ninja -targetrid illumos-x64 -cmakeargs "-DCLR_DOTNET_RID=illumos-x64" -cmakeargs "-DCLR_DOTNET_HOST_PATH='/runtime/.dotnet/dotnet'" -cmakeargs "-DCDAC_BUILD_TOOL_BINARY_PATH=/runtime/artifacts/bin/coreclr/illumos.x64.Release/cdac-build-tool/cdac-build-tool.dll" -cmakeargs "-DFEATURE_DYNAMIC_CODE_COMPILED=1" " exited with code 1.

Build FAILED.

/runtime/src/coreclr/runtime.proj(120,5): error MSB3073: The command ""/runtime/src/coreclr/build-runtime.sh" -x64 -release gcc -cross -os illumos -ninja -targetrid illumos-x64 -cmakeargs "-DCLR_DOTNET_RID=illumos-x64" -cmakeargs "-DCLR_DOTNET_HOST_PATH='/runtime/.dotnet/dotnet'" -cmakeargs "-DCDAC_BUILD_TOOL_BINARY_PATH=/runtime/artifacts/bin/coreclr/illumos.x64.Release/cdac-build-tool/cdac-build-tool.dll" -cmakeargs "-DFEATURE_DYNAMIC_CODE_COMPILED=1" " exited with code 1.
    0 Warning(s)
    1 Error(s)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting, so sunos has MADV_FREE defined and yet no madvise, that's quite weird.

@gwr gwr Jun 12, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's in <sys/mman.h>

#if !defined(_STRICT_POSIX)
extern int mincore(caddr_t, size_t, char *);
extern int memcntl(void *, size_t, int, void *, int, int);
extern int madvise(void *, size_t, int);
[...]

Does the build supply -D_STRICT_POSIX or something?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's consistent once you see it's a namespace thing: madvise is a non-standard SVR4/BSD extension, posix_madvise is the standardized one, and we build with -D_XPG4_2 (strict X/Open). From the rootfs's sys/mman.h:

#if (_POSIX_C_SOURCE > 2) || defined(_XPG4_2)
extern void *mmap(void *, size_t, int, int, int, off_t);
...
#else   /* (_POSIX_C_SOURCE > 2) || defined(_XPG4_2) */
...
extern int madvise(caddr_t, size_t, int);
...
#endif

#if !defined(__XOPEN_OR_POSIX) || defined(_XPG6) || defined(__EXTENSIONS__)
extern int posix_madvise(void *, size_t, int);
#endif

#if (_POSIX_C_SOURCE <= 2) && !defined(_XPG4_2) || defined(__EXTENSIONS__)
...
#define MADV_FREE               5       /* contents can be freed */
...
#endif

So on illumos the posix-y path is the preferred one, and posix_madvise(POSIX_MADV_DONTNEED) is what they consider "modern".

// Tell the kernel that the application doesn't need the pages in the range.
// Freeing the pages can be delayed until a memory pressure occurs.
st = madvise(address, size, MADV_FREE);
Expand Down
6 changes: 3 additions & 3 deletions src/coreclr/jit/gentree.h
Original file line number Diff line number Diff line change
Expand Up @@ -4534,7 +4534,7 @@ struct AsyncCallInfo
// Behavior where we continue for each call depends on how it was
// configured and whether it is a task await or custom await. This field
// records that behavior.
ContinuationContextHandling ContinuationContextHandling = ContinuationContextHandling::None;
::ContinuationContextHandling ContinuationContextHandling = ContinuationContextHandling::None;

// Tail awaits do not generate suspension points and the JIT instead
// directly returns the callee's continuation to the caller.
Expand Down Expand Up @@ -8096,8 +8096,8 @@ struct GenTreeIndir : public GenTreeOp
unsigned Scale();
ssize_t Offset();

unsigned Size() const;
ValueSize ValueSize() const;
unsigned Size() const;
::ValueSize ValueSize() const;

GenTreeIndir(genTreeOps oper, var_types type, GenTree* addr, GenTree* data)
: GenTreeOp(oper, type, addr, data)
Expand Down
8 changes: 4 additions & 4 deletions src/native/libs/System.Native/pal_io.c
Original file line number Diff line number Diff line change
Expand Up @@ -1865,8 +1865,8 @@ int32_t SystemNative_ReadThreadInfo(int32_t pid, int32_t tid, ThreadInfo* thread

lwpsinfo_t pr;
int result = Common_Read(fd, &pr, sizeof(pr));
close(fd);
if (result < sizeof (pr))
close(ToFileDescriptor(fd));
if (result < (int)sizeof(pr))
{
errno = EIO;
return -1;
Expand Down Expand Up @@ -1913,8 +1913,8 @@ int32_t SystemNative_ReadProcessInfo(int32_t pid, ProcessInfo* processInfo, uint

psinfo_t pr;
int result = Common_Read(fd, &pr, sizeof(pr));
close(fd);
if (result < sizeof (pr))
close(ToFileDescriptor(fd));
if (result < (int)sizeof(pr))
{
errno = EIO;
return -1;
Expand Down
12 changes: 10 additions & 2 deletions src/native/libs/System.Native/pal_process.c
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ handler_from_sigaction (struct sigaction *sa)
}
else
{
return sa->sa_handler;
return (VoidIntFn)sa->sa_handler;
}
}

Expand Down Expand Up @@ -838,7 +838,10 @@ static int32_t ForkAndExecProcessInternal(
struct sigaction sa_default;
struct sigaction sa_old;
memset(&sa_default, 0, sizeof(sa_default)); // On some architectures, sa_mask is a struct so assigning zero to it doesn't compile
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wstrict-prototypes"
sa_default.sa_handler = SIG_DFL;

@am11 am11 Jun 12, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are raised from system headers; I couldn't find a way to make clang happy, so I went with the suppression (and gcc surprisingly ignores it so we don't need additional #ifdef __clang type of condition).

#pragma clang diagnostic pop
for (int sig = 1; sig < NSIG; ++sig)
{
if (sig == SIGKILL || sig == SIGSTOP)
Expand All @@ -848,7 +851,12 @@ static int32_t ForkAndExecProcessInternal(
if (!sigaction(sig, NULL, &sa_old))
{
void (*oldhandler)(int) = handler_from_sigaction (&sa_old);
if (oldhandler != SIG_IGN && oldhandler != SIG_DFL)
bool hasCustomHandler;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wstrict-prototypes"
hasCustomHandler = oldhandler != SIG_IGN && oldhandler != SIG_DFL;
Comment thread
janvorli marked this conversation as resolved.
#pragma clang diagnostic pop
if (hasCustomHandler)
{
// It has a custom handler, put the default handler back.
// We check first to preserve flags on default handlers.
Expand Down
20 changes: 15 additions & 5 deletions src/native/libs/System.Native/pal_signal.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,18 +69,28 @@ static bool IsSaSigInfo(struct sigaction* action)
static bool IsSigDfl(struct sigaction* action)
{
assert(action);
bool isDefault;
// macOS can return sigaction with SIG_DFL and SA_SIGINFO.
// SA_SIGINFO means we should use sa_sigaction, but here we want to check sa_handler.
// So we ignore SA_SIGINFO when sa_sigaction and sa_handler are at the same address.
return (&action->sa_handler == (void*)&action->sa_sigaction || !IsSaSigInfo(action)) &&
action->sa_handler == SIG_DFL;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wstrict-prototypes"
isDefault = (&action->sa_handler == (void*)&action->sa_sigaction || !IsSaSigInfo(action)) &&
action->sa_handler == SIG_DFL;
#pragma clang diagnostic pop
return isDefault;
}

static bool IsSigIgn(struct sigaction* action)
{
assert(action);
return (&action->sa_handler == (void*)&action->sa_sigaction || !IsSaSigInfo(action)) &&
action->sa_handler == SIG_IGN;
bool isIgnored;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wstrict-prototypes"
isIgnored = (&action->sa_handler == (void*)&action->sa_sigaction || !IsSaSigInfo(action)) &&
action->sa_handler == SIG_IGN;
#pragma clang diagnostic pop
return isIgnored;
}

bool TryConvertSignalCodeToPosixSignal(int signalCode, PosixSignal* posixSignal)
Expand Down Expand Up @@ -239,7 +249,7 @@ static void SignalHandler(int sig, siginfo_t* siginfo, void* context)
else
{
assert(origHandler->sa_handler);
origHandler->sa_handler(sig);
((void (*)(int))origHandler->sa_handler)(sig);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the sa_handler type on illumos?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

C and C++ have different types:

struct sigaction {
	int sa_flags;
	union {
#ifdef	__cplusplus
		void (*_handler)(int);
#else
		void (*_handler)();
#endif
#if defined(__EXTENSIONS__) || defined(_KERNEL) || \
	(!defined(_STRICT_STDC) && !defined(__XOPEN_OR_POSIX)) || \
	(_POSIX_C_SOURCE > 2) || defined(_XPG4_2)
		void (*_sigaction)(int, siginfo_t *, void *);
#endif
	}	_funcptr;
	sigset_t sa_mask;
#ifndef _LP64
	int sa_resv[2];
#endif
};
#define	sa_handler	_funcptr._handler
#define	sa_sigaction	_funcptr._sigaction

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Our rootfs has an older version of kernel. There is no new update from https://github.com/illumos/sysroot/ in a while.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you set sa_handler, the right type is (void (*)(int)), and if you
set sa_sigaction, the right type is (void (*)(int, siginfo_t *, void *))
Those are union arms. Here'e the relevant section of sys/signa.h


/*
 * The signal handler routine can have either one or three arguments.  With
 * K&R C code could use either form so not specifing the arguments neatly
 * finessed the problem.  Modern C and any C++ do not accept this.  To them
 * "(*sa_handler)()" indicates a routine with no arguments (what used to be
 * "(*sa_handler)(void)").  One or the other form must be used and the only
 * logical choice is "(*sa_handler)(int)" to allow the SIG_* defines to work.
 * "(*sa_sigaction)(int, siginfo_t *, void *)" can be used for the three
 * argument form.
 */

/*
 * Note: storage overlap by sa_handler and sa_sigaction
 */
struct sigaction {
	int sa_flags;
	union {
		void (*_handler)(int);
#if defined(__EXTENSIONS__) || defined(_KERNEL) || \
	(!defined(_STRICT_STDC) && !defined(__XOPEN_OR_POSIX)) || \
	(_POSIX_C_SOURCE > 2) || defined(_XPG4_2)
		void (*_sigaction)(int, siginfo_t *, void *);
#endif
	}	_funcptr;
	sigset_t sa_mask;
#ifndef _LP64
	int sa_resv[2];
#endif
};
#define	sa_handler	_funcptr._handler
#define	sa_sigaction	_funcptr._sigaction

}

}
Expand Down
14 changes: 9 additions & 5 deletions src/native/libs/System.Native/pal_threading.c
Original file line number Diff line number Diff line change
Expand Up @@ -259,10 +259,12 @@ void SystemNative_LowLevelFutex_WakeByAddressSingle(int32_t* address)
}
#else // defined(TARGET_LINUX)

#ifdef DEBUG
#define DEBUGNOTRETURN __attribute__((noreturn))
#else
#define DEBUGNOTRETURN
// On illumos/Solaris libc's assert is not annotated noreturn, so marking these stubs noreturn would
// trigger -Winvalid-noreturn there. Only apply the attribute on other platforms.
#if defined(DEBUG) && !defined(TARGET_SUNOS)
#define DEBUGNOTRETURN __attribute__((noreturn))
#else
#define DEBUGNOTRETURN
#endif

DEBUGNOTRETURN
Expand All @@ -281,7 +283,7 @@ int32_t SystemNative_LowLevelFutex_WaitOnAddressTimeout(int32_t* address, int32_
(void)comparand; // unused
(void)timeoutMilliseconds; // unused
assert_msg(false, "Futex is not supported on this platform", 0);
#ifndef DEBUG
#if !defined(DEBUG) || defined(TARGET_SUNOS)
// trivial implementation of Wait always wakes spuriously.
return 1;
#endif
Expand All @@ -295,6 +297,8 @@ void SystemNative_LowLevelFutex_WakeByAddressSingle(int32_t* address)
// trivial implementation of Wake does nothing.
}

#undef DEBUGNOTRETURN

#endif // defined(TARGET_LINUX)

int32_t SystemNative_CreateThread(uintptr_t stackSize, void *(*startAddress)(void*), void *parameter)
Expand Down
2 changes: 1 addition & 1 deletion src/native/libs/System.Native/pal_time.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ int32_t SystemNative_UTimensat(const char* path, TimeSpec* times)

updatedTimes[1].tv_sec = (time_t)times[1].tv_sec;
updatedTimes[1].tv_nsec = (long)times[1].tv_nsec;
while (CheckInterrupted(result = utimensat(AT_FDCWD, path, updatedTimes, AT_SYMLINK_NOFOLLOW)));
while (CheckInterrupted(result = utimensat((int)AT_FDCWD, path, updatedTimes, AT_SYMLINK_NOFOLLOW)));
#else
struct timeval updatedTimes[2];
updatedTimes[0].tv_sec = (long)times[0].tv_sec;
Expand Down
7 changes: 5 additions & 2 deletions src/native/libs/System.Native/pal_uid.c
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,8 @@ static pthread_mutex_t s_groupLock = PTHREAD_MUTEX_INITIALIZER;
#endif

#if !HAVE_GETGROUPLIST
int getgrouplist(const char *uname, gid_t agroup, gid_t *groups, int *groupCount)
// Distinct name: avoids -Wmissing-prototypes (illumos) and clashing with grp.h's decl (Emscripten).
static int pal_getgrouplist(const char *uname, gid_t agroup, gid_t *groups, int *groupCount)
{
int ngroups = 1;
int maxgroups = *groupCount;
Expand Down Expand Up @@ -204,8 +205,10 @@ int32_t SystemNative_GetGroupList(const char* name, uint32_t group, uint32_t* gr
#ifdef __APPLE__
// On OSX groups are passed as a signed int.
rv = getgrouplist(name, (int)group, (int*)groups, &groupsAvailable);
#else
#elif HAVE_GETGROUPLIST
rv = getgrouplist(name, group, groups, &groupsAvailable);
#else
rv = pal_getgrouplist(name, group, groups, &groupsAvailable);
#endif

#ifdef USE_GROUPLIST_LOCK
Expand Down
Loading