From f8b56c50bab051f7f2a1fb47be4101f0e2099f2f Mon Sep 17 00:00:00 2001 From: TheBrokenRail Date: Sun, 10 Nov 2024 05:26:16 -0500 Subject: [PATCH] Runtime Refactor! --- CMakeLists.txt | 66 ++++++++++++++++++--------- include/trampoline/env-list.h | 3 ++ include/trampoline/types.h | 13 +++--- native/CMakeLists.txt | 25 ----------- native/src/loop.h | 5 --- native/src/main.cpp | 55 ----------------------- native/src/memory.cpp | 24 ---------- native/src/memory.h | 8 ---- native/src/signals.cpp | 24 ---------- native/src/signals.h | 5 --- native/src/trampoline.h | 6 --- qemu/CMakeLists.txt | 55 +++++++++++++---------- qemu/runtime.patch | 71 ++++++++++++++++++++++++++++++ qemu/src/trampoline.patch | 57 ------------------------ {native/src => src}/log.h | 2 +- src/main.cpp | 25 +++++++++++ src/main.h | 12 +++++ {native/src => src/pipe}/loop.cpp | 35 +++++++-------- src/pipe/main.cpp | 46 +++++++++++++++++++ src/pipe/main.h | 20 +++++++++ src/pipe/memory.cpp | 30 +++++++++++++ src/qemu/qemu.cpp | 25 +++++++++++ src/qemu/qemu.h | 23 ++++++++++ src/syscall/handler.cpp | 21 +++++++++ src/syscall/main.cpp | 20 +++++++++ src/syscall/main.h | 10 +++++ {native/src => src}/trampoline.cpp | 19 ++++---- src/trampoline.h | 11 +++++ 28 files changed, 430 insertions(+), 286 deletions(-) create mode 100644 include/trampoline/env-list.h delete mode 100644 native/CMakeLists.txt delete mode 100644 native/src/loop.h delete mode 100644 native/src/main.cpp delete mode 100644 native/src/memory.cpp delete mode 100644 native/src/memory.h delete mode 100644 native/src/signals.cpp delete mode 100644 native/src/signals.h delete mode 100644 native/src/trampoline.h create mode 100644 qemu/runtime.patch delete mode 100644 qemu/src/trampoline.patch rename {native/src => src}/log.h (84%) create mode 100644 src/main.cpp create mode 100644 src/main.h rename {native/src => src/pipe}/loop.cpp (62%) create mode 100644 src/pipe/main.cpp create mode 100644 src/pipe/main.h create mode 100644 src/pipe/memory.cpp create mode 100644 src/qemu/qemu.cpp create mode 100644 src/qemu/qemu.h create mode 100644 src/syscall/handler.cpp create mode 100644 src/syscall/main.cpp create mode 100644 src/syscall/main.h rename {native/src => src}/trampoline.cpp (52%) create mode 100644 src/trampoline.h diff --git a/CMakeLists.txt b/CMakeLists.txt index c46ac13..bfa9f70 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,25 +7,51 @@ project(runtime) add_library(trampoline-headers INTERFACE) target_include_directories(trampoline-headers INTERFACE include) +# Check Architecture +include(CheckSymbolExists) +check_symbol_exists("__arm__" "" NO_RUNTIME_NEEDED) +if(NO_RUNTIME_NEEDED) + return() +endif() + +# Only Headers +option(TRAMPOLINE_HEADERS_ONLY "Skip Building Runtime" FALSE) +if(TRAMPOLINE_HEADERS_ONLY) + return() +endif() +target_compile_definitions(trampoline-headers INTERFACE MCPI_BUILD_RUNTIME) + # Build -if(NOT TRAMPOLINE_HEADERS_ONLY) - # Check Architecture - include(CheckSymbolExists) - check_symbol_exists("__arm__" "" NO_RUNTIME_NEEDED) - if(NOT NO_RUNTIME_NEEDED) - check_symbol_exists("__aarch64__" "" USE_NATIVE_RUNTIME) - check_symbol_exists("__x86_64__" "" USE_QEMU_RUNTIME) - # Include Correct Sub-Project - set(RUNTIME_RPATH "$ORIGIN/../lib/native") - set(RUNTIME_EXTRA_LINK_FLAG "--disable-new-dtags") - target_compile_definitions(trampoline-headers INTERFACE MCPI_BUILD_RUNTIME) - if(USE_NATIVE_RUNTIME) - add_subdirectory(native) - elseif(USE_QEMU_RUNTIME) - add_subdirectory(qemu) - target_compile_definitions(trampoline-headers INTERFACE MCPI_RUNTIME_IS_QEMU) - else() - message(FATAL_ERROR "Unsupported Architecture") - endif() - endif() +add_executable(runtime + src/main.cpp + src/trampoline.cpp + src/pipe/loop.cpp + src/pipe/main.cpp + src/pipe/memory.cpp +) + +# QEMU +check_symbol_exists("__x86_64__" "" USE_QEMU) +if(USE_QEMU) + add_subdirectory(qemu) + target_sources(runtime PRIVATE + src/syscall/main.cpp + src/syscall/handler.cpp + src/qemu/qemu.cpp + ) +endif() + +# Warnings +target_compile_options(runtime PRIVATE -Wall -Wextra -Werror -Wpointer-arith -Wshadow -Wnull-dereference) + +# Link +target_link_libraries(runtime dl rt trampoline-headers) + +# RPath +set_target_properties(runtime PROPERTIES INSTALL_RPATH "$ORIGIN/../lib/native") +target_link_options(runtime PRIVATE "LINKER:--disable-new-dtags") + +# Install +if(DEFINED MCPI_BIN_DIR) + install(TARGETS runtime DESTINATION "${MCPI_BIN_DIR}") endif() \ No newline at end of file diff --git a/include/trampoline/env-list.h b/include/trampoline/env-list.h new file mode 100644 index 0000000..78ccaa6 --- /dev/null +++ b/include/trampoline/env-list.h @@ -0,0 +1,3 @@ +ENV(MCPI_USE_PIPE_TRAMPOLINE, "Use The Pipe-Based Trampoline") +ENV(_MCPI_TRAMPOLINE_ARGUMENTS, "") +ENV(_MCPI_TRAMPOLINE_RETURN_VALUE, "") \ No newline at end of file diff --git a/include/trampoline/types.h b/include/trampoline/types.h index 9b2b237..f190977 100644 --- a/include/trampoline/types.h +++ b/include/trampoline/types.h @@ -3,15 +3,13 @@ #include // Maximum Arguments Length -#define MAX_TRAMPOLINE_ARGS_SIZE 2097152 // 2 MiB +#define MAX_TRAMPOLINE_ARGS_SIZE 16777216 // 16 MiB // System Call Constants #define TRAMPOLINE_SYSCALL 0x1337 #define QEMU_GUEST_BASE 0x40000 // Arbitrary Value (Aligns To 4k And 16k Page Sizes) -// Pipe Constants -#define TRAMPOLINE_ARGUMENTS_PIPE_ENV "_MCPI_TRAMPOLINE_ARGUMENTS" -#define TRAMPOLINE_RETURN_VALUE_PIPE_ENV "_MCPI_TRAMPOLINE_RETURN_VALUE" +// Pipe Command Structure struct trampoline_pipe_arguments { uint32_t id; uint32_t allow_early_return; @@ -20,4 +18,9 @@ struct trampoline_pipe_arguments { // Function Types typedef void (*trampoline_writer_t)(uint32_t guest_addr, const void *data, uint32_t size); -typedef uint32_t (*trampoline_t)(trampoline_writer_t writer, uint32_t id, const unsigned char *args); \ No newline at end of file +typedef uint32_t (*trampoline_t)(trampoline_writer_t writer, uint32_t id, const unsigned char *args); + +// Environmental Variables +#define ENV(name, ...) constexpr const char *name##_ENV = #name; +#include "env-list.h" +#undef ENV \ No newline at end of file diff --git a/native/CMakeLists.txt b/native/CMakeLists.txt deleted file mode 100644 index 06e0748..0000000 --- a/native/CMakeLists.txt +++ /dev/null @@ -1,25 +0,0 @@ -project(native-runtime) - -# Build -add_executable(runtime - src/memory.cpp - src/main.cpp - src/trampoline.cpp - src/loop.cpp - src/signals.cpp -) - -# Warnings -target_compile_options(runtime PRIVATE -Wall -Wextra -Werror -Wpointer-arith -Wshadow -Wnull-dereference) - -# Link -target_link_libraries(runtime dl rt trampoline-headers) - -# RPath -set_target_properties(runtime PROPERTIES INSTALL_RPATH "${RUNTIME_RPATH}") -target_link_options(runtime PRIVATE "LINKER:${RUNTIME_EXTRA_LINK_FLAG}") - -# Install -function(install_runtime bin_dir) - install(TARGETS runtime DESTINATION "${bin_dir}") -endfunction() diff --git a/native/src/loop.h b/native/src/loop.h deleted file mode 100644 index c312928..0000000 --- a/native/src/loop.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -void create_pipes(); -void pipe_read_loop(); -void init_pipe_guest(); diff --git a/native/src/main.cpp b/native/src/main.cpp deleted file mode 100644 index 3946f32..0000000 --- a/native/src/main.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include -#include -#include -#include -#include - -#include "log.h" -#include "memory.h" -#include "loop.h" -#include "signals.h" - -// Main -int main(__attribute__((unused)) int argc, char *argv[]) { - // Check Arguments - if (argc < 2) { - ERR("Invalid Arguments"); - } - // Setup - create_pipes(); - // Fork - pid_t pid = fork(); - if (pid == -1) { - ERR("Unable To Fork Process: %s", strerror(errno)); - } else if (pid == 0) { - // Setup - setpgid(0, 0); - init_pipe_guest(); - // Kill Child If Parent Exits First - prctl(PR_SET_PDEATHSIG, SIGKILL); - // Execute Program - execvp(argv[1], (char *const *) &argv[1]); - ERR("Unable To Execute Program: %s: %s", argv[1], strerror(errno)); - } else { - // Parent - - // Setup Trampoline - init_signals(pid); - init_memory(pid); - - // Read From Pipes - pipe_read_loop(); - - // Reap Child - int status; - if (waitpid(pid, &status, 0) == -1) { - ERR("Unable To Reap Child: %s", strerror(errno)); - } - // Exit - if (WIFEXITED(status)) { - exit(WEXITSTATUS(status)); - } else { - ERR("Unusual Exit"); - } - } -} \ No newline at end of file diff --git a/native/src/memory.cpp b/native/src/memory.cpp deleted file mode 100644 index e991f04..0000000 --- a/native/src/memory.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include -#include -#include - -#include "memory.h" -#include "log.h" - -// Read/Write Memory -static pid_t guest_pid; -void init_memory(pid_t pid) { - guest_pid = pid; -} -void memory_writer(uint32_t guest_addr, const void *data, uint32_t size) { - iovec local[1]; - local[0].iov_base = (void *) data; - local[0].iov_len = size; - iovec remote[1]; - remote[0].iov_base = (void *) (uint64_t) guest_addr; - remote[0].iov_len = size; - const ssize_t ret = process_vm_writev(guest_pid, local, 1, remote, 1, 0); - if (ret != size) { - ERR("Unable To Write Data: %#10x: %s", guest_addr, strerror(errno)); - } -} \ No newline at end of file diff --git a/native/src/memory.h b/native/src/memory.h deleted file mode 100644 index 15fc92b..0000000 --- a/native/src/memory.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -#include -#include - -// Read External Memory -void memory_writer(uint32_t guest_addr, const void *data, uint32_t size); -void init_memory(pid_t guest_pid); \ No newline at end of file diff --git a/native/src/signals.cpp b/native/src/signals.cpp deleted file mode 100644 index 9569aa0..0000000 --- a/native/src/signals.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include - -#include "signals.h" - -// Signal Handlers -static pid_t guest_pid = -1; -static void exit_handler(__attribute__((unused)) int signal_id) { - // Stop Child - kill(guest_pid, SIGTERM); -} - -// Init -void init_signals(pid_t pid) { - guest_pid = pid; - // Install Signal Handlers - struct sigaction act_sigint = {}; - act_sigint.sa_flags = SA_RESTART; - act_sigint.sa_handler = &exit_handler; - sigaction(SIGINT, &act_sigint, nullptr); - struct sigaction act_sigterm = {}; - act_sigterm.sa_flags = SA_RESTART; - act_sigterm.sa_handler = &exit_handler; - sigaction(SIGTERM, &act_sigterm, nullptr); -} \ No newline at end of file diff --git a/native/src/signals.h b/native/src/signals.h deleted file mode 100644 index 1d18035..0000000 --- a/native/src/signals.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#include - -void init_signals(pid_t guest_pid); \ No newline at end of file diff --git a/native/src/trampoline.h b/native/src/trampoline.h deleted file mode 100644 index 04503c8..0000000 --- a/native/src/trampoline.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -#include - -uint32_t trampoline(uint32_t id, const unsigned char *args); -void init_trampoline(); \ No newline at end of file diff --git a/qemu/CMakeLists.txt b/qemu/CMakeLists.txt index 26115e1..4f7e95b 100644 --- a/qemu/CMakeLists.txt +++ b/qemu/CMakeLists.txt @@ -6,15 +6,13 @@ if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.24.0) endif() # Archive -if(NOT DEFINED RUNTIME_QEMU_ARCHIVE) - message(FATAL_ERROR "Missing QEMU Archive") +set(RUNTIME_QEMU_ARCHIVE "" CACHE FILEPATH "QEMU Path") +if(NOT EXISTS "${RUNTIME_QEMU_ARCHIVE}") + message(FATAL_ERROR "Missing QEMU Archive!") endif() -# Flatpak Support -set(QEMU_FLATPAK_PATCH "") -if(MCPI_IS_FLATPAK_BUILD) - set(QEMU_FLATPAK_PATCH "sed" "-i" "s/libdrm/libdrm-dis/g" "/meson.build") -endif() +# Library +set(QEMU_LIBRARY "/lib/libqemu-arm.so") # Build include(ExternalProject) @@ -22,42 +20,51 @@ set(PKGCONFIG_ENV "") if(DEFINED ENV{PKG_CONFIG_LIBDIR}) set(PKGCONFIG_ENV "PKG_CONFIG_LIBDIR=$ENV{PKG_CONFIG_LIBDIR}") endif() -set(EXTRA_C_FLAGS "-s -I${CMAKE_CURRENT_SOURCE_DIR}/../include") ExternalProject_Add(qemu URL "${RUNTIME_QEMU_ARCHIVE}" # Configure Build CONFIGURE_COMMAND "${CMAKE_COMMAND}" "-E" "env" ${PKGCONFIG_ENV} - "CFLAGS=${EXTRA_C_FLAGS}" - "CXXFLAGS=${EXTRA_C_FLAGS}" "/configure" - "--prefix=${CMAKE_INSTALL_PREFIX}" + "--prefix=" "--cross-prefix=" "--cc=${CMAKE_C_COMPILER}" "--cxx=${CMAKE_CXX_COMPILER}" - "--extra-ldflags=-ldl -Wl,-rpath=${RUNTIME_RPATH} -Wl,${RUNTIME_EXTRA_LINK_FLAG}" "--disable-debug-info" + "--enable-strip" + "--enable-pie" + "--enable-lto" + "-Db_staticpic=true" + "-Db_lundef=false" "--target-list=arm-linux-user" "--without-default-features" USES_TERMINAL_CONFIGURE TRUE # Build Command - BUILD_COMMAND "ninja" "qemu-arm" - BUILD_BYPRODUCTS "/qemu-arm" + BUILD_COMMAND "ninja" USES_TERMINAL_BUILD TRUE - # Disable Install/Test Commands - INSTALL_COMMAND "" + # Install + INSTALL_DIR "${QEMU_INSTALL_DIR}" + INSTALL_COMMAND "ninja" "install" + INSTALL_BYPRODUCTS "${QEMU_LIBRARY}" + # Disable Test Command TEST_COMMAND "" # Patch Command - PATCH_COMMAND "patch" "-p1" "<" "${CMAKE_CURRENT_SOURCE_DIR}/src/trampoline.patch" - COMMAND ${QEMU_FLATPAK_PATCH} + PATCH_COMMAND "patch" "-p1" "<" "${CMAKE_CURRENT_SOURCE_DIR}/runtime.patch" ) +# Get Full Library Path +ExternalProject_Get_Property(qemu INSTALL_DIR) +string(PREPEND QEMU_LIBRARY "${INSTALL_DIR}") + +# Link +target_link_libraries(runtime "${QEMU_LIBRARY}") +target_compile_definitions(trampoline-headers INTERFACE MCPI_HAS_QEMU) + # Install -function(install_runtime bin_dir legal_dir) - ExternalProject_Get_property(qemu BINARY_DIR) - install(PROGRAMS "${BINARY_DIR}/qemu-arm" DESTINATION "${bin_dir}" RENAME "runtime") +if(DEFINED MCPI_LIB_DIR) + install(PROGRAMS "${QEMU_LIBRARY}" DESTINATION "${MCPI_LIB_DIR}") # License - ExternalProject_Get_property(qemu SOURCE_DIR) - install(FILES "${SOURCE_DIR}/COPYING" DESTINATION "${legal_dir}/qemu") -endfunction() \ No newline at end of file + ExternalProject_Get_Property(qemu SOURCE_DIR) + install(FILES "${SOURCE_DIR}/COPYING" DESTINATION "${MCPI_LEGAL_DIR}/qemu") +endif() \ No newline at end of file diff --git a/qemu/runtime.patch b/qemu/runtime.patch new file mode 100644 index 0000000..c98e9f8 --- /dev/null +++ b/qemu/runtime.patch @@ -0,0 +1,71 @@ +--- a/linux-user/main.c ++++ b/linux-user/main.c +@@ -671,7 +671,8 @@ + return optind; + } + +-int main(int argc, char **argv, char **envp) ++#pragma GCC diagnostic ignored "-Wmissing-prototypes" ++int qemu_main(int argc, char **argv, char **envp) + { + struct target_pt_regs regs1, *regs = ®s1; + struct image_info info1, *info = &info1; +--- a/linux-user/syscall.c ++++ b/linux-user/syscall.c +@@ -9077,6 +9077,8 @@ + int, __to_dfd, const char *, __to_pathname, unsigned int, flag) + #endif + ++extern int trampoline_handle_syscall(int32_t num, uint32_t arg1, uint32_t arg2); ++ + /* This is an internal helper for do_syscall so that it is easier + * to have a single return point, so that actions, such as logging + * of syscall results, can be performed. +@@ -9101,6 +9103,10 @@ + #endif + void *p; + ++ if (trampoline_handle_syscall(num, arg1, arg2)) { ++ return 0; ++ } ++ + switch(num) { + case TARGET_NR_exit: + /* In old applications this may be used to implement _exit(2). +--- a/meson.build ++++ b/meson.build +@@ -2470,7 +2470,6 @@ + config_host_data.set('CONFIG_LINUX_MAGIC_H', cc.has_header('linux/magic.h')) + config_host_data.set('CONFIG_VALGRIND_H', cc.has_header('valgrind/valgrind.h')) + config_host_data.set('HAVE_BTRFS_H', cc.has_header('linux/btrfs.h')) +-config_host_data.set('HAVE_DRM_H', cc.has_header('libdrm/drm.h')) + config_host_data.set('HAVE_PTY_H', cc.has_header('pty.h')) + config_host_data.set('HAVE_SYS_DISK_H', cc.has_header('sys/disk.h')) + config_host_data.set('HAVE_SYS_IOCCOM_H', cc.has_header('sys/ioccom.h')) +@@ -4015,14 +4014,13 @@ + exe_name += '-unsigned' + endif + +- emulator = executable(exe_name, exe['sources'], ++ emulator = library(exe_name, exe['sources'], + install: true, + c_args: c_args, + dependencies: arch_deps + exe['dependencies'], + objects: lib.extract_all_objects(recursive: true), + link_depends: [block_syms, qemu_syms], +- link_args: link_args, +- win_subsystem: exe['win_subsystem']) ++ link_args: link_args) + + if host_os == 'darwin' + icon = 'pc-bios/qemu.rsrc' +@@ -4166,9 +4164,7 @@ + + subdir('scripts') + subdir('tools') +-subdir('pc-bios') + subdir('docs') +-subdir('tests') + if gtk.found() + subdir('po') + endif \ No newline at end of file diff --git a/qemu/src/trampoline.patch b/qemu/src/trampoline.patch deleted file mode 100644 index 45d8265..0000000 --- a/qemu/src/trampoline.patch +++ /dev/null @@ -1,57 +0,0 @@ ---- a/linux-user/syscall.c -+++ b/linux-user/syscall.c -@@ -17,6 +17,8 @@ - * along with this program; if not, see . - */ - #define _ATFILE_SOURCE -+#include -+#include - #include "qemu/osdep.h" - #include "qemu/cutils.h" - #include "qemu/path.h" -@@ -9070,6 +9072,13 @@ _syscall5(int, sys_move_mount, int, __from_dfd, const char *, __from_pathname, - int, __to_dfd, const char *, __to_pathname, unsigned int, flag) - #endif - -+// g2h For Trampoline -+static CPUState *_trampoline_g2h_cpu = NULL; -+static void _trampoline_writer(uint32_t guest_addr, const void *data, uint32_t size) { -+ void *out = g2h(_trampoline_g2h_cpu, guest_addr); -+ memcpy(out, data, size); -+} -+ - /* This is an internal helper for do_syscall so that it is easier - * to have a single return point, so that actions, such as logging - * of syscall results, can be performed. -@@ -9095,6 +9104,31 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, - void *p; - - switch(num) { -+ case TRAMPOLINE_SYSCALL: { -+ // Load Trampoline -+ static trampoline_t _trampoline = NULL; -+ if (_trampoline == NULL) { -+ // Open Library -+ void *_trampoline_handle = dlopen("libmedia-layer-trampoline.so", RTLD_NOW); -+ // Load Function -+ if (_trampoline_handle != NULL) { -+ _trampoline = dlsym(_trampoline_handle, "trampoline"); -+ } -+ } -+ if (_trampoline == NULL) { -+ // Failed To Load -+ qemu_log("Unable To Load Media Layer Trampoline: %s\n", dlerror()); -+ return -TARGET_ENOSYS; -+ } -+ // Call Trampoline -+ _trampoline_g2h_cpu = cpu; -+ uint32_t _trampoline_id = arg1; -+ __attribute__((unused)) uint32_t _trampoline_length = arg2; -+ const unsigned char *_trampoline_args = g2h(cpu, arg3); -+ uint32_t _trampoline_ret = _trampoline(_trampoline_writer, _trampoline_id, _trampoline_args); -+ *(uint32_t *) _trampoline_args = _trampoline_ret; -+ return 0; -+ } - case TARGET_NR_exit: - /* In old applications this may be used to implement _exit(2). - However in threaded applications it is used for thread termination, diff --git a/native/src/log.h b/src/log.h similarity index 84% rename from native/src/log.h rename to src/log.h index c08b19c..c00b978 100644 --- a/native/src/log.h +++ b/src/log.h @@ -6,5 +6,5 @@ #define ERR(format, ...) \ { \ fprintf(stderr, "RUNTIME ERROR: " format "\n", ##__VA_ARGS__); \ - exit(EXIT_FAILURE); \ + exit(EXIT_FAILURE); \ } diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..a22fc3c --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,25 @@ +#include "log.h" +#include "pipe/main.h" +#ifdef MCPI_HAS_QEMU +#include "syscall/main.h" +#endif + +// Main +int main(int argc, char *argv[]) { + // Check Arguments + if (argc < 2) { + ERR("Invalid Arguments"); + } + // Update Arguments + argc--; + argv++; + // Create Implementation + Implementation *impl = new PipeImplementation; +#ifdef MCPI_HAS_QEMU + if (getenv(MCPI_USE_PIPE_TRAMPOLINE_ENV) == nullptr) { + delete impl; + impl = new SyscallImplementation; + } +#endif + return impl->main(argc, argv); +} \ No newline at end of file diff --git a/src/main.h b/src/main.h new file mode 100644 index 0000000..92a3cee --- /dev/null +++ b/src/main.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +#include "trampoline.h" + +struct Implementation { + virtual ~Implementation() = default; + virtual int main(int argc, char *argv[]) = 0; + virtual void memory_writer(uint32_t guest_addr, const void *data, uint32_t size) const = 0; + Trampoline trampoline; +}; \ No newline at end of file diff --git a/native/src/loop.cpp b/src/pipe/loop.cpp similarity index 62% rename from native/src/loop.cpp rename to src/pipe/loop.cpp index 7184e7f..6dfe790 100644 --- a/native/src/loop.cpp +++ b/src/pipe/loop.cpp @@ -5,51 +5,50 @@ #include -#include "loop.h" - -#include "log.h" -#include "trampoline.h" -#include "memory.h" +#include "../log.h" +#include "main.h" // Setup Pipes #define PIPE_READ 0 #define PIPE_WRITE 1 -static int arguments_pipe[2]; -static int return_value_pipe[2]; static void safe_pipe(int *out) { - int ret = pipe(out); + const int ret = pipe(out); if (ret != 0) { ERR("Unable To Create Pipe: %s", strerror(errno)); } } -void create_pipes() { +void PipeImplementation::init() { + setenv(MCPI_USE_PIPE_TRAMPOLINE_ENV, "", 1); safe_pipe(arguments_pipe); safe_pipe(return_value_pipe); } -// Guest -void init_pipe_guest() { +// Parent +void PipeImplementation::init_parent() const { // Close Unneeded Pipes close(arguments_pipe[PIPE_READ]); close(return_value_pipe[PIPE_WRITE]); // Setup Environment - setenv(TRAMPOLINE_ARGUMENTS_PIPE_ENV, std::to_string(arguments_pipe[PIPE_WRITE]).c_str(), true); - setenv(TRAMPOLINE_RETURN_VALUE_PIPE_ENV, std::to_string(return_value_pipe[PIPE_READ]).c_str(), true); + setenv(_MCPI_TRAMPOLINE_ARGUMENTS_ENV, std::to_string(arguments_pipe[PIPE_WRITE]).c_str(), true); + setenv(_MCPI_TRAMPOLINE_RETURN_VALUE_ENV, std::to_string(return_value_pipe[PIPE_READ]).c_str(), true); } -// Host -void pipe_read_loop() { +// Child +void PipeImplementation::init_child() { // Close Unneeded Pipes close(arguments_pipe[PIPE_WRITE]); close(return_value_pipe[PIPE_READ]); + // Init Trampoline + trampoline.init(this); +} +void PipeImplementation::loop_child() const { // Wait For Commands trampoline_pipe_arguments cmd = {}; while (read(arguments_pipe[PIPE_READ], &cmd, sizeof(trampoline_pipe_arguments)) > 0) { - init_trampoline(); // Only Init If Needed static unsigned char args[MAX_TRAMPOLINE_ARGS_SIZE]; size_t position = 0; while (position < cmd.length) { - ssize_t ret = read(arguments_pipe[PIPE_READ], args + position, cmd.length - position); + const ssize_t ret = read(arguments_pipe[PIPE_READ], args + position, cmd.length - position); if (ret == -1) { ERR("Unable To Read Trampoline Arguments"); } else { @@ -58,7 +57,7 @@ void pipe_read_loop() { } uint32_t ret = trampoline(cmd.id, args); if (!cmd.allow_early_return) { - ssize_t ret2 = write(return_value_pipe[PIPE_WRITE], &ret, sizeof(uint32_t)); + const ssize_t ret2 = write(return_value_pipe[PIPE_WRITE], &ret, sizeof(uint32_t)); if (ret2 == -1) { ERR("Unable To Write Trampoline Return Value"); } diff --git a/src/pipe/main.cpp b/src/pipe/main.cpp new file mode 100644 index 0000000..266335c --- /dev/null +++ b/src/pipe/main.cpp @@ -0,0 +1,46 @@ +#include +#include +#include +#include +#include +#include + +#include "../log.h" +#ifdef MCPI_HAS_QEMU +#include "../qemu/qemu.h" +#endif +#include "main.h" + +// Main +int PipeImplementation::main(const int argc, char *argv[]) { + // Setup + init(); + // Fork + const pid_t pid = fork(); + if (pid == -1) { + ERR("Unable To Fork Process: %s", strerror(errno)); + } else if (pid == 0) { + // Child + + // Setup + setpgid(0, 0); + init_child(); + // Read From Pipes + loop_child(); + // Exit + return 0; + } else { + // Parent/Guest + + // Setup + init_parent(); + prctl(PR_SET_PTRACER, pid); + // Execute Program +#ifdef MCPI_HAS_QEMU + return QEMU::run(argc, argv); +#else + execvp(argv[0], (char *const *) argv); + ERR("Unable To Execute Program: %s: %s", argv[0], strerror(errno)); +#endif + } +} \ No newline at end of file diff --git a/src/pipe/main.h b/src/pipe/main.h new file mode 100644 index 0000000..669172f --- /dev/null +++ b/src/pipe/main.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +#include "../main.h" + +struct PipeImplementation final : Implementation { + int main(int argc, char *argv[]) override; + void memory_writer(uint32_t guest_addr, const void *data, uint32_t size) const override; +private: + void init(); + void init_child(); + void init_parent() const; + void loop_child() const; + // Pipes + int arguments_pipe[2] = {}; // Send Trampoline Commands From Parent -> Child + int return_value_pipe[2] = {}; // Send Return Value From Child -> Parent + // PID + const pid_t guest_pid = getpid(); +}; \ No newline at end of file diff --git a/src/pipe/memory.cpp b/src/pipe/memory.cpp new file mode 100644 index 0000000..efdd846 --- /dev/null +++ b/src/pipe/memory.cpp @@ -0,0 +1,30 @@ +#include +#include +#include + +#include "main.h" +#include "../log.h" +#ifdef MCPI_HAS_QEMU +#include "../qemu/qemu.h" +#endif + +// Write Memory To Parent/Guest +void PipeImplementation::memory_writer(const uint32_t guest_addr, const void *data, const uint32_t size) const { + iovec local[1]; + local[0].iov_base = (void *) data; + local[0].iov_len = size; + iovec remote[1]; + void *fixed_guest_addr = +#ifdef MCPI_HAS_QEMU + QEMU::guest_to_host(guest_addr) +#else + (void *) (uint64_t) guest_addr +#endif + ; + remote[0].iov_base = fixed_guest_addr; + remote[0].iov_len = size; + const ssize_t ret = process_vm_writev(guest_pid, local, 1, remote, 1, 0); + if (ret != size) { + ERR("Unable To Write Data: 0x%08x: %s", guest_addr, strerror(errno)); + } +} \ No newline at end of file diff --git a/src/qemu/qemu.cpp b/src/qemu/qemu.cpp new file mode 100644 index 0000000..b5aa2f7 --- /dev/null +++ b/src/qemu/qemu.cpp @@ -0,0 +1,25 @@ +#include +#include + +#include + +#include "qemu.h" + +// Access QEMU's Memory +void *QEMU::guest_to_host(const uint32_t guest_addr) { + return (void *) (uintptr_t) (guest_addr + QEMU_GUEST_BASE);; +} + +// Run +#define _TO_STRING(x) #x +#define TO_STRING(x) _TO_STRING(x) +int QEMU::run(const int argc, char **argv) { + // Get Arguments + std::vector new_args; + new_args.push_back("qemu"); + new_args.push_back("-B"); + new_args.push_back(TO_STRING(QEMU_GUEST_BASE)); + new_args.insert(new_args.end(), argv, argv + argc); + // Run + return qemu_main(int(new_args.size()), (char **) new_args.data(), environ); +} diff --git a/src/qemu/qemu.h b/src/qemu/qemu.h new file mode 100644 index 0000000..0ef342c --- /dev/null +++ b/src/qemu/qemu.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +#ifndef MCPI_HAS_QEMU +#error "QEMU Is Not Present!" +#endif + +// QEMU API +extern "C" { + // Called By Patched QEMU + int trampoline_handle_syscall(int32_t num, uint32_t arg1, uint32_t arg2); + // Main + int qemu_main(int argc, char **argv, char **envp); +} + +// Utility Functions +namespace QEMU { + // Memory + void *guest_to_host(uint32_t guest_addr); + // Run + int run(int argc, char **argv); +} \ No newline at end of file diff --git a/src/syscall/handler.cpp b/src/syscall/handler.cpp new file mode 100644 index 0000000..a1e6bd0 --- /dev/null +++ b/src/syscall/handler.cpp @@ -0,0 +1,21 @@ +#include + +#include "../qemu/qemu.h" +#include "main.h" + +// Handle Syscall +int trampoline_handle_syscall(const int32_t num, const uint32_t arg1, uint32_t arg2) { + // Check Syscall + if (num != TRAMPOLINE_SYSCALL) { + return 0; + } + // Run + SyscallImplementation::instance->handle_syscall(arg1, arg2); + return 1; +} +void SyscallImplementation::handle_syscall(const uint32_t arg1, const uint32_t arg2) const { + const uint32_t id = arg1; + const unsigned char *args = (const unsigned char *) QEMU::guest_to_host(arg2); + const uint32_t ret = trampoline(id, args); + *(uint32_t *) args = ret; +} diff --git a/src/syscall/main.cpp b/src/syscall/main.cpp new file mode 100644 index 0000000..1e6891b --- /dev/null +++ b/src/syscall/main.cpp @@ -0,0 +1,20 @@ +#include +#include + +#include "main.h" +#include "../qemu/qemu.h" + +// Main +SyscallImplementation *SyscallImplementation::instance = nullptr; +int SyscallImplementation::main(const int argc, char *argv[]) { + instance = this; + // Setup + trampoline.init(this); + // Run + return QEMU::run(argc, argv); +} + +// Read From QEMU's Memory +void SyscallImplementation::memory_writer(const uint32_t guest_addr, const void *data, const uint32_t size) const { + memcpy(QEMU::guest_to_host(guest_addr), data, size); +} \ No newline at end of file diff --git a/src/syscall/main.h b/src/syscall/main.h new file mode 100644 index 0000000..77cb861 --- /dev/null +++ b/src/syscall/main.h @@ -0,0 +1,10 @@ +#pragma once + +#include "../main.h" + +struct SyscallImplementation final : Implementation { + int main(int argc, char *argv[]) override; + void memory_writer(uint32_t guest_addr, const void *data, uint32_t size) const override; + void handle_syscall(uint32_t arg1, uint32_t arg2) const; + static SyscallImplementation *instance; +}; \ No newline at end of file diff --git a/native/src/trampoline.cpp b/src/trampoline.cpp similarity index 52% rename from native/src/trampoline.cpp rename to src/trampoline.cpp index c31a525..af52795 100644 --- a/native/src/trampoline.cpp +++ b/src/trampoline.cpp @@ -3,21 +3,22 @@ #include "trampoline.h" #include "memory.h" #include "log.h" +#include "main.h" + +// Memory Writer +const Implementation *stored_impl = nullptr; +static void memory_writer(const uint32_t guest_addr, const void *data, const uint32_t size) { + stored_impl->memory_writer(guest_addr, data, size); +} // Run Trampoline -typedef uint32_t (*trampoline_t)(typeof(memory_writer) *writer, uint32_t id, const unsigned char *args); -static trampoline_t func = nullptr; -uint32_t trampoline(uint32_t id, const unsigned char *args) { +uint32_t Trampoline::operator()(const uint32_t id, const unsigned char *args) const { return func(memory_writer, id, args); } // Init -void init_trampoline() { - static bool is_setup = false; - if (is_setup) { - return; - } - is_setup = true; +void Trampoline::init(const Implementation *impl) { + stored_impl = impl; // Open Library void *handle = dlopen("libmedia-layer-trampoline.so", RTLD_NOW); if (handle != nullptr) { diff --git a/src/trampoline.h b/src/trampoline.h new file mode 100644 index 0000000..6faae3a --- /dev/null +++ b/src/trampoline.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +struct Implementation; +struct Trampoline { + void init(const Implementation *impl); + uint32_t operator()(uint32_t id, const unsigned char *args) const; +private: + trampoline_t func = nullptr; +}; \ No newline at end of file