diff mbox series

[RFC,v6,2/9] build: Implement libnative library and the build machinery for libnative

Message ID 20230912212842.658374-3-fufuyqqqqqq@gmail.com
State New
Headers show
Series Native Library Calls | expand

Commit Message

Yeqi Fu Sept. 12, 2023, 9:28 p.m. UTC
This commit implements a shared library, where native functions are
rewritten as special instructions. At runtime, user programs load
the shared library, and special instructions are executed when
native functions are called.

Signed-off-by: Yeqi Fu <fufuyqqqqqq@gmail.com>
---
 Makefile                            |  2 ++
 common-user/native/Makefile.include |  8 +++++
 common-user/native/Makefile.target  | 22 +++++++++++++
 common-user/native/libnative.S      | 51 +++++++++++++++++++++++++++++
 configure                           | 39 ++++++++++++++++++++++
 5 files changed, 122 insertions(+)
 create mode 100644 common-user/native/Makefile.include
 create mode 100644 common-user/native/Makefile.target
 create mode 100644 common-user/native/libnative.S

Comments

Helge Deller Sept. 15, 2023, 9:44 a.m. UTC | #1
On 9/12/23 23:28, Yeqi Fu wrote:
> This commit implements a shared library, where native functions are
> rewritten as special instructions. At runtime, user programs load
> the shared library, and special instructions are executed when
> native functions are called.

Hello Yeqi,

I like the idea of speeding up linux-user with your approach.
Do you have a git tree which I can pull for testing (or please
mention the base commit your patches are based on)?

How does the emulation behaves if a guest has bugs and accesses
wrong memory locations, e.g.:
	memcpy(NULL, "Hello", 6)
Will it segfault the same way as if it would have run natively?
At least I think the signal IP addresses will be different.

Regarding you implemenation:

> diff --git a/common-user/native/libnative.S b/common-user/native/libnative.S
> new file mode 100644
> index 0000000000..bc51dabedf
> --- /dev/null
> +++ b/common-user/native/libnative.S
> @@ -0,0 +1,51 @@
> +.macro special_instr sym
> +#if defined(__i386__)

you use here #ifdefs,

> +         ud0     \sym-1f, %eax; 1:
> +#elif defined(__x86_64__)
> +         ud0     \sym(%rip), %eax
> +#elif defined(__arm__) || defined(__aarch64__)
> +         hlt     0xffff
> +1:      .word   \sym - 1b
> +#elif defined(__mips__)
> +         syscall 0xffff
> +1:      .word   \sym - 1b
> +#else
> +# error
> +#endif
> +.endm
> +
> +.macro ret_instr
> +#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__)

and here again,

> +         ret
> +#elif defined(__arm__)
> +         bx     lr
> +#elif defined(__mips__)
> +         jr     $ra
> +#else
> +# error
> +#endif
> +.endm
> +
> +/* Symbols of native functions */
> +
> +.macro define_function name
> +         .text
> +\name:
> +         special_instr 9f

and here the pointer to the string.

> +         ret_instr
> +         .globl \name
> +         .type \name, %function
> +         .size \name, . - \name
> +
> +         .section .rodata
> +9:      .asciz  "\name"
> +.endm

IMHO, I think it would be easier if you just do:

+/* wrapper for native functions */
+
+.macro define_function name
+         .text
+         .align 8  /* function is 8-byte aligned */
+\name:
+/* every arch has up to 8 bytes for trigger and return instruction */
+#if defined(__i386__)
+         ud0     0, %eax
+         ret
+#elif defined(__x86_64__)
+         ud0     0, %eax
+         ret
+#elif defined(__mips__)
+         syscall 0xffff
+         jr     $ra
+#<...more ifdef for arches...>
+#endif
+
+/* the native function name is stored 8 bytes behind \name symbol: */
+         .align 8
+         .asciz  "\name"
+
+         .globl \name
+         .type \name, %function
+         .size \name, . - \name
+.endm

with that you
- save some bytes, code & pointers
- don't need to load the pointer to the native function string (as it's
   always stored as ascii 8 bytes behind the function itself)
- don't need to adjust the IP
- simplifies the asm code and reduced the ifdefs
- ...

Helge

> +
> +define_function memcmp
> +define_function memcpy
> +define_function memset
> +define_function strcat
> +define_function strcmp
> +define_function strcpy
> +define_function strncmp
> +define_function strncpy
diff mbox series

Patch

