qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [RFC v2 0/6] Native Library Calls
@ 2023-06-07 16:47 Yeqi Fu
  2023-06-07 16:47 ` [RFC v2 1/6] build: Add configure options for native calls Yeqi Fu
                   ` (5 more replies)
  0 siblings, 6 replies; 17+ messages in thread
From: Yeqi Fu @ 2023-06-07 16:47 UTC (permalink / raw)
  To: alex.bennee; +Cc: richard.henderson, qemu-devel, Yeqi Fu

This patch introduces a set of feature instructions for native calls
and provides helpers to translate these instructions to corresponding
native functions. A shared library is also implemented, where native
functions are rewritten as feature instructions. At runtime, user
programs load the shared library, and feature instructions are
executed when native functions are called. This patch is applicable
to user programs with architectures x86, x86_64, arm, aarch64, mips,
and mips64. To build, compile libnative.c into a shared library for
the user program's architecture and run the
'../configure --enable-user-native-call && make' command.

Yeqi Fu (6):
  build: Add configure options for native calls
  Add the libnative library
  target/i386: Add native library calls
  target/mips: Add native library calls
  target/arm: Add native library calls
  linux-user: Add '-native-bypass' option

 Makefile                             |  4 ++
 common-user/native/Makefile.include  |  9 ++++
 common-user/native/Makefile.target   | 22 ++++++++++
 common-user/native/libnative.c       | 65 ++++++++++++++++++++++++++++
 configure                            | 50 +++++++++++++++++++++
 docs/devel/build-system.rst          |  4 ++
 include/native/libnative.h           | 11 +++++
 include/native/native-func.h         | 11 +++++
 include/qemu/envlist.h               |  1 +
 linux-user/main.c                    | 23 ++++++++++
 meson.build                          |  8 ++++
 meson_options.txt                    |  2 +
 scripts/meson-buildoptions.sh        |  4 ++
 target/arm/helper.c                  | 47 ++++++++++++++++++++
 target/arm/helper.h                  |  6 +++
 target/arm/tcg/translate-a64.c       | 22 ++++++++++
 target/arm/tcg/translate.c           | 25 ++++++++++-
 target/arm/tcg/translate.h           | 19 ++++++++
 target/i386/helper.h                 |  6 +++
 target/i386/tcg/translate.c          | 20 +++++++++
 target/i386/tcg/user/meson.build     |  1 +
 target/i386/tcg/user/native_helper.c | 65 ++++++++++++++++++++++++++++
 target/mips/helper.h                 |  6 +++
 target/mips/tcg/meson.build          |  1 +
 target/mips/tcg/native_helper.c      | 55 +++++++++++++++++++++++
 target/mips/tcg/translate.c          | 20 ++++++++-
 target/mips/tcg/translate.h          | 12 +++++
 util/envlist.c                       | 56 ++++++++++++++++++++++++
 28 files changed, 573 insertions(+), 2 deletions(-)
 create mode 100644 common-user/native/Makefile.include
 create mode 100644 common-user/native/Makefile.target
 create mode 100644 common-user/native/libnative.c
 create mode 100644 include/native/libnative.h
 create mode 100644 include/native/native-func.h
 create mode 100644 target/i386/tcg/user/native_helper.c
 create mode 100644 target/mips/tcg/native_helper.c

-- 
2.34.1



^ permalink raw reply	[flat|nested] 17+ messages in thread

* [RFC v2 1/6] build: Add configure options for native calls
  2023-06-07 16:47 [RFC v2 0/6] Native Library Calls Yeqi Fu
@ 2023-06-07 16:47 ` Yeqi Fu
  2023-06-09  5:08   ` Manos Pitsidianakis
  2023-06-12 11:54   ` Alex Bennée
  2023-06-07 16:47 ` [RFC v2 2/6] Add the libnative library Yeqi Fu
                   ` (4 subsequent siblings)
  5 siblings, 2 replies; 17+ messages in thread
From: Yeqi Fu @ 2023-06-07 16:47 UTC (permalink / raw)
  To: alex.bennee
  Cc: richard.henderson, qemu-devel, Yeqi Fu, Paolo Bonzini,
	Thomas Huth, Riku Voipio, Daniel P. Berrangé,
	Marc-André Lureau, Philippe Mathieu-Daudé

Signed-off-by: Yeqi Fu <fufuyqqqqqq@gmail.com>
---
 Makefile                            |  4 +++
 common-user/native/Makefile.include |  9 ++++++
 common-user/native/Makefile.target  | 22 +++++++++++++
 configure                           | 50 +++++++++++++++++++++++++++++
 docs/devel/build-system.rst         |  4 +++
 meson.build                         |  8 +++++
 meson_options.txt                   |  2 ++
 scripts/meson-buildoptions.sh       |  4 +++
 8 files changed, 103 insertions(+)
 create mode 100644 common-user/native/Makefile.include
 create mode 100644 common-user/native/Makefile.target

diff --git a/Makefile b/Makefile
index 3c7d67142f..923da109bf 100644
--- a/Makefile
+++ b/Makefile
@@ -185,6 +185,10 @@ SUBDIR_MAKEFLAGS=$(if $(V),,--no-print-directory --quiet)
 
 include $(SRC_PATH)/tests/Makefile.include
 
+ifeq ($(CONFIG_USER_NATIVE),y)
+	include $(SRC_PATH)/common-user/native/Makefile.include
+endif
+
 all: recurse-all
 
 ROMS_RULES=$(foreach t, all clean distclean, $(addsuffix /$(t), $(ROMS)))
diff --git a/common-user/native/Makefile.include b/common-user/native/Makefile.include
new file mode 100644
index 0000000000..40d20bcd4c
--- /dev/null
+++ b/common-user/native/Makefile.include
@@ -0,0 +1,9 @@
+.PHONY: build-native
+build-native: $(NATIVE_TARGETS:%=build-native-library-%)
+$(NATIVE_TARGETS:%=build-native-library-%): build-native-library-%:
+	$(call quiet-command, \
+	    $(MAKE) -C common-user/native/$* $(SUBDIR_MAKEFLAGS), \
+	"BUILD","$* native library")
+# endif
+
+all: build-native
diff --git a/common-user/native/Makefile.target b/common-user/native/Makefile.target
new file mode 100644
index 0000000000..1038367b37
--- /dev/null
+++ b/common-user/native/Makefile.target
@@ -0,0 +1,22 @@
+# -*- Mode: makefile -*-
+#
+# Library for native calls 
+#
+
+all:
+-include ../config-host.mak
+-include config-target.mak
+
+CFLAGS+=-I$(SRC_PATH)/include -O1 -fPIC -shared -fno-stack-protector
+LDFLAGS+=
+
+SRC = $(SRC_PATH)/common-user/native/libnative.c
+TARGET = libnative.so
+
+all: $(TARGET)
+
+$(TARGET): $(SRC)
+	$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS)
+
+clean:
+	rm -f $(TARGET)
diff --git a/configure b/configure
index 2a556d14c9..cc94d10c98 100755
--- a/configure
+++ b/configure
@@ -275,6 +275,7 @@ use_containers="yes"
 gdb_bin=$(command -v "gdb-multiarch" || command -v "gdb")
 gdb_arches=""
 werror=""
+user_native_call="disabled"
 
 # Don't accept a target_list environment variable.
 unset target_list
@@ -787,6 +788,10 @@ for opt do
   ;;
   --disable-vfio-user-server) vfio_user_server="disabled"
   ;;
+  --enable-user-native-call) user_native_call="enabled"
+  ;;
+  --disable-user-native-call) user_native_call="disabled"
+  ;;
   # everything else has the same name in configure and meson
   --*) meson_option_parse "$opt" "$optarg"
   ;;
@@ -1898,6 +1903,50 @@ if test "$tcg" = "enabled"; then
 fi
 )
 
+# common-user/native configuration
+native_flag_i386="-DTARGET_I386"
+native_flag_x86_64="-DTARGET_X86_64"
+native_flag_mips="-DTARGET_MIPS"
+native_flag_mips64="-DTARGET_MIPS64"
+native_flag_arm="-DTARGET_ARM"
+native_flag_aarch64="-DTARGET_AARCH64"
+
+(config_host_mak=common-user/native/config-host.mak
+mkdir -p common-user/native
+echo "# Automatically generated by configure - do not modify" > $config_host_mak
+echo "SRC_PATH=$source_path" >> $config_host_mak
+echo "HOST_CC=$host_cc" >> $config_host_mak
+
+native_targets=
+for target in $target_list; do
+  arch=${target%%-*}
+
+  case $target in
+    *-linux-user|*-bsd-user)
+    if probe_target_compiler $target || test -n "$container_image"; then
+        mkdir -p "common-user/native/$target"
+        config_target_mak=common-user/native/$target/config-target.mak
+        ln -sf "$source_path/common-user/native/Makefile.target" "common-user/native/$target/Makefile"
+        echo "# Automatically generated by configure - do not modify" > "$config_target_mak"
+        echo "TARGET_NAME=$arch" >> "$config_target_mak"
+        echo "TARGET=$target" >> "$config_target_mak"
+        eval "target_native_flag=\${native_flag_$target_arch}"
+        target_cflags="$target_cflags $target_native_flag"
+        write_target_makefile "build-native-library-$target" >> "$config_target_mak"
+        native_targets="$native_targets $target"
+    fi
+  ;;
+  esac
+done
+
+# if native enabled
+if test "$user_native_call" = "enabled"; then
+    echo "CONFIG_USER_NATIVE=y" >> config-host.mak
+    echo "NATIVE_TARGETS=$native_targets" >> config-host.mak
+    
+fi
+)
+
 if test "$skip_meson" = no; then
   cross="config-meson.cross.new"
   meson_quote() {
@@ -1980,6 +2029,7 @@ if test "$skip_meson" = no; then
   test "$smbd" != '' && meson_option_add "-Dsmbd=$smbd"
   test "$tcg" != enabled && meson_option_add "-Dtcg=$tcg"
   test "$vfio_user_server" != auto && meson_option_add "-Dvfio_user_server=$vfio_user_server"
+  test "$user_native_call" != auto && meson_option_add "-Duser_native_call=$user_native_call"
   run_meson() {
     NINJA=$ninja $meson setup --prefix "$prefix" "$@" $cross_arg "$PWD" "$source_path"
   }
diff --git a/docs/devel/build-system.rst b/docs/devel/build-system.rst
index 551c5a5ac0..05cfa8a21a 100644
--- a/docs/devel/build-system.rst
+++ b/docs/devel/build-system.rst
@@ -494,6 +494,10 @@ Built by configure:
   Configuration variables used to build the firmware and TCG tests,
   including paths to cross compilation toolchains.
 
+``common-user/native/config-host.mak``, ``common-user/native/*/config-target.mak``
+  Configuration variables used to build the native call libraries
+  including paths to cross compilation toolchains.
+
 ``pyvenv``
 
   A Python virtual environment that is used for all Python code running
diff --git a/meson.build b/meson.build
index 0a5cdefd4d..04e99a4f25 100644
--- a/meson.build
+++ b/meson.build
@@ -2012,6 +2012,11 @@ have_virtfs_proxy_helper = get_option('virtfs_proxy_helper') \
     .require(libcap_ng.found(), error_message: 'the virtfs proxy helper requires libcap-ng') \
     .allowed()
 
+have_user_native_call = get_option('user_native_call') \
+    .require(have_user, error_message: 'user_native_call requires user') \
+    .require(targetos == 'linux', error_message: 'user_native_call requires Linux') \
+    .allowed()
+
 if get_option('block_drv_ro_whitelist') == ''
   config_host_data.set('CONFIG_BDRV_RO_WHITELIST', '')
 else
@@ -2853,6 +2858,9 @@ foreach target : target_dirs
       error('Target @0@ is only available on a Linux host'.format(target))
     endif
     config_target += { 'CONFIG_LINUX_USER': 'y' }
+    if have_user_native_call
+      config_target += { 'CONFIG_USER_NATIVE_CALL': 'y' }
+    endif
   elif target.endswith('bsd-user')
     if 'CONFIG_BSD' not in config_host
       if default_targets
diff --git a/meson_options.txt b/meson_options.txt
index 90237389e2..57035e02f5 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -352,3 +352,5 @@ option('slirp_smbd', type : 'feature', value : 'auto',
 
 option('hexagon_idef_parser', type : 'boolean', value : true,
        description: 'use idef-parser to automatically generate TCG code for the Hexagon frontend')
+option('user_native_call', type : 'feature', value : 'disabled',
+       description: 'native bypass for library calls in user mode only')
diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh
index 5714fd93d9..9eda1898d6 100644
--- a/scripts/meson-buildoptions.sh
+++ b/scripts/meson-buildoptions.sh
@@ -173,6 +173,8 @@ meson_options_help() {
   printf "%s\n" '  tpm             TPM support'
   printf "%s\n" '  u2f             U2F emulation support'
   printf "%s\n" '  usb-redir       libusbredir support'
+  printf "%s\n" '  user-native-call'
+  printf "%s\n" '                  native bypass for library calls in user mode only'
   printf "%s\n" '  vde             vde network backend support'
   printf "%s\n" '  vdi             vdi image format support'
   printf "%s\n" '  vduse-blk-export'
@@ -472,6 +474,8 @@ _meson_option_parse() {
     --disable-u2f) printf "%s" -Du2f=disabled ;;
     --enable-usb-redir) printf "%s" -Dusb_redir=enabled ;;
     --disable-usb-redir) printf "%s" -Dusb_redir=disabled ;;
+    --enable-user-native-call) printf "%s" -Duser_native_call=enabled ;;
+    --disable-user-native-call) printf "%s" -Duser_native_call=disabled ;;
     --enable-vde) printf "%s" -Dvde=enabled ;;
     --disable-vde) printf "%s" -Dvde=disabled ;;
     --enable-vdi) printf "%s" -Dvdi=enabled ;;
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [RFC v2 2/6] Add the libnative library
  2023-06-07 16:47 [RFC v2 0/6] Native Library Calls Yeqi Fu
  2023-06-07 16:47 ` [RFC v2 1/6] build: Add configure options for native calls Yeqi Fu
