diff -urN /tmp/a/attachListener_solaris.cpp b/src/hotspot/os/solaris/attachListener_solaris.cpp
--- /tmp/a/attachListener_solaris.cpp	1970-01-01 01:00:00.000000000 +0100
+++ b/src/hotspot/os/solaris/attachListener_solaris.cpp	2024-12-05 11:44:56.839300592 +0000
@@ -0,0 +1,755 @@
+/*
+ * Copyright (c) 2005, 2019, 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.
+ *
+ * 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 "precompiled.hpp"
+#include "logging/log.hpp"
+#include "runtime/interfaceSupport.inline.hpp"
+#include "runtime/os.inline.hpp"
+#include "services/attachListener.hpp"
+#include "services/dtraceAttacher.hpp"
+#include "utilities/vmError.hpp"
+
+#include <door.h>
+#include <limits.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+
+// stropts.h uses STR in stream ioctl defines
+#undef STR
+#include <stropts.h>
+#undef STR
+#define STR(a) #a
+
+// The attach mechanism on Solaris is implemented using the Doors IPC
+// mechanism. The first tool to attempt to attach causes the attach
+// listener thread to startup. This thread creates a door that is
+// associated with a function that enqueues an operation to the attach
+// listener. The door is attached to a file in the file system so that
+// client (tools) can locate it. To enqueue an operation to the VM the
+// client calls through the door which invokes the enqueue function in
+// this process. The credentials of the client are checked and if the
+// effective uid matches this process then the operation is enqueued.
+// When an operation completes the attach listener is required to send the
+// operation result and any result data to the client. In this implementation
+// the result is returned via a UNIX domain socket. A pair of connected
+// sockets (socketpair) is created in the enqueue function and the file
+// descriptor for one of the sockets is returned to the client as the
+// return from the door call. The other end is retained in this process.
+// When the operation completes the result is sent to the client and
+// the socket is closed.
+
+// forward reference
+class SolarisAttachOperation;
+
+class SolarisAttachListener: AllStatic {
+ private:
+
+  // the path to which we attach the door file descriptor
+  static char _door_path[PATH_MAX+1];
+  static volatile bool _has_door_path;
+
+  // door descriptor returned by door_create
+  static int _door_descriptor;
+
+  static bool _atexit_registered;
+
+  // mutex to protect operation list
+  static mutex_t _mutex;
+
+  // semaphore to wakeup listener thread
+  static sema_t _wakeup;
+
+  static mutex_t* mutex()                               { return &_mutex; }
+  static sema_t* wakeup()                               { return &_wakeup; }
+
+  // enqueued operation list
+  static SolarisAttachOperation* _head;
+  static SolarisAttachOperation* _tail;
+
+  static SolarisAttachOperation* head()                 { return _head; }
+  static void set_head(SolarisAttachOperation* head)    { _head = head; }
+
+  static SolarisAttachOperation* tail()                 { return _tail; }
+  static void set_tail(SolarisAttachOperation* tail)    { _tail = tail; }
+
+  // create the door
+  static int create_door();
+
+ public:
+  enum {
+    ATTACH_PROTOCOL_VER = 1                             // protocol version
+  };
+  enum {
+    ATTACH_ERROR_BADREQUEST     = 100,                  // error code returned by
+    ATTACH_ERROR_BADVERSION     = 101,                  // the door call
+    ATTACH_ERROR_RESOURCE       = 102,
+    ATTACH_ERROR_INTERNAL       = 103,
+    ATTACH_ERROR_DENIED         = 104
+  };
+
+  static void set_door_path(char* path) {
+    if (path == NULL) {
+      _door_path[0] = '\0';
+      _has_door_path = false;
+    } else {
+      strncpy(_door_path, path, PATH_MAX);
+      _door_path[PATH_MAX] = '\0';      // ensure it's nul terminated
+      _has_door_path = true;
+    }
+  }
+
+  static void set_door_descriptor(int dd)               { _door_descriptor = dd; }
+
+  // initialize the listener
+  static int init();
+
+  static bool has_door_path()                           { return _has_door_path; }
+  static char* door_path()                              { return _door_path; }
+  static int door_descriptor()                          { return _door_descriptor; }
+
+  // enqueue an operation
+  static void enqueue(SolarisAttachOperation* op);
+
+  // dequeue an operation
+  static SolarisAttachOperation* dequeue();
+};
+
+
+// SolarisAttachOperation is an AttachOperation that additionally encapsulates
+// a socket connection to the requesting client/tool. SolarisAttachOperation
+// can additionally be held in a linked list.
+
+class SolarisAttachOperation: public AttachOperation {
+ private:
+  friend class SolarisAttachListener;
+
+  // connection to client
+  int _socket;
+
+  // linked list support
+  SolarisAttachOperation* _next;
+
+  SolarisAttachOperation* next()                         { return _next; }
+  void set_next(SolarisAttachOperation* next)            { _next = next; }
+
+ public:
+  void complete(jint res, bufferedStream* st);
+
+  int socket() const                                     { return _socket; }
+  void set_socket(int s)                                 { _socket = s; }
+
+  SolarisAttachOperation(char* name) : AttachOperation(name) {
+    set_socket(-1);
+    set_next(NULL);
+  }
+};
+
+// statics
+char SolarisAttachListener::_door_path[PATH_MAX+1];
+volatile bool SolarisAttachListener::_has_door_path;
+int SolarisAttachListener::_door_descriptor = -1;
+bool SolarisAttachListener::_atexit_registered = false;
+mutex_t SolarisAttachListener::_mutex;
+sema_t SolarisAttachListener::_wakeup;
+SolarisAttachOperation* SolarisAttachListener::_head = NULL;
+SolarisAttachOperation* SolarisAttachListener::_tail = NULL;
+
+// Supporting class to help split a buffer into individual components
+class ArgumentIterator : public StackObj {
+ private:
+  char* _pos;
+  char* _end;
+ public:
+  ArgumentIterator(char* arg_buffer, size_t arg_size) {
+    _pos = arg_buffer;
+    _end = _pos + arg_size - 1;
+  }
+  char* next() {
+    if (*_pos == '\0') {
+      // advance the iterator if possible (null arguments)
+      if (_pos < _end) {
+        _pos += 1;
+      }
+      return NULL;
+    }
+    char* res = _pos;
+    char* next_pos = strchr(_pos, '\0');
+    if (next_pos < _end)  {
+      next_pos++;
+    }
+    _pos = next_pos;
+    return res;
+  }
+};
+
+// Calls from the door function to check that the client credentials
+// match this process. Returns 0 if credentials okay, otherwise -1.
+static int check_credentials() {
+  ucred_t *cred_info = NULL;
+  int ret = -1; // deny by default
+
+  // get client credentials
+  if (door_ucred(&cred_info) == -1) {
+    return -1; // unable to get them, deny
+  }
+
+  // get euid/egid from ucred_free
+  uid_t ucred_euid = ucred_geteuid(cred_info);
+  gid_t ucred_egid = ucred_getegid(cred_info);
+
+  // check that the effective uid/gid matches
+  if (os::Posix::matches_effective_uid_and_gid_or_root(ucred_euid, ucred_egid)) {
+    ret =  0;  // allow
+  }
+
+  ucred_free(cred_info);
+  return ret;
+}
+
+
+// Parses the argument buffer to create an AttachOperation that we should
+// enqueue to the attach listener.
+// The buffer is expected to be formatted as follows:
+// <ver>0<cmd>0<arg>0<arg>0<arg>0
+// where <ver> is the version number (must be "1"), <cmd> is the command
+// name ("load, "datadump", ...) and <arg> is an argument.
+//
+static SolarisAttachOperation* create_operation(char* argp, size_t arg_size, int* err) {
+  // assume bad request until parsed
+  *err = SolarisAttachListener::ATTACH_ERROR_BADREQUEST;
+
+  if (arg_size < 2 || argp[arg_size-1] != '\0') {
+    return NULL;   // no ver or not null terminated
+  }
+
+  // Use supporting class to iterate over the buffer
+  ArgumentIterator args(argp, arg_size);
+
+  // First check the protocol version
+  char* ver = args.next();
+  if (ver == NULL) {
+    return NULL;
+  }
+  if (atoi(ver) != SolarisAttachListener::ATTACH_PROTOCOL_VER) {
+    *err = SolarisAttachListener::ATTACH_ERROR_BADVERSION;
+    return NULL;
+  }
+
+  // Get command name and create the operation
+  char* name = args.next();
+  if (name == NULL || strlen(name) > AttachOperation::name_length_max) {
+    return NULL;
+  }
+  SolarisAttachOperation* op = new SolarisAttachOperation(name);
+
+  // Iterate over the arguments
+  for (int i=0; i<AttachOperation::arg_count_max; i++) {
+    char* arg = args.next();
+    if (arg == NULL) {
+      op->set_arg(i, NULL);
+    } else {
+      if (strlen(arg) > AttachOperation::arg_length_max) {
+        delete op;
+        return NULL;
+      }
+      op->set_arg(i, arg);
+    }
+  }
+
+  // return operation
+  *err = 0;
+  return op;
+}
+
+// This is door function which the client executes via a door_call.
+extern "C" {
+  static void enqueue_proc(void* cookie, char* argp, size_t arg_size,
+                           door_desc_t* dt, uint_t n_desc)
+  {
+    int return_fd = -1;
+    SolarisAttachOperation* op = NULL;
+
+    // wait up to 10 seconds for listener to be up and running
+    jint res = 0;
+    int sleep_count = 0;
+    while (!AttachListener::is_initialized()) {
+      sleep(1); // 1 second
+      sleep_count++;
+      if (sleep_count > 10) { // try for 10 seconds
+        debug_only(warning("door_call when not enabled"));
+        res = (jint)SolarisAttachListener::ATTACH_ERROR_INTERNAL;
+        break;
+      }
+    }
+
+    // check client credentials
+    if (res == 0) {
+      if (check_credentials() != 0) {
+        res = (jint)SolarisAttachListener::ATTACH_ERROR_DENIED;
+      }
+    }
+
+    // if we are stopped at ShowMessageBoxOnError then maybe we can
+    // load a diagnostic library
+    if (res == 0 && VMError::is_error_reported()) {
+      if (ShowMessageBoxOnError) {
+        // TBD - support loading of diagnostic library here
+      }
+
+      // can't enqueue operation after fatal error
+      res = (jint)SolarisAttachListener::ATTACH_ERROR_RESOURCE;
+    }
+
+    // create the operation
+    if (res == 0) {
+      int err;
+      op = create_operation(argp, arg_size, &err);
+      res = (op == NULL) ? (jint)err : 0;
+    }
+
+    // create a pair of connected sockets. Store the file descriptor
+    // for one end in the operation and enqueue the operation. The
+    // file descriptor for the other end will be returned to the client.
+    if (res == 0) {
+      int s[2];
+      if (socketpair(PF_UNIX, SOCK_STREAM, 0, s) < 0) {
+        delete op;
+        res = (jint)SolarisAttachListener::ATTACH_ERROR_RESOURCE;
+      } else {
+        op->set_socket(s[0]);
+        return_fd = s[1];
+        SolarisAttachListener::enqueue(op);
+      }
+    }
+
+    // Return 0 (success) + file descriptor, or non-0 (error)
+    if (res == 0) {
+      door_desc_t desc;
+      // DOOR_RELEASE flag makes sure fd is closed after passing it to
+      // the client.  See door_return(3DOOR) man page.
+      desc.d_attributes = DOOR_DESCRIPTOR | DOOR_RELEASE;
+      desc.d_data.d_desc.d_descriptor = return_fd;
+      door_return((char*)&res, sizeof(res), &desc, 1);
+    } else {
+      door_return((char*)&res, sizeof(res), NULL, 0);
+    }
+  }
+}
+
+// atexit hook to detach the door and remove the file
+extern "C" {
+  static void listener_cleanup() {
+    int dd = SolarisAttachListener::door_descriptor();
+    if (dd >= 0) {
+      SolarisAttachListener::set_door_descriptor(-1);
+      ::close(dd);
+    }
+    if (SolarisAttachListener::has_door_path()) {
+      char* path = SolarisAttachListener::door_path();
+      ::fdetach(path);
+      ::unlink(path);
+      SolarisAttachListener::set_door_path(NULL);
+    }
+  }
+}
+
+// Create the door
+int SolarisAttachListener::create_door() {
+  char door_path[PATH_MAX+1];
+  char initial_path[PATH_MAX+1];
+  int fd, res;
+
+  // register exit function
+  if (!_atexit_registered) {
+    _atexit_registered = true;
+    ::atexit(listener_cleanup);
+  }
+
+  // create the door descriptor
+  int dd = ::door_create(enqueue_proc, NULL, 0);
+  if (dd < 0) {
+    return -1;
+  }
+
+  // create initial file to attach door descriptor
+  snprintf(door_path, sizeof(door_path), "%s/.java_pid%d",
+           os::get_temp_directory(), os::current_process_id());
+  snprintf(initial_path, sizeof(initial_path), "%s.tmp", door_path);
+  RESTARTABLE(::creat(initial_path, S_IRUSR | S_IWUSR), fd);
+  if (fd == -1) {
+    log_debug(attach)("attempt to create door file %s failed (%d)", initial_path, errno);
+    ::door_revoke(dd);
+    return -1;
+  }
+  assert(fd >= 0, "bad file descriptor");
+  ::close(fd);
+
+  // attach the door descriptor to the file
+  if ((res = ::fattach(dd, initial_path)) == -1) {
+    // if busy then detach and try again
+    if (errno == EBUSY) {
+      ::fdetach(initial_path);
+      res = ::fattach(dd, initial_path);
+    }
+    if (res == -1) {
+      log_debug(attach)("unable to create door - fattach failed (%d)", errno);
+      ::door_revoke(dd);
+      dd = -1;
+    }
+  }
+
+  // rename file so that clients can attach
+  if (dd >= 0) {
+    if (::rename(initial_path, door_path) == -1) {
+        ::close(dd);
+        ::fdetach(initial_path);
+        log_debug(attach)("unable to create door - rename %s to %s failed (%d)", initial_path, door_path, errno);
+        dd = -1;
+    }
+  }
+  if (dd >= 0) {
+    set_door_descriptor(dd);
+    set_door_path(door_path);
+    log_trace(attach)("door file %s created successfully", door_path);
+  } else {
+    // unable to create door, attach it to file, or rename file into place
+    ::unlink(initial_path);
+    return -1;
+  }
+
+  return 0;
+}
+
+// Initialization - create the door, locks, and other initialization
+int SolarisAttachListener::init() {
+  if (create_door()) {
+    return -1;
+  }
+
+  int status = os::Solaris::mutex_init(&_mutex);
+  assert_status(status==0, status, "mutex_init");
+
+  status = ::sema_init(&_wakeup, 0, NULL, NULL);
+  assert_status(status==0, status, "sema_init");
+
+  set_head(NULL);
+  set_tail(NULL);
+
+  return 0;
+}
+
+// Dequeue an operation
+SolarisAttachOperation* SolarisAttachListener::dequeue() {
+  for (;;) {
+    int res;
+
+    // wait for somebody to enqueue something
+    while ((res = ::sema_wait(wakeup())) == EINTR)
+      ;
+    if (res) {
+      warning("sema_wait failed: %s", os::strerror(res));
+      return NULL;
+    }
+
+    // lock the list
+    res = os::Solaris::mutex_lock(mutex());
+    assert(res == 0, "mutex_lock failed");
+
+    // remove the head of the list
+    SolarisAttachOperation* op = head();
+    if (op != NULL) {
+      set_head(op->next());
+      if (head() == NULL) {
+        set_tail(NULL);
+      }
+    }
+
+    // unlock
+    os::Solaris::mutex_unlock(mutex());
+
+    // if we got an operation when return it.
+    if (op != NULL) {
+      return op;
+    }
+  }
+}
+
+// Enqueue an operation
+void SolarisAttachListener::enqueue(SolarisAttachOperation* op) {
+  // lock list
+  int res = os::Solaris::mutex_lock(mutex());
+  assert(res == 0, "mutex_lock failed");
+
+  // enqueue at tail
+  op->set_next(NULL);
+  if (head() == NULL) {
+    set_head(op);
+  } else {
+    tail()->set_next(op);
+  }
+  set_tail(op);
+
+  // wakeup the attach listener
+  RESTARTABLE(::sema_post(wakeup()), res);
+  assert(res == 0, "sema_post failed");
+
+  // unlock
+  os::Solaris::mutex_unlock(mutex());
+}
+
+
+// support function - writes the (entire) buffer to a socket
+static int write_fully(int s, char* buf, int len) {
+  do {
+    int n = ::write(s, buf, len);
+    if (n == -1) {
+      if (errno != EINTR) return -1;
+    } else {
+      buf += n;
+      len -= n;
+    }
+  }
+  while (len > 0);
+  return 0;
+}
+
+// Complete an operation by sending the operation result and any result
+// output to the client. At this time the socket is in blocking mode so
+// potentially we can block if there is a lot of data and the client is
+// non-responsive. For most operations this is a non-issue because the
+// default send buffer is sufficient to buffer everything. In the future
+// if there are operations that involves a very big reply then it the
+// socket could be made non-blocking and a timeout could be used.
+
+void SolarisAttachOperation::complete(jint res, bufferedStream* st) {
+  if (this->socket() >= 0) {
+    JavaThread* thread = JavaThread::current();
+    ThreadBlockInVM tbivm(thread);
+
+    // write operation result
+    char msg[32];
+    sprintf(msg, "%d\n", res);
+    int rc = write_fully(this->socket(), msg, strlen(msg));
+
+    // write any result data
+    if (rc == 0) {
+      write_fully(this->socket(), (char*) st->base(), st->size());
+      ::shutdown(this->socket(), 2);
+    }
+
+    // close socket and we're done
+    ::close(this->socket());
+  }
+  delete this;
+}
+
+
+// AttachListener functions
+
+AttachOperation* AttachListener::dequeue() {
+  JavaThread* thread = JavaThread::current();
+  ThreadBlockInVM tbivm(thread);
+
+  AttachOperation* op = SolarisAttachListener::dequeue();
+
+  return op;
+}
+
+
+// Performs initialization at vm startup
+// For Solaris we remove any stale .java_pid file which could cause
+// an attaching process to think we are ready to receive a door_call
+// before we are properly initialized
+
+void AttachListener::vm_start() {
+  char fn[PATH_MAX+1];
+  struct stat64 st;
+  int ret;
+
+  int n = snprintf(fn, sizeof(fn), "%s/.java_pid%d",
+           os::get_temp_directory(), os::current_process_id());
+  assert(n < sizeof(fn), "java_pid file name buffer overflow");
+
+  RESTARTABLE(::stat64(fn, &st), ret);
+  if (ret == 0) {
+    ret = ::unlink(fn);
+    if (ret == -1) {
+      log_debug(attach)("Failed to remove stale attach pid file at %s", fn);
+    }
+  }
+}
+
+int AttachListener::pd_init() {
+  JavaThread* thread = JavaThread::current();
+  ThreadBlockInVM tbivm(thread);
+
+  int ret_code = SolarisAttachListener::init();
+
+  return ret_code;
+}
+
+// Attach Listener is started lazily except in the case when
+// +ReduseSignalUsage is used
+bool AttachListener::init_at_startup() {
+  if (ReduceSignalUsage) {
+    return true;
+  } else {
+    return false;
+  }
+}
+
+bool AttachListener::check_socket_file() {
+  int ret;
+  struct stat64 st;
+  ret = stat64(SolarisAttachListener::door_path(), &st);
+  if (ret == -1) { // need to restart attach listener.
+    log_debug(attach)("Door file %s does not exist - Restart Attach Listener",
+                      SolarisAttachListener::door_path());
+
+    listener_cleanup();
+
+    // wait to terminate current attach listener instance...
+    while (AttachListener::transit_state(AL_INITIALIZING,
+                                         AL_NOT_INITIALIZED) != AL_NOT_INITIALIZED) {
+      os::naked_yield();
+    }
+    return is_init_trigger();
+  }
+  return false;
+}
+
+// If the file .attach_pid<pid> exists in the working directory
+// or /tmp then this is the trigger to start the attach mechanism
+bool AttachListener::is_init_trigger() {
+  if (init_at_startup() || is_initialized()) {
+    return false;               // initialized at startup or already initialized
+  }
+  char fn[PATH_MAX + 1];
+  int ret;
+  struct stat64 st;
+  sprintf(fn, ".attach_pid%d", os::current_process_id());
+  RESTARTABLE(::stat64(fn, &st), ret);
+  if (ret == -1) {
+    log_trace(attach)("Failed to find attach file: %s, trying alternate", fn);
+    snprintf(fn, sizeof(fn), "%s/.attach_pid%d",
+             os::get_temp_directory(), os::current_process_id());
+    RESTARTABLE(::stat64(fn, &st), ret);
+    if (ret == -1) {
+      log_debug(attach)("Failed to find attach file: %s", fn);
+    }
+  }
+  if (ret == 0) {
+    // simple check to avoid starting the attach mechanism when
+    // a bogus non-root user creates the file
+    if (os::Posix::matches_effective_uid_or_root(st.st_uid)) {
+      init();
+      log_trace(attach)("Attach triggered by %s", fn);
+      return true;
+    } else {
+      log_debug(attach)("File %s has wrong user id %d (vs %d). Attach is not triggered", fn, st.st_uid, geteuid());
+    }
+  }
+  return false;
+}
+
+// if VM aborts then detach/cleanup
+void AttachListener::abort() {
+  listener_cleanup();
+}
+
+void AttachListener::pd_data_dump() {
+  os::signal_notify(SIGQUIT);
+}
+
+static jint enable_dprobes(AttachOperation* op, outputStream* out) {
+  const char* probe = op->arg(0);
+  if (probe == NULL || probe[0] == '\0') {
+    out->print_cr("No probe specified");
+    return JNI_ERR;
+  } else {
+    char *end;
+    long val = strtol(probe, &end, 10);
+    if (end == probe || val < 0 || val > INT_MAX) {
+      out->print_cr("invalid probe type");
+      return JNI_ERR;
+    } else {
+      int probe_typess = (int) val;
+      DTrace::enable_dprobes(probe_typess);
+      return JNI_OK;
+    }
+  }
+}
+
+// platform specific operations table
+static AttachOperationFunctionInfo funcs[] = {
+  { "enabledprobes", enable_dprobes },
+  { NULL, NULL }
+};
+
+AttachOperationFunctionInfo* AttachListener::pd_find_operation(const char* name) {
+  int i;
+  for (i = 0; funcs[i].name != NULL; i++) {
+    if (strcmp(funcs[i].name, name) == 0) {
+      return &funcs[i];
+    }
+  }
+  return NULL;
+}
+
+// Solaris specific global flag set. Currently, we support only
+// changing ExtendedDTraceProbes flag.
+jint AttachListener::pd_set_flag(AttachOperation* op, outputStream* out) {
+  const char* name = op->arg(0);
+  assert(name != NULL, "flag name should not be null");
+  bool flag = true;
+  const char* arg1;
+  if ((arg1 = op->arg(1)) != NULL) {
+    char *end;
+    flag = (strtol(arg1, &end, 10) != 0);
+    if (arg1 == end) {
+      out->print_cr("flag value has to be an integer");
+      return JNI_ERR;
+    }
+  }
+
+  if (strcmp(name, "ExtendedDTraceProbes") == 0) {
+    DTrace::set_extended_dprobes(flag);
+    return JNI_OK;
+  }
+
+  if (strcmp(name, "DTraceMonitorProbes") == 0) {
+    DTrace::set_monitor_dprobes(flag);
+    return JNI_OK;
+  }
+
+  out->print_cr("flag '%s' cannot be changed", name);
+  return JNI_ERR;
+}
+
+void AttachListener::pd_detachall() {
+  DTrace::detach_all_clients();
+}
diff -urN /tmp/a/c1_globals_solaris.hpp b/src/hotspot/os/solaris/c1_globals_solaris.hpp
--- /tmp/a/c1_globals_solaris.hpp	1970-01-01 01:00:00.000000000 +0100
+++ b/src/hotspot/os/solaris/c1_globals_solaris.hpp	2024-12-05 11:44:56.839376075 +0000
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2000, 2019, 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.
+ *
+ * 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.
+ *
+ */
+
+#ifndef OS_SOLARIS_C1_GLOBALS_SOLARIS_HPP
+#define OS_SOLARIS_C1_GLOBALS_SOLARIS_HPP
+
+#include "utilities/globalDefinitions.hpp"
+#include "utilities/macros.hpp"
+
+//
+// Sets the default values for operating system dependent flags used by the
+// client compiler. (see c1_globals.hpp)
+//
+
+#endif // OS_SOLARIS_C1_GLOBALS_SOLARIS_HPP
diff -urN /tmp/a/c2_globals_solaris.hpp b/src/hotspot/os/solaris/c2_globals_solaris.hpp
--- /tmp/a/c2_globals_solaris.hpp	1970-01-01 01:00:00.000000000 +0100
+++ b/src/hotspot/os/solaris/c2_globals_solaris.hpp	2024-12-05 11:44:56.839454093 +0000
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2000, 2019, 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.
+ *
+ * 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.
+ *
+ */
+
+#ifndef OS_SOLARIS_C2_GLOBALS_SOLARIS_HPP
+#define OS_SOLARIS_C2_GLOBALS_SOLARIS_HPP
+
+#include "utilities/globalDefinitions.hpp"
+#include "utilities/macros.hpp"
+
+//
+// Sets the default values for operating system dependent flags used by the
+// server compiler. (see c2_globals.hpp)
+//
+
+#endif // OS_SOLARIS_C2_GLOBALS_SOLARIS_HPP
diff -urN /tmp/a/decoder_solaris.cpp b/src/hotspot/os/solaris/decoder_solaris.cpp
--- /tmp/a/decoder_solaris.cpp	1970-01-01 01:00:00.000000000 +0100
+++ b/src/hotspot/os/solaris/decoder_solaris.cpp	2024-12-05 11:44:56.872172375 +0000
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 1997, 2012, 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.
+ *
+ * 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 "jvm.h"
+#include "utilities/decoder_elf.hpp"
+
+#include <cxxabi.h>
+
+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;
+}
+
diff -urN /tmp/a/dtrace/jhelper.d b/src/hotspot/os/solaris/dtrace/jhelper.d
--- /tmp/a/dtrace/jhelper.d	1970-01-01 01:00:00.000000000 +0100
+++ b/src/hotspot/os/solaris/dtrace/jhelper.d	2024-12-05 11:44:56.839727590 +0000
@@ -0,0 +1,546 @@
+/*
+ * Copyright (c) 2003, 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.
+ *
+ * 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.
+ *
+ */
+
+/* This file is auto-generated */
+#include "JvmOffsetsIndex.h"
+
+#define DEBUG
+
+#ifdef DEBUG
+#define MARK_LINE this->line = __LINE__
+#else
+#define MARK_LINE
+#endif
+
+#ifdef _LP64
+#define STACK_BIAS 0x7ff
+#define pointer uint64_t
+#else
+#define STACK_BIAS 0
+#define pointer uint32_t
+#endif
+
+extern pointer __JvmOffsets;
+
+/* GrowableArray<CodeHeaps*>* */
+extern pointer __1cJCodeCacheG_heaps_;
+
+extern pointer __1cIUniverseO_collectedHeap_;
+
+extern pointer __1cHnmethodG__vtbl_;
+extern pointer __1cGMethodG__vtbl_;
+extern pointer __1cKBufferBlobG__vtbl_;
+
+#define copyin_ptr(ADDR)    *(pointer*)  copyin((pointer) (ADDR), sizeof(pointer))
+#define copyin_uchar(ADDR)  *(uchar_t*)  copyin((pointer) (ADDR), sizeof(uchar_t))
+#define copyin_uint16(ADDR) *(uint16_t*) copyin((pointer) (ADDR), sizeof(uint16_t))
+#define copyin_uint32(ADDR) *(uint32_t*) copyin((pointer) (ADDR), sizeof(uint32_t))
+#define copyin_int32(ADDR)  *(int32_t*)  copyin((pointer) (ADDR), sizeof(int32_t))
+#define copyin_uint8(ADDR)  *(uint8_t*)  copyin((pointer) (ADDR), sizeof(uint8_t))
+
+#define copyin_offset(JVM_CONST)  JVM_CONST = \
+	copyin_int32(JvmOffsetsPtr + IDX_##JVM_CONST * sizeof(int32_t))
+
+int init_done;
+
+dtrace:helper:ustack:
+{
+  MARK_LINE;
+  this->done = 0;
+  /*
+   * TBD:
+   * Here we initialize init_done, otherwise jhelper does not work.
+   * Therefore, copyin_offset() statements work multiple times now.
+   * There is a hope we could avoid it in the future, and so,
+   * this initialization can be removed.
+   */
+  init_done  = 0;
+  this->error = (char *) NULL;
+  this->result = (char *) NULL;
+  this->isMethod = 0;
+  this->codecache = 0;
+  this->klass = (pointer) NULL;
+  this->vtbl  = (pointer) NULL;
+  this->suffix = '\0';
+}
+
+dtrace:helper:ustack:
+{
+  MARK_LINE;
+  /* Initialization of JvmOffsets constants */
+  JvmOffsetsPtr = (pointer) &``__JvmOffsets;
+}
+
+dtrace:helper:ustack:
+/!init_done && !this->done/
+{
+  MARK_LINE;
+
+  copyin_offset(POINTER_SIZE);
+  copyin_offset(COMPILER);
+  copyin_offset(OFFSET_CollectedHeap_reserved);
+  copyin_offset(OFFSET_MemRegion_start);
+  copyin_offset(OFFSET_MemRegion_word_size);
+  copyin_offset(SIZE_HeapWord);
+
+  copyin_offset(OFFSET_interpreter_frame_method);
+  copyin_offset(OFFSET_Klass_name);
+  copyin_offset(OFFSET_ConstantPool_pool_holder);
+
+  copyin_offset(OFFSET_HeapBlockHeader_used);
+  copyin_offset(OFFSET_oopDesc_metadata);
+
+  copyin_offset(OFFSET_Symbol_length);
+  copyin_offset(OFFSET_Symbol_body);
+
+  copyin_offset(OFFSET_Method_constMethod);
+  copyin_offset(OFFSET_ConstMethod_constants);
+  copyin_offset(OFFSET_ConstMethod_name_index);
+  copyin_offset(OFFSET_ConstMethod_signature_index);
+
+  copyin_offset(OFFSET_CodeHeap_memory);
+  copyin_offset(OFFSET_CodeHeap_segmap);
+  copyin_offset(OFFSET_CodeHeap_log2_segment_size);
+
+  copyin_offset(OFFSET_GrowableArray_CodeHeap_data);
+  copyin_offset(OFFSET_GrowableArray_CodeHeap_len);
+
+  copyin_offset(OFFSET_VirtualSpace_low);
+  copyin_offset(OFFSET_VirtualSpace_high);
+
+  copyin_offset(OFFSET_CodeBlob_name);
+
+  copyin_offset(OFFSET_nmethod_method);
+  copyin_offset(SIZE_HeapBlockHeader);
+  copyin_offset(SIZE_oopDesc);
+  copyin_offset(SIZE_ConstantPool);
+
+  copyin_offset(OFFSET_NarrowPtrStruct_base);
+  copyin_offset(OFFSET_NarrowPtrStruct_shift);
+
+  /*
+   * The PC to translate is in arg0.
+   */
+  this->pc = arg0;
+
+  /*
+   * The methodPtr is in %l2 on SPARC.  This can be found at
+   * offset 8 from the frame pointer on 32-bit processes.
+   */
+#if   defined(__sparc)
+  this->methodPtr = copyin_ptr(arg1 + 2 * sizeof(pointer) + STACK_BIAS);
+#elif defined(__i386) || defined(__amd64)
+  this->methodPtr = copyin_ptr(arg1 + OFFSET_interpreter_frame_method);
+#else
+#error "Don't know architecture"
+#endif
+
+  /* Read address of GrowableArray<CodeHeaps*> */
+  // this->code_heaps_address = copyin_ptr(&``__1cJCodeCacheG_heaps_);
+  this->code_heaps_address =  * ( uint64_t * ) copyin ( ( uint64_t ) ( &``__1cJCodeCacheG_heaps_ ) , sizeof ( uint64_t ) );
+
+  /* Read address of _data array field in GrowableArray */
+  this->code_heaps_array_address = copyin_ptr(this->code_heaps_address + OFFSET_GrowableArray_CodeHeap_data);
+  this->number_of_heaps = copyin_uint32(this->code_heaps_address + OFFSET_GrowableArray_CodeHeap_len);
+
+  this->Method_vtbl = (pointer) &``__1cGMethodG__vtbl_;
+
+  /*
+   * Get Java heap bounds
+   */
+  // this->Universe_collectedHeap = copyin_ptr(&``__1cIUniverseO_collectedHeap_);
+  this->Universe_collectedHeap =  * ( uint64_t * ) copyin ( ( uint64_t ) ( &``__1cIUniverseO_collectedHeap_ ) , sizeof ( uint64_t ) );
+
+  this->heap_start = copyin_ptr(this->Universe_collectedHeap +
+      OFFSET_CollectedHeap_reserved +
+      OFFSET_MemRegion_start);
+  this->heap_size = SIZE_HeapWord *
+    copyin_ptr(this->Universe_collectedHeap +
+        OFFSET_CollectedHeap_reserved +
+        OFFSET_MemRegion_word_size
+        );
+  this->heap_end = this->heap_start + this->heap_size;
+}
+
+/*
+ * IMPORTANT: At the moment the ustack helper supports up to 5 code heaps in
+ * the code cache. If more code heaps are added the following probes have to
+ * be extended. This is done by simply adding a probe to get the heap bounds
+ * and another probe to set the code heap address of the newly created heap.
+ */
+
+/*
+ * ----- BEGIN: Get bounds of code heaps -----
+ */
+dtrace:helper:ustack:
+/init_done < 1 && this->number_of_heaps >= 1 && !this->done/
+{
+  MARK_LINE;
+  /* CodeHeap 1 */
+  init_done = 1;
+  this->code_heap1_address = copyin_ptr(this->code_heaps_array_address);
+  this->code_heap1_low = copyin_ptr(this->code_heap1_address +
+      OFFSET_CodeHeap_memory + OFFSET_VirtualSpace_low);
+  this->code_heap1_high = copyin_ptr(this->code_heap1_address +
+      OFFSET_CodeHeap_memory + OFFSET_VirtualSpace_high);
+}
+
+dtrace:helper:ustack:
+/init_done < 2 && this->number_of_heaps >= 2 && !this->done/
+{
+  MARK_LINE;
+  /* CodeHeap 2 */
+  init_done = 2;
+  this->code_heaps_array_address = this->code_heaps_array_address + POINTER_SIZE;
+  this->code_heap2_address = copyin_ptr(this->code_heaps_array_address);
+  this->code_heap2_low = copyin_ptr(this->code_heap2_address +
+      OFFSET_CodeHeap_memory + OFFSET_VirtualSpace_low);
+  this->code_heap2_high = copyin_ptr(this->code_heap2_address +
+      OFFSET_CodeHeap_memory + OFFSET_VirtualSpace_high);
+}
+
+dtrace:helper:ustack:
+/init_done < 3 && this->number_of_heaps >= 3 && !this->done/
+{
+  /* CodeHeap 3 */
+  init_done = 3;
+  this->code_heaps_array_address = this->code_heaps_array_address + POINTER_SIZE;
+  this->code_heap3_address = copyin_ptr(this->code_heaps_array_address);
+  this->code_heap3_low = copyin_ptr(this->code_heap3_address +
+      OFFSET_CodeHeap_memory + OFFSET_VirtualSpace_low);
+  this->code_heap3_high = copyin_ptr(this->code_heap3_address +
+      OFFSET_CodeHeap_memory + OFFSET_VirtualSpace_high);
+}
+
+dtrace:helper:ustack:
+/init_done < 4 && this->number_of_heaps >= 4 && !this->done/
+{
+  /* CodeHeap 4 */
+  init_done = 4;
+  this->code_heaps_array_address = this->code_heaps_array_address + POINTER_SIZE;
+  this->code_heap4_address = copyin_ptr(this->code_heaps_array_address);
+  this->code_heap4_low = copyin_ptr(this->code_heap4_address +
+      OFFSET_CodeHeap_memory + OFFSET_VirtualSpace_low);
+  this->code_heap4_high = copyin_ptr(this->code_heap4_address +
+      OFFSET_CodeHeap_memory + OFFSET_VirtualSpace_high);
+}
+
+dtrace:helper:ustack:
+/init_done < 5 && this->number_of_heaps >= 5 && !this->done/
+{
+  /* CodeHeap 5 */
+  init_done = 5;
+  this->code_heaps_array_address = this->code_heaps_array_address + POINTER_SIZE;
+  this->code_heap5_address = copyin_ptr(this->code_heaps_array_address);
+  this->code_heap5_low = copyin_ptr(this->code_heap5_address +
+      OFFSET_CodeHeap_memory + OFFSET_VirtualSpace_low);
+  this->code_heap5_high = copyin_ptr(this->code_heap5_address +
+      OFFSET_CodeHeap_memory + OFFSET_VirtualSpace_high);
+}
+/*
+ * ----- END: Get bounds of code heaps -----
+ */
+
+/*
+ * ----- BEGIN: Get address of the code heap pc points to -----
+ */
+dtrace:helper:ustack:
+/!this->done && this->number_of_heaps >= 1 && this->code_heap1_low <= this->pc && this->pc < this->code_heap1_high/
+{
+  MARK_LINE;
+  this->codecache = 1;
+  this->code_heap_address = this->code_heap1_address;
+}
+
+dtrace:helper:ustack:
+/!this->done && this->number_of_heaps >= 2 && this->code_heap2_low <= this->pc && this->pc < this->code_heap2_high/
+{
+  MARK_LINE;
+  this->codecache = 1;
+  this->code_heap_address = this->code_heap2_address;
+}
+
+dtrace:helper:ustack:
+/!this->done && this->number_of_heaps >= 3 && this->code_heap3_low <= this->pc && this->pc < this->code_heap3_high/
+{
+  MARK_LINE;
+  this->codecache = 1;
+  this->code_heap_address = this->code_heap3_address;
+}
+
+dtrace:helper:ustack:
+/!this->done && this->number_of_heaps >= 4 && this->code_heap4_low <= this->pc && this->pc < this->code_heap4_high/
+{
+  MARK_LINE;
+  this->codecache = 1;
+  this->code_heap_address = this->code_heap4_address;
+}
+
+dtrace:helper:ustack:
+/!this->done && this->number_of_heaps >= 5 && this->code_heap5_low <= this->pc && this->pc < this->code_heap5_high/
+{
+  MARK_LINE;
+  this->codecache = 1;
+  this->code_heap_address = this->code_heap5_address;
+}
+/*
+ * ----- END: Get address of the code heap pc points to -----
+ */
+
+dtrace:helper:ustack:
+/!this->done && this->codecache/
+{
+  MARK_LINE;
+  /*
+   * Get code heap configuration
+   */
+  this->code_heap_low = copyin_ptr(this->code_heap_address +
+      OFFSET_CodeHeap_memory + OFFSET_VirtualSpace_low);
+  this->code_heap_segmap_low = copyin_ptr(this->code_heap_address +
+      OFFSET_CodeHeap_segmap + OFFSET_VirtualSpace_low);
+  this->code_heap_log2_segment_size = copyin_uint32(
+      this->code_heap_address + OFFSET_CodeHeap_log2_segment_size);
+
+  /*
+   * Find start
+   */
+  this->segment = (this->pc - this->code_heap_low) >>
+    this->code_heap_log2_segment_size;
+  this->block = this->code_heap_segmap_low;
+  this->tag = copyin_uchar(this->block + this->segment);
+}
+
+dtrace:helper:ustack:
+/!this->done && this->codecache && this->tag > 0/
+{
+  MARK_LINE;
+  this->tag = copyin_uchar(this->block + this->segment);
+  this->segment = this->segment - this->tag;
+}
+
+dtrace:helper:ustack:
+/!this->done && this->codecache && this->tag > 0/
+{
+  MARK_LINE;
+  this->tag = copyin_uchar(this->block + this->segment);
+  this->segment = this->segment - this->tag;
+}
+
+dtrace:helper:ustack:
+/!this->done && this->codecache && this->tag > 0/
+{
+  MARK_LINE;
+  this->tag = copyin_uchar(this->block + this->segment);
+  this->segment = this->segment - this->tag;
+}
+
+dtrace:helper:ustack:
+/!this->done && this->codecache && this->tag > 0/
+{
+  MARK_LINE;
+  this->tag = copyin_uchar(this->block + this->segment);
+  this->segment = this->segment - this->tag;
+}
+
+dtrace:helper:ustack:
+/!this->done && this->codecache && this->tag > 0/
+{
+  MARK_LINE;
+  this->tag = copyin_uchar(this->block + this->segment);
+  this->segment = this->segment - this->tag;
+}
+
+dtrace:helper:ustack:
+/!this->done && this->codecache && this->tag > 0/
+{
+  MARK_LINE;
+  this->error = "<couldn't find start>";
+  this->done = 1;
+}
+
+dtrace:helper:ustack:
+/!this->done && this->codecache/
+{
+  MARK_LINE;
+  this->block = this->code_heap_low +
+    (this->segment << this->code_heap_log2_segment_size);
+  this->used = copyin_uint32(this->block + OFFSET_HeapBlockHeader_used);
+}
+
+dtrace:helper:ustack:
+/!this->done && this->codecache && !this->used/
+{
+  MARK_LINE;
+  this->error = "<block not in use>";
+  this->done = 1;
+}
+
+dtrace:helper:ustack:
+/!this->done && this->codecache/
+{
+  MARK_LINE;
+  this->start = this->block + SIZE_HeapBlockHeader;
+  this->vtbl = copyin_ptr(this->start);
+
+  this->nmethod_vtbl            = (pointer) &``__1cHnmethodG__vtbl_;
+  this->BufferBlob_vtbl         = (pointer) &``__1cKBufferBlobG__vtbl_;
+}
+
+dtrace:helper:ustack:
+/!this->done && this->vtbl == this->nmethod_vtbl/
+{
+  MARK_LINE;
+  this->methodPtr = copyin_ptr(this->start + OFFSET_nmethod_method);
+  this->suffix = '*';
+  this->isMethod = 1;
+}
+
+dtrace:helper:ustack:
+/!this->done && this->vtbl == this->BufferBlob_vtbl/
+{
+  MARK_LINE;
+  this->name = copyin_ptr(this->start + OFFSET_CodeBlob_name);
+}
+
+
+dtrace:helper:ustack:
+/!this->done && this->vtbl == this->BufferBlob_vtbl && this->methodPtr != 0/
+{
+  MARK_LINE;
+  this->klass = copyin_ptr(this->methodPtr);
+  this->isMethod = this->klass == this->Method_vtbl;
+  this->done = !this->isMethod;
+}
+
+dtrace:helper:ustack:
+/!this->done && !this->isMethod/
+{
+  MARK_LINE;
+  this->name = copyin_ptr(this->start + OFFSET_CodeBlob_name);
+  this->result = this->name != 0 ? copyinstr(this->name) : "<CodeBlob>";
+  this->done = 1;
+}
+
+dtrace:helper:ustack:
+/!this->done && this->isMethod/
+{
+  MARK_LINE;
+  this->constMethod = copyin_ptr(this->methodPtr +
+      OFFSET_Method_constMethod);
+
+  this->nameIndex = copyin_uint16(this->constMethod +
+      OFFSET_ConstMethod_name_index);
+
+  this->signatureIndex = copyin_uint16(this->constMethod +
+      OFFSET_ConstMethod_signature_index);
+
+  this->constantPool = copyin_ptr(this->constMethod +
+      OFFSET_ConstMethod_constants);
+
+  this->nameSymbol = copyin_ptr(this->constantPool +
+      this->nameIndex * sizeof (pointer) + SIZE_ConstantPool);
+  /* The symbol is a CPSlot and has lower bit set to indicate metadata */
+  this->nameSymbol &= (~1); /* remove metadata lsb */
+
+  this->nameSymbolLength = copyin_uint16(this->nameSymbol +
+      OFFSET_Symbol_length);
+
+  this->signatureSymbol = copyin_ptr(this->constantPool +
+      this->signatureIndex * sizeof (pointer) + SIZE_ConstantPool);
+  this->signatureSymbol &= (~1); /* remove metadata lsb */
+
+  this->signatureSymbolLength = copyin_uint16(this->signatureSymbol +
+      OFFSET_Symbol_length);
+
+  this->klassPtr = copyin_ptr(this->constantPool +
+      OFFSET_ConstantPool_pool_holder);
+
+  this->klassSymbol = copyin_ptr(this->klassPtr +
+      OFFSET_Klass_name);
+
+  this->klassSymbolLength = copyin_uint16(this->klassSymbol +
+      OFFSET_Symbol_length);
+
+  /*
+   * Enough for three strings, plus the '.', plus the trailing '\0'.
+   */
+  this->result = (char *) alloca(this->klassSymbolLength +
+      this->nameSymbolLength +
+      this->signatureSymbolLength + 2 + 1);
+
+  copyinto(this->klassSymbol + OFFSET_Symbol_body,
+      this->klassSymbolLength, this->result);
+
+  /*
+   * Add the '.' between the class and the name.
+   */
+  this->result[this->klassSymbolLength] = '.';
+
+  copyinto(this->nameSymbol + OFFSET_Symbol_body,
+      this->nameSymbolLength,
+      this->result + this->klassSymbolLength + 1);
+
+  copyinto(this->signatureSymbol + OFFSET_Symbol_body,
+      this->signatureSymbolLength,
+      this->result + this->klassSymbolLength +
+      this->nameSymbolLength + 1);
+
+  /*
+   * Now we need to add a trailing '\0' and possibly a tag character.
+   */
+  this->result[this->klassSymbolLength + 1 +
+      this->nameSymbolLength +
+      this->signatureSymbolLength] = this->suffix;
+  this->result[this->klassSymbolLength + 2 +
+      this->nameSymbolLength +
+      this->signatureSymbolLength] = '\0';
+
+  this->done = 1;
+}
+
+dtrace:helper:ustack:
+/this->done && this->error == (char *) NULL/
+{
+  this->result;
+}
+
+dtrace:helper:ustack:
+/this->done && this->error != (char *) NULL/
+{
+  this->error;
+}
+
+dtrace:helper:ustack:
+/!this->done && this->codecache/
+{
+  this->done = 1;
+  "error";
+}
+
+
+dtrace:helper:ustack:
+/!this->done/
+{
+  NULL;
+}
diff -urN /tmp/a/globals_solaris.hpp b/src/hotspot/os/solaris/globals_solaris.hpp
--- /tmp/a/globals_solaris.hpp	1970-01-01 01:00:00.000000000 +0100
+++ b/src/hotspot/os/solaris/globals_solaris.hpp	2024-12-05 11:44:56.839804759 +0000
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2005, 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.
+ *
+ * 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.
+ *
+ */
+
+#ifndef OS_SOLARIS_GLOBALS_SOLARIS_HPP
+#define OS_SOLARIS_GLOBALS_SOLARIS_HPP
+
+//
+// Defines Solaris specific flags. They are not available on other platforms.
+//
+#define RUNTIME_OS_FLAGS(develop, \
+                         develop_pd, \
+                         product, \
+                         product_pd, \
+                         notproduct, \
+                         range, \
+                         constraint)
+
+//
+// Defines Solaris-specific default values. The flags are available on all
+// platforms, but they may have different default values on other platforms.
+//
+define_pd_global(size_t, PreTouchParallelChunkSize, 1 * G);
+define_pd_global(bool, UseLargePages, true);
+define_pd_global(bool, UseLargePagesIndividualAllocation, false);
+define_pd_global(bool, UseThreadPriorities, false);
+
+#endif // OS_SOLARIS_GLOBALS_SOLARIS_HPP
diff -urN /tmp/a/osThread_solaris.cpp b/src/hotspot/os/solaris/osThread_solaris.cpp
--- /tmp/a/osThread_solaris.cpp	1970-01-01 01:00:00.000000000 +0100
+++ b/src/hotspot/os/solaris/osThread_solaris.cpp	2024-12-05 11:44:56.839879917 +0000
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 1998, 2014, 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.
+ *
+ * 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.
+ *
+ */
+
+// no precompiled headers
+#include "runtime/handles.inline.hpp"
+#include "runtime/mutexLocker.hpp"
+#include "runtime/os.hpp"
+#include "runtime/osThread.hpp"
+#include "runtime/safepoint.hpp"
+#include "runtime/vmThread.hpp"
+
+#include <signal.h>
+
+ // ***************************************************************
+ // Platform dependent initialization and cleanup
+ // ***************************************************************
+
+void OSThread::pd_initialize() {
+  _thread_id                         = 0;
+  sigemptyset(&_caller_sigmask);
+
+  _vm_created_thread                 = false;
+}
+
+void OSThread::pd_destroy() {
+}
diff -urN /tmp/a/osThread_solaris.hpp b/src/hotspot/os/solaris/osThread_solaris.hpp
--- /tmp/a/osThread_solaris.hpp	1970-01-01 01:00:00.000000000 +0100
+++ b/src/hotspot/os/solaris/osThread_solaris.hpp	2024-12-05 11:44:56.839966647 +0000
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 1997, 2019, 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.
+ *
+ * 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.
+ *
+ */
+
+#ifndef OS_SOLARIS_OSTHREAD_SOLARIS_HPP
+#define OS_SOLARIS_OSTHREAD_SOLARIS_HPP
+
+// This is embedded via include into the class OSThread
+ public:
+  typedef thread_t thread_id_t;
+
+ private:
+  uint     _lwp_id;            // lwp ID, only used with bound threads
+  int      _native_priority;   // Saved native priority when starting
+                               // a bound thread
+  sigset_t _caller_sigmask;    // Caller's signal mask
+  bool     _vm_created_thread; // true if the VM created this thread,
+                               // false if primary thread or attached thread
+ public:
+  uint     lwp_id() const          { return _lwp_id; }
+  int      native_priority() const { return _native_priority; }
+
+  // Set and get state of _vm_created_thread flag
+  void set_vm_created()           { _vm_created_thread = true; }
+  bool is_vm_created()            { return _vm_created_thread; }
+
+  // Methods to save/restore caller's signal mask
+  sigset_t  caller_sigmask() const       { return _caller_sigmask; }
+  void    set_caller_sigmask(sigset_t sigmask)  { _caller_sigmask = sigmask; }
+
+#ifndef PRODUCT
+  // Used for debugging, return a unique integer for each thread.
+  int thread_identifier() const   { return _thread_id; }
+#endif
+#ifdef ASSERT
+  // On solaris reposition can fail in two ways:
+  // 1: a mismatched pc, because signal is delivered too late, target thread
+  //    is resumed.
+  // 2: on a timeout where signal is lost, target thread is resumed.
+  bool valid_reposition_failure() {
+    // only 1 and 2 can happen and we can handle both of them
+    return true;
+  }
+#endif
+  void set_lwp_id(uint id)           { _lwp_id = id; }
+  void set_native_priority(int prio) { _native_priority = prio; }
+
+ public:
+  pthread_t pthread_id() const {
+    // Here: same as OSThread::thread_id()
+    return _thread_id;
+  }
+  os::SuspendResume sr;
+
+ private:
+  void* _siginfo;
+  ucontext_t* _ucontext;
+
+ public:
+  void set_siginfo(void* ptr)             { _siginfo = ptr;   }
+  ucontext_t* ucontext() const { return _ucontext; }
+  void set_ucontext(ucontext_t* ptr) { _ucontext = ptr; }
+
+  // ***************************************************************
+  // Platform dependent initialization and cleanup
+  // ***************************************************************
+
+private:
+
+  void pd_initialize();
+  void pd_destroy();
+
+#endif // OS_SOLARIS_OSTHREAD_SOLARIS_HPP
diff -urN /tmp/a/os_perf_solaris.cpp b/src/hotspot/os/solaris/os_perf_solaris.cpp
--- /tmp/a/os_perf_solaris.cpp	1970-01-01 01:00:00.000000000 +0100
+++ b/src/hotspot/os/solaris/os_perf_solaris.cpp	2024-12-05 11:44:56.840206908 +0000
@@ -0,0 +1,808 @@
+/*
+ * Copyright (c) 2012, 2019, 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.
+ *
+ * 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 "precompiled.hpp"
+#include "jvm.h"
+#include "memory/allocation.inline.hpp"
+#include "runtime/os.hpp"
+#include "runtime/os_perf.hpp"
+#include "os_solaris.inline.hpp"
+#include "utilities/globalDefinitions.hpp"
+#include "utilities/macros.hpp"
+
+#include CPU_HEADER(vm_version_ext)
+
+#include <sys/types.h>
+#include <procfs.h>
+#include <dirent.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <kstat.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/sysinfo.h>
+#include <sys/lwp.h>
+#include <pthread.h>
+#include <time.h>
+#include <utmpx.h>
+#include <dlfcn.h>
+#include <sys/loadavg.h>
+#include <limits.h>
+
+static const double NANOS_PER_SEC = 1000000000.0;
+
+struct CPUPerfTicks {
+  kstat_t* kstat;
+  uint64_t last_idle;
+  uint64_t last_total;
+  double   last_ratio;
+};
+
+struct CPUPerfCounters {
+  int           nProcs;
+  CPUPerfTicks* jvmTicks;
+  kstat_ctl_t*  kstat_ctrl;
+};
+
+static int get_info(const char* path, void* info, size_t s, off_t o) {
+  assert(path != NULL, "path is NULL!");
+  assert(info != NULL, "info is NULL!");
+
+  int fd = -1;
+
+  if ((fd = os::open(path, O_RDONLY, 0)) < 0) {
+    return OS_ERR;
+  }
+  if (pread(fd, info, s, o) != s) {
+    close(fd);
+    return OS_ERR;
+  }
+  close(fd);
+  return OS_OK;
+}
+
+static int get_psinfo2(void* info, size_t s, off_t o) {
+  return get_info("/proc/self/psinfo", info, s, o);
+}
+
+static int get_psinfo(psinfo_t* info) {
+  return get_psinfo2(info, sizeof(*info), 0);
+}
+
+static int read_cpustat(kstat_ctl_t* kstat_ctrl, CPUPerfTicks* load, cpu_stat_t* cpu_stat) {
+  assert(kstat_ctrl != NULL, "kstat_ctrl pointer is NULL!");
+  assert(load != NULL, "load pointer is NULL!");
+  assert(cpu_stat != NULL, "cpu_stat pointer is NULL!");
+
+  if (load->kstat == NULL) {
+    // no handle.
+    return OS_ERR;
+  }
+  if (kstat_read(kstat_ctrl, load->kstat, cpu_stat) == OS_ERR) {
+    // disable handle for this CPU
+     load->kstat = NULL;
+     return OS_ERR;
+  }
+  return OS_OK;
+}
+
+static double get_cpu_load(int which_logical_cpu, CPUPerfCounters* counters) {
+  assert(counters != NULL, "counters pointer is NULL!");
+
+  cpu_stat_t  cpu_stat = {0};
+
+  if (which_logical_cpu >= counters->nProcs) {
+    return .0;
+  }
+
+  CPUPerfTicks load = counters->jvmTicks[which_logical_cpu];
+  if (read_cpustat(counters->kstat_ctrl, &load, &cpu_stat) != OS_OK) {
+    return .0;
+  }
+
+  uint_t* usage = cpu_stat.cpu_sysinfo.cpu;
+  if (usage == NULL) {
+    return .0;
+  }
+
+  uint64_t c_idle  = usage[CPU_IDLE];
+  uint64_t c_total = 0;
+
+  for (int i = 0; i < CPU_STATES; i++) {
+    c_total += usage[i];
+  }
+
+  // Calculate diff against previous snapshot
+  uint64_t d_idle  = c_idle - load.last_idle;
+  uint64_t d_total = c_total - load.last_total;
+
+  /** update if weve moved */
+  if (d_total > 0) {
+    // Save current values for next time around
+    load.last_idle  = c_idle;
+    load.last_total = c_total;
+    load.last_ratio = (double) (d_total - d_idle) / d_total;
+  }
+
+  return load.last_ratio;
+}
+
+static int get_boot_time(uint64_t* time) {
+  assert(time != NULL, "time pointer is NULL!");
+  setutxent();
+  for(;;) {
+    struct utmpx* u;
+    if ((u = getutxent()) == NULL) {
+      break;
+    }
+    if (u->ut_type == BOOT_TIME) {
+      *time = u->ut_xtime;
+      endutxent();
+      return OS_OK;
+    }
+  }
+  endutxent();
+  return OS_ERR;
+}
+
+static int get_noof_context_switches(CPUPerfCounters* counters, uint64_t* switches) {
+  assert(switches != NULL, "switches pointer is NULL!");
+  assert(counters != NULL, "counter pointer is NULL!");
+  *switches = 0;
+  uint64_t s = 0;
+
+  // Collect data from all CPUs
+  for (int i = 0; i < counters->nProcs; i++) {
+    cpu_stat_t cpu_stat = {0};
+    CPUPerfTicks load = counters->jvmTicks[i];
+
+    if (read_cpustat(counters->kstat_ctrl, &load, &cpu_stat) == OS_OK) {
+      s += cpu_stat.cpu_sysinfo.pswitch;
+    } else {
+      //fail fast...
+      return OS_ERR;
+    }
+  }
+  *switches = s;
+  return OS_OK;
+}
+
+static int perf_context_switch_rate(CPUPerfCounters* counters, double* rate) {
+  assert(counters != NULL, "counters is NULL!");
+  assert(rate != NULL, "rate pointer is NULL!");
+  static pthread_mutex_t contextSwitchLock = PTHREAD_MUTEX_INITIALIZER;
+  static uint64_t lastTime = 0;
+  static uint64_t lastSwitches = 0;
+  static double   lastRate = 0.0;
+
+  uint64_t lt = 0;
+  int res = 0;
+
+  if (lastTime == 0) {
+    uint64_t tmp;
+    if (get_boot_time(&tmp) < 0) {
+      return OS_ERR;
+    }
+    lt = tmp * 1000;
+  }
+
+  res = OS_OK;
+
+  pthread_mutex_lock(&contextSwitchLock);
+  {
+
+    uint64_t sw = 0;
+    clock_t t, d;
+
+    if (lastTime == 0) {
+      lastTime = lt;
+    }
+
+    t = clock();
+    d = t - lastTime;
+
+    if (d == 0) {
+      *rate = lastRate;
+    } else if (get_noof_context_switches(counters, &sw)== OS_OK) {
+      *rate      = ((double)(sw - lastSwitches) / d) * 1000;
+      lastRate     = *rate;
+      lastSwitches = sw;
+      lastTime     = t;
+    } else {
+      *rate = 0.0;
+      res   = OS_ERR;
+    }
+    if (*rate < 0.0) {
+      *rate = 0.0;
+      lastRate = 0.0;
+    }
+  }
+  pthread_mutex_unlock(&contextSwitchLock);
+  return res;
+ }
+
+
+
+class CPUPerformanceInterface::CPUPerformance : public CHeapObj<mtInternal> {
+   friend class CPUPerformanceInterface;
+ private:
+  CPUPerfCounters _counters;
+  int cpu_load(int which_logical_cpu, double* cpu_load);
+  int context_switch_rate(double* rate);
+  int cpu_load_total_process(double* cpu_load);
+  int cpu_loads_process(double* pjvmUserLoad, double* pjvmKernelLoad, double* psystemTotalLoad);
+
+  CPUPerformance();
+  ~CPUPerformance();
+  bool initialize();
+};
+
+CPUPerformanceInterface::CPUPerformance::CPUPerformance() {
+  _counters.nProcs = 0;
+  _counters.jvmTicks = NULL;
+  _counters.kstat_ctrl = NULL;
+}
+
+bool CPUPerformanceInterface::CPUPerformance::initialize() {
+  // initialize kstat control structure,
+  _counters.kstat_ctrl = kstat_open();
+  assert(_counters.kstat_ctrl != NULL, "error initializing kstat control structure!");
+
+  if (NULL == _counters.kstat_ctrl) {
+    return false;
+  }
+
+  // Get number of CPU(s)
+  if ((_counters.nProcs = sysconf(_SC_NPROCESSORS_ONLN)) == OS_ERR) {
+    // ignore error?
+    _counters.nProcs = 1;
+  }
+
+  assert(_counters.nProcs > 0, "no CPUs detected in sysconf call!");
+  if (_counters.nProcs == 0) {
+    return false;
+  }
+
+  // Data structure(s) for saving CPU load (one per CPU)
+  size_t array_entry_count = _counters.nProcs;
+  _counters.jvmTicks = NEW_C_HEAP_ARRAY(CPUPerfTicks, array_entry_count, mtInternal);
+  memset(_counters.jvmTicks, 0, array_entry_count * sizeof(*_counters.jvmTicks));
+
+  // Get kstat cpu_stat counters for every CPU
+  // loop over kstat to find our cpu_stat(s)
+  int i = 0;
+  for (kstat_t* kstat = _counters.kstat_ctrl->kc_chain; kstat != NULL; kstat = kstat->ks_next) {
+    if (strncmp(kstat->ks_module, "cpu_stat", 8) == 0) {
+      if (kstat_read(_counters.kstat_ctrl, kstat, NULL) == OS_ERR) {
+        continue;
+      }
+      if (i == _counters.nProcs) {
+        // more cpu_stats than reported CPUs
+        break;
+      }
+      _counters.jvmTicks[i++].kstat = kstat;
+    }
+  }
+  return true;
+}
+
+CPUPerformanceInterface::CPUPerformance::~CPUPerformance() {
+  FREE_C_HEAP_ARRAY(char, _counters.jvmTicks);
+  if (_counters.kstat_ctrl != NULL) {
+    kstat_close(_counters.kstat_ctrl);
+  }
+}
+
+int CPUPerformanceInterface::CPUPerformance::cpu_load(int which_logical_cpu, double* cpu_load) {
+  assert(cpu_load != NULL, "cpu_load pointer is NULL!");
+  double t = .0;
+  if (-1 == which_logical_cpu) {
+    for (int i = 0; i < _counters.nProcs; i++) {
+      t += get_cpu_load(i, &_counters);
+    }
+    // Cap total systemload to 1.0
+    t = MIN2<double>((t / _counters.nProcs), 1.0);
+  } else {
+    t = MIN2<double>(get_cpu_load(which_logical_cpu, &_counters), 1.0);
+  }
+
+  *cpu_load = t;
+  return OS_OK;
+}
+
+int CPUPerformanceInterface::CPUPerformance::cpu_load_total_process(double* cpu_load) {
+  assert(cpu_load != NULL, "cpu_load pointer is NULL!");
+
+  psinfo_t info;
+
+  // Get the percentage of "recent cpu usage" from all the lwp:s in the JVM:s
+  // process. This is returned as a value between 0.0 and 1.0 multiplied by 0x8000.
+  if (get_psinfo2(&info.pr_pctcpu, sizeof(info.pr_pctcpu), offsetof(psinfo_t, pr_pctcpu)) != 0) {
+    *cpu_load = 0.0;
+    return OS_ERR;
+  }
+  *cpu_load = (double) info.pr_pctcpu / 0x8000;
+  return OS_OK;
+}
+
+int CPUPerformanceInterface::CPUPerformance::cpu_loads_process(double* pjvmUserLoad, double* pjvmKernelLoad, double* psystemTotalLoad) {
+  assert(pjvmUserLoad != NULL, "pjvmUserLoad not inited");
+  assert(pjvmKernelLoad != NULL, "pjvmKernelLoad not inited");
+  assert(psystemTotalLoad != NULL, "psystemTotalLoad not inited");
+
+  static uint64_t lastTime;
+  static uint64_t lastUser, lastKernel;
+  static double lastUserRes, lastKernelRes;
+
+  pstatus_t pss;
+  psinfo_t  info;
+
+  *pjvmKernelLoad = *pjvmUserLoad = *psystemTotalLoad = 0;
+  if (get_info("/proc/self/status", &pss.pr_utime, sizeof(timestruc_t)*2, offsetof(pstatus_t, pr_utime)) != 0) {
+    return OS_ERR;
+  }
+
+  if (get_psinfo(&info) != 0) {
+    return OS_ERR;
+  }
+
+  // get the total time in user, kernel and total time
+  // check ratios for 'lately' and multiply the 'recent load'.
+  uint64_t time   = (info.pr_time.tv_sec * NANOS_PER_SEC) + info.pr_time.tv_nsec;
+  uint64_t user   = (pss.pr_utime.tv_sec * NANOS_PER_SEC) + pss.pr_utime.tv_nsec;
+  uint64_t kernel = (pss.pr_stime.tv_sec * NANOS_PER_SEC) + pss.pr_stime.tv_nsec;
+  uint64_t diff   = time - lastTime;
+  double load     = (double) info.pr_pctcpu / 0x8000;
+
+  if (diff > 0) {
+    lastUserRes = (load * (user - lastUser)) / diff;
+    lastKernelRes = (load * (kernel - lastKernel)) / diff;
+
+    // BUG9182835 - patch for clamping these values to sane ones.
+    lastUserRes   = MIN2<double>(1, lastUserRes);
+    lastUserRes   = MAX2<double>(0, lastUserRes);
+    lastKernelRes = MIN2<double>(1, lastKernelRes);
+    lastKernelRes = MAX2<double>(0, lastKernelRes);
+  }
+
+  double t = .0;
+  cpu_load(-1, &t);
+  // clamp at user+system and 1.0
+  if (lastUserRes + lastKernelRes > t) {
+    t = MIN2<double>(lastUserRes + lastKernelRes, 1.0);
+  }
+
+  *pjvmUserLoad   = lastUserRes;
+  *pjvmKernelLoad = lastKernelRes;
+  *psystemTotalLoad = t;
+
+  lastTime   = time;
+  lastUser   = user;
+  lastKernel = kernel;
+
+  return OS_OK;
+}
+
+int CPUPerformanceInterface::CPUPerformance::context_switch_rate(double* rate) {
+  return perf_context_switch_rate(&_counters, rate);
+}
+
+CPUPerformanceInterface::CPUPerformanceInterface() {
+  _impl = NULL;
+}
+
+bool CPUPerformanceInterface::initialize() {
+  _impl = new CPUPerformanceInterface::CPUPerformance();
+  return _impl->initialize();
+}
+
+CPUPerformanceInterface::~CPUPerformanceInterface(void) {
+  if (_impl != NULL) {
+    delete _impl;
+  }
+}
+
+int CPUPerformanceInterface::cpu_load(int which_logical_cpu, double* cpu_load) const {
+  return _impl->cpu_load(which_logical_cpu, cpu_load);
+}
+
+int CPUPerformanceInterface::cpu_load_total_process(double* cpu_load) const {
+  return _impl->cpu_load_total_process(cpu_load);
+}
+
+int CPUPerformanceInterface::cpu_loads_process(double* pjvmUserLoad, double* pjvmKernelLoad, double* psystemTotalLoad) const {
+  return _impl->cpu_loads_process(pjvmUserLoad, pjvmKernelLoad, psystemTotalLoad);
+}
+
+int CPUPerformanceInterface::context_switch_rate(double* rate) const {
+  return _impl->context_switch_rate(rate);
+}
+
+class SystemProcessInterface::SystemProcesses : public CHeapObj<mtInternal> {
+  friend class SystemProcessInterface;
+ private:
+  class ProcessIterator : public CHeapObj<mtInternal> {
+    friend class SystemProcessInterface::SystemProcesses;
+   private:
+    DIR*           _dir;
+    struct dirent* _entry;
+    bool           _valid;
+
+    ProcessIterator();
+    ~ProcessIterator();
+    bool initialize();
+
+    bool is_valid() const { return _valid; }
+    bool is_valid_entry(struct dirent* const entry) const;
+    bool is_dir(const char* const name) const;
+    char* allocate_string(const char* const str) const;
+    int current(SystemProcess* const process_info);
+    int next_process();
+  };
+
+  ProcessIterator* _iterator;
+  SystemProcesses();
+  bool initialize();
+  ~SystemProcesses();
+
+  //information about system processes
+  int system_processes(SystemProcess** system_processes, int* no_of_sys_processes) const;
+};
+
+bool SystemProcessInterface::SystemProcesses::ProcessIterator::is_dir(const char* name) const {
+  struct stat64 mystat;
+  int ret_val = 0;
+
+  ret_val = ::stat64(name, &mystat);
+
+  if (ret_val < 0) {
+    return false;
+  }
+  ret_val = S_ISDIR(mystat.st_mode);
+  return ret_val > 0;
+}
+
+// if it has a numeric name, is a directory and has a 'psinfo' file in it
+bool SystemProcessInterface::SystemProcesses::ProcessIterator::is_valid_entry(struct dirent* entry) const {
+  // ignore the "." and ".." directories
+  if ((strcmp(entry->d_name, ".") == 0) ||
+      (strcmp(entry->d_name, "..") == 0)) {
+    return false;
+  }
+
+  char buffer[PATH_MAX] = {0};
+  uint64_t size = 0;
+  bool result = false;
+  FILE *fp = NULL;
+
+  if (atoi(entry->d_name) != 0) {
+    jio_snprintf(buffer, PATH_MAX, "/proc/%s", entry->d_name);
+
+    if (is_dir(buffer)) {
+      memset(buffer, 0, PATH_MAX);
+      jio_snprintf(buffer, PATH_MAX, "/proc/%s/psinfo", entry->d_name);
+      if ((fp = fopen(buffer, "r")) != NULL) {
+        int nread = 0;
+        psinfo_t psinfo_data;
+        if ((nread = fread(&psinfo_data, 1, sizeof(psinfo_t), fp)) != -1) {
+          // only considering system process owned by root
+          if (psinfo_data.pr_uid == 0) {
+            result = true;
+          }
+        }
+      }
+    }
+  }
+
+  if (fp != NULL) {
+    fclose(fp);
+  }
+
+  return result;
+}
+
+char* SystemProcessInterface::SystemProcesses::ProcessIterator::allocate_string(const char* str) const {
+  if (str != NULL) {
+    return os::strdup_check_oom(str, mtInternal);
+  }
+  return NULL;
+}
+
+int SystemProcessInterface::SystemProcesses::ProcessIterator::current(SystemProcess* process_info) {
+  if (!is_valid()) {
+    return OS_ERR;
+  }
+
+  char psinfo_path[PATH_MAX] = {0};
+  jio_snprintf(psinfo_path, PATH_MAX, "/proc/%s/psinfo", _entry->d_name);
+
+  FILE *fp = NULL;
+  if ((fp = fopen(psinfo_path, "r")) == NULL) {
+    return OS_ERR;
+  }
+
+  int nread = 0;
+  psinfo_t psinfo_data;
+  if ((nread = fread(&psinfo_data, 1, sizeof(psinfo_t), fp)) == -1) {
+    fclose(fp);
+    return OS_ERR;
+  }
+
+  char *exe_path = NULL;
+  if ((psinfo_data.pr_fname != NULL) &&
+      (psinfo_data.pr_psargs != NULL)) {
+    char *path_substring = strstr(psinfo_data.pr_psargs, psinfo_data.pr_fname);
+    if (path_substring != NULL) {
+      int len = path_substring - psinfo_data.pr_psargs;
+      exe_path = NEW_C_HEAP_ARRAY(char, len+1, mtInternal);
+      jio_snprintf(exe_path, len, "%s", psinfo_data.pr_psargs);
+      exe_path[len] = '\0';
+    }
+  }
+
+  process_info->set_pid(atoi(_entry->d_name));
+  process_info->set_name(allocate_string(psinfo_data.pr_fname));
+  process_info->set_path(allocate_string(exe_path));
+  process_info->set_command_line(allocate_string(psinfo_data.pr_psargs));
+
+  if (exe_path != NULL) {
+    FREE_C_HEAP_ARRAY(char, exe_path);
+  }
+
+  if (fp != NULL) {
+    fclose(fp);
+  }
+
+  return OS_OK;
+}
+
+int SystemProcessInterface::SystemProcesses::ProcessIterator::next_process() {
+  if (!is_valid()) {
+    return OS_ERR;
+  }
+
+  do {
+    _entry = os::readdir(_dir);
+    if (_entry == NULL) {
+      // Error or reached end.  Could use errno to distinguish those cases.
+      _valid = false;
+      return OS_ERR;
+    }
+  } while(!is_valid_entry(_entry));
+
+  _valid = true;
+  return OS_OK;
+}
+
+SystemProcessInterface::SystemProcesses::ProcessIterator::ProcessIterator() {
+  _dir = NULL;
+  _entry = NULL;
+  _valid = false;
+}
+
+bool SystemProcessInterface::SystemProcesses::ProcessIterator::initialize() {
+  _dir = os::opendir("/proc");
+  _entry = NULL;
+  _valid = true;
+  next_process();
+
+  return true;
+}
+
+SystemProcessInterface::SystemProcesses::ProcessIterator::~ProcessIterator() {
+  if (_dir != NULL) {
+    os::closedir(_dir);
+  }
+}
+
+SystemProcessInterface::SystemProcesses::SystemProcesses() {
+  _iterator = NULL;
+}
+
+bool SystemProcessInterface::SystemProcesses::initialize() {
+  _iterator = new SystemProcessInterface::SystemProcesses::ProcessIterator();
+  return _iterator->initialize();
+}
+
+SystemProcessInterface::SystemProcesses::~SystemProcesses() {
+  if (_iterator != NULL) {
+    delete _iterator;
+  }
+}
+
+int SystemProcessInterface::SystemProcesses::system_processes(SystemProcess** system_processes, int* no_of_sys_processes) const {
+  assert(system_processes != NULL, "system_processes pointer is NULL!");
+  assert(no_of_sys_processes != NULL, "system_processes counter pointer is NULL!");
+  assert(_iterator != NULL, "iterator is NULL!");
+
+  // initialize pointers
+  *no_of_sys_processes = 0;
+  *system_processes = NULL;
+
+  while (_iterator->is_valid()) {
+    SystemProcess* tmp = new SystemProcess();
+    _iterator->current(tmp);
+
+    //if already existing head
+    if (*system_processes != NULL) {
+      //move "first to second"
+      tmp->set_next(*system_processes);
+    }
+    // new head
+    *system_processes = tmp;
+    // increment
+    (*no_of_sys_processes)++;
+    // step forward
+    _iterator->next_process();
+  }
+  return OS_OK;
+}
+
+int SystemProcessInterface::system_processes(SystemProcess** system_procs, int* no_of_sys_processes) const {
+  return _impl->system_processes(system_procs, no_of_sys_processes);
+}
+
+SystemProcessInterface::SystemProcessInterface() {
+  _impl = NULL;
+}
+
+bool SystemProcessInterface::initialize() {
+  _impl = new SystemProcessInterface::SystemProcesses();
+  return _impl->initialize();
+
+}
+
+SystemProcessInterface::~SystemProcessInterface() {
+  if (_impl != NULL) {
+    delete _impl;
+  }
+}
+
+CPUInformationInterface::CPUInformationInterface() {
+  _cpu_info = NULL;
+}
+
+bool CPUInformationInterface::initialize() {
+  _cpu_info = new CPUInformation();
+  _cpu_info->set_number_of_hardware_threads(VM_Version_Ext::number_of_threads());
+  _cpu_info->set_number_of_cores(VM_Version_Ext::number_of_cores());
+  _cpu_info->set_number_of_sockets(VM_Version_Ext::number_of_sockets());
+  _cpu_info->set_cpu_name(VM_Version_Ext::cpu_name());
+  _cpu_info->set_cpu_description(VM_Version_Ext::cpu_description());
+  return true;
+}
+
+CPUInformationInterface::~CPUInformationInterface() {
+  if (_cpu_info != NULL) {
+    if (_cpu_info->cpu_name() != NULL) {
+      const char* cpu_name = _cpu_info->cpu_name();
+      FREE_C_HEAP_ARRAY(char, cpu_name);
+      _cpu_info->set_cpu_name(NULL);
+    }
+    if (_cpu_info->cpu_description() != NULL) {
+      const char* cpu_desc = _cpu_info->cpu_description();
+      FREE_C_HEAP_ARRAY(char, cpu_desc);
+      _cpu_info->set_cpu_description(NULL);
+    }
+    delete _cpu_info;
+  }
+}
+
+int CPUInformationInterface::cpu_information(CPUInformation& cpu_info) {
+  if (_cpu_info == NULL) {
+    return OS_ERR;
+  }
+
+  cpu_info = *_cpu_info; // shallow copy assignment
+  return OS_OK;
+}
+
+class NetworkPerformanceInterface::NetworkPerformance : public CHeapObj<mtInternal> {
+  friend class NetworkPerformanceInterface;
+ private:
+  NetworkPerformance();
+  NONCOPYABLE(NetworkPerformance);
+  bool initialize();
+  ~NetworkPerformance();
+  int network_utilization(NetworkInterface** network_interfaces) const;
+};
+
+NetworkPerformanceInterface::NetworkPerformance::NetworkPerformance() {
+
+}
+
+bool NetworkPerformanceInterface::NetworkPerformance::initialize() {
+  return true;
+}
+
+NetworkPerformanceInterface::NetworkPerformance::~NetworkPerformance() {
+
+}
+
+int NetworkPerformanceInterface::NetworkPerformance::network_utilization(NetworkInterface** network_interfaces) const
+{
+  kstat_ctl_t* ctl = kstat_open();
+  if (ctl == NULL) {
+    return OS_ERR;
+  }
+
+  NetworkInterface* ret = NULL;
+  for (kstat_t* k = ctl->kc_chain; k != NULL; k = k->ks_next) {
+    if (strcmp(k->ks_class, "net") != 0) {
+      continue;
+    }
+    if (strcmp(k->ks_module, "link") != 0) {
+      continue;
+    }
+
+    if (kstat_read(ctl, k, NULL) == -1) {
+      return OS_ERR;
+    }
+
+    uint64_t bytes_in = UINT64_MAX;
+    uint64_t bytes_out = UINT64_MAX;
+    for (unsigned int i = 0; i < k->ks_ndata; ++i) {
+      kstat_named_t* data = &reinterpret_cast<kstat_named_t*>(k->ks_data)[i];
+      if (strcmp(data->name, "rbytes64") == 0) {
+        bytes_in = data->value.ui64;
+      }
+      else if (strcmp(data->name, "obytes64") == 0) {
+        bytes_out = data->value.ui64;
+      }
+    }
+
+    if ((bytes_in != UINT64_MAX) && (bytes_out != UINT64_MAX)) {
+      NetworkInterface* cur = new NetworkInterface(k->ks_name, bytes_in, bytes_out, ret);
+      ret = cur;
+    }
+  }
+
+  kstat_close(ctl);
+  *network_interfaces = ret;
+
+  return OS_OK;
+}
+
+NetworkPerformanceInterface::NetworkPerformanceInterface() {
+  _impl = NULL;
+}
+
+NetworkPerformanceInterface::~NetworkPerformanceInterface() {
+  if (_impl != NULL) {
+    delete _impl;
+  }
+}
+
+bool NetworkPerformanceInterface::initialize() {
+  _impl = new NetworkPerformanceInterface::NetworkPerformance();
+  return _impl->initialize();
+}
+
+int NetworkPerformanceInterface::network_utilization(NetworkInterface** network_interfaces) const {
+  return _impl->network_utilization(network_interfaces);
+}
diff -urN /tmp/a/os_share_solaris.hpp b/src/hotspot/os/solaris/os_share_solaris.hpp
--- /tmp/a/os_share_solaris.hpp	1970-01-01 01:00:00.000000000 +0100
+++ b/src/hotspot/os/solaris/os_share_solaris.hpp	2024-12-05 11:44:56.840280725 +0000
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 1999, 2019, 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.
+ *
+ * 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.
+ *
+ */
+
+#ifndef OS_SOLARIS_OS_SHARE_SOLARIS_HPP
+#define OS_SOLARIS_OS_SHARE_SOLARIS_HPP
+
+// Defines the interfaces to Solaris operating systems that vary across platforms
+
+// misc
+extern "C" {
+  void signalHandler(int, siginfo_t*, void*);
+}
+void resolve_lwp_exit_calls(void);
+void handle_unexpected_exception(Thread* thread, int sig, siginfo_t* info, address pc, address adjusted_pc);
+#ifndef PRODUCT
+void continue_with_dump(void);
+#endif
+
+#define PROCFILE_LENGTH 128
+
+#endif // OS_SOLARIS_OS_SHARE_SOLARIS_HPP
diff -urN /tmp/a/os_solaris.cpp b/src/hotspot/os/solaris/os_solaris.cpp
--- /tmp/a/os_solaris.cpp	1970-01-01 01:00:00.000000000 +0100
+++ b/src/hotspot/os/solaris/os_solaris.cpp	2024-12-05 11:44:56.897756134 +0000
@@ -0,0 +1,3939 @@
+/*
+ * Copyright (c) 1997, 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.
+ *
+ * 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.
+ *
+ */
+
+// no precompiled headers
+#include "jvm.h"
+#include "classfile/classLoader.hpp"
+#include "classfile/systemDictionary.hpp"
+#include "classfile/vmSymbols.hpp"
+#include "code/icBuffer.hpp"
+#include "code/vtableStubs.hpp"
+#include "compiler/compileBroker.hpp"
+#include "compiler/disassembler.hpp"
+#include "interpreter/interpreter.hpp"
+#include "jvmtifiles/jvmti.h"
+#include "logging/log.hpp"
+#include "logging/logStream.hpp"
+#include "memory/allocation.inline.hpp"
+#include "memory/universe.hpp"
+#include "oops/oop.inline.hpp"
+#include "os_share_solaris.hpp"
+#include "os_solaris.inline.hpp"
+#include "prims/jniFastGetField.hpp"
+#include "prims/jvm_misc.hpp"
+#include "runtime/arguments.hpp"
+#include "runtime/atomic.hpp"
+#include "runtime/globals.hpp"
+#include "runtime/globals_extension.hpp"
+#include "runtime/interfaceSupport.inline.hpp"
+#include "runtime/java.hpp"
+#include "runtime/javaCalls.hpp"
+#include "runtime/mutexLocker.hpp"
+#include "runtime/objectMonitor.hpp"
+#include "runtime/orderAccess.hpp"
+#include "runtime/osThread.hpp"
+#include "runtime/perfMemory.hpp"
+#include "runtime/sharedRuntime.hpp"
+#include "runtime/statSampler.hpp"
+#include "runtime/stubRoutines.hpp"
+#include "runtime/thread.inline.hpp"
+#include "runtime/threadCritical.hpp"
+#include "runtime/timer.hpp"
+#include "runtime/vm_version.hpp"
+#include "semaphore_posix.hpp"
+#include "services/attachListener.hpp"
+#include "services/memTracker.hpp"
+#include "services/runtimeService.hpp"
+#include "signals_posix.hpp"
+#include "utilities/align.hpp"
+#include "utilities/decoder.hpp"
+#include "utilities/defaultStream.hpp"
+#include "utilities/events.hpp"
+#include "utilities/growableArray.hpp"
+#include "utilities/macros.hpp"
+#include "utilities/vmError.hpp"
+
+// put OS-includes here
+# include <dlfcn.h>
+# include <errno.h>
+# include <exception>
+# include <link.h>
+# include <poll.h>
+# include <pthread.h>
+# include <setjmp.h>
+# include <signal.h>
+# include <stdio.h>
+# include <alloca.h>
+# include <sys/filio.h>
+# include <sys/ipc.h>
+# include <sys/lwp.h>
+# include <sys/machelf.h>     // for elf Sym structure used by dladdr1
+# include <sys/mman.h>
+# include <sys/processor.h>
+# include <sys/procset.h>
+# include <sys/pset.h>
+# include <sys/resource.h>
+# include <sys/shm.h>
+# include <sys/socket.h>
+# include <sys/stat.h>
+# include <sys/systeminfo.h>
+# include <sys/time.h>
+# include <sys/times.h>
+# include <sys/types.h>
+# include <sys/wait.h>
+# include <sys/utsname.h>
+# include <thread.h>
+# include <unistd.h>
+# include <sys/priocntl.h>
+# include <sys/rtpriocntl.h>
+# include <sys/tspriocntl.h>
+# include <sys/iapriocntl.h>
+# include <sys/fxpriocntl.h>
+# include <sys/loadavg.h>
+# include <string.h>
+# include <stdio.h>
+
+# define _STRUCTURED_PROC 1  //  this gets us the new structured proc interfaces of 5.6 & later
+# include <sys/procfs.h>     //  see comment in <sys/procfs.h>
+
+#define MAX_PATH (2 * K)
+
+// for timer info max values which include all bits
+#define ALL_64_BITS CONST64(0xFFFFFFFFFFFFFFFF)
+
+int os::Solaris::_page_size = -1;
+
+// Here are some liblgrp types from sys/lgrp_user.h to be able to
+// compile on older systems without this header file.
+
+#ifndef MADV_ACCESS_LWP
+  #define  MADV_ACCESS_LWP   7       /* next LWP to access heavily */
+#endif
+#ifndef MADV_ACCESS_MANY
+  #define  MADV_ACCESS_MANY  8       /* many processes to access heavily */
+#endif
+
+#ifndef LGRP_RSRC_CPU
+  #define LGRP_RSRC_CPU      0       /* CPU resources */
+#endif
+#ifndef LGRP_RSRC_MEM
+  #define LGRP_RSRC_MEM      1       /* memory resources */
+#endif
+
+// guarded in sys/mman.h
+extern "C" {
+extern int getpagesizes(size_t[], int);
+}
+
+// Values for ThreadPriorityPolicy == 1
+int prio_policy1[CriticalPriority+1] = {
+  -99999,  0, 16,  32,  48,  64,
+          80, 96, 112, 124, 127, 127 };
+
+// System parameters used internally
+static clock_t clock_tics_per_sec = 100;
+
+address os::Solaris::handler_start;  // start pc of thr_sighndlrinfo
+address os::Solaris::handler_end;    // end pc of thr_sighndlrinfo
+
+address os::Solaris::_main_stack_base = NULL;  // 4352906 workaround
+
+os::Solaris::pthread_setname_np_func_t os::Solaris::_pthread_setname_np = NULL;
+
+// "default" initializers for missing libc APIs
+extern "C" {
+  int memcntl(void *, size_t, int, void *, int, int);
+  int meminfo(const uint64_t *, int, const uint_t *, int, uint64_t *, uint_t *);
+}
+
+// "default" initializers for pthread-based synchronization
+extern "C" {
+  static int pthread_mutex_default_init(mutex_t *mx, int scope, void *arg) { memset(mx, 0, sizeof(mutex_t)); return 0; }
+  static int pthread_cond_default_init(cond_t *cv, int scope, void *arg){ memset(cv, 0, sizeof(cond_t)); return 0; }
+}
+
+static void unpackTime(timespec* absTime, bool isAbsolute, jlong time);
+
+static inline size_t adjust_stack_size(address base, size_t size) {
+  if ((ssize_t)size < 0) {
+    // 4759953: Compensate for ridiculous stack size.
+    size = max_intx;
+  }
+  if (size > (size_t)base) {
+    // 4812466: Make sure size doesn't allow the stack to wrap the address space.
+    size = (size_t)base;
+  }
+  return size;
+}
+
+static inline stack_t get_stack_info() {
+  stack_t st;
+  int retval = thr_stksegment(&st);
+  st.ss_size = adjust_stack_size((address)st.ss_sp, st.ss_size);
+  assert(retval == 0, "incorrect return value from thr_stksegment");
+  assert((address)&st < (address)st.ss_sp, "Invalid stack base returned");
+  assert((address)&st > (address)st.ss_sp-st.ss_size, "Invalid stack size returned");
+  return st;
+}
+
+bool os::is_primordial_thread(void) {
+  int r = thr_main();
+  guarantee(r == 0 || r == 1, "CR6501650 or CR6493689");
+  return r == 1;
+}
+
+address os::current_stack_base() {
+  bool _is_primordial_thread = is_primordial_thread();
+
+  // Workaround 4352906, avoid calls to thr_stksegment by
+  // thr_main after the first one (it looks like we trash
+  // some data, causing the value for ss_sp to be incorrect).
+  if (!_is_primordial_thread || os::Solaris::_main_stack_base == NULL) {
+    stack_t st = get_stack_info();
+    if (_is_primordial_thread) {
+      // cache initial value of stack base
+      os::Solaris::_main_stack_base = (address)st.ss_sp;
+    }
+    return (address)st.ss_sp;
+  } else {
+    guarantee(os::Solaris::_main_stack_base != NULL, "Attempt to use null cached stack base");
+    return os::Solaris::_main_stack_base;
+  }
+}
+
+size_t os::current_stack_size() {
+  size_t size;
+
+  if (!is_primordial_thread()) {
+    size = get_stack_info().ss_size;
+  } else {
+    struct rlimit limits;
+    getrlimit(RLIMIT_STACK, &limits);
+    size = adjust_stack_size(os::Solaris::_main_stack_base, (size_t)limits.rlim_cur);
+  }
+  // base may not be page aligned
+  address base = current_stack_base();
+  address bottom = align_up(base - size, os::vm_page_size());;
+  return (size_t)(base - bottom);
+}
+
+struct tm* os::localtime_pd(const time_t* clock, struct tm*  res) {
+  return localtime_r(clock, res);
+}
+
+jint os::Solaris::_os_thread_limit = 0;
+volatile jint os::Solaris::_os_thread_count = 0;
+
+julong os::available_memory() {
+  return Solaris::available_memory();
+}
+
+julong os::Solaris::available_memory() {
+  return (julong)sysconf(_SC_AVPHYS_PAGES) * os::vm_page_size();
+}
+
+julong os::Solaris::_physical_memory = 0;
+
+julong os::physical_memory() {
+  return Solaris::physical_memory();
+}
+
+static hrtime_t first_hrtime = 0;
+static const hrtime_t hrtime_hz = 1000*1000*1000;
+static volatile hrtime_t max_hrtime = 0;
+
+
+void os::Solaris::initialize_system_info() {
+  set_processor_count(sysconf(_SC_NPROCESSORS_CONF));
+  _physical_memory = (julong)sysconf(_SC_PHYS_PAGES) *
+                                     (julong)sysconf(_SC_PAGESIZE);
+}
+
+uint os::processor_id() {
+  const processorid_t id = ::getcpuid();
+  assert(id >= 0 && id < _processor_count, "Invalid processor id");
+  return (uint)id;
+}
+
+int os::active_processor_count() {
+  // User has overridden the number of active processors
+  if (ActiveProcessorCount > 0) {
+    log_trace(os)("active_processor_count: "
+                  "active processor count set by user : %d",
+                  ActiveProcessorCount);
+    return ActiveProcessorCount;
+  }
+
+  int online_cpus = sysconf(_SC_NPROCESSORS_ONLN);
+  pid_t pid = getpid();
+  psetid_t pset = PS_NONE;
+  // Are we running in a processor set or is there any processor set around?
+  if (pset_bind(PS_QUERY, P_PID, pid, &pset) == 0) {
+    uint_t pset_cpus;
+    // Query the number of cpus available to us.
+    if (pset_info(pset, NULL, &pset_cpus, NULL) == 0) {
+      assert(pset_cpus > 0 && pset_cpus <= online_cpus, "sanity check");
+      return pset_cpus;
+    }
+  }
+  // Otherwise return number of online cpus
+  return online_cpus;
+}
+
+void os::set_native_thread_name(const char *name) {
+  if (Solaris::_pthread_setname_np != NULL) {
+    // Only the first 31 bytes of 'name' are processed by pthread_setname_np
+    // but we explicitly copy into a size-limited buffer to avoid any
+    // possible overflow.
+    char buf[32];
+    snprintf(buf, sizeof(buf), "%s", name);
+    buf[sizeof(buf) - 1] = '\0';
+    Solaris::_pthread_setname_np(pthread_self(), buf);
+  }
+}
+
+bool os::bind_to_processor(uint processor_id) {
+  // We assume that a processorid_t can be stored in a uint.
+  assert(sizeof(uint) == sizeof(processorid_t),
+         "can't convert uint to processorid_t");
+  int bind_result =
+    processor_bind(P_LWPID,                       // bind LWP.
+                   P_MYID,                        // bind current LWP.
+                   (processorid_t) processor_id,  // id.
+                   NULL);                         // don't return old binding.
+  return (bind_result == 0);
+}
+
+// Return true if user is running as root.
+
+bool os::have_special_privileges() {
+  static bool init = false;
+  static bool privileges = false;
+  if (!init) {
+    privileges = (getuid() != geteuid()) || (getgid() != getegid());
+    init = true;
+  }
+  return privileges;
+}
+
+
+void os::init_system_properties_values() {
+  // The next steps are taken in the product version:
+  //
+  // Obtain the JAVA_HOME value from the location of libjvm.so.
+  // This library should be located at:
+  // <JAVA_HOME>/jre/lib/<arch>/{client|server}/libjvm.so.
+  //
+  // If "/jre/lib/" appears at the right place in the path, then we
+  // assume libjvm.so is installed in a JDK and we use this path.
+  //
+  // Otherwise exit with message: "Could not create the Java virtual machine."
+  //
+  // The following extra steps are taken in the debugging version:
+  //
+  // If "/jre/lib/" does NOT appear at the right place in the path
+  // instead of exit check for $JAVA_HOME environment variable.
+  //
+  // If it is defined and we are able to locate $JAVA_HOME/jre/lib/<arch>,
+  // then we append a fake suffix "hotspot/libjvm.so" to this path so
+  // it looks like libjvm.so is installed there
+  // <JAVA_HOME>/jre/lib/<arch>/hotspot/libjvm.so.
+  //
+  // Otherwise exit.
+  //
+  // Important note: if the location of libjvm.so changes this
+  // code needs to be changed accordingly.
+
+// Base path of extensions installed on the system.
+#define SYS_EXT_DIR     "/usr/jdk/packages"
+#define EXTENSIONS_DIR  "/lib/ext"
+
+  // Buffer that fits several sprintfs.
+  // Note that the space for the colon and the trailing null are provided
+  // by the nulls included by the sizeof operator.
+  const size_t bufsize =
+    MAX3((size_t)MAXPATHLEN,  // For dll_dir & friends.
+         sizeof(SYS_EXT_DIR) + sizeof("/lib/"), // invariant ld_library_path
+         (size_t)MAXPATHLEN + sizeof(EXTENSIONS_DIR) + sizeof(SYS_EXT_DIR) + sizeof(EXTENSIONS_DIR)); // extensions dir
+  char *buf = NEW_C_HEAP_ARRAY(char, bufsize, mtInternal);
+
+  // sysclasspath, java_home, dll_dir
+  {
+    char *pslash;
+    os::jvm_path(buf, bufsize);
+
+    // Found the full path to libjvm.so.
+    // Now cut the path to <java_home>/jre if we can.
+    *(strrchr(buf, '/')) = '\0'; // Get rid of /libjvm.so.
+    pslash = strrchr(buf, '/');
+    if (pslash != NULL) {
+      *pslash = '\0';            // Get rid of /{client|server|hotspot}.
+    }
+    Arguments::set_dll_dir(buf);
+
+    if (pslash != NULL) {
+      pslash = strrchr(buf, '/');
+      if (pslash != NULL) {
+        *pslash = '\0';        // Get rid of /lib.
+      }
+    }
+    Arguments::set_java_home(buf);
+    if (!set_boot_path('/', ':')) {
+      vm_exit_during_initialization("Failed setting boot class path.", NULL);
+    }
+  }
+
+  // Where to look for native libraries.
+  {
+    // Use dlinfo() to determine the correct java.library.path.
+    //
+    // If we're launched by the Java launcher, and the user
+    // does not set java.library.path explicitly on the commandline,
+    // the Java launcher sets LD_LIBRARY_PATH for us and unsets
+    // LD_LIBRARY_PATH_32 and LD_LIBRARY_PATH_64.  In this case
+    // dlinfo returns LD_LIBRARY_PATH + crle settings (including
+    // /usr/lib), which is exactly what we want.
+    //
+    // If the user does set java.library.path, it completely
+    // overwrites this setting, and always has.
+    //
+    // If we're not launched by the Java launcher, we may
+    // get here with any/all of the LD_LIBRARY_PATH[_32|64]
+    // settings.  Again, dlinfo does exactly what we want.
+
+    Dl_serinfo     info_sz, *info = &info_sz;
+    Dl_serpath     *path;
+    char           *library_path;
+    char           *common_path = buf;
+
+    // Determine search path count and required buffer size.
+    if (dlinfo(RTLD_SELF, RTLD_DI_SERINFOSIZE, (void *)info) == -1) {
+      FREE_C_HEAP_ARRAY(char, buf);
+      vm_exit_during_initialization("dlinfo SERINFOSIZE request", dlerror());
+    }
+
+    // Allocate new buffer and initialize.
+    info = (Dl_serinfo*)NEW_C_HEAP_ARRAY(char, info_sz.dls_size, mtInternal);
+    info->dls_size = info_sz.dls_size;
+    info->dls_cnt = info_sz.dls_cnt;
+
+    // Obtain search path information.
+    if (dlinfo(RTLD_SELF, RTLD_DI_SERINFO, (void *)info) == -1) {
+      FREE_C_HEAP_ARRAY(char, buf);
+      FREE_C_HEAP_ARRAY(char, info);
+      vm_exit_during_initialization("dlinfo SERINFO request", dlerror());
+    }
+
+    path = &info->dls_serpath[0];
+
+    // Note: Due to a legacy implementation, most of the library path
+    // is set in the launcher. This was to accommodate linking restrictions
+    // on legacy Solaris implementations (which are no longer supported).
+    // Eventually, all the library path setting will be done here.
+    //
+    // However, to prevent the proliferation of improperly built native
+    // libraries, the new path component /usr/jdk/packages is added here.
+
+    // Construct the invariant part of ld_library_path.
+    sprintf(common_path, SYS_EXT_DIR "/lib");
+
+    // Struct size is more than sufficient for the path components obtained
+    // through the dlinfo() call, so only add additional space for the path
+    // components explicitly added here.
+    size_t library_path_size = info->dls_size + strlen(common_path);
+    library_path = NEW_C_HEAP_ARRAY(char, library_path_size, mtInternal);
+    library_path[0] = '\0';
+
+    // Construct the desired Java library path from the linker's library
+    // search path.
+    //
+    // For compatibility, it is optimal that we insert the additional path
+    // components specific to the Java VM after those components specified
+    // in LD_LIBRARY_PATH (if any) but before those added by the ld.so
+    // infrastructure.
+    if (info->dls_cnt == 0) { // Not sure this can happen, but allow for it.
+      strcpy(library_path, common_path);
+    } else {
+      int inserted = 0;
+      uint_t i;
+      for (i = 0; i < info->dls_cnt; i++, path++) {
+        uint_t flags = path->dls_flags & LA_SER_MASK;
+        if (((flags & LA_SER_LIBPATH) == 0) && !inserted) {
+          strcat(library_path, common_path);
+          strcat(library_path, os::path_separator());
+          inserted = 1;
+        }
+        strcat(library_path, path->dls_name);
+        strcat(library_path, os::path_separator());
+      }
+      // Eliminate trailing path separator.
+      library_path[strlen(library_path)-1] = '\0';
+    }
+
+    // happens before argument parsing - can't use a trace flag
+    // tty->print_raw("init_system_properties_values: native lib path: ");
+    // tty->print_raw_cr(library_path);
+
+    // Callee copies into its own buffer.
+    Arguments::set_library_path(library_path);
+
+    FREE_C_HEAP_ARRAY(char, library_path);
+    FREE_C_HEAP_ARRAY(char, info);
+  }
+
+  // Extensions directories.
+  sprintf(buf, "%s" EXTENSIONS_DIR ":" SYS_EXT_DIR EXTENSIONS_DIR, Arguments::get_java_home());
+  Arguments::set_ext_dirs(buf);
+
+  FREE_C_HEAP_ARRAY(char, buf);
+
+#undef SYS_EXT_DIR
+#undef EXTENSIONS_DIR
+}
+
+void os::breakpoint() {
+  BREAKPOINT;
+}
+
+extern "C" void breakpoint() {
+  // use debugger to set breakpoint here
+}
+
+static thread_t main_thread;
+
+// Thread start routine for all newly created threads
+extern "C" void* thread_native_entry(void* thread_addr) {
+
+  Thread* thread = (Thread*)thread_addr;
+
+  thread->record_stack_base_and_size();
+
+  // Try to randomize the cache line index of hot stack frames.
+  // This helps when threads of the same stack traces evict each other's
+  // cache lines. The threads can be either from the same JVM instance, or
+  // from different JVM instances. The benefit is especially true for
+  // processors with hyperthreading technology.
+  static int counter = 0;
+  int pid = os::current_process_id();
+  alloca(((pid ^ counter++) & 7) * 128);
+
+  int prio;
+
+  thread->initialize_thread_current();
+
+  OSThread* osthr = thread->osthread();
+
+  osthr->set_lwp_id(_lwp_self());  // Store lwp in case we are bound
+
+  log_info(os, thread)("Thread is alive (tid: " UINTX_FORMAT ").",
+    os::current_thread_id());
+
+  if (UseNUMA) {
+    int lgrp_id = os::numa_get_group_id();
+    if (lgrp_id != -1) {
+      thread->set_lgrp_id(lgrp_id);
+    }
+  }
+
+  // Our priority was set when we were created, and stored in the
+  // osthread, but couldn't be passed through to our LWP until now.
+  // So read back the priority and set it again.
+
+  if (osthr->thread_id() != -1) {
+    if (UseThreadPriorities) {
+      int prio = osthr->native_priority();
+      if (ThreadPriorityVerbose) {
+        tty->print_cr("Starting Thread " INTPTR_FORMAT ", LWP is "
+                      INTPTR_FORMAT ", setting priority: %d\n",
+                      (uintx)osthr->thread_id(), (uintx)osthr->lwp_id(), prio);
+      }
+      os::set_native_priority(thread, prio);
+    }
+  } else if (ThreadPriorityVerbose) {
+    warning("Can't set priority in _start routine, thread id hasn't been set\n");
+  }
+
+  assert(osthr->get_state() == RUNNABLE, "invalid os thread state");
+
+  // initialize signal mask for this thread
+  PosixSignals::hotspot_sigmask(thread);
+
+  os::Solaris::init_thread_fpu_state();
+
+  thread->call_run();
+
+  // Note: at this point the thread object may already have deleted itself.
+  // Do not dereference it from here on out.
+
+  // One less thread is executing
+  // When the VMThread gets here, the main thread may have already exited
+  // which frees the CodeHeap containing the Atomic::dec code
+  if (thread != VMThread::vm_thread() && VMThread::vm_thread() != NULL) {
+    Atomic::dec(&os::Solaris::_os_thread_count);
+  }
+
+  log_info(os, thread)("Thread finished (tid: " UINTX_FORMAT ").", os::current_thread_id());
+
+  if (UseDetachedThreads) {
+    thr_exit(NULL);
+    ShouldNotReachHere();
+  }
+  return NULL;
+}
+
+static OSThread* create_os_thread(Thread* thread, thread_t thread_id) {
+  // Allocate the OSThread object
+  OSThread* osthread = new OSThread(NULL, NULL);
+  if (osthread == NULL) return NULL;
+
+  // Store info on the Solaris thread into the OSThread
+  osthread->set_thread_id(thread_id);
+  osthread->set_lwp_id(_lwp_self());
+
+  if (UseNUMA) {
+    int lgrp_id = os::numa_get_group_id();
+    if (lgrp_id != -1) {
+      thread->set_lgrp_id(lgrp_id);
+    }
+  }
+
+  if (ThreadPriorityVerbose) {
+    tty->print_cr("In create_os_thread, Thread " INTPTR_FORMAT ", LWP is " INTPTR_FORMAT "\n",
+                  (uintx)osthread->thread_id(), (uintx)osthread->lwp_id());
+  }
+
+  // Initial thread state is INITIALIZED, not SUSPENDED
+  osthread->set_state(INITIALIZED);
+
+  return osthread;
+}
+
+bool os::create_attached_thread(JavaThread* thread) {
+#ifdef ASSERT
+  thread->verify_not_published();
+#endif
+  OSThread* osthread = create_os_thread(thread, thr_self());
+  if (osthread == NULL) {
+    return false;
+  }
+
+  // Initial thread state is RUNNABLE
+  osthread->set_state(RUNNABLE);
+  thread->set_osthread(osthread);
+
+  // initialize signal mask for this thread
+  // and save the caller's signal mask
+  PosixSignals::hotspot_sigmask(thread);
+
+  log_info(os, thread)("Thread attached (tid: " UINTX_FORMAT ").",
+    os::current_thread_id());
+
+  return true;
+}
+
+bool os::create_main_thread(JavaThread* thread) {
+#ifdef ASSERT
+  thread->verify_not_published();
+#endif
+  if (_starting_thread == NULL) {
+    _starting_thread = create_os_thread(thread, main_thread);
+    if (_starting_thread == NULL) {
+      return false;
+    }
+  }
+
+  // The primodial thread is runnable from the start
+  _starting_thread->set_state(RUNNABLE);
+
+  thread->set_osthread(_starting_thread);
+
+  // initialize signal mask for this thread
+  // and save the caller's signal mask
+  PosixSignals::hotspot_sigmask(thread);
+
+  return true;
+}
+
+// Helper function to trace thread attributes, similar to os::Posix::describe_pthread_attr()
+static char* describe_thr_create_attributes(char* buf, size_t buflen,
+                                            size_t stacksize, long flags) {
+  stringStream ss(buf, buflen);
+  ss.print("stacksize: " SIZE_FORMAT "k, ", stacksize / 1024);
+  ss.print("flags: ");
+  #define PRINT_FLAG(f) if (flags & f) ss.print( #f " ");
+  #define ALL(X) \
+    X(THR_SUSPENDED) \
+    X(THR_DETACHED) \
+    X(THR_BOUND) \
+    X(THR_NEW_LWP) \
+    X(THR_DAEMON)
+  ALL(PRINT_FLAG)
+  #undef ALL
+  #undef PRINT_FLAG
+  return buf;
+}
+
+// return default stack size for thr_type
+size_t os::Posix::default_stack_size(os::ThreadType thr_type) {
+  // default stack size when not specified by caller is 1M (2M for LP64)
+  size_t s = (BytesPerWord >> 2) * K * K;
+  return s;
+}
+
+bool os::create_thread(Thread* thread, ThreadType thr_type,
+                       size_t req_stack_size) {
+  // Allocate the OSThread object
+  OSThread* osthread = new OSThread(NULL, NULL);
+  if (osthread == NULL) {
+    return false;
+  }
+
+  if (ThreadPriorityVerbose) {
+    char *thrtyp;
+    switch (thr_type) {
+    case vm_thread:
+      thrtyp = (char *)"vm";
+      break;
+    case cgc_thread:
+      thrtyp = (char *)"cgc";
+      break;
+    case pgc_thread:
+      thrtyp = (char *)"pgc";
+      break;
+    case java_thread:
+      thrtyp = (char *)"java";
+      break;
+    case compiler_thread:
+      thrtyp = (char *)"compiler";
+      break;
+    case watcher_thread:
+      thrtyp = (char *)"watcher";
+      break;
+    default:
+      thrtyp = (char *)"unknown";
+      break;
+    }
+    tty->print_cr("In create_thread, creating a %s thread\n", thrtyp);
+  }
+
+  // calculate stack size if it's not specified by caller
+  size_t stack_size = os::Posix::get_initial_stack_size(thr_type, req_stack_size);
+
+  // Initial state is ALLOCATED but not INITIALIZED
+  osthread->set_state(ALLOCATED);
+
+  if (os::Solaris::_os_thread_count > os::Solaris::_os_thread_limit) {
+    // We got lots of threads. Check if we still have some address space left.
+    // Need to be at least 5Mb of unreserved address space. We do check by
+    // trying to reserve some.
+    const size_t VirtualMemoryBangSize = 20*K*K;
+    char* mem = os::reserve_memory(VirtualMemoryBangSize);
+    if (mem == NULL) {
+      delete osthread;
+      return false;
+    } else {
+      // Release the memory again
+      os::release_memory(mem, VirtualMemoryBangSize);
+    }
+  }
+
+  // Setup osthread because the child thread may need it.
+  thread->set_osthread(osthread);
+
+  // Create the Solaris thread
+  thread_t tid = 0;
+  long     flags = (UseDetachedThreads ? THR_DETACHED : 0) | THR_SUSPENDED;
+  int      status;
+
+  // Mark that we don't have an lwp or thread id yet.
+  // In case we attempt to set the priority before the thread starts.
+  osthread->set_lwp_id(-1);
+  osthread->set_thread_id(-1);
+
+  status = thr_create(NULL, stack_size, thread_native_entry, thread, flags, &tid);
+
+  char buf[64];
+  if (status == 0) {
+    log_info(os, thread)("Thread \"%s\" started (tid: " UINTX_FORMAT ", attributes: %s). ",
+      thread->name(), (uintx) tid, describe_thr_create_attributes(buf, sizeof(buf), stack_size, flags));
+  } else {
+    log_warning(os, thread)("Failed to start thread \"%s\" - thr_create failed (%s) for attributes: %s.",
+      thread->name(), os::errno_name(status), describe_thr_create_attributes(buf, sizeof(buf), stack_size, flags));
+    // Log some OS information which might explain why creating the thread failed.
+    log_info(os, thread)("Number of threads approx. running in the VM: %d", Threads::number_of_threads());
+    LogStream st(Log(os, thread)::info());
+    os::Posix::print_rlimit_info(&st);
+    os::print_memory_info(&st);
+  }
+
+  if (status != 0) {
+    thread->set_osthread(NULL);
+    // Need to clean up stuff we've allocated so far
+    delete osthread;
+    return false;
+  }
+
+  Atomic::inc(&os::Solaris::_os_thread_count);
+
+  // Store info on the Solaris thread into the OSThread
+  osthread->set_thread_id(tid);
+
+  // Remember that we created this thread so we can set priority on it
+  osthread->set_vm_created();
+
+  // Most thread types will set an explicit priority before starting the thread,
+  // but for those that don't we need a valid value to read back in thread_native_entry.
+  osthread->set_native_priority(NormPriority);
+
+  // Initial thread state is INITIALIZED, not SUSPENDED
+  osthread->set_state(INITIALIZED);
+
+  // The thread is returned suspended (in state INITIALIZED), and is started higher up in the call chain
+  return true;
+}
+
+// CR 7190089: on Solaris, primordial thread's stack needs adjusting.
+// Without the adjustment, stack size is incorrect if stack is set to unlimited (ulimit -s unlimited).
+void os::Solaris::correct_stack_boundaries_for_primordial_thread(Thread* thr) {
+  assert(is_primordial_thread(), "Call only for primordial thread");
+
+  JavaThread* jt = (JavaThread *)thr;
+  assert(jt != NULL, "Sanity check");
+  size_t stack_size;
+  address base = jt->stack_base();
+  if (Arguments::created_by_java_launcher()) {
+    // Use 2MB to allow for Solaris 7 64 bit mode.
+    stack_size = JavaThread::stack_size_at_create() == 0
+      ? 2048*K : JavaThread::stack_size_at_create();
+
+    // There are rare cases when we may have already used more than
+    // the basic stack size allotment before this method is invoked.
+    // Attempt to allow for a normally sized java_stack.
+    size_t current_stack_offset = (size_t)(base - (address)&stack_size);
+    stack_size += ReservedSpace::page_align_size_down(current_stack_offset);
+  } else {
+    // 6269555: If we were not created by a Java launcher, i.e. if we are
+    // running embedded in a native application, treat the primordial thread
+    // as much like a native attached thread as possible.  This means using
+    // the current stack size from thr_stksegment(), unless it is too large
+    // to reliably setup guard pages.  A reasonable max size is 8MB.
+    size_t current_size = os::current_stack_size();
+    // This should never happen, but just in case....
+    if (current_size == 0) current_size = 2 * K * K;
+    stack_size = current_size > (8 * K * K) ? (8 * K * K) : current_size;
+  }
+  address bottom = align_up(base - stack_size, os::vm_page_size());;
+  stack_size = (size_t)(base - bottom);
+
+  assert(stack_size > 0, "Stack size calculation problem");
+
+  if (stack_size > jt->stack_size()) {
+#ifndef PRODUCT
+    struct rlimit limits;
+    getrlimit(RLIMIT_STACK, &limits);
+    size_t size = adjust_stack_size(base, (size_t)limits.rlim_cur);
+    assert(size >= jt->stack_size(), "Stack size problem in main thread");
+#endif
+    tty->print_cr("Stack size of " SIZE_FORMAT " Kb exceeds current limit of " SIZE_FORMAT " Kb.\n"
+                  "(Stack sizes are rounded up to a multiple of the system page size.)\n"
+                  "See limit(1) to increase the stack size limit.",
+                  stack_size / K, jt->stack_size() / K);
+    vm_exit(1);
+  }
+  assert(jt->stack_size() >= stack_size,
+         "Attempt to map more stack than was allocated");
+  jt->set_stack_size(stack_size);
+
+}
+
+
+
+// Free Solaris resources related to the OSThread
+void os::free_thread(OSThread* osthread) {
+  assert(osthread != NULL, "os::free_thread but osthread not set");
+
+  // We are told to free resources of the argument thread,
+  // but we can only really operate on the current thread.
+  assert(Thread::current()->osthread() == osthread,
+         "os::free_thread but not current thread");
+
+  // Restore caller's signal mask
+  sigset_t sigmask = osthread->caller_sigmask();
+  pthread_sigmask(SIG_SETMASK, &sigmask, NULL);
+
+  delete osthread;
+}
+
+void os::pd_start_thread(Thread* thread) {
+  int status = thr_continue(thread->osthread()->thread_id());
+  assert_status(status == 0, status, "thr_continue failed");
+}
+
+
+intx os::current_thread_id() {
+  return (intx)thr_self();
+}
+
+static pid_t _initial_pid = 0;
+
+int os::current_process_id() {
+  return (int)(_initial_pid ? _initial_pid : getpid());
+}
+
+// gethrtime() should be monotonic according to the documentation,
+// but some virtualized platforms are known to break this guarantee.
+// getTimeNanos() must be guaranteed not to move backwards, so we
+// are forced to add a check here.
+inline hrtime_t getTimeNanos() {
+  const hrtime_t now = gethrtime();
+  const hrtime_t prev = max_hrtime;
+  if (now <= prev) {
+    return prev;   // same or retrograde time;
+  }
+  const hrtime_t obsv = Atomic::cmpxchg(&max_hrtime, prev, now);
+  assert(obsv >= prev, "invariant");   // Monotonicity
+  // If the CAS succeeded then we're done and return "now".
+  // If the CAS failed and the observed value "obsv" is >= now then
+  // we should return "obsv".  If the CAS failed and now > obsv > prv then
+  // some other thread raced this thread and installed a new value, in which case
+  // we could either (a) retry the entire operation, (b) retry trying to install now
+  // or (c) just return obsv.  We use (c).   No loop is required although in some cases
+  // we might discard a higher "now" value in deference to a slightly lower but freshly
+  // installed obsv value.   That's entirely benign -- it admits no new orderings compared
+  // to (a) or (b) -- and greatly reduces coherence traffic.
+  // We might also condition (c) on the magnitude of the delta between obsv and now.
+  // Avoiding excessive CAS operations to hot RW locations is critical.
+  // See https://blogs.oracle.com/dave/entry/cas_and_cache_trivia_invalidate
+  return (prev == obsv) ? now : obsv;
+}
+
+// Time since start-up in seconds to a fine granularity.
+// Used by VMSelfDestructTimer and the MemProfiler.
+double os::elapsedTime() {
+  return (double)(getTimeNanos() - first_hrtime) / (double)hrtime_hz;
+}
+
+jlong os::elapsed_counter() {
+  return (jlong)(getTimeNanos() - first_hrtime);
+}
+
+jlong os::elapsed_frequency() {
+  return hrtime_hz;
+}
+
+// Return the real, user, and system times in seconds from an
+// arbitrary fixed point in the past.
+bool os::getTimesSecs(double* process_real_time,
+                      double* process_user_time,
+                      double* process_system_time) {
+  struct tms ticks;
+  clock_t real_ticks = times(&ticks);
+
+  if (real_ticks == (clock_t) (-1)) {
+    return false;
+  } else {
+    double ticks_per_second = (double) clock_tics_per_sec;
+    *process_user_time = ((double) ticks.tms_utime) / ticks_per_second;
+    *process_system_time = ((double) ticks.tms_stime) / ticks_per_second;
+    // For consistency return the real time from getTimeNanos()
+    // converted to seconds.
+    *process_real_time = ((double) getTimeNanos()) / ((double) NANOUNITS);
+
+    return true;
+  }
+}
+
+bool os::supports_vtime() { return true; }
+
+double os::elapsedVTime() {
+  return (double)gethrvtime() / (double)hrtime_hz;
+}
+
+char * os::local_time_string(char *buf, size_t buflen) {
+  struct tm t;
+  time_t long_time;
+  time(&long_time);
+  localtime_r(&long_time, &t);
+  jio_snprintf(buf, buflen, "%d-%02d-%02d %02d:%02d:%02d",
+               t.tm_year + 1900, t.tm_mon + 1, t.tm_mday,
+               t.tm_hour, t.tm_min, t.tm_sec);
+  return buf;
+}
+
+// DLL functions
+
+const char* os::dll_file_extension() { return ".so"; }
+
+// This must be hard coded because it's the system's temporary
+// directory not the java application's temp directory, ala java.io.tmpdir.
+const char* os::get_temp_directory() { return "/tmp"; }
+
+// check if addr is inside libjvm.so
+bool os::address_is_in_vm(address addr) {
+  static address libjvm_base_addr;
+  Dl_info dlinfo;
+
+  if (libjvm_base_addr == NULL) {
+    if (dladdr(CAST_FROM_FN_PTR(void *, os::address_is_in_vm), &dlinfo) != 0) {
+      libjvm_base_addr = (address)dlinfo.dli_fbase;
+    }
+    assert(libjvm_base_addr !=NULL, "Cannot obtain base address for libjvm");
+  }
+
+  if (dladdr((void *)addr, &dlinfo) != 0) {
+    if (libjvm_base_addr == (address)dlinfo.dli_fbase) return true;
+  }
+
+  return false;
+}
+
+typedef int (*dladdr1_func_type)(void *, Dl_info *, void **, int);
+static dladdr1_func_type dladdr1_func = NULL;
+
+bool os::dll_address_to_function_name(address addr, char *buf,
+                                      int buflen, int * offset,
+                                      bool demangle) {
+  // buf is not optional, but offset is optional
+  assert(buf != NULL, "sanity check");
+
+  Dl_info dlinfo;
+
+  // dladdr1_func was initialized in os::init()
+  if (dladdr1_func != NULL) {
+    // yes, we have dladdr1
+
+    // Support for dladdr1 is checked at runtime; it may be
+    // available even if the vm is built on a machine that does
+    // not have dladdr1 support.  Make sure there is a value for
+    // RTLD_DL_SYMENT.
+#ifndef RTLD_DL_SYMENT
+  #define RTLD_DL_SYMENT 1
+#endif
+#ifdef _LP64
+    Elf64_Sym * info;
+#else
+    Elf32_Sym * info;
+#endif
+    if (dladdr1_func((void *)addr, &dlinfo, (void **)&info,
+                     RTLD_DL_SYMENT) != 0) {
+      // see if we have a matching symbol that covers our address
+      if (dlinfo.dli_saddr != NULL &&
+          (char *)dlinfo.dli_saddr + info->st_size > (char *)addr) {
+        if (dlinfo.dli_sname != NULL) {
+          if (!(demangle && Decoder::demangle(dlinfo.dli_sname, buf, buflen))) {
+            jio_snprintf(buf, buflen, "%s", dlinfo.dli_sname);
+          }
+          if (offset != NULL) *offset = addr - (address)dlinfo.dli_saddr;
+          return true;
+        }
+      }
+      // no matching symbol so try for just file info
+      if (dlinfo.dli_fname != NULL && dlinfo.dli_fbase != NULL) {
+        if (Decoder::decode((address)(addr - (address)dlinfo.dli_fbase),
+                            buf, buflen, offset, dlinfo.dli_fname, demangle)) {
+          return true;
+        }
+      }
+    }
+    buf[0] = '\0';
+    if (offset != NULL) *offset  = -1;
+    return false;
+  }
+
+  // no, only dladdr is available
+  if (dladdr((void *)addr, &dlinfo) != 0) {
+    // see if we have a matching symbol
+    if (dlinfo.dli_saddr != NULL && dlinfo.dli_sname != NULL) {
+      if (!(demangle && Decoder::demangle(dlinfo.dli_sname, buf, buflen))) {
+        jio_snprintf(buf, buflen, dlinfo.dli_sname);
+      }
+      if (offset != NULL) *offset = addr - (address)dlinfo.dli_saddr;
+      return true;
+    }
+    // no matching symbol so try for just file info
+    if (dlinfo.dli_fname != NULL && dlinfo.dli_fbase != NULL) {
+      if (Decoder::decode((address)(addr - (address)dlinfo.dli_fbase),
+                          buf, buflen, offset, dlinfo.dli_fname, demangle)) {
+        return true;
+      }
+    }
+  }
+  buf[0] = '\0';
+  if (offset != NULL) *offset  = -1;
+  return false;
+}
+
+bool os::dll_address_to_library_name(address addr, char* buf,
+                                     int buflen, int* offset) {
+  // buf is not optional, but offset is optional
+  assert(buf != NULL, "sanity check");
+
+  Dl_info dlinfo;
+
+  if (dladdr((void*)addr, &dlinfo) != 0) {
+    if (dlinfo.dli_fname != NULL) {
+      jio_snprintf(buf, buflen, "%s", dlinfo.dli_fname);
+    }
+    if (dlinfo.dli_fbase != NULL && offset != NULL) {
+      *offset = addr - (address)dlinfo.dli_fbase;
+    }
+    return true;
+  }
+
+  buf[0] = '\0';
+  if (offset) *offset = -1;
+  return false;
+}
+
+int os::get_loaded_modules_info(os::LoadedModulesCallbackFunc callback, void *param) {
+  Dl_info dli;
+  // Sanity check?
+  if (dladdr(CAST_FROM_FN_PTR(void *, os::get_loaded_modules_info), &dli) == 0 ||
+      dli.dli_fname == NULL) {
+    return 1;
+  }
+
+  void * handle = dlopen(dli.dli_fname, RTLD_LAZY);
+  if (handle == NULL) {
+    return 1;
+  }
+
+  Link_map *map;
+  dlinfo(handle, RTLD_DI_LINKMAP, &map);
+  if (map == NULL) {
+    dlclose(handle);
+    return 1;
+  }
+
+  while (map->l_prev != NULL) {
+    map = map->l_prev;
+  }
+
+  while (map != NULL) {
+    // Iterate through all map entries and call callback with fields of interest
+    if(callback(map->l_name, (address)map->l_addr, (address)0, param)) {
+      dlclose(handle);
+      return 1;
+    }
+    map = map->l_next;
+  }
+
+  dlclose(handle);
+  return 0;
+}
+
+int _print_dll_info_cb(const char * name, address base_address, address top_address, void * param) {
+  outputStream * out = (outputStream *) param;
+  out->print_cr(INTPTR_FORMAT " \t%s", (intptr_t)base_address, name);
+  return 0;
+}
+
+void os::print_dll_info(outputStream * st) {
+  st->print_cr("Dynamic libraries:"); st->flush();
+  if (get_loaded_modules_info(_print_dll_info_cb, (void *)st)) {
+    st->print_cr("Error: Cannot print dynamic libraries.");
+  }
+}
+
+static void change_endianness(Elf32_Half& val) {
+  unsigned char *ptr = (unsigned char *)&val;
+  unsigned char swp = ptr[0];
+  ptr[0] = ptr[1];
+  ptr[1] = swp;
+}
+
+// Loads .dll/.so and
+// in case of error it checks if .dll/.so was built for the
+// same architecture as Hotspot is running on
+
+void * os::dll_load(const char *filename, char *ebuf, int ebuflen) {
+  log_info(os)("attempting shared library load of %s", filename);
+
+  void * result= ::dlopen(filename, RTLD_LAZY);
+  if (result != NULL) {
+    // Successful loading
+    Events::log(NULL, "Loaded shared library %s", filename);
+    log_info(os)("shared library load of %s was successful", filename);
+    return result;
+  }
+
+  Elf32_Ehdr elf_head;
+  const char* error_report = ::dlerror();
+  if (error_report == NULL) {
+    error_report = "dlerror returned no error description";
+  }
+  if (ebuf != NULL && ebuflen > 0) {
+    ::strncpy(ebuf, error_report, ebuflen-1);
+    ebuf[ebuflen-1]='\0';
+  }
+
+  Events::log(NULL, "Loading shared library %s failed, %s", filename, error_report);
+  log_info(os)("shared library load of %s failed, %s", filename, error_report);
+
+  int diag_msg_max_length=ebuflen-strlen(ebuf);
+  char* diag_msg_buf=ebuf+strlen(ebuf);
+
+  if (diag_msg_max_length==0) {
+    // No more space in ebuf for additional diagnostics message
+    return NULL;
+  }
+
+
+  int file_descriptor= ::open(filename, O_RDONLY | O_NONBLOCK);
+
+  if (file_descriptor < 0) {
+    // Can't open library, report dlerror() message
+    return NULL;
+  }
+
+  bool failed_to_read_elf_head=
+    (sizeof(elf_head)!=
+     (::read(file_descriptor, &elf_head,sizeof(elf_head))));
+
+  ::close(file_descriptor);
+  if (failed_to_read_elf_head) {
+    // file i/o error - report dlerror() msg
+    return NULL;
+  }
+
+  if (elf_head.e_ident[EI_DATA] != LITTLE_ENDIAN_ONLY(ELFDATA2LSB) BIG_ENDIAN_ONLY(ELFDATA2MSB)) {
+    // handle invalid/out of range endianness values
+    if (elf_head.e_ident[EI_DATA] == 0 || elf_head.e_ident[EI_DATA] > 2) {
+      return NULL;
+    }
+    change_endianness(elf_head.e_machine);
+  }
+
+  typedef struct {
+    Elf32_Half    code;         // Actual value as defined in elf.h
+    Elf32_Half    compat_class; // Compatibility of archs at VM's sense
+    unsigned char elf_class;    // 32 or 64 bit
+    unsigned char endianess;    // MSB or LSB
+    char*         name;         // String representation
+  } arch_t;
+
+  static const arch_t arch_array[]={
+    {EM_386,         EM_386,     ELFCLASS32, ELFDATA2LSB, (char*)"IA 32"},
+    {EM_486,         EM_386,     ELFCLASS32, ELFDATA2LSB, (char*)"IA 32"},
+    {EM_IA_64,       EM_IA_64,   ELFCLASS64, ELFDATA2LSB, (char*)"IA 64"},
+    {EM_X86_64,      EM_X86_64,  ELFCLASS64, ELFDATA2LSB, (char*)"AMD 64"},
+    {EM_SPARC,       EM_SPARC,   ELFCLASS32, ELFDATA2MSB, (char*)"Sparc 32"},
+    {EM_SPARC32PLUS, EM_SPARC,   ELFCLASS32, ELFDATA2MSB, (char*)"Sparc 32"},
+    {EM_SPARCV9,     EM_SPARCV9, ELFCLASS64, ELFDATA2MSB, (char*)"Sparc v9 64"},
+    {EM_PPC,         EM_PPC,     ELFCLASS32, ELFDATA2MSB, (char*)"Power PC 32"},
+    {EM_PPC64,       EM_PPC64,   ELFCLASS64, ELFDATA2MSB, (char*)"Power PC 64"},
+    {EM_ARM,         EM_ARM,     ELFCLASS32, ELFDATA2LSB, (char*)"ARM"},
+    // we only support 64 bit z architecture
+    {EM_S390,        EM_S390,    ELFCLASS64, ELFDATA2MSB, (char*)"IBM System/390"},
+    {EM_AARCH64,     EM_AARCH64, ELFCLASS64, ELFDATA2LSB, (char*)"AARCH64"}
+  };
+
+#if  (defined IA32)
+  static  Elf32_Half running_arch_code=EM_386;
+#elif   (defined AMD64)
+  static  Elf32_Half running_arch_code=EM_X86_64;
+#elif  (defined IA64)
+  static  Elf32_Half running_arch_code=EM_IA_64;
+#elif  (defined __sparc) && (defined _LP64)
+  static  Elf32_Half running_arch_code=EM_SPARCV9;
+#elif  (defined __sparc) && (!defined _LP64)
+  static  Elf32_Half running_arch_code=EM_SPARC;
+#elif  (defined __powerpc64__)
+  static  Elf32_Half running_arch_code=EM_PPC64;
+#elif  (defined __powerpc__)
+  static  Elf32_Half running_arch_code=EM_PPC;
+#elif (defined ARM)
+  static  Elf32_Half running_arch_code=EM_ARM;
+#else
+  #error Method os::dll_load requires that one of following is defined:\
+       IA32, AMD64, IA64, __sparc, __powerpc__, ARM, ARM
+#endif
+
+  // Identify compatibility class for VM's architecture and library's architecture
+  // Obtain string descriptions for architectures
+
+  arch_t lib_arch={elf_head.e_machine,0,elf_head.e_ident[EI_CLASS], elf_head.e_ident[EI_DATA], NULL};
+  int running_arch_index=-1;
+
+  for (unsigned int i=0; i < ARRAY_SIZE(arch_array); i++) {
+    if (running_arch_code == arch_array[i].code) {
+      running_arch_index    = i;
+    }
+    if (lib_arch.code == arch_array[i].code) {
+      lib_arch.compat_class = arch_array[i].compat_class;
+      lib_arch.name         = arch_array[i].name;
+    }
+  }
+
+  assert(running_arch_index != -1,
+         "Didn't find running architecture code (running_arch_code) in arch_array");
+  if (running_arch_index == -1) {
+    // Even though running architecture detection failed
+    // we may still continue with reporting dlerror() message
+    return NULL;
+  }
+
+  if (lib_arch.compat_class != arch_array[running_arch_index].compat_class) {
+    if (lib_arch.name != NULL) {
+      ::snprintf(diag_msg_buf, diag_msg_max_length-1,
+                 " (Possible cause: can't load %s .so on a %s platform)",
+                 lib_arch.name, arch_array[running_arch_index].name);
+    } else {
+      ::snprintf(diag_msg_buf, diag_msg_max_length-1,
+                 " (Possible cause: can't load this .so (machine code=0x%x) on a %s platform)",
+                 lib_arch.code, arch_array[running_arch_index].name);
+    }
+    return NULL;
+  }
+
+  if (lib_arch.endianess != arch_array[running_arch_index].endianess) {
+    ::snprintf(diag_msg_buf, diag_msg_max_length-1," (Possible cause: endianness mismatch)");
+    return NULL;
+  }
+
+  // ELF file class/capacity : 0 - invalid, 1 - 32bit, 2 - 64bit
+  if (lib_arch.elf_class > 2 || lib_arch.elf_class < 1) {
+    ::snprintf(diag_msg_buf, diag_msg_max_length-1, " (Possible cause: invalid ELF file class)");
+    return NULL;
+  }
+
+  if (lib_arch.elf_class != arch_array[running_arch_index].elf_class) {
+    ::snprintf(diag_msg_buf, diag_msg_max_length-1,
+               " (Possible cause: architecture word width mismatch, can't load %d-bit .so on a %d-bit platform)",
+               (int) lib_arch.elf_class * 32, arch_array[running_arch_index].elf_class * 32);
+    return NULL;
+  }
+
+  return NULL;
+}
+
+static inline time_t get_mtime(const char* filename) {
+  struct stat st;
+  int ret = os::stat(filename, &st);
+  assert(ret == 0, "failed to stat() file '%s': %s", filename, os::strerror(errno));
+  return st.st_mtime;
+}
+
+int os::compare_file_modified_times(const char* file1, const char* file2) {
+  time_t t1 = get_mtime(file1);
+  time_t t2 = get_mtime(file2);
+  return t1 - t2;
+}
+
+static bool _print_ascii_file(const char* filename, outputStream* st) {
+  int fd = ::open(filename, O_RDONLY);
+  if (fd == -1) {
+    return false;
+  }
+
+  char buf[32];
+  int bytes;
+  while ((bytes = ::read(fd, buf, sizeof(buf))) > 0) {
+    st->print_raw(buf, bytes);
+  }
+
+  ::close(fd);
+
+  return true;
+}
+
+void os::print_os_info_brief(outputStream* st) {
+  os::Solaris::print_distro_info(st);
+
+  os::Posix::print_uname_info(st);
+
+  os::Solaris::print_libversion_info(st);
+}
+
+void os::print_os_info(outputStream* st) {
+  st->print("OS:");
+
+  os::Solaris::print_distro_info(st);
+
+  os::Posix::print_uname_info(st);
+
+  os::Posix::print_uptime_info(st);
+
+  os::Solaris::print_libversion_info(st);
+
+  os::Posix::print_rlimit_info(st);
+
+  os::Posix::print_load_average(st);
+}
+
+void os::Solaris::print_distro_info(outputStream* st) {
+  if (!_print_ascii_file("/etc/release", st)) {
+    st->print("Solaris");
+  }
+  st->cr();
+}
+
+void os::get_summary_os_info(char* buf, size_t buflen) {
+  strncpy(buf, "Solaris", buflen);  // default to plain solaris
+  FILE* fp = fopen("/etc/release", "r");
+  if (fp != NULL) {
+    char tmp[256];
+    // Only get the first line and chop out everything but the os name.
+    if (fgets(tmp, sizeof(tmp), fp)) {
+      char* ptr = tmp;
+      // skip past whitespace characters
+      while (*ptr != '\0' && (*ptr == ' ' || *ptr == '\t' || *ptr == '\n')) ptr++;
+      if (*ptr != '\0') {
+        char* nl = strchr(ptr, '\n');
+        if (nl != NULL) *nl = '\0';
+        strncpy(buf, ptr, buflen);
+      }
+    }
+    fclose(fp);
+  }
+}
+
+void os::Solaris::print_libversion_info(outputStream* st) {
+  st->print("  (T2 libthread)");
+  st->cr();
+}
+
+static bool check_addr0(outputStream* st) {
+  jboolean status = false;
+  const int read_chunk = 200;
+  int ret = 0;
+  int nmap = 0;
+  int fd = ::open("/proc/self/map",O_RDONLY);
+  if (fd >= 0) {
+    prmap_t *p = NULL;
+    char *mbuff = (char *) calloc(read_chunk, sizeof(prmap_t));
+    if (NULL == mbuff) {
+      ::close(fd);
+      return status;
+    }
+    while ((ret = ::read(fd, mbuff, read_chunk*sizeof(prmap_t))) > 0) {
+      //check if read() has not read partial data
+      if( 0 != ret % sizeof(prmap_t)){
+        break;
+      }
+      nmap = ret / sizeof(prmap_t);
+      p = (prmap_t *)mbuff;
+      for(int i = 0; i < nmap; i++){
+        if (p->pr_vaddr == 0x0) {
+          st->print("Warning: Address: " PTR_FORMAT ", Size: " SIZE_FORMAT "K, ",p->pr_vaddr, p->pr_size/1024);
+          st->print("Mapped file: %s, ", p->pr_mapname[0] == '\0' ? "None" : p->pr_mapname);
+          st->print("Access: ");
+          st->print("%s",(p->pr_mflags & MA_READ)  ? "r" : "-");
+          st->print("%s",(p->pr_mflags & MA_WRITE) ? "w" : "-");
+          st->print("%s",(p->pr_mflags & MA_EXEC)  ? "x" : "-");
+          st->cr();
+          status = true;
+        }
+        p++;
+      }
+    }
+    free(mbuff);
+    ::close(fd);
+  }
+  return status;
+}
+
+void os::get_summary_cpu_info(char* buf, size_t buflen) {
+  // Get MHz with system call. We don't seem to already have this.
+  processor_info_t stats;
+  processorid_t id = getcpuid();
+  int clock = 0;
+  if (processor_info(id, &stats) != -1) {
+    clock = stats.pi_clock;  // pi_processor_type isn't more informative than below
+  }
+#ifdef AMD64
+  snprintf(buf, buflen, "x86 64 bit %d MHz", clock);
+#else
+  snprintf(buf, buflen, "Unknown 64 bit %d MHz", clock);
+#endif
+}
+
+void os::pd_print_cpu_info(outputStream* st, char* buf, size_t buflen) {
+  // Nothing to do for now.
+}
+
+void os::print_memory_info(outputStream* st) {
+  st->print("Memory:");
+  st->print(" %dk page", os::vm_page_size()>>10);
+  st->print(", physical " UINT64_FORMAT "k", os::physical_memory()>>10);
+  st->print("(" UINT64_FORMAT "k free)", os::available_memory() >> 10);
+  st->cr();
+  (void) check_addr0(st);
+}
+
+static int Maxsignum = 0;
+
+static char saved_jvm_path[MAXPATHLEN] = { 0 };
+
+// Find the full path to the current module, libjvm.so
+void os::jvm_path(char *buf, jint buflen) {
+  // Error checking.
+  if (buflen < MAXPATHLEN) {
+    assert(false, "must use a large-enough buffer");
+    buf[0] = '\0';
+    return;
+  }
+  // Lazy resolve the path to current module.
+  if (saved_jvm_path[0] != 0) {
+    strcpy(buf, saved_jvm_path);
+    return;
+  }
+
+  Dl_info dlinfo;
+  int ret = dladdr(CAST_FROM_FN_PTR(void *, os::jvm_path), &dlinfo);
+  assert(ret != 0, "cannot locate libjvm");
+  if (ret != 0 && dlinfo.dli_fname != NULL) {
+    if (os::Posix::realpath((char *)dlinfo.dli_fname, buf, buflen) == NULL) {
+      return;
+    }
+  } else {
+    buf[0] = '\0';
+    return;
+  }
+
+  if (Arguments::sun_java_launcher_is_altjvm()) {
+    // Support for the java launcher's '-XXaltjvm=<path>' option. Typical
+    // value for buf is "<JAVA_HOME>/jre/lib/<arch>/<vmtype>/libjvm.so".
+    // If "/jre/lib/" appears at the right place in the string, then
+    // assume we are installed in a JDK and we're done.  Otherwise, check
+    // for a JAVA_HOME environment variable and fix up the path so it
+    // looks like libjvm.so is installed there (append a fake suffix
+    // hotspot/libjvm.so).
+    const char *p = buf + strlen(buf) - 1;
+    for (int count = 0; p > buf && count < 5; ++count) {
+      for (--p; p > buf && *p != '/'; --p)
+        /* empty */ ;
+    }
+
+    if (strncmp(p, "/jre/lib/", 9) != 0) {
+      // Look for JAVA_HOME in the environment.
+      char* java_home_var = ::getenv("JAVA_HOME");
+      if (java_home_var != NULL && java_home_var[0] != 0) {
+        char* jrelib_p;
+        int   len;
+
+        // Check the current module name "libjvm.so".
+        p = strrchr(buf, '/');
+        assert(strstr(p, "/libjvm") == p, "invalid library name");
+
+        if (os::Posix::realpath(java_home_var, buf, buflen) == NULL) {
+          return;
+        }
+        // determine if this is a legacy image or modules image
+        // modules image doesn't have "jre" subdirectory
+        len = strlen(buf);
+        assert(len < buflen, "Ran out of buffer space");
+        jrelib_p = buf + len;
+        snprintf(jrelib_p, buflen-len, "/jre/lib");
+        if (0 != access(buf, F_OK)) {
+          snprintf(jrelib_p, buflen-len, "/lib");
+        }
+
+        if (0 == access(buf, F_OK)) {
+          // Use current module name "libjvm.so"
+          len = strlen(buf);
+          snprintf(buf + len, buflen-len, "/hotspot/libjvm.so");
+        } else {
+          // Go back to path of .so
+          if (os::Posix::realpath((char *)dlinfo.dli_fname, buf, buflen) == NULL) {
+            return;
+          }
+        }
+      }
+    }
+  }
+
+  strncpy(saved_jvm_path, buf, MAXPATHLEN);
+  saved_jvm_path[MAXPATHLEN - 1] = '\0';
+}
+
+
+void os::print_jni_name_prefix_on(outputStream* st, int args_size) {
+  // no prefix required, not even "_"
+}
+
+
+void os::print_jni_name_suffix_on(outputStream* st, int args_size) {
+  // no suffix required
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Virtual Memory
+
+int os::vm_page_size() {
+  assert(os::Solaris::page_size() != -1, "must call os::init");
+  return os::Solaris::page_size();
+}
+
+// Solaris allocates memory by pages.
+int os::vm_allocation_granularity() {
+  assert(os::Solaris::page_size() != -1, "must call os::init");
+  return os::Solaris::page_size();
+}
+
+static bool recoverable_mmap_error(int err) {
+  // See if the error is one we can let the caller handle. This
+  // list of errno values comes from the Solaris mmap(2) man page.
+  switch (err) {
+  case EBADF:
+  case EINVAL:
+  case ENOTSUP:
+    // let the caller deal with these errors
+    return true;
+
+  default:
+    // Any remaining errors on this OS can cause our reserved mapping
+    // to be lost. That can cause confusion where different data
+    // structures think they have the same memory mapped. The worst
+    // scenario is if both the VM and a library think they have the
+    // same memory mapped.
+    return false;
+  }
+}
+
+static void warn_fail_commit_memory(char* addr, size_t bytes, bool exec,
+                                    int err) {
+  warning("INFO: os::commit_memory(" PTR_FORMAT ", " SIZE_FORMAT
+          ", %d) failed; error='%s' (errno=%d)", p2i(addr), bytes, exec,
+          os::strerror(err), err);
+}
+
+static void warn_fail_commit_memory(char* addr, size_t bytes,
+                                    size_t alignment_hint, bool exec,
+                                    int err) {
+  warning("INFO: os::commit_memory(" PTR_FORMAT ", " SIZE_FORMAT
+          ", " SIZE_FORMAT ", %d) failed; error='%s' (errno=%d)", p2i(addr),
+          bytes, alignment_hint, exec, os::strerror(err), err);
+}
+
+int os::Solaris::commit_memory_impl(char* addr, size_t bytes, bool exec) {
+  int prot = exec ? PROT_READ|PROT_WRITE|PROT_EXEC : PROT_READ|PROT_WRITE;
+  size_t size = bytes;
+  char *res = Solaris::mmap_chunk(addr, size, MAP_PRIVATE|MAP_FIXED, prot);
+  if (res != NULL) {
+    if (UseNUMAInterleaving) {
+        numa_make_global(addr, bytes);
+    }
+    return 0;
+  }
+
+  int err = errno;  // save errno from mmap() call in mmap_chunk()
+
+  if (!recoverable_mmap_error(err)) {
+    warn_fail_commit_memory(addr, bytes, exec, err);
+    vm_exit_out_of_memory(bytes, OOM_MMAP_ERROR, "committing reserved memory.");
+  }
+
+  return err;
+}
+
+bool os::pd_commit_memory(char* addr, size_t bytes, bool exec) {
+  return Solaris::commit_memory_impl(addr, bytes, exec) == 0;
+}
+
+void os::pd_commit_memory_or_exit(char* addr, size_t bytes, bool exec,
+                                  const char* mesg) {
+  assert(mesg != NULL, "mesg must be specified");
+  int err = os::Solaris::commit_memory_impl(addr, bytes, exec);
+  if (err != 0) {
+    // the caller wants all commit errors to exit with the specified mesg:
+    warn_fail_commit_memory(addr, bytes, exec, err);
+    vm_exit_out_of_memory(bytes, OOM_MMAP_ERROR, "%s", mesg);
+  }
+}
+
+size_t os::Solaris::page_size_for_alignment(size_t alignment) {
+  assert(is_aligned(alignment, (size_t) vm_page_size()),
+         SIZE_FORMAT " is not aligned to " SIZE_FORMAT,
+         alignment, (size_t) vm_page_size());
+
+  const int page_sizes_max = 9;
+  size_t _illumos_page_sizes[page_sizes_max];
+  int n = getpagesizes(_illumos_page_sizes, page_sizes_max);
+  for (int i = 0; _illumos_page_sizes[i] != 0; i++) {
+    if (is_aligned(alignment, _illumos_page_sizes[i])) {
+      return _illumos_page_sizes[i];
+    }
+  }
+
+  return (size_t) vm_page_size();
+}
+
+int os::Solaris::commit_memory_impl(char* addr, size_t bytes,
+                                    size_t alignment_hint, bool exec) {
+  int err = Solaris::commit_memory_impl(addr, bytes, exec);
+  if (err == 0 && UseLargePages && alignment_hint > 0) {
+    assert(is_aligned(bytes, alignment_hint),
+           SIZE_FORMAT " is not aligned to " SIZE_FORMAT, bytes, alignment_hint);
+
+    // The syscall memcntl requires an exact page size (see man memcntl for details).
+    size_t page_size = page_size_for_alignment(alignment_hint);
+    if (page_size > (size_t) vm_page_size()) {
+      (void)Solaris::setup_large_pages(addr, bytes, page_size);
+    }
+  }
+  return err;
+}
+
+bool os::pd_commit_memory(char* addr, size_t bytes, size_t alignment_hint,
+                          bool exec) {
+  return Solaris::commit_memory_impl(addr, bytes, alignment_hint, exec) == 0;
+}
+
+void os::pd_commit_memory_or_exit(char* addr, size_t bytes,
+                                  size_t alignment_hint, bool exec,
+                                  const char* mesg) {
+  assert(mesg != NULL, "mesg must be specified");
+  int err = os::Solaris::commit_memory_impl(addr, bytes, alignment_hint, exec);
+  if (err != 0) {
+    // the caller wants all commit errors to exit with the specified mesg:
+    warn_fail_commit_memory(addr, bytes, alignment_hint, exec, err);
+    vm_exit_out_of_memory(bytes, OOM_MMAP_ERROR, "%s", mesg);
+  }
+}
+
+// Uncommit the pages in a specified region.
+void os::pd_free_memory(char* addr, size_t bytes, size_t alignment_hint) {
+  if (posix_madvise(addr, bytes, MADV_FREE) < 0) {
+    debug_only(warning("MADV_FREE failed."));
+    return;
+  }
+}
+
+bool os::pd_create_stack_guard_pages(char* addr, size_t size) {
+  return os::commit_memory(addr, size, !ExecMem);
+}
+
+bool os::remove_stack_guard_pages(char* addr, size_t size) {
+  return os::uncommit_memory(addr, size);
+}
+
+// Change the page size in a given range.
+void os::pd_realign_memory(char *addr, size_t bytes, size_t alignment_hint) {
+  assert((intptr_t)addr % alignment_hint == 0, "Address should be aligned.");
+  assert((intptr_t)(addr + bytes) % alignment_hint == 0, "End should be aligned.");
+  if (UseLargePages) {
+    size_t page_size = Solaris::page_size_for_alignment(alignment_hint);
+    if (page_size > (size_t) vm_page_size()) {
+      Solaris::setup_large_pages(addr, bytes, page_size);
+    }
+  }
+}
+
+// Tell the OS to make the range local to the first-touching LWP
+void os::numa_make_local(char *addr, size_t bytes, int lgrp_hint) {
+  assert((intptr_t)addr % os::vm_page_size() == 0, "Address should be page-aligned.");
+  if (posix_madvise(addr, bytes, MADV_ACCESS_LWP) < 0) {
+    debug_only(warning("MADV_ACCESS_LWP failed."));
+  }
+}
+
+// Tell the OS that this range would be accessed from different LWPs.
+void os::numa_make_global(char *addr, size_t bytes) {
+  assert((intptr_t)addr % os::vm_page_size() == 0, "Address should be page-aligned.");
+  if (posix_madvise(addr, bytes, MADV_ACCESS_MANY) < 0) {
+    debug_only(warning("MADV_ACCESS_MANY failed."));
+  }
+}
+
+// Get the number of the locality groups.
+size_t os::numa_get_groups_num() {
+  size_t n = Solaris::lgrp_nlgrps(Solaris::lgrp_cookie());
+  return n != -1 ? n : 1;
+}
+
+// Get a list of leaf locality groups. A leaf lgroup is group that
+// doesn't have any children. Typical leaf group is a CPU or a CPU/memory
+// board. An LWP is assigned to one of these groups upon creation.
+size_t os::numa_get_leaf_groups(int *ids, size_t size) {
+  if ((ids[0] = Solaris::lgrp_root(Solaris::lgrp_cookie())) == -1) {
+    ids[0] = 0;
+    return 1;
+  }
+  int result_size = 0, top = 1, bottom = 0, cur = 0;
+  for (unsigned int k = 0; k < size; k++) {
+    int r = Solaris::lgrp_children(Solaris::lgrp_cookie(), ids[cur],
+                                   (Solaris::lgrp_id_t*)&ids[top], size - top);
+    if (r == -1) {
+      ids[0] = 0;
+      return 1;
+    }
+    if (!r) {
+      // That's a leaf node.
+      assert(bottom <= cur, "Sanity check");
+      // Check if the node has memory
+      if (Solaris::lgrp_resources(Solaris::lgrp_cookie(), ids[cur],
+                                  NULL, 0, LGRP_RSRC_MEM) > 0) {
+        ids[bottom++] = ids[cur];
+      }
+    }
+    top += r;
+    cur++;
+  }
+  if (bottom == 0) {
+    // Handle a situation, when the OS reports no memory available.
+    // Assume UMA architecture.
+    ids[0] = 0;
+    return 1;
+  }
+  return bottom;
+}
+
+// Detect the topology change. Typically happens during CPU plugging-unplugging.
+bool os::numa_topology_changed() {
+  int is_stale = Solaris::lgrp_cookie_stale(Solaris::lgrp_cookie());
+  if (is_stale != -1 && is_stale) {
+    Solaris::lgrp_fini(Solaris::lgrp_cookie());
+    Solaris::lgrp_cookie_t c = Solaris::lgrp_init(Solaris::LGRP_VIEW_CALLER);
+    assert(c != 0, "Failure to initialize LGRP API");
+    Solaris::set_lgrp_cookie(c);
+    return true;
+  }
+  return false;
+}
+
+// Get the group id of the current LWP.
+int os::numa_get_group_id() {
+  int lgrp_id = Solaris::lgrp_home(P_LWPID, P_MYID);
+  if (lgrp_id == -1) {
+    return 0;
+  }
+  const int size = os::numa_get_groups_num();
+  int *ids = (int*)alloca(size * sizeof(int));
+
+  // Get the ids of all lgroups with memory; r is the count.
+  int r = Solaris::lgrp_resources(Solaris::lgrp_cookie(), lgrp_id,
+                                  (Solaris::lgrp_id_t*)ids, size, LGRP_RSRC_MEM);
+  if (r <= 0) {
+    return 0;
+  }
+  return ids[os::random() % r];
+}
+
+int os::numa_get_group_id_for_address(const void* address) {
+  return 0;
+}
+
+// Request information about the page.
+bool os::get_page_info(char *start, page_info* info) {
+  const uint_t info_types[] = { MEMINFO_VLGRP, MEMINFO_VPAGESIZE };
+  uint64_t addr = (uintptr_t)start;
+  uint64_t outdata[2];
+  uint_t validity = 0;
+
+  if (meminfo(&addr, 1, info_types, 2, outdata, &validity) < 0) {
+    return false;
+  }
+
+  info->size = 0;
+  info->lgrp_id = -1;
+
+  if ((validity & 1) != 0) {
+    if ((validity & 2) != 0) {
+      info->lgrp_id = outdata[0];
+    }
+    if ((validity & 4) != 0) {
+      info->size = outdata[1];
+    }
+    return true;
+  }
+  return false;
+}
+
+// Scan the pages from start to end until a page different than
+// the one described in the info parameter is encountered.
+char *os::scan_pages(char *start, char* end, page_info* page_expected,
+                     page_info* page_found) {
+  const uint_t info_types[] = { MEMINFO_VLGRP, MEMINFO_VPAGESIZE };
+  const size_t types = sizeof(info_types) / sizeof(info_types[0]);
+  uint64_t addrs[MAX_MEMINFO_CNT], outdata[types * MAX_MEMINFO_CNT + 1];
+  uint_t validity[MAX_MEMINFO_CNT];
+
+  size_t page_size = MAX2((size_t)os::vm_page_size(), page_expected->size);
+  uint64_t p = (uint64_t)start;
+  while (p < (uint64_t)end) {
+    addrs[0] = p;
+    size_t addrs_count = 1;
+    while (addrs_count < MAX_MEMINFO_CNT && addrs[addrs_count - 1] + page_size < (uint64_t)end) {
+      addrs[addrs_count] = addrs[addrs_count - 1] + page_size;
+      addrs_count++;
+    }
+
+    if (meminfo(addrs, addrs_count, info_types, types, outdata, validity) < 0) {
+      return NULL;
+    }
+
+    size_t i = 0;
+    for (; i < addrs_count; i++) {
+      if ((validity[i] & 1) != 0) {
+        if ((validity[i] & 4) != 0) {
+          if (outdata[types * i + 1] != page_expected->size) {
+            break;
+          }
+        } else if (page_expected->size != 0) {
+          break;
+        }
+
+        if ((validity[i] & 2) != 0 && page_expected->lgrp_id > 0) {
+          if (outdata[types * i] != page_expected->lgrp_id) {
+            break;
+          }
+        }
+      } else {
+        return NULL;
+      }
+    }
+
+    if (i < addrs_count) {
+      if ((validity[i] & 2) != 0) {
+        page_found->lgrp_id = outdata[types * i];
+      } else {
+        page_found->lgrp_id = -1;
+      }
+      if ((validity[i] & 4) != 0) {
+        page_found->size = outdata[types * i + 1];
+      } else {
+        page_found->size = 0;
+      }
+      return (char*)addrs[i];
+    }
+
+    p = addrs[addrs_count - 1] + page_size;
+  }
+  return end;
+}
+
+bool os::pd_uncommit_memory(char* addr, size_t bytes, bool exec) {
+  size_t size = bytes;
+  // Map uncommitted pages PROT_NONE so we fail early if we touch an
+  // uncommitted page. Otherwise, the read/write might succeed if we
+  // have enough swap space to back the physical page.
+  return
+    NULL != Solaris::mmap_chunk(addr, size,
+                                MAP_PRIVATE|MAP_FIXED|MAP_NORESERVE,
+                                PROT_NONE);
+}
+
+char* os::Solaris::mmap_chunk(char *addr, size_t size, int flags, int prot) {
+  char *b = (char *)mmap(addr, size, prot, flags, os::Solaris::_dev_zero_fd, 0);
+
+  if (b == MAP_FAILED) {
+    return NULL;
+  }
+  return b;
+}
+
+char* os::Solaris::anon_mmap(char* requested_addr, size_t bytes) {
+  char* addr = requested_addr;
+  int flags = MAP_PRIVATE | MAP_NORESERVE;
+
+  // Map uncommitted pages PROT_NONE so we fail early if we touch an
+  // uncommitted page. Otherwise, the read/write might succeed if we
+  // have enough swap space to back the physical page.
+  return mmap_chunk(addr, bytes, flags, PROT_NONE);
+}
+
+char* os::pd_reserve_memory(size_t bytes, bool exec) {
+  char* addr = Solaris::anon_mmap(NULL, bytes);
+
+  return addr;
+}
+
+char* os::pd_attempt_map_memory_to_file_at(char* requested_addr, size_t bytes, int file_desc) {
+  assert(file_desc >= 0, "file_desc is not valid");
+  char* result = pd_attempt_reserve_memory_at(requested_addr, bytes, !ExecMem);
+  if (result != NULL) {
+    if (replace_existing_mapping_with_file_mapping(result, bytes, file_desc) == NULL) {
+      vm_exit_during_initialization(err_msg("Error in mapping Java heap at the given filesystem directory"));
+    }
+  }
+  return result;
+}
+
+// Reserve memory at an arbitrary address, only if that area is
+// available (and not reserved for something else).
+
+char* os::pd_attempt_reserve_memory_at(char* requested_addr, size_t bytes, bool exec) {
+  // Assert only that the size is a multiple of the page size, since
+  // that's all that mmap requires, and since that's all we really know
+  // about at this low abstraction level.  If we need higher alignment,
+  // we can either pass an alignment to this method or verify alignment
+  // in one of the methods further up the call chain.  See bug 5044738.
+  assert(bytes % os::vm_page_size() == 0, "reserving unexpected size block");
+
+  // Since snv_84, Solaris attempts to honor the address hint - see 5003415.
+  char* addr = Solaris::anon_mmap(requested_addr, bytes);
+
+  volatile int err = errno;
+  if (addr == requested_addr) {
+    return addr;
+  }
+
+  if (addr != NULL) {
+    pd_unmap_memory(addr, bytes);
+  }
+
+  return NULL;
+}
+
+bool os::pd_release_memory(char* addr, size_t bytes) {
+  size_t size = bytes;
+  return munmap(addr, size) == 0;
+}
+
+static bool solaris_mprotect(char* addr, size_t bytes, int prot) {
+  assert(addr == (char*)align_down((uintptr_t)addr, os::vm_page_size()),
+         "addr must be page aligned");
+  Events::log(NULL, "Protecting memory [" INTPTR_FORMAT "," INTPTR_FORMAT "] with protection modes %x", p2i(addr), p2i(addr+bytes), prot);
+  int retVal = mprotect(addr, bytes, prot);
+  return retVal == 0;
+}
+
+// Protect memory (Used to pass readonly pages through
+// JNI GetArray<type>Elements with empty arrays.)
+// Also, used for serialization page and for compressed oops null pointer
+// checking.
+bool os::protect_memory(char* addr, size_t bytes, ProtType prot,
+                        bool is_committed) {
+  unsigned int p = 0;
+  switch (prot) {
+  case MEM_PROT_NONE: p = PROT_NONE; break;
+  case MEM_PROT_READ: p = PROT_READ; break;
+  case MEM_PROT_RW:   p = PROT_READ|PROT_WRITE; break;
+  case MEM_PROT_RWX:  p = PROT_READ|PROT_WRITE|PROT_EXEC; break;
+  default:
+    ShouldNotReachHere();
+  }
+  // is_committed is unused.
+  return solaris_mprotect(addr, bytes, p);
+}
+
+// guard_memory and unguard_memory only happens within stack guard pages.
+// Since ISM pertains only to the heap, guard and unguard memory should not
+/// happen with an ISM region.
+bool os::guard_memory(char* addr, size_t bytes) {
+  return solaris_mprotect(addr, bytes, PROT_NONE);
+}
+
+bool os::unguard_memory(char* addr, size_t bytes) {
+  return solaris_mprotect(addr, bytes, PROT_READ|PROT_WRITE);
+}
+
+// Large page support
+static size_t _large_page_size = 0;
+
+bool os::Solaris::mpss_sanity_check(bool warn, size_t* page_size) {
+  const unsigned int usable_count = VM_Version::page_size_count();
+  if (usable_count == 1) {
+    return false;
+  }
+
+  // Find the page sizes supported by the system
+  const int page_sizes_max = 9;
+  size_t _illumos_page_sizes[page_sizes_max];
+  int n = getpagesizes(_illumos_page_sizes, page_sizes_max);
+  assert(n > 0, "illumos bug?");
+
+  if (n == 1) return false;     // Only one page size available.
+
+  // Skip sizes larger than 4M (or LargePageSizeInBytes if it was set)
+  const size_t size_limit =
+    FLAG_IS_DEFAULT(LargePageSizeInBytes) ? 4 * M : LargePageSizeInBytes;
+  int beg;
+  for (beg = 0; beg < n; ++beg) {
+    if (_illumos_page_sizes[beg] <= size_limit) {
+      _page_sizes.add(_illumos_page_sizes[beg]);
+      if (_illumos_page_sizes[beg] > *page_size) {
+        *page_size = _illumos_page_sizes[beg];
+      }
+    }
+  }
+  // make sure we add the default
+  _page_sizes.add(vm_page_size());
+  return true;
+}
+
+void os::large_page_init() {
+  if (UseLargePages) {
+    // print a warning if any large page related flag is specified on command line
+    bool warn_on_failure = !FLAG_IS_DEFAULT(UseLargePages)        ||
+                           !FLAG_IS_DEFAULT(LargePageSizeInBytes);
+
+    UseLargePages = Solaris::mpss_sanity_check(warn_on_failure, &_large_page_size);
+  }
+}
+
+bool os::Solaris::is_valid_page_size(size_t bytes) {
+  return _page_sizes.contains(bytes);
+}
+
+bool os::Solaris::setup_large_pages(caddr_t start, size_t bytes, size_t align) {
+  assert(is_valid_page_size(align), SIZE_FORMAT " is not a valid page size", align);
+  assert(is_aligned((void*) start, align),
+         PTR_FORMAT " is not aligned to " SIZE_FORMAT, p2i((void*) start), align);
+  assert(is_aligned(bytes, align),
+         SIZE_FORMAT " is not aligned to " SIZE_FORMAT, bytes, align);
+
+  // Signal to OS that we want large pages for addresses
+  // from addr, addr + bytes
+  struct memcntl_mha mpss_struct;
+  mpss_struct.mha_cmd = MHA_MAPSIZE_VA;
+  mpss_struct.mha_pagesize = align;
+  mpss_struct.mha_flags = 0;
+  // Upon successful completion, memcntl() returns 0
+  if (memcntl(start, bytes, MC_HAT_ADVISE, (caddr_t) &mpss_struct, 0, 0)) {
+    debug_only(warning("Attempt to use MPSS failed."));
+    return false;
+  }
+  return true;
+}
+
+char* os::pd_reserve_memory_special(size_t size, size_t alignment, size_t page_size, char* addr, bool exec) {
+  fatal("os::reserve_memory_special should not be called on Solaris.");
+  return NULL;
+}
+
+bool os::pd_release_memory_special(char* base, size_t bytes) {
+  fatal("os::release_memory_special should not be called on Solaris.");
+  return false;
+}
+
+size_t os::large_page_size() {
+  return _large_page_size;
+}
+
+// MPSS allows application to commit large page memory on demand; with ISM
+// the entire memory region must be allocated as shared memory.
+bool os::can_commit_large_page_memory() {
+  return true;
+}
+
+bool os::can_execute_large_page_memory() {
+  return true;
+}
+
+// Used to convert frequent JVM_Yield() to nops
+bool os::dont_yield() {
+  if (DontYieldALot) {
+    static hrtime_t last_time = 0;
+    hrtime_t diff = getTimeNanos() - last_time;
+
+    if (diff < DontYieldALotInterval * 1000000) {
+      return true;
+    }
+
+    last_time += diff;
+
+    return false;
+  } else {
+    return false;
+  }
+}
+
+// Note that yield semantics are defined by the scheduling class to which
+// the thread currently belongs.  Typically, yield will _not yield to
+// other equal or higher priority threads that reside on the dispatch queues
+// of other CPUs.
+
+void os::naked_yield() {
+  thr_yield();
+}
+
+// Interface for setting lwp priorities.  We are using T2 libthread,
+// which forces the use of bound threads, so all of our threads will
+// be assigned to real lwp's.  Using the thr_setprio function is
+// meaningless in this mode so we must adjust the real lwp's priority.
+// The routines below implement the getting and setting of lwp priorities.
+//
+// Note: There are three priority scales used on Solaris.  Java priorities
+//       which range from 1 to 10, libthread "thr_setprio" scale which range
+//       from 0 to 127, and the current scheduling class of the process we
+//       are running in.  This is typically from -60 to +60.
+//       The setting of the lwp priorities is done after a call to thr_setprio
+//       so Java priorities are mapped to libthread priorities and we map from
+//       the latter to lwp priorities.  We don't keep priorities stored in
+//       Java priorities since some of our worker threads want to set priorities
+//       higher than all Java threads.
+//
+// For related information:
+// (1)  man -s 2 priocntl
+// (2)  man -s 4 priocntl
+// (3)  man dispadmin
+// =    librt.so
+// =    libthread/common/rtsched.c - thrp_setlwpprio().
+// =    ps -cL <pid> ... to validate priority.
+// =    sched_get_priority_min and _max
+//              pthread_create
+//              sched_setparam
+//              pthread_setschedparam
+//
+// Assumptions:
+// +    We assume that all threads in the process belong to the same
+//              scheduling class.   IE. a homogeneous process.
+// +    Must be root or in IA group to change change "interactive" attribute.
+//              Priocntl() will fail silently.  The only indication of failure is when
+//              we read-back the value and notice that it hasn't changed.
+// +    Interactive threads enter the runq at the head, non-interactive at the tail.
+// +    For RT, change timeslice as well.  Invariant:
+//              constant "priority integral"
+//              Konst == TimeSlice * (60-Priority)
+//              Given a priority, compute appropriate timeslice.
+// +    Higher numerical values have higher priority.
+
+// sched class attributes
+typedef struct {
+  int   schedPolicy;              // classID
+  int   maxPrio;
+  int   minPrio;
+} SchedInfo;
+
+
+static SchedInfo tsLimits, iaLimits, rtLimits, fxLimits;
+
+#ifdef ASSERT
+static int  ReadBackValidate = 1;
+#endif
+static int  myClass     = 0;
+static int  myMin       = 0;
+static int  myMax       = 0;
+static int  myCur       = 0;
+static bool priocntl_enable = false;
+
+static const int criticalPrio = FXCriticalPriority;
+static int java_MaxPriority_to_os_priority = 0; // Saved mapping
+
+
+// lwp_priocntl_init
+//
+// Try to determine the priority scale for our process.
+//
+// Return errno or 0 if OK.
+//
+static int lwp_priocntl_init() {
+  int rslt;
+  pcinfo_t ClassInfo;
+  pcparms_t ParmInfo;
+  int i;
+
+  if (!UseThreadPriorities) return 0;
+
+  // If ThreadPriorityPolicy is 1, switch tables
+  if (ThreadPriorityPolicy == 1) {
+    for (i = 0; i < CriticalPriority+1; i++)
+      os::java_to_os_priority[i] = prio_policy1[i];
+  }
+  if (UseCriticalJavaThreadPriority) {
+    // MaxPriority always maps to the FX scheduling class and criticalPrio.
+    // See set_native_priority() and set_lwp_class_and_priority().
+    // Save original MaxPriority mapping in case attempt to
+    // use critical priority fails.
+    java_MaxPriority_to_os_priority = os::java_to_os_priority[MaxPriority];
+    // Set negative to distinguish from other priorities
+    os::java_to_os_priority[MaxPriority] = -criticalPrio;
+  }
+
+  // Get IDs for a set of well-known scheduling classes.
+  // TODO-FIXME: GETCLINFO returns the current # of classes in the
+  // the system.  We should have a loop that iterates over the
+  // classID values, which are known to be "small" integers.
+
+  strcpy(ClassInfo.pc_clname, "TS");
+  ClassInfo.pc_cid = -1;
+  rslt = priocntl(P_ALL, 0, PC_GETCID, (caddr_t)&ClassInfo);
+  if (rslt < 0) return errno;
+  assert(ClassInfo.pc_cid != -1, "cid for TS class is -1");
+  tsLimits.schedPolicy = ClassInfo.pc_cid;
+  tsLimits.maxPrio = ((tsinfo_t*)ClassInfo.pc_clinfo)->ts_maxupri;
+  tsLimits.minPrio = -tsLimits.maxPrio;
+
+  strcpy(ClassInfo.pc_clname, "IA");
+  ClassInfo.pc_cid = -1;
+  rslt = priocntl(P_ALL, 0, PC_GETCID, (caddr_t)&ClassInfo);
+  if (rslt < 0) return errno;
+  assert(ClassInfo.pc_cid != -1, "cid for IA class is -1");
+  iaLimits.schedPolicy = ClassInfo.pc_cid;
+  iaLimits.maxPrio = ((iainfo_t*)ClassInfo.pc_clinfo)->ia_maxupri;
+  iaLimits.minPrio = -iaLimits.maxPrio;
+
+  strcpy(ClassInfo.pc_clname, "RT");
+  ClassInfo.pc_cid = -1;
+  rslt = priocntl(P_ALL, 0, PC_GETCID, (caddr_t)&ClassInfo);
+  if (rslt < 0) return errno;
+  assert(ClassInfo.pc_cid != -1, "cid for RT class is -1");
+  rtLimits.schedPolicy = ClassInfo.pc_cid;
+  rtLimits.maxPrio = ((rtinfo_t*)ClassInfo.pc_clinfo)->rt_maxpri;
+  rtLimits.minPrio = 0;
+
+  strcpy(ClassInfo.pc_clname, "FX");
+  ClassInfo.pc_cid = -1;
+  rslt = priocntl(P_ALL, 0, PC_GETCID, (caddr_t)&ClassInfo);
+  if (rslt < 0) return errno;
+  assert(ClassInfo.pc_cid != -1, "cid for FX class is -1");
+  fxLimits.schedPolicy = ClassInfo.pc_cid;
+  fxLimits.maxPrio = ((fxinfo_t*)ClassInfo.pc_clinfo)->fx_maxupri;
+  fxLimits.minPrio = 0;
+
+  // Query our "current" scheduling class.
+  // This will normally be IA, TS or, rarely, FX or RT.
+  memset(&ParmInfo, 0, sizeof(ParmInfo));
+  ParmInfo.pc_cid = PC_CLNULL;
+  rslt = priocntl(P_PID, P_MYID, PC_GETPARMS, (caddr_t)&ParmInfo);
+  if (rslt < 0) return errno;
+  myClass = ParmInfo.pc_cid;
+
+  // We now know our scheduling classId, get specific information
+  // about the class.
+  ClassInfo.pc_cid = myClass;
+  ClassInfo.pc_clname[0] = 0;
+  rslt = priocntl((idtype)0, 0, PC_GETCLINFO, (caddr_t)&ClassInfo);
+  if (rslt < 0) return errno;
+
+  if (ThreadPriorityVerbose) {
+    tty->print_cr("lwp_priocntl_init: Class=%d(%s)...", myClass, ClassInfo.pc_clname);
+  }
+
+  memset(&ParmInfo, 0, sizeof(pcparms_t));
+  ParmInfo.pc_cid = PC_CLNULL;
+  rslt = priocntl(P_PID, P_MYID, PC_GETPARMS, (caddr_t)&ParmInfo);
+  if (rslt < 0) return errno;
+
+  if (ParmInfo.pc_cid == rtLimits.schedPolicy) {
+    myMin = rtLimits.minPrio;
+    myMax = rtLimits.maxPrio;
+  } else if (ParmInfo.pc_cid == iaLimits.schedPolicy) {
+    iaparms_t *iaInfo  = (iaparms_t*)ParmInfo.pc_clparms;
+    myMin = iaLimits.minPrio;
+    myMax = iaLimits.maxPrio;
+    myMax = MIN2(myMax, (int)iaInfo->ia_uprilim);       // clamp - restrict
+  } else if (ParmInfo.pc_cid == tsLimits.schedPolicy) {
+    tsparms_t *tsInfo  = (tsparms_t*)ParmInfo.pc_clparms;
+    myMin = tsLimits.minPrio;
+    myMax = tsLimits.maxPrio;
+    myMax = MIN2(myMax, (int)tsInfo->ts_uprilim);       // clamp - restrict
+  } else if (ParmInfo.pc_cid == fxLimits.schedPolicy) {
+    fxparms_t *fxInfo = (fxparms_t*)ParmInfo.pc_clparms;
+    myMin = fxLimits.minPrio;
+    myMax = fxLimits.maxPrio;
+    myMax = MIN2(myMax, (int)fxInfo->fx_uprilim);       // clamp - restrict
+  } else {
+    // No clue - punt
+    if (ThreadPriorityVerbose) {
+      tty->print_cr("Unknown scheduling class: %s ... \n",
+                    ClassInfo.pc_clname);
+    }
+    return EINVAL;      // no clue, punt
+  }
+
+  if (ThreadPriorityVerbose) {
+    tty->print_cr("Thread priority Range: [%d..%d]\n", myMin, myMax);
+  }
+
+  priocntl_enable = true;  // Enable changing priorities
+  return 0;
+}
+
+#define IAPRI(x)        ((iaparms_t *)((x).pc_clparms))
+#define RTPRI(x)        ((rtparms_t *)((x).pc_clparms))
+#define TSPRI(x)        ((tsparms_t *)((x).pc_clparms))
+#define FXPRI(x)        ((fxparms_t *)((x).pc_clparms))
+
+
+// scale_to_lwp_priority
+//
+// Convert from the libthread "thr_setprio" scale to our current
+// lwp scheduling class scale.
+//
+static int scale_to_lwp_priority(int rMin, int rMax, int x) {
+  int v;
+
+  if (x == 127) return rMax;            // avoid round-down
+  v = (((x*(rMax-rMin)))/128)+rMin;
+  return v;
+}
+
+
+// set_lwp_class_and_priority
+int set_lwp_class_and_priority(int ThreadID, int lwpid,
+                               int newPrio, int new_class, bool scale) {
+  int rslt;
+  int Actual, Expected, prv;
+  pcparms_t ParmInfo;                   // for GET-SET
+#ifdef ASSERT
+  pcparms_t ReadBack;                   // for readback
+#endif
+
+  // Set priority via PC_GETPARMS, update, PC_SETPARMS
+  // Query current values.
+  // TODO: accelerate this by eliminating the PC_GETPARMS call.
+  // Cache "pcparms_t" in global ParmCache.
+  // TODO: elide set-to-same-value
+
+  // If something went wrong on init, don't change priorities.
+  if (!priocntl_enable) {
+    if (ThreadPriorityVerbose) {
+      tty->print_cr("Trying to set priority but init failed, ignoring");
+    }
+    return EINVAL;
+  }
+
+  // If lwp hasn't started yet, just return
+  // the _start routine will call us again.
+  if (lwpid <= 0) {
+    if (ThreadPriorityVerbose) {
+      tty->print_cr("deferring the set_lwp_class_and_priority of thread "
+                    INTPTR_FORMAT " to %d, lwpid not set",
+                    (uintx)ThreadID, newPrio);
+    }
+    return 0;
+  }
+
+  if (ThreadPriorityVerbose) {
+    tty->print_cr ("set_lwp_class_and_priority("
+                   INTPTR_FORMAT "@" INTPTR_FORMAT " %d) ",
+                   (uintx)ThreadID, (uintx)lwpid, newPrio);
+  }
+
+  memset(&ParmInfo, 0, sizeof(pcparms_t));
+  ParmInfo.pc_cid = PC_CLNULL;
+  rslt = priocntl(P_LWPID, lwpid, PC_GETPARMS, (caddr_t)&ParmInfo);
+  if (rslt < 0) return errno;
+
+  int cur_class = ParmInfo.pc_cid;
+  ParmInfo.pc_cid = (id_t)new_class;
+
+  if (new_class == rtLimits.schedPolicy) {
+    rtparms_t *rtInfo  = (rtparms_t*)ParmInfo.pc_clparms;
+    rtInfo->rt_pri     = scale ? scale_to_lwp_priority(rtLimits.minPrio,
+                                                       rtLimits.maxPrio, newPrio)
+                               : newPrio;
+    rtInfo->rt_tqsecs  = RT_NOCHANGE;
+    rtInfo->rt_tqnsecs = RT_NOCHANGE;
+    if (ThreadPriorityVerbose) {
+      tty->print_cr("RT: %d->%d\n", newPrio, rtInfo->rt_pri);
+    }
+  } else if (new_class == iaLimits.schedPolicy) {
+    iaparms_t* iaInfo  = (iaparms_t*)ParmInfo.pc_clparms;
+    int maxClamped     = MIN2(iaLimits.maxPrio,
+                              cur_class == new_class
+                              ? (int)iaInfo->ia_uprilim : iaLimits.maxPrio);
+    iaInfo->ia_upri    = scale ? scale_to_lwp_priority(iaLimits.minPrio,
+                                                       maxClamped, newPrio)
+                               : newPrio;
+    iaInfo->ia_uprilim = cur_class == new_class
+                           ? IA_NOCHANGE : (pri_t)iaLimits.maxPrio;
+    iaInfo->ia_mode    = IA_NOCHANGE;
+    if (ThreadPriorityVerbose) {
+      tty->print_cr("IA: [%d...%d] %d->%d\n",
+                    iaLimits.minPrio, maxClamped, newPrio, iaInfo->ia_upri);
+    }
+  } else if (new_class == tsLimits.schedPolicy) {
+    tsparms_t* tsInfo  = (tsparms_t*)ParmInfo.pc_clparms;
+    int maxClamped     = MIN2(tsLimits.maxPrio,
+                              cur_class == new_class
+                              ? (int)tsInfo->ts_uprilim : tsLimits.maxPrio);
+    tsInfo->ts_upri    = scale ? scale_to_lwp_priority(tsLimits.minPrio,
+                                                       maxClamped, newPrio)
+                               : newPrio;
+    tsInfo->ts_uprilim = cur_class == new_class
+                           ? TS_NOCHANGE : (pri_t)tsLimits.maxPrio;
+    if (ThreadPriorityVerbose) {
+      tty->print_cr("TS: [%d...%d] %d->%d\n",
+                    tsLimits.minPrio, maxClamped, newPrio, tsInfo->ts_upri);
+    }
+  } else if (new_class == fxLimits.schedPolicy) {
+    fxparms_t* fxInfo  = (fxparms_t*)ParmInfo.pc_clparms;
+    int maxClamped     = MIN2(fxLimits.maxPrio,
+                              cur_class == new_class
+                              ? (int)fxInfo->fx_uprilim : fxLimits.maxPrio);
+    fxInfo->fx_upri    = scale ? scale_to_lwp_priority(fxLimits.minPrio,
+                                                       maxClamped, newPrio)
+                               : newPrio;
+    fxInfo->fx_uprilim = cur_class == new_class
+                           ? FX_NOCHANGE : (pri_t)fxLimits.maxPrio;
+    fxInfo->fx_tqsecs  = FX_NOCHANGE;
+    fxInfo->fx_tqnsecs = FX_NOCHANGE;
+    if (ThreadPriorityVerbose) {
+      tty->print_cr("FX: [%d...%d] %d->%d\n",
+                    fxLimits.minPrio, maxClamped, newPrio, fxInfo->fx_upri);
+    }
+  } else {
+    if (ThreadPriorityVerbose) {
+      tty->print_cr("Unknown new scheduling class %d\n", new_class);
+    }
+    return EINVAL;    // no clue, punt
+  }
+
+  rslt = priocntl(P_LWPID, lwpid, PC_SETPARMS, (caddr_t)&ParmInfo);
+  if (ThreadPriorityVerbose && rslt) {
+    tty->print_cr ("PC_SETPARMS ->%d %d\n", rslt, errno);
+  }
+  if (rslt < 0) return errno;
+
+#ifdef ASSERT
+  // Sanity check: read back what we just attempted to set.
+  // In theory it could have changed in the interim ...
+  //
+  // The priocntl system call is tricky.
+  // Sometimes it'll validate the priority value argument and
+  // return EINVAL if unhappy.  At other times it fails silently.
+  // Readbacks are prudent.
+
+  if (!ReadBackValidate) return 0;
+
+  memset(&ReadBack, 0, sizeof(pcparms_t));
+  ReadBack.pc_cid = PC_CLNULL;
+  rslt = priocntl(P_LWPID, lwpid, PC_GETPARMS, (caddr_t)&ReadBack);
+  assert(rslt >= 0, "priocntl failed");
+  Actual = Expected = 0xBAD;
+  assert(ParmInfo.pc_cid == ReadBack.pc_cid, "cid's don't match");
+  if (ParmInfo.pc_cid == rtLimits.schedPolicy) {
+    Actual   = RTPRI(ReadBack)->rt_pri;
+    Expected = RTPRI(ParmInfo)->rt_pri;
+  } else if (ParmInfo.pc_cid == iaLimits.schedPolicy) {
+    Actual   = IAPRI(ReadBack)->ia_upri;
+    Expected = IAPRI(ParmInfo)->ia_upri;
+  } else if (ParmInfo.pc_cid == tsLimits.schedPolicy) {
+    Actual   = TSPRI(ReadBack)->ts_upri;
+    Expected = TSPRI(ParmInfo)->ts_upri;
+  } else if (ParmInfo.pc_cid == fxLimits.schedPolicy) {
+    Actual   = FXPRI(ReadBack)->fx_upri;
+    Expected = FXPRI(ParmInfo)->fx_upri;
+  } else {
+    if (ThreadPriorityVerbose) {
+      tty->print_cr("set_lwp_class_and_priority: unexpected class in readback: %d\n",
+                    ParmInfo.pc_cid);
+    }
+  }
+
+  if (Actual != Expected) {
+    if (ThreadPriorityVerbose) {
+      tty->print_cr ("set_lwp_class_and_priority(%d %d) Class=%d: actual=%d vs expected=%d\n",
+                     lwpid, newPrio, ReadBack.pc_cid, Actual, Expected);
+    }
+  }
+#endif
+
+  return 0;
+}
+
+// Solaris only gives access to 128 real priorities at a time,
+// so we expand Java's ten to fill this range.  This would be better
+// if we dynamically adjusted relative priorities.
+//
+// The ThreadPriorityPolicy option allows us to select 2 different
+// priority scales.
+//
+// ThreadPriorityPolicy=0
+// Since the Solaris' default priority is MaximumPriority, we do not
+// set a priority lower than Max unless a priority lower than
+// NormPriority is requested.
+//
+// ThreadPriorityPolicy=1
+// This mode causes the priority table to get filled with
+// linear values.  NormPriority gets mapped to 50% of the
+// Maximum priority and so on.  This will cause VM threads
+// to get unfair treatment against other Solaris processes
+// which do not explicitly alter their thread priorities.
+
+int os::java_to_os_priority[CriticalPriority + 1] = {
+  -99999,         // 0 Entry should never be used
+
+  0,              // 1 MinPriority
+  32,             // 2
+  64,             // 3
+
+  96,             // 4
+  127,            // 5 NormPriority
+  127,            // 6
+
+  127,            // 7
+  127,            // 8
+  127,            // 9 NearMaxPriority
+
+  127,            // 10 MaxPriority
+
+  -criticalPrio   // 11 CriticalPriority
+};
+
+OSReturn os::set_native_priority(Thread* thread, int newpri) {
+  OSThread* osthread = thread->osthread();
+
+  // Save requested priority in case the thread hasn't been started
+  osthread->set_native_priority(newpri);
+
+  // Check for critical priority request
+  bool fxcritical = false;
+  if (newpri == -criticalPrio) {
+    fxcritical = true;
+    newpri = criticalPrio;
+  }
+
+  assert(newpri >= MinimumPriority && newpri <= MaximumPriority, "bad priority mapping");
+  if (!UseThreadPriorities) return OS_OK;
+
+  int status = 0;
+
+  if (!fxcritical) {
+    // Use thr_setprio only if we have a priority that thr_setprio understands
+    status = thr_setprio(thread->osthread()->thread_id(), newpri);
+  }
+
+  int lwp_status =
+          set_lwp_class_and_priority(osthread->thread_id(),
+                                     osthread->lwp_id(),
+                                     newpri,
+                                     fxcritical ? fxLimits.schedPolicy : myClass,
+                                     !fxcritical);
+  if (lwp_status != 0 && fxcritical) {
+    // Try again, this time without changing the scheduling class
+    newpri = java_MaxPriority_to_os_priority;
+    lwp_status = set_lwp_class_and_priority(osthread->thread_id(),
+                                            osthread->lwp_id(),
+                                            newpri, myClass, false);
+  }
+  status |= lwp_status;
+  return (status == 0) ? OS_OK : OS_ERR;
+}
+
+
+OSReturn os::get_native_priority(const Thread* const thread,
+                                 int *priority_ptr) {
+  int p;
+  if (!UseThreadPriorities) {
+    *priority_ptr = NormalPriority;
+    return OS_OK;
+  }
+  int status = thr_getprio(thread->osthread()->thread_id(), &p);
+  if (status != 0) {
+    return OS_ERR;
+  }
+  *priority_ptr = p;
+  return OS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+void os::print_statistics() {
+}
+
+// This does not do anything on Solaris. This is basically a hook for being
+// able to use structured exception handling (thread-local exception filters) on, e.g., Win32.
+void os::os_exception_wrapper(java_call_t f, JavaValue* value,
+                              const methodHandle& method, JavaCallArguments* args,
+                              JavaThread* thread) {
+  f(value, method, args, thread);
+}
+
+void report_error(const char* file_name, int line_no, const char* title,
+                  const char* format, ...);
+
+// (Static) wrappers for the liblgrp API
+os::Solaris::lgrp_home_func_t os::Solaris::_lgrp_home;
+os::Solaris::lgrp_init_func_t os::Solaris::_lgrp_init;
+os::Solaris::lgrp_fini_func_t os::Solaris::_lgrp_fini;
+os::Solaris::lgrp_root_func_t os::Solaris::_lgrp_root;
+os::Solaris::lgrp_children_func_t os::Solaris::_lgrp_children;
+os::Solaris::lgrp_resources_func_t os::Solaris::_lgrp_resources;
+os::Solaris::lgrp_nlgrps_func_t os::Solaris::_lgrp_nlgrps;
+os::Solaris::lgrp_cookie_stale_func_t os::Solaris::_lgrp_cookie_stale;
+os::Solaris::lgrp_cookie_t os::Solaris::_lgrp_cookie = 0;
+
+static address resolve_symbol_lazy(const char* name) {
+  address addr = (address) dlsym(RTLD_DEFAULT, name);
+  if (addr == NULL) {
+    // RTLD_DEFAULT was not defined on some early versions of 2.5.1
+    addr = (address) dlsym(RTLD_NEXT, name);
+  }
+  return addr;
+}
+
+static address resolve_symbol(const char* name) {
+  address addr = resolve_symbol_lazy(name);
+  if (addr == NULL) {
+    fatal("resolve_symbol failed (%s)", dlerror());
+  }
+  return addr;
+}
+
+void os::Solaris::libthread_init() {
+  address func = (address)dlsym(RTLD_DEFAULT, "_thr_suspend_allmutators");
+
+  lwp_priocntl_init();
+
+  // RTLD_DEFAULT was not defined on some early versions of 5.5.1
+  if (func == NULL) {
+    func = (address) dlsym(RTLD_NEXT, "_thr_suspend_allmutators");
+    // Guarantee that this VM is running on an new enough OS (5.6 or
+    // later) that it will have a new enough libthread.so.
+    guarantee(func != NULL, "libthread.so is too old.");
+  }
+
+  int size;
+  void (*handler_info_func)(address *, int *);
+  handler_info_func = CAST_TO_FN_PTR(void (*)(address *, int *), resolve_symbol("thr_sighndlrinfo"));
+  handler_info_func(&handler_start, &size);
+  handler_end = handler_start + size;
+}
+
+
+int_fnP_mutex_tP os::Solaris::_mutex_lock;
+int_fnP_mutex_tP os::Solaris::_mutex_trylock;
+int_fnP_mutex_tP os::Solaris::_mutex_unlock;
+int_fnP_mutex_tP_i_vP os::Solaris::_mutex_init;
+int_fnP_mutex_tP os::Solaris::_mutex_destroy;
+int os::Solaris::_mutex_scope = USYNC_THREAD;
+
+int_fnP_cond_tP_mutex_tP_timestruc_tP os::Solaris::_cond_timedwait;
+int_fnP_cond_tP_mutex_tP os::Solaris::_cond_wait;
+int_fnP_cond_tP os::Solaris::_cond_signal;
+int_fnP_cond_tP os::Solaris::_cond_broadcast;
+int_fnP_cond_tP_i_vP os::Solaris::_cond_init;
+int_fnP_cond_tP os::Solaris::_cond_destroy;
+int os::Solaris::_cond_scope = USYNC_THREAD;
+bool os::Solaris::_synchronization_initialized;
+
+void os::Solaris::synchronization_init() {
+  os::Solaris::set_mutex_scope(USYNC_THREAD);
+  os::Solaris::set_cond_scope(USYNC_THREAD);
+
+  if (UsePthreads) {
+    os::Solaris::set_mutex_lock(CAST_TO_FN_PTR(int_fnP_mutex_tP, resolve_symbol("pthread_mutex_lock")));
+    os::Solaris::set_mutex_trylock(CAST_TO_FN_PTR(int_fnP_mutex_tP, resolve_symbol("pthread_mutex_trylock")));
+    os::Solaris::set_mutex_unlock(CAST_TO_FN_PTR(int_fnP_mutex_tP, resolve_symbol("pthread_mutex_unlock")));
+    os::Solaris::set_mutex_init(pthread_mutex_default_init);
+    os::Solaris::set_mutex_destroy(CAST_TO_FN_PTR(int_fnP_mutex_tP, resolve_symbol("pthread_mutex_destroy")));
+
+    os::Solaris::set_cond_timedwait(CAST_TO_FN_PTR(int_fnP_cond_tP_mutex_tP_timestruc_tP, resolve_symbol("pthread_cond_timedwait")));
+    os::Solaris::set_cond_wait(CAST_TO_FN_PTR(int_fnP_cond_tP_mutex_tP, resolve_symbol("pthread_cond_wait")));
+    os::Solaris::set_cond_signal(CAST_TO_FN_PTR(int_fnP_cond_tP, resolve_symbol("pthread_cond_signal")));
+    os::Solaris::set_cond_broadcast(CAST_TO_FN_PTR(int_fnP_cond_tP, resolve_symbol("pthread_cond_broadcast")));
+    os::Solaris::set_cond_init(pthread_cond_default_init);
+    os::Solaris::set_cond_destroy(CAST_TO_FN_PTR(int_fnP_cond_tP, resolve_symbol("pthread_cond_destroy")));
+  } else {
+    os::Solaris::set_mutex_lock(CAST_TO_FN_PTR(int_fnP_mutex_tP, resolve_symbol("mutex_lock")));
+    os::Solaris::set_mutex_trylock(CAST_TO_FN_PTR(int_fnP_mutex_tP, resolve_symbol("mutex_trylock")));
+    os::Solaris::set_mutex_unlock(CAST_TO_FN_PTR(int_fnP_mutex_tP, resolve_symbol("mutex_unlock")));
+    os::Solaris::set_mutex_init(::mutex_init);
+    os::Solaris::set_mutex_destroy(::mutex_destroy);
+
+    os::Solaris::set_cond_timedwait(CAST_TO_FN_PTR(int_fnP_cond_tP_mutex_tP_timestruc_tP, resolve_symbol("cond_timedwait")));
+    os::Solaris::set_cond_wait(CAST_TO_FN_PTR(int_fnP_cond_tP_mutex_tP, resolve_symbol("cond_wait")));
+    os::Solaris::set_cond_signal(CAST_TO_FN_PTR(int_fnP_cond_tP, resolve_symbol("cond_signal")));
+    os::Solaris::set_cond_broadcast(CAST_TO_FN_PTR(int_fnP_cond_tP, resolve_symbol("cond_broadcast")));
+    os::Solaris::set_cond_init(::cond_init);
+      os::Solaris::set_cond_destroy(::cond_destroy);
+  }
+  _synchronization_initialized = true;
+}
+
+bool os::Solaris::liblgrp_init() {
+  void *handle = dlopen("liblgrp.so.1", RTLD_LAZY);
+  if (handle != NULL) {
+    os::Solaris::set_lgrp_home(CAST_TO_FN_PTR(lgrp_home_func_t, dlsym(handle, "lgrp_home")));
+    os::Solaris::set_lgrp_init(CAST_TO_FN_PTR(lgrp_init_func_t, dlsym(handle, "lgrp_init")));
+    os::Solaris::set_lgrp_fini(CAST_TO_FN_PTR(lgrp_fini_func_t, dlsym(handle, "lgrp_fini")));
+    os::Solaris::set_lgrp_root(CAST_TO_FN_PTR(lgrp_root_func_t, dlsym(handle, "lgrp_root")));
+    os::Solaris::set_lgrp_children(CAST_TO_FN_PTR(lgrp_children_func_t, dlsym(handle, "lgrp_children")));
+    os::Solaris::set_lgrp_resources(CAST_TO_FN_PTR(lgrp_resources_func_t, dlsym(handle, "lgrp_resources")));
+    os::Solaris::set_lgrp_nlgrps(CAST_TO_FN_PTR(lgrp_nlgrps_func_t, dlsym(handle, "lgrp_nlgrps")));
+    os::Solaris::set_lgrp_cookie_stale(CAST_TO_FN_PTR(lgrp_cookie_stale_func_t,
+                                                      dlsym(handle, "lgrp_cookie_stale")));
+
+    lgrp_cookie_t c = lgrp_init(LGRP_VIEW_CALLER);
+    set_lgrp_cookie(c);
+    return true;
+  }
+  return false;
+}
+
+// int pset_getloadavg(psetid_t pset, double loadavg[], int nelem);
+typedef long (*pset_getloadavg_type)(psetid_t pset, double loadavg[], int nelem);
+static pset_getloadavg_type pset_getloadavg_ptr = NULL;
+
+void init_pset_getloadavg_ptr(void) {
+  pset_getloadavg_ptr =
+    (pset_getloadavg_type)dlsym(RTLD_DEFAULT, "pset_getloadavg");
+  if (pset_getloadavg_ptr == NULL) {
+    log_warning(os)("pset_getloadavg function not found");
+  }
+}
+
+int os::Solaris::_dev_zero_fd = -1;
+
+// this is called _before_ the global arguments have been parsed
+void os::init(void) {
+  _initial_pid = getpid();
+
+  max_hrtime = first_hrtime = gethrtime();
+
+  init_random(1234567);
+
+  Solaris::set_page_size(sysconf(_SC_PAGESIZE));
+  if (Solaris::page_size() == -1) {
+    fatal("os_solaris.cpp: os::init: sysconf failed (%s)", os::strerror(errno));
+  }
+  _page_sizes.add(Solaris::page_size());
+
+  Solaris::initialize_system_info();
+
+  int fd = ::open("/dev/zero", O_RDWR);
+  if (fd < 0) {
+    fatal("os::init: cannot open /dev/zero (%s)", os::strerror(errno));
+  } else {
+    Solaris::set_dev_zero_fd(fd);
+
+    // Close on exec, child won't inherit.
+    fcntl(fd, F_SETFD, FD_CLOEXEC);
+  }
+
+  clock_tics_per_sec = CLK_TCK;
+
+  // check if dladdr1() exists; dladdr1 can provide more information than
+  // dladdr for os::dll_address_to_function_name. It comes with SunOS 5.9
+  // and is available on linker patches for 5.7 and 5.8.
+  // libdl.so must have been loaded, this call is just an entry lookup
+  void * hdl = dlopen("libdl.so", RTLD_NOW);
+  if (hdl) {
+    dladdr1_func = CAST_TO_FN_PTR(dladdr1_func_type, dlsym(hdl, "dladdr1"));
+  }
+
+  // main_thread points to the thread that created/loaded the JVM.
+  main_thread = thr_self();
+
+  // dynamic lookup of functions that may not be available in our lowest
+  // supported Solaris release
+  void * handle = dlopen("libc.so.1", RTLD_LAZY);
+  if (handle != NULL) {
+    Solaris::_pthread_setname_np =  // from 11.3
+        (Solaris::pthread_setname_np_func_t)dlsym(handle, "pthread_setname_np");
+  }
+
+  // Shared Posix initialization
+  os::Posix::init();
+}
+
+// To install functions for atexit system call
+extern "C" {
+  static void perfMemory_exit_helper() {
+    perfMemory_exit();
+  }
+}
+
+// this is called _after_ the global arguments have been parsed
+jint os::init_2(void) {
+  // Check and sets minimum stack sizes against command line options
+  if (Posix::set_minimum_stack_sizes() == JNI_ERR) {
+    return JNI_ERR;
+  }
+
+  Solaris::libthread_init();
+
+  if (UseNUMA) {
+    if (!Solaris::liblgrp_init()) {
+      FLAG_SET_ERGO(UseNUMA, false);
+    } else {
+      size_t lgrp_limit = os::numa_get_groups_num();
+      int *lgrp_ids = NEW_C_HEAP_ARRAY(int, lgrp_limit, mtInternal);
+      size_t lgrp_num = os::numa_get_leaf_groups(lgrp_ids, lgrp_limit);
+      FREE_C_HEAP_ARRAY(int, lgrp_ids);
+      if (lgrp_num < 2) {
+        // There's only one locality group, disable NUMA
+        UseNUMA = false;
+      }
+    }
+  }
+
+  // When NUMA requested, not-NUMA-aware allocations default to interleaving.
+  if (UseNUMA && !UseNUMAInterleaving) {
+    FLAG_SET_ERGO_IF_DEFAULT(UseNUMAInterleaving, true);
+  }
+
+  if (PosixSignals::init() == JNI_ERR) {
+    return JNI_ERR;
+  }
+
+  // initialize synchronization primitives
+  Solaris::synchronization_init();
+  DEBUG_ONLY(os::set_mutex_init_done();)
+
+  if (MaxFDLimit) {
+    // set the number of file descriptors to max. print out error
+    // if getrlimit/setrlimit fails but continue regardless.
+    struct rlimit nbr_files;
+    int status = getrlimit(RLIMIT_NOFILE, &nbr_files);
+    if (status != 0) {
+      log_info(os)("os::init_2 getrlimit failed: %s", os::strerror(errno));
+    } else {
+      nbr_files.rlim_cur = nbr_files.rlim_max;
+      status = setrlimit(RLIMIT_NOFILE, &nbr_files);
+      if (status != 0) {
+        log_info(os)("os::init_2 setrlimit failed: %s", os::strerror(errno));
+      }
+    }
+  }
+
+  // Calculate theoretical max. size of Threads to guard gainst
+  // artificial out-of-memory situations, where all available address-
+  // space has been reserved by thread stacks. Default stack size is 1Mb.
+  size_t pre_thread_stack_size = (JavaThread::stack_size_at_create()) ?
+    JavaThread::stack_size_at_create() : (1*K*K);
+  assert(pre_thread_stack_size != 0, "Must have a stack");
+  // Solaris has a maximum of 4Gb of user programs. Calculate the thread limit when
+  // we should start doing Virtual Memory banging. Currently when the threads will
+  // have used all but 200Mb of space.
+  size_t max_address_space = ((unsigned int)4 * K * K * K) - (200 * K * K);
+  Solaris::_os_thread_limit = max_address_space / pre_thread_stack_size;
+
+  // at-exit methods are called in the reverse order of their registration.
+  // In Solaris 7 and earlier, atexit functions are called on return from
+  // main or as a result of a call to exit(3C). There can be only 32 of
+  // these functions registered and atexit() does not set errno. In Solaris
+  // 8 and later, there is no limit to the number of functions registered
+  // and atexit() sets errno. In addition, in Solaris 8 and later, atexit
+  // functions are called upon dlclose(3C) in addition to return from main
+  // and exit(3C).
+
+  if (PerfAllowAtExitRegistration) {
+    // only register atexit functions if PerfAllowAtExitRegistration is set.
+    // atexit functions can be delayed until process exit time, which
+    // can be problematic for embedded VM situations. Embedded VMs should
+    // call DestroyJavaVM() to assure that VM resources are released.
+
+    // note: perfMemory_exit_helper atexit function may be removed in
+    // the future if the appropriate cleanup code can be added to the
+    // VM_Exit VMOperation's doit method.
+    if (atexit(perfMemory_exit_helper) != 0) {
+      warning("os::init2 atexit(perfMemory_exit_helper) failed");
+    }
+  }
+
+  // Init pset_loadavg function pointer
+  init_pset_getloadavg_ptr();
+
+  // Shared Posix initialization
+  os::Posix::init_2();
+
+  return JNI_OK;
+}
+
+// This code originates from JDK's sysOpen and open64_w
+// from src/solaris/hpi/src/system_md.c
+
+int os::open(const char *path, int oflag, int mode) {
+  if (strlen(path) > MAX_PATH - 1) {
+    errno = ENAMETOOLONG;
+    return -1;
+  }
+  int fd;
+
+  fd = ::open64(path, oflag, mode);
+  if (fd == -1) return -1;
+
+  // If the open succeeded, the file might still be a directory
+  {
+    struct stat64 buf64;
+    int ret = ::fstat64(fd, &buf64);
+    int st_mode = buf64.st_mode;
+
+    if (ret != -1) {
+      if ((st_mode & S_IFMT) == S_IFDIR) {
+        errno = EISDIR;
+        ::close(fd);
+        return -1;
+      }
+    } else {
+      ::close(fd);
+      return -1;
+    }
+  }
+
+  // All file descriptors that are opened in the JVM and not
+  // specifically destined for a subprocess should have the
+  // close-on-exec flag set.  If we don't set it, then careless 3rd
+  // party native code might fork and exec without closing all
+  // appropriate file descriptors (e.g. as we do in closeDescriptors in
+  // UNIXProcess.c), and this in turn might:
+  //
+  // - cause end-of-file to fail to be detected on some file
+  //   descriptors, resulting in mysterious hangs, or
+  //
+  // - might cause an fopen in the subprocess to fail on a system
+  //   suffering from bug 1085341.
+  //
+  // (Yes, the default setting of the close-on-exec flag is a Unix
+  // design flaw)
+  //
+  // See:
+  // 1085341: 32-bit stdio routines should support file descriptors >255
+  // 4843136: (process) pipe file descriptor from Runtime.exec not being closed
+  // 6339493: (process) Runtime.exec does not close all file descriptors on Solaris 9
+  //
+#ifdef FD_CLOEXEC
+  {
+    int flags = ::fcntl(fd, F_GETFD);
+    if (flags != -1) {
+      ::fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
+    }
+  }
+#endif
+
+  return fd;
+}
+
+// create binary file, rewriting existing file if required
+int os::create_binary_file(const char* path, bool rewrite_existing) {
+  int oflags = O_WRONLY | O_CREAT;
+  if (!rewrite_existing) {
+    oflags |= O_EXCL;
+  }
+  return ::open64(path, oflags, S_IREAD | S_IWRITE);
+}
+
+// return current position of file pointer
+jlong os::current_file_offset(int fd) {
+  return (jlong)::lseek64(fd, (off64_t)0, SEEK_CUR);
+}
+
+// move file pointer to the specified offset
+jlong os::seek_to_file_offset(int fd, jlong offset) {
+  return (jlong)::lseek64(fd, (off64_t)offset, SEEK_SET);
+}
+
+int os::available(int fd, jlong *bytes) {
+  assert(((JavaThread*)Thread::current())->thread_state() == _thread_in_native,
+         "Assumed _thread_in_native");
+  jlong cur, end;
+  int mode;
+  struct stat64 buf64;
+
+  if (::fstat64(fd, &buf64) >= 0) {
+    mode = buf64.st_mode;
+    if (S_ISCHR(mode) || S_ISFIFO(mode) || S_ISSOCK(mode)) {
+      int n,ioctl_return;
+
+      RESTARTABLE(::ioctl(fd, FIONREAD, &n), ioctl_return);
+      if (ioctl_return>= 0) {
+        *bytes = n;
+        return 1;
+      }
+    }
+  }
+  if ((cur = ::lseek64(fd, 0L, SEEK_CUR)) == -1) {
+    return 0;
+  } else if ((end = ::lseek64(fd, 0L, SEEK_END)) == -1) {
+    return 0;
+  } else if (::lseek64(fd, cur, SEEK_SET) == -1) {
+    return 0;
+  }
+  *bytes = end - cur;
+  return 1;
+}
+
+// Map a block of memory.
+char* os::pd_map_memory(int fd, const char* file_name, size_t file_offset,
+                        char *addr, size_t bytes, bool read_only,
+                        bool allow_exec) {
+  int prot;
+  int flags;
+
+  if (read_only) {
+    prot = PROT_READ;
+    flags = MAP_SHARED;
+  } else {
+    prot = PROT_READ | PROT_WRITE;
+    flags = MAP_PRIVATE;
+  }
+
+  if (allow_exec) {
+    prot |= PROT_EXEC;
+  }
+
+  if (addr != NULL) {
+    flags |= MAP_FIXED;
+  }
+
+  char* mapped_address = (char*)mmap(addr, (size_t)bytes, prot, flags,
+                                     fd, file_offset);
+  if (mapped_address == MAP_FAILED) {
+    return NULL;
+  }
+  return mapped_address;
+}
+
+
+// Remap a block of memory.
+char* os::pd_remap_memory(int fd, const char* file_name, size_t file_offset,
+                          char *addr, size_t bytes, bool read_only,
+                          bool allow_exec) {
+  // same as map_memory() on this OS
+  return os::map_memory(fd, file_name, file_offset, addr, bytes, read_only,
+                        allow_exec);
+}
+
+
+// Unmap a block of memory.
+bool os::pd_unmap_memory(char* addr, size_t bytes) {
+  return munmap(addr, bytes) == 0;
+}
+
+void os::pause() {
+  char filename[MAX_PATH];
+  if (PauseAtStartupFile && PauseAtStartupFile[0]) {
+    jio_snprintf(filename, MAX_PATH, "%s", PauseAtStartupFile);
+  } else {
+    jio_snprintf(filename, MAX_PATH, "./vm.paused.%d", current_process_id());
+  }
+
+  int fd = ::open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+  if (fd != -1) {
+    struct stat buf;
+    ::close(fd);
+    while (::stat(filename, &buf) == 0) {
+      (void)::poll(NULL, 0, 100);
+    }
+  } else {
+    jio_fprintf(stderr,
+                "Could not open pause file '%s', continuing immediately.\n", filename);
+  }
+}
+
+#ifndef PRODUCT
+#ifdef INTERPOSE_ON_SYSTEM_SYNCH_FUNCTIONS
+// Turn this on if you need to trace synch operations.
+// Set RECORD_SYNCH_LIMIT to a large-enough value,
+// and call record_synch_enable and record_synch_disable
+// around the computation of interest.
+
+void record_synch(char* name, bool returning);  // defined below
+
+class RecordSynch {
+  char* _name;
+ public:
+  RecordSynch(char* name) :_name(name) { record_synch(_name, false); }
+  ~RecordSynch()                       { record_synch(_name, true); }
+};
+
+#define CHECK_SYNCH_OP(ret, name, params, args, inner)          \
+extern "C" ret name params {                                    \
+  typedef ret name##_t params;                                  \
+  static name##_t* implem = NULL;                               \
+  static int callcount = 0;                                     \
+  if (implem == NULL) {                                         \
+    implem = (name##_t*) dlsym(RTLD_NEXT, #name);               \
+    if (implem == NULL)  fatal(dlerror());                      \
+  }                                                             \
+  ++callcount;                                                  \
+  RecordSynch _rs(#name);                                       \
+  inner;                                                        \
+  return implem args;                                           \
+}
+// in dbx, examine callcounts this way:
+// for n in $(eval whereis callcount | awk '{print $2}'); do print $n; done
+
+#define CHECK_POINTER_OK(p) \
+  (!Universe::is_fully_initialized() || !Universe::is_reserved_heap((oop)(p)))
+#define CHECK_MU \
+  if (!CHECK_POINTER_OK(mu)) fatal("Mutex must be in C heap only.");
+#define CHECK_CV \
+  if (!CHECK_POINTER_OK(cv)) fatal("Condvar must be in C heap only.");
+#define CHECK_P(p) \
+  if (!CHECK_POINTER_OK(p))  fatal(false,  "Pointer must be in C heap only.");
+
+#define CHECK_MUTEX(mutex_op) \
+  CHECK_SYNCH_OP(int, mutex_op, (mutex_t *mu), (mu), CHECK_MU);
+
+CHECK_MUTEX(   mutex_lock)
+CHECK_MUTEX(  _mutex_lock)
+CHECK_MUTEX( mutex_unlock)
+CHECK_MUTEX(_mutex_unlock)
+CHECK_MUTEX( mutex_trylock)
+CHECK_MUTEX(_mutex_trylock)
+
+#define CHECK_COND(cond_op) \
+  CHECK_SYNCH_OP(int, cond_op, (cond_t *cv, mutex_t *mu), (cv, mu), CHECK_MU; CHECK_CV);
+
+CHECK_COND( cond_wait);
+CHECK_COND(_cond_wait);
+CHECK_COND(_cond_wait_cancel);
+
+#define CHECK_COND2(cond_op) \
+  CHECK_SYNCH_OP(int, cond_op, (cond_t *cv, mutex_t *mu, timestruc_t* ts), (cv, mu, ts), CHECK_MU; CHECK_CV);
+
+CHECK_COND2( cond_timedwait);
+CHECK_COND2(_cond_timedwait);
+CHECK_COND2(_cond_timedwait_cancel);
+
+// do the _lwp_* versions too
+#define mutex_t lwp_mutex_t
+#define cond_t  lwp_cond_t
+CHECK_MUTEX(  _lwp_mutex_lock)
+CHECK_MUTEX(  _lwp_mutex_unlock)
+CHECK_MUTEX(  _lwp_mutex_trylock)
+CHECK_MUTEX( __lwp_mutex_lock)
+CHECK_MUTEX( __lwp_mutex_unlock)
+CHECK_MUTEX( __lwp_mutex_trylock)
+CHECK_MUTEX(___lwp_mutex_lock)
+CHECK_MUTEX(___lwp_mutex_unlock)
+
+CHECK_COND(  _lwp_cond_wait);
+CHECK_COND( __lwp_cond_wait);
+CHECK_COND(___lwp_cond_wait);
+
+CHECK_COND2(  _lwp_cond_timedwait);
+CHECK_COND2( __lwp_cond_timedwait);
+#undef mutex_t
+#undef cond_t
+
+CHECK_SYNCH_OP(int, _lwp_suspend2,       (int lwp, int *n), (lwp, n), 0);
+CHECK_SYNCH_OP(int,__lwp_suspend2,       (int lwp, int *n), (lwp, n), 0);
+CHECK_SYNCH_OP(int, _lwp_kill,           (int lwp, int n),  (lwp, n), 0);
+CHECK_SYNCH_OP(int,__lwp_kill,           (int lwp, int n),  (lwp, n), 0);
+CHECK_SYNCH_OP(int, _lwp_sema_wait,      (lwp_sema_t* p),   (p),  CHECK_P(p));
+CHECK_SYNCH_OP(int,__lwp_sema_wait,      (lwp_sema_t* p),   (p),  CHECK_P(p));
+CHECK_SYNCH_OP(int, _lwp_cond_broadcast, (lwp_cond_t* cv),  (cv), CHECK_CV);
+CHECK_SYNCH_OP(int,__lwp_cond_broadcast, (lwp_cond_t* cv),  (cv), CHECK_CV);
+
+
+// recording machinery:
+
+enum { RECORD_SYNCH_LIMIT = 200 };
+char* record_synch_name[RECORD_SYNCH_LIMIT];
+void* record_synch_arg0ptr[RECORD_SYNCH_LIMIT];
+bool record_synch_returning[RECORD_SYNCH_LIMIT];
+thread_t record_synch_thread[RECORD_SYNCH_LIMIT];
+int record_synch_count = 0;
+bool record_synch_enabled = false;
+
+// in dbx, examine recorded data this way:
+// for n in name arg0ptr returning thread; do print record_synch_$n[0..record_synch_count-1]; done
+
+void record_synch(char* name, bool returning) {
+  if (record_synch_enabled) {
+    if (record_synch_count < RECORD_SYNCH_LIMIT) {
+      record_synch_name[record_synch_count] = name;
+      record_synch_returning[record_synch_count] = returning;
+      record_synch_thread[record_synch_count] = thr_self();
+      record_synch_arg0ptr[record_synch_count] = &name;
+      record_synch_count++;
+    }
+    // put more checking code here:
+    // ...
+  }
+}
+
+void record_synch_enable() {
+  // start collecting trace data, if not already doing so
+  if (!record_synch_enabled)  record_synch_count = 0;
+  record_synch_enabled = true;
+}
+
+void record_synch_disable() {
+  // stop collecting trace data
+  record_synch_enabled = false;
+}
+
+#endif // INTERPOSE_ON_SYSTEM_SYNCH_FUNCTIONS
+#endif // PRODUCT
+
+const intptr_t thr_time_off  = (intptr_t)(&((prusage_t *)(NULL))->pr_utime);
+const intptr_t thr_time_size = (intptr_t)(&((prusage_t *)(NULL))->pr_ttime) -
+                               (intptr_t)(&((prusage_t *)(NULL))->pr_utime);
+
+
+// JVMTI & JVM monitoring and management support
+// The thread_cpu_time() and current_thread_cpu_time() are only
+// supported if is_thread_cpu_time_supported() returns true.
+// They are not supported on Solaris T1.
+
+// current_thread_cpu_time(bool) and thread_cpu_time(Thread*, bool)
+// are used by JVM M&M and JVMTI to get user+sys or user CPU time
+// of a thread.
+//
+// current_thread_cpu_time() and thread_cpu_time(Thread *)
+// returns the fast estimate available on the platform.
+
+// hrtime_t gethrvtime() return value includes
+// user time but does not include system time
+jlong os::current_thread_cpu_time() {
+  return (jlong) gethrvtime();
+}
+
+jlong os::thread_cpu_time(Thread *thread) {
+  // return user level CPU time only to be consistent with
+  // what current_thread_cpu_time returns.
+  // thread_cpu_time_info() must be changed if this changes
+  return os::thread_cpu_time(thread, false /* user time only */);
+}
+
+jlong os::current_thread_cpu_time(bool user_sys_cpu_time) {
+  if (user_sys_cpu_time) {
+    return os::thread_cpu_time(Thread::current(), user_sys_cpu_time);
+  } else {
+    return os::current_thread_cpu_time();
+  }
+}
+
+jlong os::thread_cpu_time(Thread *thread, bool user_sys_cpu_time) {
+  char proc_name[64];
+  int count;
+  prusage_t prusage;
+  jlong lwp_time;
+  int fd;
+
+  sprintf(proc_name, "/proc/%d/lwp/%d/lwpusage",
+          getpid(),
+          thread->osthread()->lwp_id());
+  fd = ::open(proc_name, O_RDONLY);
+  if (fd == -1) return -1;
+
+  do {
+    count = ::pread(fd,
+                    (void *)&prusage.pr_utime,
+                    thr_time_size,
+                    thr_time_off);
+  } while (count < 0 && errno == EINTR);
+  ::close(fd);
+  if (count < 0) return -1;
+
+  if (user_sys_cpu_time) {
+    // user + system CPU time
+    lwp_time = (((jlong)prusage.pr_stime.tv_sec +
+                 (jlong)prusage.pr_utime.tv_sec) * (jlong)1000000000) +
+                 (jlong)prusage.pr_stime.tv_nsec +
+                 (jlong)prusage.pr_utime.tv_nsec;
+  } else {
+    // user level CPU time only
+    lwp_time = ((jlong)prusage.pr_utime.tv_sec * (jlong)1000000000) +
+                (jlong)prusage.pr_utime.tv_nsec;
+  }
+
+  return (lwp_time);
+}
+
+void os::current_thread_cpu_time_info(jvmtiTimerInfo *info_ptr) {
+  info_ptr->max_value = ALL_64_BITS;      // will not wrap in less than 64 bits
+  info_ptr->may_skip_backward = false;    // elapsed time not wall time
+  info_ptr->may_skip_forward = false;     // elapsed time not wall time
+  info_ptr->kind = JVMTI_TIMER_USER_CPU;  // only user time is returned
+}
+
+void os::thread_cpu_time_info(jvmtiTimerInfo *info_ptr) {
+  info_ptr->max_value = ALL_64_BITS;      // will not wrap in less than 64 bits
+  info_ptr->may_skip_backward = false;    // elapsed time not wall time
+  info_ptr->may_skip_forward = false;     // elapsed time not wall time
+  info_ptr->kind = JVMTI_TIMER_USER_CPU;  // only user time is returned
+}
+
+bool os::is_thread_cpu_time_supported() {
+  return true;
+}
+
+// System loadavg support.  Returns -1 if load average cannot be obtained.
+// Return the load average for our processor set if the primitive exists
+// (Solaris 9 and later).  Otherwise just return system wide loadavg.
+int os::loadavg(double loadavg[], int nelem) {
+  if (pset_getloadavg_ptr != NULL) {
+    return (*pset_getloadavg_ptr)(PS_MYID, loadavg, nelem);
+  } else {
+    return ::getloadavg(loadavg, nelem);
+  }
+}
+
+//---------------------------------------------------------------------------------
+
+bool os::find(address addr, outputStream* st) {
+  Dl_info dlinfo;
+  memset(&dlinfo, 0, sizeof(dlinfo));
+  if (dladdr(addr, &dlinfo) != 0) {
+    st->print(PTR_FORMAT ": ", p2i(addr));
+    if (dlinfo.dli_sname != NULL && dlinfo.dli_saddr != NULL) {
+      st->print("%s+" PTR_FORMAT, dlinfo.dli_sname,
+                p2i(addr) - p2i(dlinfo.dli_saddr));
+    } else if (dlinfo.dli_fbase != NULL) {
+      st->print("<offset " PTR_FORMAT ">", p2i(addr) - p2i(dlinfo.dli_fbase));
+    } else {
+      st->print("<absolute address>");
+    }
+    if (dlinfo.dli_fname != NULL) {
+      st->print(" in %s", dlinfo.dli_fname);
+    }
+    if (dlinfo.dli_fbase != NULL) {
+      st->print(" at " PTR_FORMAT, p2i(dlinfo.dli_fbase));
+    }
+    st->cr();
+
+    if (Verbose) {
+      // decode some bytes around the PC
+      address begin = clamp_address_in_page(addr-40, addr, os::vm_page_size());
+      address end   = clamp_address_in_page(addr+40, addr, os::vm_page_size());
+      address       lowest = (address) dlinfo.dli_sname;
+      if (!lowest)  lowest = (address) dlinfo.dli_fbase;
+      if (begin < lowest)  begin = lowest;
+      Dl_info dlinfo2;
+      if (dladdr(end, &dlinfo2) != 0 && dlinfo2.dli_saddr != dlinfo.dli_saddr
+          && end > dlinfo2.dli_saddr && dlinfo2.dli_saddr > begin) {
+        end = (address) dlinfo2.dli_saddr;
+      }
+      Disassembler::decode(begin, end, st);
+    }
+    return true;
+  }
+  return false;
+}
+
+// Following function has been added to support HotSparc's libjvm.so running
+// under Solaris production JDK 1.2.2 / 1.3.0.  These came from
+// src/solaris/hpi/native_threads in the EVM codebase.
+//
+// NOTE: This is no longer needed in the 1.3.1 and 1.4 production release
+// libraries and should thus be removed. We will leave it behind for a while
+// until we no longer want to able to run on top of 1.3.0 Solaris production
+// JDK. See 4341971.
+
+#define STACK_SLACK 0x800
+
+extern "C" {
+  intptr_t sysThreadAvailableStackWithSlack() {
+    stack_t st;
+    intptr_t retval, stack_top;
+    retval = thr_stksegment(&st);
+    assert(retval == 0, "incorrect return value from thr_stksegment");
+    assert((address)&st < (address)st.ss_sp, "Invalid stack base returned");
+    assert((address)&st > (address)st.ss_sp-st.ss_size, "Invalid stack size returned");
+    stack_top=(intptr_t)st.ss_sp-st.ss_size;
+    return ((intptr_t)&stack_top - stack_top - STACK_SLACK);
+  }
+}
+
+// ObjectMonitor park-unpark infrastructure ...
+//
+// We implement Solaris and Linux PlatformEvents with the
+// obvious condvar-mutex-flag triple.
+// Another alternative that works quite well is pipes:
+// Each PlatformEvent consists of a pipe-pair.
+// The thread associated with the PlatformEvent
+// calls park(), which reads from the input end of the pipe.
+// Unpark() writes into the other end of the pipe.
+// The write-side of the pipe must be set NDELAY.
+// Unfortunately pipes consume a large # of handles.
+// Native solaris lwp_park() and lwp_unpark() work nicely, too.
+// Using pipes for the 1st few threads might be workable, however.
+//
+// park() is permitted to return spuriously.
+// Callers of park() should wrap the call to park() in
+// an appropriate loop.  A litmus test for the correct
+// usage of park is the following: if park() were modified
+// to immediately return 0 your code should still work,
+// albeit degenerating to a spin loop.
+//
+// In a sense, park()-unpark() just provides more polite spinning
+// and polling with the key difference over naive spinning being
+// that a parked thread needs to be explicitly unparked() in order
+// to wake up and to poll the underlying condition.
+//
+// Assumption:
+//    Only one parker can exist on an event, which is why we allocate
+//    them per-thread. Multiple unparkers can coexist.
+//
+// _Event transitions in park()
+//   -1 => -1 : illegal
+//    1 =>  0 : pass - return immediately
+//    0 => -1 : block; then set _Event to 0 before returning
+//
+// _Event transitions in unpark()
+//    0 => 1 : just return
+//    1 => 1 : just return
+//   -1 => either 0 or 1; must signal target thread
+//         That is, we can safely transition _Event from -1 to either
+//         0 or 1.
+//
+// _Event serves as a restricted-range semaphore.
+//   -1 : thread is blocked, i.e. there is a waiter
+//    0 : neutral: thread is running or ready,
+//        could have been signaled after a wait started
+//    1 : signaled - thread is running or ready
+//
+// Another possible encoding of _Event would be with
+// explicit "PARKED" == 01b and "SIGNALED" == 10b bits.
+//
+// TODO-FIXME: add DTRACE probes for:
+// 1.   Tx parks
+// 2.   Ty unparks Tx
+// 3.   Tx resumes from park
+
+
+// utility to compute the abstime argument to timedwait.
+// TODO-FIXME: switch from compute_abstime() to unpackTime().
+
+static timestruc_t* compute_abstime(timestruc_t* abstime, jlong millis) {
+  // millis is the relative timeout time
+  // abstime will be the absolute timeout time
+  if (millis < 0)  millis = 0;
+  struct timeval now;
+  int status = gettimeofday(&now, NULL);
+  assert(status == 0, "gettimeofday");
+  jlong seconds = millis / 1000;
+  jlong max_wait_period;
+
+  max_wait_period = 50000000;
+
+  millis %= 1000;
+  if (seconds > max_wait_period) {      // see man cond_timedwait(3T)
+    seconds = max_wait_period;
+  }
+  abstime->tv_sec = now.tv_sec  + seconds;
+  long       usec = now.tv_usec + millis * 1000;
+  if (usec >= 1000000) {
+    abstime->tv_sec += 1;
+    usec -= 1000000;
+  }
+  abstime->tv_nsec = usec * 1000;
+  return abstime;
+}
+
+void os::PlatformEvent::park() {           // AKA: down()
+  // Transitions for _Event:
+  //   -1 => -1 : illegal
+  //    1 =>  0 : pass - return immediately
+  //    0 => -1 : block; then set _Event to 0 before returning
+
+  // Invariant: Only the thread associated with the Event/PlatformEvent
+  // may call park().
+  assert(_nParked == 0, "invariant");
+
+  int v;
+  for (;;) {
+    v = _Event;
+    if (Atomic::cmpxchg(&_Event, v, v-1) == v) break;
+  }
+  guarantee(v >= 0, "invariant");
+  if (v == 0) {
+    // Do this the hard way by blocking ...
+    // See http://monaco.sfbay/detail.jsf?cr=5094058.
+    int status = os::Solaris::mutex_lock(_mutex);
+    assert_status(status == 0, status, "mutex_lock");
+    guarantee(_nParked == 0, "invariant");
+    ++_nParked;
+    while (_Event < 0) {
+      // for some reason, under 2.7 lwp_cond_wait() may return ETIME ...
+      // Treat this the same as if the wait was interrupted
+      // With usr/lib/lwp going to kernel, always handle ETIME
+      status = os::Solaris::cond_wait(_cond, _mutex);
+      if (status == ETIME) status = EINTR;
+      assert_status(status == 0 || status == EINTR, status, "cond_wait");
+    }
+    --_nParked;
+    _Event = 0;
+    status = os::Solaris::mutex_unlock(_mutex);
+    assert_status(status == 0, status, "mutex_unlock");
+    // Paranoia to ensure our locked and lock-free paths interact
+    // correctly with each other.
+    OrderAccess::fence();
+  }
+}
+
+int os::PlatformEvent::park(jlong millis) {
+  // Transitions for _Event:
+  //   -1 => -1 : illegal
+  //    1 =>  0 : pass - return immediately
+  //    0 => -1 : block; then set _Event to 0 before returning
+
+  guarantee(_nParked == 0, "invariant");
+  int v;
+  for (;;) {
+    v = _Event;
+    if (Atomic::cmpxchg(&_Event, v, v-1) == v) break;
+  }
+  guarantee(v >= 0, "invariant");
+  if (v != 0) return OS_OK;
+
+  int ret = OS_TIMEOUT;
+  timestruc_t abst;
+  compute_abstime(&abst, millis);
+
+  // See http://monaco.sfbay/detail.jsf?cr=5094058.
+  int status = os::Solaris::mutex_lock(_mutex);
+  assert_status(status == 0, status, "mutex_lock");
+  guarantee(_nParked == 0, "invariant");
+  ++_nParked;
+  while (_Event < 0) {
+    int status = os::Solaris::cond_timedwait(_cond, _mutex, &abst);
+    assert_status(status == 0 || status == EINTR ||
+                  status == ETIME || status == ETIMEDOUT,
+                  status, "cond_timedwait");
+    if (!FilterSpuriousWakeups) break;                // previous semantics
+    if (status == ETIME || status == ETIMEDOUT) break;
+    // We consume and ignore EINTR and spurious wakeups.
+  }
+  --_nParked;
+  if (_Event >= 0) ret = OS_OK;
+  _Event = 0;
+  status = os::Solaris::mutex_unlock(_mutex);
+  assert_status(status == 0, status, "mutex_unlock");
+  // Paranoia to ensure our locked and lock-free paths interact
+  // correctly with each other.
+  OrderAccess::fence();
+  return ret;
+}
+
+void os::PlatformEvent::unpark() {
+  // Transitions for _Event:
+  //    0 => 1 : just return
+  //    1 => 1 : just return
+  //   -1 => either 0 or 1; must signal target thread
+  //         That is, we can safely transition _Event from -1 to either
+  //         0 or 1.
+  // See also: "Semaphores in Plan 9" by Mullender & Cox
+  //
+  // Note: Forcing a transition from "-1" to "1" on an unpark() means
+  // that it will take two back-to-back park() calls for the owning
+  // thread to block. This has the benefit of forcing a spurious return
+  // from the first park() call after an unpark() call which will help
+  // shake out uses of park() and unpark() without condition variables.
+
+  if (Atomic::xchg(&_Event, 1) >= 0) return;
+
+  // If the thread associated with the event was parked, wake it.
+  // Wait for the thread assoc with the PlatformEvent to vacate.
+  int status = os::Solaris::mutex_lock(_mutex);
+  assert_status(status == 0, status, "mutex_lock");
+  int AnyWaiters = _nParked;
+  status = os::Solaris::mutex_unlock(_mutex);
+  assert_status(status == 0, status, "mutex_unlock");
+  guarantee(AnyWaiters == 0 || AnyWaiters == 1, "invariant");
+  if (AnyWaiters != 0) {
+    // Note that we signal() *after* dropping the lock for "immortal" Events.
+    // This is safe and avoids a common class of  futile wakeups.  In rare
+    // circumstances this can cause a thread to return prematurely from
+    // cond_{timed}wait() but the spurious wakeup is benign and the victim
+    // will simply re-test the condition and re-park itself.
+    // This provides particular benefit if the underlying platform does not
+    // provide wait morphing.
+    status = os::Solaris::cond_signal(_cond);
+    assert_status(status == 0, status, "cond_signal");
+  }
+}
+
+// JSR166
+// -------------------------------------------------------
+
+os::PlatformParker::PlatformParker() : _counter(0) {
+  int status;
+  status = os::Solaris::cond_init(_cond);
+  assert_status(status == 0, status, "cond_init");
+  status = os::Solaris::mutex_init(_mutex);
+  assert_status(status == 0, status, "mutex_init");
+}
+
+os::PlatformParker::~PlatformParker() {
+  int status;
+  status = os::Solaris::cond_destroy(_cond);
+  assert_status(status == 0, status, "cond_destroy");
+  status = os::Solaris::mutex_destroy(_mutex);
+  assert_status(status == 0, status, "mutex_destroy");
+}
+
+// The solaris and linux implementations of park/unpark are fairly
+// conservative for now, but can be improved. They currently use a
+// mutex/condvar pair, plus _counter.
+// Park decrements _counter if > 0, else does a condvar wait.  Unpark
+// sets count to 1 and signals condvar.  Only one thread ever waits
+// on the condvar. Contention seen when trying to park implies that someone
+// is unparking you, so don't wait. And spurious returns are fine, so there
+// is no need to track notifications.
+
+#define MAX_SECS 100000000
+
+// This code is common to linux and solaris and will be moved to a
+// common place in dolphin.
+//
+// The passed in time value is either a relative time in nanoseconds
+// or an absolute time in milliseconds. Either way it has to be unpacked
+// into suitable seconds and nanoseconds components and stored in the
+// given timespec structure.
+// Given time is a 64-bit value and the time_t used in the timespec is only
+// a signed-32-bit value (except on 64-bit Linux) we have to watch for
+// overflow if times way in the future are given. Further on Solaris versions
+// prior to 10 there is a restriction (see cond_timedwait) that the specified
+// number of seconds, in abstime, is less than current_time  + 100,000,000.
+// As it will be 28 years before "now + 100000000" will overflow we can
+// ignore overflow and just impose a hard-limit on seconds using the value
+// of "now + 100,000,000". This places a limit on the timeout of about 3.17
+// years from "now".
+//
+static void unpackTime(timespec* absTime, bool isAbsolute, jlong time) {
+  assert(time > 0, "convertTime");
+
+  struct timeval now;
+  int status = gettimeofday(&now, NULL);
+  assert(status == 0, "gettimeofday");
+
+  time_t max_secs = now.tv_sec + MAX_SECS;
+
+  if (isAbsolute) {
+    jlong secs = time / 1000;
+    if (secs > max_secs) {
+      absTime->tv_sec = max_secs;
+    } else {
+      absTime->tv_sec = secs;
+    }
+    absTime->tv_nsec = (time % 1000) * NANOSECS_PER_MILLISEC;
+  } else {
+    jlong secs = time / NANOSECS_PER_SEC;
+    if (secs >= MAX_SECS) {
+      absTime->tv_sec = max_secs;
+      absTime->tv_nsec = 0;
+    } else {
+      absTime->tv_sec = now.tv_sec + secs;
+      absTime->tv_nsec = (time % NANOSECS_PER_SEC) + now.tv_usec*1000;
+      if (absTime->tv_nsec >= NANOSECS_PER_SEC) {
+        absTime->tv_nsec -= NANOSECS_PER_SEC;
+        ++absTime->tv_sec; // note: this must be <= max_secs
+      }
+    }
+  }
+  assert(absTime->tv_sec >= 0, "tv_sec < 0");
+  assert(absTime->tv_sec <= max_secs, "tv_sec > max_secs");
+  assert(absTime->tv_nsec >= 0, "tv_nsec < 0");
+  assert(absTime->tv_nsec < NANOSECS_PER_SEC, "tv_nsec >= nanos_per_sec");
+}
+
+void Parker::park(bool isAbsolute, jlong time) {
+  // Ideally we'd do something useful while spinning, such
+  // as calling unpackTime().
+
+  // Optional fast-path check:
+  // Return immediately if a permit is available.
+  // We depend on Atomic::xchg() having full barrier semantics
+  // since we are doing a lock-free update to _counter.
+  if (Atomic::xchg(&_counter, 0) > 0) return;
+
+  // Optional fast-exit: Check interrupt before trying to wait
+  Thread* thread = Thread::current();
+  assert(thread->is_Java_thread(), "Must be JavaThread");
+  JavaThread *jt = (JavaThread *)thread;
+  if (jt->is_interrupted(false)) {
+    return;
+  }
+
+  // First, demultiplex/decode time arguments
+  timespec absTime;
+  if (time < 0 || (isAbsolute && time == 0)) { // don't wait at all
+    return;
+  }
+  if (time > 0) {
+    // Warning: this code might be exposed to the old Solaris time
+    // round-down bugs.  Grep "roundingFix" for details.
+    unpackTime(&absTime, isAbsolute, time);
+  }
+
+  // Enter safepoint region
+  // Beware of deadlocks such as 6317397.
+  // The per-thread Parker:: _mutex is a classic leaf-lock.
+  // In particular a thread must never block on the Threads_lock while
+  // holding the Parker:: mutex.  If safepoints are pending both the
+  // the ThreadBlockInVM() CTOR and DTOR may grab Threads_lock.
+  ThreadBlockInVM tbivm(jt);
+
+  // Can't access interrupt state now that we are _thread_blocked. If we've
+  // been interrupted since we checked above then _counter will be > 0.
+
+  // Don't wait if cannot get lock since interference arises from
+  // unblocking.
+  if (os::Solaris::mutex_trylock(_mutex) != 0) {
+    return;
+  }
+
+  int status;
+
+  if (_counter > 0)  { // no wait needed
+    _counter = 0;
+    status = os::Solaris::mutex_unlock(_mutex);
+    assert(status == 0, "invariant");
+    // Paranoia to ensure our locked and lock-free paths interact
+    // correctly with each other and Java-level accesses.
+    OrderAccess::fence();
+    return;
+  }
+
+  OSThreadWaitState osts(thread->osthread(), false /* not Object.wait() */);
+
+  // Do this the hard way by blocking ...
+  // See http://monaco.sfbay/detail.jsf?cr=5094058.
+  if (time == 0) {
+    status = os::Solaris::cond_wait(_cond, _mutex);
+  } else {
+    status = os::Solaris::cond_timedwait (_cond, _mutex, &absTime);
+  }
+  // Note that an untimed cond_wait() can sometimes return ETIME on older
+  // versions of the Solaris.
+  assert_status(status == 0 || status == EINTR ||
+                status == ETIME || status == ETIMEDOUT,
+                status, "cond_timedwait");
+
+  _counter = 0;
+  status = os::Solaris::mutex_unlock(_mutex);
+  assert_status(status == 0, status, "mutex_unlock");
+  // Paranoia to ensure our locked and lock-free paths interact
+  // correctly with each other and Java-level accesses.
+  OrderAccess::fence();
+}
+
+void Parker::unpark() {
+  int status = os::Solaris::mutex_lock(_mutex);
+  assert(status == 0, "invariant");
+  const int s = _counter;
+  _counter = 1;
+  status = os::Solaris::mutex_unlock(_mutex);
+  assert(status == 0, "invariant");
+
+  if (s < 1) {
+    status = os::Solaris::cond_signal(_cond);
+    assert(status == 0, "invariant");
+  }
+}
+
+// Platform Mutex/Monitor implementations
+
+os::PlatformMutex::PlatformMutex() {
+  int status = os::Solaris::mutex_init(&_mutex);
+  assert_status(status == 0, status, "mutex_init");
+}
+
+os::PlatformMutex::~PlatformMutex() {
+  int status = os::Solaris::mutex_destroy(&_mutex);
+  assert_status(status == 0, status, "mutex_destroy");
+}
+
+void os::PlatformMutex::lock() {
+  int status = os::Solaris::mutex_lock(&_mutex);
+  assert_status(status == 0, status, "mutex_lock");
+}
+
+void os::PlatformMutex::unlock() {
+  int status = os::Solaris::mutex_unlock(&_mutex);
+  assert_status(status == 0, status, "mutex_unlock");
+}
+
+bool os::PlatformMutex::try_lock() {
+  int status = os::Solaris::mutex_trylock(&_mutex);
+  assert_status(status == 0 || status == EBUSY, status, "mutex_trylock");
+  return status == 0;
+}
+
+os::PlatformMonitor::PlatformMonitor() {
+  int status = os::Solaris::cond_init(&_cond);
+  assert_status(status == 0, status, "cond_init");
+}
+
+os::PlatformMonitor::~PlatformMonitor() {
+  int status = os::Solaris::cond_destroy(&_cond);
+  assert_status(status == 0, status, "cond_destroy");
+}
+
+// Must already be locked
+int os::PlatformMonitor::wait(jlong millis) {
+  assert(millis >= 0, "negative timeout");
+  if (millis > 0) {
+    timestruc_t abst;
+    int ret = OS_TIMEOUT;
+    compute_abstime(&abst, millis);
+    int status = os::Solaris::cond_timedwait(&_cond, &_mutex, &abst);
+    assert_status(status == 0 || status == EINTR ||
+                  status == ETIME || status == ETIMEDOUT,
+                  status, "cond_timedwait");
+    // EINTR acts as spurious wakeup - which is permitted anyway
+    if (status == 0 || status == EINTR) {
+      ret = OS_OK;
+    }
+    return ret;
+  } else {
+    int status = os::Solaris::cond_wait(&_cond, &_mutex);
+    assert_status(status == 0 || status == EINTR,
+                  status, "cond_wait");
+    return OS_OK;
+  }
+}
+
+void os::PlatformMonitor::notify() {
+  int status = os::Solaris::cond_signal(&_cond);
+  assert_status(status == 0, status, "cond_signal");
+}
+
+void os::PlatformMonitor::notify_all() {
+  int status = os::Solaris::cond_broadcast(&_cond);
+  assert_status(status == 0, status, "cond_broadcast");
+}
+
+// Get the default path to the core file
+// Returns the length of the string
+int os::get_core_path(char* buffer, size_t bufferSize) {
+  const char* p = get_current_directory(buffer, bufferSize);
+
+  if (p == NULL) {
+    assert(p != NULL, "failed to get current directory");
+    return 0;
+  }
+
+  jio_snprintf(buffer, bufferSize, "%s/core or core.%d",
+                                              p, current_process_id());
+
+  return strlen(buffer);
+}
+
+bool os::supports_map_sync() {
+  return false;
+}
+
+#ifndef PRODUCT
+void TestReserveMemorySpecial_test() {
+  // No tests available for this platform
+}
+#endif
+
+bool os::start_debugging(char *buf, int buflen) {
+  int len = (int)strlen(buf);
+  char *p = &buf[len];
+
+  jio_snprintf(p, buflen-len,
+               "\n\n"
+               "Do you want to debug the problem?\n\n"
+               "To debug, run 'dbx - %d'; then switch to thread " INTX_FORMAT "\n"
+               "Enter 'yes' to launch dbx automatically (PATH must include dbx)\n"
+               "Otherwise, press RETURN to abort...",
+               os::current_process_id(), os::current_thread_id());
+
+  bool yes = os::message_box("Unexpected Error", buf);
+
+  if (yes) {
+    // yes, user asked VM to launch debugger
+    jio_snprintf(buf, sizeof(buf), "dbx - %d", os::current_process_id());
+
+    os::fork_and_exec(buf);
+    yes = false;
+  }
+  return yes;
+}
+
+void os::print_memory_mappings(char* addr, size_t bytes, outputStream* st) {}
diff -urN /tmp/a/os_solaris.hpp b/src/hotspot/os/solaris/os_solaris.hpp
--- /tmp/a/os_solaris.hpp	1970-01-01 01:00:00.000000000 +0100
+++ b/src/hotspot/os/solaris/os_solaris.hpp	2024-12-05 11:44:56.897304885 +0000
@@ -0,0 +1,320 @@
+/*
+ * Copyright (c) 1997, 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.
+ *
+ * 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.
+ *
+ */
+
+#ifndef OS_SOLARIS_OS_SOLARIS_HPP
+#define OS_SOLARIS_OS_SOLARIS_HPP
+
+// Solaris_OS defines the interface to Solaris operating systems
+
+// see thr_setprio(3T) for the basis of these numbers
+#define MinimumPriority 0
+#define NormalPriority  64
+#define MaximumPriority 127
+
+// FX/60 is critical thread class/priority on T4
+#define FXCriticalPriority 60
+
+// Information about the protection of the page at address '0' on this os.
+static bool zero_page_read_protected() { return true; }
+
+class Solaris {
+  friend class os;
+
+ private:
+
+  // Support for "new" libthread APIs for getting & setting thread context (2.8)
+#define TRS_VALID       0
+#define TRS_NONVOLATILE 1
+#define TRS_LWPID       2
+#define TRS_INVALID     3
+
+  // initialized to libthread or lwp synchronization primitives depending on UseLWPSychronization
+  static int_fnP_mutex_tP _mutex_lock;
+  static int_fnP_mutex_tP _mutex_trylock;
+  static int_fnP_mutex_tP _mutex_unlock;
+  static int_fnP_mutex_tP_i_vP _mutex_init;
+  static int_fnP_mutex_tP _mutex_destroy;
+  static int _mutex_scope;
+
+  static int_fnP_cond_tP_mutex_tP_timestruc_tP _cond_timedwait;
+  static int_fnP_cond_tP_mutex_tP _cond_wait;
+  static int_fnP_cond_tP _cond_signal;
+  static int_fnP_cond_tP _cond_broadcast;
+  static int_fnP_cond_tP_i_vP _cond_init;
+  static int_fnP_cond_tP _cond_destroy;
+  static int _cond_scope;
+
+  static bool _synchronization_initialized;
+
+  typedef uintptr_t       lgrp_cookie_t;
+  typedef id_t            lgrp_id_t;
+  typedef int             lgrp_rsrc_t;
+  typedef enum lgrp_view {
+    LGRP_VIEW_CALLER,       // what's available to the caller
+    LGRP_VIEW_OS            // what's available to operating system
+  } lgrp_view_t;
+
+  typedef lgrp_id_t (*lgrp_home_func_t)(idtype_t idtype, id_t id);
+  typedef lgrp_cookie_t (*lgrp_init_func_t)(lgrp_view_t view);
+  typedef int (*lgrp_fini_func_t)(lgrp_cookie_t cookie);
+  typedef lgrp_id_t (*lgrp_root_func_t)(lgrp_cookie_t cookie);
+  typedef int (*lgrp_children_func_t)(lgrp_cookie_t  cookie,  lgrp_id_t  parent,
+                                      lgrp_id_t *lgrp_array, uint_t lgrp_array_size);
+  typedef int (*lgrp_resources_func_t)(lgrp_cookie_t  cookie,  lgrp_id_t  lgrp,
+                                       lgrp_id_t *lgrp_array, uint_t lgrp_array_size,
+                                       lgrp_rsrc_t type);
+  typedef int (*lgrp_nlgrps_func_t)(lgrp_cookie_t cookie);
+  typedef int (*lgrp_cookie_stale_func_t)(lgrp_cookie_t cookie);
+
+  static lgrp_home_func_t _lgrp_home;
+  static lgrp_init_func_t _lgrp_init;
+  static lgrp_fini_func_t _lgrp_fini;
+  static lgrp_root_func_t _lgrp_root;
+  static lgrp_children_func_t _lgrp_children;
+  static lgrp_resources_func_t _lgrp_resources;
+  static lgrp_nlgrps_func_t _lgrp_nlgrps;
+  static lgrp_cookie_stale_func_t _lgrp_cookie_stale;
+  static lgrp_cookie_t _lgrp_cookie;
+
+  // Large Page Support
+  static bool is_valid_page_size(size_t bytes);
+  static size_t page_size_for_alignment(size_t alignment);
+  static bool setup_large_pages(caddr_t start, size_t bytes, size_t align);
+
+  typedef int (*pthread_setname_np_func_t)(pthread_t, const char*);
+  static pthread_setname_np_func_t _pthread_setname_np;
+
+ public:
+  // Large Page Support--ISM.
+  static bool largepage_range(char* addr, size_t size);
+
+  static address handler_start, handler_end; // start and end pc of thr_sighndlrinfo
+
+  static bool valid_ucontext(Thread* thread, const ucontext_t* valid, const ucontext_t* suspect);
+  static const ucontext_t* get_valid_uc_in_signal_handler(Thread* thread,
+                                                    const ucontext_t* uc);
+
+  static intptr_t*   ucontext_get_sp(const ucontext_t* uc);
+  // ucontext_get_fp() is only used by Solaris X86 (see note below)
+  static intptr_t*   ucontext_get_fp(const ucontext_t* uc);
+
+  static bool get_frame_at_stack_banging_point(JavaThread* thread, ucontext_t* uc, frame* fr);
+
+  static void init_thread_fpu_state(void);
+
+ protected:
+  static int _page_size;
+  // Solaris-specific interface goes here
+  static julong available_memory();
+  static julong physical_memory() { return _physical_memory; }
+  static julong _physical_memory;
+  static void initialize_system_info();
+  static int _dev_zero_fd;
+  static int get_dev_zero_fd() { return _dev_zero_fd; }
+  static void set_dev_zero_fd(int fd) { _dev_zero_fd = fd; }
+  static int commit_memory_impl(char* addr, size_t bytes, bool exec);
+  static int commit_memory_impl(char* addr, size_t bytes,
+                                size_t alignment_hint, bool exec);
+  static char* mmap_chunk(char *addr, size_t size, int flags, int prot);
+  static char* anon_mmap(char* requested_addr, size_t bytes);
+  static bool mpss_sanity_check(bool warn, size_t * page_size);
+
+  // Workaround for 4352906. thr_stksegment sometimes returns
+  // a bad value for the primordial thread's stack base when
+  // it is called more than one time.
+  // Workaround is to cache the initial value to avoid further
+  // calls to thr_stksegment.
+  // It appears that someone (Hotspot?) is trashing the user's
+  // proc_t structure (note that this is a system struct).
+  static address _main_stack_base;
+
+  static void print_distro_info(outputStream* st);
+  static void print_libversion_info(outputStream* st);
+
+ public:
+  static int page_size(void)                                        { return _page_size; }
+  static void set_page_size(int val)                                { _page_size = val; }
+
+  static void libthread_init();
+  static void synchronization_init();
+  static bool liblgrp_init();
+
+  // Allows us to switch between lwp and thread -based synchronization
+  static int mutex_lock(mutex_t *mx)    { return _mutex_lock(mx); }
+  static int mutex_trylock(mutex_t *mx) { return _mutex_trylock(mx); }
+  static int mutex_unlock(mutex_t *mx)  { return _mutex_unlock(mx); }
+  static int mutex_init(mutex_t *mx)    { return _mutex_init(mx, os::Solaris::mutex_scope(), NULL); }
+  static int mutex_destroy(mutex_t *mx) { return _mutex_destroy(mx); }
+  static int mutex_scope()              { return _mutex_scope; }
+
+  static void set_mutex_lock(int_fnP_mutex_tP func)      { _mutex_lock = func; }
+  static void set_mutex_trylock(int_fnP_mutex_tP func)   { _mutex_trylock = func; }
+  static void set_mutex_unlock(int_fnP_mutex_tP func)    { _mutex_unlock = func; }
+  static void set_mutex_init(int_fnP_mutex_tP_i_vP func) { _mutex_init = func; }
+  static void set_mutex_destroy(int_fnP_mutex_tP func)   { _mutex_destroy = func; }
+  static void set_mutex_scope(int scope)                 { _mutex_scope = scope; }
+
+  static int cond_timedwait(cond_t *cv, mutex_t *mx, timestruc_t *abst) { return _cond_timedwait(cv, mx, abst); }
+  static int cond_wait(cond_t *cv, mutex_t *mx) { return _cond_wait(cv, mx); }
+  static int cond_signal(cond_t *cv)            { return _cond_signal(cv); }
+  static int cond_broadcast(cond_t *cv)         { return _cond_broadcast(cv); }
+  static int cond_init(cond_t *cv)              { return _cond_init(cv, os::Solaris::cond_scope(), NULL); }
+  static int cond_destroy(cond_t *cv)           { return _cond_destroy(cv); }
+  static int cond_scope()                       { return _cond_scope; }
+
+  static void set_cond_timedwait(int_fnP_cond_tP_mutex_tP_timestruc_tP func) { _cond_timedwait = func; }
+  static void set_cond_wait(int_fnP_cond_tP_mutex_tP func) { _cond_wait = func; }
+  static void set_cond_signal(int_fnP_cond_tP func)        { _cond_signal = func; }
+  static void set_cond_broadcast(int_fnP_cond_tP func)     { _cond_broadcast = func; }
+  static void set_cond_init(int_fnP_cond_tP_i_vP func)     { _cond_init = func; }
+  static void set_cond_destroy(int_fnP_cond_tP func)       { _cond_destroy = func; }
+  static void set_cond_scope(int scope)                    { _cond_scope = scope; }
+
+  static bool synchronization_initialized()                { return _synchronization_initialized; }
+
+  static void set_lgrp_home(lgrp_home_func_t func) { _lgrp_home = func; }
+  static void set_lgrp_init(lgrp_init_func_t func) { _lgrp_init = func; }
+  static void set_lgrp_fini(lgrp_fini_func_t func) { _lgrp_fini = func; }
+  static void set_lgrp_root(lgrp_root_func_t func) { _lgrp_root = func; }
+  static void set_lgrp_children(lgrp_children_func_t func)   { _lgrp_children = func; }
+  static void set_lgrp_resources(lgrp_resources_func_t func) { _lgrp_resources = func; }
+  static void set_lgrp_nlgrps(lgrp_nlgrps_func_t func)       { _lgrp_nlgrps = func; }
+  static void set_lgrp_cookie_stale(lgrp_cookie_stale_func_t func) { _lgrp_cookie_stale = func; }
+  static void set_lgrp_cookie(lgrp_cookie_t cookie)  { _lgrp_cookie = cookie; }
+
+  static id_t lgrp_home(idtype_t type, id_t id)      { return _lgrp_home != NULL ? _lgrp_home(type, id) : -1; }
+  static lgrp_cookie_t lgrp_init(lgrp_view_t view)   { return _lgrp_init != NULL ? _lgrp_init(view) : 0; }
+  static int lgrp_fini(lgrp_cookie_t cookie)         { return _lgrp_fini != NULL ? _lgrp_fini(cookie) : -1; }
+  static lgrp_id_t lgrp_root(lgrp_cookie_t cookie)   { return _lgrp_root != NULL ? _lgrp_root(cookie) : -1; }
+  static int lgrp_children(lgrp_cookie_t  cookie,  lgrp_id_t  parent,
+                           lgrp_id_t *lgrp_array, uint_t lgrp_array_size) {
+    return _lgrp_children != NULL ? _lgrp_children(cookie, parent, lgrp_array, lgrp_array_size) : -1;
+  }
+  static int lgrp_resources(lgrp_cookie_t  cookie,  lgrp_id_t  lgrp,
+                            lgrp_id_t *lgrp_array, uint_t lgrp_array_size,
+                            lgrp_rsrc_t type) {
+    return _lgrp_resources != NULL ? _lgrp_resources(cookie, lgrp, lgrp_array, lgrp_array_size, type) : -1;
+  }
+
+  static int lgrp_nlgrps(lgrp_cookie_t cookie)       { return _lgrp_nlgrps != NULL ? _lgrp_nlgrps(cookie) : -1; }
+  static int lgrp_cookie_stale(lgrp_cookie_t cookie) {
+    return _lgrp_cookie_stale != NULL ? _lgrp_cookie_stale(cookie) : -1;
+  }
+  static lgrp_cookie_t lgrp_cookie()                 { return _lgrp_cookie; }
+
+  static sigset_t* unblocked_signals();
+  static sigset_t* vm_signals();
+
+  // %%% Following should be promoted to os.hpp:
+  // Trace number of created threads
+  static          jint  _os_thread_limit;
+  static volatile jint  _os_thread_count;
+
+  static void correct_stack_boundaries_for_primordial_thread(Thread* thr);
+
+  // Stack repair handling
+
+  // none present
+
+};
+
+class PlatformEvent : public CHeapObj<mtSynchronizer> {
+ private:
+  double CachePad[4];   // increase odds that _mutex is sole occupant of cache line
+  volatile int _Event;
+  int _nParked;
+  int _pipev[2];
+  mutex_t _mutex[1];
+  cond_t  _cond[1];
+  double PostPad[2];
+
+ protected:
+  // Defining a protected ctor effectively gives us an abstract base class.
+  // That is, a PlatformEvent can never be instantiated "naked" but only
+  // as a part of a ParkEvent (recall that ParkEvent extends PlatformEvent).
+  // TODO-FIXME: make dtor private
+  ~PlatformEvent() { guarantee(0, "invariant"); }
+  PlatformEvent() {
+    int status;
+    status = os::Solaris::cond_init(_cond);
+    assert_status(status == 0, status, "cond_init");
+    status = os::Solaris::mutex_init(_mutex);
+    assert_status(status == 0, status, "mutex_init");
+    _Event   = 0;
+    _nParked = 0;
+    _pipev[0] = _pipev[1] = -1;
+  }
+
+ public:
+  // Exercise caution using reset() and fired() -- they may require MEMBARs
+  void reset() { _Event = 0; }
+  int  fired() { return _Event; }
+  void park();
+  int  park(jlong millis);
+  void unpark();
+};
+
+class PlatformParker {
+  NONCOPYABLE(PlatformParker);
+ protected:
+  volatile int _counter;
+  mutex_t _mutex[1];
+  cond_t  _cond[1];
+
+ public:
+  PlatformParker();
+  ~PlatformParker();
+};
+
+// Platform specific implementations that underpin VM Mutex/Monitor classes
+
+class PlatformMutex : public CHeapObj<mtSynchronizer> {
+  NONCOPYABLE(PlatformMutex);
+
+ protected:
+  mutex_t _mutex; // Native mutex for locking
+
+ public:
+  PlatformMutex();
+  ~PlatformMutex();
+  void lock();
+  void unlock();
+  bool try_lock();
+};
+
+class PlatformMonitor : public PlatformMutex {
+ private:
+  cond_t  _cond;  // Native condition variable for blocking
+
+  NONCOPYABLE(PlatformMonitor);
+
+ public:
+  PlatformMonitor();
+  ~PlatformMonitor();
+  int wait(jlong millis);
+  void notify();
+  void notify_all();
+};
+
+#endif // OS_SOLARIS_OS_SOLARIS_HPP
diff -urN /tmp/a/os_solaris.inline.hpp b/src/hotspot/os/solaris/os_solaris.inline.hpp
--- /tmp/a/os_solaris.inline.hpp	1970-01-01 01:00:00.000000000 +0100
+++ b/src/hotspot/os/solaris/os_solaris.inline.hpp	2024-12-05 11:44:56.841889121 +0000
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 1997, 2019, 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.
+ *
+ * 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.
+ *
+ */
+
+#ifndef OS_SOLARIS_OS_SOLARIS_INLINE_HPP
+#define OS_SOLARIS_OS_SOLARIS_INLINE_HPP
+
+#include "runtime/os.hpp"
+#include "os_posix.inline.hpp"
+
+// System includes
+#include <sys/param.h>
+#include <dlfcn.h>
+#include <sys/socket.h>
+#include <poll.h>
+#include <sys/filio.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <setjmp.h>
+
+inline bool os::uses_stack_guard_pages() {
+  return true;
+}
+
+inline bool os::must_commit_stack_guard_pages() {
+  assert(uses_stack_guard_pages(), "sanity check");
+  int r = thr_main() ;
+  guarantee (r == 0 || r == 1, "CR6501650 or CR6493689") ;
+  return r;
+}
+
+
+// Bang the shadow pages if they need to be touched to be mapped.
+inline void os::map_stack_shadow_pages(address sp) {
+}
+
+// Trim-native support, stubbed out for now, may be enabled later
+inline bool os::can_trim_native_heap() { return false; }
+inline bool os::trim_native_heap(os::size_change_t* rss_change) { return false; }
+
+//////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+inline bool os::numa_has_static_binding()   { return false; }
+inline bool os::numa_has_group_homing()     { return true;  }
+
+#endif // OS_SOLARIS_OS_SOLARIS_INLINE_HPP
diff -urN /tmp/a/threadCritical_solaris.cpp b/src/hotspot/os/solaris/threadCritical_solaris.cpp
--- /tmp/a/threadCritical_solaris.cpp	1970-01-01 01:00:00.000000000 +0100
+++ b/src/hotspot/os/solaris/threadCritical_solaris.cpp	2024-12-05 11:44:56.841971627 +0000
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2001, 2016, 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.
+ *
+ * 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 "precompiled.hpp"
+#include "runtime/os.hpp"
+#include "runtime/thread.inline.hpp"
+#include "runtime/threadCritical.hpp"
+
+// OS-includes here
+#include <thread.h>
+#include <synch.h>
+
+//
+// See threadCritical.hpp for details of this class.
+//
+// For some reason, we don't do locking until the
+// os::init() call completes. I'm not sure why this
+// is, and have left it that way for now. This should
+// be reviewed later.
+
+static  mutex_t  global_mut;
+static  thread_t global_mut_owner = -1;
+static  int      global_mut_count = 0;
+
+ThreadCritical::ThreadCritical() {
+  if (os::Solaris::synchronization_initialized()) {
+    thread_t owner = thr_self();
+    if (global_mut_owner != owner) {
+      if (os::Solaris::mutex_lock(&global_mut))
+        fatal("ThreadCritical::ThreadCritical: mutex_lock failed (%s)",
+              os::strerror(errno));
+      assert(global_mut_count == 0, "must have clean count");
+      assert(global_mut_owner == -1, "must have clean owner");
+    }
+    global_mut_owner = owner;
+    ++global_mut_count;
+  } else {
+    assert (Threads::number_of_threads() == 0, "valid only during initialization");
+  }
+}
+
+ThreadCritical::~ThreadCritical() {
+  if (os::Solaris::synchronization_initialized()) {
+    assert(global_mut_owner == thr_self(), "must have correct owner");
+    assert(global_mut_count > 0, "must have correct count");
+    --global_mut_count;
+    if (global_mut_count == 0) {
+      global_mut_owner = -1;
+      if (os::Solaris::mutex_unlock(&global_mut))
+        fatal("ThreadCritical::~ThreadCritical: mutex_unlock failed (%s)", os::strerror(errno));
+    }
+  } else {
+    assert (Threads::number_of_threads() == 0, "valid only during initialization");
+  }
+}
diff -urN /tmp/a/vmStructs_solaris.hpp b/src/hotspot/os/solaris/vmStructs_solaris.hpp
--- /tmp/a/vmStructs_solaris.hpp	1970-01-01 01:00:00.000000000 +0100
+++ b/src/hotspot/os/solaris/vmStructs_solaris.hpp	2024-12-05 11:44:56.842049188 +0000
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2015, 2019, 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.
+ *
+ * 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.
+ *
+ */
+
+#ifndef OS_SOLARIS_VMSTRUCTS_SOLARIS_HPP
+#define OS_SOLARIS_VMSTRUCTS_SOLARIS_HPP
+
+// These are the OS-specific fields, types and integer
+// constants required by the Serviceability Agent. This file is
+// referenced by vmStructs.cpp.
+
+#define VM_STRUCTS_OS(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) \
+  nonstatic_field(OSThread, _thread_id, OSThread::thread_id_t)
+
+#define VM_TYPES_OS(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) \
+  declare_unsigned_integer_type(OSThread::thread_id_t)
+
+#define VM_INT_CONSTANTS_OS(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant)
+
+#define VM_LONG_CONSTANTS_OS(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant)
+
+#define VM_ADDRESSES_OS(declare_address, declare_preprocessor_address, declare_function)
+
+#endif // OS_SOLARIS_VMSTRUCTS_SOLARIS_HPP
