فهرست منبع

Ports: Add OpenJDK port

Port of OpenJDK 17.0.2, zero VM only.

More work needed to get the full hotspot VM up and running :^)

Co-Authored-By: Andrew Kaster <akaster@serenityos.org>
Timur Sultanov 3 سال پیش
والد
کامیت
1f886f94bd

+ 1 - 0
Ports/AvailablePorts.md

@@ -162,6 +162,7 @@ This list is also available at [ports.serenityos.net](https://ports.serenityos.n
 | [`ntbtls`](ntbtls/)                                 | The Not Too Bad TLS Library                                     | 0.2.0                    | https://gnupg.org/software/ntbtls/index.html                                   |
 | [`nyancat`](nyancat/)                               | Nyancat                                                         |                          | https://github.com/klange/nyancat                                              |
 | [`oksh`](oksh/)                                     | oksh                                                            | 7.1                      | https://github.com/ibara/oksh                                                  |
+| [`OpenJDK`](OpenJDK/)                               | OpenJDK                                                         | 17.0.2                   | https://openjdk.java.net/                                                      |
 | [`openssh`](openssh/)                               | OpenSSH                                                         | 9.0-94eb685              | https://github.com/openssh/openssh-portable                                    |
 | [`openssl`](openssl/)                               | OpenSSL                                                         | 1.1.1o                   | https://www.openssl.org/                                                       |
 | [`openttd`](openttd/)                               | OpenTTD                                                         | 12.2                     | https://www.openttd.org/                                                       |

+ 57 - 0
Ports/OpenJDK/package.sh

@@ -0,0 +1,57 @@
+#!/usr/bin/env -S USE_CCACHE=false bash ../.port_include.sh
+
+port='OpenJDK'
+version='17.0.2'
+workdir="jdk17u-dev-jdk-${version}-ga"
+useconfigure='true'
+use_fresh_config_guess='true'
+config_guess_paths=("make/autoconf/build-aux/autoconf-config.guess")
+use_fresh_config_sub='true'
+config_sub_paths=("make/autoconf/build-aux/autoconf-config.sub")
+auth_type='sha256'
+files="https://github.com/openjdk/jdk17u-dev/archive/refs/tags/jdk-${version}-ga.tar.gz jdk-${version}-ga.tar.gz cb5b2a5d0916723d340f2c5bacd4607f8b8dc3a18dc8019fcfabf5306e2a4112"
+depends=("fontconfig" "libffi")
+
+configure() {
+    TOOLCHAIN_ARGS=()
+    WARNING_IGNORE_FLAGS='-Wno-error=switch -Wno-maybe-uninitialized'
+    if [ $SERENITY_TOOLCHAIN = 'Clang' ]; then
+        # We need the build CC and CXX to actually be clang when using clang to cross-compile
+        #    ... for some reason.
+        TOOLCHAIN_ARGS=("--with-toolchain-type=clang"
+                        "BUILD_CC=clang"
+                        "BUILD_CXX=clang++")
+        WARNING_IGNORE_FLAGS="${WARNING_IGNORE_FLAGS} -Wno-error=bitwise-instead-of-logical"
+    fi
+
+    # Note: To use ccache with OpenJDK, pass --enable-ccache
+    #     It rejects the ccache symlinks.
+
+    run bash configure \
+        AR=${AR} \
+        READELF=${READELF} \
+        STRIP=${STRIP} \
+        CXXFILT=${CXXFILT} \
+        BUILD_AR=${HOST_AR} \
+        BUILD_OBJCOPY=${HOST_OBJCOPY} \
+        BUILD_STRIP=${HOST_STRIP} \
+        --openjdk-target=${SERENITY_ARCH}-pc-serenity \
+        --with-sysroot=${SERENITY_INSTALL_ROOT} \
+        --with-jvm-variants=zero \
+        --enable-headless-only \
+        --with-debug-level=fastdebug \
+        --with-native-debug-symbols=internal \
+        --with-tools-dir=${SERENITY_TOOLCHAIN_BINDIR} \
+        --with-extra-cflags="${WARNING_IGNORE_FLAGS}" \
+        --with-extra-cxxflags="${WARNING_IGNORE_FLAGS}" \
+        "${TOOLCHAIN_ARGS[@]}"
+}
+
+build() {
+    run make java.base jdk.compiler java.logging
+}
+
+install() {
+    run mkdir -p ${SERENITY_INSTALL_ROOT}/usr/local/lib/jvm/
+    run sh -c "cp ./build/serenity-* ${SERENITY_INSTALL_ROOT}/usr/local/lib/jvm/ -rf"
+}

+ 152 - 0
Ports/OpenJDK/patches/0001-make-Add-Serenity-support-masquerading-as-BSD-when-n.patch

@@ -0,0 +1,152 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Timur Sultanov <SultanovTS@yandex.ru>
+Date: Wed, 16 Feb 2022 21:04:18 +0300
+Subject: [PATCH] make: Add Serenity support, masquerading as BSD when
+ necessary
+
+Co-Authored-By: Andrew Kaster <akaster@serenityos.org>
+---
+ make/autoconf/flags-cflags.m4                |  3 +++
+ make/autoconf/flags-ldflags.m4               | 11 +++++++++++
+ make/autoconf/platform.m4                    |  8 ++++++++
+ make/autoconf/toolchain.m4                   |  1 +
+ make/common/modules/LauncherCommon.gmk       |  3 +++
+ make/hotspot/lib/JvmMapfile.gmk              | 12 ++++++++++++
+ make/modules/java.base/lib/CoreLibraries.gmk |  1 +
+ 7 files changed, 39 insertions(+)
+
+diff --git a/make/autoconf/flags-cflags.m4 b/make/autoconf/flags-cflags.m4
+index 5eed1138f..62e53a1c4 100644
+--- a/make/autoconf/flags-cflags.m4
++++ b/make/autoconf/flags-cflags.m4
+@@ -382,6 +382,9 @@ AC_DEFUN([FLAGS_SETUP_CFLAGS_HELPER],
+     CFLAGS_OS_DEF_JVM="-DAIX"
+   elif test "x$OPENJDK_TARGET_OS" = xbsd; then
+     CFLAGS_OS_DEF_JDK="-D_ALLBSD_SOURCE"
++  elif test "x$OPENJDK_TARGET_OS" = xserenity; then
++    CFLAGS_OS_DEF_JDK="-DSERENITY"
++    CFLAGS_OS_DEF_JVM="-DSERENITY"
+   elif test "x$OPENJDK_TARGET_OS" = xwindows; then
+     CFLAGS_OS_DEF_JVM="-D_WINDOWS -DWIN32 -D_JNI_IMPLEMENTATION_"
+   fi
+diff --git a/make/autoconf/flags-ldflags.m4 b/make/autoconf/flags-ldflags.m4
+index 23bb33e87..e3deb0c3f 100644
+--- a/make/autoconf/flags-ldflags.m4
++++ b/make/autoconf/flags-ldflags.m4
+@@ -110,6 +110,17 @@ AC_DEFUN([FLAGS_SETUP_LDFLAGS_HELPER],
+     fi
+   fi
+ 
++  if test "x$OPENJDK_TARGET_OS" = xserenity; then
++    if test "x$TOOLCHAIN_TYPE" = xgcc; then
++      # FIXME: -lgcc_s should not be required, toolchain spec file issue
++      OS_LDFLAGS_JVM_ONLY="$OS_LDFLAGS_JVM_ONLY -ldl -lpthread -lgcc_s"
++      OS_LDFLAGS="$OS_LDFLAGS -ldl -lpthread -lgcc_s"
++    elif test "x$TOOLCHAIN_TYPE" = xclang; then
++      OS_LDFLAGS_JVM_ONLY="$OS_LDFLAGS_JVM_ONLY -ldl -lpthread"
++      OS_LDFLAGS="$OS_LDFLAGS -ldl -lpthread"
++    fi
++  fi
++
+   # Setup debug level-dependent LDFLAGS
+   if test "x$TOOLCHAIN_TYPE" = xgcc; then
+     if test "x$OPENJDK_TARGET_OS" = xlinux; then
+diff --git a/make/autoconf/platform.m4 b/make/autoconf/platform.m4
+index 205d64f56..6e668edc4 100644
+--- a/make/autoconf/platform.m4
++++ b/make/autoconf/platform.m4
+@@ -220,6 +220,10 @@ AC_DEFUN([PLATFORM_EXTRACT_VARS_FROM_OS],
+       VAR_OS=aix
+       VAR_OS_TYPE=unix
+       ;;
++    *serenity*)
++      VAR_OS=serenity
++      VAR_OS_TYPE=unix
++      ;;
+     *)
+       AC_MSG_ERROR([unsupported operating system $1])
+       ;;
+@@ -521,6 +525,8 @@ AC_DEFUN([PLATFORM_SETUP_LEGACY_VARS_HELPER],
+   HOTSPOT_$1_OS=${OPENJDK_$1_OS}
+   if test "x$OPENJDK_$1_OS" = xmacosx; then
+     HOTSPOT_$1_OS=bsd
++  elif test "x$OPENJDK_$1_OS" = xserenity; then
++    HOTSPOT_$1_OS=bsd
+   fi
+   AC_SUBST(HOTSPOT_$1_OS)
+ 
+@@ -587,6 +593,8 @@ AC_DEFUN([PLATFORM_SETUP_LEGACY_VARS_HELPER],
+     OPENJDK_$1_OS_INCLUDE_SUBDIR="win32"
+   elif test "x$OPENJDK_TARGET_OS" = "xmacosx"; then
+     OPENJDK_$1_OS_INCLUDE_SUBDIR="darwin"
++  elif test "x$OPENJDK_TARGET_OS" = "xserenity"; then
++    OPENJDK_$1_OS_INCLUDE_SUBDIR="bsd"
+   fi
+   AC_SUBST(OPENJDK_$1_OS_INCLUDE_SUBDIR)
+ ])
+diff --git a/make/autoconf/toolchain.m4 b/make/autoconf/toolchain.m4
+index 69540e160..badd84a50 100644
+--- a/make/autoconf/toolchain.m4
++++ b/make/autoconf/toolchain.m4
+@@ -42,6 +42,7 @@ VALID_TOOLCHAINS_linux="gcc clang"
+ VALID_TOOLCHAINS_macosx="gcc clang"
+ VALID_TOOLCHAINS_aix="xlc"
+ VALID_TOOLCHAINS_windows="microsoft"
++VALID_TOOLCHAINS_serenity="gcc clang"
+ 
+ # Toolchain descriptions
+ TOOLCHAIN_DESCRIPTION_clang="clang/LLVM"
+diff --git a/make/common/modules/LauncherCommon.gmk b/make/common/modules/LauncherCommon.gmk
+index 7ad0375e2..8100f655e 100644
+--- a/make/common/modules/LauncherCommon.gmk
++++ b/make/common/modules/LauncherCommon.gmk
+@@ -157,11 +157,14 @@ define SetupBuildLauncherBody
+           $$($1_LDFLAGS), \
+       LDFLAGS_linux := $$(call SET_EXECUTABLE_ORIGIN,/../lib) \
+           -L$(call FindLibDirForModule, java.base), \
++      LDFLAGS_serenity := $$(call SET_EXECUTABLE_ORIGIN,/../lib) \
++          -L$(call FindLibDirForModule, java.base), \
+       LDFLAGS_macosx := $$(call SET_EXECUTABLE_ORIGIN,/../lib) \
+           -L$(call FindLibDirForModule, java.base), \
+       LDFLAGS_aix := -L$(SUPPORT_OUTPUTDIR)/native/java.base, \
+       LIBS := $(JDKEXE_LIBS) $$($1_LIBS), \
+       LIBS_linux := -ljli -lpthread $(LIBDL), \
++      LIBS_serenity := -ljli -lpthread $(LIBDL), \
+       LIBS_macosx := -ljli -framework Cocoa -framework Security \
+           -framework ApplicationServices, \
+       LIBS_aix := -ljli_static, \
+diff --git a/make/hotspot/lib/JvmMapfile.gmk b/make/hotspot/lib/JvmMapfile.gmk
+index 5cba93178..752727d0d 100644
+--- a/make/hotspot/lib/JvmMapfile.gmk
++++ b/make/hotspot/lib/JvmMapfile.gmk
+@@ -64,6 +64,18 @@ ifeq ($(call isTargetOs, linux), true)
+         if ($$3 ~ /$(FILTER_SYMBOLS_PATTERN)/) print $$3; \
+       }'
+ 
++else ifeq ($(call isTargetOs, serenity), true)
++  DUMP_SYMBOLS_CMD := $(NM) --defined-only *.o
++  ifneq ($(FILTER_SYMBOLS_PATTERN), )
++    FILTER_SYMBOLS_PATTERN := $(FILTER_SYMBOLS_PATTERN)|
++  endif
++  FILTER_SYMBOLS_PATTERN := $(FILTER_SYMBOLS_PATTERN)^_ZTV|^gHotSpotVM|^UseSharedSpaces$$
++  FILTER_SYMBOLS_PATTERN := $(FILTER_SYMBOLS_PATTERN)|^_ZN9Arguments17SharedArchivePathE$$
++  FILTER_SYMBOLS_AWK_SCRIPT := \
++      '{ \
++        if ($$3 ~ /$(FILTER_SYMBOLS_PATTERN)/) print $$3; \
++      }'
++
+ else ifeq ($(call isTargetOs, macosx), true)
+   # nm on macosx prints out "warning: nm: no name list" to stderr for
+   # files without symbols. Hide this, even at the expense of hiding real errors.
+diff --git a/make/modules/java.base/lib/CoreLibraries.gmk b/make/modules/java.base/lib/CoreLibraries.gmk
+index 1d5fede2a..0a61d009f 100644
+--- a/make/modules/java.base/lib/CoreLibraries.gmk
++++ b/make/modules/java.base/lib/CoreLibraries.gmk
+@@ -209,6 +209,7 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBJLI, \
+     LIBS_unix := $(LIBZ_LIBS), \
+     LIBS_linux := $(LIBDL) -lpthread, \
+     LIBS_aix := $(LIBDL),\
++    LIBS_serenity := $(LIBDL) -lpthread, \
+     LIBS_macosx := -framework Cocoa -framework Security -framework ApplicationServices, \
+     LIBS_windows := advapi32.lib comctl32.lib user32.lib, \
+ ))

+ 98 - 0
Ports/OpenJDK/patches/0002-make-Build-with-c-20-when-targeting-serenity.patch

