Yeqi Fu <fufuyqqq...@gmail.com> writes:

> 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 <fufuyqqq...@gmail.com>
> ---
>  Makefile                            |  2 +
>  common-user/native/Makefile.include |  9 ++++
>  common-user/native/Makefile.target  | 22 ++++++++++
>  common-user/native/libnative.c      | 67 +++++++++++++++++++++++++++++
>  configure                           | 39 +++++++++++++++++
>  include/native/libnative.h          |  8 ++++
>  6 files changed, 147 insertions(+)
>  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
>
> 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..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..0c1241b368
> --- /dev/null
> +++ b/common-user/native/Makefile.target
> @@ -0,0 +1,22 @@
> +# -*- Mode: makefile -*-
> +#
> +# Library for native calls
> +#
> +
> +all:
> +-include ../../config-host.mak

This is sensitive to the out of tree build structure the user chooses. For
example:

  ➜  pwd
  /home/alex/lsrc/qemu.git/builds/user/common-user/native/aarch64-linux-user
  🕙16:20:08 alex@zen:common-user/native/aarch64-linux-user  on  
review/native-lib-calls-v4 [$!?] 
  ➜  make libnative.so
  make: *** No rule to make target '/common-user/native/libnative.c', needed by 
'libnative.so'.  Stop.
  🕙16:20:13 alex@zen:common-user/native/aarch64-linux-user  on  
review/native-lib-calls-v4 [$!?] [🔴 USAGE] 
  ✗  

I think this can be solved the same way as we do for tests/tcg by
symlinking the config-host.mak into place and referring to it directly
or adjusting the include to ../../../config-host.mak because the top of
the build tree has a symlinked copy as well.


> +-include config-target.mak
> +
> +CFLAGS+=-O1 -fPIC -shared -fno-stack-protector -I$(SRC_PATH)/include 
> -D$(TARGET_NAME)
> +LDFLAGS+=
> +
> +SRC = $(SRC_PATH)/common-user/native/libnative.c
> +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.c b/common-user/native/libnative.c
> new file mode 100644
> index 0000000000..662ae6fbfe
> --- /dev/null
> +++ b/common-user/native/libnative.c
> @@ -0,0 +1,67 @@
> +#include <stdint.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +
> +#include "native/libnative.h"
> +
> +#define WRAP_NATIVE()                                 \
> +    do {                                              \
> +        __asm__ volatile(__CALL_EXPR : : : "memory"); \
> +    } while (0)
> +
> +#if defined(i386) || defined(x86_64)
> +/*
> + * An unused instruction is utilized to mark a native call.
> + */
> +#define __CALL_EXPR ".byte 0x0f, 0xff;"
> +#endif
> +
> +#if defined(arm) || defined(aarch64)
> +/*
> + * HLT is an invalid instruction for userspace and usefully has 16
> + * bits of spare immeadiate data which we can stuff data in.
> + */
> +#define __CALL_EXPR "hlt 0xffff;"
> +#endif
> +
> +#if defined(mips) || defined(mips64)
> +/*
> + * The syscall instruction contains 20 unused bits, which are typically
> + * set to 0. These bits can be used to store non-zero data,
> + * distinguishing them from a regular syscall instruction.
> + */
> +#define __CALL_EXPR "syscall 0xffff;"
> +#endif
> +
> +void *memcpy(void *dest, const void *src, size_t n)
> +{
> +    WRAP_NATIVE();
> +}
> +int memcmp(const void *s1, const void *s2, size_t n)
> +{
> +    WRAP_NATIVE();
> +}
> +void *memset(void *s, int c, size_t n)
> +{
> +    WRAP_NATIVE();
> +}
> +char *strncpy(char *dest, const char *src, size_t n)
> +{
> +    WRAP_NATIVE();
> +}
> +int strncmp(const char *s1, const char *s2, size_t n)
> +{
> +    WRAP_NATIVE();
> +}
> +char *strcpy(char *dest, const char *src)
> +{
> +    WRAP_NATIVE();
> +}
> +char *strcat(char *dest, const char *src)
> +{
> +    WRAP_NATIVE();
> +}
> +int strcmp(const char *s1, const char *s2)
> +{
> +    WRAP_NATIVE();
> +}
> diff --git a/configure b/configure
> index a076583141..e02fc2c5c0 100755
> --- a/configure
> +++ b/configure
> @@ -1822,6 +1822,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() {
> diff --git a/include/native/libnative.h b/include/native/libnative.h
> new file mode 100644
> index 0000000000..ec990d8e5f
> --- /dev/null
> +++ b/include/native/libnative.h
> @@ -0,0 +1,8 @@
> +void *memset(void *s, int c, size_t n);
> +void *memcpy(void *dest, const void *src, size_t n);
> +char *strncpy(char *dest, const char *src, size_t n);
> +int memcmp(const void *s1, const void *s2, size_t n);
> +int strncmp(const char *s1, const char *s2, size_t n);
> +char *strcpy(char *dest, const char *src);
> +char *strcat(char *dest, const char *src);
> +int strcmp(const char *s1, const char *s2);


-- 
Alex Bennée
Virtualisation Tech Lead @ Linaro

Reply via email to