Runtime Refactor!
This commit is contained in:
parent
377f9ddbc4
commit
f8b56c50ba
@ -7,25 +7,51 @@ project(runtime)
|
|||||||
add_library(trampoline-headers INTERFACE)
|
add_library(trampoline-headers INTERFACE)
|
||||||
target_include_directories(trampoline-headers INTERFACE include)
|
target_include_directories(trampoline-headers INTERFACE include)
|
||||||
|
|
||||||
# Build
|
|
||||||
if(NOT TRAMPOLINE_HEADERS_ONLY)
|
|
||||||
# Check Architecture
|
# Check Architecture
|
||||||
include(CheckSymbolExists)
|
include(CheckSymbolExists)
|
||||||
check_symbol_exists("__arm__" "" NO_RUNTIME_NEEDED)
|
check_symbol_exists("__arm__" "" NO_RUNTIME_NEEDED)
|
||||||
if(NOT NO_RUNTIME_NEEDED)
|
if(NO_RUNTIME_NEEDED)
|
||||||
check_symbol_exists("__aarch64__" "" USE_NATIVE_RUNTIME)
|
return()
|
||||||
check_symbol_exists("__x86_64__" "" USE_QEMU_RUNTIME)
|
endif()
|
||||||
# Include Correct Sub-Project
|
|
||||||
set(RUNTIME_RPATH "$ORIGIN/../lib/native")
|
# Only Headers
|
||||||
set(RUNTIME_EXTRA_LINK_FLAG "--disable-new-dtags")
|
option(TRAMPOLINE_HEADERS_ONLY "Skip Building Runtime" FALSE)
|
||||||
|
if(TRAMPOLINE_HEADERS_ONLY)
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
target_compile_definitions(trampoline-headers INTERFACE MCPI_BUILD_RUNTIME)
|
target_compile_definitions(trampoline-headers INTERFACE MCPI_BUILD_RUNTIME)
|
||||||
if(USE_NATIVE_RUNTIME)
|
|
||||||
add_subdirectory(native)
|
# Build
|
||||||
elseif(USE_QEMU_RUNTIME)
|
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)
|
add_subdirectory(qemu)
|
||||||
target_compile_definitions(trampoline-headers INTERFACE MCPI_RUNTIME_IS_QEMU)
|
target_sources(runtime PRIVATE
|
||||||
else()
|
src/syscall/main.cpp
|
||||||
message(FATAL_ERROR "Unsupported Architecture")
|
src/syscall/handler.cpp
|
||||||
endif()
|
src/qemu/qemu.cpp
|
||||||
|
)
|
||||||
endif()
|
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()
|
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>
|
#include <stdint.h>
|
||||||
|
|
||||||
// Maximum Arguments Length
|
// Maximum Arguments Length
|
||||||
#define MAX_TRAMPOLINE_ARGS_SIZE 2097152 // 2 MiB
|
#define MAX_TRAMPOLINE_ARGS_SIZE 16777216 // 16 MiB
|
||||||
|
|
||||||
// System Call Constants
|
// System Call Constants
|
||||||
#define TRAMPOLINE_SYSCALL 0x1337
|
#define TRAMPOLINE_SYSCALL 0x1337
|
||||||
#define QEMU_GUEST_BASE 0x40000 // Arbitrary Value (Aligns To 4k And 16k Page Sizes)
|
#define QEMU_GUEST_BASE 0x40000 // Arbitrary Value (Aligns To 4k And 16k Page Sizes)
|
||||||
|
|
||||||
// Pipe Constants
|
// Pipe Command Structure
|
||||||
#define TRAMPOLINE_ARGUMENTS_PIPE_ENV "_MCPI_TRAMPOLINE_ARGUMENTS"
|
|
||||||
#define TRAMPOLINE_RETURN_VALUE_PIPE_ENV "_MCPI_TRAMPOLINE_RETURN_VALUE"
|
|
||||||
struct trampoline_pipe_arguments {
|
struct trampoline_pipe_arguments {
|
||||||
uint32_t id;
|
uint32_t id;
|
||||||
uint32_t allow_early_return;
|
uint32_t allow_early_return;
|
||||||
@ -21,3 +19,8 @@ struct trampoline_pipe_arguments {
|
|||||||
// Function Types
|
// Function Types
|
||||||
typedef void (*trampoline_writer_t)(uint32_t guest_addr, const void *data, uint32_t size);
|
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()
|
endif()
|
||||||
|
|
||||||
# Archive
|
# Archive
|
||||||
if(NOT DEFINED RUNTIME_QEMU_ARCHIVE)
|
set(RUNTIME_QEMU_ARCHIVE "" CACHE FILEPATH "QEMU Path")
|
||||||
message(FATAL_ERROR "Missing QEMU Archive")
|
if(NOT EXISTS "${RUNTIME_QEMU_ARCHIVE}")
|
||||||
|
message(FATAL_ERROR "Missing QEMU Archive!")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Flatpak Support
|
# Library
|
||||||
set(QEMU_FLATPAK_PATCH "")
|
set(QEMU_LIBRARY "/lib/libqemu-arm.so")
|
||||||
if(MCPI_IS_FLATPAK_BUILD)
|
|
||||||
set(QEMU_FLATPAK_PATCH "sed" "-i" "s/libdrm/libdrm-dis/g" "<SOURCE_DIR>/meson.build")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Build
|
# Build
|
||||||
include(ExternalProject)
|
include(ExternalProject)
|
||||||
@ -22,42 +20,51 @@ set(PKGCONFIG_ENV "")
|
|||||||
if(DEFINED ENV{PKG_CONFIG_LIBDIR})
|
if(DEFINED ENV{PKG_CONFIG_LIBDIR})
|
||||||
set(PKGCONFIG_ENV "PKG_CONFIG_LIBDIR=$ENV{PKG_CONFIG_LIBDIR}")
|
set(PKGCONFIG_ENV "PKG_CONFIG_LIBDIR=$ENV{PKG_CONFIG_LIBDIR}")
|
||||||
endif()
|
endif()
|
||||||
set(EXTRA_C_FLAGS "-s -I${CMAKE_CURRENT_SOURCE_DIR}/../include")
|
|
||||||
ExternalProject_Add(qemu
|
ExternalProject_Add(qemu
|
||||||
URL "${RUNTIME_QEMU_ARCHIVE}"
|
URL "${RUNTIME_QEMU_ARCHIVE}"
|
||||||
# Configure Build
|
# Configure Build
|
||||||
CONFIGURE_COMMAND
|
CONFIGURE_COMMAND
|
||||||
"${CMAKE_COMMAND}" "-E" "env"
|
"${CMAKE_COMMAND}" "-E" "env"
|
||||||
${PKGCONFIG_ENV}
|
${PKGCONFIG_ENV}
|
||||||
"CFLAGS=${EXTRA_C_FLAGS}"
|
|
||||||
"CXXFLAGS=${EXTRA_C_FLAGS}"
|
|
||||||
"<SOURCE_DIR>/configure"
|
"<SOURCE_DIR>/configure"
|
||||||
"--prefix=${CMAKE_INSTALL_PREFIX}"
|
"--prefix=<INSTALL_DIR>"
|
||||||
"--cross-prefix="
|
"--cross-prefix="
|
||||||
"--cc=${CMAKE_C_COMPILER}"
|
"--cc=${CMAKE_C_COMPILER}"
|
||||||
"--cxx=${CMAKE_CXX_COMPILER}"
|
"--cxx=${CMAKE_CXX_COMPILER}"
|
||||||
"--extra-ldflags=-ldl -Wl,-rpath=${RUNTIME_RPATH} -Wl,${RUNTIME_EXTRA_LINK_FLAG}"
|
|
||||||
"--disable-debug-info"
|
"--disable-debug-info"
|
||||||
|
"--enable-strip"
|
||||||
|
"--enable-pie"
|
||||||
|
"--enable-lto"
|
||||||
|
"-Db_staticpic=true"
|
||||||
|
"-Db_lundef=false"
|
||||||
"--target-list=arm-linux-user"
|
"--target-list=arm-linux-user"
|
||||||
"--without-default-features"
|
"--without-default-features"
|
||||||
USES_TERMINAL_CONFIGURE TRUE
|
USES_TERMINAL_CONFIGURE TRUE
|
||||||
# Build Command
|
# Build Command
|
||||||
BUILD_COMMAND "ninja" "qemu-arm"
|
BUILD_COMMAND "ninja"
|
||||||
BUILD_BYPRODUCTS "<BINARY_DIR>/qemu-arm"
|
|
||||||
USES_TERMINAL_BUILD TRUE
|
USES_TERMINAL_BUILD TRUE
|
||||||
# Disable Install/Test Commands
|
# Install
|
||||||
INSTALL_COMMAND ""
|
INSTALL_DIR "${QEMU_INSTALL_DIR}"
|
||||||
|
INSTALL_COMMAND "ninja" "install"
|
||||||
|
INSTALL_BYPRODUCTS "<INSTALL_DIR>${QEMU_LIBRARY}"
|
||||||
|
# Disable Test Command
|
||||||
TEST_COMMAND ""
|
TEST_COMMAND ""
|
||||||
# Patch Command
|
# Patch Command
|
||||||
PATCH_COMMAND "patch" "-p1" "<" "${CMAKE_CURRENT_SOURCE_DIR}/src/trampoline.patch"
|
PATCH_COMMAND "patch" "-p1" "<" "${CMAKE_CURRENT_SOURCE_DIR}/runtime.patch"
|
||||||
COMMAND ${QEMU_FLATPAK_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
|
# Install
|
||||||
function(install_runtime bin_dir legal_dir)
|
if(DEFINED MCPI_LIB_DIR)
|
||||||
ExternalProject_Get_property(qemu BINARY_DIR)
|
install(PROGRAMS "${QEMU_LIBRARY}" DESTINATION "${MCPI_LIB_DIR}")
|
||||||
install(PROGRAMS "${BINARY_DIR}/qemu-arm" DESTINATION "${bin_dir}" RENAME "runtime")
|
|
||||||
# License
|
# License
|
||||||
ExternalProject_Get_property(qemu SOURCE_DIR)
|
ExternalProject_Get_Property(qemu SOURCE_DIR)
|
||||||
install(FILES "${SOURCE_DIR}/COPYING" DESTINATION "${legal_dir}/qemu")
|
install(FILES "${SOURCE_DIR}/COPYING" DESTINATION "${MCPI_LEGAL_DIR}/qemu")
|
||||||
endfunction()
|
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,
|
|
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 <trampoline/types.h>
|
||||||
|
|
||||||
#include "loop.h"
|
#include "../log.h"
|
||||||
|
#include "main.h"
|
||||||
#include "log.h"
|
|
||||||
#include "trampoline.h"
|
|
||||||
#include "memory.h"
|
|
||||||
|
|
||||||
// Setup Pipes
|
// Setup Pipes
|
||||||
#define PIPE_READ 0
|
#define PIPE_READ 0
|
||||||
#define PIPE_WRITE 1
|
#define PIPE_WRITE 1
|
||||||
static int arguments_pipe[2];
|
|
||||||
static int return_value_pipe[2];
|
|
||||||
static void safe_pipe(int *out) {
|
static void safe_pipe(int *out) {
|
||||||
int ret = pipe(out);
|
const int ret = pipe(out);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
ERR("Unable To Create Pipe: %s", strerror(errno));
|
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(arguments_pipe);
|
||||||
safe_pipe(return_value_pipe);
|
safe_pipe(return_value_pipe);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Guest
|
// Parent
|
||||||
void init_pipe_guest() {
|
void PipeImplementation::init_parent() const {
|
||||||
// Close Unneeded Pipes
|
// Close Unneeded Pipes
|
||||||
close(arguments_pipe[PIPE_READ]);
|
close(arguments_pipe[PIPE_READ]);
|
||||||
close(return_value_pipe[PIPE_WRITE]);
|
close(return_value_pipe[PIPE_WRITE]);
|
||||||
// Setup Environment
|
// Setup Environment
|
||||||
setenv(TRAMPOLINE_ARGUMENTS_PIPE_ENV, std::to_string(arguments_pipe[PIPE_WRITE]).c_str(), true);
|
setenv(_MCPI_TRAMPOLINE_ARGUMENTS_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_RETURN_VALUE_ENV, std::to_string(return_value_pipe[PIPE_READ]).c_str(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Host
|
// Child
|
||||||
void pipe_read_loop() {
|
void PipeImplementation::init_child() {
|
||||||
// Close Unneeded Pipes
|
// Close Unneeded Pipes
|
||||||
close(arguments_pipe[PIPE_WRITE]);
|
close(arguments_pipe[PIPE_WRITE]);
|
||||||
close(return_value_pipe[PIPE_READ]);
|
close(return_value_pipe[PIPE_READ]);
|
||||||
|
// Init Trampoline
|
||||||
|
trampoline.init(this);
|
||||||
|
}
|
||||||
|
void PipeImplementation::loop_child() const {
|
||||||
// Wait For Commands
|
// Wait For Commands
|
||||||
trampoline_pipe_arguments cmd = {};
|
trampoline_pipe_arguments cmd = {};
|
||||||
while (read(arguments_pipe[PIPE_READ], &cmd, sizeof(trampoline_pipe_arguments)) > 0) {
|
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];
|
static unsigned char args[MAX_TRAMPOLINE_ARGS_SIZE];
|
||||||
size_t position = 0;
|
size_t position = 0;
|
||||||
while (position < cmd.length) {
|
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) {
|
if (ret == -1) {
|
||||||
ERR("Unable To Read Trampoline Arguments");
|
ERR("Unable To Read Trampoline Arguments");
|
||||||
} else {
|
} else {
|
||||||
@ -58,7 +57,7 @@ void pipe_read_loop() {
|
|||||||
}
|
}
|
||||||
uint32_t ret = trampoline(cmd.id, args);
|
uint32_t ret = trampoline(cmd.id, args);
|
||||||
if (!cmd.allow_early_return) {
|
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) {
|
if (ret2 == -1) {
|
||||||
ERR("Unable To Write Trampoline Return Value");
|
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 "trampoline.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "log.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
|
// Run Trampoline
|
||||||
typedef uint32_t (*trampoline_t)(typeof(memory_writer) *writer, uint32_t id, const unsigned char *args);
|
uint32_t Trampoline::operator()(const uint32_t id, const unsigned char *args) const {
|
||||||
static trampoline_t func = nullptr;
|
|
||||||
uint32_t trampoline(uint32_t id, const unsigned char *args) {
|
|
||||||
return func(memory_writer, id, args);
|
return func(memory_writer, id, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init
|
// Init
|
||||||
void init_trampoline() {
|
void Trampoline::init(const Implementation *impl) {
|
||||||
static bool is_setup = false;
|
stored_impl = impl;
|
||||||
if (is_setup) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
is_setup = true;
|
|
||||||
// Open Library
|
// Open Library
|
||||||
void *handle = dlopen("libmedia-layer-trampoline.so", RTLD_NOW);
|
void *handle = dlopen("libmedia-layer-trampoline.so", RTLD_NOW);
|
||||||
if (handle != nullptr) {
|
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