@@ -0,0 +1,98 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Andrew Kaster <andrewdkaster@gmail.com>
+Date: Sun, 12 Jun 2022 23:13:56 -0600
+Subject: [PATCH] make: Build with c++20 when targeting serenity
+
+---
+ make/autoconf/flags-cflags.m4                 | 8 ++++++--
+ src/hotspot/share/utilities/chunkedList.hpp   | 2 +-
+ src/hotspot/share/utilities/events.hpp        | 2 +-
+ src/hotspot/share/utilities/growableArray.hpp | 2 +-
+ src/hotspot/share/utilities/linkedlist.hpp    | 2 +-
+ 5 files changed, 10 insertions(+), 6 deletions(-)
+
+diff --git a/make/autoconf/flags-cflags.m4 b/make/autoconf/flags-cflags.m4
+index 62e53a1c4..9239dfb43 100644
+--- a/make/autoconf/flags-cflags.m4
++++ b/make/autoconf/flags-cflags.m4
+@@ -154,7 +154,8 @@ AC_DEFUN([FLAGS_SETUP_WARNINGS],
+       WARNINGS_ENABLE_ALL_CFLAGS="-Wall -Wextra -Wformat=2 $WARNINGS_ENABLE_ADDITIONAL"
+       WARNINGS_ENABLE_ALL_CXXFLAGS="$WARNINGS_ENABLE_ALL_CFLAGS $WARNINGS_ENABLE_ADDITIONAL_CXX"
+ 
+-      DISABLED_WARNINGS="unused-parameter unused"
++      DISABLED_WARNINGS="unused-parameter unused address stringop-overflow stringop-truncation format-truncation use-after-free"
++      DISABLED_WARNINGS_CXX="volatile deprecated-enum-enum-conversion deprecated-enum-float-conversion"
+       ;;
+ 
+     clang)
+@@ -166,7 +167,7 @@ AC_DEFUN([FLAGS_SETUP_WARNINGS],
+           -Wunused-function -Wundef -Wunused-value -Woverloaded-virtual"
+       WARNINGS_ENABLE_ALL="-Wall -Wextra -Wformat=2 $WARNINGS_ENABLE_ADDITIONAL"
+ 
+-      DISABLED_WARNINGS="unknown-warning-option unused-parameter unused"
++      DISABLED_WARNINGS="unknown-warning-option unused-parameter unused deprecated-volatile deprecated-anon-enum-enum-conversion deprecated-enum-float-conversion ambiguous-reversed-operator"
+ 
+       ;;
+ 
+@@ -529,6 +530,9 @@ AC_DEFUN([FLAGS_SETUP_CFLAGS_HELPER],
+   else
+     AC_MSG_ERROR([Don't know how to enable C++14 for this toolchain])
+   fi
++  if test "x$OPENJDK_TARGET_OS" = xserenity; then
++    LANGSTD_CXXFLAGS="-std=c++20"
++  fi
+   TOOLCHAIN_CFLAGS_JDK_CXXONLY="$TOOLCHAIN_CFLAGS_JDK_CXXONLY $LANGSTD_CXXFLAGS"
+   TOOLCHAIN_CFLAGS_JVM="$TOOLCHAIN_CFLAGS_JVM $LANGSTD_CXXFLAGS"
+   ADLC_LANGSTD_CXXFLAGS="$LANGSTD_CXXFLAGS"
+diff --git a/src/hotspot/share/utilities/chunkedList.hpp b/src/hotspot/share/utilities/chunkedList.hpp
+index 1a899ee2b..13f05cd3a 100644
+--- a/src/hotspot/share/utilities/chunkedList.hpp
++++ b/src/hotspot/share/utilities/chunkedList.hpp
+@@ -44,7 +44,7 @@ template <class T, MEMFLAGS F> class ChunkedList : public CHeapObj<F> {
+   }
+ 
+  public:
+-  ChunkedList<T, F>() : _top(_values), _next_used(NULL), _next_free(NULL) {}
++  ChunkedList() : _top(_values), _next_used(NULL), _next_free(NULL) {}
+ 
+   bool is_full() const {
+     return _top == end();
+diff --git a/src/hotspot/share/utilities/events.hpp b/src/hotspot/share/utilities/events.hpp
+index b5d67bd6a..3cf3b399f 100644
+--- a/src/hotspot/share/utilities/events.hpp
++++ b/src/hotspot/share/utilities/events.hpp
+@@ -99,7 +99,7 @@ template <class T> class EventLogBase : public EventLog {
+   EventRecord<T>* _records;
+ 
+  public:
+-  EventLogBase<T>(const char* name, const char* handle, int length = LogEventsBufferEntries):
++  EventLogBase(const char* name, const char* handle, int length = LogEventsBufferEntries):
+     _mutex(Mutex::event, name, true, Mutex::_safepoint_check_never),
+     _name(name),
+     _handle(handle),
+diff --git a/src/hotspot/share/utilities/growableArray.hpp b/src/hotspot/share/utilities/growableArray.hpp
+index b75283843..67074b86f 100644
+--- a/src/hotspot/share/utilities/growableArray.hpp
++++ b/src/hotspot/share/utilities/growableArray.hpp
+@@ -118,7 +118,7 @@ class GrowableArrayView : public GrowableArrayBase {
+ protected:
+   E* _data; // data array
+ 
+-  GrowableArrayView<E>(E* data, int initial_max, int initial_len) :
++  GrowableArrayView(E* data, int initial_max, int initial_len) :
+       GrowableArrayBase(initial_max, initial_len), _data(data) {}
+ 
+   ~GrowableArrayView() {}
+diff --git a/src/hotspot/share/utilities/linkedlist.hpp b/src/hotspot/share/utilities/linkedlist.hpp
+index 16ee6a844..2c5ffe6cb 100644
+--- a/src/hotspot/share/utilities/linkedlist.hpp
++++ b/src/hotspot/share/utilities/linkedlist.hpp
+@@ -82,7 +82,7 @@ template <class E> class LinkedListNode : public ResourceObj {
+ template <class E> class LinkedList : public ResourceObj {
+  protected:
+   LinkedListNode<E>*    _head;
+-  NONCOPYABLE(LinkedList<E>);
++  NONCOPYABLE(LinkedList);
+ 
+  public:
+   LinkedList() : _head(NULL) { }

+ 22 - 0
Ports/OpenJDK/patches/0003-make-Remove-CUPS-dependency.patch

@@ -0,0 +1,22 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Timur Sultanov <SultanovTS@yandex.ru>
+Date: Thu, 17 Feb 2022 09:37:02 +0300
+Subject: [PATCH] make: Remove CUPS dependency
+
+---
+ make/autoconf/libraries.m4 | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/make/autoconf/libraries.m4 b/make/autoconf/libraries.m4
+index a65d91ee9..7c779d012 100644
+--- a/make/autoconf/libraries.m4
++++ b/make/autoconf/libraries.m4
+@@ -26,7 +26,7 @@
+ # Major library component reside in separate files.
+ m4_include([lib-alsa.m4])
+ m4_include([lib-bundled.m4])
+-m4_include([lib-cups.m4])
++# m4_include([lib-cups.m4])
+ m4_include([lib-ffi.m4])
+ m4_include([lib-freetype.m4])
+ m4_include([lib-std.m4])

+ 400 - 0
Ports/OpenJDK/patches/0004-hotspot-Add-workarounds-for-BSD-differences-from-ser.patch

@@ -0,0 +1,400 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Andrew Kaster <akaster@serenityos.org>
+Date: Sun, 12 Jun 2022 15:51:39 -0600
+Subject: [PATCH] hotspot: Add workarounds for BSD differences from serenity
+
+For the most part, we can pretend to be *BSD.
+
+However, for some methods, we need to convince hotspot that we're macOS,
+and others need serenity-specific ifdefs due to the lack of sysctl in
+serenity.
+
+Co-Authored-By: Timur Sultanov <sultanovts@yandex.ru>
+---
+ src/hotspot/os/bsd/attachListener_bsd.cpp     | 12 +++
+ src/hotspot/os/bsd/osThread_bsd.cpp           |  6 +-
+ src/hotspot/os/bsd/os_bsd.cpp                 | 76 ++++++++++++++++++-
+ src/hotspot/os/bsd/os_perf_bsd.cpp            |  4 +
+ .../os_cpu/bsd_zero/bytes_bsd_zero.hpp        |  2 +
+ src/hotspot/os_cpu/bsd_zero/os_bsd_zero.cpp   | 18 ++++-
+ src/hotspot/share/classfile/classLoader.cpp   |  2 +-
+ 7 files changed, 113 insertions(+), 7 deletions(-)
+
+diff --git a/src/hotspot/os/bsd/attachListener_bsd.cpp b/src/hotspot/os/bsd/attachListener_bsd.cpp
+index 9daad43dc..092b4d94a 100644
+--- a/src/hotspot/os/bsd/attachListener_bsd.cpp
++++ b/src/hotspot/os/bsd/attachListener_bsd.cpp
+@@ -358,11 +358,23 @@ BsdAttachOperation* BsdAttachListener::dequeue() {
+     // get the credentials of the peer and check the effective uid/guid
+     uid_t puid;
+     gid_t pgid;
++#if defined(SERENITY)
++    struct ucred creds = {};
++    socklen_t creds_size = sizeof(creds);
++    if (::getsockopt(s, SOL_SOCKET, SO_PEERCRED, &creds, &creds_size) != 0) {
++      log_debug(attach)("Failed to get peer id");
++      ::close(s);
++      continue;
++    }
++    puid = creds.uid;
++    pgid = creds.gid;
++#else
+     if (::getpeereid(s, &puid, &pgid) != 0) {
+       log_debug(attach)("Failed to get peer id");
+       ::close(s);
+       continue;
+     }
++#endif
+ 
+     if (!os::Posix::matches_effective_uid_and_gid_or_root(puid, pgid)) {
+       log_debug(attach)("euid/egid check failed (%d/%d vs %d/%d)", puid, pgid,
+diff --git a/src/hotspot/os/bsd/osThread_bsd.cpp b/src/hotspot/os/bsd/osThread_bsd.cpp
+index 9eba7288f..d7164e5d5 100644
+--- a/src/hotspot/os/bsd/osThread_bsd.cpp
++++ b/src/hotspot/os/bsd/osThread_bsd.cpp
+@@ -31,13 +31,17 @@
+ 
+ void OSThread::pd_initialize() {
+   assert(this != NULL, "check");
+-#ifdef __APPLE__
++#if defined(__APPLE__) || defined(SERENITY)
+   _thread_id        = 0;
+ #else
+   _thread_id        = NULL;
+ #endif
+   _unique_thread_id = 0;
++#if defined(SERENITY)
++  _pthread_id       = 0;
++#else
+   _pthread_id       = NULL;
++#endif
+   _siginfo          = NULL;
+   _ucontext         = NULL;
+   _expanding_stack  = 0;
+diff --git a/src/hotspot/os/bsd/os_bsd.cpp b/src/hotspot/os/bsd/os_bsd.cpp
+index 1896c036c..4452b5e9b 100644
+--- a/src/hotspot/os/bsd/os_bsd.cpp
++++ b/src/hotspot/os/bsd/os_bsd.cpp
+@@ -87,8 +87,10 @@
+ # include <sys/resource.h>
+ # include <sys/socket.h>
+ # include <sys/stat.h>
++#ifndef SERENITY
+ # include <sys/syscall.h>
+ # include <sys/sysctl.h>
++#endif
+ # include <sys/time.h>
+ # include <sys/times.h>
+ # include <sys/types.h>
+@@ -99,6 +101,12 @@
+   #include <elf.h>
+ #endif
+ 
++#if defined(SERENITY)
++#include "utilities/decoder_elf.hpp"
++#include "utilities/elfFile.hpp"
++#include <cxxabi.h>
++#endif
++
+ #ifdef __APPLE__
+   #include <mach-o/dyld.h>
+ #endif
+@@ -162,6 +170,9 @@ julong os::Bsd::available_memory() {
+ // for more info see :
+ // https://man.openbsd.org/sysctl.2
+ void os::Bsd::print_uptime_info(outputStream* st) {
++#ifdef SERENITY
++  st->print("OS uptime: unknown"); // FIXME: Grab uptime
++#else
+   struct timeval boottime;
+   size_t len = sizeof(boottime);
+   int mib[2];
+@@ -173,6 +184,7 @@ void os::Bsd::print_uptime_info(outputStream* st) {
+     time_t currsec = time(NULL);
+     os::print_dhm(st, "OS uptime:", (long) difftime(currsec, bootsec));
+   }
++#endif
+ }
+ 
+ julong os::physical_memory() {
+@@ -221,6 +233,10 @@ static char cpu_arch[] = "ppc";
+ 
+ 
+ void os::Bsd::initialize_system_info() {
++#if defined (SERENITY)
++  set_processor_count(1); // FIXME
++  _physical_memory = 256 * 1024 * 1024; // FIXME
++#else
+   int mib[2];
+   size_t len;
+   int cpu_val;
+@@ -275,6 +291,7 @@ void os::Bsd::initialize_system_info() {
+     _physical_memory = MIN2(_physical_memory, (julong)limits.rlim_cur);
+   }
+ #endif
++#endif // SERENITY
+ }
+ 
+ #ifdef __APPLE__
+@@ -363,12 +380,18 @@ void os::init_system_properties_values() {
+     if (pslash != NULL) {
+       pslash = strrchr(buf, '/');
+       if (pslash != NULL) {
++#ifdef SERENITY
++      // no <arch> dir on serenity
++      *pslash = '\0';        // Get rid of /lib.
++      }
++#else
+         *pslash = '\0';          // Get rid of /<arch>.
+         pslash = strrchr(buf, '/');
+         if (pslash != NULL) {
+           *pslash = '\0';        // Get rid of /lib.
+         }
+       }
++#endif
+     }
+     Arguments::set_java_home(buf);
+     if (!set_boot_path('/', ':')) {
+@@ -877,6 +900,10 @@ pid_t os::Bsd::gettid() {
+     #else
+       #ifdef __NetBSD__
+   retval = (pid_t) syscall(SYS__lwp_self);
++      #else
++        #ifdef SERENITY
++         retval = ::gettid();
++        #endif
+       #endif
+     #endif
+   #endif
+@@ -885,6 +912,7 @@ pid_t os::Bsd::gettid() {
+   if (retval == -1) {
+     return getpid();
+   }
++  return retval;
+ }
+ 
+ intx os::current_thread_id() {
+@@ -942,6 +970,25 @@ bool os::address_is_in_vm(address addr) {
+   return false;
+ }
+ 
++#ifdef SERENITY
++// We put this here so that we don't need to add an entire file just to dup this method from the linux decoder
++bool ElfDecoder::demangle(const char* symbol, char *buf, int buflen) {
++  int   status;
++  char* result;
++  size_t size = (size_t)buflen;
++
++  // Don't pass buf to __cxa_demangle. In case of the 'buf' is too small,
++  // __cxa_demangle will call system "realloc" for additional memory, which
++  // may use different malloc/realloc mechanism that allocates 'buf'.
++  if ((result = abi::__cxa_demangle(symbol, NULL, NULL, &status)) != NULL) {
++    jio_snprintf(buf, buflen, "%s", result);
++      // call c library's free
++      ::free(result);
++      return true;
++  }
++  return false;
++}
++#endif // SERENITY
+ 
+ #define MACH_MAXSYMLEN 256
+ 
+@@ -1013,7 +1060,7 @@ bool os::dll_address_to_library_name(address addr, char* buf,
+ // in case of error it checks if .dll/.so was built for the
+ // same architecture as Hotspot is running on
+ 
+-#ifdef __APPLE__
++#if defined(__APPLE__) || defined(SERENITY)
+ void * os::dll_load(const char *filename, char *ebuf, int ebuflen) {
+ #ifdef STATIC_BUILD
+   return os::get_default_process_handle();
+@@ -1226,7 +1273,7 @@ void * os::dll_load(const char *filename, char *ebuf, int ebuflen) {
+   return NULL;
+ #endif // STATIC_BUILD
+ }
+-#endif // !__APPLE__
++#endif // !__APPLE__ || !SERENITY
+ 
+ void* os::get_default_process_handle() {
+ #ifdef __APPLE__
+@@ -1305,6 +1352,7 @@ int os::get_loaded_modules_info(os::LoadedModulesCallbackFunc callback, void *pa
+ }
+ 
+ void os::get_summary_os_info(char* buf, size_t buflen) {
++#ifndef SERENITY
+   // These buffers are small because we want this to be brief
+   // and not use a lot of stack while generating the hs_err file.
+   char os[100];
+@@ -1342,6 +1390,10 @@ void os::get_summary_os_info(char* buf, size_t buflen) {
+       snprintf(buf, buflen, "%s %s, macOS %s (%s)", os, release, osproductversion, build);
+     }
+   } else
++#endif
++#else
++  const char os[] = "SerenityOS";
++  const char release[] = "1.0-dev";
+ #endif
+   snprintf(buf, buflen, "%s %s", os, release);
+ }
+@@ -1369,6 +1421,7 @@ void os::pd_print_cpu_info(outputStream* st, char* buf, size_t buflen) {
+ }
+ 
+ void os::get_summary_cpu_info(char* buf, size_t buflen) {
++#ifndef SERENITY
+   unsigned int mhz;
+   size_t size = sizeof(mhz);
+   int mib[] = { CTL_HW, HW_CPU_FREQ };
+@@ -1399,9 +1452,13 @@ void os::get_summary_cpu_info(char* buf, size_t buflen) {
+   }
+ #endif
+   snprintf(buf, buflen, "\"%s\" %s%s %d MHz", model, machine, emulated, mhz);
++#else
++  snprintf(buf, buflen, "%s", "FIXME: Implement CPU Info");
++#endif
+ }
+ 
+ void os::print_memory_info(outputStream* st) {
++#ifndef SERENITY
+   xsw_usage swap_usage;
+   size_t size = sizeof(swap_usage);
+ 
+@@ -1423,6 +1480,9 @@ void os::print_memory_info(outputStream* st) {
+   }
+ 
+   st->cr();
++#else
++  st->print("Memory: FIXME unknown");
++#endif
+ }
+ 
+ static char saved_jvm_path[MAXPATHLEN] = {0};
+@@ -1584,6 +1644,10 @@ bool os::pd_commit_memory(char* addr, size_t size, bool exec) {
+     }
+   }
+ #else
++  #if defined(SERENITY)
++  // FIXME: Mount location of java install with MS_WXALLOWED and MS_AXALLOWED
++  prot &= ~PROT_EXEC;
++  #endif
+   uintptr_t res = (uintptr_t) ::mmap(addr, size, prot,
+                                      MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0);
+   if (res != (uintptr_t) MAP_FAILED) {
+@@ -1994,6 +2058,10 @@ OSReturn os::get_native_priority(const Thread* const thread, int *priority_ptr)
+ extern void report_error(char* file_name, int line_no, char* title,
+                          char* format, ...);
+ 
++#if defined(SERENITY) && !defined(CLK_TCK)
++#define CLK_TCK 100
++#endif
++
+ // this is called _before_ the most of global arguments have been parsed
+ void os::init(void) {
+   char dummy;   // used to get a guess on initial stack address
+@@ -2535,7 +2603,11 @@ bool os::is_thread_cpu_time_supported() {
+ // Bsd doesn't yet have a (official) notion of processor sets,
+ // so just return the system wide load average.
+ int os::loadavg(double loadavg[], int nelem) {
++#ifdef SERENITY
++  return -1;
++#else
+   return ::getloadavg(loadavg, nelem);
++#endif
+ }
+ 
+ void os::pause() {
+diff --git a/src/hotspot/os/bsd/os_perf_bsd.cpp b/src/hotspot/os/bsd/os_perf_bsd.cpp
+index e69bfc795..4e67e2e4b 100644
+--- a/src/hotspot/os/bsd/os_perf_bsd.cpp
++++ b/src/hotspot/os/bsd/os_perf_bsd.cpp
+@@ -425,6 +425,9 @@ NetworkPerformanceInterface::NetworkPerformance::~NetworkPerformance() {
+ }
+ 
+ int NetworkPerformanceInterface::NetworkPerformance::network_utilization(NetworkInterface** network_interfaces) const {
++#ifdef SERENITY
++  return OS_ERR; // FIXME: Get stats from Network interface daemon
++#else
+   size_t len;
+   int mib[] = {CTL_NET, PF_ROUTE, /* protocol number */ 0, /* address family */ 0, NET_RT_IFLIST2, /* NET_RT_FLAGS mask*/ 0};
+   if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &len, NULL, 0) != 0) {
+@@ -464,6 +467,7 @@ int NetworkPerformanceInterface::NetworkPerformance::network_utilization(Network
+   *network_interfaces = ret;
+ 
+   return OS_OK;
++#endif
+ }
+ 
+ NetworkPerformanceInterface::NetworkPerformanceInterface() {
+diff --git a/src/hotspot/os_cpu/bsd_zero/bytes_bsd_zero.hpp b/src/hotspot/os_cpu/bsd_zero/bytes_bsd_zero.hpp
+index 0da7ecc78..bd1ee9a67 100644
+--- a/src/hotspot/os_cpu/bsd_zero/bytes_bsd_zero.hpp
++++ b/src/hotspot/os_cpu/bsd_zero/bytes_bsd_zero.hpp
+@@ -30,6 +30,8 @@
+ 
+ #ifdef __APPLE__
+ #  include <libkern/OSByteOrder.h>
++#elif defined(SERENITY)
++#  include <endian.h>
+ #else
+ #  include <sys/endian.h>
+ #endif
+diff --git a/src/hotspot/os_cpu/bsd_zero/os_bsd_zero.cpp b/src/hotspot/os_cpu/bsd_zero/os_bsd_zero.cpp
+index a9fda1d4b..494f073ac 100644
+--- a/src/hotspot/os_cpu/bsd_zero/os_bsd_zero.cpp
++++ b/src/hotspot/os_cpu/bsd_zero/os_bsd_zero.cpp
+@@ -23,11 +23,15 @@
+  *
+  */
+ 
+-#if !defined(__APPLE__) && !defined(__NetBSD__)
++#if !defined(__APPLE__) && !defined(__NetBSD__) && !defined(SERENITY)
+ #include <pthread.h>
+ # include <pthread_np.h> /* For pthread_attr_get_np */
+ #endif
+ 
++#if defined(SERENITY)
++#  include <serenity.h>
++#endif
++
+ // no precompiled headers
+ #include "jvm.h"
+ #include "asm/assembler.inline.hpp"
+@@ -56,8 +60,7 @@
+ #include "utilities/vmError.hpp"
+ 
+ address os::current_stack_pointer() {
+-  address dummy = (address) &dummy;
+-  return dummy;
++  return (address) __builtin_frame_address(0);
+ }
+ 
+ frame os::get_sender_for_C_frame(frame* fr) {
+@@ -194,6 +197,15 @@ static void current_stack_region(address *bottom, size_t *size) {
+   stack_top = (address) ss.ss_sp;
+   stack_bytes  = ss.ss_size;
+   stack_bottom = stack_top - stack_bytes;
++#elif defined(SERENITY)
++  uintptr_t real_stack_bottom = 0;
++  int rslt = get_stack_bounds(&real_stack_bottom, &stack_bytes);
++
++  if (rslt < 0)
++    fatal("get_stack_bounds failed with error = " INT32_FORMAT, rslt);
++
++  stack_bottom = (address)real_stack_bottom;
++  stack_top = stack_bottom + stack_bytes;
+ #else
+   pthread_attr_t attr;
+ 
+diff --git a/src/hotspot/share/classfile/classLoader.cpp b/src/hotspot/share/classfile/classLoader.cpp
+index 0287b73e5..101299b2f 100644
+--- a/src/hotspot/share/classfile/classLoader.cpp
++++ b/src/hotspot/share/classfile/classLoader.cpp
+@@ -249,7 +249,7 @@ ClassFileStream* ClassPathDirEntry::open_stream(JavaThread* current, const char*
+   struct stat st;
+   if (os::stat(path, &st) == 0) {
+     // found file, open it
+-    int file_handle = os::open(path, 0, 0);
++    int file_handle = os::open(path, O_RDONLY, 0);
+     if (file_handle != -1) {
+       // read contents into resource array
+       u1* buffer = NEW_RESOURCE_ARRAY(u1, st.st_size);

+ 268 - 0
Ports/OpenJDK/patches/0005-hotspot-Update-non-BSD-native-modules-for-Serenity.patch

@@ -0,0 +1,268 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Timur Sultanov <sultanovts@yandex.ru>
+Date: Sun, 12 Jun 2022 13:55:07 -0600
+Subject: [PATCH] hotspot: Update non-BSD native modules for Serenity
+
+Co-Authored-By: Andrew Kaster <akaster@serenityos.org>
+---
+ src/hotspot/os/posix/os_posix.cpp             | 23 ++++++++++++++++++-
+ src/hotspot/os/posix/signals_posix.cpp        | 12 ++++++++++
+ src/hotspot/share/runtime/os.cpp              |  6 ++++-
+ src/hotspot/share/runtime/os.hpp              |  2 +-
+ src/hotspot/share/runtime/semaphore.hpp       |  2 +-
+ .../share/utilities/globalDefinitions.hpp     |  4 ++++
+ .../share/utilities/globalDefinitions_gcc.hpp |  6 ++---
+ src/hotspot/share/utilities/ostream.cpp       |  2 +-
+ 8 files changed, 49 insertions(+), 8 deletions(-)
+
+diff --git a/src/hotspot/os/posix/os_posix.cpp b/src/hotspot/os/posix/os_posix.cpp
+index 9eb1fcbcc..0676cb83d 100644
+--- a/src/hotspot/os/posix/os_posix.cpp
++++ b/src/hotspot/os/posix/os_posix.cpp
+@@ -65,7 +65,9 @@
+ #include <sys/wait.h>
+ #include <time.h>
+ #include <unistd.h>
++#ifndef SERENITY
+ #include <utmpx.h>
++#endif
+ 
+ #ifdef __APPLE__
+   #include <crt_externs.h>
+@@ -272,7 +274,12 @@ static int util_posix_fallocate(int fd, off_t offset, off_t len) {
+   }
+   return -1;
+ #else
++#ifndef SERENITY
+   return posix_fallocate(fd, offset, len);
++#else
++  // YOLO
++  return 0;
++#endif
+ #endif
+ }
+ 
+@@ -418,6 +425,7 @@ void os::Posix::print_load_average(outputStream* st) {
+ // unfortunately it does not work on macOS and Linux because the utx chain has no entry
+ // for reboot at least on my test machines
+ void os::Posix::print_uptime_info(outputStream* st) {
++#ifndef SERENITY
+   int bootsec = -1;
+   int currsec = time(NULL);
+   struct utmpx* ent;
+@@ -432,6 +440,9 @@ void os::Posix::print_uptime_info(outputStream* st) {
+   if (bootsec != -1) {
+     os::print_dhm(st, "OS uptime:", (long) (currsec-bootsec));
+   }
++#else
++    st->print("OS uptime: not implemented");
++#endif
+ }
+ 
+ static void print_rlimit(outputStream* st, const char* msg,
+@@ -470,7 +481,9 @@ void os::Posix::print_rlimit_info(outputStream* st) {
+ 
+   print_rlimit(st, ", THREADS", RLIMIT_THREADS);
+ #else
++#ifndef SERENITY
+   print_rlimit(st, ", NPROC", RLIMIT_NPROC);
++#endif
+ #endif
+ 
+   print_rlimit(st, ", NOFILE", RLIMIT_NOFILE);
+@@ -638,7 +651,11 @@ void os::dll_unload(void *lib) {
+ }
+ 
+ jlong os::lseek(int fd, jlong offset, int whence) {
++#ifdef SERENITY
++  return (jlong) ::lseek(fd, offset, whence);
++#else
+   return (jlong) BSD_ONLY(::lseek) NOT_BSD(::lseek64)(fd, offset, whence);
++#endif
+ }
+ 
+ int os::fsync(int fd) {
+@@ -646,7 +663,11 @@ int os::fsync(int fd) {
+ }
+ 
+ int os::ftruncate(int fd, jlong length) {
+-   return BSD_ONLY(::ftruncate) NOT_BSD(::ftruncate64)(fd, length);
++#ifdef SERENITY
++  return ::ftruncate(fd, length);
++#else
++  return BSD_ONLY(::ftruncate) NOT_BSD(::ftruncate64)(fd, length);
++#endif
+ }
+ 
+ const char* os::get_current_directory(char *buf, size_t buflen) {
+diff --git a/src/hotspot/os/posix/signals_posix.cpp b/src/hotspot/os/posix/signals_posix.cpp
+index 2c020a794..9f3316f5b 100644
+--- a/src/hotspot/os/posix/signals_posix.cpp
++++ b/src/hotspot/os/posix/signals_posix.cpp
+@@ -552,6 +552,8 @@ public:
+ #define JVM_HANDLE_XXX_SIGNAL JVM_handle_aix_signal
+ #elif defined(LINUX)
+ #define JVM_HANDLE_XXX_SIGNAL JVM_handle_linux_signal
++#elif defined(SERENITY)
++#define JVM_HANDLE_XXX_SIGNAL JVM_handle_serenity_signal
+ #else
+ #error who are you?
+ #endif
+@@ -933,8 +935,10 @@ static bool get_signal_code_description(const siginfo_t* si, enum_sigcode_desc_t
+     { SIGFPE,  FPE_FLTRES,   "FPE_FLTRES",   "Floating-point inexact result." },
+     { SIGFPE,  FPE_FLTINV,   "FPE_FLTINV",   "Invalid floating-point operation." },
+     { SIGFPE,  FPE_FLTSUB,   "FPE_FLTSUB",   "Subscript out of range." },
++#ifndef SERENITY
+     { SIGSEGV, SEGV_MAPERR,  "SEGV_MAPERR",  "Address not mapped to object." },
+     { SIGSEGV, SEGV_ACCERR,  "SEGV_ACCERR",  "Invalid permissions for mapped object." },
++#endif
+ #if defined(AIX)
+     // no explanation found what keyerr would be
+     { SIGSEGV, SEGV_KEYERR,  "SEGV_KEYERR",  "key error" },
+@@ -942,11 +946,13 @@ static bool get_signal_code_description(const siginfo_t* si, enum_sigcode_desc_t
+ #if defined(IA64) && !defined(AIX)
+     { SIGSEGV, SEGV_PSTKOVF, "SEGV_PSTKOVF", "Paragraph stack overflow" },
+ #endif
++#ifndef SERENITY
+     { SIGBUS,  BUS_ADRALN,   "BUS_ADRALN",   "Invalid address alignment." },
+     { SIGBUS,  BUS_ADRERR,   "BUS_ADRERR",   "Nonexistent physical address." },
+     { SIGBUS,  BUS_OBJERR,   "BUS_OBJERR",   "Object-specific hardware error." },
+     { SIGTRAP, TRAP_BRKPT,   "TRAP_BRKPT",   "Process breakpoint." },
+     { SIGTRAP, TRAP_TRACE,   "TRAP_TRACE",   "Process trace trap." },
++#endif
+     { SIGCHLD, CLD_EXITED,   "CLD_EXITED",   "Child has exited." },
+     { SIGCHLD, CLD_KILLED,   "CLD_KILLED",   "Child has terminated abnormally and did not create a core file." },
+     { SIGCHLD, CLD_DUMPED,   "CLD_DUMPED",   "Child has terminated abnormally and created a core file." },
+@@ -967,11 +973,17 @@ static bool get_signal_code_description(const siginfo_t* si, enum_sigcode_desc_t
+   const struct {
+     int code; const char* s_code; const char* s_desc;
+   } t2 [] = {
++    { SIGTRAP,      "SIGTRAP",   "SIGTRAP FIXME" },
++    { SIGBUS,       "SIGBUS",   "SIGBUS FIXME" },
++    { SIGILL,       "SIGILL",   "Illegal opcode FIXME." },
++    { SIGSEGV,      "SIGSEGV",  "SIGSEGV FIXME" },
++#ifndef SERENITY
+     { SI_USER,      "SI_USER",     "Signal sent by kill()." },
+     { SI_QUEUE,     "SI_QUEUE",    "Signal sent by the sigqueue()." },
+     { SI_TIMER,     "SI_TIMER",    "Signal generated by expiration of a timer set by timer_settime()." },
+     { SI_ASYNCIO,   "SI_ASYNCIO",  "Signal generated by completion of an asynchronous I/O request." },
+     { SI_MESGQ,     "SI_MESGQ",    "Signal generated by arrival of a message on an empty message queue." },
++#endif
+     // Linux specific
+ #ifdef SI_TKILL
+     { SI_TKILL,     "SI_TKILL",    "Signal sent by tkill (pthread_kill)" },
+diff --git a/src/hotspot/share/runtime/os.cpp b/src/hotspot/share/runtime/os.cpp
+index 9b8e667f9..4e9a5f0e6 100644
+--- a/src/hotspot/share/runtime/os.cpp
++++ b/src/hotspot/share/runtime/os.cpp
+@@ -155,7 +155,7 @@ char* os::iso8601_time(jlong milliseconds_since_19700101, char* buffer, size_t b
+   // No offset when dealing with UTC
+   time_t UTC_to_local = 0;
+   if (!utc) {
+-#if defined(_ALLBSD_SOURCE) || defined(_GNU_SOURCE)
++#if (defined(_ALLBSD_SOURCE) || defined(_GNU_SOURCE)) && !defined(SERENITY)
+     UTC_to_local = -(time_struct.tm_gmtoff);
+ #elif defined(_WINDOWS)
+     long zone;
+@@ -1502,6 +1502,7 @@ size_t os::page_size_for_region_unaligned(size_t region_size, size_t min_pages)
+ }
+ 
+ static const char* errno_to_string (int e, bool short_text) {
++#ifndef SERENITY
+   #define ALL_SHARED_ENUMS(X) \
+     X(E2BIG, "Argument list too long") \
+     X(EACCES, "Permission denied") \
+@@ -1579,6 +1580,9 @@ static const char* errno_to_string (int e, bool short_text) {
+     X(ETXTBSY, "Text file busy") \
+     X(EWOULDBLOCK, "Operation would block") \
+     X(EXDEV, "Cross-device link")
++#else
++  #define ALL_SHARED_ENUMS(X) ENUMERATE_ERRNO_CODES(X)
++#endif
+ 
+   #define DEFINE_ENTRY(e, text) { e, #e, text },
+ 
+diff --git a/src/hotspot/share/runtime/os.hpp b/src/hotspot/share/runtime/os.hpp
+index 7eaaa9db9..50591f707 100644
+--- a/src/hotspot/share/runtime/os.hpp
++++ b/src/hotspot/share/runtime/os.hpp
+@@ -468,7 +468,7 @@ class os: AllStatic {
+   // need special-case handling of the primordial thread if it attaches
+   // to the VM.
+   static bool is_primordial_thread(void)
+-#if defined(_WINDOWS) || defined(BSD)
++#if defined(_WINDOWS) || defined(BSD) || defined(SERENITY)
+     // No way to identify the primordial thread.
+     { return false; }
+ #else
+diff --git a/src/hotspot/share/runtime/semaphore.hpp b/src/hotspot/share/runtime/semaphore.hpp
+index 0e19c101d..afc007e7a 100644
+--- a/src/hotspot/share/runtime/semaphore.hpp
++++ b/src/hotspot/share/runtime/semaphore.hpp
+@@ -28,7 +28,7 @@
+ #include "memory/allocation.hpp"
+ #include "utilities/globalDefinitions.hpp"
+ 
+-#if defined(LINUX) || defined(AIX)
++#if defined(LINUX) || defined(AIX) || defined(SERENITY)
+ # include "semaphore_posix.hpp"
+ #elif defined(BSD)
+ # include "semaphore_bsd.hpp"
+diff --git a/src/hotspot/share/utilities/globalDefinitions.hpp b/src/hotspot/share/utilities/globalDefinitions.hpp
+index 082a3272c..65157b52e 100644
+--- a/src/hotspot/share/utilities/globalDefinitions.hpp
++++ b/src/hotspot/share/utilities/globalDefinitions.hpp
+@@ -1209,5 +1209,9 @@ template<typename K> bool primitive_equals(const K& k0, const K& k1) {
+   return k0 == k1;
+ }
+ 
++#ifdef SERENITY
++#define MAX2(a,b)          ((a)>(b)?(a):(b))
++#define alloca(p)              __builtin_alloca(p)
++#endif
+ 
+ #endif // SHARE_UTILITIES_GLOBALDEFINITIONS_HPP
+diff --git a/src/hotspot/share/utilities/globalDefinitions_gcc.hpp b/src/hotspot/share/utilities/globalDefinitions_gcc.hpp
+index 30cca9ee7..7cac45142 100644
+--- a/src/hotspot/share/utilities/globalDefinitions_gcc.hpp
++++ b/src/hotspot/share/utilities/globalDefinitions_gcc.hpp
+@@ -48,7 +48,7 @@
+ #include <limits.h>
+ #include <errno.h>
+ 
+-#if defined(LINUX) || defined(_ALLBSD_SOURCE)
++#if defined(LINUX) || defined(_ALLBSD_SOURCE) || defined(SERENITY)
+ #include <inttypes.h>
+ #include <signal.h>
+ #ifndef __OpenBSD__
+@@ -79,7 +79,7 @@
+   #define NULL_WORD  NULL
+ #endif
+ 
+-#if !defined(LINUX) && !defined(_ALLBSD_SOURCE)
++#if !defined(LINUX) && !defined(_ALLBSD_SOURCE) && !defined(SERENITY)
+ // Compiler-specific primitive types
+ typedef unsigned short     uint16_t;
+ #ifndef _UINT32_T
+@@ -111,7 +111,7 @@ typedef uint64_t julong;
+ // checking for nanness
+ #if defined(__APPLE__)
+ inline int g_isnan(double f) { return isnan(f); }
+-#elif defined(LINUX) || defined(_ALLBSD_SOURCE)
++#elif defined(LINUX) || defined(_ALLBSD_SOURCE) || defined(SERENITY)
+ inline int g_isnan(float  f) { return isnan(f); }
+ inline int g_isnan(double f) { return isnan(f); }
+ #else
+diff --git a/src/hotspot/share/utilities/ostream.cpp b/src/hotspot/share/utilities/ostream.cpp
+index 04995064f..e25c3a429 100644
+--- a/src/hotspot/share/utilities/ostream.cpp
++++ b/src/hotspot/share/utilities/ostream.cpp
+@@ -1065,7 +1065,7 @@ bufferedStream::~bufferedStream() {
+ 
+ #ifndef PRODUCT
+ 
+-#if defined(LINUX) || defined(AIX) || defined(_ALLBSD_SOURCE)
++#if defined(LINUX) || defined(AIX) || defined(_ALLBSD_SOURCE) || defined(SERENITY)
+ #include <sys/types.h>
+ #include <sys/socket.h>
+ #include <netinet/in.h>

+ 2394 - 0
Ports/OpenJDK/patches/0006-Add-serenity-specific-modules-to-java.base-and-jdk.a.patch

@@ -0,0 +1,2394 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Timur Sultanov <sultanovts@yandex.ru>
+Date: Sun, 12 Jun 2022 15:58:40 -0600
+Subject: [PATCH] Add serenity-specific modules to java.base and jdk.attach
+
+It would be nice to re-direct the build to the same files *BSD use, but
+for now we've got our own copy
+
+Co-Authored-By: Andrew Kaster <akaster@serenityos.org>
+---
+ .../DefaultAsynchronousChannelProvider.java   |  47 ++
+ .../sun/nio/ch/DefaultSelectorProvider.java   |  54 ++
+ .../SerenityAsynchronousChannelProvider.java  |  91 +++
+ .../classes/sun/nio/ch/SerenityPollPort.java  | 538 ++++++++++++++++++
+ .../sun/nio/fs/DefaultFileSystemProvider.java |  53 ++
+ .../classes/sun/nio/fs/SerenityFileStore.java | 105 ++++
+ .../sun/nio/fs/SerenityFileSystem.java        |  94 +++
+ .../nio/fs/SerenityFileSystemProvider.java    |  52 ++
+ .../sun/nio/fs/SerenityNativeDispatcher.java  |  49 ++
+ .../serenity/native/libnet/serenity_close.c   | 458 +++++++++++++++
+ .../sun/tools/attach/AttachProviderImpl.java  |  82 +++
+ .../sun/tools/attach/VirtualMachineImpl.java  | 326 +++++++++++
+ .../native/libattach/VirtualMachineImpl.c     | 328 +++++++++++
+ 13 files changed, 2277 insertions(+)
+ create mode 100644 src/java.base/serenity/classes/sun/nio/ch/DefaultAsynchronousChannelProvider.java
+ create mode 100644 src/java.base/serenity/classes/sun/nio/ch/DefaultSelectorProvider.java
+ create mode 100644 src/java.base/serenity/classes/sun/nio/ch/SerenityAsynchronousChannelProvider.java
+ create mode 100644 src/java.base/serenity/classes/sun/nio/ch/SerenityPollPort.java
+ create mode 100644 src/java.base/serenity/classes/sun/nio/fs/DefaultFileSystemProvider.java
+ create mode 100644 src/java.base/serenity/classes/sun/nio/fs/SerenityFileStore.java
+ create mode 100644 src/java.base/serenity/classes/sun/nio/fs/SerenityFileSystem.java
+ create mode 100644 src/java.base/serenity/classes/sun/nio/fs/SerenityFileSystemProvider.java
+ create mode 100644 src/java.base/serenity/classes/sun/nio/fs/SerenityNativeDispatcher.java
+ create mode 100644 src/java.base/serenity/native/libnet/serenity_close.c
+ create mode 100644 src/jdk.attach/serenity/classes/sun/tools/attach/AttachProviderImpl.java
+ create mode 100644 src/jdk.attach/serenity/classes/sun/tools/attach/VirtualMachineImpl.java
+ create mode 100644 src/jdk.attach/serenity/native/libattach/VirtualMachineImpl.c
+
+diff --git a/src/java.base/serenity/classes/sun/nio/ch/DefaultAsynchronousChannelProvider.java b/src/java.base/serenity/classes/sun/nio/ch/DefaultAsynchronousChannelProvider.java
+new file mode 100644
+index 000000000..7a7bfe089
+--- /dev/null
++++ b/src/java.base/serenity/classes/sun/nio/ch/DefaultAsynchronousChannelProvider.java
+@@ -0,0 +1,47 @@
++/*
++ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
++ *
++ * This code is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License version 2 only, as
++ * published by the Free Software Foundation.  Oracle designates this
++ * particular file as subject to the "Classpath" exception as provided
++ * by Oracle in the LICENSE file that accompanied this code.
++ *
++ * This code is distributed in the hope that it will be useful, but WITHOUT
++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
++ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
++ * version 2 for more details (a copy is included in the LICENSE file that
++ * accompanied this code).
++ *
++ * You should have received a copy of the GNU General Public License version
++ * 2 along with this work; if not, write to the Free Software Foundation,
++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
++ *
++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
++ * or visit www.oracle.com if you need additional information or have any
++ * questions.
++ */
++
++package sun.nio.ch;
++
++import java.nio.channels.spi.AsynchronousChannelProvider;
++
++/**
++ * Creates this platform's default AsynchronousChannelProvider
++ */
++
++public class DefaultAsynchronousChannelProvider {
++
++    /**
++     * Prevent instantiation.
++     */
++    private DefaultAsynchronousChannelProvider() { }
++
++    /**
++     * Returns the default AsynchronousChannelProvider.
++     */
++    public static AsynchronousChannelProvider create() {
++        return new SerenityAsynchronousChannelProvider();
++    }
++}
+diff --git a/src/java.base/serenity/classes/sun/nio/ch/DefaultSelectorProvider.java b/src/java.base/serenity/classes/sun/nio/ch/DefaultSelectorProvider.java
+new file mode 100644
+index 000000000..86d3ade19
+--- /dev/null
++++ b/src/java.base/serenity/classes/sun/nio/ch/DefaultSelectorProvider.java
+@@ -0,0 +1,54 @@
++/*
++ * Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.
++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
++ *
++ * This code is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License version 2 only, as
++ * published by the Free Software Foundation.  Oracle designates this
++ * particular file as subject to the "Classpath" exception as provided
++ * by Oracle in the LICENSE file that accompanied this code.
++ *
++ * This code is distributed in the hope that it will be useful, but WITHOUT
++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
++ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
++ * version 2 for more details (a copy is included in the LICENSE file that
++ * accompanied this code).
++ *
++ * You should have received a copy of the GNU General Public License version
++ * 2 along with this work; if not, write to the Free Software Foundation,
++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
++ *
++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
++ * or visit www.oracle.com if you need additional information or have any
++ * questions.
++ */
++
++package sun.nio.ch;
++
++import java.security.AccessController;
++import java.security.PrivilegedAction;
++
++/**
++ * Creates this platform's default SelectorProvider
++ */
++
++@SuppressWarnings("removal")
++public class DefaultSelectorProvider {
++    private static final SelectorProviderImpl INSTANCE;
++    static {
++        PrivilegedAction<SelectorProviderImpl> pa = PollSelectorProvider::new;
++        INSTANCE = AccessController.doPrivileged(pa);
++    }
++
++    /**
++     * Prevent instantiation.
++     */
++    private DefaultSelectorProvider() { }
++
++    /**
++     * Returns the default SelectorProvider implementation.
++     */
++    public static SelectorProviderImpl get() {
++        return INSTANCE;
++    }
++}
+\ No newline at end of file
+diff --git a/src/java.base/serenity/classes/sun/nio/ch/SerenityAsynchronousChannelProvider.java b/src/java.base/serenity/classes/sun/nio/ch/SerenityAsynchronousChannelProvider.java
+new file mode 100644
+index 000000000..2daa2cca4
+--- /dev/null
++++ b/src/java.base/serenity/classes/sun/nio/ch/SerenityAsynchronousChannelProvider.java
+@@ -0,0 +1,91 @@
++/*
++ * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
++ * Copyright (c) 2012 SAP SE. All rights reserved.
++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
++ *
++ * This code is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License version 2 only, as
++ * published by the Free Software Foundation.  Oracle designates this
++ * particular file as subject to the "Classpath" exception as provided
++ * by Oracle in the LICENSE file that accompanied this code.
++ *
++ * This code is distributed in the hope that it will be useful, but WITHOUT
++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
++ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
++ * version 2 for more details (a copy is included in the LICENSE file that
++ * accompanied this code).
++ *
++ * You should have received a copy of the GNU General Public License version
++ * 2 along with this work; if not, write to the Free Software Foundation,
++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
++ *
++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
++ * or visit www.oracle.com if you need additional information or have any
++ * questions.
++ */
++
++package sun.nio.ch;
++
++import java.nio.channels.*;
++import java.nio.channels.spi.AsynchronousChannelProvider;
++import java.util.concurrent.ExecutorService;
++import java.util.concurrent.ThreadFactory;
++import java.io.IOException;
++
++public class SerenityAsynchronousChannelProvider
++    extends AsynchronousChannelProvider
++{
++    private static volatile SerenityPollPort defaultPort;
++
++    private SerenityPollPort defaultEventPort() throws IOException {
++        if (defaultPort == null) {
++            synchronized (SerenityAsynchronousChannelProvider.class) {
++                if (defaultPort == null) {
++                    defaultPort = new SerenityPollPort(this, ThreadPool.getDefault()).start();
++                }
++            }
++        }
++        return defaultPort;
++    }
++
++    public SerenityAsynchronousChannelProvider() {
++    }
++
++    @Override
++    public AsynchronousChannelGroup openAsynchronousChannelGroup(int nThreads, ThreadFactory factory)
++        throws IOException
++    {
++        return new SerenityPollPort(this, ThreadPool.create(nThreads, factory)).start();
++    }
++
++    @Override
++    public AsynchronousChannelGroup openAsynchronousChannelGroup(ExecutorService executor, int initialSize)
++        throws IOException
++    {
++        return new SerenityPollPort(this, ThreadPool.wrap(executor, initialSize)).start();
++    }
++
++    private Port toPort(AsynchronousChannelGroup group) throws IOException {
++        if (group == null) {
++            return defaultEventPort();
++        } else {
++            if (!(group instanceof SerenityPollPort))
++                throw new IllegalChannelGroupException();
++            return (Port)group;
++        }
++    }
++
++    @Override
++    public AsynchronousServerSocketChannel openAsynchronousServerSocketChannel(AsynchronousChannelGroup group)
++        throws IOException
++    {
++        return new UnixAsynchronousServerSocketChannelImpl(toPort(group));
++    }
++
++    @Override
++    public AsynchronousSocketChannel openAsynchronousSocketChannel(AsynchronousChannelGroup group)
++        throws IOException
++    {
++        return new UnixAsynchronousSocketChannelImpl(toPort(group));
++    }
++}
+diff --git a/src/java.base/serenity/classes/sun/nio/ch/SerenityPollPort.java b/src/java.base/serenity/classes/sun/nio/ch/SerenityPollPort.java
+new file mode 100644
+index 000000000..0894d1814
+--- /dev/null
++++ b/src/java.base/serenity/classes/sun/nio/ch/SerenityPollPort.java
+@@ -0,0 +1,538 @@
++/*
++ * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
++ * Copyright (c) 2012 SAP SE. All rights reserved.
++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
++ *
++ * This code is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License version 2 only, as
++ * published by the Free Software Foundation.  Oracle designates this
++ * particular file as subject to the "Classpath" exception as provided
++ * by Oracle in the LICENSE file that accompanied this code.
++ *
++ * This code is distributed in the hope that it will be useful, but WITHOUT
++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
++ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
++ * version 2 for more details (a copy is included in the LICENSE file that
++ * accompanied this code).
++ *
++ * You should have received a copy of the GNU General Public License version
++ * 2 along with this work; if not, write to the Free Software Foundation,
++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
++ *
++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
++ * or visit www.oracle.com if you need additional information or have any
++ * questions.
++ */
++
++package sun.nio.ch;
++
++import java.nio.channels.spi.AsynchronousChannelProvider;
++import java.io.IOException;
++import java.util.HashSet;
++import java.util.Iterator;
++import java.util.concurrent.ArrayBlockingQueue;
++import java.util.concurrent.RejectedExecutionException;
++import java.util.concurrent.atomic.AtomicInteger;
++import java.util.concurrent.locks.ReentrantLock;
++import jdk.internal.misc.Unsafe;
++
++/**
++ * AsynchronousChannelGroup implementation based on the AIX pollset framework.
++ */
++final class SerenityPollPort
++    extends Port
++{
++    private static final Unsafe unsafe = Unsafe.getUnsafe();
++
++    static {
++        IOUtil.load();
++        init();
++    }
++
++    /**
++     * struct pollfd {
++     *     int fd;
++     *     short events;
++     *     short revents;
++     * }
++     */
++    private static final int SIZEOF_POLLFD    = eventSize();
++    private static final int OFFSETOF_EVENTS  = eventsOffset();
++    private static final int OFFSETOF_REVENTS = reventsOffset();
++    private static final int OFFSETOF_FD      = fdOffset();
++
++    // opcodes
++    private static final int PS_ADD     = 0x0;
++    private static final int PS_MOD     = 0x1;
++    private static final int PS_DELETE  = 0x2;
++
++    // maximum number of events to poll at a time
++    private static final int MAX_POLL_EVENTS = 512;
++
++    // pollset ID
++    private final int pollset;
++
++    // true if port is closed
++    private boolean closed;
++
++    // socket pair used for wakeup
++    private final int sp[];
++
++    // socket pair used to indicate pending pollsetCtl calls
++    // Background info: pollsetCtl blocks when another thread is in a pollsetPoll call.
++    private final int ctlSp[];
++
++    // number of wakeups pending
++    private final AtomicInteger wakeupCount = new AtomicInteger();
++
++    // address of the poll array passed to pollset_poll
++    private final long address;
++
++    // encapsulates an event for a channel
++    static class Event {
++        final PollableChannel channel;
++        final int events;
++
++        Event(PollableChannel channel, int events) {
++            this.channel = channel;
++            this.events = events;
++        }
++
++        PollableChannel channel()   { return channel; }
++        int events()                { return events; }
++    }
++
++    // queue of events for cases that a polling thread dequeues more than one
++    // event
++    private final ArrayBlockingQueue<Event> queue;
++    private final Event NEED_TO_POLL = new Event(null, 0);
++    private final Event EXECUTE_TASK_OR_SHUTDOWN = new Event(null, 0);
++    private final Event CONTINUE_AFTER_CTL_EVENT = new Event(null, 0);
++
++    // encapsulates a pollset control event for a file descriptor
++    static class ControlEvent {
++        final int fd;
++        final int events;
++        final boolean removeOnly;
++        int error = 0;
++
++        ControlEvent(int fd, int events, boolean removeOnly) {
++            this.fd = fd;
++            this.events = events;
++            this.removeOnly = removeOnly;
++        }
++
++        int fd()                 { return fd; }
++        int events()             { return events; }
++        boolean removeOnly()     { return removeOnly; }
++        int error()              { return error; }
++        void setError(int error) { this.error = error; }
++    }
++
++    // queue of control events that need to be processed
++    // (this object is also used for synchronization)
++    private final HashSet<ControlEvent> controlQueue = new HashSet<ControlEvent>();
++
++    // lock used to check whether a poll operation is ongoing
++    private final ReentrantLock controlLock = new ReentrantLock();
++
++    SerenityPollPort(AsynchronousChannelProvider provider, ThreadPool pool)
++        throws IOException
++    {
++        super(provider, pool);
++
++        // open pollset
++        this.pollset = pollsetCreate();
++
++        // create socket pair for wakeup mechanism
++        int[] sv = new int[2];
++        try {
++            socketpair(sv);
++            // register one end with pollset
++            pollsetCtl(pollset, PS_ADD, sv[0], Net.POLLIN);
++        } catch (IOException x) {
++            pollsetDestroy(pollset);
++            throw x;
++        }
++        this.sp = sv;
++
++        // create socket pair for pollset control mechanism
++        sv = new int[2];
++        try {
++            socketpair(sv);
++            // register one end with pollset
++            pollsetCtl(pollset, PS_ADD, sv[0], Net.POLLIN);
++        } catch (IOException x) {
++            pollsetDestroy(pollset);
++            throw x;
++        }
++        this.ctlSp = sv;
++
++        // allocate the poll array
++        this.address = allocatePollArray(MAX_POLL_EVENTS);
++
++        // create the queue and offer the special event to ensure that the first
++        // threads polls
++        this.queue = new ArrayBlockingQueue<Event>(MAX_POLL_EVENTS);
++        this.queue.offer(NEED_TO_POLL);
++    }
++
++    SerenityPollPort start() {
++        startThreads(new EventHandlerTask());
++        return this;
++    }
++
++    /**
++     * Release all resources
++     */
++    private void implClose() {
++        synchronized (this) {
++            if (closed)
++                return;
++            closed = true;
++        }
++        freePollArray(address);
++        close0(sp[0]);
++        close0(sp[1]);
++        close0(ctlSp[0]);
++        close0(ctlSp[1]);
++        pollsetDestroy(pollset);
++    }
++
++    private void wakeup() {
++        if (wakeupCount.incrementAndGet() == 1) {
++            // write byte to socketpair to force wakeup
++            try {
++                interrupt(sp[1]);
++            } catch (IOException x) {
++                throw new AssertionError(x);
++            }
++        }
++    }
++
++    @Override
++    void executeOnHandlerTask(Runnable task) {
++        synchronized (this) {
++            if (closed)
++                throw new RejectedExecutionException();
++            offerTask(task);
++            wakeup();
++        }
++    }
++
++    @Override
++    void shutdownHandlerTasks() {
++        /*
++         * If no tasks are running then just release resources; otherwise
++         * write to the one end of the socketpair to wakeup any polling threads.
++         */
++        int nThreads = threadCount();
++        if (nThreads == 0) {
++            implClose();
++        } else {
++            // send interrupt to each thread
++            while (nThreads-- > 0) {
++                wakeup();
++            }
++        }
++    }
++
++    // invoke by clients to register a file descriptor
++    @Override
++    void startPoll(int fd, int events) {
++        queueControlEvent(new ControlEvent(fd, events, false));
++    }
++
++    // Callback method for implementations that need special handling when fd is removed
++    @Override
++    protected void preUnregister(int fd) {
++        queueControlEvent(new ControlEvent(fd, 0, true));
++    }
++
++    // Add control event into queue and wait for completion.
++    // In case the control lock is free, this method also tries to apply the control change directly.
++    private void queueControlEvent(ControlEvent ev) {
++        // pollsetCtl blocks when a poll call is ongoing. This is very probable.
++        // Therefore we let the polling thread do the pollsetCtl call.
++        synchronized (controlQueue) {
++            controlQueue.add(ev);
++            // write byte to socketpair to force wakeup
++            try {
++                interrupt(ctlSp[1]);
++            } catch (IOException x) {
++                throw new AssertionError(x);
++            }
++            do {
++                // Directly empty queue if no poll call is ongoing.
++                if (controlLock.tryLock()) {
++                    try {
++                        processControlQueue();
++                    } finally {
++                        controlLock.unlock();
++                    }
++                } else {
++                    try {
++                        // Do not starve in case the polling thread returned before
++                        // we could write to ctlSp[1] but the polling thread did not
++                        // release the control lock until we checked. Therefore, use
++                        // a timed wait for the time being.
++                        controlQueue.wait(100);
++                    } catch (InterruptedException e) {
++                        // ignore exception and try again
++                    }
++                }
++            } while (controlQueue.contains(ev));
++        }
++        if (ev.error() != 0) {
++            throw new AssertionError();
++        }
++    }
++
++    // Process all events currently stored in the control queue.
++    private void processControlQueue() {
++        synchronized (controlQueue) {
++            // TODO: this is ripped straight out of AIX implementation, who knows if it will work for Serenity
++            Iterator<ControlEvent> iter = controlQueue.iterator();
++            while (iter.hasNext()) {
++                ControlEvent ev = iter.next();
++                pollsetCtl(pollset, PS_DELETE, ev.fd(), 0);
++                if (!ev.removeOnly()) {
++                    ev.setError(pollsetCtl(pollset, PS_MOD, ev.fd(), ev.events()));
++                }
++                iter.remove();
++            }
++            controlQueue.notifyAll();
++        }
++    }
++
++    /*
++     * Task to process events from pollset and dispatch to the channel's
++     * onEvent handler.
++     *
++     * Events are retreived from pollset in batch and offered to a BlockingQueue
++     * where they are consumed by handler threads. A special "NEED_TO_POLL"
++     * event is used to signal one consumer to re-poll when all events have
++     * been consumed.
++     */
++    private class EventHandlerTask implements Runnable {
++        private Event poll() throws IOException {
++            try {
++                for (;;) {
++                    int n;
++                    controlLock.lock();
++                    try {
++                        n = pollsetPoll(pollset, address, MAX_POLL_EVENTS);
++                    } finally {
++                        controlLock.unlock();
++                    }
++                    /*
++                     * 'n' events have been read. Here we map them to their
++                     * corresponding channel in batch and queue n-1 so that
++                     * they can be handled by other handler threads. The last
++                     * event is handled by this thread (and so is not queued).
++                     */
++                    fdToChannelLock.readLock().lock();
++                    try {
++                        while (n-- > 0) {
++                            long eventAddress = getEvent(address, n);
++                            int fd = getDescriptor(eventAddress);
++
++                            // To emulate one shot semantic we need to remove
++                            // the file descriptor here.
++                            if (fd != sp[0] && fd != ctlSp[0]) {
++                                synchronized (controlQueue) {
++                                    pollsetCtl(pollset, PS_DELETE, fd, 0);
++                                }
++                            }
++
++                            // wakeup
++                            if (fd == sp[0]) {
++                                if (wakeupCount.decrementAndGet() == 0) {
++                                    // no more wakeups so drain pipe
++                                    drain1(sp[0]);
++                                }
++
++                                // queue special event if there are more events
++                                // to handle.
++                                if (n > 0) {
++                                    queue.offer(EXECUTE_TASK_OR_SHUTDOWN);
++                                    continue;
++                                }
++                                return EXECUTE_TASK_OR_SHUTDOWN;
++                            }
++
++                            // wakeup to process control event
++                            if (fd == ctlSp[0]) {
++                                synchronized (controlQueue) {
++                                    drain1(ctlSp[0]);
++                                    processControlQueue();
++                                }
++                                if (n > 0) {
++                                    continue;
++                                }
++                                return CONTINUE_AFTER_CTL_EVENT;
++                            }
++
++                            PollableChannel channel = fdToChannel.get(fd);
++                            if (channel != null) {
++                                int events = getRevents(eventAddress);
++                                Event ev = new Event(channel, events);
++
++                                // n-1 events are queued; This thread handles
++                                // the last one except for the wakeup
++                                if (n > 0) {
++                                    queue.offer(ev);
++                                } else {
++                                    return ev;
++                                }
++                            }
++                        }
++                    } finally {
++                        fdToChannelLock.readLock().unlock();
++                    }
++                }
++            } finally {
++                // to ensure that some thread will poll when all events have
++                // been consumed
++                queue.offer(NEED_TO_POLL);
++            }
++        }
++
++        public void run() {
++            Invoker.GroupAndInvokeCount myGroupAndInvokeCount =
++                Invoker.getGroupAndInvokeCount();
++            final boolean isPooledThread = (myGroupAndInvokeCount != null);
++            boolean replaceMe = false;
++            Event ev;
++            try {
++                for (;;) {
++                    // reset invoke count
++                    if (isPooledThread)
++                        myGroupAndInvokeCount.resetInvokeCount();
++
++                    try {
++                        replaceMe = false;
++                        ev = queue.take();
++
++                        // no events and this thread has been "selected" to
++                        // poll for more.
++                        if (ev == NEED_TO_POLL) {
++                            try {
++                                ev = poll();
++                            } catch (IOException x) {
++                                x.printStackTrace();
++                                return;
++                            }
++                        }
++                    } catch (InterruptedException x) {
++                        continue;
++                    }
++
++                    // contine after we processed a control event
++                    if (ev == CONTINUE_AFTER_CTL_EVENT) {
++                        continue;
++                    }
++
++                    // handle wakeup to execute task or shutdown
++                    if (ev == EXECUTE_TASK_OR_SHUTDOWN) {
++                        Runnable task = pollTask();
++                        if (task == null) {
++                            // shutdown request
++                            return;
++                        }
++                        // run task (may throw error/exception)
++                        replaceMe = true;
++                        task.run();
++                        continue;
++                    }
++
++                    // process event
++                    try {
++                        ev.channel().onEvent(ev.events(), isPooledThread);
++                    } catch (Error x) {
++                        replaceMe = true; throw x;
++                    } catch (RuntimeException x) {
++                        replaceMe = true; throw x;
++                    }
++                }
++            } finally {
++                // last handler to exit when shutdown releases resources
++                int remaining = threadExit(this, replaceMe);
++                if (remaining == 0 && isShutdown()) {
++                    implClose();
++                }
++            }
++        }
++    }
++
++    /**
++     * Allocates a poll array to handle up to {@code count} events.
++     */
++    private static long allocatePollArray(int count) {
++        return unsafe.allocateMemory(count * SIZEOF_POLLFD);
++    }
++
++    /**
++     * Free a poll array
++     */
++    private static void freePollArray(long address) {
++        unsafe.freeMemory(address);
++    }
++
++    /**
++     * Returns event[i];
++     */
++    private static long getEvent(long address, int i) {
++        return address + (SIZEOF_POLLFD*i);
++    }
++
++    /**
++     * Returns event->fd
++     */
++    private static int getDescriptor(long eventAddress) {
++        return unsafe.getInt(eventAddress + OFFSETOF_FD);
++    }
++
++    /**
++     * Returns event->events
++     */
++    private static int getEvents(long eventAddress) {
++        return unsafe.getChar(eventAddress + OFFSETOF_EVENTS);
++    }
++
++    /**
++     * Returns event->revents
++     */
++    private static int getRevents(long eventAddress) {
++        return unsafe.getChar(eventAddress + OFFSETOF_REVENTS);
++    }
++
++    // -- Native methods --
++
++    private static native void init();
++
++    private static native int eventSize();
++
++    private static native int eventsOffset();
++
++    private static native int reventsOffset();
++
++    private static native int fdOffset();
++
++    private static native int pollsetCreate() throws IOException;
++
++    private static native int pollsetCtl(int pollset, int opcode, int fd, int events);
++
++    private static native int pollsetPoll(int pollset, long pollAddress, int numfds)
++        throws IOException;
++
++    private static native void pollsetDestroy(int pollset);
++
++    private static native void socketpair(int[] sv) throws IOException;
++
++    private static native void interrupt(int fd) throws IOException;
++
++    private static native void drain1(int fd) throws IOException;
++
++    private static native void close0(int fd);
++}
+diff --git a/src/java.base/serenity/classes/sun/nio/fs/DefaultFileSystemProvider.java b/src/java.base/serenity/classes/sun/nio/fs/DefaultFileSystemProvider.java
+new file mode 100644
+index 000000000..b24f3de01
+--- /dev/null
++++ b/src/java.base/serenity/classes/sun/nio/fs/DefaultFileSystemProvider.java
+@@ -0,0 +1,53 @@
++/*
++ * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
++ *
++ * This code is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License version 2 only, as
++ * published by the Free Software Foundation.  Oracle designates this
++ * particular file as subject to the "Classpath" exception as provided
++ * by Oracle in the LICENSE file that accompanied this code.
++ *
++ * This code is distributed in the hope that it will be useful, but WITHOUT
++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
++ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
++ * version 2 for more details (a copy is included in the LICENSE file that
++ * accompanied this code).
++ *
++ * You should have received a copy of the GNU General Public License version
++ * 2 along with this work; if not, write to the Free Software Foundation,
++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
++ *
++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
++ * or visit www.oracle.com if you need additional information or have any
++ * questions.
++ */
++
++package sun.nio.fs;
++
++import java.nio.file.FileSystem;
++
++/**
++ * Creates this platform's default FileSystemProvider.
++ */
++
++public class DefaultFileSystemProvider {
++    private static final SerenityFileSystemProvider INSTANCE
++        = new SerenityFileSystemProvider();
++
++    private DefaultFileSystemProvider() { }
++
++    /**
++     * Returns the platform's default file system provider.
++     */
++    public static SerenityFileSystemProvider instance() {
++        return INSTANCE;
++    }
++
++    /**
++     * Returns the platform's default file system.
++     */
++    public static FileSystem theFileSystem() {
++        return INSTANCE.theFileSystem();
++    }
++}
+diff --git a/src/java.base/serenity/classes/sun/nio/fs/SerenityFileStore.java b/src/java.base/serenity/classes/sun/nio/fs/SerenityFileStore.java
+new file mode 100644
+index 000000000..3f408ec9b
+--- /dev/null
++++ b/src/java.base/serenity/classes/sun/nio/fs/SerenityFileStore.java
+@@ -0,0 +1,105 @@
++/*
++ * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
++ * Copyright (c) 2013 SAP SE. All rights reserved.
++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
++ *
++ * This code is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License version 2 only, as
++ * published by the Free Software Foundation.  Oracle designates this
++ * particular file as subject to the "Classpath" exception as provided
++ * by Oracle in the LICENSE file that accompanied this code.
++ *
++ * This code is distributed in the hope that it will be useful, but WITHOUT
++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
++ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
++ * version 2 for more details (a copy is included in the LICENSE file that
++ * accompanied this code).
++ *
++ * You should have received a copy of the GNU General Public License version
++ * 2 along with this work; if not, write to the Free Software Foundation,
++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
++ *
++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
++ * or visit www.oracle.com if you need additional information or have any
++ * questions.
++ */
++
++package sun.nio.fs;
++
++import java.nio.file.attribute.*;
++import java.util.*;
++import java.io.IOException;
++
++/**
++ * AIX implementation of FileStore
++ */
++
++class SerenityFileStore
++    extends UnixFileStore
++{
++
++    SerenityFileStore(UnixPath file) throws IOException {
++        super(file);
++    }
++
++    SerenityFileStore(UnixFileSystem fs, UnixMountEntry entry) throws IOException {
++        super(fs, entry);
++    }
++
++    /**
++     * Finds, and returns, the mount entry for the file system where the file
++     * resides.
++     */
++    @Override
++    UnixMountEntry findMountEntry() throws IOException {
++        SerenityFileSystem fs = (SerenityFileSystem)file().getFileSystem();
++
++        // step 1: get realpath
++        UnixPath path = null;
++        try {
++            byte[] rp = UnixNativeDispatcher.realpath(file());
++            path = new UnixPath(fs, rp);
++        } catch (UnixException x) {
++            x.rethrowAsIOException(file());
++        }
++
++        // step 2: find mount point
++        UnixPath parent = path.getParent();
++        while (parent != null) {
++            UnixFileAttributes attrs = null;
++            try {
++                attrs = UnixFileAttributes.get(parent, true);
++            } catch (UnixException x) {
++                x.rethrowAsIOException(parent);
++            }
++            if (attrs.dev() != dev())
++                break;
++            path = parent;
++            parent = parent.getParent();
++        }
++
++        // step 3: lookup mounted file systems
++        byte[] dir = path.asByteArray();
++        for (UnixMountEntry entry: fs.getMountEntries()) {
++            if (Arrays.equals(dir, entry.dir()))
++                return entry;
++        }
++
++        throw new IOException("Mount point not found");
++    }
++
++    @Override
++    protected boolean isExtendedAttributesEnabled(UnixPath path) {
++        return false;
++    }
++
++    @Override
++    public boolean supportsFileAttributeView(Class<? extends FileAttributeView> type) {
++        return super.supportsFileAttributeView(type);
++    }
++
++    @Override
++    public boolean supportsFileAttributeView(String name) {
++        return super.supportsFileAttributeView(name);
++    }
++}
+diff --git a/src/java.base/serenity/classes/sun/nio/fs/SerenityFileSystem.java b/src/java.base/serenity/classes/sun/nio/fs/SerenityFileSystem.java
+new file mode 100644
+index 000000000..bee588a7e
+--- /dev/null
++++ b/src/java.base/serenity/classes/sun/nio/fs/SerenityFileSystem.java
+@@ -0,0 +1,94 @@
++/*
++ * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
++ * Copyright (c) 2013 SAP SE. All rights reserved.
++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
++ *
++ * This code is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License version 2 only, as
++ * published by the Free Software Foundation.  Oracle designates this
++ * particular file as subject to the "Classpath" exception as provided
++ * by Oracle in the LICENSE file that accompanied this code.
++ *
++ * This code is distributed in the hope that it will be useful, but WITHOUT
++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
++ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
++ * version 2 for more details (a copy is included in the LICENSE file that
++ * accompanied this code).
++ *
++ * You should have received a copy of the GNU General Public License version
++ * 2 along with this work; if not, write to the Free Software Foundation,
++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
++ *
++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
++ * or visit www.oracle.com if you need additional information or have any
++ * questions.
++ */
++
++package sun.nio.fs;
++
++import java.nio.file.*;
++import java.nio.file.attribute.*;
++import java.io.IOException;
++import java.util.*;
++import static sun.nio.fs.SerenityNativeDispatcher.*;
++
++/**
++ * AIX implementation of FileSystem
++ */
++
++class SerenityFileSystem extends UnixFileSystem {
++
++    SerenityFileSystem(UnixFileSystemProvider provider, String dir) {
++        super(provider, dir);
++    }
++
++    @Override
++    public WatchService newWatchService()
++        throws IOException
++    {
++        return new PollingWatchService();
++    }
++
++    // lazy initialization of the list of supported attribute views
++    private static class SupportedFileFileAttributeViewsHolder {
++        static final Set<String> supportedFileAttributeViews =
++            supportedFileAttributeViews();
++        private static Set<String> supportedFileAttributeViews() {
++            Set<String> result = new HashSet<String>();
++            result.addAll(UnixFileSystem.standardFileAttributeViews());
++            return Collections.unmodifiableSet(result);
++        }
++    }
++
++    @Override
++    public Set<String> supportedFileAttributeViews() {
++        return SupportedFileFileAttributeViewsHolder.supportedFileAttributeViews;
++    }
++
++    @Override
++    void copyNonPosixAttributes(int ofd, int nfd) {
++        // TODO: Implement if needed.
++    }
++
++    /**
++     * Returns object to iterate over the mount entries returned by mntctl
++     */
++    @Override
++    Iterable<UnixMountEntry> getMountEntries() {
++        UnixMountEntry[] entries = null;
++        try {
++            entries = getmntctl();
++        } catch (UnixException x) {
++            // nothing we can do
++        }
++        if (entries == null) {
++            return Collections.emptyList();
++        }
++        return Arrays.asList(entries);
++    }
++
++    @Override
++    FileStore getFileStore(UnixMountEntry entry) throws IOException {
++        return new SerenityFileStore(this, entry);
++    }
++}
+diff --git a/src/java.base/serenity/classes/sun/nio/fs/SerenityFileSystemProvider.java b/src/java.base/serenity/classes/sun/nio/fs/SerenityFileSystemProvider.java
+new file mode 100644
+index 000000000..8582190af
+--- /dev/null
++++ b/src/java.base/serenity/classes/sun/nio/fs/SerenityFileSystemProvider.java
+@@ -0,0 +1,52 @@
++/*
++ * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
++ * Copyright (c) 2013 SAP SE. All rights reserved.
++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
++ *
++ * This code is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License version 2 only, as
++ * published by the Free Software Foundation.  Oracle designates this
++ * particular file as subject to the "Classpath" exception as provided
++ * by Oracle in the LICENSE file that accompanied this code.
++ *
++ * This code is distributed in the hope that it will be useful, but WITHOUT
++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
++ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
++ * version 2 for more details (a copy is included in the LICENSE file that
++ * accompanied this code).
++ *
++ * You should have received a copy of the GNU General Public License version
++ * 2 along with this work; if not, write to the Free Software Foundation,
++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
++ *
++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
++ * or visit www.oracle.com if you need additional information or have any
++ * questions.
++ */
++
++package sun.nio.fs;
++
++import java.io.IOException;
++
++/**
++ * Serenity implementation of FileSystemProvider
++ */
++
++class SerenityFileSystemProvider extends UnixFileSystemProvider {
++    public SerenityFileSystemProvider() {
++        super();
++    }
++
++    @Override
++    SerenityFileSystem newFileSystem(String dir) {
++        return new SerenityFileSystem(this, dir);
++    }
++
++    /**
++     * @see sun.nio.fs.UnixFileSystemProvider#getFileStore(sun.nio.fs.UnixPath)
++     */
++    @Override
++    SerenityFileStore getFileStore(UnixPath path) throws IOException {
++        return new SerenityFileStore(path);
++    }
++}
+diff --git a/src/java.base/serenity/classes/sun/nio/fs/SerenityNativeDispatcher.java b/src/java.base/serenity/classes/sun/nio/fs/SerenityNativeDispatcher.java
+new file mode 100644
+index 000000000..7c50b719c
+--- /dev/null
++++ b/src/java.base/serenity/classes/sun/nio/fs/SerenityNativeDispatcher.java
+@@ -0,0 +1,49 @@
++/*
++ * Copyright (c) 2008, 2019, Oracle and/or its affiliates. All rights reserved.
++ * Copyright (c) 2013 SAP SE. All rights reserved.
++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
++ *
++ * This code is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License version 2 only, as
++ * published by the Free Software Foundation.  Oracle designates this
++ * particular file as subject to the "Classpath" exception as provided
++ * by Oracle in the LICENSE file that accompanied this code.
++ *
++ * This code is distributed in the hope that it will be useful, but WITHOUT
++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
++ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
++ * version 2 for more details (a copy is included in the LICENSE file that
++ * accompanied this code).
++ *
++ * You should have received a copy of the GNU General Public License version
++ * 2 along with this work; if not, write to the Free Software Foundation,
++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
++ *
++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
++ * or visit www.oracle.com if you need additional information or have any
++ * questions.
++ */
++
++package sun.nio.fs;
++
++/**
++ * Serenity specific system calls.
++ */
++
++class SerenityNativeDispatcher extends UnixNativeDispatcher {
++    private SerenityNativeDispatcher() { }
++
++    /**
++     * Special implementation of 'getextmntent' (see SolarisNativeDispatcher)
++     * that returns all entries at once.
++     */
++    static native UnixMountEntry[] getmntctl() throws UnixException;
++
++    // initialize
++    private static native void init();
++
++    static {
++        jdk.internal.loader.BootLoader.loadLibrary("nio");
++        init();
++    }
++}
+diff --git a/src/java.base/serenity/native/libnet/serenity_close.c b/src/java.base/serenity/native/libnet/serenity_close.c
+new file mode 100644
+index 000000000..6a177bbb9
+--- /dev/null
++++ b/src/java.base/serenity/native/libnet/serenity_close.c
+@@ -0,0 +1,458 @@
++/*
++ * Copyright (c) 2001, 2020, Oracle and/or its affiliates. All rights reserved.
++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
++ *
++ * This code is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License version 2 only, as
++ * published by the Free Software Foundation.  Oracle designates this
++ * particular file as subject to the "Classpath" exception as provided
++ * by Oracle in the LICENSE file that accompanied this code.
++ *
++ * This code is distributed in the hope that it will be useful, but WITHOUT
++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
++ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
++ * version 2 for more details (a copy is included in the LICENSE file that
++ * accompanied this code).
++ *
++ * You should have received a copy of the GNU General Public License version
++ * 2 along with this work; if not, write to the Free Software Foundation,
++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
++ *
++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
++ * or visit www.oracle.com if you need additional information or have any
++ * questions.
++ */
++
++#include <assert.h>
++#include <limits.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <sys/param.h>
++#include <signal.h>
++#include <pthread.h>
++#include <sys/types.h>
++#include <sys/socket.h>
++#include <sys/select.h>
++#include <sys/time.h>
++#include <sys/resource.h>
++#include <sys/uio.h>
++#include <unistd.h>
++#include <errno.h>
++#include <poll.h>
++#include "jvm.h"
++#include "net_util.h"
++
++/*
++ * Stack allocated by thread when doing blocking operation
++ */
++typedef struct threadEntry {
++    pthread_t thr;                      /* this thread */
++    struct threadEntry *next;           /* next thread */
++    int intr;                           /* interrupted */
++} threadEntry_t;
++
++/*
++ * Heap allocated during initialized - one entry per fd
++ */
++typedef struct {
++    pthread_mutex_t lock;               /* fd lock */
++    threadEntry_t *threads;             /* threads blocked on fd */
++} fdEntry_t;
++
++/*
++ * Signal to unblock thread
++ */
++static int sigWakeup = SIGIO;
++
++/*
++ * fdTable holds one entry per file descriptor, up to a certain
++ * maximum.
++ * Theoretically, the number of possible file descriptors can get
++ * large, though usually it does not. Entries for small value file
++ * descriptors are kept in a simple table, which covers most scenarios.
++ * Entries for large value file descriptors are kept in an overflow
++ * table, which is organized as a sparse two dimensional array whose
++ * slabs are allocated on demand. This covers all corner cases while
++ * keeping memory consumption reasonable.
++ */
++
++/* Base table for low value file descriptors */
++static fdEntry_t* fdTable = NULL;
++/* Maximum size of base table (in number of entries). */
++static const int fdTableMaxSize = 0x1000; /* 4K */
++/* Actual size of base table (in number of entries) */
++static int fdTableLen = 0;
++/* Max. theoretical number of file descriptors on system. */
++static int fdLimit = 0;
++
++/* Overflow table, should base table not be large enough. Organized as
++ *   an array of n slabs, each holding 64k entries.
++ */
++static fdEntry_t** fdOverflowTable = NULL;
++/* Number of slabs in the overflow table */
++static int fdOverflowTableLen = 0;
++/* Number of entries in one slab */
++static const int fdOverflowTableSlabSize = 0x10000; /* 64k */
++pthread_mutex_t fdOverflowTableLock = PTHREAD_MUTEX_INITIALIZER;
++
++/*
++ * Null signal handler
++ */
++static void sig_wakeup(int sig) {
++}
++
++/*
++ * Initialization routine (executed when library is loaded)
++ * Allocate fd tables and sets up signal handler.
++ */
++static void __attribute((constructor)) init() {
++    struct rlimit nbr_files;
++    sigset_t sigset;
++    struct sigaction sa;
++    int i = 0;
++
++    /* Determine the maximum number of possible file descriptors. */
++    if (-1 == getrlimit(RLIMIT_NOFILE, &nbr_files)) {
++        fprintf(stderr, "library initialization failed - "
++                "unable to get max # of allocated fds\n");
++        abort();
++    }
++    if (nbr_files.rlim_max != RLIM_INFINITY) {
++        fdLimit = nbr_files.rlim_max;
++    } else {
++        /* We just do not know. */
++        fdLimit = INT_MAX;
++    }
++
++    /* Allocate table for low value file descriptors. */
++    fdTableLen = fdLimit < fdTableMaxSize ? fdLimit : fdTableMaxSize;
++    fdTable = (fdEntry_t*) calloc(fdTableLen, sizeof(fdEntry_t));
++    if (fdTable == NULL) {
++        fprintf(stderr, "library initialization failed - "
++                "unable to allocate file descriptor table - out of memory");
++        abort();
++    } else {
++        for (i = 0; i < fdTableLen; i ++) {
++            pthread_mutex_init(&fdTable[i].lock, NULL);
++        }
++    }
++
++    /* Allocate overflow table, if needed */
++    if (fdLimit > fdTableMaxSize) {
++        fdOverflowTableLen = ((fdLimit - fdTableMaxSize) / fdOverflowTableSlabSize) + 1;
++        fdOverflowTable = (fdEntry_t**) calloc(fdOverflowTableLen, sizeof(fdEntry_t*));
++        if (fdOverflowTable == NULL) {
++            fprintf(stderr, "library initialization failed - "
++                    "unable to allocate file descriptor overflow table - out of memory");
++            abort();
++        }
++    }
++
++    /*
++     * Setup the signal handler
++     */
++    sa.sa_handler = sig_wakeup;
++    sa.sa_flags   = 0;
++    sigemptyset(&sa.sa_mask);
++    sigaction(sigWakeup, &sa, NULL);
++
++    sigemptyset(&sigset);
++    sigaddset(&sigset, sigWakeup);
++    sigprocmask(SIG_UNBLOCK, &sigset, NULL);
++}
++
++/*
++ * Return the fd table for this fd.
++ */
++static inline fdEntry_t *getFdEntry(int fd)
++{
++    fdEntry_t* result = NULL;
++
++    if (fd < 0) {
++        return NULL;
++    }
++
++    /* This should not happen. If it does, our assumption about
++     * max. fd value was wrong. */
++    assert(fd < fdLimit);
++
++    if (fd < fdTableMaxSize) {
++        /* fd is in base table. */
++        assert(fd < fdTableLen);
++        result = &fdTable[fd];
++    } else {
++        /* fd is in overflow table. */
++        const int indexInOverflowTable = fd - fdTableMaxSize;
++        const int rootindex = indexInOverflowTable / fdOverflowTableSlabSize;
++        const int slabindex = indexInOverflowTable % fdOverflowTableSlabSize;
++        fdEntry_t* slab = NULL;
++        assert(rootindex < fdOverflowTableLen);
++        assert(slabindex < fdOverflowTableSlabSize);
++        pthread_mutex_lock(&fdOverflowTableLock);
++        /* Allocate new slab in overflow table if needed */
++        if (fdOverflowTable[rootindex] == NULL) {
++            fdEntry_t* const newSlab =
++                (fdEntry_t*)calloc(fdOverflowTableSlabSize, sizeof(fdEntry_t));
++            if (newSlab == NULL) {
++                fprintf(stderr, "Unable to allocate file descriptor overflow"
++                        " table slab - out of memory");
++                pthread_mutex_unlock(&fdOverflowTableLock);
++                abort();
++            } else {
++                int i;
++                for (i = 0; i < fdOverflowTableSlabSize; i ++) {
++                    pthread_mutex_init(&newSlab[i].lock, NULL);
++                }
++                fdOverflowTable[rootindex] = newSlab;
++            }
++        }
++        pthread_mutex_unlock(&fdOverflowTableLock);
++        slab = fdOverflowTable[rootindex];
++        result = &slab[slabindex];
++    }
++
++    return result;
++
++}
++
++
++/*
++ * Start a blocking operation :-
++ *    Insert thread onto thread list for the fd.
++ */
++static inline void startOp(fdEntry_t *fdEntry, threadEntry_t *self)
++{
++    self->thr = pthread_self();
++    self->intr = 0;
++
++    pthread_mutex_lock(&(fdEntry->lock));
++    {
++        self->next = fdEntry->threads;
++        fdEntry->threads = self;
++    }
++    pthread_mutex_unlock(&(fdEntry->lock));
++}
++
++/*
++ * End a blocking operation :-
++ *     Remove thread from thread list for the fd
++ *     If fd has been interrupted then set errno to EBADF
++ */
++static inline void endOp
++    (fdEntry_t *fdEntry, threadEntry_t *self)
++{
++    int orig_errno = errno;
++    pthread_mutex_lock(&(fdEntry->lock));
++    {
++        threadEntry_t *curr, *prev=NULL;
++        curr = fdEntry->threads;
++        while (curr != NULL) {
++            if (curr == self) {
++                if (curr->intr) {
++                    orig_errno = EBADF;
++                }
++                if (prev == NULL) {
++                    fdEntry->threads = curr->next;
++                } else {
++                    prev->next = curr->next;
++                }
++                break;
++            }
++            prev = curr;
++            curr = curr->next;
++        }
++    }
++    pthread_mutex_unlock(&(fdEntry->lock));
++    errno = orig_errno;
++}
++
++/*
++ * Close or dup2 a file descriptor ensuring that all threads blocked on
++ * the file descriptor are notified via a wakeup signal.
++ *
++ *      fd1 < 0    => close(fd2)
++ *      fd1 >= 0   => dup2(fd1, fd2)
++ *
++ * Returns -1 with errno set if operation fails.
++ */
++static int closefd(int fd1, int fd2) {
++    int rv, orig_errno;
++    fdEntry_t *fdEntry = getFdEntry(fd2);
++    if (fdEntry == NULL) {
++        errno = EBADF;
++        return -1;
++    }
++
++    /*
++     * Lock the fd to hold-off additional I/O on this fd.
++     */
++    pthread_mutex_lock(&(fdEntry->lock));
++
++    {
++        /*
++         * Send a wakeup signal to all threads blocked on this
++         * file descriptor.
++         */
++        threadEntry_t *curr = fdEntry->threads;
++        while (curr != NULL) {
++            curr->intr = 1;
++            pthread_kill( curr->thr, sigWakeup );
++            curr = curr->next;
++        }
++
++        /*
++         * And close/dup the file descriptor
++         * (restart if interrupted by signal)
++         */
++        do {
++            if (fd1 < 0) {
++                rv = close(fd2);
++            } else {
++                rv = dup2(fd1, fd2);
++            }
++        } while (rv == -1 && errno == EINTR);
++
++    }
++
++    /*
++     * Unlock without destroying errno
++     */
++    orig_errno = errno;
++    pthread_mutex_unlock(&(fdEntry->lock));
++    errno = orig_errno;
++
++    return rv;
++}
++
++/*
++ * Wrapper for dup2 - same semantics as dup2 system call except
++ * that any threads blocked in an I/O system call on fd2 will be
++ * preempted and return -1/EBADF;
++ */
++int NET_Dup2(int fd, int fd2) {
++    if (fd < 0) {
++        errno = EBADF;
++        return -1;
++    }
++    return closefd(fd, fd2);
++}
++
++/*
++ * Wrapper for close - same semantics as close system call
++ * except that any threads blocked in an I/O on fd will be
++ * preempted and the I/O system call will return -1/EBADF.
++ */
++int NET_SocketClose(int fd) {
++    return closefd(-1, fd);
++}
++
++/************** Basic I/O operations here ***************/
++
++/*
++ * Macro to perform a blocking IO operation. Restarts
++ * automatically if interrupted by signal (other than
++ * our wakeup signal)
++ */
++#define BLOCKING_IO_RETURN_INT(FD, FUNC) {      \
++    int ret;                                    \
++    threadEntry_t self;                         \
++    fdEntry_t *fdEntry = getFdEntry(FD);        \
++    if (fdEntry == NULL) {                      \
++        errno = EBADF;                          \
++        return -1;                              \
++    }                                           \
++    do {                                        \
++        startOp(fdEntry, &self);                \
++        ret = FUNC;                             \
++        endOp(fdEntry, &self);                  \
++    } while (ret == -1 && errno == EINTR);      \
++    return ret;                                 \
++}
++
++int NET_Read(int s, void* buf, size_t len) {
++    BLOCKING_IO_RETURN_INT( s, recv(s, buf, len, 0) );
++}
++
++int NET_NonBlockingRead(int s, void* buf, size_t len) {
++    BLOCKING_IO_RETURN_INT( s, recv(s, buf, len, MSG_DONTWAIT));
++}
++
++int NET_RecvFrom(int s, void *buf, int len, unsigned int flags,
++       struct sockaddr *from, socklen_t *fromlen) {
++    BLOCKING_IO_RETURN_INT( s, recvfrom(s, buf, len, flags, from, fromlen) );
++}
++
++int NET_Send(int s, void *msg, int len, unsigned int flags) {
++    BLOCKING_IO_RETURN_INT( s, send(s, msg, len, flags) );
++}
++
++int NET_SendTo(int s, const void *msg, int len,  unsigned  int
++       flags, const struct sockaddr *to, int tolen) {
++    BLOCKING_IO_RETURN_INT( s, sendto(s, msg, len, flags, to, tolen) );
++}
++
++int NET_Accept(int s, struct sockaddr *addr, socklen_t *addrlen) {
++    BLOCKING_IO_RETURN_INT( s, accept(s, addr, addrlen) );
++}
++
++int NET_Connect(int s, struct sockaddr *addr, int addrlen) {
++    BLOCKING_IO_RETURN_INT( s, connect(s, addr, addrlen) );
++}
++
++int NET_Poll(struct pollfd *ufds, unsigned int nfds, int timeout) {
++    BLOCKING_IO_RETURN_INT( ufds[0].fd, poll(ufds, nfds, timeout) );
++}
++
++/*
++ * Wrapper for poll(s, timeout).
++ * Auto restarts with adjusted timeout if interrupted by
++ * signal other than our wakeup signal.
++ */
++int NET_Timeout(JNIEnv *env, int s, long timeout, jlong nanoTimeStamp) {
++    jlong prevNanoTime = nanoTimeStamp;
++    jlong nanoTimeout = (jlong)timeout * NET_NSEC_PER_MSEC;
++    fdEntry_t *fdEntry = getFdEntry(s);
++
++    /*
++     * Check that fd hasn't been closed.
++     */
++    if (fdEntry == NULL) {
++        errno = EBADF;
++        return -1;
++    }
++
++    for(;;) {
++        struct pollfd pfd;
++        int rv;
++        threadEntry_t self;
++
++        /*
++         * Poll the fd. If interrupted by our wakeup signal
++         * errno will be set to EBADF.
++         */
++        pfd.fd = s;
++        pfd.events = POLLIN | POLLERR;
++
++        startOp(fdEntry, &self);
++        rv = poll(&pfd, 1, nanoTimeout / NET_NSEC_PER_MSEC);
++        endOp(fdEntry, &self);
++        /*
++         * If interrupted then adjust timeout. If timeout
++         * has expired return 0 (indicating timeout expired).
++         */
++        if (rv < 0 && errno == EINTR) {
++            if (timeout > 0) {
++                jlong newNanoTime = JVM_NanoTime(env, 0);
++                nanoTimeout -= newNanoTime - prevNanoTime;
++                if (nanoTimeout < NET_NSEC_PER_MSEC) {
++                    return 0;
++                }
++                prevNanoTime = newNanoTime;
++            } else {
++                continue; // timeout is -1, so  loop again.
++            }
++        } else {
++            return rv;
++        }
++    }
++}
+diff --git a/src/jdk.attach/serenity/classes/sun/tools/attach/AttachProviderImpl.java b/src/jdk.attach/serenity/classes/sun/tools/attach/AttachProviderImpl.java
+new file mode 100644
+index 000000000..2f6fc4d4d
+--- /dev/null
++++ b/src/jdk.attach/serenity/classes/sun/tools/attach/AttachProviderImpl.java
+@@ -0,0 +1,82 @@
++/*
++ * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
++ * Copyright (c) 2013 SAP SE. All rights reserved.
++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
++ *
++ * This code is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License version 2 only, as
++ * published by the Free Software Foundation.  Oracle designates this
++ * particular file as subject to the "Classpath" exception as provided
++ * by Oracle in the LICENSE file that accompanied this code.
++ *
++ * This code is distributed in the hope that it will be useful, but WITHOUT
++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
++ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
++ * version 2 for more details (a copy is included in the LICENSE file that
++ * accompanied this code).
++ *
++ * You should have received a copy of the GNU General Public License version
++ * 2 along with this work; if not, write to the Free Software Foundation,
++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
++ *
++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
++ * or visit www.oracle.com if you need additional information or have any
++ * questions.
++ */
++package sun.tools.attach;
++
++import com.sun.tools.attach.VirtualMachine;
++import com.sun.tools.attach.VirtualMachineDescriptor;
++import com.sun.tools.attach.AttachNotSupportedException;
++import java.io.IOException;
++
++// Based on linux/classes/sun/tools/attach/AttachProviderImpl.java.
++
++/*
++ * An AttachProvider implementation for Aix that uses a UNIX domain
++ * socket.
++ */
++public class AttachProviderImpl extends HotSpotAttachProvider {
++
++    public AttachProviderImpl() {
++    }
++
++    public String name() {
++        return "sun";
++    }
++
++    public String type() {
++        return "socket";
++    }
++
++    public VirtualMachine attachVirtualMachine(String vmid)
++        throws AttachNotSupportedException, IOException
++    {
++        checkAttachPermission();
++
++        // AttachNotSupportedException will be thrown if the target VM can be determined
++        // to be not attachable.
++        testAttachable(vmid);
++
++        return new VirtualMachineImpl(this, vmid);
++    }
++
++    public VirtualMachine attachVirtualMachine(VirtualMachineDescriptor vmd)
++        throws AttachNotSupportedException, IOException
++    {
++        if (vmd.provider() != this) {
++            throw new AttachNotSupportedException("provider mismatch");
++        }
++        // To avoid re-checking if the VM if attachable, we check if the descriptor
++        // is for a hotspot VM - these descriptors are created by the listVirtualMachines
++        // implementation which only returns a list of attachable VMs.
++        if (vmd instanceof HotSpotVirtualMachineDescriptor) {
++            assert ((HotSpotVirtualMachineDescriptor)vmd).isAttachable();
++            checkAttachPermission();
++            return new VirtualMachineImpl(this, vmd.id());
++        } else {
++            return attachVirtualMachine(vmd.id());
++        }
++    }
++
++}
+diff --git a/src/jdk.attach/serenity/classes/sun/tools/attach/VirtualMachineImpl.java b/src/jdk.attach/serenity/classes/sun/tools/attach/VirtualMachineImpl.java
+new file mode 100644
+index 000000000..0c432edee
+--- /dev/null
++++ b/src/jdk.attach/serenity/classes/sun/tools/attach/VirtualMachineImpl.java
+@@ -0,0 +1,326 @@
++/*
++ * Copyright (c) 2008, 2021, Oracle and/or its affiliates. All rights reserved.
++ * Copyright (c) 2015, 2019 SAP SE. All rights reserved.
++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
++ *
++ * This code is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License version 2 only, as
++ * published by the Free Software Foundation.  Oracle designates this
++ * particular file as subject to the "Classpath" exception as provided
++ * by Oracle in the LICENSE file that accompanied this code.
++ *
++ * This code is distributed in the hope that it will be useful, but WITHOUT
++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
++ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
++ * version 2 for more details (a copy is included in the LICENSE file that
++ * accompanied this code).
++ *
++ * You should have received a copy of the GNU General Public License version
++ * 2 along with this work; if not, write to the Free Software Foundation,
++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
++ *
++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
++ * or visit www.oracle.com if you need additional information or have any
++ * questions.
++ */
++package sun.tools.attach;
++
++import com.sun.tools.attach.AttachOperationFailedException;
++import com.sun.tools.attach.AgentLoadException;
++import com.sun.tools.attach.AttachNotSupportedException;
++import com.sun.tools.attach.spi.AttachProvider;
++
++import java.io.InputStream;
++import java.io.IOException;
++import java.io.File;
++
++/*
++ * Aix implementation of HotSpotVirtualMachine
++ */
++public class VirtualMachineImpl extends HotSpotVirtualMachine {
++    // "/tmp" is used as a global well-known location for the files
++    // .java_pid<pid>. and .attach_pid<pid>. It is important that this
++    // location is the same for all processes, otherwise the tools
++    // will not be able to find all Hotspot processes.
++    // Any changes to this needs to be synchronized with HotSpot.
++    private static final String tmpdir = "/tmp";
++    String socket_path;
++
++    /**
++     * Attaches to the target VM
++     */
++    VirtualMachineImpl(AttachProvider provider, String vmid)
++        throws AttachNotSupportedException, IOException
++    {
++        super(provider, vmid);
++
++        // This provider only understands pids
++        int pid;
++        try {
++            pid = Integer.parseInt(vmid);
++            if (pid < 1) {
++                throw new NumberFormatException();
++            }
++        } catch (NumberFormatException x) {
++            throw new AttachNotSupportedException("Invalid process identifier: " + vmid);
++        }
++
++        // Find the socket file. If not found then we attempt to start the
++        // attach mechanism in the target VM by sending it a QUIT signal.
++        // Then we attempt to find the socket file again.
++        File socket_file = new File(tmpdir, ".java_pid" + pid);
++        socket_path = socket_file.getPath();
++        if (!socket_file.exists()) {
++            // Keep canonical version of File, to delete, in case target process ends and /proc link has gone:
++            File f = createAttachFile(pid).getCanonicalFile();
++            try {
++                sendQuitTo(pid);
++
++                // give the target VM time to start the attach mechanism
++                final int delay_step = 100;
++                final long timeout = attachTimeout();
++                long time_spend = 0;
++                long delay = 0;
++                do {
++                    // Increase timeout on each attempt to reduce polling
++                    delay += delay_step;
++                    try {
++                        Thread.sleep(delay);
++                    } catch (InterruptedException x) { }
++
++                    time_spend += delay;
++                    if (time_spend > timeout/2 && !socket_file.exists()) {
++                        // Send QUIT again to give target VM the last chance to react
++                        sendQuitTo(pid);
++                    }
++                } while (time_spend <= timeout && !socket_file.exists());
++                if (!socket_file.exists()) {
++                    throw new AttachNotSupportedException(
++                        String.format("Unable to open socket file %s: " +
++                          "target process %d doesn't respond within %dms " +
++                           "or HotSpot VM not loaded", socket_path, pid,
++                                      time_spend));
++                }
++            } finally {
++                f.delete();
++            }
++        }
++
++        // Check that the file owner/permission to avoid attaching to
++        // bogus process
++        checkPermissions(socket_path);
++
++        // Check that we can connect to the process
++        // - this ensures we throw the permission denied error now rather than
++        // later when we attempt to enqueue a command.
++        int s = socket();
++        try {
++            connect(s, socket_path);
++        } finally {
++            close(s);
++        }
++    }
++
++    /**
++     * Detach from the target VM
++     */
++    public void detach() throws IOException {
++        synchronized (this) {
++            if (socket_path != null) {
++                socket_path = null;
++            }
++        }
++    }
++
++    // protocol version
++    private final static String PROTOCOL_VERSION = "1";
++
++    // known errors
++    private final static int ATTACH_ERROR_BADVERSION = 101;
++
++    /**
++     * Execute the given command in the target VM.
++     */
++    InputStream execute(String cmd, Object ... args) throws AgentLoadException, IOException {
++        assert args.length <= 3;                // includes null
++
++        // did we detach?
++        synchronized (this) {
++            if (socket_path == null) {
++                throw new IOException("Detached from target VM");
++            }
++        }
++
++        // create UNIX socket
++        int s = socket();
++
++        // connect to target VM
++        try {
++            connect(s, socket_path);
++        } catch (IOException x) {
++            close(s);
++            throw x;
++        }
++
++        IOException ioe = null;
++
++        // connected - write request
++        // <ver> <cmd> <args...>
++        try {
++            writeString(s, PROTOCOL_VERSION);
++            writeString(s, cmd);
++
++            for (int i = 0; i < 3; i++) {
++                if (i < args.length && args[i] != null) {
++                    writeString(s, (String)args[i]);
++                } else {
++                    writeString(s, "");
++                }
++            }
++        } catch (IOException x) {
++            ioe = x;
++        }
++
++
++        // Create an input stream to read reply
++        SocketInputStream sis = new SocketInputStream(s);
++
++        // Read the command completion status
++        int completionStatus;
++        try {
++            completionStatus = readInt(sis);
++        } catch (IOException x) {
++            sis.close();
++            if (ioe != null) {
++                throw ioe;
++            } else {
++                throw x;
++            }
++        }
++
++        if (completionStatus != 0) {
++            // read from the stream and use that as the error message
++            String message = readErrorMessage(sis);
++            sis.close();
++
++            // In the event of a protocol mismatch then the target VM
++            // returns a known error so that we can throw a reasonable
++            // error.
++            if (completionStatus == ATTACH_ERROR_BADVERSION) {
++                throw new IOException("Protocol mismatch with target VM");
++            }
++
++            // Special-case the "load" command so that the right exception is
++            // thrown.
++            if (cmd.equals("load")) {
++                String msg = "Failed to load agent library";
++                if (!message.isEmpty())
++                    msg += ": " + message;
++                throw new AgentLoadException(msg);
++            } else {
++                if (message.isEmpty())
++                    message = "Command failed in target VM";
++                throw new AttachOperationFailedException(message);
++            }
++        }
++
++        // Return the input stream so that the command output can be read
++        return sis;
++    }
++
++    /*
++     * InputStream for the socket connection to get target VM
++     */
++    private class SocketInputStream extends InputStream {
++        int s;
++
++        public SocketInputStream(int s) {
++            this.s = s;
++        }
++
++        public synchronized int read() throws IOException {
++            byte b[] = new byte[1];
++            int n = this.read(b, 0, 1);
++            if (n == 1) {
++                return b[0] & 0xff;
++            } else {
++                return -1;
++            }
++        }
++
++        public synchronized int read(byte[] bs, int off, int len) throws IOException {
++            if ((off < 0) || (off > bs.length) || (len < 0) ||
++                ((off + len) > bs.length) || ((off + len) < 0)) {
++                throw new IndexOutOfBoundsException();
++            } else if (len == 0)
++                return 0;
++
++            return VirtualMachineImpl.read(s, bs, off, len);
++        }
++
++        public synchronized void close() throws IOException {
++            if (s != -1) {
++                int toClose = s;
++                s = -1;
++                VirtualMachineImpl.close(toClose);
++            }
++        }
++    }
++
++    // On Aix a simple handshake is used to start the attach mechanism
++    // if not already started. The client creates a .attach_pid<pid> file in the
++    // target VM's working directory (or temp directory), and the SIGQUIT handler
++    // checks for the file.
++    private File createAttachFile(int pid) throws IOException {
++        String fn = ".attach_pid" + pid;
++        String path = "/proc/" + pid + "/cwd/" + fn;
++        File f = new File(path);
++        try {
++            f.createNewFile();
++        } catch (IOException x) {
++            f = new File(tmpdir, fn);
++            f.createNewFile();
++        }
++        return f;
++    }
++
++    /*
++     * Write/sends the given to the target VM. String is transmitted in
++     * UTF-8 encoding.
++     */
++    private void writeString(int fd, String s) throws IOException {
++        if (s.length() > 0) {
++            byte b[];
++            try {
++                b = s.getBytes("UTF-8");
++            } catch (java.io.UnsupportedEncodingException x) {
++                throw new InternalError(x);
++            }
++            VirtualMachineImpl.write(fd, b, 0, b.length);
++        }
++        byte b[] = new byte[1];
++        b[0] = 0;
++        write(fd, b, 0, 1);
++    }
++
++
++    //-- native methods
++
++    static native void sendQuitTo(int pid) throws IOException;
++
++    static native void checkPermissions(String path) throws IOException;
++
++    static native int socket() throws IOException;
++
++    static native void connect(int fd, String path) throws IOException;
++
++    static native void close(int fd) throws IOException;
++
++    static native int read(int fd, byte buf[], int off, int bufLen) throws IOException;
++
++    static native void write(int fd, byte buf[], int off, int bufLen) throws IOException;
++
++    static {
++        System.loadLibrary("attach");
++    }
++}
+diff --git a/src/jdk.attach/serenity/native/libattach/VirtualMachineImpl.c b/src/jdk.attach/serenity/native/libattach/VirtualMachineImpl.c
+new file mode 100644
+index 000000000..d20a6f012
+--- /dev/null
++++ b/src/jdk.attach/serenity/native/libattach/VirtualMachineImpl.c
+@@ -0,0 +1,328 @@
++/*
++ * Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved.
++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
++ *
++ * This code is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License version 2 only, as
++ * published by the Free Software Foundation.  Oracle designates this
++ * particular file as subject to the "Classpath" exception as provided
++ * by Oracle in the LICENSE file that accompanied this code.
++ *
++ * This code is distributed in the hope that it will be useful, but WITHOUT
++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
++ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
++ * version 2 for more details (a copy is included in the LICENSE file that
++ * accompanied this code).
++ *
++ * You should have received a copy of the GNU General Public License version
++ * 2 along with this work; if not, write to the Free Software Foundation,
++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
++ *
++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
++ * or visit www.oracle.com if you need additional information or have any
++ * questions.
++ */
++
++#include "jni_util.h"
++
++#include <sys/socket.h>
++#include <sys/stat.h>
++#include <sys/syslimits.h>
++#include <sys/types.h>
++#include <sys/un.h>
++#include <errno.h>
++#include <fcntl.h>
++#include <signal.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <unistd.h>
++
++#include "sun_tools_attach_VirtualMachineImpl.h"
++
++#define RESTARTABLE(_cmd, _result) do { \
++  do { \
++    _result = _cmd; \
++  } while((_result == -1) && (errno == EINTR)); \
++} while(0)
++
++#define ROOT_UID 0
++
++/*
++ * Declare library specific JNI_Onload entry if static build
++ */
++DEF_STATIC_JNI_OnLoad
++
++/*
++ * Class:     sun_tools_attach_VirtualMachineImpl
++ * Method:    socket
++ * Signature: ()I
++ */
++JNIEXPORT jint JNICALL Java_sun_tools_attach_VirtualMachineImpl_socket
++  (JNIEnv *env, jclass cls)
++{
++    int fd = socket(PF_UNIX, SOCK_STREAM, 0);
++    if (fd == -1) {
++        JNU_ThrowIOExceptionWithLastError(env, "socket");
++    }
++    return (jint)fd;
++}
++
++/*
++ * Class:     sun_tools_attach_VirtualMachineImpl
++ * Method:    connect
++ * Signature: (ILjava/lang/String;)I
++ */
++JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_connect
++  (JNIEnv *env, jclass cls, jint fd, jstring path)
++{
++    jboolean isCopy;
++    const char* p = GetStringPlatformChars(env, path, &isCopy);
++    if (p != NULL) {
++        struct sockaddr_un addr;
++        int err = 0;
++
++        memset(&addr, 0, sizeof(addr));
++        addr.sun_family = AF_UNIX;
++        /* strncpy is safe because addr.sun_path was zero-initialized before. */
++        strncpy(addr.sun_path, p, sizeof(addr.sun_path) - 1);
++
++        if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
++            err = errno;
++        }
++
++        if (isCopy) {
++            JNU_ReleaseStringPlatformChars(env, path, p);
++        }
++
++        /*
++         * If the connect failed then we throw the appropriate exception
++         * here (can't throw it before releasing the string as can't call
++         * JNI with pending exception)
++         */
++        if (err != 0) {
++            if (err == ENOENT) {
++                JNU_ThrowByName(env, "java/io/FileNotFoundException", NULL);
++            } else {
++                char* msg = strdup(strerror(err));
++                JNU_ThrowIOException(env, msg);
++                if (msg != NULL) {
++                    free(msg);
++                }
++            }
++        }
++    }
++}
++
++/*
++ * Class:     sun_tools_attach_VirtualMachineImpl
++ * Method:    sendQuitTo
++ * Signature: (I)V
++ */
++JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_sendQuitTo
++  (JNIEnv *env, jclass cls, jint pid)
++{
++    if (kill((pid_t)pid, SIGQUIT)) {
++        JNU_ThrowIOExceptionWithLastError(env, "kill");
++    }
++}
++
++/*
++ * Class:     sun_tools_attach_VirtualMachineImpl
++ * Method:    checkPermissions
++ * Signature: (Ljava/lang/String;)V
++ */
++JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_checkPermissions
++  (JNIEnv *env, jclass cls, jstring path)
++{
++    jboolean isCopy;
++    const char* p = GetStringPlatformChars(env, path, &isCopy);
++    if (p != NULL) {
++        struct stat sb;
++        uid_t uid, gid;
++        int res;
++
++        memset(&sb, 0, sizeof(struct stat));
++
++        /*
++         * Check that the path is owned by the effective uid/gid of this
++         * process. Also check that group/other access is not allowed.
++         */
++        uid = geteuid();
++        gid = getegid();
++
++        res = stat(p, &sb);
++        if (res != 0) {
++            /* save errno */
++            res = errno;
++        }
++
++        if (res == 0) {
++            char msg[100];
++            jboolean isError = JNI_FALSE;
++            if (sb.st_uid != uid && uid != ROOT_UID) {
++                snprintf(msg, sizeof(msg),
++                    "file should be owned by the current user (which is %d) but is owned by %d", uid, sb.st_uid);
++                isError = JNI_TRUE;
++            } else if (sb.st_gid != gid && uid != ROOT_UID) {
++                snprintf(msg, sizeof(msg),
++                    "file's group should be the current group (which is %d) but the group is %d", gid, sb.st_gid);
++                isError = JNI_TRUE;
++            } else if ((sb.st_mode & (S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) != 0) {
++                snprintf(msg, sizeof(msg),
++                    "file should only be readable and writable by the owner but has 0%03o access", sb.st_mode & 0777);
++                isError = JNI_TRUE;
++            }
++            if (isError) {
++                char buf[256];
++                snprintf(buf, sizeof(buf), "well-known file %s is not secure: %s", p, msg);
++                JNU_ThrowIOException(env, buf);
++            }
++        } else {
++            char* msg = strdup(strerror(res));
++            JNU_ThrowIOException(env, msg);
++            if (msg != NULL) {
++                free(msg);
++            }
++        }
++
++        if (isCopy) {
++            JNU_ReleaseStringPlatformChars(env, path, p);
++        }
++    }
++}
++
++/*
++ * Class:     sun_tools_attach_VirtualMachineImpl
++ * Method:    close
++ * Signature: (I)V
++ */
++JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_close
++  (JNIEnv *env, jclass cls, jint fd)
++{
++    int res;
++    shutdown(fd, SHUT_RDWR);
++    RESTARTABLE(close(fd), res);
++}
++
++/*
++ * Class:     sun_tools_attach_VirtualMachineImpl
++ * Method:    read
++ * Signature: (I[BI)I
++ */
++JNIEXPORT jint JNICALL Java_sun_tools_attach_VirtualMachineImpl_read
++  (JNIEnv *env, jclass cls, jint fd, jbyteArray ba, jint off, jint baLen)
++{
++    unsigned char buf[128];
++    size_t len = sizeof(buf);
++    ssize_t n;
++
++    size_t remaining = (size_t)(baLen - off);
++    if (len > remaining) {
++        len = remaining;
++    }
++
++    RESTARTABLE(read(fd, buf, len), n);
++    if (n == -1) {
++        JNU_ThrowIOExceptionWithLastError(env, "read");
++    } else {
++        if (n == 0) {
++            n = -1;     // EOF
++        } else {
++            (*env)->SetByteArrayRegion(env, ba, off, (jint)n, (jbyte *)(buf));
++        }
++    }
++    return n;
++}
++
++/*
++ * Class:     sun_tools_attach_VirtualMachineImpl
++ * Method:    write
++ * Signature: (I[B)V
++ */
++JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_write
++  (JNIEnv *env, jclass cls, jint fd, jbyteArray ba, jint off, jint bufLen)
++{
++    size_t remaining = bufLen;
++    do {
++        unsigned char buf[128];
++        size_t len = sizeof(buf);
++        int n;
++
++        if (len > remaining) {
++            len = remaining;
++        }
++        (*env)->GetByteArrayRegion(env, ba, off, len, (jbyte *)buf);
++
++        RESTARTABLE(write(fd, buf, len), n);
++        if (n > 0) {
++            off += n;
++            remaining -= n;
++        } else {
++            JNU_ThrowIOExceptionWithLastError(env, "write");
++            return;
++        }
++
++    } while (remaining > 0);
++}
++
++/*
++ * Class:     sun_tools_attach_BSDVirtualMachine
++ * Method:    createAttachFile
++ * Signature: (Ljava.lang.String;)V
++ */
++JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_createAttachFile0(JNIEnv *env, jclass cls, jstring path)
++{
++    const char* _path;
++    jboolean isCopy;
++    int fd, rc;
++
++    _path = GetStringPlatformChars(env, path, &isCopy);
++    if (_path == NULL) {
++        JNU_ThrowIOException(env, "Must specify a path");
++        return;
++    }
++
++    RESTARTABLE(open(_path, O_CREAT | O_EXCL, S_IWUSR | S_IRUSR), fd);
++    if (fd == -1) {
++        /* release p here before we throw an I/O exception */
++        if (isCopy) {
++            JNU_ReleaseStringPlatformChars(env, path, _path);
++        }
++        JNU_ThrowIOExceptionWithLastError(env, "open");
++        return;
++    }
++
++    RESTARTABLE(chown(_path, geteuid(), getegid()), rc);
++
++    RESTARTABLE(close(fd), rc);
++
++    /* release p here */
++    if (isCopy) {
++        JNU_ReleaseStringPlatformChars(env, path, _path);
++    }
++}
++
++/*
++ * Class:     sun_tools_attach_BSDVirtualMachine
++ * Method:    getTempDir
++ * Signature: (V)Ljava.lang.String;
++ */
++JNIEXPORT jstring JNICALL Java_sun_tools_attach_VirtualMachineImpl_getTempDir(JNIEnv *env, jclass cls)
++{
++    // This must be hard coded because it's the system's temporary
++    // directory not the java application's temp directory, ala java.io.tmpdir.
++
++#ifdef __APPLE__
++    // macosx has a secure per-user temporary directory.
++    // Don't cache the result as this is only called once.
++    char path[PATH_MAX];
++    int pathSize = confstr(_CS_DARWIN_USER_TEMP_DIR, path, PATH_MAX);
++    if (pathSize == 0 || pathSize > PATH_MAX) {
++        strlcpy(path, "/tmp", sizeof(path));
++    }
++    return JNU_NewStringPlatform(env, path);
++#else /* __APPLE__ */
++    return (*env)->NewStringUTF(env, "/tmp");
++#endif /* __APPLE__ */
++}

+ 606 - 0
Ports/OpenJDK/patches/0007-java.base-Update-native-modules-to-support-Serenity.patch

@@ -0,0 +1,606 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Timur Sultanov <SultanovTS@yandex.ru>
+Date: Wed, 16 Feb 2022 21:06:23 +0300
+Subject: [PATCH] java.base: Update native modules to support Serenity
+
+---
+ .../nio/ch/SocketOptionRegistry.java.template | 21 +++++
+ src/java.base/share/native/libjava/io_util.h  |  5 +-
+ src/java.base/share/native/libjli/jli_util.h  |  3 +
+ src/java.base/share/native/libzip/zip_util.c  |  2 +-
+ .../sun/nio/fs/UnixConstants.java.template    | 92 ++++++++++++++++++-
+ .../native/libjava/ProcessHandleImpl_unix.c   |  2 +-
+ .../unix/native/libjava/TimeZone_md.c         |  4 +-
+ .../unix/native/libjava/io_util_md.c          |  2 +-
+ .../unix/native/libjava/io_util_md.h          |  6 +-
+ src/java.base/unix/native/libjsig/jsig.c      |  4 +
+ .../unix/native/libnet/Inet4AddressImpl.c     |  3 +
+ .../unix/native/libnet/Inet6AddressImpl.c     |  4 +
+ .../unix/native/libnet/NetworkInterface.c     | 11 ++-
+ .../unix/native/libnet/net_util_md.h          |  4 +
+ .../unix/native/libnio/MappedMemoryUtils.c    |  4 +
+ .../native/libnio/ch/DatagramDispatcher.c     |  1 +
+ .../unix/native/libnio/ch/FileChannelImpl.c   |  3 +
+ .../native/libnio/ch/FileDispatcherImpl.c     |  7 +-
+ src/java.base/unix/native/libnio/ch/FileKey.c |  2 +-
+ .../unix/native/libnio/ch/NativeThread.c      |  2 +-
+ src/java.base/unix/native/libnio/ch/Net.c     |  2 +-
+ .../native/libnio/fs/UnixNativeDispatcher.c   | 39 +++++++-
+ 22 files changed, 207 insertions(+), 16 deletions(-)
+
+diff --git a/src/java.base/share/classes/sun/nio/ch/SocketOptionRegistry.java.template b/src/java.base/share/classes/sun/nio/ch/SocketOptionRegistry.java.template
+index 0672ced15..55bde3569 100644
+--- a/src/java.base/share/classes/sun/nio/ch/SocketOptionRegistry.java.template
++++ b/src/java.base/share/classes/sun/nio/ch/SocketOptionRegistry.java.template
+@@ -50,6 +50,24 @@
+ #endif
+ #endif
+ 
++#define SO_RCVTIMEO 0
++#define SO_SNDTIMEO 1
++#define SO_TYPE 2
++#define SO_ERROR 3
++#define SO_PEERCRED 4
++#define SO_SNDBUF 5
++#define SO_RCVBUF 6
++#define SO_DEBUG 7
++#define SO_REUSEADDR 8
++#define SO_BINDTODEVICE 9
++#define SO_KEEPALIVE 10
++#define SO_TIMESTAMP 11
++#define SO_BROADCAST 12
++#define SO_LINGER 13
++#define SO_ACCEPTCONN 14
++#define SO_DONTROUTE 15
++#define SO_OOBINLINE 16
++
+ /* To be able to name the Java constants the same as the C constants without
+    having the preprocessor rewrite those identifiers, add PREFIX_ to all
+    identifiers matching a C constant. The PREFIX_ is filtered out in the
+@@ -125,6 +143,9 @@ class SocketOptionRegistry {
+ 
+ #ifdef AF_INET6
+             // IPPROTO_IPV6 is 41
++#ifdef SERENITY
++#define IPV6_TCLASS 1
++#endif
+             map.put(new RegistryKey(StandardSocketOptions.PREFIX_IP_TOS,
+                 StandardProtocolFamily.INET6), new OptionKey(41, IPV6_TCLASS));
+             map.put(new RegistryKey(StandardSocketOptions.PREFIX_IP_MULTICAST_IF,
+diff --git a/src/java.base/share/native/libjava/io_util.h b/src/java.base/share/native/libjava/io_util.h
+index 6e960c034..cbd1d087e 100644
+--- a/src/java.base/share/native/libjava/io_util.h
++++ b/src/java.base/share/native/libjava/io_util.h
+@@ -30,11 +30,14 @@ extern jfieldID IO_fd_fdID;
+ extern jfieldID IO_handle_fdID;
+ extern jfieldID IO_append_fdID;
+ 
+-#ifdef _ALLBSD_SOURCE
++#if defined(_ALLBSD_SOURCE) || defined(SERENITY)
+ #include <fcntl.h>
+ #ifndef O_SYNC
+ #define O_SYNC  O_FSYNC
+ #endif
++#if defined(SERENITY)
++#define O_DSYNC O_SYNC
++#endif
+ #ifndef O_DSYNC
+ #define O_DSYNC O_FSYNC
+ #endif
+diff --git a/src/java.base/share/native/libjli/jli_util.h b/src/java.base/share/native/libjli/jli_util.h
+index 3512b1e96..e60f7581f 100644
+--- a/src/java.base/share/native/libjli/jli_util.h
++++ b/src/java.base/share/native/libjli/jli_util.h
+@@ -108,6 +108,9 @@ JLI_CmdToArgs(char *cmdline);
+ #define _LARGFILE64_SOURCE
+ #define JLI_Lseek                       lseek64
+ #endif
++#ifdef SERENITY
++#define JLI_Lseek                       lseek
++#endif
+ #ifdef MACOSX
+ #define JLI_Lseek                       lseek
+ #endif
+diff --git a/src/java.base/share/native/libzip/zip_util.c b/src/java.base/share/native/libzip/zip_util.c
+index fbbd9d850..eef30b9e4 100644
+--- a/src/java.base/share/native/libzip/zip_util.c
++++ b/src/java.base/share/native/libzip/zip_util.c
+@@ -46,7 +46,7 @@
+ #include "zip_util.h"
+ #include <zlib.h>
+ 
+-#ifdef _ALLBSD_SOURCE
++#if defined(_ALLBSD_SOURCE) || defined(SERENITY)
+ #define off64_t off_t
+ #define mmap64 mmap
+ #endif
+diff --git a/src/java.base/unix/classes/sun/nio/fs/UnixConstants.java.template b/src/java.base/unix/classes/sun/nio/fs/UnixConstants.java.template
+index d60283d24..5d428521b 100644
+--- a/src/java.base/unix/classes/sun/nio/fs/UnixConstants.java.template
++++ b/src/java.base/unix/classes/sun/nio/fs/UnixConstants.java.template
+@@ -31,6 +31,95 @@
+ #include <fcntl.h>
+ #include <sys/stat.h>
+ 
++#ifdef SERENITY
++
++#define ESUCCESS 0
++#define EPERM 1
++#define ENOENT 2
++#define ESRCH 3
++#define EINTR 4
++#define EIO 5
++#define ENXIO 6
++#define E2BIG 7
++#define ENOEXEC 8
++#define EBADF 9
++#define ECHILD 10
++#define EAGAIN 12
++#define ENOMEM 12
++#define EACCES 13
++#define EFAULT 14
++#define ENOTBLK 15
++#define EBUSY 16
++#define EEXIST 17
++#define EXDEV 18
++#define ENODEV 19
++#define ENOTDIR 20
++#define EISDIR 21
++#define EINVAL 22
++#define ENFILE 23
++#define EMFILE 24
++#define ENOTTY 25
++#define ETXTBSY 26
++#define EFBIG 27
++#define ENOSPC 28
++#define ESPIPE 29
++#define EROFS 30
++#define EMLINK 31
++#define EPIPE 32
++#define ERANGE 33
++#define ENAMETOOLONG 34
++#define ELOOP 35
++#define EOVERFLOW 36
++#define EOPNOTSUPP 37
++#define ENOSYS 38
++#define ENOTIMPL 39
++#define EAFNOSUPPORT 40
++#define ENOTSOCK 41
++#define EADDRINUSE 42
++#define ENOTEMPTY 43
++#define EDOM 44
++#define ECONNREFUSED 45
++#define EHOSTDOWN 46
++#define EADDRNOTAVAIL 47
++#define EISCONN 48
++#define ECONNABORTED 49
++#define EALREADY 50
++#define ECONNRESET 51
++#define EDESTADDRREQ 52
++#define EHOSTUNREACH 53
++#define EILSEQ 54
++#define EMSGSIZE 55
++#define ENETDOWN 56
++#define ENETUNREACH 57
++#define ENETRESET 58
++#define ENOBUFS 59
++#define ENOLCK 60
++#define ENOMSG 61
++#define ENOPROTOOPT 62
++#define ENOTCONN 63
++#define ESHUTDOWN 64
++#define ETOOMANYREFS 65
++#define EPROTONOSUPPORT 66
++#define ESOCKTNOSUPPORT 67
++#define EDEADLK 68
++#define ETIMEDOUT 69
++#define EPROTOTYPE 70
++#define EINPROGRESS 71
++#define ENOTHREAD 72
++#define EPROTO 73
++#define ENOTSUP 74
++#define EPFNOSUPPORT 75
++#define EDQUOT 76
++#define EDIRINTOSELF 77
++#define ENOTRECOVERABLE 78
++#define ECANCELED 79
++#define EMAXERRNO 80
++
++
++#define EWOULDBLOCK EAGAIN //Serenity doesn't define it
++#define ENODATA EMAXERRNO
++#endif
++
+ /* To be able to name the Java constants the same as the C constants without
+    having the preprocessor rewrite those identifiers, add PREFIX_ to all
+    identifiers matching a C constant. The PREFIX_ is filtered out in the
+@@ -120,7 +210,7 @@ class UnixConstants {
+ // fgetxattr error codes for absent attributes depend on the OS:
+ #ifdef _ALLBSD_SOURCE
+     static final int PREFIX_XATTR_NOT_FOUND = ENOATTR;
+-#elif __linux__
++#elif defined(__linux__)
+     static final int PREFIX_XATTR_NOT_FOUND = ENODATA;
+ #else
+     // not supported (dummy values will not be used at runtime).
+diff --git a/src/java.base/unix/native/libjava/ProcessHandleImpl_unix.c b/src/java.base/unix/native/libjava/ProcessHandleImpl_unix.c
+index d53e88764..eddb5f169 100644
+--- a/src/java.base/unix/native/libjava/ProcessHandleImpl_unix.c
++++ b/src/java.base/unix/native/libjava/ProcessHandleImpl_unix.c
+@@ -488,7 +488,7 @@ void unix_getUserInfo(JNIEnv* env, jobject jinfo, uid_t uid) {
+  * The following functions are common on Solaris, Linux and AIX.
+  */
+ 
+-#if defined (__linux__) || defined(_AIX)
++#if defined (__linux__) || defined(_AIX) || defined(SERENITY)
+ 
+ /*
+  * Returns the children of the requested pid and optionally each parent and
+diff --git a/src/java.base/unix/native/libjava/TimeZone_md.c b/src/java.base/unix/native/libjava/TimeZone_md.c
+index 94dfc207f..2a6c3851a 100644
+--- a/src/java.base/unix/native/libjava/TimeZone_md.c
++++ b/src/java.base/unix/native/libjava/TimeZone_md.c
+@@ -53,7 +53,7 @@ static char *isFileIdentical(char* buf, size_t size, char *pathname);
+ #define filegets        fgets
+ #define fileclose       fclose
+ 
+-#if defined(_ALLBSD_SOURCE)
++#if defined(_ALLBSD_SOURCE) || defined(SERENITY)
+ #define stat64 stat
+ #define lstat64 lstat
+ #define fstat64 fstat
+@@ -75,7 +75,7 @@ static const char popularZones[][4] = {"UTC", "GMT"};
+ static const char *ETC_ENVIRONMENT_FILE = "/etc/environment";
+ #endif
+ 
+-#if defined(__linux__) || defined(MACOSX)
++#if defined(__linux__) || defined(MACOSX) || defined(SERENITY)
+ 
+ /*
+  * Returns a pointer to the zone ID portion of the given zoneinfo file
+diff --git a/src/java.base/unix/native/libjava/io_util_md.c b/src/java.base/unix/native/libjava/io_util_md.c
+index e207c57d4..8afabc544 100644
+--- a/src/java.base/unix/native/libjava/io_util_md.c
++++ b/src/java.base/unix/native/libjava/io_util_md.c
+@@ -30,7 +30,7 @@
+ #include <string.h>
+ #include <unistd.h>
+ 
+-#if defined(__linux__) || defined(_ALLBSD_SOURCE) || defined(_AIX)
++#if defined(__linux__) || defined(_ALLBSD_SOURCE) || defined(_AIX) || defined(SERENITY)
+ #include <sys/ioctl.h>
+ #endif
+ 
+diff --git a/src/java.base/unix/native/libjava/io_util_md.h b/src/java.base/unix/native/libjava/io_util_md.h
+index 3dccf64f4..3e9e7d3b0 100644
+--- a/src/java.base/unix/native/libjava/io_util_md.h
++++ b/src/java.base/unix/native/libjava/io_util_md.h
+@@ -66,7 +66,7 @@ FD getFD(JNIEnv *env, jobject cur, jfieldID fid);
+ #define IO_SetLength handleSetLength
+ #define IO_GetLength handleGetLength
+ 
+-#ifdef _ALLBSD_SOURCE
++#if defined(_ALLBSD_SOURCE) || defined(SERENITY)
+ #define open64 open
+ #define fstat64 fstat
+ #define stat64 stat
+@@ -77,6 +77,10 @@ FD getFD(JNIEnv *env, jobject cur, jfieldID fid);
+ #define IO_Lseek lseek64
+ #endif
+ 
++#ifdef SERENITY
++#define statvfs64 statvfs
++#endif
++
+ /*
+  * On Solaris, the handle field is unused
+  */
+diff --git a/src/java.base/unix/native/libjsig/jsig.c b/src/java.base/unix/native/libjsig/jsig.c
+index 1108b2f9c..d891aab93 100644
+--- a/src/java.base/unix/native/libjsig/jsig.c
++++ b/src/java.base/unix/native/libjsig/jsig.c
+@@ -100,6 +100,10 @@ static sa_handler_t call_os_signal(int sig, sa_handler_t disp,
+                                    bool is_sigset) {
+   sa_handler_t res;
+ 
++#ifdef SERENITY
++#define RTLD_NEXT 0 //stub out RTLD_NEXT
++#endif
++
+   if (os_signal == NULL) {
+     // Deprecation warning first time through
+     printf(HOTSPOT_VM_DISTRO " VM warning: the use of signal() and sigset() "
+diff --git a/src/java.base/unix/native/libnet/Inet4AddressImpl.c b/src/java.base/unix/native/libnet/Inet4AddressImpl.c
+index b165be7ce..a4cd70003 100644
+--- a/src/java.base/unix/native/libnet/Inet4AddressImpl.c
++++ b/src/java.base/unix/native/libnet/Inet4AddressImpl.c
+@@ -335,6 +335,7 @@ static jboolean
+ ping4(JNIEnv *env, jint fd, SOCKETADDRESS *sa, SOCKETADDRESS *netif,
+       jint timeout, jint ttl)
+ {
++#ifndef SERENITY
+     jint n, size = 60 * 1024, hlen, tmout2, seq = 1;
+     socklen_t len;
+     unsigned char sendbuf[1500], recvbuf[1500];
+@@ -438,6 +439,8 @@ ping4(JNIEnv *env, jint fd, SOCKETADDRESS *sa, SOCKETADDRESS *netif,
+         timeout -= 1000;
+     } while (timeout > 0);
+     close(fd);
++#endif
++    //FIXME implement setsockopt(IPPROTO_IP)
+     return JNI_FALSE;
+ }
+ 
+diff --git a/src/java.base/unix/native/libnet/Inet6AddressImpl.c b/src/java.base/unix/native/libnet/Inet6AddressImpl.c
+index 058f3d3a7..61460fda8 100644
+--- a/src/java.base/unix/native/libnet/Inet6AddressImpl.c
++++ b/src/java.base/unix/native/libnet/Inet6AddressImpl.c
+@@ -29,7 +29,9 @@
+ #include <sys/time.h>
+ #include <sys/types.h>
+ #include <netinet/in.h>
++#ifndef SERENITY
+ #include <netinet/icmp6.h>
++#endif
+ 
+ #if defined(_ALLBSD_SOURCE)
+ #include <ifaddrs.h>
+@@ -539,6 +541,7 @@ static jboolean
+ ping6(JNIEnv *env, jint fd, SOCKETADDRESS *sa, SOCKETADDRESS *netif,
+       jint timeout, jint ttl)
+ {
++#ifndef SERENITY
+     jint n, size = 60 * 1024, tmout2, seq = 1;
+     socklen_t len;
+     unsigned char sendbuf[1500], recvbuf[1500];
+@@ -643,6 +646,7 @@ ping6(JNIEnv *env, jint fd, SOCKETADDRESS *sa, SOCKETADDRESS *netif,
+         timeout -= 1000;
+     } while (timeout > 0);
+     close(fd);
++#endif
+     return JNI_FALSE;
+ }
+ 
+diff --git a/src/java.base/unix/native/libnet/NetworkInterface.c b/src/java.base/unix/native/libnet/NetworkInterface.c
+index 990bc1bcc..fdeff2bf6 100644
+--- a/src/java.base/unix/native/libnet/NetworkInterface.c
++++ b/src/java.base/unix/native/libnet/NetworkInterface.c
+@@ -43,6 +43,10 @@
+ #include <ifaddrs.h>
+ #endif
+ 
++#if defined(SERENITY)
++#include <ifaddrs.h>
++#endif
++
+ #include "net_util.h"
+ 
+ #include "java_net_InetAddress.h"
+@@ -1351,7 +1355,7 @@ static int getMacAddress
+ static int getMTU(JNIEnv *env, int sock, const char *ifname) {
+     struct ifreq if2;
+     memset((char *)&if2, 0, sizeof(if2));
+-    strncpy(if2.ifr_name, ifname, sizeof(if2.ifr_name) - 1);
++    memcpy(if2.ifr_name, ifname, sizeof(if2.ifr_name) - 1);
+ 
+     if (ioctl(sock, SIOCGIFMTU, (char *)&if2) < 0) {
+         JNU_ThrowByNameWithMessageAndLastError
+@@ -1664,7 +1668,7 @@ static int getFlags(int sock, const char *ifname, int *flags) {
+ #endif /* _AIX */
+ 
+ /** BSD **/
+-#if defined(_ALLBSD_SOURCE)
++#if defined(_ALLBSD_SOURCE) || defined(SERENITY)
+ 
+ /*
+  * Opens a socket for further ioctl calls. Tries AF_INET socket first and
+@@ -1803,6 +1807,7 @@ static int getMacAddress
+   (JNIEnv *env, const char *ifname, const struct in_addr *addr,
+    unsigned char *buf)
+ {
++#ifndef SERENITY // FIXME: define sockaddr_dl in net/if_dl.h
+     struct ifaddrs *ifa0, *ifa;
+     struct sockaddr *saddr;
+     int i;
+@@ -1827,7 +1832,7 @@ static int getMacAddress
+         }
+         freeifaddrs(ifa0);
+     }
+-
++#endif
+     return -1;
+ }
+ 
+diff --git a/src/java.base/unix/native/libnet/net_util_md.h b/src/java.base/unix/native/libnet/net_util_md.h
+index 68835987b..f99b11207 100644
+--- a/src/java.base/unix/native/libnet/net_util_md.h
++++ b/src/java.base/unix/native/libnet/net_util_md.h
+@@ -30,6 +30,10 @@
+ #include <poll.h>
+ #include <sys/socket.h>
+ 
++#ifdef SERENITY
++#include <netinet/in.h>
++#endif
++
+ /************************************************************************
+  * Macros and constants
+  */
+diff --git a/src/java.base/unix/native/libnio/MappedMemoryUtils.c b/src/java.base/unix/native/libnio/MappedMemoryUtils.c
+index e90acd286..34787fff4 100644
+--- a/src/java.base/unix/native/libnio/MappedMemoryUtils.c
++++ b/src/java.base/unix/native/libnio/MappedMemoryUtils.c
+@@ -58,6 +58,7 @@ JNIEXPORT jboolean JNICALL
+ Java_java_nio_MappedMemoryUtils_isLoaded0(JNIEnv *env, jobject obj, jlong address,
+                                          jlong len, jlong numPages)
+ {
++#ifndef SERENITY
+     jboolean loaded = JNI_TRUE;
+     int result = 0;
+     long i = 0;
+@@ -100,6 +101,9 @@ Java_java_nio_MappedMemoryUtils_isLoaded0(JNIEnv *env, jobject obj, jlong addres
+     }
+     free(vec);
+     return loaded;
++#else
++    return JNI_FALSE; //FIXME implement mincore() or equivalent
++#endif
+ }
+ 
+ 
+diff --git a/src/java.base/unix/native/libnio/ch/FileChannelImpl.c b/src/java.base/unix/native/libnio/ch/FileChannelImpl.c
+index 8cc753e6a..28fa55269 100644
+--- a/src/java.base/unix/native/libnio/ch/FileChannelImpl.c
++++ b/src/java.base/unix/native/libnio/ch/FileChannelImpl.c
+@@ -39,6 +39,9 @@
+ #include <sys/uio.h>
+ #define lseek64 lseek
+ #define mmap64 mmap
++#elif defined(SERENITY)
++#define lseek64 lseek
++#define mmap64 mmap
+ #endif
+ 
+ #include "jni.h"
+diff --git a/src/java.base/unix/native/libnio/ch/FileDispatcherImpl.c b/src/java.base/unix/native/libnio/ch/FileDispatcherImpl.c
+index c0c31d6ab..3a4d8a632 100644
+--- a/src/java.base/unix/native/libnio/ch/FileDispatcherImpl.c
++++ b/src/java.base/unix/native/libnio/ch/FileDispatcherImpl.c
+@@ -40,7 +40,7 @@
+ #include <sys/ioctl.h>
+ #endif
+ 
+-#if defined(_ALLBSD_SOURCE)
++#if defined(_ALLBSD_SOURCE) || defined(SERENITY)
+ #define lseek64 lseek
+ #define stat64 stat
+ #define flock64 flock
+@@ -54,6 +54,11 @@
+ #define fdatasync fsync
+ #endif
+ 
++#ifdef SERENITY
++#define statvfs64 statvfs
++#define fstatvfs64 fstatvfs
++#endif
++
+ #include "jni.h"
+ #include "jni_util.h"
+ #include "jvm.h"
+diff --git a/src/java.base/unix/native/libnio/ch/FileKey.c b/src/java.base/unix/native/libnio/ch/FileKey.c
+index bdb42a632..a433cdf01 100644
+--- a/src/java.base/unix/native/libnio/ch/FileKey.c
++++ b/src/java.base/unix/native/libnio/ch/FileKey.c
+@@ -30,7 +30,7 @@
+ #include "nio_util.h"
+ #include "sun_nio_ch_FileKey.h"
+ 
+-#ifdef _ALLBSD_SOURCE
++#if defined(_ALLBSD_SOURCE) || defined(SERENITY)
+ #define stat64 stat
+ 
+ #define fstat64 fstat
+diff --git a/src/java.base/unix/native/libnio/ch/NativeThread.c b/src/java.base/unix/native/libnio/ch/NativeThread.c
+index 92dcb9e56..4bf09d03b 100644
+--- a/src/java.base/unix/native/libnio/ch/NativeThread.c
++++ b/src/java.base/unix/native/libnio/ch/NativeThread.c
+@@ -40,7 +40,7 @@
+ #elif defined(_AIX)
+   /* Also defined in net/aix_close.c */
+   #define INTERRUPT_SIGNAL (SIGRTMAX - 1)
+-#elif defined(_ALLBSD_SOURCE)
++#elif defined(_ALLBSD_SOURCE) || defined(SERENITY)
+   /* Also defined in net/bsd_close.c */
+   #define INTERRUPT_SIGNAL SIGIO
+ #else
+diff --git a/src/java.base/unix/native/libnio/ch/Net.c b/src/java.base/unix/native/libnio/ch/Net.c
+index 42a07359d..ca1401861 100644
+--- a/src/java.base/unix/native/libnio/ch/Net.c
++++ b/src/java.base/unix/native/libnio/ch/Net.c
+@@ -701,7 +701,7 @@ JNIEXPORT jint JNICALL
+ Java_sun_nio_ch_Net_blockOrUnblock6(JNIEnv *env, jobject this, jboolean block, jobject fdo,
+                                     jbyteArray group, jint index, jbyteArray source)
+ {
+-#ifdef __APPLE__
++#if defined(__APPLE__) || defined(SERENITY)
+     /* no IPv6 exclude-mode filtering for now */
+     return IOS_UNAVAILABLE;
+ #else
+diff --git a/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c b/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c
+index 9df8be1e6..993e240db 100644
+--- a/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c
++++ b/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c
+@@ -57,7 +57,7 @@
+ #include <string.h>
+ #endif
+ 
+-#ifdef _ALLBSD_SOURCE
++#if defined(_ALLBSD_SOURCE) || defined(SERENITY)
+ #include <string.h>
+ 
+ #define stat64 stat
+@@ -1114,6 +1114,7 @@ Java_sun_nio_fs_UnixNativeDispatcher_getgrgid(JNIEnv* env, jclass this, jint gid
+     int retry;
+ 
+     /* initial size of buffer for group record */
++#ifndef SERENITY
+     buflen = (int)sysconf(_SC_GETGR_R_SIZE_MAX);
+     if (buflen == -1)
+         buflen = ENT_BUF_SIZE;
+@@ -1156,6 +1157,24 @@ Java_sun_nio_fs_UnixNativeDispatcher_getgrgid(JNIEnv* env, jclass this, jint gid
+ 
+     } while (retry);
+ 
++#else
++    // FIXME: Not thread safe, implement getgrgid_r
++    errno = 0;
++    struct group * g = getgrgid(gid);
++    if (g == NULL || g->gr_name == NULL || *(g->gr_name) == '\0') {
++        if (errno == 0)
++            errno = ENOENT;
++        throwUnixException(env, errno);
++    }
++    else {
++        jsize len = strlen(g->gr_name);
++        result = (*env)->NewByteArray(env, len);
++        if (result != NULL) {
++            (*env)->SetByteArrayRegion(env, result, 0, len, (jbyte*)(g->gr_name));
++        }
++    }
++#endif
++
+     return result;
+ }
+ 
+@@ -1204,6 +1223,7 @@ Java_sun_nio_fs_UnixNativeDispatcher_getgrnam0(JNIEnv* env, jclass this,
+     jlong nameAddress)
+ {
+     jint gid = -1;
++#ifndef SERENITY
+     int buflen, retry;
+ 
+     /* initial size of buffer for group record */
+@@ -1248,6 +1268,23 @@ Java_sun_nio_fs_UnixNativeDispatcher_getgrnam0(JNIEnv* env, jclass this,
+         free(grbuf);
+ 
+     } while (retry);
++#else
++    // FIXME: Not thread safe, implement getgrnam_r
++    const char* name = (const char*)jlong_to_ptr(nameAddress);
++    errno = 0;
++    struct group * g = getgrnam(name);
++    if (g == NULL || g->gr_name == NULL || *(g->gr_name) == '\0') {
++        /* not found or error */
++        if (errno != 0 && errno != ENOENT && errno != ESRCH &&
++            errno != EBADF && errno != EPERM)
++        {
++            throwUnixException(env, errno);
++        }
++    }
++    else {
++        gid = g->gr_gid;
++    }
++#endif
+ 
+     return gid;
+ }

+ 259 - 0
Ports/OpenJDK/patches/0008-java.base-Enable-java.lang.Process-on-serenity.patch

@@ -0,0 +1,259 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Andrew Kaster <andrewdkaster@gmail.com>
+Date: Sun, 12 Jun 2022 23:15:17 -0600
+Subject: [PATCH] java.base: Enable java.lang.Process on serenity
+
+---
+ make/modules/java.base/Launcher.gmk           |   2 +-
+ make/modules/java.base/lib/CoreLibraries.gmk  |   3 +
+ .../libjava/ProcessHandleImpl_serenity.cpp    | 167 ++++++++++++++++++
+ .../unix/classes/java/lang/ProcessImpl.java   |   7 +-
+ 4 files changed, 177 insertions(+), 2 deletions(-)
+ create mode 100644 src/java.base/serenity/native/libjava/ProcessHandleImpl_serenity.cpp
+
+diff --git a/make/modules/java.base/Launcher.gmk b/make/modules/java.base/Launcher.gmk
+index 700ddefda..78c884dae 100644
+--- a/make/modules/java.base/Launcher.gmk
++++ b/make/modules/java.base/Launcher.gmk
+@@ -73,7 +73,7 @@ endif
+ 
+ ################################################################################
+ 
+-ifeq ($(call isTargetOs, macosx aix linux), true)
++ifeq ($(call isTargetOs, macosx aix linux serenity), true)
+   $(eval $(call SetupJdkExecutable, BUILD_JSPAWNHELPER, \
+       NAME := jspawnhelper, \
+       SRC := $(TOPDIR)/src/$(MODULE)/unix/native/jspawnhelper, \
+diff --git a/make/modules/java.base/lib/CoreLibraries.gmk b/make/modules/java.base/lib/CoreLibraries.gmk
+index 0a61d009f..7867a3095 100644
+--- a/make/modules/java.base/lib/CoreLibraries.gmk
++++ b/make/modules/java.base/lib/CoreLibraries.gmk
+@@ -90,6 +90,8 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBJAVA, \
+     OPTIMIZATION := HIGH, \
+     CFLAGS := $(CFLAGS_JDKLIB) \
+         $(LIBJAVA_CFLAGS), \
++    CXXFLAGS := $(CXXFLAGS_JDKLIB) \
++        $(LIBJAVA_CXXFLAGS), \
+     jdk_util.c_CFLAGS := $(VERSION_CFLAGS), \
+     EXTRA_HEADER_DIRS := libfdlibm, \
+     WARNINGS_AS_ERRORS_xlc := false, \
+@@ -102,6 +104,7 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBJAVA, \
+     LIBS_unix := -ljvm, \
+     LIBS_linux := $(LIBDL), \
+     LIBS_aix := $(LIBDL) $(LIBM),\
++    LIBS_serenity := $(LIBDL) -lcore, \
+     LIBS_macosx := -framework CoreFoundation \
+         -framework Foundation \
+         -framework SystemConfiguration, \
+diff --git a/src/java.base/serenity/native/libjava/ProcessHandleImpl_serenity.cpp b/src/java.base/serenity/native/libjava/ProcessHandleImpl_serenity.cpp
+new file mode 100644
+index 000000000..e9bb2ec4a
+--- /dev/null
++++ b/src/java.base/serenity/native/libjava/ProcessHandleImpl_serenity.cpp
+@@ -0,0 +1,167 @@
++/*
++ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
++ *
++ * This code is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License version 2 only, as
++ * published by the Free Software Foundation.  Oracle designates this
++ * particular file as subject to the "Classpath" exception as provided
++ * by Oracle in the LICENSE file that accompanied this code.
++ *
++ * This code is distributed in the hope that it will be useful, but WITHOUT
++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
++ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
++ * version 2 for more details (a copy is included in the LICENSE file that
++ * accompanied this code).
++ *
++ * You should have received a copy of the GNU General Public License version
++ * 2 along with this work; if not, write to the Free Software Foundation,
++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
++ *
++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
++ * or visit www.oracle.com if you need additional information or have any
++ * questions.
++ */
++
++#define AK_DONT_REPLACE_STD
++
++#include "jvm.h"
++#include "jni.h"
++#include "jni_util.h"
++#include "java_lang_String.h"
++
++extern "C" {
++#include "ProcessHandleImpl_unix.h"
++}
++
++#include <AK/JsonArray.h>
++#include <LibCore/ProcessStatisticsReader.h>
++#include <LibCore/Stream.h>
++#include <stdio.h>
++#include <string.h>
++
++/*
++ * Implementation of native ProcessHandleImpl functions for SERENITY.
++ * See ProcessHandleImpl_unix.c for more details.
++ */
++
++#define JAVA_TRY(expression, message) \
++    ({                                                                                   \
++        auto _temporary_result = (expression);                                           \
++        if (_temporary_result.is_error()) [[unlikely]]                                   \
++            return throwSerenityError(env, _temporary_result.release_error(), (message));  \
++        _temporary_result.release_value();                                               \
++    })
++
++
++static RefPtr<Core::File> proc_all;
++
++extern "C" {
++void os_initNative(JNIEnv *env, jclass clazz) {
++    proc_all = MUST(Core::File::open("/proc/all", Core::OpenMode::ReadOnly));
++}
++
++jint os_getChildren(JNIEnv *env, jlong jpid, jlongArray jarray,
++                    jlongArray jparentArray, jlongArray jstimesArray) {
++    return unix_getChildren(env, jpid, jarray, jparentArray, jstimesArray);
++}
++
++pid_t os_getParentPidAndTimings(JNIEnv *env, pid_t pid, jlong *total, jlong *start) {
++    auto maybe_stats = Core::ProcessStatisticsReader::get_all(proc_all);
++    if (!maybe_stats.has_value()) {
++        JNU_ThrowByNameWithLastError(env,
++            "java/lang/RuntimeException", "ProcessStatisticsReader::get_all failed");
++            return -1;
++    }
++    auto stats = maybe_stats.release_value();
++    auto proc_it = find_if(stats.processes.begin(), stats.processes.end(), [pid](auto& proc_stats) {
++        return proc_stats.pid == pid;
++    });
++    if (proc_it == stats.processes.end()) {
++        JNU_ThrowByNameWithLastError(env,
++            "java/lang/RuntimeException", "Selected pid does not exist");
++        return -1;
++    }
++    auto& proc = *proc_it;
++
++    for (auto& thread : proc.threads) {
++        *total += thread.time_user + thread.time_kernel;
++    }
++
++    *start = 0; // FIXME: When did thread start? not reported in /proc/all
++
++    return proc.ppid;
++}
++
++
++static void throwSerenityError(JNIEnv* env, Error const& e, StringView msg) {
++    char err_buf[256];
++    if (e.is_errno())
++        getErrorString(e.code(), err_buf, sizeof(err_buf));
++    else
++        strncpy(err_buf, e.string_literal().characters_without_null_termination(), sizeof(err_buf) - 1);
++    jstring s = JNU_NewStringPlatform(env, err_buf);
++    if (s != NULL) {
++        jobject x = JNU_NewObjectByName(env, "java/lang/RuntimeException",
++                                        "(Ljava/lang/String;)V", s);
++        if (x != NULL) {
++            env->Throw((jthrowable)x);
++        }
++    }
++    if (!env->ExceptionOccurred()) {
++        JNU_ThrowByName(env, "java/lang/RuntimeException", msg.characters_without_null_termination());
++    }
++}
++
++void os_getCmdlineAndUserInfo(JNIEnv *env, jobject jinfo, pid_t pid) {
++    auto maybe_stats = Core::ProcessStatisticsReader::get_all(proc_all);
++    if (!maybe_stats.has_value()) {
++        JNU_ThrowByNameWithLastError(env,
++            "java/lang/RuntimeException", "ProcessStatisticsReader::get_all failed");
++        return;
++    }
++
++    auto stats = maybe_stats.release_value();
++    auto proc_it = find_if(stats.processes.begin(), stats.processes.end(), [pid](auto& proc_stats) {
++        return proc_stats.pid == pid;
++    });
++    if (proc_it == stats.processes.end()) {
++        JNU_ThrowByNameWithLastError(env,
++            "java/lang/RuntimeException", "Selected pid does not exist");
++        return;
++    }
++    auto& proc = *proc_it;
++
++    unix_getUserInfo(env, jinfo, proc.pid);
++    JNU_CHECK_EXCEPTION(env);
++
++    char cmdline_name[256];
++    snprintf(cmdline_name , sizeof(cmdline_name) - 1, "/proc/%d/cmdline", pid);
++
++    auto cmdline_file = JAVA_TRY(Core::Stream::File::open(cmdline_name, Core::Stream::OpenMode::Read), "Unable to open /proc/pid/cmdline"sv);
++    auto contents = JAVA_TRY(cmdline_file->read_all(), "Unable to read /proc/pid/cmdline"sv);
++    auto cmdline = JAVA_TRY(JsonValue::from_string(contents), "Invalid JSON in /proc/pid/cmdline"sv);
++
++    if (!cmdline.is_array())
++        return throwSerenityError(env, Error::from_string_literal("Not an array"), "Unexpected JSON in /proc/pid/cmdline");
++
++    jstring cmdexe = JNU_NewStringPlatform(env, cmdline.as_array()[0].as_string().characters());
++    env->ExceptionClear();        // unconditionally clear any exception
++    env->SetObjectField(jinfo, ProcessHandleImpl_Info_commandID, cmdexe);
++
++    int arr_size = cmdline.as_array().size();
++    jclass string_clazz = JNU_ClassString(env);
++    CHECK_NULL(string_clazz);
++    jobjectArray java_cmdline = env->NewObjectArray(arr_size, string_clazz, NULL);
++    CHECK_NULL(java_cmdline);
++    jstring elem = NULL;
++    for (int i = 0; i < arr_size; ++i) {
++        elem = JNU_NewStringPlatform(env, cmdline.as_array()[i].as_string().characters());
++        CHECK_NULL(elem);
++        env->SetObjectArrayElement(java_cmdline, i, elem);
++        JNU_CHECK_EXCEPTION(env);
++    }
++    env->SetObjectField(jinfo, ProcessHandleImpl_Info_argumentsID, java_cmdline);
++    JNU_CHECK_EXCEPTION(env);
++}
++}
+diff --git a/src/java.base/unix/classes/java/lang/ProcessImpl.java b/src/java.base/unix/classes/java/lang/ProcessImpl.java
+index 2bf36f8f1..317bbf158 100644
+--- a/src/java.base/unix/classes/java/lang/ProcessImpl.java
++++ b/src/java.base/unix/classes/java/lang/ProcessImpl.java
+@@ -89,7 +89,9 @@ final class ProcessImpl extends Process {
+ 
+         BSD(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK),
+ 
+-        AIX(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK);
++        AIX(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK),
++
++        SERENITY(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK);
+ 
+         final LaunchMechanism defaultLaunchMechanism;
+         final Set<LaunchMechanism> validLaunchMechanisms;
+@@ -135,6 +137,7 @@ final class ProcessImpl extends Process {
+             if (osName.equals("Linux")) { return LINUX; }
+             if (osName.contains("OS X")) { return BSD; }
+             if (osName.equals("AIX")) { return AIX; }
++            if (osName.equals("SerenityOS")) { return SERENITY; }
+ 
+             throw new Error(osName + " is not a supported OS platform.");
+         }
+@@ -348,6 +351,7 @@ final class ProcessImpl extends Process {
+         switch (platform) {
+             case LINUX:
+             case BSD:
++            case SERENITY:
+                 stdin = (fds[0] == -1) ?
+                         ProcessBuilder.NullOutputStream.INSTANCE :
+                         new ProcessPipeOutputStream(fds[0]);
+@@ -467,6 +471,7 @@ final class ProcessImpl extends Process {
+             case LINUX:
+             case BSD:
+             case AIX:
++            case SERENITY:
+                 // There is a risk that pid will be recycled, causing us to
+                 // kill the wrong process!  So we only terminate processes
+                 // that appear to still be running.  Even with this check,

+ 51 - 0
Ports/OpenJDK/patches/ReadMe.md

@@ -0,0 +1,51 @@
+# Patches for OpenJDK on SerenityOS
+
+## `0001-make-Add-Serenity-support-masquerading-as-BSD-when-n.patch`
+
+make: Add Serenity support, masquerading as BSD when necessary
+
+
+## `0002-make-Build-with-c-20-when-targeting-serenity.patch`
+
+make: Build with c++20 when targeting serenity
+
+
+## `0003-make-Remove-CUPS-dependency.patch`
+
+make: Remove CUPS dependency
+
+
+## `0004-hotspot-Add-workarounds-for-BSD-differences-from-ser.patch`
+
+hotspot: Add workarounds for BSD differences from serenity
+
+For the most part, we can pretend to be *BSD.
+
+However, for some methods, we need to convince hotspot that we're macOS,
+and others need serenity-specific ifdefs due to the lack of sysctl in
+serenity.
+
+
+## `0005-hotspot-Update-non-BSD-native-modules-for-Serenity.patch`
+
+hotspot: Update non-BSD native modules for Serenity
+
+
+## `0006-Add-serenity-specific-modules-to-java.base-and-jdk.a.patch`
+
+Add serenity-specific modules to java.base and jdk.attach
+
+It would be nice to re-direct the build to the same files *BSD use, but
+for now we've got our own copy
+
+
+## `0007-java.base-Update-native-modules-to-support-Serenity.patch`
+
+java.base: Update native modules to support Serenity
+
+
+## `0008-java.base-Enable-java.lang.Process-on-serenity.patch`
+
+java.base: Enable java.lang.Process on serenity
+
+