Runtime Refactor!
This commit is contained in:
parent
377f9ddbc4
commit
f8b56c50ba
@ -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()
|
3
include/trampoline/env-list.h
Normal file
3
include/trampoline/env-list.h
Normal file
@ -0,0 +1,3 @@
|
||||
ENV(MCPI_USE_PIPE_TRAMPOLINE, "Use The Pipe-Based Trampoline")
|
||||
ENV(_MCPI_TRAMPOLINE_ARGUMENTS, "")
|
||||
ENV(_MCPI_TRAMPOLINE_RETURN_VALUE, "")
|
@ -3,15 +3,13 @@
|
||||
#include <stdint.h>
|
||||
|
||||
// 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);
|
||||
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
|
@ -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()
|
@ -1,5 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
void create_pipes();
|
||||
void pipe_read_loop();
|
||||
void init_pipe_guest();
|
@ -1,55 +0,0 @@
|
||||
#include <unistd.h>
|
||||
#include <cstring>
|
||||
#include <cerrno>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/prctl.h>
|
||||
|
||||
#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");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
#include <sys/uio.h>
|
||||
#include <cstring>
|
||||
#include <cerrno>
|
||||
|
||||
#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));
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <sys/types.h>
|
||||
|
||||
// Read External Memory
|
||||
void memory_writer(uint32_t guest_addr, const void *data, uint32_t size);
|
||||
void init_memory(pid_t guest_pid);
|
@ -1,24 +0,0 @@
|
||||
#include <sys/wait.h>
|
||||
|
||||
#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);
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
void init_signals(pid_t guest_pid);
|
@ -1,6 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <trampoline/types.h>
|
||||
|
||||
uint32_t trampoline(uint32_t id, const unsigned char *args);
|
||||
void init_trampoline();
|
@ -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" "<SOURCE_DIR>/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}"
|
||||
"<SOURCE_DIR>/configure"
|
||||
"--prefix=${CMAKE_INSTALL_PREFIX}"
|
||||
"--prefix=<INSTALL_DIR>"
|
||||
"--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 "<BINARY_DIR>/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 "<INSTALL_DIR>${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()
|
||||
ExternalProject_Get_Property(qemu SOURCE_DIR)
|
||||
install(FILES "${SOURCE_DIR}/COPYING" DESTINATION "${MCPI_LEGAL_DIR}/qemu")
|
||||
endif()
|
71
qemu/runtime.patch
Normal file
71
qemu/runtime.patch
Normal file
@ -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
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#define _ATFILE_SOURCE
|
||||
+#include <dlfcn.h>
|
||||
+#include <trampoline/types.h>
|
||||
#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,
|
@ -6,5 +6,5 @@
|
||||
#define ERR(format, ...) \
|
||||
{ \
|
||||
fprintf(stderr, "RUNTIME ERROR: " format "\n", ##__VA_ARGS__); \
|
||||
exit(EXIT_FAILURE); \
|
||||
exit(EXIT_FAILURE); \
|
||||
}
|
25
src/main.cpp
Normal file
25
src/main.cpp
Normal file
@ -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);
|
||||
}
|
12
src/main.h
Normal file
12
src/main.h
Normal file
@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#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;
|
||||
};
|
@ -5,51 +5,50 @@
|
||||
|
||||
#include <trampoline/types.h>
|
||||
|
||||
#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");
|
||||
}
|
46
src/pipe/main.cpp
Normal file
46
src/pipe/main.cpp
Normal file
@ -0,0 +1,46 @@
|
||||
#include <unistd.h>
|
||||
#include <cstring>
|
||||
#include <cerrno>
|
||||
#include <sys/wait.h>
|
||||
#include <linux/prctl.h>
|
||||
#include <sys/prctl.h>
|
||||
|
||||
#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
|
||||
}
|
||||
}
|
20
src/pipe/main.h
Normal file
20
src/pipe/main.h
Normal file
@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#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();
|
||||
};
|
30
src/pipe/memory.cpp
Normal file
30
src/pipe/memory.cpp
Normal file
@ -0,0 +1,30 @@
|
||||
#include <sys/uio.h>
|
||||
#include <cstring>
|
||||
#include <cerrno>
|
||||
|
||||
#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));
|
||||
}
|
||||
}
|
25
src/qemu/qemu.cpp
Normal file
25
src/qemu/qemu.cpp
Normal file
@ -0,0 +1,25 @@
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
|
||||
#include <trampoline/types.h>
|
||||
|
||||
#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<const char *> 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);
|
||||
}
|
23
src/qemu/qemu.h
Normal file
23
src/qemu/qemu.h
Normal file
@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#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);
|
||||
}
|
21
src/syscall/handler.cpp
Normal file
21
src/syscall/handler.cpp
Normal file
@ -0,0 +1,21 @@
|
||||
#include <trampoline/types.h>
|
||||
|
||||
#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;
|
||||
}
|
20
src/syscall/main.cpp
Normal file
20
src/syscall/main.cpp
Normal file
@ -0,0 +1,20 @@
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#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);
|
||||
}
|
10
src/syscall/main.h
Normal file
10
src/syscall/main.h
Normal file
@ -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;
|
||||
};
|
@ -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) {
|
11
src/trampoline.h
Normal file
11
src/trampoline.h
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <trampoline/types.h>
|
||||
|
||||
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;
|
||||
};
|
Loading…
Reference in New Issue
Block a user