@ 2023-06-07 16:47 ` Yeqi Fu
  2023-06-15  7:59   ` Alex Bennée
  2023-06-07 16:47 ` [RFC v2 3/6] target/i386: Add native library calls Yeqi Fu
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 17+ messages in thread
From: Yeqi Fu @ 2023-06-07 16:47 UTC (permalink / raw)
  To: alex.bennee; +Cc: richard.henderson, qemu-devel, Yeqi Fu, Riku Voipio

Signed-off-by: Yeqi Fu <fufuyqqqqqq@gmail.com>
---
 common-user/native/libnative.c | 65 ++++++++++++++++++++++++++++++++++
 include/native/libnative.h     | 11 ++++++
 include/native/native-func.h   | 11 ++++++
 3 files changed, 87 insertions(+)
 create mode 100644 common-user/native/libnative.c
 create mode 100644 include/native/libnative.h
 create mode 100644 include/native/native-func.h

diff --git a/common-user/native/libnative.c b/common-user/native/libnative.c
new file mode 100644
index 0000000000..d40e43c6fe
--- /dev/null
+++ b/common-user/native/libnative.c
@@ -0,0 +1,65 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "native/libnative.h"
+#include "native/native-func.h"
+
+#define STR_MACRO(str) #str
+#define STR(num) STR_MACRO(num)
+
+#if defined(TARGET_I386) || defined(TARGET_X86_64)
+
+/* unused opcode */
+#define __PREFIX_INSTR \
+    ".byte 0x0f,0xff;"
+
+#define NATIVE_CALL_EXPR(func) \
+    __PREFIX_INSTR             \
+    ".word " STR(func) ";" : ::
+#endif
+
+#if defined(TARGET_ARM) || defined(TARGET_AARCH64)
+
+/* unused syscall number */
+#define __PREFIX_INSTR \
+    "svc 0xff;"
+
+#define NATIVE_CALL_EXPR(func) \
+    __PREFIX_INSTR             \
+    ".word " STR(func) ";" : ::
+
+#endif
+
+#if defined(TARGET_MIPS) || defined(TARGET_MIPS64)
+
+/* unused bytes in syscall instructions */
+#define NATIVE_CALL_EXPR(func) \
+    ".long " STR((0x1 << 24) + (func << 8) + 0xC) ";" : ::
+
+#endif
+
+void *memcpy(void *dest, const void *src, size_t n)
+{
+    __asm__ volatile(NATIVE_CALL_EXPR(NATIVE_MEMCPY));
+}
+
+int memcmp(const void *s1, const void *s2, size_t n)
+{
+    __asm__ volatile(NATIVE_CALL_EXPR(NATIVE_MEMCMP));
+}
+void *memset(void *s, int c, size_t n)
+{
+    __asm__ volatile(NATIVE_CALL_EXPR(NATIVE_MEMSET));
+}
+char *strcpy(char *dest, const char *src)
+{
+    __asm__ volatile(NATIVE_CALL_EXPR(NATIVE_STRCPY));
+}
+int strcmp(const char *s1, const char *s2)
+{
+    __asm__ volatile(NATIVE_CALL_EXPR(NATIVE_STRCMP));
+}
+char *strcat(char *dest, const char *src)
+{
+    __asm__ volatile(NATIVE_CALL_EXPR(NATIVE_STRCAT));
+}
diff --git a/include/native/libnative.h b/include/native/libnative.h
new file mode 100644
index 0000000000..d3c24f89f4
--- /dev/null
+++ b/include/native/libnative.h
@@ -0,0 +1,11 @@
+#ifndef __LIBNATIVE_H__
+#define __LIBNATIVE_H__
+
+void *memcpy(void *dest, const void *src, size_t n);
+int memcmp(const void *s1, const void *s2, size_t n);
+void *memset(void *s, int c, size_t n);
+char *strcpy(char *dest, const char *src);
+int strcmp(const char *s1, const char *s2);
+char *strcat(char *dest, const char *src);
+
+#endif /* __LIBNATIVE_H__ */
diff --git a/include/native/native-func.h b/include/native/native-func.h
new file mode 100644
index 0000000000..d48a8e547a
--- /dev/null
+++ b/include/native/native-func.h
@@ -0,0 +1,11 @@
+#ifndef __NATIVE_FUNC_H__
+#define __NATIVE_FUNC_H__
+
+#define NATIVE_MEMCPY 0x1001
+#define NATIVE_MEMCMP 0x1002
+#define NATIVE_MEMSET 0x1003
+#define NATIVE_STRCPY 0x1004
+#define NATIVE_STRCMP 0x1005
+#define NATIVE_STRCAT 0x1006
+
+#endif
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [RFC v2 3/6] target/i386: Add native library calls
  2023-06-07 16:47 [RFC v2 0/6] Native Library Calls Yeqi Fu
  2023-06-07 16:47 ` [RFC v2 1/6] build: Add configure options for native calls Yeqi Fu
  2023-06-07 16:47 ` [RFC v2 2/6] Add the libnative library Yeqi Fu
@ 2023-06-07 16:47 ` Yeqi Fu
  2023-06-07 19:08   ` Richard Henderson
  2023-06-07 19:19   ` Richard Henderson
  2023-06-07 16:47 ` [RFC v2 4/6] target/mips: " Yeqi Fu
                   ` (2 subsequent siblings)
  5 siblings, 2 replies; 17+ messages in thread
From: Yeqi Fu @ 2023-06-07 16:47 UTC (permalink / raw)
  To: alex.bennee
  Cc: richard.henderson, qemu-devel, Yeqi Fu, Paolo Bonzini,
	Eduardo Habkost

Signed-off-by: Yeqi Fu <fufuyqqqqqq@gmail.com>
---
 target/i386/helper.h                 |  6 +++
 target/i386/tcg/translate.c          | 20 +++++++++
 target/i386/tcg/user/meson.build     |  1 +
 target/i386/tcg/user/native_helper.c | 65 ++++++++++++++++++++++++++++
 4 files changed, 92 insertions(+)
 create mode 100644 target/i386/tcg/user/native_helper.c

diff --git a/target/i386/helper.h b/target/i386/helper.h
index e627a93107..6c91655887 100644
--- a/target/i386/helper.h
+++ b/target/i386/helper.h
@@ -221,3 +221,9 @@ DEF_HELPER_3(rcrq, tl, env, tl, tl)
 #endif
 
 DEF_HELPER_1(rdrand, tl, env)
+
+#if defined(CONFIG_USER_ONLY)  && defined(CONFIG_USER_NATIVE_CALL)
+DEF_HELPER_1(native_memcpy, void, env)
+DEF_HELPER_1(native_memcmp, void, env)
+DEF_HELPER_1(native_memset, void, env)
+#endif
diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c
index 91c9c0c478..eb0c1e9566 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-func.h"
 
 #define PREFIX_REPZ   0x01
 #define PREFIX_REPNZ  0x02
@@ -6806,6 +6807,25 @@ static bool disas_insn(DisasContext *s, CPUState *cpu)
     case 0x1d0 ... 0x1fe:
         disas_insn_new(s, cpu, b);
         break;
+    /* One unknown opcode for native call */
+#if defined(CONFIG_USER_ONLY)  && defined(CONFIG_USER_NATIVE_CALL)
+    case 0x1ff:
+        uint16_t sig = x86_lduw_code(env, s);
+        switch (sig) {
+        case NATIVE_MEMCPY:
+            gen_helper_native_memcpy(cpu_env);
+            break;
+        case NATIVE_MEMSET:
+            gen_helper_native_memset(cpu_env);
+            break;
+        case NATIVE_MEMCMP:
+            gen_helper_native_memcmp(cpu_env);
+            break;
+        default:
+            goto unknown_op;
+        }
+        break;
+#endif
     default:
         goto unknown_op;
     }
diff --git a/target/i386/tcg/user/meson.build b/target/i386/tcg/user/meson.build
index 1df6bc4343..490808bd65 100644
--- a/target/i386/tcg/user/meson.build
+++ b/target/i386/tcg/user/meson.build
@@ -1,4 +1,5 @@
 i386_user_ss.add(when: ['CONFIG_TCG', 'CONFIG_USER_ONLY'], if_true: files(
   'excp_helper.c',
   'seg_helper.c',
+  'native_helper.c',
 ))
diff --git a/target/i386/tcg/user/native_helper.c b/target/i386/tcg/user/native_helper.c
new file mode 100644
index 0000000000..4a9b98eee2
--- /dev/null
+++ b/target/i386/tcg/user/native_helper.c
@@ -0,0 +1,65 @@
+/*
+ *  native function call helpers
+ */
+
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "exec/helper-proto.h"
+#include "exec/exec-all.h"
+#include "exec/cpu_ldst.h"
+#include "tcg/helper-tcg.h"
+#include "tcg/seg_helper.h"
+
+#ifdef TARGET_X86_64
+#define NATIVE_FN_W_3W()           \
+    target_ulong arg0, arg1, arg2; \
+    arg0 = env->regs[R_EDI];       \
+    arg1 = env->regs[R_ESI];       \
+    arg2 = env->regs[R_EDX];
+#else
+/*
+ *  linux x86 has several calling conventions. The following implementation
+ *  is for the most commonly used cdecl calling convention.
+ */
+#define NATIVE_FN_W_3W()                                   \
+    target_ulong arg0, arg1, arg2;                         \
+    arg0 = *(target_ulong *)g2h(cs, env->regs[R_ESP] + 4); \
+    arg1 = *(target_ulong *)g2h(cs, env->regs[R_ESP] + 8); \
+    arg2 = *(target_ulong *)g2h(cs, env->regs[R_ESP] + 12);
+#endif
+
+void helper_native_memcpy(CPUX86State *env)
+{
+    CPUState *cs = env_cpu(env);
+    NATIVE_FN_W_3W();
+    void *ret;
+    void *dest = g2h(cs, arg0);
+    void *src = g2h(cs, arg1);
+    size_t n = (size_t)arg2;
+    ret = memcpy(dest, src, n);
+    env->regs[R_EAX] = (target_ulong)h2g(ret);
+}
+
+void helper_native_memcmp(CPUX86State *env)
+{
+    CPUState *cs = env_cpu(env);
+    NATIVE_FN_W_3W();
+    int ret;
+    void *s1 = g2h(cs, arg0);
+    void *s2 = g2h(cs, arg1);
+    size_t n = (size_t)arg2;
+    ret = memcmp(s1, s2, n);
+    env->regs[R_EAX] = ret;
+}
+
+void helper_native_memset(CPUX86State *env)
+{
+    CPUState *cs = env_cpu(env);
+    NATIVE_FN_W_3W();
+    void *ret;
+    void *s = g2h(cs, arg0);
+    int c = (int)arg1;
+    size_t n = (size_t)arg2;
+    ret = memset(s, c, n);
+    env->regs[R_EAX] = (target_ulong)h2g(ret);
+}
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [RFC v2 4/6] target/mips: Add native library calls
  2023-06-07 16:47 [RFC v2 0/6] Native Library Calls Yeqi Fu
                   ` (2 preceding siblings ...)
  2023-06-07 16:47 ` [RFC v2 3/6] target/i386: Add native library calls Yeqi Fu
