diff mbox series

[RFC,v6,3/9] linux-user: Implement native-bypass option support

Message ID 20230912212842.658374-4-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 support for the native-bypass option
in linux-user. By utilizing this functionality, the specified
shared library can be loaded into the user program. This is
achieved by dynamically modifying the /etc/ld.so.preload file,
enabling the user program to load the shared library effortlessly.

Signed-off-by: Yeqi Fu <fufuyqqqqqq@gmail.com>
---
 include/native/native.h |  7 ++++++
 linux-user/main.c       | 20 +++++++++++++++
 linux-user/syscall.c    | 55 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 82 insertions(+)
 create mode 100644 include/native/native.h
diff mbox series

Patch

diff --git a/include/native/native.h b/include/native/native.h
new file mode 100644
index 0000000000..12462a261e
--- /dev/null
+++ b/include/native/native.h
@@ -0,0 +1,7 @@ 
+#if defined(CONFIG_USER_ONLY) && defined(CONFIG_NATIVE_CALL)
+extern char *native_lib_path;
+/* Check if the native-bypass option is enabled. */
+#define native_bypass_enabled() (native_lib_path != NULL)
+#else
+#define native_bypass_enabled() false
+#endif
diff --git a/linux-user/main.c b/linux-user/main.c
index dba67ffa36..4c1d515944 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -60,6 +60,11 @@ 
 #include "semihosting/semihost.h"
 #endif
 
+#if defined(CONFIG_NATIVE_CALL)
+#include "native/native.h"
+char *native_lib_path;
+#endif
+
 #ifndef AT_FLAGS_PRESERVE_ARGV0
 #define AT_FLAGS_PRESERVE_ARGV0_BIT 0
 #define AT_FLAGS_PRESERVE_ARGV0 (1 << AT_FLAGS_PRESERVE_ARGV0_BIT)
@@ -293,6 +298,17 @@  static void handle_arg_set_env(const char *arg)
     free(r);
 }
 
+#if defined(CONFIG_NATIVE_CALL)
+static void handle_arg_native_bypass(const char *arg)
+{
+    if (!g_file_test(arg, G_FILE_TEST_IS_REGULAR)) {
+        fprintf(stderr, "native library %s does not exist\n", arg);
+        exit(EXIT_FAILURE);
+    }
+    native_lib_path = g_strdup(arg);
+}
+#endif
+
 static void handle_arg_unset_env(const char *arg)
 {
     char *r, *p, *token;
@@ -522,6 +538,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_NATIVE_CALL)
+    {"native-bypass", "QEMU_NATIVE_BYPASS", true, handle_arg_native_bypass,
+     "",           "native bypass for library calls"},
+#endif
     {NULL, NULL, false, NULL, NULL, NULL}
 };
 
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 08162cc966..7034f58373 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -143,6 +143,7 @@ 
 #include "fd-trans.h"
 #include "tcg/tcg.h"
 #include "cpu_loop-common.h"
+#include "native/native.h"
 
 #ifndef CLONE_IO
 #define CLONE_IO                0x80000000      /* Clone io context */
@@ -8503,6 +8504,40 @@  static int open_hardware(CPUArchState *cpu_env, int fd)
 }
 #endif
 
+#if defined(CONFIG_NATIVE_CALL)
+static int is_ld_so_preload(const char *filename, const char *entry)
+{
+    if (native_bypass_enabled() && !strcmp(filename, entry)) {
+        return 1;
+    }
+    return 0;
+}
+
+/* This function is only called when the "native-bypass" option is provided. */
+static int open_ld_so_preload(CPUArchState *cpu_env, int fd)
+{
+    FILE *fp;
+    char *line = NULL;
+    size_t len = 0;
+    ssize_t read;
+
+    dprintf(fd, "%s\n", native_lib_path);
+    fp = fopen("/etc/ld.so.preload", "r");
+    if (fp == NULL) {
+        return 0;
+    }
+
+    while ((read = getline(&line, &len, fp)) != -1) {
+        dprintf(fd, "%s", line);
+    }
+
+    free(line);
+    fclose(fp);
+
+    return 0;
+}
+#endif
+
 int do_guest_openat(CPUArchState *cpu_env, int dirfd, const char *pathname,
                     int flags, mode_t mode, bool safe)
 {
@@ -8527,6 +8562,9 @@  int do_guest_openat(CPUArchState *cpu_env, int dirfd, const char *pathname,
 #endif
 #if defined(TARGET_M68K)
         { "/proc/hardware", open_hardware, is_proc },
+#endif
+#if defined(CONFIG_NATIVE_CALL)
+        { "/etc/ld.so.preload", open_ld_so_preload, is_ld_so_preload },
 #endif
         { NULL, NULL, NULL }
     };
@@ -9523,6 +9561,11 @@  static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1,
             return -TARGET_EFAULT;
         }
         ret = get_errno(access(path(p), arg2));
+        if (ret != 0 && native_bypass_enabled()) {
+            if (strcmp(p, "/etc/ld.so.preload") == 0 && arg2 == R_OK) {
+                return 0;
+            }
+        }
         unlock_user(p, arg1, 0);
         return ret;
 #endif
@@ -9532,6 +9575,12 @@  static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1,
             return -TARGET_EFAULT;
         }
         ret = get_errno(faccessat(arg1, p, arg3, 0));
+        if (ret != 0 && native_bypass_enabled()) {
+            if (strcmp(p, "/etc/ld.so.preload") == 0 && arg1 == AT_FDCWD &&
+                arg3 == R_OK) {
+                return 0;
+            }
+        }
         unlock_user(p, arg2, 0);
         return ret;
 #endif
@@ -9541,6 +9590,12 @@  static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1,
             return -TARGET_EFAULT;
         }
         ret = get_errno(faccessat(arg1, p, arg3, arg4));
+        if (ret != 0 && native_bypass_enabled()) {
+            if (strcmp(p, "/etc/ld.so.preload") == 0 && arg1 == AT_FDCWD &&
+                arg3 == R_OK) {
+                return 0;
+            }
+        }
         unlock_user(p, arg2, 0);
         return ret;
 #endif