diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e91191..2b177be 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,23 +4,11 @@ cmake_minimum_required(VERSION 3.17.0) project(runtime) # Headers -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) +add_subdirectory(lib) +if(TARGET trampoline) 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 add_executable(runtime src/main.cpp @@ -45,9 +33,15 @@ endif() target_compile_options(runtime PRIVATE -Wall -Wextra -Werror -Wpointer-arith -Wshadow -Wnull-dereference) # Link -target_link_libraries(runtime dl rt trampoline-headers) +target_link_libraries(runtime + dl + rt + trampoline-headers +) # Install if(DEFINED MCPI_BIN_DIR) install(TARGETS runtime DESTINATION "${MCPI_BIN_DIR}") + # License + install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE" DESTINATION "${MCPI_LEGAL_DIR}/${PROJECT_NAME}") endif() \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/README.md b/README.md index c6777ca..d6de637 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,29 @@ -# MCPI-Reborn Runtime -**Fact:** Minecraft: Pi Edition is a 32-bit ARM program. +# Reborn Runtime +This is a simple program allowing ARM32 code to easily call "native" code. -**Another fact:** Most modern computers do not use 32-bit ARM and therefore cannot run MCPI natively. +By running an ARM32 program inside this runtime, it gains the ability to call `raw_trampoline`. This function copies its arguments and passes them to the `trampoline` function inside `libmedia-layer-trampoline.so`. -**Solution:** This project allows MCPI to run on modern computers. +The runtime also automatically uses QEMU on x86_64 systems. -## How -This project works differently depending on the host system's architecture. +## Terminology +- "Guest" code is the main ARM32 program. +- "Host" code is the native code located in `libmedia-layer-trampoline.so`. -### 64-Bit x86 Host -On this platform, a patched version of QEMU is used. +## Example +There is a simple C example [here](./example). It sends a given string to the host, which returns the string's length multiplied by two. -QEMU emulates ARM code so MCPI can run on x86 hardware. And the patch adds a system-call which allows MCPI to run graphics code on the host. This prevents the need for emulated GPU drivers. +## Early Returning +`raw_trampoline` supports returning before the host code has finished executing. This can be enabled by passing `true` to `allow_early_return`. However, this is only supported in certain circumstances, can cause race-conditions, and prevents the guest code from receiving the host's return value. -### 64-Bit ARM Host -QEMU is not necessary on this platform because it can already run 32-bit ARM code natively. +## Syscall Versus Pipe Trampolines +The runtime supports two methods of passing data between the guest and host. +- System Call + - Data is passed through a custom system-call added to QEMU. + - Only supported on x86_64. +- Pipes + - Data is passed through standard UNIX pipes. + - Supports early-returning. + - Can be forced using an environmental variable. -Instead, the runtime is implemented as two processes: a parent and a child. The child becomes MCPI and can send graphics commands to the parent. And because the parent is 64-bit, 32-bit drivers are not needed. - -### 32-Bit ARM Host -This project is unnecessary on this platform. - -### Other Architectures -Any unlisted platforms are unsupported. +## Licensing +The runtime itself is licensed with [GPLv2](./LICENSE) to comply with QEMU's license. However, the `raw_trampoline` function and associated headers are licensed with the [Unlicense](lib/LICENSE) as they do not directly link to the runtime. \ No newline at end of file diff --git a/example/.gitignore b/example/.gitignore new file mode 100644 index 0000000..6789c8f --- /dev/null +++ b/example/.gitignore @@ -0,0 +1,2 @@ +/build +/run.sh \ No newline at end of file diff --git a/example/build.sh b/example/build.sh new file mode 100755 index 0000000..ff0fa4f --- /dev/null +++ b/example/build.sh @@ -0,0 +1,51 @@ +#!/bin/sh + +set -e + +# Run Script +RUN="$(pwd)/run.sh" +rm -f "${RUN}" +out() { + echo "$1" >> "${RUN}" +} +out '#!/bin/sh' +out 'set -e' +chmod +x "${RUN}" + +# Create Build Directory +rm -rf build +dir() { + mkdir "$1" + cd "$1" +} +dir build + +# Build Runtime +dir runtime +build() { + cmake -GNinja "../../$1" + cmake --build . +} +build ../ +out "export PATH=\"$(pwd):\${PATH}\"" +cd ../ + +# Build Host Component +build_example_part() { + dir "$1" + build "$1" +} +build_example_part host +out "export LD_LIBRARY_PATH=\"$(pwd):\${LD_LIBRARY_PATH}\"" +cd ../ + +# Build Guest Component +build_example_part guest + +# Finalize Script +out "export QEMU_LD_PREFIX=/usr/arm-linux-gnueabihf" +out "exec runtime \"$(pwd)/example\"" + +# Done +echo 'BUILD COMPLETE!' +echo 'See ./run.sh' \ No newline at end of file diff --git a/example/guest/CMakeLists.txt b/example/guest/CMakeLists.txt new file mode 100644 index 0000000..3e76e41 --- /dev/null +++ b/example/guest/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.17.0) + +# Build For ARM +set(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc) +set(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++) +set(CMAKE_SYSTEM_NAME "Linux") +set(CMAKE_SYSTEM_PROCESSOR "arm") + +# Start Project +project(guest) + +# Build Library +add_subdirectory(../../lib lib) + +# Build +add_executable(example src/example.c) +target_link_libraries(example trampoline) \ No newline at end of file diff --git a/example/guest/src/example.c b/example/guest/src/example.c new file mode 100644 index 0000000..6f6400c --- /dev/null +++ b/example/guest/src/example.c @@ -0,0 +1,19 @@ +#include +#include +#include + +#include + +void run(uint32_t cmd, const char *str) { + // Arguments Must Be Modifiable + unsigned char *args = (unsigned char *) strdup(str); + // Sned Command + fprintf(stderr, "Guest Is Sending: %u: %s\n", cmd, str); + uint32_t ret = raw_trampoline(cmd, 0, strlen(str) + 1, args); + fprintf(stderr, "Guest Has Received: %u\n", ret); + free(args); +} +int main() { + run(0, "Hello World!"); + run(1, "Bye World!"); +} \ No newline at end of file diff --git a/example/host/CMakeLists.txt b/example/host/CMakeLists.txt new file mode 100644 index 0000000..03b248d --- /dev/null +++ b/example/host/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.17.0) + +# Start Project +project(host) + +# Build Library +add_subdirectory(../../lib lib) + +# Build +add_library(media-layer-trampoline SHARED src/trampoline.c) +target_link_libraries(media-layer-trampoline trampoline-headers) \ No newline at end of file diff --git a/example/host/src/trampoline.c b/example/host/src/trampoline.c new file mode 100644 index 0000000..5360daf --- /dev/null +++ b/example/host/src/trampoline.c @@ -0,0 +1,14 @@ +#include +#include + +#include + +// writer: Function That Can Write To Guest Memory +// id: Command ID +// args: Pointer To Command Arguments +// Return Value: Returned To The Guest (Unless Early Return Is Enabled) +uint32_t trampoline(trampoline_writer_t writer, uint32_t id, const unsigned char *args) { + const char *str = (const char *) args; + fprintf(stderr, "Host Has Recieved: %u: %s\n", id, str); + return strlen(str) * 2; +} \ No newline at end of file diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt new file mode 100644 index 0000000..d7ca7b6 --- /dev/null +++ b/lib/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.17.0) + +# Start Project +project(trampoline) + +# Headers +add_library(trampoline-headers INTERFACE) +target_include_directories(trampoline-headers INTERFACE include) + +# Check Architecture +include(CheckSymbolExists) +check_symbol_exists("__arm__" "" TRAMPOLINE_IS_GUEST) +if(NOT TRAMPOLINE_IS_GUEST) + target_compile_definitions(trampoline-headers INTERFACE MCPI_BUILD_RUNTIME) + return() +endif() + +# Library To Call Trampoline +add_library(trampoline OBJECT src/guest.cpp) +target_link_libraries(trampoline trampoline-headers) \ No newline at end of file diff --git a/lib/LICENSE b/lib/LICENSE new file mode 100644 index 0000000..fdddb29 --- /dev/null +++ b/lib/LICENSE @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to diff --git a/include/trampoline/env-list.h b/lib/include/trampoline/env-list.h similarity index 100% rename from include/trampoline/env-list.h rename to lib/include/trampoline/env-list.h diff --git a/include/trampoline/types.h b/lib/include/trampoline/types.h similarity index 55% rename from include/trampoline/types.h rename to lib/include/trampoline/types.h index ef848bd..0078936 100644 --- a/include/trampoline/types.h +++ b/lib/include/trampoline/types.h @@ -2,6 +2,11 @@ #include +// C++ Support +#ifdef __cplusplus +extern "C" { +#endif + // Maximum Arguments Length #define MAX_TRAMPOLINE_ARGS_SIZE 33554432 // 32 MiB @@ -17,10 +22,26 @@ struct trampoline_pipe_arguments { }; // Function Types +#ifdef MCPI_BUILD_RUNTIME 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_raw_t(trampoline_writer_t writer, uint32_t id, const unsigned char *args); +trampoline_raw_t trampoline; +typedef trampoline_raw_t *trampoline_t; +#endif // Environmental Variables +#ifdef __cplusplus #define ENV(name, ...) constexpr const char *name##_ENV = #name; #include "env-list.h" -#undef ENV \ No newline at end of file +#undef ENV +#endif + +// Call Trampoline From Guest Code +#ifndef MCPI_BUILD_RUNTIME +uint32_t raw_trampoline(uint32_t id, int allow_early_return, uint32_t length, unsigned char *args); +#endif + +// C++ +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/src/guest.cpp b/lib/src/guest.cpp new file mode 100644 index 0000000..ece1315 --- /dev/null +++ b/lib/src/guest.cpp @@ -0,0 +1,86 @@ +#include +#include +#include + +#include + +// Logging +#define ERR(format, ...) \ + ({ \ + fprintf(stderr, "TRAMPOLINE ERROR: " format "\n", ##__VA_ARGS__); \ + exit(EXIT_FAILURE); \ + }) + +// Syscall Method +static uint32_t trampoline_syscall(const uint32_t id, unsigned char *args) { + // Make Syscall + const long ret = syscall(TRAMPOLINE_SYSCALL, id, args); // This Modifies Arguments + if (ret == -1) { + // Error + ERR("System Call Error: %s", strerror(errno)); + } + // Return + return *(uint32_t *) args; +} + +// Pipe Method +static int get_pipe(const char *env) { + const char *value = getenv(env); + if (value == nullptr) { + ERR("Missing Variable: %s", env); + } + const std::string str = value; + return std::stoi(str); +} +static uint32_t trampoline_pipe(const uint32_t id, const bool allow_early_return, const uint32_t length, const unsigned char *args) { + // Get Pipes + static int arguments_pipe = -1; + static int return_value_pipe = -1; + if (arguments_pipe == -1) { + arguments_pipe = get_pipe(_MCPI_TRAMPOLINE_ARGUMENTS_ENV); + return_value_pipe = get_pipe(_MCPI_TRAMPOLINE_RETURN_VALUE_ENV); + } + // Write Command + const trampoline_pipe_arguments cmd = { + .id = id, + .allow_early_return = allow_early_return, + .length = length + }; + if (write(arguments_pipe, &cmd, sizeof(trampoline_pipe_arguments)) != sizeof(trampoline_pipe_arguments)) { + ERR("Unable To Write Command"); + } + // Write Arguments + size_t position = 0; + while (position < length) { + const ssize_t ret = write(arguments_pipe, args + position, length - position); + if (ret == -1) { + ERR("Unable To Write Arguments"); + } else { + position += ret; + } + } + if (allow_early_return) { + return 0; + } + // Return + uint32_t ret; + if (read(return_value_pipe, &ret, sizeof(uint32_t)) != sizeof(uint32_t)) { + ERR("Unable To Read Return Value"); + } + return ret; +} + +// Main Function +uint32_t raw_trampoline(const uint32_t id, const int allow_early_return, const uint32_t length, unsigned char *args) { + if (length > MAX_TRAMPOLINE_ARGS_SIZE) { + ERR("Command Too Big"); + } + // Configure Method + static bool use_syscall = getenv(MCPI_USE_PIPE_TRAMPOLINE_ENV) == nullptr; + // Use Correct Method + if (use_syscall) { + return trampoline_syscall(id, args); + } else { + return trampoline_pipe(id, allow_early_return, length, args); + } +} \ No newline at end of file diff --git a/qemu/CMakeLists.txt b/qemu/CMakeLists.txt index 71f96cd..aebde23 100644 --- a/qemu/CMakeLists.txt +++ b/qemu/CMakeLists.txt @@ -6,46 +6,53 @@ if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.24.0) endif() # Archive -set(RUNTIME_QEMU_ARCHIVE "" CACHE FILEPATH "QEMU Path") -if(NOT EXISTS "${RUNTIME_QEMU_ARCHIVE}") - message(FATAL_ERROR "Missing QEMU Archive!") -endif() +set(QEMU_ARCHIVE "qemu-9.2.0.tar.xz") +set(QEMU_HASH "f859f0bc65e1f533d040bbe8c92bcfecee5af2c921a6687c652fb44d089bd894") # Library set(QEMU_LIBRARY "/lib/libqemu-arm.so") -# Build -include(ExternalProject) +# Patches +set(PATCHES "${CMAKE_CURRENT_SOURCE_DIR}/patches") +set(PATCH_CMD "patch" "-p1" "<") + +# PkgConfig set(PKGCONFIG_ENV "") if(DEFINED ENV{PKG_CONFIG_LIBDIR}) set(PKGCONFIG_ENV "PKG_CONFIG_LIBDIR=$ENV{PKG_CONFIG_LIBDIR}") endif() + +# Build +include(ExternalProject) ExternalProject_Add(qemu - URL "${RUNTIME_QEMU_ARCHIVE}" + URL "${CMAKE_CURRENT_SOURCE_DIR}/${QEMU_ARCHIVE}" + URL_HASH "SHA256=${QEMU_HASH}" # Configure Build CONFIGURE_COMMAND "${CMAKE_COMMAND}" "-E" "env" ${PKGCONFIG_ENV} "/configure" "--prefix=" + # Cross-Compile "--cross-prefix=" "--cc=${CMAKE_C_COMPILER}" "--cxx=${CMAKE_CXX_COMPILER}" + # Optimize "--disable-debug-info" "--enable-strip" "--enable-pie" - "-Db_staticpic=true" - "-Db_lundef=false" + # Minimal Build "--target-list=arm-linux-user" "--without-default-features" USES_TERMINAL_CONFIGURE TRUE # Build Command - BUILD_COMMAND "ninja" - COMMAND "ninja" "install" + BUILD_COMMAND "ninja" "install" BUILD_BYPRODUCTS "${QEMU_LIBRARY}" USES_TERMINAL_BUILD TRUE # Patch Command - PATCH_COMMAND "patch" "-p1" "<" "${CMAKE_CURRENT_SOURCE_DIR}/runtime.patch" + PATCH_COMMAND ${PATCH_CMD} "${PATCHES}/build-as-shared-library.patch" + COMMAND ${PATCH_CMD} "${PATCHES}/flatpak-support.patch" + COMMAND ${PATCH_CMD} "${PATCHES}/trampoline-syscall.patch" # Disable Install Command INSTALL_COMMAND "" ) diff --git a/qemu/patches/build-as-shared-library.patch b/qemu/patches/build-as-shared-library.patch new file mode 100644 index 0000000..14704d9 --- /dev/null +++ b/qemu/patches/build-as-shared-library.patch @@ -0,0 +1,49 @@ +--- 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/meson.build ++++ b/meson.build +@@ -1,6 +1,6 @@ + project('qemu', ['c'], meson_version: '>=1.5.0', + default_options: ['warning_level=1', 'c_std=gnu11', 'cpp_std=gnu++11', 'b_colorout=auto', +- 'b_staticpic=false', 'stdsplit=false', 'optimization=2', 'b_pie=true'], ++ 'b_staticpic=true', 'stdsplit=false', 'optimization=2', 'b_pie=true'], + version: files('VERSION')) + + meson.add_devenv({ 'MESON_BUILD_ROOT' : meson.project_build_root() }) +@@ -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 diff --git a/qemu/patches/flatpak-support.patch b/qemu/patches/flatpak-support.patch new file mode 100644 index 0000000..b4703a9 --- /dev/null +++ b/qemu/patches/flatpak-support.patch @@ -0,0 +1,10 @@ +--- a/meson.build ++++ b/meson.build +@@ -2613,7 +2613,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_OPENAT2_H', cc.has_header('linux/openat2.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')) diff --git a/qemu/patches/trampoline-syscall.patch b/qemu/patches/trampoline-syscall.patch new file mode 100644 index 0000000..40a9ccb --- /dev/null +++ b/qemu/patches/trampoline-syscall.patch @@ -0,0 +1,31 @@ +--- a/meson.build ++++ b/meson.build +@@ -1,5 +1,5 @@ + project('qemu', ['c'], meson_version: '>=1.5.0', +- default_options: ['warning_level=1', 'c_std=gnu11', 'cpp_std=gnu++11', 'b_colorout=auto', ++ default_options: ['warning_level=1', 'c_std=gnu11', 'cpp_std=gnu++11', 'b_colorout=auto', 'b_lundef=false', + 'b_staticpic=true', 'stdsplit=false', 'optimization=2', 'b_pie=true'], + version: files('VERSION')) + +--- 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). diff --git a/qemu/qemu-9.2.0.tar.xz b/qemu/qemu-9.2.0.tar.xz new file mode 100644 index 0000000..42f4e20 Binary files /dev/null and b/qemu/qemu-9.2.0.tar.xz differ diff --git a/qemu/runtime.patch b/qemu/runtime.patch deleted file mode 100644 index c98e9f8..0000000 --- a/qemu/runtime.patch +++ /dev/null @@ -1,71 +0,0 @@ ---- 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/src/log.h b/src/log.h index c00b978..49ddf3d 100644 --- a/src/log.h +++ b/src/log.h @@ -4,7 +4,7 @@ #include #define ERR(format, ...) \ - { \ + ({ \ fprintf(stderr, "RUNTIME ERROR: " format "\n", ##__VA_ARGS__); \ exit(EXIT_FAILURE); \ - } + })