@ 2023-06-07 16:47 ` Yeqi Fu
  2023-06-07 19:15   ` Richard Henderson
  2023-06-07 16:47 ` [RFC v2 5/6] target/arm: " Yeqi Fu
  2023-06-07 16:47 ` [RFC v2 6/6] linux-user: Add '-native-bypass' option Yeqi Fu
  5 siblings, 1 reply; 17+ messages in thread
From: Yeqi Fu @ 2023-06-07 16:47 UTC (permalink / raw)
  To: alex.bennee
  Cc: richard.henderson, qemu-devel, Yeqi Fu,
	Philippe Mathieu-Daudé, Aurelien Jarno, Jiaxun Yang,
	Aleksandar Rikalo

Signed-off-by: Yeqi Fu <fufuyqqqqqq@gmail.com>
---
 target/mips/helper.h            |  6 ++++
 target/mips/tcg/meson.build     |  1 +
 target/mips/tcg/native_helper.c | 55 +++++++++++++++++++++++++++++++++
 target/mips/tcg/translate.c     | 20 +++++++++++-
 target/mips/tcg/translate.h     | 12 +++++++
 5 files changed, 93 insertions(+), 1 deletion(-)
 create mode 100644 target/mips/tcg/native_helper.c

diff --git a/target/mips/helper.h b/target/mips/helper.h
index de32d82e98..9fa949d78c 100644
--- a/target/mips/helper.h
+++ b/target/mips/helper.h
@@ -589,6 +589,12 @@ DEF_HELPER_FLAGS_3(dmthlip, 0, void, tl, tl, env)
 DEF_HELPER_FLAGS_3(wrdsp, 0, void, tl, tl, env)
 DEF_HELPER_FLAGS_2(rddsp, 0, tl, tl, env)
 
+#if defined(CONFIG_USER_ONLY)  && defined(CONFIG_USER_NATIVE_CALL)
+DEF_HELPER_1(native_memcpy, void, env)
+DEF_HELPER_1(native_memcmp, void, env)
+DEF_HELPER_1(native_memset, void, env)
+#endif
+
 #ifndef CONFIG_USER_ONLY
 #include "tcg/sysemu_helper.h.inc"
 #endif /* !CONFIG_USER_ONLY */
diff --git a/target/mips/tcg/meson.build b/target/mips/tcg/meson.build
index 7ee969ec8f..fb1ea64047 100644
--- a/target/mips/tcg/meson.build
+++ b/target/mips/tcg/meson.build
@@ -22,6 +22,7 @@ mips_ss.add(files(
   'txx9_translate.c',
   'vr54xx_helper.c',
   'vr54xx_translate.c',
+  'native_helper.c',
 ))
 mips_ss.add(when: 'TARGET_MIPS64', if_true: files(
   'tx79_translate.c',
diff --git a/target/mips/tcg/native_helper.c b/target/mips/tcg/native_helper.c
new file mode 100644
index 0000000000..bfd9c92e17
--- /dev/null
+++ b/target/mips/tcg/native_helper.c
@@ -0,0 +1,55 @@
+/*
+ *  native function call helpers
+ */
+
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "exec/helper-proto.h"
+#include "exec/exec-all.h"
+#include "exec/cpu_ldst.h"
+
+#if defined(CONFIG_USER_ONLY)  && defined(CONFIG_USER_NATIVE_CALL)
+
+#define NATIVE_FN_W_3W()                   \
+    target_ulong arg0, arg1, arg2;         \
+    arg0 = env->active_tc.gpr[4]; /*"a0"*/ \
+    arg1 = env->active_tc.gpr[5]; /*"a1"*/ \
+    arg2 = env->active_tc.gpr[6]; /*"a2"*/
+
+void helper_native_memcpy(CPUMIPSState *env)
+{
+    CPUState *cs = env_cpu(env);
+    NATIVE_FN_W_3W();
+    void *ret;
+    void *dest = g2h(cs, arg0);
+    void *src = g2h(cs, arg1);
+    size_t n = (size_t)arg2;
+    ret = memcpy(dest, src, n);
+    env->active_tc.gpr[2] = (target_ulong)h2g(ret);
+}
+
+void helper_native_memcmp(CPUMIPSState *env)
+{
+    CPUState *cs = env_cpu(env);
+    NATIVE_FN_W_3W();
+    int ret;
+    void *s1 = g2h(cs, arg0);
+    void *s2 = g2h(cs, arg1);
+    size_t n = (size_t)arg2;
+    ret = memcmp(s1, s2, n);
+    env->active_tc.gpr[2] = ret;
+}
+
+void helper_native_memset(CPUMIPSState *env)
+{
+    CPUState *cs = env_cpu(env);
+    NATIVE_FN_W_3W();
+    void *ret;
+    void *s = g2h(cs, arg0);
+    int c = (int)arg1;
+    size_t n = (size_t)arg2;
+    ret = memset(s, c, n);
+    env->active_tc.gpr[2] = (target_ulong)h2g(ret);
+}
+
+#endif
diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c
index a6ca2e5a3b..d68ce6bc2f 100644
--- a/target/mips/tcg/translate.c
+++ b/target/mips/tcg/translate.c
@@ -36,6 +36,7 @@
 #include "qemu/qemu-print.h"
 #include "fpu_helper.h"
 #include "translate.h"
+#include "native/native-func.h"
 
 /*
  * Many sysemu-only helpers are not reachable for user-only.
@@ -13591,7 +13592,24 @@ static void decode_opc_special(CPUMIPSState *env, DisasContext *ctx)
         gen_helper_pmon(cpu_env, tcg_constant_i32(sa));
 #endif
         break;
-    case OPC_SYSCALL:
+    case OPC_SYSCALL:  /* 00 00 00 0C */
+        if (native_bypass() && ((((ctx->opcode) >> 24) & 0xff) == 0x1)) {
+            uint16_t sig =  (ctx->opcode) >> 8 & 0xffff;
+            switch (sig) {
+            case NATIVE_MEMCPY:
+                gen_helper_native_memcpy(cpu_env);
+                break;
+            case NATIVE_MEMSET:
+                gen_helper_native_memset(cpu_env);
+                break;
+            case NATIVE_MEMCMP:
+                gen_helper_native_memcmp(cpu_env);
+                break;
+            default:
+                gen_reserved_instruction(ctx);
+            }
+            break;
+        }
         generate_exception_end(ctx, EXCP_SYSCALL);
         break;
     case OPC_BREAK:
diff --git a/target/mips/tcg/translate.h b/target/mips/tcg/translate.h
index 69f85841d2..f0112d88aa 100644
--- a/target/mips/tcg/translate.h
+++ b/target/mips/tcg/translate.h
@@ -237,3 +237,15 @@ static inline bool cpu_is_bigendian(DisasContext *ctx)
 }
 
 #endif
+
+/*
+ * Check if the native bypass feature is enabled.
+ */
+static inline bool native_bypass(void)
+{
+#if defined(CONFIG_USER_ONLY) && defined(CONFIG_USER_NATIVE_CALL)
+    return true;
+#else
+    return false;
+#endif
+}
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [RFC v2 5/6] target/arm: Add native library calls
  2023-06-07 16:47 [RFC v2 0/6] Native Library Calls Yeqi Fu
                   ` (3 preceding siblings ...)
  2023-06-07 16:47 ` [RFC v2 4/6] target/mips: " Yeqi Fu
@ 2023-06-07 16:47 ` Yeqi Fu
  2023-06-07 16:47 ` [RFC v2 6/6] linux-user: Add '-native-bypass' option Yeqi Fu
  5 siblings, 0 replies; 17+ messages in thread
From: Yeqi Fu @ 2023-06-07 16:47 UTC (permalink / raw)
  To: alex.bennee
  Cc: richard.henderson, qemu-devel, Yeqi Fu, Peter Maydell,
	open list:ARM TCG CPUs

Signed-off-by: Yeqi Fu <fufuyqqqqqq@gmail.com>
---
 target/arm/helper.c            | 47 ++++++++++++++++++++++++++++++++++
 target/arm/helper.h            |  6 +++++
 target/arm/tcg/translate-a64.c | 22 ++++++++++++++++
 target/arm/tcg/translate.c     | 25 +++++++++++++++++-
 target/arm/tcg/translate.h     | 19 ++++++++++++++
 5 files changed, 118 insertions(+), 1 deletion(-)

diff --git a/target/arm/helper.c b/target/arm/helper.c
index 0b7fd2e7e6..03fbc3724b 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -25,6 +25,7 @@
 #include "sysemu/tcg.h"
 #include "qapi/error.h"
 #include "qemu/guest-random.h"
+#include "exec/cpu_ldst.h"
 #ifdef CONFIG_TCG
 #include "semihosting/common-semi.h"
 #endif
@@ -12045,3 +12046,49 @@ void aarch64_sve_change_el(CPUARMState *env, int old_el,
     }
 }
 #endif
+
+#if defined(CONFIG_USER_ONLY)  && defined(CONFIG_USER_NATIVE_CALL)
+
+#define NATIVE_FN_W_3W()           \
+    target_ulong arg0, arg1, arg2; \
+    arg0 = env->regs[0];           \
+    arg1 = env->regs[1];           \
+    arg2 = env->regs[2];
+
+void helper_native_memcpy(CPUARMState *env)
+{
+    CPUState *cs = env_cpu(env);
+    NATIVE_FN_W_3W();
+    void *ret;
+    void *dest = g2h(cs, arg0);
+    void *src = g2h(cs, arg1);
+    size_t n = (size_t)arg2;
+    ret = memcpy(dest, src, n);
+    env->regs[0] = (target_ulong)h2g(ret);
+}
+
+void helper_native_memcmp(CPUARMState *env)
+{
+    CPUState *cs = env_cpu(env);
+    NATIVE_FN_W_3W();
+    int ret;
+    void *s1 = g2h(cs, arg0);
+    void *s2 = g2h(cs, arg1);
+    size_t n = (size_t)arg2;
+    ret = memcmp(s1, s2, n);
+    env->regs[0] = ret;
+}
+
+void helper_native_memset(CPUARMState *env)
+{
+    CPUState *cs = env_cpu(env);
+    NATIVE_FN_W_3W();
+    void *ret;
+    void *s = g2h(cs, arg0);
+    int c = (int)arg1;
+    size_t n = (size_t)arg2;
+    ret = memset(s, c, n);
+    env->regs[0] = (target_ulong)h2g(ret);
+}
+
+#endif
diff --git a/target/arm/helper.h b/target/arm/helper.h
index 3335c2b10b..57144bf6fb 100644
--- a/target/arm/helper.h
+++ b/target/arm/helper.h
@@ -1038,6 +1038,12 @@ DEF_HELPER_FLAGS_5(gvec_uclamp_s, TCG_CALL_NO_RWG,
 DEF_HELPER_FLAGS_5(gvec_uclamp_d, TCG_CALL_NO_RWG,
                    void, ptr, ptr, ptr, ptr, i32)
 
+#if defined(CONFIG_USER_ONLY)  && defined(CONFIG_USER_NATIVE_CALL)
+DEF_HELPER_1(native_memcpy, void, env)
+DEF_HELPER_1(native_memcmp, void, env)
+DEF_HELPER_1(native_memset, void, env)
+#endif
+
 #ifdef TARGET_AARCH64
 #include "tcg/helper-a64.h"
 #include "tcg/helper-sve.h"
diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c
index 741a608739..04421af6c6 100644
--- a/target/arm/tcg/translate-a64.c
+++ b/target/arm/tcg/translate-a64.c
@@ -35,6 +35,7 @@
 #include "cpregs.h"
 #include "translate-a64.h"
 #include "qemu/atomic128.h"
+#include "native/native-func.h"
 
 static TCGv_i64 cpu_X[32];
 static TCGv_i64 cpu_pc;
@@ -2291,6 +2292,9 @@ static void disas_exc(DisasContext *s, uint32_t insn)
             if (s->fgt_svc) {
                 gen_exception_insn_el(s, 0, EXCP_UDEF, syndrome, 2);
                 break;
+            } else if (native_bypass() && imm16 == 0xff) {
+                s->native_call_status = true;
+                break;
             }
             gen_ss_advance(s);
             gen_exception_insn(s, 4, EXCP_SWI, syndrome);
@@ -14203,6 +14207,24 @@ static void aarch64_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu)
     s->fp_access_checked = false;
     s->sve_access_checked = false;
 
