* [RFC v5 05/10] tcg: Add tcg opcodes and helpers for native library calls
@ 2023-08-25 10:45 Yeqi Fu
2023-08-25 10:45 ` [RFC v5 06/10] target/i386: Add support " Yeqi Fu
` (5 more replies)
0 siblings, 6 replies; 7+ messages in thread
From: Yeqi Fu @ 2023-08-25 10:45 UTC (permalink / raw)
To: alex.bennee; +Cc: richard.henderson, qemu-devel, Yeqi Fu, Paolo Bonzini
This commit implements tcg opcodes and helpers for native library
calls. A table is used to store the parameter types and return value
types for each native library function. In terms of types, only three
types are of real concern: the two base sizes int and intptr_t, and
if the value is a pointer, tcg_gen_g2h and tcg_gen_h2g are used for
address conversion.
Signed-off-by: Yeqi Fu <fufuyqqqqqq@gmail.com>
---
accel/tcg/tcg-runtime.h | 22 ++++
include/native/native-defs.h | 42 ++++++++
include/tcg/tcg-op-common.h | 11 ++
include/tcg/tcg.h | 9 ++
tcg/tcg-op.c | 193 ++++++++++++++++++++++++++++++++++-
5 files changed, 276 insertions(+), 1 deletion(-)
create mode 100644 include/native/native-defs.h
diff --git a/accel/tcg/tcg-runtime.h b/accel/tcg/tcg-runtime.h
index 39e68007f9..bda78b4489 100644
--- a/accel/tcg/tcg-runtime.h
+++ b/accel/tcg/tcg-runtime.h
@@ -37,6 +37,28 @@ DEF_HELPER_FLAGS_1(exit_atomic, TCG_CALL_NO_WG, noreturn, env)
*/
#define helper_memset memset
DEF_HELPER_FLAGS_3(memset, TCG_CALL_NO_RWG, ptr, ptr, int, ptr)
+
+#define helper_memcpy memcpy
+DEF_HELPER_FLAGS_3(memcpy, TCG_CALL_NO_RWG, ptr, ptr, ptr, ptr)
+
+#define helper_strncpy strncpy
+DEF_HELPER_FLAGS_3(strncpy, TCG_CALL_NO_RWG, ptr, ptr, ptr, ptr)
+
+#define helper_memcmp memcmp
+DEF_HELPER_FLAGS_3(memcmp, TCG_CALL_NO_RWG, int, ptr, ptr, ptr)
+
+#define helper_strncmp strncmp
+DEF_HELPER_FLAGS_3(strncmp, TCG_CALL_NO_RWG, int, ptr, ptr, ptr)
+
+#define helper_strcpy strcpy
+DEF_HELPER_FLAGS_2(strcpy, TCG_CALL_NO_RWG, ptr, ptr, ptr)
+
+#define helper_strcat strcat
+DEF_HELPER_FLAGS_2(strcat, TCG_CALL_NO_RWG, ptr, ptr, ptr)
+
+#define helper_strcmp strcmp
+DEF_HELPER_FLAGS_2(strcmp, TCG_CALL_NO_RWG, int, ptr, ptr)
+
#endif /* IN_HELPER_PROTO */
DEF_HELPER_FLAGS_3(ld_i128, TCG_CALL_NO_WG, i128, env, i64, i32)
diff --git a/include/native/native-defs.h b/include/native/native-defs.h
new file mode 100644
index 0000000000..517bb86db0
--- /dev/null
+++ b/include/native/native-defs.h
@@ -0,0 +1,42 @@
+/*
+ * Argument encoding. We only really care about 3 types. The two base
+ * sizes (int and intptr_t) and if the value is a pointer (in which
+ * case we need to adjust it g2h before passing to the native
+ * function).
+ */
+#define TYPE_NO_ARG 0x0
+#define TYPE_INT_ARG 0x1
+#define TYPE_IPTR_ARG 0x2
+#define TYPE_PTR_ARG 0x3
+
+#define ENCODE_TYPE(ret_value, arg1, arg2, arg3) \
+ ((ret_value) | (arg1 << 4) | (arg2 << 8) | (arg3 << 12))
+
+#define TYPE_AAAP \
+ ENCODE_TYPE(TYPE_PTR_ARG, TYPE_PTR_ARG, TYPE_PTR_ARG, TYPE_IPTR_ARG)
+#define TYPE_IAAP \
+ ENCODE_TYPE(TYPE_INT_ARG, TYPE_PTR_ARG, TYPE_PTR_ARG, TYPE_IPTR_ARG)
+#define TYPE_AAIP \
+ ENCODE_TYPE(TYPE_PTR_ARG, TYPE_PTR_ARG, TYPE_INT_ARG, TYPE_IPTR_ARG)
+#define TYPE_AAA \
+ ENCODE_TYPE(TYPE_PTR_ARG, TYPE_PTR_ARG, TYPE_PTR_ARG, TYPE_NO_ARG)
+#define TYPE_IAA \
+ ENCODE_TYPE(TYPE_INT_ARG, TYPE_PTR_ARG, TYPE_PTR_ARG, TYPE_NO_ARG)
+
+typedef void (*helper_pppp)(struct TCGv_ptr_d *, struct TCGv_ptr_d *,
+ struct TCGv_ptr_d *, struct TCGv_ptr_d *);
+typedef void (*helper_ippp)(struct TCGv_i32_d *, struct TCGv_ptr_d *,
+ struct TCGv_ptr_d *, struct TCGv_ptr_d *);
+typedef void (*helper_ppip)(struct TCGv_ptr_d *, struct TCGv_ptr_d *,
+ struct TCGv_i32_d *, struct TCGv_ptr_d *);
+typedef void (*helper_ppp)(struct TCGv_ptr_d *, struct TCGv_ptr_d *,
+ struct TCGv_ptr_d *);
+typedef void (*helper_ipp)(struct TCGv_i32_d *, struct TCGv_ptr_d *,
+ struct TCGv_ptr_d *);
+typedef void (*helper_func)(void *, ...);
+
+typedef struct {
+ const char *func;
+ helper_func helper;
+ uint32_t type;
+} FuncHelper;
diff --git a/include/tcg/tcg-op-common.h b/include/tcg/tcg-op-common.h
index be382bbf77..2e712f1573 100644
--- a/include/tcg/tcg-op-common.h
+++ b/include/tcg/tcg-op-common.h
@@ -903,6 +903,12 @@ void tcg_gen_ld_vec(TCGv_vec r, TCGv_ptr base, TCGArg offset);
void tcg_gen_st_vec(TCGv_vec r, TCGv_ptr base, TCGArg offset);
void tcg_gen_stl_vec(TCGv_vec r, TCGv_ptr base, TCGArg offset, TCGType t);
+/* Host <-> guest conversions */
+void tcg_gen_g2h_i32(TCGv_ptr ret, TCGv_i32 arg);
+void tcg_gen_g2h_i64(TCGv_ptr ret, TCGv_i64 arg);
+void tcg_gen_h2g_i32(TCGv_i32 ret, TCGv_ptr arg);
+void tcg_gen_h2g_i64(TCGv_i64 ret, TCGv_ptr arg);
+
/* Host pointer ops */
#if UINTPTR_MAX == UINT32_MAX
@@ -938,6 +944,11 @@ static inline void tcg_gen_addi_ptr(TCGv_ptr r, TCGv_ptr a, intptr_t b)
glue(tcg_gen_addi_,PTR)((NAT)r, (NAT)a, b);
}
+static inline void tcg_gen_subi_ptr(TCGv_ptr r, TCGv_ptr a, intptr_t b)
+{
+ glue(tcg_gen_subi_, PTR)((NAT)r, (NAT)a, b);
+}
+
static inline void tcg_gen_mov_ptr(TCGv_ptr d, TCGv_ptr s)
{
glue(tcg_gen_mov_,PTR)((NAT)d, (NAT)s);
diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h
index 0875971719..a4c0f26dc4 100644
--- a/include/tcg/tcg.h
+++ b/include/tcg/tcg.h
@@ -35,6 +35,9 @@
#include "tcg-target.h"
#include "tcg/tcg-cond.h"
#include "tcg/debug-assert.h"
+#ifdef CONFIG_USER_ONLY
+#include "exec/user/guest-base.h"
+#endif
/* XXX: make safe guess about sizes */
#define MAX_OP_PER_INSTR 266
@@ -1148,4 +1151,10 @@ static inline const TCGOpcode *tcg_swap_vecop_list(const TCGOpcode *n)
bool tcg_can_emit_vecop_list(const TCGOpcode *, TCGType, unsigned);
+/* native call */
+void gen_native_call_i32(const char *func_name, TCGv_i32 ret,
+ TCGv_i32 arg1, TCGv_i32 arg2, TCGv_i32 arg3);
+void gen_native_call_i64(const char *func_name, TCGv_i64 ret,
+ TCGv_i64 arg1, TCGv_i64 arg2, TCGv_i64 arg3);
+
#endif /* TCG_H */
diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c
index 7aadb37756..8cd39e4ab4 100644
--- a/tcg/tcg-op.c
+++ b/tcg/tcg-op.c
@@ -29,7 +29,7 @@
#include "exec/translation-block.h"
#include "exec/plugin-gen.h"
#include "tcg-internal.h"
-
+#include "native/native-defs.h"
void tcg_gen_op1(TCGOpcode opc, TCGArg a1)
{
@@ -2852,3 +2852,194 @@ void tcg_gen_lookup_and_goto_ptr(void)
tcg_gen_op1i(INDEX_op_goto_ptr, tcgv_ptr_arg(ptr));
tcg_temp_free_ptr(ptr);
}
+
+#ifdef CONFIG_USER_ONLY
+void tcg_gen_g2h_i32(TCGv_ptr ret, TCGv_i32 arg)
+{
+ TCGv_ptr temp = tcg_temp_new_ptr();
+ tcg_gen_ext_i32_ptr(temp, arg);
+ tcg_gen_addi_ptr(ret, temp, guest_base);
+ tcg_temp_free_ptr(temp);
+}
+
+void tcg_gen_g2h_i64(TCGv_ptr ret, TCGv_i64 arg)
+{
+ TCGv_ptr temp = tcg_temp_new_ptr();
+ tcg_gen_trunc_i64_ptr(temp, arg); /* Not sure */
+ tcg_gen_addi_ptr(ret, temp, guest_base);
+ tcg_temp_free_ptr(temp);
+}
+
+void tcg_gen_h2g_i32(TCGv_i32 ret, TCGv_ptr arg)
+{
+ TCGv_ptr temp = tcg_temp_new_ptr();
+ tcg_gen_subi_ptr(temp, arg, guest_base);
+ tcg_gen_trunc_ptr_i32(ret, temp);
+ tcg_temp_free_ptr(temp);
+}
+
+void tcg_gen_h2g_i64(TCGv_i64 ret, TCGv_ptr arg)
+{
+ TCGv_ptr temp = tcg_temp_new_ptr();
+ tcg_gen_subi_ptr(temp, arg, guest_base);
+ tcg_gen_extu_ptr_i64(ret, temp);
+ tcg_temp_free_ptr(temp);
+}
+
+#else
+void tcg_gen_g2h_i32(TCGv_ptr ret, TCGv_i32 arg)
+{
+}
+void tcg_gen_g2h_i64(TCGv_ptr ret, TCGv_i64 arg)
+{
+}
+void tcg_gen_h2g_i32(TCGv_i32 ret, TCGv_ptr arg)
+{
+}
+void tcg_gen_h2g_i64(TCGv_i64 ret, TCGv_ptr arg)
+{
+}
+#endif
+
+static const FuncHelper func_helper_table[] = {
+ { .func = "memset",
+ .helper = (helper_func)gen_helper_memset,
+ .type = TYPE_AAIP },
+ { .func = "memcpy",
+ .helper = (helper_func)gen_helper_memcpy,
+ .type = TYPE_AAAP },
+ { .func = "strncpy",
+ .helper = (helper_func)gen_helper_strncpy,
+ .type = TYPE_AAAP },
+ { .func = "memcmp",
+ .helper = (helper_func)gen_helper_memcmp,
+ .type = TYPE_IAAP },
+ { .func = "strncmp",
+ .helper = (helper_func)gen_helper_strncmp,
+ .type = TYPE_IAAP },
+ { .func = "strcpy",
+ .helper = (helper_func)gen_helper_strcpy,
+ .type = TYPE_AAA },
+ { .func = "strcat",
+ .helper = (helper_func)gen_helper_strcat,
+ .type = TYPE_AAA },
+ { .func = "strcmp",
+ .helper = (helper_func)gen_helper_strcmp,
+ .type = TYPE_IAA },
+};
+/* p: iptr ; i: i32 ; a: ptr(address) */
+void gen_native_call_i32(const char *func_name, TCGv_i32 ret, TCGv_i32 arg1,
+ TCGv_i32 arg2, TCGv_i32 arg3)
+{
+ TCGv_ptr arg1_ptr = tcg_temp_new_ptr();
+ TCGv_ptr arg2_ptr = tcg_temp_new_ptr();
+ TCGv_ptr arg3_ptr = tcg_temp_new_ptr();
+ TCGv_ptr ret_ptr = tcg_temp_new_ptr();
+ unsigned int i;
+ for (i = 0; i < sizeof(func_helper_table) / sizeof(FuncHelper); i++) {
+ if (strcmp(func_name, func_helper_table[i].func) == 0) {
+ break;
+ }
+ }
+ g_assert(i < sizeof(func_helper_table) / sizeof(FuncHelper));
+ switch (func_helper_table[i].type) {
+ case TYPE_AAIP:
+ tcg_gen_g2h_i32(arg1_ptr, arg1);
+ tcg_gen_ext_i32_ptr(arg3_ptr, arg3);
+ ((helper_ppip)(func_helper_table[i].helper))(ret_ptr, arg1_ptr, arg2,
+ arg3_ptr);
+ goto ret_ptr;
+ case TYPE_AAAP:
+ tcg_gen_g2h_i32(arg1_ptr, arg1);
+ tcg_gen_g2h_i32(arg2_ptr, arg2);
+ tcg_gen_ext_i32_ptr(arg3_ptr, arg3);
+ ((helper_pppp)(func_helper_table[i].helper))(ret_ptr, arg1_ptr,
+ arg2_ptr, arg3_ptr);
+ goto ret_ptr;
+ case TYPE_IAAP:
+ tcg_gen_g2h_i32(arg1_ptr, arg1);
+ tcg_gen_g2h_i32(arg2_ptr, arg2);
+ tcg_gen_ext_i32_ptr(arg3_ptr, arg3);
+ ((helper_ippp)(func_helper_table[i].helper))(ret, arg1_ptr, arg2_ptr,
+ arg3_ptr);
+ return;
+ case TYPE_AAA:
+ tcg_gen_g2h_i32(arg1_ptr, arg1);
+ tcg_gen_g2h_i32(arg2_ptr, arg2);
+ ((helper_ppp)(func_helper_table[i].helper))(ret_ptr, arg1_ptr,
+ arg2_ptr);
+ goto ret_ptr;
+ case TYPE_IAA:
+ tcg_gen_g2h_i32(arg1_ptr, arg1);
+ tcg_gen_g2h_i32(arg2_ptr, arg2);
+ ((helper_ipp)(func_helper_table[i].helper))(ret, arg1_ptr, arg2_ptr);
+ return;
+ default:
+ g_assert_not_reached();
+ }
+ret_ptr:
+ tcg_gen_h2g_i32(ret, ret_ptr);
+ return;
+}
+
+void gen_native_call_i64(const char *func_name, TCGv_i64 ret, TCGv_i64 arg1,
+ TCGv_i64 arg2, TCGv_i64 arg3)
+{
+ TCGv_ptr arg1_ptr = tcg_temp_new_ptr();
+ TCGv_ptr arg2_ptr = tcg_temp_new_ptr();
+ TCGv_ptr arg3_ptr = tcg_temp_new_ptr();
+ TCGv_ptr ret_ptr = tcg_temp_new_ptr();
+ TCGv_i32 arg2_i32, ret_i32 = tcg_temp_new_i32();
+ unsigned int i;
+ for (i = 0; i < sizeof(func_helper_table) / sizeof(FuncHelper); i++) {
+ if (strcmp(func_name, func_helper_table[i].func) == 0) {
+ break;
+ }
+ }
+ g_assert(i < sizeof(func_helper_table) / sizeof(FuncHelper));
+
+ switch (func_helper_table[i].type) {
+ case TYPE_AAIP:
+ tcg_gen_g2h_i64(arg1_ptr, arg1);
+ arg2_i32 = tcg_temp_new_i32();
+ tcg_gen_extrl_i64_i32(arg2_i32, arg2);
+ tcg_gen_trunc_i64_ptr(arg3_ptr, arg3);
+ ((helper_ppip)(func_helper_table[i].helper))(ret_ptr, arg1_ptr,
+ arg2_i32, arg3_ptr);
+ goto ret_ptr;
+ case TYPE_AAAP:
+ tcg_gen_g2h_i64(arg1_ptr, arg1);
+ tcg_gen_g2h_i64(arg2_ptr, arg2);
+ tcg_gen_trunc_i64_ptr(arg3_ptr, arg3);
+ ((helper_pppp)(func_helper_table[i].helper))(ret_ptr, arg1_ptr,
+ arg2_ptr, arg3_ptr);
+ goto ret_ptr;
+ case TYPE_IAAP:
+ tcg_gen_g2h_i64(arg1_ptr, arg1);
+ tcg_gen_g2h_i64(arg2_ptr, arg2);
+ tcg_gen_trunc_i64_ptr(arg3_ptr, arg3);
+ ((helper_ippp)(func_helper_table[i].helper))(ret_i32, arg1_ptr,
+ arg2_ptr, arg3_ptr);
+ goto ret_i32;
+ case TYPE_AAA:
+ tcg_gen_g2h_i64(arg1_ptr, arg1);
+ tcg_gen_g2h_i64(arg2_ptr, arg2);
+ ((helper_ppp)(func_helper_table[i].helper))(ret_ptr, arg1_ptr,
+ arg2_ptr);
+ goto ret_ptr;
+ case TYPE_IAA:
+ tcg_gen_g2h_i64(arg1_ptr, arg1);
+ tcg_gen_g2h_i64(arg2_ptr, arg2);
+ ((helper_ipp)(func_helper_table[i].helper))(ret_i32, arg1_ptr,
+ arg2_ptr);
+ goto ret_i32;
+ default:
+ g_assert_not_reached();
+ }
+ret_ptr:
+ tcg_gen_h2g_i64(ret, ret_ptr);
+ return;
+ret_i32:
+ tcg_gen_extu_i32_i64(ret, ret_i32);
+ return;
+}
--
2.34.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [RFC v5 06/10] target/i386: Add support for native library calls
2023-08-25 10:45 [RFC v5 05/10] tcg: Add tcg opcodes and helpers for native library calls Yeqi Fu
@ 2023-08-25 10:45 ` Yeqi Fu
2023-08-25 10:45 ` [RFC v5 07/10] target/mips: " Yeqi Fu
` (4 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Yeqi Fu @ 2023-08-25 10:45 UTC (permalink / raw)
To: alex.bennee
Cc: richard.henderson, qemu-devel, Yeqi Fu, Laurent Vivier,
Paolo Bonzini, Eduardo Habkost
This commit introduces support for native library calls on the
i386 target. When encountering special instructions reserved
for native calls, this commit extracts the function name and
generates the corresponding native call.
Signed-off-by: Yeqi Fu <fufuyqqqqqq@gmail.com>
---
configs/targets/i386-linux-user.mak | 1 +
configs/targets/x86_64-linux-user.mak | 1 +
target/i386/tcg/translate.c | 37 +++++++++++++++++++++++++++
3 files changed, 39 insertions(+)
diff --git a/configs/targets/i386-linux-user.mak b/configs/targets/i386-linux-user.mak
index 5b2546a430..2d8bca8f93 100644
--- a/configs/targets/i386-linux-user.mak
+++ b/configs/targets/i386-linux-user.mak
@@ -2,3 +2,4 @@ TARGET_ARCH=i386
TARGET_SYSTBL_ABI=i386
TARGET_SYSTBL=syscall_32.tbl
TARGET_XML_FILES= gdb-xml/i386-32bit.xml
+CONFIG_NATIVE_CALL=y
diff --git a/configs/targets/x86_64-linux-user.mak b/configs/targets/x86_64-linux-user.mak
index 9ceefbb615..a53b017454 100644
--- a/configs/targets/x86_64-linux-user.mak
+++ b/configs/targets/x86_64-linux-user.mak
@@ -3,3 +3,4 @@ TARGET_BASE_ARCH=i386
TARGET_SYSTBL_ABI=common,64
TARGET_SYSTBL=syscall_64.tbl
TARGET_XML_FILES= gdb-xml/i386-64bit.xml
+CONFIG_NATIVE_CALL=y
diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c
index 90c7b32f36..a9e37977e7 100644
--- a/target/i386/tcg/translate.c
+++ b/target/i386/tcg/translate.c
@@ -33,6 +33,7 @@
#include "helper-tcg.h"
#include "exec/log.h"
+#include "native/native.h"
#define HELPER_H "helper.h"
#include "exec/helper-info.c.inc"
@@ -3075,6 +3076,36 @@ static void gen_cmpxchg16b(DisasContext *s, CPUX86State *env, int modrm)
}
#endif
+static void gen_native_call(CPUState *cpu, DisasContext *s, CPUX86State *env)
+{
+#ifdef CONFIG_USER_ONLY
+ char *func_name;
+ TCGv ret = tcg_temp_new();
+ TCGv arg1 = tcg_temp_new();
+ TCGv arg2 = tcg_temp_new();
+ TCGv arg3 = tcg_temp_new();
+#ifdef TARGET_X86_64
+ uint64_t func_tmp = x86_ldq_code(env, s);
+ func_name = g2h(cpu, func_tmp);
+ tcg_gen_mov_tl(arg1, cpu_regs[R_EDI]);
+ tcg_gen_mov_tl(arg2, cpu_regs[R_ESI]);
+ tcg_gen_mov_tl(arg3, cpu_regs[R_EDX]);
+ gen_native_call_i64(func_name, ret, arg1, arg2, arg3);
+#else
+ uint32_t func_tmp = x86_ldl_code(env, s);
+ func_name = g2h(cpu, func_tmp);
+ tcg_gen_addi_tl(arg1, cpu_regs[R_ESP], 4);
+ gen_op_ld_v(s, MO_UL, arg1, arg1);
+ tcg_gen_addi_tl(arg2, cpu_regs[R_ESP], 8);
+ gen_op_ld_v(s, MO_UL, arg2, arg2);
+ tcg_gen_addi_tl(arg3, cpu_regs[R_ESP], 12);
+ gen_op_ld_v(s, MO_UL, arg3, arg3);
+ gen_native_call_i32(func_name, ret, arg1, arg2, arg3);
+#endif
+ tcg_gen_mov_tl(cpu_regs[R_EAX], ret);
+#endif
+}
+
/* convert one instruction. s->base.is_jmp is set if the translation must
be stopped. Return the next pc value */
static bool disas_insn(DisasContext *s, CPUState *cpu)
@@ -6810,6 +6841,12 @@ static bool disas_insn(DisasContext *s, CPUState *cpu)
case 0x1d0 ... 0x1fe:
disas_insn_new(s, cpu, b);
break;
+ case 0x1ff:
+ if (native_bypass_enabled()) {
+ gen_native_call(cpu, s, env);
+ break;
+ }
+ break;
default:
goto unknown_op;
}
--
2.34.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [RFC v5 07/10] target/mips: Add support for native library calls
2023-08-25 10:45 [RFC v5 05/10] tcg: Add tcg opcodes and helpers for native library calls Yeqi Fu
2023-08-25 10:45 ` [RFC v5 06/10] target/i386: Add support " Yeqi Fu
@ 2023-08-25 10:45 ` Yeqi Fu
2023-08-25 10:45 ` [RFC v5 08/10] target/arm: " Yeqi Fu
` (3 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Yeqi Fu @ 2023-08-25 10:45 UTC (permalink / raw)
To: alex.bennee
Cc: richard.henderson, qemu-devel, Yeqi Fu,
Philippe Mathieu-Daudé, Jiaxun Yang, Laurent Vivier,
Aurelien Jarno, Aleksandar Rikalo
This commit introduces support for native library calls on the
mips target. When encountering special instructions reserved
for native calls, this commit extracts the function name and
generates the corresponding native call.
Signed-off-by: Yeqi Fu <fufuyqqqqqq@gmail.com>
---
configs/targets/mips-linux-user.mak | 1 +
configs/targets/mips64-linux-user.mak | 1 +
target/mips/tcg/translate.c | 36 ++++++++++++++++++++++++++-
3 files changed, 37 insertions(+), 1 deletion(-)
diff --git a/configs/targets/mips-linux-user.mak b/configs/targets/mips-linux-user.mak
index b4569a9893..fa005d487a 100644
--- a/configs/targets/mips-linux-user.mak
+++ b/configs/targets/mips-linux-user.mak
@@ -3,3 +3,4 @@ TARGET_ABI_MIPSO32=y
TARGET_SYSTBL_ABI=o32
TARGET_SYSTBL=syscall_o32.tbl
TARGET_BIG_ENDIAN=y
+CONFIG_NATIVE_CALL=y
diff --git a/configs/targets/mips64-linux-user.mak b/configs/targets/mips64-linux-user.mak
index d2ff509a11..ecfe6bcf73 100644
--- a/configs/targets/mips64-linux-user.mak
+++ b/configs/targets/mips64-linux-user.mak
@@ -4,3 +4,4 @@ TARGET_BASE_ARCH=mips
TARGET_SYSTBL_ABI=n64
TARGET_SYSTBL=syscall_n64.tbl
TARGET_BIG_ENDIAN=y
+CONFIG_NATIVE_CALL=y
diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c
index 74af91e4f5..fa58f9e12f 100644
--- a/target/mips/tcg/translate.c
+++ b/target/mips/tcg/translate.c
@@ -36,6 +36,7 @@
#include "exec/helper-info.c.inc"
#undef HELPER_H
+#include "native/native.h"
/*
* Many sysemu-only helpers are not reachable for user-only.
@@ -13484,10 +13485,38 @@ static void decode_opc_special_legacy(CPUMIPSState *env, DisasContext *ctx)
}
}
+static void gen_native_call(DisasContext *ctx, CPUMIPSState *env)
+{
+#ifdef CONFIG_USER_ONLY
+ char *func_name;
+ TCGv arg1 = tcg_temp_new();
+ TCGv arg2 = tcg_temp_new();
+ TCGv arg3 = tcg_temp_new();
+ TCGv ret = tcg_temp_new();
+ tcg_gen_mov_tl(arg1, cpu_gpr[4]);
+ tcg_gen_mov_tl(arg2, cpu_gpr[5]);
+ tcg_gen_mov_tl(arg3, cpu_gpr[6]);
+#if defined(TARGET_MIPS64)
+ uint64_t func_tmp =
+ translator_ldq(env, &ctx->base, ctx->base.pc_next + 8);
+ ctx->base.pc_next += 12;
+ func_name = g2h(env_cpu(env), func_tmp);
+ gen_native_call_i64(func_name, ret, arg1, arg2, arg3);
+#else
+ uint32_t func_tmp =
+ translator_ldl(env, &ctx->base, ctx->base.pc_next + 4);
+ ctx->base.pc_next += 4;
+ func_name = g2h(env_cpu(env), func_tmp);
+ gen_native_call_i32(func_name, ret, arg1, arg2, arg3);
+#endif
+ tcg_gen_mov_tl(cpu_gpr[2], ret);
+#endif
+}
+
static void decode_opc_special(CPUMIPSState *env, DisasContext *ctx)
{
int rs, rt, rd, sa;
- uint32_t op1;
+ uint32_t op1, sig;
rs = (ctx->opcode >> 21) & 0x1f;
rt = (ctx->opcode >> 16) & 0x1f;
@@ -13583,6 +13612,11 @@ static void decode_opc_special(CPUMIPSState *env, DisasContext *ctx)
#endif
break;
case OPC_SYSCALL:
+ sig = (ctx->opcode) >> 6;
+ if ((sig == 0xffff) && native_bypass_enabled()) {
+ gen_native_call(ctx, env);
+ break;
+ }
generate_exception_end(ctx, EXCP_SYSCALL);
break;
case OPC_BREAK:
--
2.34.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [RFC v5 08/10] target/arm: Add support for native library calls
2023-08-25 10:45 [RFC v5 05/10] tcg: Add tcg opcodes and helpers for native library calls Yeqi Fu
2023-08-25 10:45 ` [RFC v5 06/10] target/i386: Add support " Yeqi Fu
2023-08-25 10:45 ` [RFC v5 07/10] target/mips: " Yeqi Fu
@ 2023-08-25 10:45 ` Yeqi Fu
2023-08-25 10:45 ` [RFC v5 09/10] tests/tcg/multiarch: Add nativecall.c test Yeqi Fu
` (2 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Yeqi Fu @ 2023-08-25 10:45 UTC (permalink / raw)
To: alex.bennee
Cc: richard.henderson, qemu-devel, Yeqi Fu, Laurent Vivier,
Peter Maydell, open list:ARM TCG CPUs
This commit introduces support for native library calls on the
arm target. When encountering special instructions reserved
for native calls, this commit extracts the function name and
generates the corresponding native call.
Signed-off-by: Yeqi Fu <fufuyqqqqqq@gmail.com>
---
configs/targets/aarch64-linux-user.mak | 1 +
configs/targets/arm-linux-user.mak | 1 +
target/arm/tcg/translate-a64.c | 31 ++++++++++++++++++++++++++
target/arm/tcg/translate.c | 28 +++++++++++++++++++++++
target/arm/tcg/translate.h | 5 +++++
5 files changed, 66 insertions(+)
diff --git a/configs/targets/aarch64-linux-user.mak b/configs/targets/aarch64-linux-user.mak
index ba8bc5fe3f..5a8fd98cd9 100644
--- a/configs/targets/aarch64-linux-user.mak
+++ b/configs/targets/aarch64-linux-user.mak
@@ -4,3 +4,4 @@ TARGET_XML_FILES= gdb-xml/aarch64-core.xml gdb-xml/aarch64-fpu.xml gdb-xml/aarch
TARGET_HAS_BFLT=y
CONFIG_SEMIHOSTING=y
CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
+CONFIG_NATIVE_CALL=y
diff --git a/configs/targets/arm-linux-user.mak b/configs/targets/arm-linux-user.mak
index 7f5d65794c..f934fb82da 100644
--- a/configs/targets/arm-linux-user.mak
+++ b/configs/targets/arm-linux-user.mak
@@ -5,3 +5,4 @@ TARGET_XML_FILES= gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml
TARGET_HAS_BFLT=y
CONFIG_SEMIHOSTING=y
CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
+CONFIG_NATIVE_CALL=y
diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c
index 3baab6aa60..0cb9228a35 100644
--- a/target/arm/tcg/translate-a64.c
+++ b/target/arm/tcg/translate-a64.c
@@ -25,6 +25,8 @@
#include "arm_ldst.h"
#include "semihosting/semihost.h"
#include "cpregs.h"
+#include "exec/cpu_ldst.h"
+#include "native/native.h"
static TCGv_i64 cpu_X[32];
static TCGv_i64 cpu_pc;
@@ -2400,6 +2402,10 @@ static bool trans_HLT(DisasContext *s, arg_i *a)
* it is required for halting debug disabled: it will UNDEF.
* Secondly, "HLT 0xf000" is the A64 semihosting syscall instruction.
*/
+ if (native_bypass_enabled() && (a->imm == 0xffff)) {
+ s->native_call_status = true;
+ return true;
+ }
if (semihosting_enabled(s->current_el == 0) && a->imm == 0xf000) {
gen_exception_internal_insn(s, EXCP_SEMIHOST);
} else {
@@ -13392,6 +13398,24 @@ void gen_gvec_rax1(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs,
tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &op);
}
+static void gen_native_call(CPUState *cpu, DisasContext *s, CPUARMState *env)
+{
+#ifdef CONFIG_USER_ONLY
+ TCGv_i64 arg1 = tcg_temp_new_i64();
+ TCGv_i64 arg2 = tcg_temp_new_i64();
+ TCGv_i64 arg3 = tcg_temp_new_i64();
+ TCGv_i64 ret = tcg_temp_new_i64();
+ uint64_t func_tmp = translator_ldq_swap(env, &s->base, s->base.pc_next,
+ bswap_code(s->sctlr_b));
+ char *func_name = g2h(cpu, func_tmp);
+ tcg_gen_mov_i64(arg1, cpu_reg(s, 0));
+ tcg_gen_mov_i64(arg2, cpu_reg(s, 1));
+ tcg_gen_mov_i64(arg3, cpu_reg(s, 2));
+ gen_native_call_i64(func_name, ret, arg1, arg2, arg3);
+ tcg_gen_mov_i64(cpu_reg(s, 0), ret);
+#endif
+}
+
/* Crypto three-reg SHA512
* 31 21 20 16 15 14 13 12 11 10 9 5 4 0
* +-----------------------+------+---+---+-----+--------+------+------+
@@ -13950,6 +13974,13 @@ static void aarch64_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu)
uint64_t pc = s->base.pc_next;
uint32_t insn;
+ if (native_bypass_enabled() && s->native_call_status) {
+ gen_native_call(cpu, s, env);
+ s->base.pc_next = pc + 8;
+ s->native_call_status = false;
+ return;
+ }
+
/* Singlestep exceptions have the highest priority. */
if (s->ss_active && !s->pstate_ss) {
/* Singlestep state is Active-pending.
diff --git a/target/arm/tcg/translate.c b/target/arm/tcg/translate.c
index 13c88ba1b9..d5eaed65f5 100644
--- a/target/arm/tcg/translate.c
+++ b/target/arm/tcg/translate.c
@@ -28,6 +28,8 @@
#include "semihosting/semihost.h"
#include "cpregs.h"
#include "exec/helper-proto.h"
+#include "exec/cpu_ldst.h"
+#include "native/native.h"
#define HELPER_H "helper.h"
#include "exec/helper-info.c.inc"
@@ -1125,6 +1127,21 @@ void gen_lookup_tb(DisasContext *s)
s->base.is_jmp = DISAS_EXIT;
}
+static void gen_native_call(CPUState *cpu, DisasContext *dc, CPUARMState *env)
+{
+#ifdef CONFIG_USER_ONLY
+ TCGv_i32 arg1 = load_reg(dc, 0);
+ TCGv_i32 arg2 = load_reg(dc, 1);
+ TCGv_i32 arg3 = load_reg(dc, 2);
+ TCGv_i32 ret = tcg_temp_new_i32();
+ uint32_t func_tmp =
+ arm_ldl_code(env, &dc->base, dc->base.pc_next, dc->sctlr_b);
+ char *func_name = g2h(cpu, func_tmp);
+ gen_native_call_i32(func_name, ret, arg1, arg2, arg3);
+ store_reg(dc, 0, ret);
+#endif
+}
+
static inline void gen_hlt(DisasContext *s, int imm)
{
/* HLT. This has two purposes.
@@ -1139,6 +1156,10 @@ static inline void gen_hlt(DisasContext *s, int imm)
* semihosting, to provide some semblance of security
* (and for consistency with our 32-bit semihosting).
*/
+ if (native_bypass_enabled() && (imm == 0xffff)) {
+ s->native_call_status = true;
+ return;
+ }
if (semihosting_enabled(s->current_el == 0) &&
(imm == (s->thumb ? 0x3c : 0xf000))) {
gen_exception_internal_insn(s, EXCP_SEMIHOST);
@@ -9329,6 +9350,13 @@ static void arm_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu)
uint32_t pc = dc->base.pc_next;
unsigned int insn;
+ if (native_bypass_enabled() && dc->native_call_status) {
+ gen_native_call(cpu, dc, env);
+ dc->base.pc_next = pc + 4;
+ dc->native_call_status = false;
+ return;
+ }
+
/* Singlestep exceptions have the highest priority. */
if (arm_check_ss_active(dc)) {
dc->base.pc_next = pc + 4;
diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h
index d1cacff0b2..3854a801e6 100644
--- a/target/arm/tcg/translate.h
+++ b/target/arm/tcg/translate.h
@@ -157,6 +157,11 @@ typedef struct DisasContext {
int c15_cpar;
/* TCG op of the current insn_start. */
TCGOp *insn_start;
+ /*
+ * Indicate whether the next instruction is a native function call (true)
+ * or not (false).
+ */
+ bool native_call_status;
} DisasContext;
typedef struct DisasCompare {
--
2.34.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [RFC v5 09/10] tests/tcg/multiarch: Add nativecall.c test
2023-08-25 10:45 [RFC v5 05/10] tcg: Add tcg opcodes and helpers for native library calls Yeqi Fu
` (2 preceding siblings ...)
2023-08-25 10:45 ` [RFC v5 08/10] target/arm: " Yeqi Fu
@ 2023-08-25 10:45 ` Yeqi Fu
2023-08-25 10:45 ` [RFC v5 10/10] docs/user: Add doc for native library calls Yeqi Fu
2023-08-26 2:24 ` [RFC v5 05/10] tcg: Add tcg opcodes and helpers " Richard Henderson
5 siblings, 0 replies; 7+ messages in thread
From: Yeqi Fu @ 2023-08-25 10:45 UTC (permalink / raw)
To: alex.bennee; +Cc: richard.henderson, qemu-devel, Yeqi Fu
Introduce a new test for native calls to ensure their functionality.
The process involves cross-compiling the test cases, building them
as dynamically linked binaries, and running these binaries which
necessitates the addition of the appropriate interpreter prefix.
Signed-off-by: Yeqi Fu <fufuyqqqqqq@gmail.com>
---
tests/tcg/multiarch/Makefile.target | 30 ++++++
tests/tcg/multiarch/native/nativecall.c | 121 ++++++++++++++++++++++++
2 files changed, 151 insertions(+)
create mode 100644 tests/tcg/multiarch/native/nativecall.c
diff --git a/tests/tcg/multiarch/Makefile.target b/tests/tcg/multiarch/Makefile.target
index 43bddeaf21..4a8d65435b 100644
--- a/tests/tcg/multiarch/Makefile.target
+++ b/tests/tcg/multiarch/Makefile.target
@@ -12,7 +12,9 @@ VPATH += $(MULTIARCH_SRC)
MULTIARCH_SRCS = $(notdir $(wildcard $(MULTIARCH_SRC)/*.c))
ifeq ($(filter %-linux-user, $(TARGET)),$(TARGET))
VPATH += $(MULTIARCH_SRC)/linux
+VPATH += $(MULTIARCH_SRC)/native
MULTIARCH_SRCS += $(notdir $(wildcard $(MULTIARCH_SRC)/linux/*.c))
+MULTIARCH_SRCS += $(notdir $(wildcard $(MULTIARCH_SRC)/native/*.c))
endif
MULTIARCH_TESTS = $(MULTIARCH_SRCS:.c=)
@@ -138,5 +140,33 @@ run-plugin-semiconsole-with-%:
TESTS += semihosting semiconsole
endif
+nativecall: nativecall.c
+ $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(filter-out -static,$(LDFLAGS))
+
+ifneq ($(LD_PREFIX),)
+ifneq ($(LIBNATIVE),)
+run-nativecall: nativecall
+ $(call run-test, $<, $(QEMU) -L $(LD_PREFIX) \
+ --native-bypass $(LIBNATIVE) $<, "nativecall")
+
+run-plugin-nativecall-with-%:
+ $(call run-test, $@, $(QEMU) $(QEMU_OPTS) \
+ -L $(LD_PREFIX) --native-bypass $(LIBNATIVE) \
+ -plugin $(PLUGIN_LIB)/$(call extract-plugin,$@)$(PLUGIN_ARGS) \
+ $(call strip-plugin,$<) 2> $<.err, \
+ $< with $*)
+else
+run-nativecall: nativecall
+ $(call skip-test, $<, "no native library found")
+run-plugin-nativecall-with-%:
+ $(call skip-test, $<, "no native library found")
+endif
+else
+run-nativecall: nativecall
+ $(call skip-test, $<, "no elf interpreter prefix found")
+run-plugin-nativecall-with-%:
+ $(call skip-test, $<, "no elf interpreter prefix found")
+endif
+
# Update TESTS
TESTS += $(MULTIARCH_TESTS)
diff --git a/tests/tcg/multiarch/native/nativecall.c b/tests/tcg/multiarch/native/nativecall.c
new file mode 100644
index 0000000000..15dbd5d937
--- /dev/null
+++ b/tests/tcg/multiarch/native/nativecall.c
@@ -0,0 +1,121 @@
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+void compare_memory(const void *a, const void *b, size_t n)
+{
+ const unsigned char *p1 = a;
+ const unsigned char *p2 = b;
+ for (size_t i = 0; i < n; i++) {
+ assert(p1[i] == p2[i]);
+ }
+}
+
+void test_memcpy(char *src)
+{
+ char dest[2000];
+ memcpy(dest, src, 2000);
+ compare_memory(dest, src, 2000);
+}
+
+void test_strncpy(char *src)
+{
+ char dest[2000];
+ strncpy(dest, src, 2000);
+ compare_memory(dest, src, 2000);
+}
+
+void test_strcpy(char *src)
+{
+ char dest[2000];
+ strcpy(dest, src);
+ compare_memory(dest, src, 2000);
+}
+
+void test_strcat()
+{
+ char src[20] = "Hello, ";
+ char dest[] = "world!";
+ char str[] = "Hello, world!";
+ strcat(src, dest);
+ compare_memory(src, str, 13);
+}
+
+void test_memcmp(char *str1, char *str2, char *str3)
+{
+ int result1 = memcmp(str1, str2, 3);
+ int result2 = memcmp(str1, str3, 3);
+ int result3 = memcmp(str3, str1, 3);
+ assert(result1 == 0);
+ assert(result2 < 0);
+ assert(result3 > 0);
+}
+
+void test_strncmp(char *str1, char *str2, char *str3)
+{
+ int result1 = strncmp(str1, str2, 3);
+ int result2 = strncmp(str1, str3, 3);
+ int result3 = strncmp(str3, str1, 3);
+ assert(result1 == 0);
+ assert(result2 < 0);
+ assert(result3 > 0);
+}
+
+void test_strcmp(char *str1, char *str2, char *str3)
+{
+ int result1 = strcmp(str1, str2);
+ int result2 = strcmp(str1, str3);
+ int result3 = strcmp(str3, str1);
+ assert(result1 == 0);
+ assert(result2 < 0);
+ assert(result3 > 0);
+}
+
+void test_memset()
+{
+ char buffer[2000];
+ memset(buffer, 'A', 2000);
+ for (int i = 0; i < 2000; i++) {
+ assert(buffer[i] == 'A');
+ }
+}
+
+/*
+ * When executing execv, an error may occur stating that the shared library from
+ * LD_PRELOAD cannot be preloaded.
+ */
+void test_execv(const char *cmd)
+{
+ char *argv[4];
+ argv[0] = (char *)"/bin/sh";
+ argv[1] = (char *)"-c";
+ argv[2] = (char *)cmd;
+ argv[3] = NULL;
+ execv("/bin/sh", argv);
+}
+
+int main()
+{
+ char buf[2000];
+ for (int i = 0; i < 2000; i++) {
+ buf[i] = 'A' + (i % 26);
+ }
+ char str1[] = "abc";
+ char str2[] = "abc";
+ char str3[] = "def";
+
+ test_memcpy(buf);
+ test_strncpy(buf);
+ test_strcpy(buf);
+ test_memcmp(str1, str2, str3);
+ test_strncmp(str1, str2, str3);
+ test_strcmp(str1, str2, str3);
+ test_strcat();
+ test_memset();
+ test_execv("echo 111");
+
+ return EXIT_SUCCESS;
+}
--
2.34.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [RFC v5 10/10] docs/user: Add doc for native library calls
2023-08-25 10:45 [RFC v5 05/10] tcg: Add tcg opcodes and helpers for native library calls Yeqi Fu
` (3 preceding siblings ...)
2023-08-25 10:45 ` [RFC v5 09/10] tests/tcg/multiarch: Add nativecall.c test Yeqi Fu
@ 2023-08-25 10:45 ` Yeqi Fu
2023-08-26 2:24 ` [RFC v5 05/10] tcg: Add tcg opcodes and helpers " Richard Henderson
5 siblings, 0 replies; 7+ messages in thread
From: Yeqi Fu @ 2023-08-25 10:45 UTC (permalink / raw)
To: alex.bennee; +Cc: richard.henderson, qemu-devel, Yeqi Fu
Signed-off-by: Yeqi Fu <fufuyqqqqqq@gmail.com>
---
docs/user/index.rst | 1 +
docs/user/native_calls.rst | 90 ++++++++++++++++++++++++++++++++++++++
2 files changed, 91 insertions(+)
create mode 100644 docs/user/native_calls.rst
diff --git a/docs/user/index.rst b/docs/user/index.rst
index 782d27cda2..d3fc9b7af1 100644
--- a/docs/user/index.rst
+++ b/docs/user/index.rst
@@ -12,3 +12,4 @@ processes compiled for one CPU on another CPU.
:maxdepth: 2
main
+ native_calls
diff --git a/docs/user/native_calls.rst b/docs/user/native_calls.rst
new file mode 100644
index 0000000000..e86675128a
--- /dev/null
+++ b/docs/user/native_calls.rst
@@ -0,0 +1,90 @@
+Native Library Calls Optimization
+=================================
+
+Description
+-----------
+
+Executing a program under QEMU's user mode subjects the entire
+program, including all library calls, to translation. It's important
+to understand that many of these library functions are optimized
+specifically for the guest architecture. Therefore, their
+translation might not yield the most efficient execution.
+
+When the semantics of a library function are well defined, we can
+capitalize on this by substituting the translated version with a call
+to the native equivalent function.
+
+To achieve tangible results, focus should be given to functions such
+as memory-related ('mem*') and string-related ('str*') functions.
+These subsets of functions often have the most significant effect
+on overall performance, making them optimal candidates for
+optimization.
+
+Implementation
+--------------
+
+Upon setting the LD_PRELOAD environment variable, the dynamic linker
+will load the library specified in LD_PRELOAD preferentially. If there
+exist functions in the LD_PRELOAD library that share names with those
+in other libraries, they will override the corresponding functions in
+those other libraries.
+
+To implement native library bypass, we created a shared library and
+re-implemented the native functions within it as a special
+instruction sequence. By means of the LD_PRELOAD environment
+variable, we load this shared library into the user program.
+Therefore, when the user program calls a native function, it actually
+executes this special instruction sequence. During execution, QEMU's
+translator captures these special instructions and executes the
+corresponding native functions.
+
+These special instructions are implemented using
+architecture-specific unused or invalid opcodes, ensuring that they
+do not conflict with existing instructions.
+
+
+i386 and x86_64
+---------------
+An unused instruction is utilized to mark a native call.
+
+arm and aarch64
+---------------
+HLT is an invalid instruction for userspace programs, and is used to
+mark a native call.
+
+mips and mips64
+---------------
+The syscall instruction contains 20 unused bits, which are typically
+set to 0. These bits can be used to store non-zero data,
+distinguishing them from a regular syscall instruction.
+
+Usage
+-----
+
+1. Install cross-compilation tools
+
+Cross-compilation tools are required to build the shared libraries
+that can hook the necessary library functions. For example, a viable
+command on Ubuntu is:
+
+::
+
+ apt install gcc-arm-linux-gnueabihf gcc-aarch64-linux-gnu \
+ gcc-mips-linux-gnu gcc-mips64-linux-gnuabi64
+
+
+2. Locate the compiled libnative.so
+
+After compilation, the libnative.so file can be found in the
+``./build/common-user/native/<target>-linux-user`` directory.
+
+3. Run the program with the ``--native-bypass`` option
+
+To run your program with native library bypass, use the
+``--native-bypass`` option to import libnative.so:
+
+::
+
+ qemu-<target> --native-bypass \
+ ./build/common-user/native/<target>-linux-user/libnative.so ./program
+
--
2.34.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [RFC v5 05/10] tcg: Add tcg opcodes and helpers for native library calls
2023-08-25 10:45 [RFC v5 05/10] tcg: Add tcg opcodes and helpers for native library calls Yeqi Fu
` (4 preceding siblings ...)
2023-08-25 10:45 ` [RFC v5 10/10] docs/user: Add doc for native library calls Yeqi Fu
@ 2023-08-26 2:24 ` Richard Henderson
5 siblings, 0 replies; 7+ messages in thread
From: Richard Henderson @ 2023-08-26 2:24 UTC (permalink / raw)
To: Yeqi Fu, alex.bennee; +Cc: qemu-devel, Paolo Bonzini
On 8/25/23 03:45, Yeqi Fu wrote:
> This commit implements tcg opcodes and helpers for native library
> calls. A table is used to store the parameter types and return value
> types for each native library function. In terms of types, only three
> types are of real concern: the two base sizes int and intptr_t, and
> if the value is a pointer, tcg_gen_g2h and tcg_gen_h2g are used for
> address conversion.
>
> Signed-off-by: Yeqi Fu <fufuyqqqqqq@gmail.com>
> ---
> accel/tcg/tcg-runtime.h | 22 ++++
> include/native/native-defs.h | 42 ++++++++
> include/tcg/tcg-op-common.h | 11 ++
> include/tcg/tcg.h | 9 ++
> tcg/tcg-op.c | 193 ++++++++++++++++++++++++++++++++++-
> 5 files changed, 276 insertions(+), 1 deletion(-)
> create mode 100644 include/native/native-defs.h
>
> diff --git a/accel/tcg/tcg-runtime.h b/accel/tcg/tcg-runtime.h
> index 39e68007f9..bda78b4489 100644
> --- a/accel/tcg/tcg-runtime.h
> +++ b/accel/tcg/tcg-runtime.h
> @@ -37,6 +37,28 @@ DEF_HELPER_FLAGS_1(exit_atomic, TCG_CALL_NO_WG, noreturn, env)
> */
> #define helper_memset memset
> DEF_HELPER_FLAGS_3(memset, TCG_CALL_NO_RWG, ptr, ptr, int, ptr)
> +
> +#define helper_memcpy memcpy
> +DEF_HELPER_FLAGS_3(memcpy, TCG_CALL_NO_RWG, ptr, ptr, ptr, ptr)
> +
> +#define helper_strncpy strncpy
> +DEF_HELPER_FLAGS_3(strncpy, TCG_CALL_NO_RWG, ptr, ptr, ptr, ptr)
> +
> +#define helper_memcmp memcmp
> +DEF_HELPER_FLAGS_3(memcmp, TCG_CALL_NO_RWG, int, ptr, ptr, ptr)
> +
> +#define helper_strncmp strncmp
> +DEF_HELPER_FLAGS_3(strncmp, TCG_CALL_NO_RWG, int, ptr, ptr, ptr)
> +
> +#define helper_strcpy strcpy
> +DEF_HELPER_FLAGS_2(strcpy, TCG_CALL_NO_RWG, ptr, ptr, ptr)
> +
> +#define helper_strcat strcat
> +DEF_HELPER_FLAGS_2(strcat, TCG_CALL_NO_RWG, ptr, ptr, ptr)
> +
> +#define helper_strcmp strcmp
> +DEF_HELPER_FLAGS_2(strcmp, TCG_CALL_NO_RWG, int, ptr, ptr)
You cannot just call these directly. This will fail immediately whenever the guest does
something silly like
memcpy(NULL, "foo", 4);
This must raise SIGSEGV to the guest.
If we leave the bulk transform to tcg, the absolute minimum is
void * HELPER(memcpy)(void *dst, void *src, target_ulong len)
{
set_helper_retaddr(GETPC());
void *r = memcpy(dst, src, len);
clear_helper_retaddr();
return r;
}
There is no way to do this thread-local storage update from TCG.
But if we need to have a helper at all, we might as well do more and *not* leave the
transform to tcg. Something akin to
target_ulong HELPER(memcpy)(target_ulong dst, target_ulong src, target_ulong len)
{
uintptr_t ra = GETPC();
CPUState *cpu = thread_cpu;
void *h_dst, *h_src;
if (!h2g_valid(src)) {
cpu_loop_exit_sigsegv(cpu, src, MMU_DATA_LOAD, 1, ra);
}
if (!h2g_valid(dst)) {
cpu_loop_exit_sigsegv(cpu, dst, MMU_DATA_STORE, 1, ra);
}
set_helper_retaddr(ra);
memcpy(g2h(cpu, dst), g2h(cpu, src), len);
clear_helper_retaddr(ra);
/* memcpy always returns its first argument */
return dst;
}
> --- /dev/null
> +++ b/include/native/native-defs.h
> @@ -0,0 +1,42 @@
> +/*
> + * Argument encoding. We only really care about 3 types. The two base
> + * sizes (int and intptr_t) and if the value is a pointer (in which
> + * case we need to adjust it g2h before passing to the native
> + * function).
> + */
> +#define TYPE_NO_ARG 0x0
> +#define TYPE_INT_ARG 0x1
> +#define TYPE_IPTR_ARG 0x2
> +#define TYPE_PTR_ARG 0x3
> +
> +#define ENCODE_TYPE(ret_value, arg1, arg2, arg3) \
> + ((ret_value) | (arg1 << 4) | (arg2 << 8) | (arg3 << 12))
Supposing we do the transform in tcg, this duplicates include/exec/helper-head.h, and
dh_typemask().
> +static const FuncHelper func_helper_table[] = {
> + { .func = "memset",
> + .helper = (helper_func)gen_helper_memset,
> + .type = TYPE_AAIP },
> + { .func = "memcpy",
> + .helper = (helper_func)gen_helper_memcpy,
> + .type = TYPE_AAAP },
> + { .func = "strncpy",
> + .helper = (helper_func)gen_helper_strncpy,
> + .type = TYPE_AAAP },
> + { .func = "memcmp",
> + .helper = (helper_func)gen_helper_memcmp,
> + .type = TYPE_IAAP },
> + { .func = "strncmp",
> + .helper = (helper_func)gen_helper_strncmp,
> + .type = TYPE_IAAP },
> + { .func = "strcpy",
> + .helper = (helper_func)gen_helper_strcpy,
> + .type = TYPE_AAA },
> + { .func = "strcat",
> + .helper = (helper_func)gen_helper_strcat,
> + .type = TYPE_AAA },
> + { .func = "strcmp",
> + .helper = (helper_func)gen_helper_strcmp,
> + .type = TYPE_IAA },
> +};
> +/* p: iptr ; i: i32 ; a: ptr(address) */
> +void gen_native_call_i32(const char *func_name, TCGv_i32 ret, TCGv_i32 arg1,
> + TCGv_i32 arg2, TCGv_i32 arg3)
> +{
> + TCGv_ptr arg1_ptr = tcg_temp_new_ptr();
> + TCGv_ptr arg2_ptr = tcg_temp_new_ptr();
> + TCGv_ptr arg3_ptr = tcg_temp_new_ptr();
> + TCGv_ptr ret_ptr = tcg_temp_new_ptr();
> + unsigned int i;
> + for (i = 0; i < sizeof(func_helper_table) / sizeof(FuncHelper); i++) {
> + if (strcmp(func_name, func_helper_table[i].func) == 0) {
> + break;
> + }
> + }
> + g_assert(i < sizeof(func_helper_table) / sizeof(FuncHelper));
So you assert() if libnative.so gives you something that doesn't match?
Surely returning false here, and allowing the translator to fail the transformation is
better. This would generate SIGILL at runtime, which still kills the guest program, but
it feels cleaner. You could also do
qemu_log_mask(LOG_GUEST_ERROR, "Unimplemented libnative call to \"%s\"\n", func_name);
r~
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2023-08-26 2:25 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-08-25 10:45 [RFC v5 05/10] tcg: Add tcg opcodes and helpers for native library calls Yeqi Fu
2023-08-25 10:45 ` [RFC v5 06/10] target/i386: Add support " Yeqi Fu
2023-08-25 10:45 ` [RFC v5 07/10] target/mips: " Yeqi Fu
2023-08-25 10:45 ` [RFC v5 08/10] target/arm: " Yeqi Fu
2023-08-25 10:45 ` [RFC v5 09/10] tests/tcg/multiarch: Add nativecall.c test Yeqi Fu
2023-08-25 10:45 ` [RFC v5 10/10] docs/user: Add doc for native library calls Yeqi Fu
2023-08-26 2:24 ` [RFC v5 05/10] tcg: Add tcg opcodes and helpers " Richard Henderson
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).