guest_thread: Create, Destroy, Alloc functions

Implements basic GuestThread member functions.

Bug: 280551726
Test: m berberis_all
Change-Id: I126e954e774a328092f3bf8f9af35e78cadd71a4
diff --git a/guest_os_primitives/Android.bp b/guest_os_primitives/Android.bp
index de80708..4bf2fe5 100644
--- a/guest_os_primitives/Android.bp
+++ b/guest_os_primitives/Android.bp
@@ -34,6 +34,7 @@
     ],
 }
 
+// Common sources to be shared across arch-specific libraries.
 cc_library_static {
     name: "libberberis_guest_os_primitives",
     defaults: ["berberis_defaults"],
@@ -50,8 +51,13 @@
         "libberberis_guest_state_headers",
         "libberberis_instrument_headers",
         "libberberis_runtime_primitives_headers",
+        "//frameworks/libs/native_bridge_support:native_bridge_guest_linker_headers",
     ],
     export_header_lib_headers: ["libberberis_guest_os_primitives_headers"],
+    include_dirs: [
+        // private/bionic_constants.h
+        "bionic/libc",
+    ],
 }
 
 cc_test_library {
diff --git a/guest_os_primitives/guest_thread.cc b/guest_os_primitives/guest_thread.cc
index f5ada87..de8ce24 100644
--- a/guest_os_primitives/guest_thread.cc
+++ b/guest_os_primitives/guest_thread.cc
@@ -15,23 +15,166 @@
  */
 
 #include "berberis/guest_os_primitives/guest_thread.h"
-#include "berberis/base/macros.h"
+
+#include <sys/mman.h>  // mprotect
+
+#if defined(__BIONIC__)
+#include "private/bionic_constants.h"
+#endif
+
+#include "berberis/base/mmap.h"
+#include "berberis/base/tracing.h"
+#include "berberis/guest_state/guest_addr.h"  // ToGuestAddr
+#include "berberis/runtime_primitives/host_stack.h"
+#include "native_bridge_support/linker/static_tls_config.h"
 
 namespace berberis {
 
+NativeBridgeStaticTlsConfig g_static_tls_config;
+
+namespace {
+
+constexpr size_t kGuestThreadPageAlignedSize = AlignUpPageSize(sizeof(GuestThread));
+
+}  // namespace
+
+// static
+GuestThread* GuestThread::Create() {
+  // ATTENTION: GuestThread is aligned on 16, as fp registers in CPUState are aligned on 16, for
+  // efficient handling with aligned SSE memory access instructions. Thus, avoid using 'new', as
+  // it might not honor alignment! See b/64554026.
+  //
+  // ATTENTION: Bionic allocates thread internal data together with thread stack.
+  // In case of user provided stack, thread internal data goes there.
+  void* thread_storage = Mmap(kGuestThreadPageAlignedSize);
+  if (thread_storage == MAP_FAILED) {
+    return nullptr;
+  }
+
+  GuestThread* thread = new (thread_storage) GuestThread;
+  CHECK(thread);
+
+  // TODO(b/281864904): Implement.
+  // InitThreadState(&thread->state_);
+  thread->state_.thread = thread;
+
+  return thread;
+}
+
+// static
 GuestThread* GuestThread::CreatePthread(void* stack, size_t stack_size, size_t guard_size) {
+  GuestThread* thread = Create();
+  if (thread == nullptr) {
+    return nullptr;
+  }
+
+  if (!thread->AllocStack(stack, stack_size, guard_size)) {
+    Destroy(thread);
+    return nullptr;
+  }
   // TODO(b/280551726): Implement.
-  UNUSED(stack, stack_size, guard_size);
-  return nullptr;
+  // SetStackRegister(&thread->state_.cpu, thread->stack_top_);
+
+  // TODO(b/281859262): Implement shadow call stack initialization.
+
+  // Static TLS must be in an independent mapping, because on creation of main thread its config
+  // is yet unknown. Loader sets main thread's static TLS explicitly later.
+  if (!thread->AllocStaticTls()) {
+    Destroy(thread);
+    return nullptr;
+  }
+
+  return thread;
 }
 
+// static
 void GuestThread::Destroy(GuestThread* thread) {
-  // TODO(b/280551726): Implement.
-  UNUSED(thread);
+  // ATTENTION: Don't run guest code from here!
+  if (ArePendingSignalsPresent(&(thread->state_))) {
+    TRACE("thread destroyed with pending signals, signals ignored!");
+  }
+
+  if (thread->host_stack_) {
+    // This happens only on cleanup after failed creation.
+    MunmapOrDie(thread->host_stack_, GetStackSizeForTranslation());
+  }
+  if (thread->mmap_size_) {
+    MunmapOrDie(thread->stack_, thread->mmap_size_);
+  }
+#if defined(__BIONIC__)
+  if (thread->static_tls_ != nullptr) {
+    MunmapOrDie(thread->static_tls_, AlignUpPageSize(g_static_tls_config.size));
+  }
+  if (thread->scs_region_ != nullptr) {
+    MunmapOrDie(thread->scs_region_, SCS_GUARD_REGION_SIZE);
+  }
+#endif  // defined(__BIONIC__)
+  MunmapOrDie(thread, kGuestThreadPageAlignedSize);
 }
 
-void GuestThread::InitStaticTls() {
-  // TODO(b/280551726): Implement.
+bool GuestThread::AllocStack(void* stack, size_t stack_size, size_t guard_size) {
+  // Here is what bionic does, see bionic/pthread_create.cpp:
+  //
+  // For user-provided stack, it assumes guard_size is included in stack size.
+  //
+  // For new stack, it adds given guard and stack sizes to get actual stack size:
+  //   |<- guard_size ->|<- stack_size -------------------->|
+  //   | guard          | stack        | pthread_internal_t | tls | GUARD |
+  //   |<- actual stack_size --------->|
+  //   ^ stack_base                    ^ stack_top
+
+  if (stack) {
+    // User-provided stack.
+    stack_ = nullptr;  // Do not unmap in Destroy!
+    mmap_size_ = 0;
+    guard_size_ = guard_size;
+    stack_size_ = stack_size;
+    stack_top_ = ToGuestAddr(stack) + stack_size_;
+    return true;
+  }
+
+  guard_size_ = AlignUpPageSize(guard_size);
+  mmap_size_ = guard_size_ + AlignUpPageSize(stack_size);
+  stack_size_ = mmap_size_;
+
+  stack_ = Mmap(mmap_size_);
+  if (stack_ == MAP_FAILED) {
+    TRACE("failed to allocate stack!");
+    stack_ = nullptr;  // Do not unmap in Destroy!
+    return false;
+  }
+
+  if (mprotect(stack_, guard_size_, PROT_NONE) != 0) {
+    TRACE("failed to protect stack!");
+    return false;
+  }
+
+  stack_top_ = ToGuestAddr(stack_) + stack_size_ - 16;
+  return true;
+}
+
+bool GuestThread::AllocShadowCallStack() {
+  // TODO(b/281859262): Implement.
+  return true;
+}
+
+bool GuestThread::AllocStaticTls() {
+  // For the main thread, this function is called twice.
+
+  CHECK_EQ(nullptr, static_tls_);
+
+#if defined(__BIONIC__)
+  if (g_static_tls_config.size > 0) {
+    static_tls_ = Mmap(AlignUpPageSize(g_static_tls_config.size));
+    if (static_tls_ == MAP_FAILED) {
+      TRACE("failed to allocate static tls!");
+      static_tls_ = nullptr;  // Do not unmap in Destroy!
+      return false;
+    }
+  }
+#endif  // defined(__BIONIC__)
+
+  return true;
 }
 
 }  // namespace berberis
\ No newline at end of file
diff --git a/guest_os_primitives/include/berberis/guest_os_primitives/guest_thread.h b/guest_os_primitives/include/berberis/guest_os_primitives/guest_thread.h
index 61fd0d9..afbcd02 100644
--- a/guest_os_primitives/include/berberis/guest_os_primitives/guest_thread.h
+++ b/guest_os_primitives/include/berberis/guest_os_primitives/guest_thread.h
@@ -46,9 +46,13 @@
   ThreadState* state() { return &state_; }
 
  private:
-  GuestThread() {}
+  GuestThread() = default;
   static GuestThread* Create();
 
+  bool AllocStack(void* stack, size_t stack_size, size_t guard_size);
+  bool AllocShadowCallStack();
+  bool AllocStaticTls();
+
   // Host stack. Valid for cloned threads only.
   void* host_stack_ = nullptr;
 
diff --git a/guest_state/include/berberis/guest_state/guest_state_riscv64.h b/guest_state/include/berberis/guest_state/guest_state_riscv64.h
index e4f2e60..f7fd01f 100644
--- a/guest_state/include/berberis/guest_state/guest_state_riscv64.h
+++ b/guest_state/include/berberis/guest_state/guest_state_riscv64.h
@@ -137,6 +137,11 @@
   void* instrument_data;
 };
 
+// TODO(b/28058920): Refactor into GuestThread.
+inline bool ArePendingSignalsPresent(const ThreadState* state) {
+  return state->pending_signals_status.load(std::memory_order_relaxed) == kPendingSignalsPresent;
+}
+
 // The ABI names come from
 // https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-cc.adoc.