+    if (native_bypass() && s->native_call_status) {
+        switch (insn) {
+        case NATIVE_MEMCPY:
+            gen_helper_native_memcpy(cpu_env);
+            break;
+        case NATIVE_MEMCMP:
+            gen_helper_native_memcmp(cpu_env);
+            break;
+        case NATIVE_MEMSET:
+            gen_helper_native_memset(cpu_env);
+            break;
+        default:
+            unallocated_encoding(s);
+        }
+        s->native_call_status = false;
+        return;
+    }
+
     if (s->pstate_il) {
         /*
          * Illegal execution state. This has priority over BTI
diff --git a/target/arm/tcg/translate.c b/target/arm/tcg/translate.c
index 7468476724..83ce0f7437 100644
--- a/target/arm/tcg/translate.c
+++ b/target/arm/tcg/translate.c
@@ -34,7 +34,7 @@
 #include "exec/helper-gen.h"
 #include "exec/log.h"
 #include "cpregs.h"
-
+#include "native/native-func.h"
 
 #define ENABLE_ARCH_4T    arm_dc_feature(s, ARM_FEATURE_V4T)
 #define ENABLE_ARCH_5     arm_dc_feature(s, ARM_FEATURE_V5)
@@ -58,6 +58,10 @@ TCGv_i32 cpu_CF, cpu_NF, cpu_VF, cpu_ZF;
 TCGv_i64 cpu_exclusive_addr;
 TCGv_i64 cpu_exclusive_val;
 
+#if defined(CONFIG_USER_ONLY) && !defined(TARGET_AARCH64)  \
+    && defined(CONFIG_USER_NATIVE_CALL)
+#endif
+
 #include "exec/gen-icount.h"
 
 static const char * const regnames[] =
@@ -8576,6 +8580,8 @@ static bool trans_SVC(DisasContext *s, arg_SVC *a)
         if (s->fgt_svc) {
             uint32_t syndrome = syn_aa32_svc(a->imm, s->thumb);
             gen_exception_insn_el(s, 0, EXCP_UDEF, syndrome, 2);
+        } else if (native_bypass() && a->imm == 0xff) {
+            s->native_call_status = true;
         } else {
             gen_update_pc(s, curr_insn_len(s));
             s->svc_imm = a->imm;
@@ -9372,6 +9378,23 @@ static void arm_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu)
     insn = arm_ldl_code(env, &dc->base, pc, dc->sctlr_b);
     dc->insn = insn;
     dc->base.pc_next = pc + 4;
+    if (native_bypass() && dc->native_call_status) {
+        switch (insn) {
+        case NATIVE_MEMCPY:
+            gen_helper_native_memcpy(cpu_env);
+            break;
+        case NATIVE_MEMCMP:
+            gen_helper_native_memcmp(cpu_env);
+            break;
+        case NATIVE_MEMSET:
+            gen_helper_native_memset(cpu_env);
+            break;
+        default:
+            unallocated_encoding(dc);
+        }
+        dc->native_call_status = false;
+        return;
+    }
     disas_arm_insn(dc, insn);
 
     arm_post_translate_insn(dc);
diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h
index a9d1f4adc2..19c4f7af7f 100644
--- a/target/arm/tcg/translate.h
+++ b/target/arm/tcg/translate.h
@@ -149,6 +149,13 @@ 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).
+     */
+#if defined(CONFIG_USER_ONLY)  && defined(CONFIG_USER_NATIVE_CALL)
+    bool native_call_status;
+#endif
 } DisasContext;
 
 typedef struct DisasCompare {
@@ -657,3 +664,15 @@ static inline void gen_restore_rmode(TCGv_i32 old, TCGv_ptr fpst)
     }
 
 #endif /* TARGET_ARM_TRANSLATE_H */
+
+/*
+ * Check if the native bypass feature is enabled.
+ */
+static inline bool native_bypass(void)
+{
+#if defined(CONFIG_USER_ONLY) && defined(CONFIG_USER_NATIVE_CALL)
+    return true;
+#else
+    return false;
+#endif
+}
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [RFC v2 6/6] linux-user: Add '-native-bypass' option
  2023-06-07 16:47 [RFC v2 0/6] Native Library Calls Yeqi Fu
                   ` (4 preceding siblings ...)
  2023-06-07 16:47 ` [RFC v2 5/6] target/arm: " Yeqi Fu
@ 2023-06-07 16:47 ` Yeqi Fu
  2023-06-09  5:24   ` Manos Pitsidianakis
  2023-06-12 13:23   ` Alex Bennée
  5 siblings, 2 replies; 17+ messages in thread
From: Yeqi Fu @ 2023-06-07 16:47 UTC (permalink / raw)
  To: alex.bennee; +Cc: richard.henderson, qemu-devel, Yeqi Fu, Laurent Vivier

Signed-off-by: Yeqi Fu <fufuyqqqqqq@gmail.com>
---
 include/qemu/envlist.h |  1 +
 linux-user/main.c      | 23 +++++++++++++++++
 util/envlist.c         | 56 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 80 insertions(+)

diff --git a/include/qemu/envlist.h b/include/qemu/envlist.h
index 6006dfae44..865eb18e17 100644
--- a/include/qemu/envlist.h
+++ b/include/qemu/envlist.h
@@ -7,6 +7,7 @@ envlist_t *envlist_create(void);
 void envlist_free(envlist_t *);
 int envlist_setenv(envlist_t *, const char *);
 int envlist_unsetenv(envlist_t *, const char *);
+int envlist_appendenv(envlist_t *, const char *, const char *);
 int envlist_parse_set(envlist_t *, const char *);
 int envlist_parse_unset(envlist_t *, const char *);
 char **envlist_to_environ(const envlist_t *, size_t *);
diff --git a/linux-user/main.c b/linux-user/main.c
index 5e6b2e1714..313c116b3b 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -125,6 +125,8 @@ static void usage(int exitcode);
 static const char *interp_prefix = CONFIG_QEMU_INTERP_PREFIX;
 const char *qemu_uname_release;
 
+static const char *native_lib;
+
 #if !defined(TARGET_DEFAULT_STACK_SIZE)
 /* XXX: on x86 MAP_GROWSDOWN only works if ESP <= address + 32, so
    we allocate a bigger stack. Need a better solution, for example
@@ -293,6 +295,13 @@ static void handle_arg_set_env(const char *arg)
     free(r);
 }
 
+#if defined(CONFIG_USER_ONLY)  && defined(CONFIG_USER_NATIVE_CALL)
+static void handle_arg_native_bypass(const char *arg)
+{
+    native_lib = arg;
+}
+#endif
+
 static void handle_arg_unset_env(const char *arg)
 {
     char *r, *p, *token;
@@ -522,6 +531,10 @@ static const struct qemu_argument arg_table[] = {
      "",           "Generate a /tmp/perf-${pid}.map file for perf"},
     {"jitdump",    "QEMU_JITDUMP",     false, handle_arg_jitdump,
      "",           "Generate a jit-${pid}.dump file for perf"},
+#if defined(CONFIG_USER_ONLY)  && defined(CONFIG_USER_NATIVE_CALL)
+    {"native-bypass", "QEMU_NATIVE_BYPASS", true, handle_arg_native_bypass,
+     "",           "native bypass for library calls in user mode only."},
+#endif
     {NULL, NULL, false, NULL, NULL, NULL}
 };
 
@@ -826,6 +839,16 @@ int main(int argc, char **argv, char **envp)
         }
     }
 
+    /* Set the library for native bypass  */
+    if (native_lib != NULL) {
+        char *token = malloc(strlen(native_lib) + 12);
+        strcpy(token, "LD_PRELOAD=");
+        strcat(token, native_lib);
+         if (envlist_appendenv(envlist, token, ":") != 0) {
+            usage(EXIT_FAILURE);
+        }
+    }
+
     target_environ = envlist_to_environ(envlist, NULL);
     envlist_free(envlist);
 
diff --git a/util/envlist.c b/util/envlist.c
index db937c0427..713d52497e 100644
--- a/util/envlist.c
+++ b/util/envlist.c
@@ -201,6 +201,62 @@ envlist_unsetenv(envlist_t *envlist, const char *env)
     return (0);
 }
 
+/*
+ * Appends environment value to envlist. If the environment
+ * variable already exists, the new value is appended to the
+ * existing one.
+ *
+ * Returns 0 in success, errno otherwise.
+ */
+int
+envlist_appendenv(envlist_t *envlist, const char *env, const char *separator)
+{
+    struct envlist_entry *entry = NULL;
+    const char *eq_sign;
+    size_t envname_len;
+
+    if ((envlist == NULL) || (env == NULL)) {
+        return (EINVAL);
+    }
+
+    /* find out first equals sign in given env */
+    eq_sign = strchr(env, '=');
+    if (eq_sign == NULL) {
+        return (EINVAL);
+    }
+    envname_len = eq_sign - env + 1;
+
+    /*
+     * If there already exists variable with given name,
+     * we append the new value to the existing one.
+     */
+    for (entry = envlist->el_entries.lh_first; entry != NULL;
+        entry = entry->ev_link.le_next) {
+        if (strncmp(entry->ev_var, env, envname_len) == 0) {
+            break;
+        }
+    }
+
+    if (entry != NULL) {
+        char *new_env_value = NULL;
+        size_t new_env_len = strlen(entry->ev_var) + strlen(eq_sign)
+            + strlen(separator) + 1;
+        new_env_value = g_malloc(new_env_len);
+        strcpy(new_env_value, entry->ev_var);
+        strcat(new_env_value, separator);
+        strcat(new_env_value, eq_sign + 1);
+        g_free((char *)entry->ev_var);
+        entry->ev_var = new_env_value;
+    } else {
+        envlist->el_count++;
+        entry = g_malloc(sizeof(*entry));
+        entry->ev_var = g_strdup(env);
+        QLIST_INSERT_HEAD(&envlist->el_entries, entry, ev_link);
+    }
+
+    return (0);
+}
+
 /*
  * Returns given envlist as array of strings (in same form that
  * global variable environ is).  Caller must free returned memory
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: [RFC v2 3/6] target/i386: Add native library calls
  2023-06-07 16:47 ` [RFC v2 3/6] target/i386: Add native library calls Yeqi Fu
@ 2023-06-07 19:08   ` Richard Henderson
  2023-06-07 19:19   ` Richard Henderson
  1 sibling, 0 replies; 17+ messages in thread
From: Richard Henderson @ 2023-06-07 19:08 UTC (permalink / raw)
  To: Yeqi Fu, alex.bennee; +Cc: qemu-devel, Paolo Bonzini, Eduardo Habkost