diff --git a/Makefile b/Makefile
index 5d48dfac18..6f6147b40f 100644
--- a/Makefile
+++ b/Makefile
@@ -182,6 +182,8 @@  SUBDIR_MAKEFLAGS=$(if $(V),,--no-print-directory --quiet)
 
 include $(SRC_PATH)/tests/Makefile.include
 
+include $(SRC_PATH)/common-user/native/Makefile.include
+
 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..65b10fddac
--- /dev/null
+++ b/common-user/native/Makefile.include
@@ -0,0 +1,8 @@ 
+.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")
+
+all: build-native
diff --git a/common-user/native/Makefile.target b/common-user/native/Makefile.target
new file mode 100644
index 0000000000..65d90102e2
--- /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+=-shared -D $(TARGET_NAME)
+LDFLAGS+=
+
+SRC = $(SRC_PATH)/common-user/native/libnative.S
+LIBNATIVE = libnative.so
+
+all: $(LIBNATIVE)
+
+$(LIBNATIVE): $(SRC)
+	$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(EXTRA_NATIVE_CALL_FLAGS) $< -o $@ $(LDFLAGS)
+
+clean:
+	rm -f $(LIBNATIVE)
diff --git a/common-user/native/libnative.S b/common-user/native/libnative.S
new file mode 100644
index 0000000000..bc51dabedf
--- /dev/null
+++ b/common-user/native/libnative.S
@@ -0,0 +1,51 @@ 
+.macro special_instr sym
+#if defined(__i386__)
+         ud0     \sym-1f, %eax; 1:
+#elif defined(__x86_64__)
+         ud0     \sym(%rip), %eax
+#elif defined(__arm__) || defined(__aarch64__)
+         hlt     0xffff
+1:      .word   \sym - 1b
+#elif defined(__mips__)
+         syscall 0xffff
+1:      .word   \sym - 1b
+#else
+# error
+#endif
+.endm
+
+.macro ret_instr
+#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__)
+         ret
+#elif defined(__arm__)
+         bx     lr
+#elif defined(__mips__)
+         jr     $ra
+#else
+# error
+#endif
+.endm
+
+/* Symbols of native functions */
+
+.macro define_function name
+         .text
+\name:
+         special_instr 9f
+         ret_instr
+         .globl \name
+         .type \name, %function
+         .size \name, . - \name
+
+         .section .rodata
+9:      .asciz  "\name"
+.endm
+
+define_function memcmp
+define_function memcpy
+define_function memset
+define_function strcat
+define_function strcmp
+define_function strcpy
+define_function strncmp
+define_function strncpy
diff --git a/configure b/configure
index 7a1e463d9c..de533b27a2 100755
--- a/configure
+++ b/configure
@@ -1826,6 +1826,45 @@  if test "$tcg" = "enabled"; then
 fi
 )
 
+# common-user/native configuration
+(mkdir -p common-user/native
+
+native_targets=
+for target in $target_list; do
+  case $target in
+    *-softmmu)
+    continue
+    ;;
+  esac
+
+  # native call is only supported on these architectures
+  arch=${target%%-*}
+  config_target_mak=common-user/native/${target}/config-target.mak
+  case $arch in
+    i386|x86_64|arm|aarch64|mips|mips64)
+      if test -f cross-build/${target}/config-target.mak; then
+        mkdir -p "common-user/native/${target}"
+        ln -srf cross-build/${target}/config-target.mak "$config_target_mak"
+        if test $arch = arm; then
+          echo "EXTRA_NATIVE_CALL_FLAGS=-marm" >> "$config_target_mak"
+        fi
+        if test $arch = $cpu || \
+          { test $arch = i386 && test $cpu = x86_64; } || \
+          { test $arch = arm && test $cpu = aarch64; } || \
+          { test $arch = mips && test $cpu = mips64; }; then
+          echo "LD_PREFIX=/" >> "$config_target_mak"
+        fi
+        echo "LIBNATIVE=$PWD/common-user/native/${target}/libnative.so" >> "$config_target_mak"
+        ln -sf ${source_path}/common-user/native/Makefile.target common-user/native/${target}/Makefile
+        native_targets="$native_targets ${target}"
+      fi
+    ;;
+  esac
+done
+
+echo "NATIVE_TARGETS=$native_targets" >> config-host.mak
+)
+
 if test "$skip_meson" = no; then
   cross="config-meson.cross.new"
   meson_quote() {