aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp3
-rw-r--r--code_gen_lib/code_gen_lib_riscv64_test.cc177
-rw-r--r--code_gen_lib/gen_wrapper_riscv64_to_x86_64.cc39
-rw-r--r--guest_abi/guest_function_wrapper_signature_test.cc6
-rw-r--r--guest_abi/include/berberis/guest_abi/guest_function_wrapper_signature.h45
-rw-r--r--guest_os_primitives/guest_signal_handling.cc16
-rw-r--r--guest_os_primitives/guest_thread.cc14
-rw-r--r--guest_os_primitives/guest_thread_clone.cc9
-rw-r--r--guest_os_primitives/include/berberis/guest_os_primitives/guest_thread.h13
-rw-r--r--guest_state/riscv64/get_cpu_state.cc22
-rw-r--r--jni/jni_trampolines.cc20
-rw-r--r--runtime_primitives/interpret_helpers_riscv64.cc13
-rw-r--r--tests/ndk_program_tests/Android.bp1
-rw-r--r--tests/ndk_program_tests/clone_test.cc141
14 files changed, 453 insertions, 66 deletions
diff --git a/Android.bp b/Android.bp
index c081cedb..7b81b549 100644
--- a/Android.bp
+++ b/Android.bp
@@ -150,7 +150,6 @@ cc_library_shared {
"libberberis_backend_riscv64_to_x86_64",
"libberberis_code_gen_lib_riscv64",
"libberberis_guest_abi_riscv64",
- "libberberis_guest_state_riscv64",
"libberberis_heavy_optimizer_riscv64",
"libberberis_interpreter_riscv64",
"libberberis_kernel_api_riscv64",
@@ -164,6 +163,8 @@ cc_library_shared {
// Proxy libraries reference symbols from guest_os_primitives,
// so we need to keep them all.
"libberberis_guest_os_primitives_riscv64",
+ // Android debuggerd reference symbols from get_cpu_state.
+ "libberberis_guest_state_riscv64",
"libberberis_runtime_riscv64_to_x86_64",
],
export_static_lib_headers: [
diff --git a/code_gen_lib/code_gen_lib_riscv64_test.cc b/code_gen_lib/code_gen_lib_riscv64_test.cc
index db81612c..789cf5fe 100644
--- a/code_gen_lib/code_gen_lib_riscv64_test.cc
+++ b/code_gen_lib/code_gen_lib_riscv64_test.cc
@@ -147,24 +147,163 @@ TEST(CodeGenLib, GenWrapGuestFunction) {
ASSERT_TRUE(g_called);
}
+void Run10UInt8(GuestAddr pc, GuestArgumentBuffer* buf) {
+ ASSERT_EQ(ToGuestAddr(&g_insn), pc);
+ ASSERT_NE(buf, nullptr);
+ ASSERT_EQ(buf->argc, 8);
+ ASSERT_EQ(buf->stack_argc, 16);
+ ASSERT_EQ(buf->resc, 1);
+ ASSERT_EQ(buf->argv[0], 0U);
+ ASSERT_EQ(buf->argv[1], 0xffU);
+ ASSERT_EQ(buf->argv[2], 2U);
+ ASSERT_EQ(buf->argv[3], 3U);
+ ASSERT_EQ(buf->argv[4], 4U);
+ ASSERT_EQ(buf->argv[5], 5U);
+ ASSERT_EQ(buf->argv[6], 6U);
+ ASSERT_EQ(buf->argv[7], 0xf9U);
+ ASSERT_EQ(buf->stack_argv[0], 0xf8U);
+ ASSERT_EQ(buf->stack_argv[1], 9U);
+ buf->argv[0] = 0xf6;
+}
+
+TEST(CodeGenLib, GenWrapGuestFunction_Run10UInt8) {
+ MachineCode machine_code;
+
+ GenWrapGuestFunction(
+ &machine_code, ToGuestAddr(&g_insn), "zzzzzzzzzzz", AsHostCode(Run10UInt8), "Run10UInt8");
+
+ ScopedExecRegion exec(&machine_code);
+
+ using Func = uint8_t(
+ uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t);
+ uint8_t res = exec.get<Func>()(0, 0xff, 2, 3, 4, 5, 6, 0xf9, 0xf8, 9);
+ ASSERT_EQ(res, 0xF6u);
+}
+
+void Run10Int8(GuestAddr pc, GuestArgumentBuffer* buf) {
+ ASSERT_EQ(ToGuestAddr(&g_insn), pc);
+ ASSERT_NE(buf, nullptr);
+ ASSERT_EQ(buf->argc, 8);
+ ASSERT_EQ(buf->stack_argc, 16);
+ ASSERT_EQ(buf->resc, 1);
+ ASSERT_EQ(buf->argv[0], 0U);
+ ASSERT_EQ(buf->argv[1], 0xffff'ffff'ffff'ffffULL);
+ ASSERT_EQ(buf->argv[2], 2U);
+ ASSERT_EQ(buf->argv[3], 3U);
+ ASSERT_EQ(buf->argv[4], 4U);
+ ASSERT_EQ(buf->argv[5], 5U);
+ ASSERT_EQ(buf->argv[6], 6U);
+ ASSERT_EQ(buf->argv[7], 0xffff'ffff'ffff'fff9ULL);
+ ASSERT_EQ(buf->stack_argv[0], 0xffff'ffff'ffff'fff8ULL);
+ ASSERT_EQ(buf->stack_argv[1], 9U);
+ buf->argv[0] = 0xffff'ffff'ffff'fff6;
+}
+
+TEST(CodeGenLib, GenWrapGuestFunction_Run10Int8) {
+ MachineCode machine_code;
+
+ GenWrapGuestFunction(
+ &machine_code, ToGuestAddr(&g_insn), "bbbbbbbbbbb", AsHostCode(Run10Int8), "Run10Int8");
+
+ ScopedExecRegion exec(&machine_code);
+
+ using Func =
+ int8_t(int8_t, int8_t, int8_t, int8_t, int8_t, int8_t, int8_t, int8_t, int8_t, int8_t);
+ int8_t res = exec.get<Func>()(0, -1, 2, 3, 4, 5, 6, -7, -8, 9);
+ ASSERT_EQ(res, -10);
+}
+
+void Run10UInt16(GuestAddr pc, GuestArgumentBuffer* buf) {
+ ASSERT_EQ(ToGuestAddr(&g_insn), pc);
+ ASSERT_NE(buf, nullptr);
+ ASSERT_EQ(buf->argc, 8);
+ ASSERT_EQ(buf->stack_argc, 16);
+ ASSERT_EQ(buf->resc, 1);
+ ASSERT_EQ(buf->argv[0], 0U);
+ ASSERT_EQ(buf->argv[1], 0xffffU);
+ ASSERT_EQ(buf->argv[2], 2U);
+ ASSERT_EQ(buf->argv[3], 3U);
+ ASSERT_EQ(buf->argv[4], 4U);
+ ASSERT_EQ(buf->argv[5], 5U);
+ ASSERT_EQ(buf->argv[6], 6U);
+ ASSERT_EQ(buf->argv[7], 0xfff9U);
+ ASSERT_EQ(buf->stack_argv[0], 0xfff8U);
+ ASSERT_EQ(buf->stack_argv[1], 9U);
+ buf->argv[0] = 0xfff6;
+}
+
+TEST(CodeGenLib, GenWrapGuestFunction_Run10UInt16) {
+ MachineCode machine_code;
+
+ GenWrapGuestFunction(
+ &machine_code, ToGuestAddr(&g_insn), "ccccccccccc", AsHostCode(Run10UInt16), "Run10UInt16");
+
+ ScopedExecRegion exec(&machine_code);
+
+ using Func = uint16_t(uint16_t,
+ uint16_t,
+ uint16_t,
+ uint16_t,
+ uint16_t,
+ uint16_t,
+ uint16_t,
+ uint16_t,
+ uint16_t,
+ uint16_t);
+ uint16_t res = exec.get<Func>()(0, 0xffff, 2, 3, 4, 5, 6, 0xfff9, 0xfff8, 9);
+ ASSERT_EQ(res, 0xfff6U);
+}
+
+void Run10Int16(GuestAddr pc, GuestArgumentBuffer* buf) {
+ ASSERT_EQ(ToGuestAddr(&g_insn), pc);
+ ASSERT_NE(buf, nullptr);
+ ASSERT_EQ(buf->argc, 8);
+ ASSERT_EQ(buf->stack_argc, 16);
+ ASSERT_EQ(buf->resc, 1);
+ ASSERT_EQ(buf->argv[0], 0U);
+ ASSERT_EQ(buf->argv[1], 0xffff'ffff'ffff'ffffULL);
+ ASSERT_EQ(buf->argv[2], 2U);
+ ASSERT_EQ(buf->argv[3], 3U);
+ ASSERT_EQ(buf->argv[4], 4U);
+ ASSERT_EQ(buf->argv[5], 5U);
+ ASSERT_EQ(buf->argv[6], 6U);
+ ASSERT_EQ(buf->argv[7], 0xffff'ffff'ffff'fff9ULL);
+ ASSERT_EQ(buf->stack_argv[0], 0xffff'ffff'ffff'fff8ULL);
+ ASSERT_EQ(buf->stack_argv[1], 9U);
+ buf->argv[0] = 0xffff'ffff'ffff'fff6;
+}
+
+TEST(CodeGenLib, GenWrapGuestFunction_Run10Int16) {
+ MachineCode machine_code;
+
+ GenWrapGuestFunction(
+ &machine_code, ToGuestAddr(&g_insn), "sssssssssss", AsHostCode(Run10Int16), "Run10Int16");
+
+ ScopedExecRegion exec(&machine_code);
+
+ using Func = int16_t(
+ int16_t, int16_t, int16_t, int16_t, int16_t, int16_t, int16_t, int16_t, int16_t, int16_t);
+ int16_t res = exec.get<Func>()(0, -1, 2, 3, 4, 5, 6, -7, -8, 9);
+ ASSERT_EQ(res, -10);
+}
+
void Run10Int(GuestAddr pc, GuestArgumentBuffer* buf) {
- ASSERT_EQ(pc, ToGuestAddr(&g_insn));
- ASSERT_NE(nullptr, buf);
- ASSERT_EQ(8, buf->argc);
- ASSERT_EQ(16, buf->stack_argc);
- ASSERT_EQ(1, buf->resc);
- // For 32-bit parameters, only least-significant bits are defined!
- ASSERT_EQ(0u, static_cast<uint32_t>(buf->argv[0]));
- ASSERT_EQ(1u, static_cast<uint32_t>(buf->argv[1]));
- ASSERT_EQ(2u, static_cast<uint32_t>(buf->argv[2]));
- ASSERT_EQ(3u, static_cast<uint32_t>(buf->argv[3]));
- ASSERT_EQ(4u, static_cast<uint32_t>(buf->argv[4]));
- ASSERT_EQ(5u, static_cast<uint32_t>(buf->argv[5]));
- ASSERT_EQ(6u, static_cast<uint32_t>(buf->argv[6]));
- ASSERT_EQ(7u, static_cast<uint32_t>(buf->argv[7]));
- ASSERT_EQ(8u, static_cast<uint32_t>(buf->stack_argv[0]));
- ASSERT_EQ(9u, static_cast<uint32_t>(buf->stack_argv[1]));
- buf->argv[0] = 45;
+ ASSERT_EQ(ToGuestAddr(&g_insn), pc);
+ ASSERT_NE(buf, nullptr);
+ ASSERT_EQ(buf->argc, 8);
+ ASSERT_EQ(buf->stack_argc, 16);
+ ASSERT_EQ(buf->resc, 1);
+ ASSERT_EQ(buf->argv[0], 0U);
+ ASSERT_EQ(buf->argv[1], 0xffff'ffff'ffff'ffffULL);
+ ASSERT_EQ(buf->argv[2], 2U);
+ ASSERT_EQ(buf->argv[3], 3U);
+ ASSERT_EQ(buf->argv[4], 4U);
+ ASSERT_EQ(buf->argv[5], 5U);
+ ASSERT_EQ(buf->argv[6], 6U);
+ ASSERT_EQ(buf->argv[7], 0xffff'ffff'ffff'fff9ULL);
+ ASSERT_EQ(buf->stack_argv[0], 0xffff'ffff'ffff'fff8ULL);
+ ASSERT_EQ(buf->stack_argv[1], 9U);
+ buf->argv[0] = 0xffff'ffff'ffff'fff6;
}
TEST(CodeGenLib, GenWrapGuestFunction_Run10Int) {
@@ -176,8 +315,8 @@ TEST(CodeGenLib, GenWrapGuestFunction_Run10Int) {
ScopedExecRegion exec(&machine_code);
using Func = int(int, int, int, int, int, int, int, int, int, int);
- int res = exec.get<Func>()(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
- ASSERT_EQ(45, res);
+ int res = exec.get<Func>()(0, -1, 2, 3, 4, 5, 6, -7, -8, 9);
+ ASSERT_EQ(res, -10);
}
void Run10Fp(GuestAddr pc, GuestArgumentBuffer* buf) {
diff --git a/code_gen_lib/gen_wrapper_riscv64_to_x86_64.cc b/code_gen_lib/gen_wrapper_riscv64_to_x86_64.cc
index 5ec2be55..65c2811b 100644
--- a/code_gen_lib/gen_wrapper_riscv64_to_x86_64.cc
+++ b/code_gen_lib/gen_wrapper_riscv64_to_x86_64.cc
@@ -28,6 +28,31 @@ namespace berberis {
using x86_64::Assembler;
+namespace {
+
+void ExtendIntArg(MacroAssembler<Assembler>& as,
+ char type,
+ Assembler::Register dst,
+ Assembler::Register src) {
+ if (type == 'z') {
+ as.Movzxbq(dst, src);
+ } else if (type == 'b') {
+ as.Movsxbq(dst, src);
+ } else if (type == 's') {
+ as.Movsxwq(dst, src);
+ } else if (type == 'c') {
+ as.Movzxwq(dst, src);
+ } else if (type == 'i') {
+ as.Movsxlq(dst, src);
+ } else if (type == 'p' || type == 'l') {
+ // Do nothing. The parameter is already 64 bits.
+ } else {
+ FATAL("non-integer signature char '%c'", type);
+ }
+}
+
+} // namespace
+
void GenWrapGuestFunction(MachineCode* mc,
GuestAddr pc,
const char* signature,
@@ -80,7 +105,8 @@ void GenWrapGuestFunction(MachineCode* mc,
int stack_argc = 0;
int host_stack_argc = 0;
for (size_t i = 1; signature[i] != '\0'; ++i) {
- if (signature[i] == 'i' || signature[i] == 'p' || signature[i] == 'l') {
+ if (signature[i] == 'z' || signature[i] == 'b' || signature[i] == 's' || signature[i] == 'c' ||
+ signature[i] == 'i' || signature[i] == 'p' || signature[i] == 'l') {
static constexpr Assembler::Register kParamRegs[] = {
Assembler::rdi,
Assembler::rsi,
@@ -90,16 +116,19 @@ void GenWrapGuestFunction(MachineCode* mc,
Assembler::r9,
};
if (argc < static_cast<int>(std::size(kParamRegs))) {
+ ExtendIntArg(as, signature[i], kParamRegs[argc], kParamRegs[argc]);
as.Movq({.base = Assembler::rsp, .disp = kArgvOffset + argc * 8}, kParamRegs[argc]);
} else if (argc < 8) {
as.Movq(Assembler::rax,
{.base = Assembler::rsp, .disp = params_offset + host_stack_argc * 8});
++host_stack_argc;
+ ExtendIntArg(as, signature[i], Assembler::rax, Assembler::rax);
as.Movq({.base = Assembler::rsp, .disp = kArgvOffset + argc * 8}, Assembler::rax);
} else {
as.Movq(Assembler::rax,
{.base = Assembler::rsp, .disp = params_offset + host_stack_argc * 8});
++host_stack_argc;
+ ExtendIntArg(as, signature[i], Assembler::rax, Assembler::rax);
as.Movq({.base = Assembler::rsp, .disp = kStackArgvOffset + stack_argc * 8},
Assembler::rax);
++stack_argc;
@@ -151,7 +180,8 @@ void GenWrapGuestFunction(MachineCode* mc,
as.Movl({.base = Assembler::rsp, .disp = kStackArgcOffset}, stack_argc * 8);
// Set resc.
- if (signature[0] == 'i' || signature[0] == 'p' || signature[0] == 'l') {
+ if (signature[0] == 'z' || signature[0] == 'b' || signature[0] == 's' || signature[0] == 'c' ||
+ signature[0] == 'i' || signature[0] == 'p' || signature[0] == 'l') {
as.Movl({.base = Assembler::rsp, .disp = kRescOffset}, 1);
as.Movl({.base = Assembler::rsp, .disp = kFpRescOffset}, 0);
} else if (signature[0] == 'f' || signature[0] == 'd') {
@@ -169,7 +199,10 @@ void GenWrapGuestFunction(MachineCode* mc,
as.Call(guest_runner);
// Get the result.
- if (signature[0] == 'i' || signature[0] == 'p' || signature[0] == 'l') {
+ if (signature[0] == 'z' || signature[0] == 'b' || signature[0] == 's' || signature[0] == 'c' ||
+ signature[0] == 'i' || signature[0] == 'p' || signature[0] == 'l') {
+ // It is not necessary to unbox integer return values. The callee will truncate rax to only
+ // retrieve the bits appropriate for the return type.
as.Movq(Assembler::rax, {.base = Assembler::rsp, .disp = kArgvOffset});
} else if (signature[0] == 'f') {
// Only take the lower 32 bits of the result register because floats are 1-extended (NaN boxed)
diff --git a/guest_abi/guest_function_wrapper_signature_test.cc b/guest_abi/guest_function_wrapper_signature_test.cc
index 6ac05860..a136779f 100644
--- a/guest_abi/guest_function_wrapper_signature_test.cc
+++ b/guest_abi/guest_function_wrapper_signature_test.cc
@@ -22,8 +22,10 @@ namespace berberis {
namespace {
-static_assert('i' == kGuestFunctionWrapperSignatureChar<bool>);
-static_assert('i' == kGuestFunctionWrapperSignatureChar<char>);
+static_assert('z' == kGuestFunctionWrapperSignatureChar<bool>);
+static_assert('b' == kGuestFunctionWrapperSignatureChar<char>);
+static_assert('s' == kGuestFunctionWrapperSignatureChar<int16_t>);
+static_assert('c' == kGuestFunctionWrapperSignatureChar<uint16_t>);
static_assert('i' == kGuestFunctionWrapperSignatureChar<int>);
static_assert('l' == kGuestFunctionWrapperSignatureChar<long long>);
static_assert('p' == kGuestFunctionWrapperSignatureChar<void*>);
diff --git a/guest_abi/include/berberis/guest_abi/guest_function_wrapper_signature.h b/guest_abi/include/berberis/guest_abi/guest_function_wrapper_signature.h
index f879e37f..382e83ae 100644
--- a/guest_abi/include/berberis/guest_abi/guest_function_wrapper_signature.h
+++ b/guest_abi/include/berberis/guest_abi/guest_function_wrapper_signature.h
@@ -27,8 +27,12 @@ namespace berberis {
//
// Supported types:
// 'v': void (as return type)
-// 'i': integer and enum types <= 32bit
-// 'l': integer and enum types == 64bit
+// 'z': unsigned integer and enum types == 8bit (jboolean equivalent)
+// 'b': signed integer and enum types == 8bit (jbyte equivalent)
+// 's': signed integer and enum types == 16bit (jshort equivalent)
+// 'c': unsigned integer types == 16bit (jchar equivalent)
+// 'i': integer and enum types == 32bit (jint equivalent)
+// 'l': integer and enum types == 64bit (jfloat equivalent)
// 'p': pointers (to objects and functions but not to members)
// 'f': float (floating point 32 bits)
// 'd': double (floating point 64 bits)
@@ -47,7 +51,42 @@ class kGuestFunctionWrapperSignatureCharHelper {
}
template <typename Type,
- std::enable_if_t<(std::is_integral_v<Type> || std::is_enum_v<Type>)&&sizeof(Type) <=
+ std::enable_if_t<(std::is_integral_v<Type> ||
+ std::is_enum_v<Type>)&&std::is_unsigned_v<Type> &&
+ sizeof(Type) == sizeof(uint8_t),
+ int> = 0>
+ static constexpr char Value() {
+ return 'z';
+ }
+
+ template <
+ typename Type,
+ std::enable_if_t<(std::is_integral_v<Type> || std::is_enum_v<Type>)&&std::is_signed_v<Type> &&
+ sizeof(Type) == sizeof(int8_t),
+ int> = 0>
+ static constexpr char Value() {
+ return 'b';
+ }
+
+ template <
+ typename Type,
+ std::enable_if_t<(std::is_integral_v<Type> || std::is_enum_v<Type>)&&std::is_signed_v<Type> &&
+ sizeof(Type) == sizeof(int16_t),
+ int> = 0>
+ static constexpr char Value() {
+ return 's';
+ }
+
+ template <typename Type,
+ std::enable_if_t<std::is_integral_v<Type> && std::is_unsigned_v<Type> &&
+ sizeof(Type) == sizeof(uint16_t),
+ int> = 0>
+ static constexpr char Value() {
+ return 'c';
+ }
+
+ template <typename Type,
+ std::enable_if_t<(std::is_integral_v<Type> || std::is_enum_v<Type>)&&sizeof(Type) ==
sizeof(int32_t),
int> = 0>
static constexpr char Value() {
diff --git a/guest_os_primitives/guest_signal_handling.cc b/guest_os_primitives/guest_signal_handling.cc
index a6b0ba49..6739ea5f 100644
--- a/guest_os_primitives/guest_signal_handling.cc
+++ b/guest_os_primitives/guest_signal_handling.cc
@@ -16,6 +16,7 @@
#include <atomic>
#include <csignal>
+#include <memory>
#include <mutex>
#if defined(__BIONIC__)
@@ -162,12 +163,15 @@ bool IsReservedSignal(int signal) {
} // namespace
void GuestThread::SetDefaultSignalActionsTable() {
- signal_actions_ = &g_signal_actions;
+ // We need to initialize shared_ptr, but we don't want to attempt to delete the default
+ // signal actions when guest thread terminates. Hence we specify a void deleter.
+ signal_actions_ = std::shared_ptr<GuestSignalActionsTable>(&g_signal_actions, [](auto) {});
}
-void GuestThread::CloneSignalActionsTableTo(GuestSignalActionsTable& new_table_storage) {
- new_table_storage = *signal_actions_;
- signal_actions_ = &new_table_storage;
+void GuestThread::CloneSignalActionsTableFrom(GuestSignalActionsTable* from_table) {
+ // Need lock to make sure from_table isn't changed concurrently.
+ std::lock_guard<std::mutex> lock(g_signal_actions_guard_mutex);
+ signal_actions_ = std::make_shared<GuestSignalActionsTable>(*from_table);
}
// Can be interrupted by another SetSignal!
@@ -298,7 +302,7 @@ void GuestThread::ProcessPendingSignalsImpl() {
siginfo_t* signal_info;
while ((signal_info = pending_signals_.DequeueSignalUnsafe())) {
- const Guest_sigaction* sa = FindSignalHandler(*signal_actions_, signal_info->si_signo);
+ const Guest_sigaction* sa = FindSignalHandler(*signal_actions_.get(), signal_info->si_signo);
ProcessGuestSignal(this, sa, signal_info);
pending_signals_.FreeSignal(signal_info);
}
@@ -318,8 +322,8 @@ bool SetGuestSignalHandler(int signal,
act = nullptr;
}
- GuestSignalAction& action = GetCurrentGuestThread()->GetSignalActionsTable()->at(signal - 1);
std::lock_guard<std::mutex> lock(g_signal_actions_guard_mutex);
+ GuestSignalAction& action = GetCurrentGuestThread()->GetSignalActionsTable()->at(signal - 1);
return action.Change(signal, act, HandleHostSignal, old_act, error);
}
diff --git a/guest_os_primitives/guest_thread.cc b/guest_os_primitives/guest_thread.cc
index 3a02c7f6..1979a81b 100644
--- a/guest_os_primitives/guest_thread.cc
+++ b/guest_os_primitives/guest_thread.cc
@@ -82,13 +82,11 @@ GuestThread* GuestThread::Create() {
intrinsics::InitState();
- thread->SetDefaultSignalActionsTable();
-
return thread;
}
// static
-GuestThread* GuestThread::CreateClone(const GuestThread* parent) {
+GuestThread* GuestThread::CreateClone(const GuestThread* parent, bool share_signal_actions) {
GuestThread* thread = Create();
if (thread == nullptr) {
return nullptr;
@@ -106,6 +104,13 @@ GuestThread* GuestThread::CreateClone(const GuestThread* parent) {
SetCPUState(*thread->state(), GetCPUState(*parent->state()));
SetTlsAddr(*thread->state(), GetTlsAddr(*parent->state()));
+ if (share_signal_actions) {
+ // New shared_ptr user.
+ thread->signal_actions_ = parent->signal_actions_;
+ } else {
+ thread->CloneSignalActionsTableFrom(parent->signal_actions_.get());
+ }
+
return thread;
}
@@ -137,6 +142,8 @@ GuestThread* GuestThread::CreatePthread(void* stack, size_t stack_size, size_t g
return nullptr;
}
+ thread->SetDefaultSignalActionsTable();
+
return thread;
}
@@ -158,6 +165,7 @@ void GuestThread::Destroy(GuestThread* thread) {
if (ArePendingSignalsPresent(*thread->state_)) {
TRACE("thread destroyed with pending signals, signals ignored!");
}
+ thread->signal_actions_.reset();
if (thread->host_stack_) {
// This happens only on cleanup after failed creation.
diff --git a/guest_os_primitives/guest_thread_clone.cc b/guest_os_primitives/guest_thread_clone.cc
index be5f6bb8..31062464 100644
--- a/guest_os_primitives/guest_thread_clone.cc
+++ b/guest_os_primitives/guest_thread_clone.cc
@@ -49,7 +49,6 @@ struct GuestThreadCloneInfo {
GuestThread* thread;
HostSigset mask;
sem_t sem;
- bool share_signal_handlers;
};
int RunClonedGuestThread(void* arg) {
@@ -60,11 +59,6 @@ int RunClonedGuestThread(void* arg) {
// TODO(b/280551726): Clear guest thread in exit syscall.
InsertCurrentThread(thread, false);
- GuestSignalActionsTable signal_actions_storage;
- if (!info->share_signal_handlers) {
- thread->CloneSignalActionsTableTo(signal_actions_storage);
- }
-
// ExecuteGuest requires pending signals enabled.
ScopedPendingSignalsEnabler scoped_pending_signals_enabler(thread);
@@ -124,11 +118,10 @@ pid_t CloneGuestThread(GuestThread* thread,
GuestThreadCloneInfo info;
- info.thread = GuestThread::CreateClone(thread);
+ info.thread = GuestThread::CreateClone(thread, (flags & CLONE_SIGHAND) != 0);
if (info.thread == nullptr) {
return EAGAIN;
}
- info.share_signal_handlers = (flags & CLONE_SIGHAND) != 0;
ThreadState& clone_thread_state = *info.thread->state();
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 4dccc019..d500428e 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
@@ -19,6 +19,7 @@
#include <csetjmp> // jmp_buf
#include <csignal> // stack_t
+#include <memory>
#include "berberis/guest_state/guest_addr.h"
#include "berberis/guest_state/guest_state_opaque.h"
@@ -58,7 +59,7 @@ struct GuestCallExecution {
class GuestThread {
public:
static GuestThread* CreatePthread(void* stack, size_t stack_size, size_t guard_size);
- static GuestThread* CreateClone(const GuestThread* parent);
+ static GuestThread* CreateClone(const GuestThread* parent, bool share_signal_actions);
static GuestThread* CreateForTest(ThreadState* state);
static void Destroy(GuestThread* thread);
static void Exit(GuestThread* thread, int status);
@@ -101,9 +102,9 @@ class GuestThread {
// TODO(b/156271630): Refactor to make this private.
void* GetHostStackTop() const;
- [[nodiscard]] GuestSignalActionsTable* GetSignalActionsTable() { return signal_actions_; }
+ [[nodiscard]] GuestSignalActionsTable* GetSignalActionsTable() { return signal_actions_.get(); }
// Use to unshare signal handlers for CLONE_VM without CLONE_SIGHAND.
- void CloneSignalActionsTableTo(GuestSignalActionsTable& new_table_storage);
+ void CloneSignalActionsTableFrom(GuestSignalActionsTable* from_table);
private:
GuestThread() = default;
@@ -137,7 +138,11 @@ class GuestThread {
ThreadState* state_ = nullptr;
SignalQueue pending_signals_;
- GuestSignalActionsTable* signal_actions_;
+ // When created with CreateClone guest threads either share or fork signal handlers
+ // from the parent. We implement this using shared_ptr semantics.
+ // When created with CreatePthread guest threads use the default global handlers,
+ // which we should never delete. So we create shared_ptr with void deleter in this case.
+ std::shared_ptr<GuestSignalActionsTable> signal_actions_;
GuestCallExecution* guest_call_execution_ = nullptr;
diff --git a/guest_state/riscv64/get_cpu_state.cc b/guest_state/riscv64/get_cpu_state.cc
index 879c6e78..a16723ff 100644
--- a/guest_state/riscv64/get_cpu_state.cc
+++ b/guest_state/riscv64/get_cpu_state.cc
@@ -17,22 +17,20 @@
#include "berberis/base/checks.h"
#include "berberis/guest_state/get_cpu_state_opaque.h"
#include "berberis/guest_state/guest_state_arch.h"
-#include "include/berberis/guest_state/guest_state_opaque.h"
+#include "berberis/guest_state/guest_state_opaque.h"
#include "native_bridge_support/guest_state_accessor/accessor.h"
namespace berberis {
-class GuestStateAccessorImplementation {
- public:
- int LoadGuestStateRegisters(const void* guest_state_data,
- size_t guest_state_data_size,
- NativeBridgeGuestRegs* guest_regs) {
- CHECK_GT(guest_state_data_size, 0);
- guest_regs->guest_arch = NATIVE_BRIDGE_ARCH_RISCV64;
- GetCpuState(guest_regs, &(static_cast<const ThreadState*>(guest_state_data))->cpu);
- return 0;
- }
-};
+extern "C" __attribute__((visibility("default"))) int LoadGuestStateRegisters(
+ const void* guest_state_data,
+ size_t guest_state_data_size,
+ NativeBridgeGuestRegs* guest_regs) {
+ CHECK_GT(guest_state_data_size, 0);
+ guest_regs->guest_arch = NATIVE_BRIDGE_ARCH_RISCV64;
+ GetCpuState(guest_regs, &(static_cast<const ThreadState*>(guest_state_data))->cpu);
+ return 0;
+}
void GetCpuState(NativeBridgeGuestRegs* guest_regs, const CPUState* state) {
CHECK_EQ(guest_regs->guest_arch, NATIVE_BRIDGE_ARCH_RISCV64);
diff --git a/jni/jni_trampolines.cc b/jni/jni_trampolines.cc
index cea6acba..c2ca4d6e 100644
--- a/jni/jni_trampolines.cc
+++ b/jni/jni_trampolines.cc
@@ -43,9 +43,13 @@ char ConvertDalvikTypeCharToWrapperTypeChar(char c) {
case 'V': // void
return 'v';
case 'Z': // boolean
+ return 'z';
case 'B': // byte
+ return 'b';
case 'S': // short
+ return 's';
case 'C': // char
+ return 'c';
case 'I': // int
return 'i';
case 'L': // class object - pointer
@@ -132,10 +136,18 @@ std::vector<jvalue> ConvertVAList(JNIEnv* env, jmethodID methodID, GuestVAListPa
jvalue& arg = result[i];
char c = short_signature[i];
switch (c) {
- case 'Z': // boolean (u8) - passed as int
- case 'B': // byte (i8) - passed as int
- case 'S': // short (i16) - passed as int
- case 'C': // char (u16) - passed as int
+ case 'Z': // boolean (u8)
+ arg.z = params.GetParam<uint8_t>();
+ break;
+ case 'B': // byte (i8)
+ arg.b = params.GetParam<int8_t>();
+ break;
+ case 'S': // short (i16)
+ arg.s = params.GetParam<int16_t>();
+ break;
+ case 'C': // char (u16)
+ arg.c = params.GetParam<uint16_t>();
+ break;
case 'I': // int (i32)
arg.i = params.GetParam<int32_t>();
break;
diff --git a/runtime_primitives/interpret_helpers_riscv64.cc b/runtime_primitives/interpret_helpers_riscv64.cc
index 428bcc30..5494d8e4 100644
--- a/runtime_primitives/interpret_helpers_riscv64.cc
+++ b/runtime_primitives/interpret_helpers_riscv64.cc
@@ -16,7 +16,10 @@
#include "berberis/runtime_primitives/interpret_helpers.h"
-#include <csignal>
+#include <signal.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
#include <cstdint>
#include "berberis/base/checks.h"
@@ -50,7 +53,15 @@ void UndefinedInsn(GuestAddr pc) {
memcpy(&code, addr, sizeof(code));
ALOGE("Undefined riscv64 instruction 0x%" PRIx32 " at %p", code, addr);
}
+#ifdef __GLIBC__
+ // Our old 2.17 glibc has a bug resulting in raise() failing after any CLONE_VM.
+ // Work around by calling tgkill directly.
+ pid_t pid = syscall(__NR_getpid);
+ pid_t tid = syscall(__NR_gettid);
+ syscall(SYS_tgkill, pid, tid, SIGILL);
+#else
raise(SIGILL);
+#endif
}
} // namespace berberis
diff --git a/tests/ndk_program_tests/Android.bp b/tests/ndk_program_tests/Android.bp
index 7ac61638..f7141639 100644
--- a/tests/ndk_program_tests/Android.bp
+++ b/tests/ndk_program_tests/Android.bp
@@ -28,6 +28,7 @@ filegroup {
name: "berberis_ndk_program_tests_srcs",
srcs: [
"atomics_test.cc",
+ "clone_test.cc",
"condvar_test.cc",
"cpp_test.cc",
"ctype_test.cc",
diff --git a/tests/ndk_program_tests/clone_test.cc b/tests/ndk_program_tests/clone_test.cc
new file mode 100644
index 00000000..2284eb3a
--- /dev/null
+++ b/tests/ndk_program_tests/clone_test.cc
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "gtest/gtest.h"
+
+#include <linux/sched.h>
+#include <sched.h>
+#include <sys/wait.h>
+
+#include <atomic>
+#include <csignal>
+#include <cstdlib>
+
+#include "berberis/ndk_program_tests/scoped_sigaction.h"
+
+namespace {
+
+constexpr size_t kChildStack = 1024;
+
+bool g_parent_handler_called;
+bool g_child_handler_called;
+bool g_grandchild_handler_called;
+
+void VerifySignalHandler(bool* flag) {
+ *flag = false;
+ raise(SIGPWR);
+ ASSERT_TRUE(*flag);
+}
+
+template <size_t kStackSize, typename Runner>
+void CloneVMAndWait(Runner runner, int extra_flags, int expect_return) {
+ void* child_stack[kStackSize];
+ pid_t tid = clone(runner, &child_stack[kStackSize], CLONE_VM | extra_flags, nullptr);
+ int status;
+ ASSERT_EQ(tid, TEMP_FAILURE_RETRY(waitpid(tid, &status, __WCLONE)));
+ ASSERT_TRUE(WIFEXITED(status));
+ ASSERT_EQ(WEXITSTATUS(status), expect_return);
+}
+
+int SharedSighandRunner(void*) {
+ // Grandchild shared handlers with child.
+ VerifySignalHandler(&g_child_handler_called);
+ struct sigaction sa {
+ .sa_handler = +[](int) { g_grandchild_handler_called = true; }
+ };
+ // We intentionally do not restore sigaction to verify that this change
+ // will also change the handler in child (parent of grandchild).
+ EXPECT_EQ(sigaction(SIGPWR, &sa, nullptr), 0);
+ VerifySignalHandler(&g_grandchild_handler_called);
+ return 21;
+}
+
+int UnsharedSighandRunner(void*) {
+ // Child inherits a copy of parent handlers.
+ VerifySignalHandler(&g_parent_handler_called);
+ struct sigaction sa {
+ .sa_handler = +[](int) { g_child_handler_called = true; }
+ };
+ // We intentionally do not restore sigaction to verify that this change
+ // doesn't affect signal handlers in parent.
+ EXPECT_EQ(sigaction(SIGPWR, &sa, nullptr), 0);
+ VerifySignalHandler(&g_child_handler_called);
+ // Now clone with shared handlers.
+ CloneVMAndWait<kChildStack>(SharedSighandRunner, CLONE_SIGHAND, 21);
+ VerifySignalHandler(&g_grandchild_handler_called);
+ return 42;
+}
+
+TEST(Clone, CloneVMSighandSharing) {
+ struct sigaction sa {
+ .sa_handler = +[](int) { g_parent_handler_called = true; }
+ };
+ ScopedSigaction scoped_sa(SIGPWR, &sa);
+ // Clone a child with non-shared signal handlers.
+ // Note that child's stack contains grandchild's stack, so should be larger.
+ CloneVMAndWait<kChildStack * 2>(UnsharedSighandRunner, 0, 42);
+ // Verify that children didn't alter parent's signal handlers.
+ VerifySignalHandler(&g_parent_handler_called);
+}
+
+// We cannot accurately detect when grandchild stack can be free'd. So
+// we just keep it in a global variable and never free.
+void* g_grandchild_stack[kChildStack];
+std::atomic<bool> g_child_finished;
+std::atomic<bool> g_grandchild_finished;
+
+int WaitUntilParentExitsAndVerifySignalHandlers(void*) {
+ while (!g_child_finished) {
+ sched_yield();
+ }
+
+ // Grandchild shares handlers with child and should still
+ // be able to use them after child terminated.
+ VerifySignalHandler(&g_child_handler_called);
+
+ g_grandchild_finished = true;
+ return 0;
+}
+
+int CloneOutlivingChild(void*) {
+ struct sigaction sa {
+ .sa_handler = +[](int) { g_child_handler_called = true; }
+ };
+ EXPECT_EQ(sigaction(SIGPWR, &sa, nullptr), 0);
+
+ clone(WaitUntilParentExitsAndVerifySignalHandlers,
+ &g_grandchild_stack[kChildStack],
+ CLONE_VM | CLONE_SIGHAND,
+ nullptr);
+ return 42;
+}
+
+TEST(Clone, CloneVMChildOutlivingParent) {
+ // We'll test grandchild outliving child.
+ g_child_finished = false;
+ g_grandchild_finished = false;
+
+ CloneVMAndWait<kChildStack>(CloneOutlivingChild, 0, 42);
+
+ g_child_finished = true;
+
+ // Wait for grandchild to finish.
+ while (!g_grandchild_finished) {
+ sched_yield();
+ }
+}
+
+} // namespace