On 6/7/23 09:47, Yeqi Fu wrote:
> +    arg0 = *(target_ulong *)g2h(cs, env->regs[R_ESP] + 4); \
> +    arg1 = *(target_ulong *)g2h(cs, env->regs[R_ESP] + 8); \
> +    arg2 = *(target_ulong *)g2h(cs, env->regs[R_ESP] + 12);

This is not correct, and will fail on big-endian hosts.

You need to use

     uintptr_t ra = GETPC();
     cpu_ldl_data_ra(env, guest_pointer, ra);

which will (amongst other things) take care of the byte swapping.

> +void helper_native_memcpy(CPUX86State *env)
> +{
> +    CPUState *cs = env_cpu(env);
> +    NATIVE_FN_W_3W();
> +    void *ret;
> +    void *dest = g2h(cs, arg0);
> +    void *src = g2h(cs, arg1);
> +    size_t n = (size_t)arg2;
> +    ret = memcpy(dest, src, n);
> +    env->regs[R_EAX] = (target_ulong)h2g(ret);
> +}

You need to do something for the case in which either src or dst is not accessible.

Routines like cpu_ldl_data_ra handle this for you, but you don't want to use that for memcpy.

There are several ways of doing this.  None of the existing helpers are ideal.

(A) void *dest = probe_write(env, arg0, arg2, MMU_USER_IDX, ra);
     void *src = probe_read(env, arg1, arg2, MMU_USER_IDX, ra);

which will raise SIGSEGV in case any byte of either region is not correctly mapped, and 
also perform the guest-to-host address remapping.  However, probe_* are written to expect 
probing of no more than one page.  Which means you'd need a loop, processing remaining 
page fractions.

(B) There is page_check_range(), which can check a large region, but doesn't handle 
address translation.  And you still wind up with a race condition if another thread 
changes page mappings at the same time.

(C) Perform the address translation etc yourself, and then protect the actual host memory 
operation in the same way as exec/cpu_ldst.h functions:

     set_helper_retaddr(ra);
     memcpy(dest, src, n);
     clear_helper_retaddr();

In this case you must also validate that 'n' is representable.  This is only an issue for 
32-bit host and 64-bit guest.  A check like (arg2 > SIZE_MAX) is likely to generate a 
silly warning about always false comparison on 64-bit hosts.  Therefore I suggest

     if (n != arg2) {
         /*
          * Overflow of size_t means that sequential pointer access would wrap.
          * We know that NULL is unmapped, so at least that one byte would fault.
          * There is nothing in the specification of memcpy that requires bytes
          * to be accessed in order, so we are allowed to fault early.
          */
         cpu_loop_exit_sigsegv(env_cpu(env), 0, MMU_DATA_LOAD, true, ra);
     }

Finally, you know the return value from the specification of memcpy: arg0.
There is no need to remap the return value back from host to guest space.


r~


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC v2 4/6] target/mips: Add native library calls
  2023-06-07 16:47 ` [RFC v2 4/6] target/mips: " Yeqi Fu
@ 2023-06-07 19:15   ` Richard Henderson
  0 siblings, 0 replies; 17+ messages in thread
From: Richard Henderson @ 2023-06-07 19:15 UTC (permalink / raw)
  To: Yeqi Fu, alex.bennee
  Cc: qemu-devel, Philippe Mathieu-Daudé, Aurelien Jarno,
	Jiaxun Yang, Aleksandar Rikalo

On 6/7/23 09:47, Yeqi Fu wrote:
> +void helper_native_memcpy(CPUMIPSState *env)
> +{
> +    CPUState *cs = env_cpu(env);
> +    NATIVE_FN_W_3W();
> +    void *ret;
> +    void *dest = g2h(cs, arg0);
> +    void *src = g2h(cs, arg1);
> +    size_t n = (size_t)arg2;
> +    ret = memcpy(dest, src, n);
> +    env->active_tc.gpr[2] = (target_ulong)h2g(ret);
> +}

I would expect everything except for the guest ABI to be handled by common code, so that 
you do not have N copies of every native emulated function.  This needs to be something like

abi_ptr do_native_memcpy(CPUArchState *env, abi_ptr dst, abi_ptr src,
                          abi_ptr len, uintptr_t ra);

void helper_native_memcpy(CPUMIPSState *env)
{
     env->active_tc.gpr[2] =
         do_native_memcpy(env, env->active_tc.gpr[4],
                          env->active_tc.gpr[5],
                          env->active_tc.gpr[6], GETPC());
}

Even better, provide some guest abstraction akin to va_start/va_arg so that all of the 
per-native function code becomes shared.


r~


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC v2 3/6] target/i386: Add native library calls
  2023-06-07 16:47 ` [RFC v2 3/6] target/i386: Add native library calls Yeqi Fu
  2023-06-07 19:08   ` Richard Henderson
@ 2023-06-07 19:19   ` Richard Henderson
  1 sibling, 0 replies; 17+ messages in thread
From: Richard Henderson @ 2023-06-07 19:19 UTC (permalink / raw)
  To: Yeqi Fu, alex.bennee; +Cc: qemu-devel, Paolo Bonzini, Eduardo Habkost

On 6/7/23 09:47, Yeqi Fu wrote:
> +    /* One unknown opcode for native call */
> +#if defined(CONFIG_USER_ONLY)  && defined(CONFIG_USER_NATIVE_CALL)
> +    case 0x1ff:
> +        uint16_t sig = x86_lduw_code(env, s);
> +        switch (sig) {
> +        case NATIVE_MEMCPY:
> +            gen_helper_native_memcpy(cpu_env);
> +            break;
> +        case NATIVE_MEMSET:
> +            gen_helper_native_memset(cpu_env);
> +            break;
> +        case NATIVE_MEMCMP:
> +            gen_helper_native_memcmp(cpu_env);
> +            break;
> +        default:
> +            goto unknown_op;
> +        }
> +        break;
> +#endif

This bit of code must be protected by native_calls_enabled() or some such, as we do with 
semihosting_enabled().

Which means that patch 6 should come before this, so that native_calls_enabled() can be 
true if and only if "-native-bypass" is given.


r~


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC v2 1/6] build: Add configure options for native calls
  2023-06-07 16:47 ` [RFC v2 1/6] build: Add configure options for native calls Yeqi Fu
@ 2023-06-09  5:08   ` Manos Pitsidianakis
  2023-06-12 11:54   ` Alex Bennée
  1 sibling, 0 replies; 17+ messages in thread
From: Manos Pitsidianakis @ 2023-06-09  5:08 UTC (permalink / raw)
  To: Yeqi Fu
  Cc: richard.henderson, alex.bennee, qemu-devel, Paolo Bonzini,
	Thomas Huth, Riku Voipio, Daniel P. Berrangé ,
	Marc-André Lureau, Philippe Mathieu-Daudé 

On Wed, 07 Jun 2023 19:47, Yeqi Fu <fufuyqqqqqq@gmail.com> wrote:
>+have_user_native_call = get_option('user_native_call') \
>+    .require(have_user, error_message: 'user_native_call requires user') \
>+    .require(targetos == 'linux', error_message: 'user_native_call requires Linux') \
>+    .allowed()


Is there a check for accepting user_native_call only for supported CPU 
user targets?

----

By the way, a question, would native calls be possible on BSD? I assume 
if yes it'd be a task for another time.


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC v2 6/6] linux-user: Add '-native-bypass' option
  2023-06-07 16:47 ` [RFC v2 6/6] linux-user: Add '-native-bypass' option Yeqi Fu
@ 2023-06-09  5:24   ` Manos Pitsidianakis
  2023-06-12 13:06     ` Alex Bennée
  2023-06-12 13:23   ` Alex Bennée
  1 sibling, 1 reply; 17+ messages in thread
From: Manos Pitsidianakis @ 2023-06-09  5:24 UTC (permalink / raw)
  To: Yeqi Fu; +Cc: richard.henderson, qemu-devel, Laurent Vivier, alex.bennee

On Wed, 07 Jun 2023 19:47, Yeqi Fu <fufuyqqqqqq@gmail.com> wrote:
>--- a/linux-user/main.c
>+++ b/linux-user/main.c
>+    /* Set the library for native bypass  */
>+    if (native_lib != NULL) {
>+        char *token = malloc(strlen(native_lib) + 12);

malloc() can fail (in rare circumstances). Check for the return value 
here. Or use g_malloc() which terminates on alloc failure.

>+        strcpy(token, "LD_PRELOAD=");
>+        strcat(token, native_lib);

(You could alternatively use snprintf() here)

> 
>diff --git a/util/envlist.c b/util/envlist.c
>index db937c0427..713d52497e 100644
>+int
>+envlist_appendenv(envlist_t *envlist, const char *env, const char *separator)
>+{
>+    struct envlist_entry *entry = NULL;
>+    const char *eq_sign;
>+    size_t envname_len;
>+
>+    if ((envlist == NULL) || (env == NULL)) {

separator must not be NULL as well.

>+        return (EINVAL);

Unnecessary parentheses here and in later returns.

>+    }
>+
>+    /* find out first equals sign in given env */
>+    eq_sign = strchr(env, '=');
>+    if (eq_sign == NULL) {

Perhaps you can return an error message to the user here also, and 
explain why it failed. You can do that by passing an error message 
pointer with the function arguments.

By the way, if strchr(strchr(env, '='), '=') != NULL, shouldn't this 
fail also?


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC v2 1/6] build: Add configure options for native calls
  2023-06-07 16:47 ` [RFC v2 1/6] build: Add configure options for native calls Yeqi Fu
  2023-06-09  5:08   ` Manos Pitsidianakis
@ 2023-06-12 11:54   ` Alex Bennée
  2023-06-12 13:02     ` Alex Bennée
  1 sibling, 1 reply; 17+ messages in thread
From: Alex Bennée @ 2023-06-12 11:54 UTC (permalink / raw)
  To: Yeqi Fu
  Cc: richard.henderson, qemu-devel, Paolo Bonzini, Thomas Huth,
	Riku Voipio, Daniel P. Berrangé, Marc-André Lureau,
	Philippe Mathieu-Daudé


Yeqi Fu <fufuyqqqqqq@gmail.com> writes:

> Signed-off-by: Yeqi Fu <fufuyqqqqqq@gmail.com>
> ---
>  Makefile                            |  4 +++
>  common-user/native/Makefile.include |  9 ++++++
>  common-user/native/Makefile.target  | 22 +++++++++++++
>  configure                           | 50 +++++++++++++++++++++++++++++
>  docs/devel/build-system.rst         |  4 +++
>  meson.build                         |  8 +++++
>  meson_options.txt                   |  2 ++
>  scripts/meson-buildoptions.sh       |  4 +++
>  8 files changed, 103 insertions(+)
>  create mode 100644 common-user/native/Makefile.include
>  create mode 100644 common-user/native/Makefile.target
>
> diff --git a/Makefile b/Makefile
> index 3c7d67142f..923da109bf 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -185,6 +185,10 @@ SUBDIR_MAKEFLAGS=$(if $(V),,--no-print-directory --quiet)
>  
>  include $(SRC_PATH)/tests/Makefile.include
>  
> +ifeq ($(CONFIG_USER_NATIVE),y)
> +	include $(SRC_PATH)/common-user/native/Makefile.include
> +endif
> +
>  all: recurse-all
>  
>  ROMS_RULES=$(foreach t, all clean distclean, $(addsuffix /$(t), $(ROMS)))
> diff --git a/common-user/native/Makefile.include b/common-user/native/Makefile.include
> new file mode 100644
> index 0000000000..40d20bcd4c
> --- /dev/null
> +++ b/common-user/native/Makefile.include
> @@ -0,0 +1,9 @@
> +.PHONY: build-native
> +build-native: $(NATIVE_TARGETS:%=build-native-library-%)
> +$(NATIVE_TARGETS:%=build-native-library-%): build-native-library-%:
> +	$(call quiet-command, \
> +	    $(MAKE) -C common-user/native/$* $(SUBDIR_MAKEFLAGS), \
> +	"BUILD","$* native library")
> +# endif
> +
> +all: build-native

I think it would be better if we could add the targets via meson and let
it deal with the multiple versions. I will defer to Paolo on how to do
this though.


> diff --git a/common-user/native/Makefile.target b/common-user/native/Makefile.target
> new file mode 100644
> index 0000000000..1038367b37
> --- /dev/null
> +++ b/common-user/native/Makefile.target
> @@ -0,0 +1,22 @@
> +# -*- Mode: makefile -*-
> +#
> +# Library for native calls 
> +#
> +
> +all:
> +-include ../config-host.mak
> +-include config-target.mak
> +
> +CFLAGS+=-I$(SRC_PATH)/include -O1 -fPIC -shared -fno-stack-protector
> +LDFLAGS+=
> +
> +SRC = $(SRC_PATH)/common-user/native/libnative.c
> +TARGET = libnative.so
> +
> +all: $(TARGET)
> +
> +$(TARGET): $(SRC)
> +	$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS)
> +
> +clean:
> +	rm -f $(TARGET)
> diff --git a/configure b/configure
> index 2a556d14c9..cc94d10c98 100755
> --- a/configure
> +++ b/configure
> @@ -275,6 +275,7 @@ use_containers="yes"
>  gdb_bin=$(command -v "gdb-multiarch" || command -v "gdb")
>  gdb_arches=""
>  werror=""
> +user_native_call="disabled"
>  
>  # Don't accept a target_list environment variable.
>  unset target_list
> @@ -787,6 +788,10 @@ for opt do
>    ;;
>    --disable-vfio-user-server) vfio_user_server="disabled"
>    ;;
> +  --enable-user-native-call) user_native_call="enabled"
> +  ;;
> +  --disable-user-native-call) user_native_call="disabled"
> +  ;;

