Runtime Refactor!

This commit is contained in:
TheBrokenRail 2024-11-10 05:26:16 -05:00
parent 377f9ddbc4
commit f8b56c50ba
28 changed files with 430 additions and 286 deletions

View File

@ -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()

View File

@ -0,0 +1,3 @@
ENV(MCPI_USE_PIPE_TRAMPOLINE, "Use The Pipe-Based Trampoline")
ENV(_MCPI_TRAMPOLINE_ARGUMENTS, "")
ENV(_MCPI_TRAMPOLINE_RETURN_VALUE, "")

View File

@ -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

View File

@ -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()

View File

@ -1,5 +0,0 @@
#pragma once
void create_pipes();
void pipe_read_loop();
void init_pipe_guest();

View File

@ -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");
}
}
}

View File

@ -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));
}
}

View File

@ -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);

View File

@ -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);
}

View File

@ -1,5 +0,0 @@
#pragma once
#include <sys/types.h>
void init_signals(pid_t guest_pid);

View File

@ -1,6 +0,0 @@
#pragma once
#include <trampoline/types.h>
uint32_t trampoline(uint32_t id, const unsigned char *args);
void init_trampoline();

View File

@ -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
View 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 = &regs1;
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

View File

@ -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,

View File

@ -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
View 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
View 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;
};

View File

@ -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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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;
};

View File

@ -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
View 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;
};