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
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() {
--
2.34.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
© 2016 - 2026 Red Hat, Inc.