I'm not sure it's worth the configuration control here. We can embed if
a given frontend has support for native calls in:

  config/targets/FOO-linux-user.mak

and simply add the symbol when each front end is enabled.

>    # everything else has the same name in configure and meson
>    --*) meson_option_parse "$opt" "$optarg"
>    ;;
> @@ -1898,6 +1903,50 @@ if test "$tcg" = "enabled"; then
>  fi
>  )
>  
> +# common-user/native configuration
> +native_flag_i386="-DTARGET_I386"
> +native_flag_x86_64="-DTARGET_X86_64"
> +native_flag_mips="-DTARGET_MIPS"
> +native_flag_mips64="-DTARGET_MIPS64"
> +native_flag_arm="-DTARGET_ARM"
> +native_flag_aarch64="-DTARGET_AARCH64"

As we have target names already in the per-target configs we could use
that instead and build the cflags there.

> +
> +(config_host_mak=common-user/native/config-host.mak
> +mkdir -p common-user/native
> +echo "# Automatically generated by configure - do not modify" > $config_host_mak
> +echo "SRC_PATH=$source_path" >> $config_host_mak
> +echo "HOST_CC=$host_cc" >> $config_host_mak
> +
> +native_targets=
> +for target in $target_list; do
> +  arch=${target%%-*}
> +
> +  case $target in
> +    *-linux-user|*-bsd-user)
> +    if probe_target_compiler $target || test -n "$container_image"; then
> +        mkdir -p "common-user/native/$target"
> +        config_target_mak=common-user/native/$target/config-target.mak
> +        ln -sf "$source_path/common-user/native/Makefile.target" "common-user/native/$target/Makefile"
> +        echo "# Automatically generated by configure - do not modify" > "$config_target_mak"
> +        echo "TARGET_NAME=$arch" >> "$config_target_mak"
> +        echo "TARGET=$target" >> "$config_target_mak"
> +        eval "target_native_flag=\${native_flag_$target_arch}"
> +        target_cflags="$target_cflags $target_native_flag"
> +        write_target_makefile "build-native-library-$target" >> "$config_target_mak"
> +        native_targets="$native_targets $target"
> +    fi
> +  ;;
> +  esac
> +done

This is basically replicating what we already have in
tests/tcg/FOO-linux-user/config-target.mak. I would suggest moving those
into a common location ($BUILD/targets/foo/compiler.mak) and then fixing
up TCG tests to use the new location. When you add the native libs you
can use the same configs.

> +
> +# if native enabled
> +if test "$user_native_call" = "enabled"; then
> +    echo "CONFIG_USER_NATIVE=y" >> config-host.mak
> +    echo "NATIVE_TARGETS=$native_targets" >> config-host.mak
> +    
> +fi
> +)
> +

see above about putting CONFIG_USER_NATIVE directly into the target mak fragments.

>  if test "$skip_meson" = no; then
>    cross="config-meson.cross.new"
>    meson_quote() {
> @@ -1980,6 +2029,7 @@ if test "$skip_meson" = no; then
>    test "$smbd" != '' && meson_option_add "-Dsmbd=$smbd"
>    test "$tcg" != enabled && meson_option_add "-Dtcg=$tcg"
>    test "$vfio_user_server" != auto && meson_option_add "-Dvfio_user_server=$vfio_user_server"
> +  test "$user_native_call" != auto && meson_option_add
>    "-Duser_native_call=$user_native_call"

and dropping this.

>    run_meson() {
>      NINJA=$ninja $meson setup --prefix "$prefix" "$@" $cross_arg "$PWD" "$source_path"
>    }
> diff --git a/docs/devel/build-system.rst b/docs/devel/build-system.rst
> index 551c5a5ac0..05cfa8a21a 100644
> --- a/docs/devel/build-system.rst
> +++ b/docs/devel/build-system.rst
> @@ -494,6 +494,10 @@ Built by configure:
>    Configuration variables used to build the firmware and TCG tests,
>    including paths to cross compilation toolchains.
>  
> +``common-user/native/config-host.mak``, ``common-user/native/*/config-target.mak``
> +  Configuration variables used to build the native call libraries
> +  including paths to cross compilation toolchains.
> +

Not needed if we re-use the TCG stuff. But remember to update the
section above when moving them.

>  ``pyvenv``
>  
>    A Python virtual environment that is used for all Python code running
> diff --git a/meson.build b/meson.build
> index 0a5cdefd4d..04e99a4f25 100644
> --- a/meson.build
> +++ b/meson.build
> @@ -2012,6 +2012,11 @@ have_virtfs_proxy_helper = get_option('virtfs_proxy_helper') \
>      .require(libcap_ng.found(), error_message: 'the virtfs proxy helper requires libcap-ng') \
>      .allowed()
>  
> +have_user_native_call = get_option('user_native_call') \
> +    .require(have_user, error_message: 'user_native_call requires user') \
> +    .require(targetos == 'linux', error_message: 'user_native_call requires Linux') \
> +    .allowed()
> +
>  if get_option('block_drv_ro_whitelist') == ''
>    config_host_data.set('CONFIG_BDRV_RO_WHITELIST', '')
>  else
> @@ -2853,6 +2858,9 @@ foreach target : target_dirs
>        error('Target @0@ is only available on a Linux host'.format(target))
>      endif
>      config_target += { 'CONFIG_LINUX_USER': 'y' }
> +    if have_user_native_call
> +      config_target += { 'CONFIG_USER_NATIVE_CALL': 'y' }
> +    endif

Not needed? Isn't CONFIG_USER_NATIVE an equivalent test?

>    elif target.endswith('bsd-user')
>      if 'CONFIG_BSD' not in config_host
>        if default_targets
> diff --git a/meson_options.txt b/meson_options.txt
> index 90237389e2..57035e02f5 100644
> --- a/meson_options.txt
> +++ b/meson_options.txt
> @@ -352,3 +352,5 @@ option('slirp_smbd', type : 'feature', value : 'auto',
>  
>  option('hexagon_idef_parser', type : 'boolean', value : true,
>         description: 'use idef-parser to automatically generate TCG code for the Hexagon frontend')
> +option('user_native_call', type : 'feature', value : 'disabled',
> +       description: 'native bypass for library calls in user mode only')
> diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh
> index 5714fd93d9..9eda1898d6 100644
> --- a/scripts/meson-buildoptions.sh
> +++ b/scripts/meson-buildoptions.sh
> @@ -173,6 +173,8 @@ meson_options_help() {
>    printf "%s\n" '  tpm             TPM support'
>    printf "%s\n" '  u2f             U2F emulation support'
>    printf "%s\n" '  usb-redir       libusbredir support'
> +  printf "%s\n" '  user-native-call'
> +  printf "%s\n" '                  native bypass for library calls in user mode only'
>    printf "%s\n" '  vde             vde network backend support'
>    printf "%s\n" '  vdi             vdi image format support'
>    printf "%s\n" '  vduse-blk-export'
> @@ -472,6 +474,8 @@ _meson_option_parse() {
>      --disable-u2f) printf "%s" -Du2f=disabled ;;
>      --enable-usb-redir) printf "%s" -Dusb_redir=enabled ;;
>      --disable-usb-redir) printf "%s" -Dusb_redir=disabled ;;
> +    --enable-user-native-call) printf "%s" -Duser_native_call=enabled ;;
> +    --disable-user-native-call) printf "%s" -Duser_native_call=disabled ;;
>      --enable-vde) printf "%s" -Dvde=enabled ;;
>      --disable-vde) printf "%s" -Dvde=disabled ;;
>      --enable-vdi) printf "%s" -Dvdi=enabled ;;

And then you can drop this.

-- 
Alex Bennée
Virtualisation Tech Lead @ Linaro


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC v2 1/6] build: Add configure options for native calls
  2023-06-12 11:54   ` Alex Bennée
@ 2023-06-12 13:02     ` Alex Bennée
  0 siblings, 0 replies; 17+ messages in thread
From: Alex Bennée @ 2023-06-12 13:02 UTC (permalink / raw)
  To: Alex Bennée
  Cc: Yeqi Fu, richard.henderson, qemu-devel, Paolo Bonzini,
	Thomas Huth, Riku Voipio, Daniel P. Berrangé,
	Marc-André Lureau, Philippe Mathieu-Daudé


Alex Bennée <alex.bennee@linaro.org> writes:

> Yeqi Fu <fufuyqqqqqq@gmail.com> writes:
>
>> Signed-off-by: Yeqi Fu <fufuyqqqqqq@gmail.com>
>> ---
>>  Makefile                            |  4 +++
>>  common-user/native/Makefile.include |  9 ++++++
>>  common-user/native/Makefile.target  | 22 +++++++++++++
>>  configure                           | 50 +++++++++++++++++++++++++++++
>>  docs/devel/build-system.rst         |  4 +++
>>  meson.build                         |  8 +++++
>>  meson_options.txt                   |  2 ++
>>  scripts/meson-buildoptions.sh       |  4 +++
>>  8 files changed, 103 insertions(+)
>>  create mode 100644 common-user/native/Makefile.include
>>  create mode 100644 common-user/native/Makefile.target
>>
>> diff --git a/Makefile b/Makefile
>> index 3c7d67142f..923da109bf 100644
>> --- a/Makefile
>> +++ b/Makefile
>> @@ -185,6 +185,10 @@ SUBDIR_MAKEFLAGS=$(if $(V),,--no-print-directory --quiet)
>>  
>>  include $(SRC_PATH)/tests/Makefile.include
>>  
>> +ifeq ($(CONFIG_USER_NATIVE),y)
>> +	include $(SRC_PATH)/common-user/native/Makefile.include
>> +endif
>> +
>>  all: recurse-all
>>  
>>  ROMS_RULES=$(foreach t, all clean distclean, $(addsuffix /$(t), $(ROMS)))
>> diff --git a/common-user/native/Makefile.include b/common-user/native/Makefile.include
>> new file mode 100644
>> index 0000000000..40d20bcd4c
>> --- /dev/null
>> +++ b/common-user/native/Makefile.include
>> @@ -0,0 +1,9 @@
>> +.PHONY: build-native
>> +build-native: $(NATIVE_TARGETS:%=build-native-library-%)
>> +$(NATIVE_TARGETS:%=build-native-library-%): build-native-library-%:
>> +	$(call quiet-command, \
>> +	    $(MAKE) -C common-user/native/$* $(SUBDIR_MAKEFLAGS), \
>> +	"BUILD","$* native library")
>> +# endif
>> +
>> +all: build-native
>
> I think it would be better if we could add the targets via meson and let
> it deal with the multiple versions. I will defer to Paolo on how to do
> this though.

OK Paolo said we won't expose cross compilers to meson so we are stuck
with pure makefiles for now... however:

<snip>
>> +
>> +(config_host_mak=common-user/native/config-host.mak
>> +mkdir -p common-user/native
>> +echo "# Automatically generated by configure - do not modify" > $config_host_mak
>> +echo "SRC_PATH=$source_path" >> $config_host_mak
>> +echo "HOST_CC=$host_cc" >> $config_host_mak
>> +
>> +native_targets=
>> +for target in $target_list; do
>> +  arch=${target%%-*}
>> +
>> +  case $target in
>> +    *-linux-user|*-bsd-user)
>> +    if probe_target_compiler $target || test -n "$container_image"; then
>> +        mkdir -p "common-user/native/$target"
>> +        config_target_mak=common-user/native/$target/config-target.mak
>> +        ln -sf "$source_path/common-user/native/Makefile.target" "common-user/native/$target/Makefile"
>> +        echo "# Automatically generated by configure - do not modify" > "$config_target_mak"
>> +        echo "TARGET_NAME=$arch" >> "$config_target_mak"
>> +        echo "TARGET=$target" >> "$config_target_mak"
>> +        eval "target_native_flag=\${native_flag_$target_arch}"
>> +        target_cflags="$target_cflags $target_native_flag"
>> +        write_target_makefile "build-native-library-$target" >> "$config_target_mak"
>> +        native_targets="$native_targets $target"
>> +    fi
>> +  ;;
>> +  esac
>> +done
>
> This is basically replicating what we already have in
> tests/tcg/FOO-linux-user/config-target.mak. I would suggest moving those
> into a common location ($BUILD/targets/foo/compiler.mak) and then fixing
> up TCG tests to use the new location. When you add the native libs you
> can use the same configs.

This should be merged with the existing config_target code.

-- 
Alex Bennée
Virtualisation Tech Lead @ Linaro


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC v2 6/6] linux-user: Add '-native-bypass' option
  2023-06-09  5:24   ` Manos Pitsidianakis
@ 2023-06-12 13:06     ` Alex Bennée
  0 siblings, 0 replies; 17+ messages in thread
From: Alex Bennée @ 2023-06-12 13:06 UTC (permalink / raw)
  To: Manos Pitsidianakis
  Cc: Yeqi Fu, richard.henderson, qemu-devel, Laurent Vivier


Manos Pitsidianakis <manos.pitsidianakis@linaro.org> writes:

> On Wed, 07 Jun 2023 19:47, Yeqi Fu <fufuyqqqqqq@gmail.com> wrote:
>>--- a/linux-user/main.c
>>+++ b/linux-user/main.c
>>+    /* Set the library for native bypass  */
>>+    if (native_lib != NULL) {
>>+        char *token = malloc(strlen(native_lib) + 12);
>
> malloc() can fail (in rare circumstances). Check for the return value
> here. Or use g_malloc() which terminates on alloc failure.

We avoid malloc in favour of g_malloc(). You can use g_try_malloc for
certain cases (although this is not one of them). However you can make
this glibs problem with something like:

    /* Set the library for native bypass  */
    if (native_lib != NULL) {
        GString *lib = g_string_new(native_lib);
        lib = g_string_prepend(lib, "LD_PRELOAD=");
        if (envlist_appendenv(envlist, g_string_free(lib, false), ":") != 0) {
            usage(EXIT_FAILURE);
        }
    }


>
>>+        strcpy(token, "LD_PRELOAD=");
>>+        strcat(token, native_lib);
>
> (You could alternatively use snprintf() here)

We have a section on strings in the developer manual:

 https://qemu.readthedocs.io/en/latest/devel/style.html#string-manipulation

so we have things like pstrcat and pstrcpy. However this isn't criticl
performance path so GString provides a nice memory safe wrapper for all
this sort of manipulation.

<snip>

-- 
Alex Bennée
Virtualisation Tech Lead @ Linaro


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC v2 6/6] linux-user: Add '-native-bypass' option
  2023-06-07 16:47 ` [RFC v2 6/6] linux-user: Add '-native-bypass' option Yeqi Fu
  2023-06-09  5:24   ` Manos Pitsidianakis
@ 2023-06-12 13:23   ` Alex Bennée
  1 sibling, 0 replies; 17+ messages in thread
From: Alex Bennée @ 2023-06-12 13:23 UTC (permalink / raw)
  To: Yeqi Fu; +Cc: richard.henderson, qemu-devel, Laurent Vivier


Yeqi Fu <fufuyqqqqqq@gmail.com> writes:

> Signed-off-by: Yeqi Fu <fufuyqqqqqq@gmail.com>
> ---
>  include/qemu/envlist.h |  1 +
>  linux-user/main.c      | 23 +++++++++++++++++
>  util/envlist.c         | 56 ++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 80 insertions(+)
>
> diff --git a/include/qemu/envlist.h b/include/qemu/envlist.h
> index 6006dfae44..865eb18e17 100644
> --- a/include/qemu/envlist.h
> +++ b/include/qemu/envlist.h
> @@ -7,6 +7,7 @@ envlist_t *envlist_create(void);
>  void envlist_free(envlist_t *);
>  int envlist_setenv(envlist_t *, const char *);
>  int envlist_unsetenv(envlist_t *, const char *);
> +int envlist_appendenv(envlist_t *, const char *, const char *);
>  int envlist_parse_set(envlist_t *, const char *);
>  int envlist_parse_unset(envlist_t *, const char *);
>  char **envlist_to_environ(const envlist_t *, size_t *);
> diff --git a/linux-user/main.c b/linux-user/main.c
> index 5e6b2e1714..313c116b3b 100644
> --- a/linux-user/main.c
> +++ b/linux-user/main.c
> @@ -125,6 +125,8 @@ static void usage(int exitcode);
>  static const char *interp_prefix = CONFIG_QEMU_INTERP_PREFIX;
>  const char *qemu_uname_release;
>  
> +static const char *native_lib;
> +
>  #if !defined(TARGET_DEFAULT_STACK_SIZE)
>  /* XXX: on x86 MAP_GROWSDOWN only works if ESP <= address + 32, so
>     we allocate a bigger stack. Need a better solution, for example
> @@ -293,6 +295,13 @@ static void handle_arg_set_env(const char *arg)
>      free(r);
>  }
>  
> +#if defined(CONFIG_USER_ONLY)  && defined(CONFIG_USER_NATIVE_CALL)

CONFIG_USER_ONLY is a pointless check as by definition this file is
user-mode only.

> +static void handle_arg_native_bypass(const char *arg)
> +{
> +    native_lib = arg;

canonicalise the path and maybe verify it exists/is accessible?

> +}
> +#endif
> +
>  static void handle_arg_unset_env(const char *arg)
>  {
>      char *r, *p, *token;
> @@ -522,6 +531,10 @@ static const struct qemu_argument arg_table[] = {
>       "",           "Generate a /tmp/perf-${pid}.map file for perf"},
>      {"jitdump",    "QEMU_JITDUMP",     false, handle_arg_jitdump,
>       "",           "Generate a jit-${pid}.dump file for perf"},
> +#if defined(CONFIG_USER_ONLY)  && defined(CONFIG_USER_NATIVE_CALL)

see above re CONFIG_USER_ONLY.

> +    {"native-bypass", "QEMU_NATIVE_BYPASS", true, handle_arg_native_bypass,
> +     "",           "native bypass for library calls in user mode only."},
> +#endif
>      {NULL, NULL, false, NULL, NULL, NULL}
>  };
>  
> @@ -826,6 +839,16 @@ int main(int argc, char **argv, char **envp)
>          }
>      }
>  
> +    /* Set the library for native bypass  */
> +    if (native_lib != NULL) {
> +        char *token = malloc(strlen(native_lib) + 12);
> +        strcpy(token, "LD_PRELOAD=");
> +        strcat(token, native_lib);
> +         if (envlist_appendenv(envlist, token, ":") != 0) {
> +            usage(EXIT_FAILURE);
> +        }
> +    }
> +
>      target_environ = envlist_to_environ(envlist, NULL);
>      envlist_free(envlist);
>  
> diff --git a/util/envlist.c b/util/envlist.c
> index db937c0427..713d52497e 100644
> --- a/util/envlist.c
> +++ b/util/envlist.c
> @@ -201,6 +201,62 @@ envlist_unsetenv(envlist_t *envlist, const char *env)
>      return (0);
>  }
>

I think adding this function should be a separate commit. It would be
nice to add some unittests for the functionality while we are at it.

> +/*
> + * Appends environment value to envlist. If the environment
> + * variable already exists, the new value is appended to the
> + * existing one.
> + *
> + * Returns 0 in success, errno otherwise.
> + */
> +int
> +envlist_appendenv(envlist_t *envlist, const char *env, const char *separator)
> +{
> +    struct envlist_entry *entry = NULL;
> +    const char *eq_sign;
> +    size_t envname_len;
> +
> +    if ((envlist == NULL) || (env == NULL)) {
> +        return (EINVAL);
> +    }
> +
> +    /* find out first equals sign in given env */
> +    eq_sign = strchr(env, '=');
> +    if (eq_sign == NULL) {
> +        return (EINVAL);
> +    }
> +    envname_len = eq_sign - env + 1;
> +
> +    /*
> +     * If there already exists variable with given name,
> +     * we append the new value to the existing one.
> +     */
> +    for (entry = envlist->el_entries.lh_first; entry != NULL;
> +        entry = entry->ev_link.le_next) {
> +        if (strncmp(entry->ev_var, env, envname_len) == 0) {
> +            break;
> +        }
> +    }
> +
> +    if (entry != NULL) {
> +        char *new_env_value = NULL;
> +        size_t new_env_len = strlen(entry->ev_var) + strlen(eq_sign)
> +            + strlen(separator) + 1;
> +        new_env_value = g_malloc(new_env_len);
> +        strcpy(new_env_value, entry->ev_var);
> +        strcat(new_env_value, separator);
> +        strcat(new_env_value, eq_sign + 1);

See other comments about string functions.

> +        g_free((char *)entry->ev_var);
> +        entry->ev_var = new_env_value;
> +    } else {
> +        envlist->el_count++;
> +        entry = g_malloc(sizeof(*entry));
> +        entry->ev_var = g_strdup(env);
> +        QLIST_INSERT_HEAD(&envlist->el_entries, entry, ev_link);
> +    }
> +
> +    return (0);
> +}
> +
>  /*
>   * Returns given envlist as array of strings (in same form that
>   * global variable environ is).  Caller must free returned memory


-- 
Alex Bennée
Virtualisation Tech Lead @ Linaro


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC v2 2/6] Add the libnative library
  2023-06-07 16:47 ` [RFC v2 2/6] Add the libnative library Yeqi Fu
@ 2023-06-15  7:59   ` Alex Bennée
  0 siblings, 0 replies; 17+ messages in thread
From: Alex Bennée @ 2023-06-15  7:59 UTC (permalink / raw)
  To: Yeqi Fu; +Cc: richard.henderson, qemu-devel, Riku Voipio


Yeqi Fu <fufuyqqqqqq@gmail.com> writes:

> Signed-off-by: Yeqi Fu <fufuyqqqqqq@gmail.com>
> ---
>  common-user/native/libnative.c | 65 ++++++++++++++++++++++++++++++++++
>  include/native/libnative.h     | 11 ++++++
>  include/native/native-func.h   | 11 ++++++
>  3 files changed, 87 insertions(+)
>  create mode 100644 common-user/native/libnative.c
>  create mode 100644 include/native/libnative.h
>  create mode 100644 include/native/native-func.h
>
> diff --git a/common-user/native/libnative.c b/common-user/native/libnative.c
> new file mode 100644
> index 0000000000..d40e43c6fe
> --- /dev/null
> +++ b/common-user/native/libnative.c
> @@ -0,0 +1,65 @@
> +#include <stdio.h>
> +#include <stdlib.h>
> +
> +#include "native/libnative.h"
> +#include "native/native-func.h"
> +
> +#define STR_MACRO(str) #str
> +#define STR(num) STR_MACRO(num)
> +
> +#if defined(TARGET_I386) || defined(TARGET_X86_64)
> +
> +/* unused opcode */
> +#define __PREFIX_INSTR \
> +    ".byte 0x0f,0xff;"
> +
> +#define NATIVE_CALL_EXPR(func) \
> +    __PREFIX_INSTR             \
> +    ".word " STR(func) ";" : ::
> +#endif
> +
> +#if defined(TARGET_ARM) || defined(TARGET_AARCH64)
> +
> +/* unused syscall number */
> +#define __PREFIX_INSTR \
> +    "svc 0xff;"
> +
> +#define NATIVE_CALL_EXPR(func) \
> +    __PREFIX_INSTR             \
> +    ".word " STR(func) ";" : ::

I think we can do a little better and encode the ABI parameters in the
expression. I hacked up an example for Aarch64 which:

  - uses HLT instead of SVC (as user-space should never see a halt)
  - encodes the size of in and out args
  - terminates the sequence with HLT 0

Using HLT gives us 16 bits of encoding informaion per instruction while
still looking vaguely sane in debuggers/in_asm dumps. Decoding this in
the translator is left as an exercise for the reader:

--8<---------------cut here---------------start------------->8---
experiment: encode ABI parameters in libnative

3 files changed, 85 insertions(+), 13 deletions(-)
configure                      |  2 +-
include/native/native-func.h   | 21 ++++++++++++
common-user/native/libnative.c | 75 +++++++++++++++++++++++++++++++++++-------

modified   configure
@@ -1821,7 +1821,7 @@ native_flag_i386="-DTARGET_I386"
 native_flag_x86_64="-DTARGET_X86_64"
 native_flag_mips="-DTARGET_MIPS"
 native_flag_mips64="-DTARGET_MIPS64"
-native_flag_arm="-DTARGET_ARM"
+native_flag_arm="-DTARGET_ARM -marm"
 native_flag_aarch64="-DTARGET_AARCH64"
 
 (config_host_mak=common-user/native/config-host.mak
modified   include/native/native-func.h
@@ -8,4 +8,25 @@
 #define NATIVE_STRCMP 0x1005
 #define NATIVE_STRCAT 0x1006
 
+/*
+ * Argument encoding. We only really care about 3 types. The two base
+ * register sizes (32 and 64) 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_I32_ARG 0x1
+#define TYPE_I64_ARG 0x2
+#define TYPE_PTR_ARG 0x3
+
+/* Add an alias for the natural register size, it might be easier to
+ * pass this in */
+#if UINTPTR_MAX == 0xFFFFFFFF
+  #define TYPE_INT_ARG TYPE_I32_ARG
+#elif UINTPTR_MAX == 0xFFFFFFFFFFFFFFFFu
+  #define TYPE_INT_ARG TYPE_I64_ARG
+#else
+  #error TBD pointer size
 #endif
+
+#endif /* __NATIVE_FUNC__ */
modified   common-user/native/libnative.c
@@ -1,5 +1,6 @@
 #include <stdio.h>
 #include <stdlib.h>
+#include <stdint.h>
 
 #include "native/libnative.h"
 #include "native/native-func.h"
@@ -20,15 +21,27 @@
 
 #if defined(TARGET_ARM) || defined(TARGET_AARCH64)
 
-/* unused syscall number */
+/*
+ * HLT is an invalid instruction for userspace and usefully has 16
+ * bits of spare immeadiate data which we can stuff data in.
+ */
+
 #define __PREFIX_INSTR \
-    "svc 0xff;"
+    "hlt 0xffff;"
 
-#define NATIVE_CALL_EXPR(func) \
-    __PREFIX_INSTR             \
-    ".word " STR(func) ";" : ::
+#define WRAP_NATIVE_CALL(id, types)                          \
+    do {                                                     \
+        __asm__ volatile(                                    \
+            __PREFIX_INSTR "\n\t"                            \
+            "hlt  %c0\n\t"                                   \
+            "hlt  %c1\n\t"                                   \
+            "hlt  0\n\t"                                     \
+            : /* no outputs */                               \
+            : "i" (id), "i" (types)                          \
+            : "memory");                                     \
+    } while (0)
 
-#endif
+#endif /* TARGET_ARM || TARGET_AARCH64 */
 
 #if defined(TARGET_MIPS) || defined(TARGET_MIPS64)
 
@@ -38,28 +51,66 @@
 
 #endif
 
+static inline const uint32_t encode_1out_3in(int rtype, int arg1, int arg2, int arg3)
+{
+    return (rtype & 0xf) |
+        ((arg1 & 0xf) << 4) |
+        ((arg2 & 0xf) << 8) |
+        ((arg3 & 0xf) << 12);
+}
+
+static inline const uint32_t encode_0out_3in(int arg1, int arg2, int arg3)
+{
+    return encode_1out_3in(TYPE_NO_ARG, arg1, arg2, arg3);
+}
+
+static inline const uint32_t encode_1out_2in(int rtype, int arg1, int arg2)
+{
+    return encode_1out_3in(rtype, arg1, arg2, TYPE_NO_ARG);
+}
+
 void *memcpy(void *dest, const void *src, size_t n)
 {
-    __asm__ volatile(NATIVE_CALL_EXPR(NATIVE_MEMCPY));
+    const uint32_t args = encode_1out_3in(TYPE_PTR_ARG,
+                                          TYPE_PTR_ARG, TYPE_PTR_ARG,
+                                          TYPE_INT_ARG);
+    WRAP_NATIVE_CALL(NATIVE_MEMCPY, args);
 }
 
 int memcmp(const void *s1, const void *s2, size_t n)
 {
-    __asm__ volatile(NATIVE_CALL_EXPR(NATIVE_MEMCMP));
+    const uint32_t args = encode_1out_3in(TYPE_INT_ARG,
+                                          TYPE_PTR_ARG, TYPE_PTR_ARG,
+                                          TYPE_INT_ARG);
+    WRAP_NATIVE_CALL(NATIVE_MEMCMP, args);
 }
+
 void *memset(void *s, int c, size_t n)
 {
-    __asm__ volatile(NATIVE_CALL_EXPR(NATIVE_MEMSET));
+    const uint32_t args = encode_1out_3in(TYPE_PTR_ARG,
+                                          TYPE_PTR_ARG,
+                                          TYPE_INT_ARG,
+                                          TYPE_INT_ARG);
+    WRAP_NATIVE_CALL(NATIVE_MEMSET, args);
 }
 char *strcpy(char *dest, const char *src)
 {
-    __asm__ volatile(NATIVE_CALL_EXPR(NATIVE_STRCPY));
+    const uint32_t args = encode_1out_2in(TYPE_PTR_ARG,
+                                          TYPE_PTR_ARG,
+                                          TYPE_PTR_ARG);
+    WRAP_NATIVE_CALL(NATIVE_STRCPY, args);
 }
 int strcmp(const char *s1, const char *s2)
 {
-    __asm__ volatile(NATIVE_CALL_EXPR(NATIVE_STRCMP));
+    const uint32_t args = encode_1out_2in(TYPE_INT_ARG,
+                                          TYPE_PTR_ARG,
+                                          TYPE_PTR_ARG);
+    WRAP_NATIVE_CALL(NATIVE_STRCMP, args);
 }
 char *strcat(char *dest, const char *src)
 {
-    __asm__ volatile(NATIVE_CALL_EXPR(NATIVE_STRCAT));
+    const uint32_t args = encode_1out_2in(TYPE_PTR_ARG,
+                                          TYPE_PTR_ARG,
+                                          TYPE_PTR_ARG);
+    WRAP_NATIVE_CALL(NATIVE_STRCAT, args);
 }

--8<---------------cut here---------------end--------------->8---

It would be nice if we could pass the string of the function name as
well but I wasn't able to find the best way to get the address of the
string into the inline assembler. However that could be added later if
we want to make the interface even more generic.



> +
> +#endif
> +
> +#if defined(TARGET_MIPS) || defined(TARGET_MIPS64)
> +
> +/* unused bytes in syscall instructions */
> +#define NATIVE_CALL_EXPR(func) \
> +    ".long " STR((0x1 << 24) + (func << 8) + 0xC) ";" : ::
> +
> +#endif
> +
> +void *memcpy(void *dest, const void *src, size_t n)
> +{
> +    __asm__ volatile(NATIVE_CALL_EXPR(NATIVE_MEMCPY));
> +}
> +
> +int memcmp(const void *s1, const void *s2, size_t n)
> +{
> +    __asm__ volatile(NATIVE_CALL_EXPR(NATIVE_MEMCMP));
> +}
> +void *memset(void *s, int c, size_t n)
> +{
> +    __asm__ volatile(NATIVE_CALL_EXPR(NATIVE_MEMSET));
> +}
> +char *strcpy(char *dest, const char *src)
> +{
> +    __asm__ volatile(NATIVE_CALL_EXPR(NATIVE_STRCPY));
> +}
> +int strcmp(const char *s1, const char *s2)
> +{
> +    __asm__ volatile(NATIVE_CALL_EXPR(NATIVE_STRCMP));
> +}
> +char *strcat(char *dest, const char *src)
> +{
> +    __asm__ volatile(NATIVE_CALL_EXPR(NATIVE_STRCAT));
> +}
> diff --git a/include/native/libnative.h b/include/native/libnative.h
> new file mode 100644
> index 0000000000..d3c24f89f4
> --- /dev/null
> +++ b/include/native/libnative.h
> @@ -0,0 +1,11 @@
> +#ifndef __LIBNATIVE_H__
> +#define __LIBNATIVE_H__
> +
> +void *memcpy(void *dest, const void *src, size_t n);
> +int memcmp(const void *s1, const void *s2, size_t n);
> +void *memset(void *s, int c, size_t n);
> +char *strcpy(char *dest, const char *src);
> +int strcmp(const char *s1, const char *s2);
> +char *strcat(char *dest, const char *src);
> +
> +#endif /* __LIBNATIVE_H__ */
> diff --git a/include/native/native-func.h b/include/native/native-func.h
> new file mode 100644
> index 0000000000..d48a8e547a
> --- /dev/null
> +++ b/include/native/native-func.h
> @@ -0,0 +1,11 @@
> +#ifndef __NATIVE_FUNC_H__
> +#define __NATIVE_FUNC_H__
> +
> +#define NATIVE_MEMCPY 0x1001
> +#define NATIVE_MEMCMP 0x1002
> +#define NATIVE_MEMSET 0x1003
> +#define NATIVE_STRCPY 0x1004
> +#define NATIVE_STRCMP 0x1005
> +#define NATIVE_STRCAT 0x1006
> +
> +#endif


-- 
Alex Bennée
Virtualisation Tech Lead @ Linaro


^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2023-06-15  8:06 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-06-07 16:47 [RFC v2 0/6] Native Library Calls Yeqi Fu
2023-06-07 16:47 ` [RFC v2 1/6] build: Add configure options for native calls Yeqi Fu
2023-06-09  5:08   ` Manos Pitsidianakis
2023-06-12 11:54   ` Alex Bennée
2023-06-12 13:02     ` Alex Bennée
2023-06-07 16:47 ` [RFC v2 2/6] Add the libnative library Yeqi Fu
2023-06-15  7:59   ` Alex Bennée
2023-06-07 16:47 ` [RFC v2 3/6] target/i386: Add native library calls Yeqi Fu
2023-06-07 19:08   ` Richard Henderson
2023-06-07 19:19   ` Richard Henderson
2023-06-07 16:47 ` [RFC v2 4/6] target/mips: " Yeqi Fu
2023-06-07 19:15   ` Richard Henderson
2023-06-07 16:47 ` [RFC v2 5/6] target/arm: " Yeqi Fu
2023-06-07 16:47 ` [RFC v2 6/6] linux-user: Add '-native-bypass' option Yeqi Fu
2023-06-09  5:24   ` Manos Pitsidianakis
2023-06-12 13:06     ` Alex Bennée
2023-06-12 13:23   ` Alex Bennée

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).