commit ef40bd03c569fdc9bcadd2e21aaa7b2cb12c5cff Author: ktkk Date: Wed Oct 15 11:30:58 2025 +0000 Initial commit diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..8392d15 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3615de0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +# Nix +result* + +# Misc +.direnv + +.cache/ +build/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100755 index 0000000..9262aae --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.18) + +set(CMAKE_CXX_STANDARD 23) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +# set(CMAKE_CXX_CLANG_TIDY clang-tidy -checks=-*,bugprone-*,clang-analyzer-*,concurrency-*,misc-*,performance-*,portability-*,readability-*) + +project(executable_memory) + +add_subdirectory(src) diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100755 index 0000000..5810fd3 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,13 @@ +{ + "version": 1, + "configurePresets": [ + { + "name": "ninja-release", + "binaryDir": "${sourceDir}/build/${presetName}", + "generator": "Ninja", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release" + } + } + ] +} diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..da51303 --- /dev/null +++ b/flake.lock @@ -0,0 +1,62 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": [ + "systems" + ] + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1760443284, + "narHash": "sha256-jDqjCfod2sCnsFSwTrHTU71El67OQ/XR2CDWcMR2JcY=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "2ea123939c5cb71d2761896c27e475aeab266364", + "type": "github" + }, + "original": { + "id": "nixpkgs", + "type": "indirect" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs", + "systems": "systems" + } + }, + "systems": { + "locked": { + "lastModified": 1680978846, + "narHash": "sha256-Gtqg8b/v49BFDpDetjclCYXm8mAnTrUzR0JnE2nv5aw=", + "owner": "nix-systems", + "repo": "x86_64-linux", + "rev": "2ecfcac5e15790ba6ce360ceccddb15ad16d08a8", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "x86_64-linux", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..d97c68f --- /dev/null +++ b/flake.nix @@ -0,0 +1,43 @@ +{ + description = "Simple JIT"; + + inputs = { + nixpkgs.url = "nixpkgs"; + systems.url = "github:nix-systems/x86_64-linux"; + flake-utils = { + url = "github:numtide/flake-utils"; + inputs.systems.follows = "systems"; + }; + }; + + outputs = + { self + , nixpkgs + , flake-utils + , ... + }: + flake-utils.lib.eachDefaultSystem (system: + let + pkgs = nixpkgs.legacyPackages.${system}; + pname = "simple-jit"; #package name + version = "0.0.1"; + src = ./.; + buildInputs = with pkgs; [ + ]; + nativeBuildInputs = with pkgs; [ + ninja + cmake + pkg-config + clang-tools + ]; + in + { + devShells.default = pkgs.mkShell { + inherit buildInputs nativeBuildInputs; + }; + + packages.default = pkgs.mkShell.override { stdenv = pkgs.clangStdenv; } { + inherit buildInputs nativeBuildInputs; + }; + }); +} diff --git a/include/Assembler.h b/include/Assembler.h new file mode 100755 index 0000000..6281d57 --- /dev/null +++ b/include/Assembler.h @@ -0,0 +1,11 @@ +#include + +#include "Types.h" + +struct Assembler { + explicit Assembler(std::vector&); + + std::vector& output; + + void emit8(u8) const; +}; diff --git a/include/Executable.h b/include/Executable.h new file mode 100755 index 0000000..9047c15 --- /dev/null +++ b/include/Executable.h @@ -0,0 +1,26 @@ +#include + +#include "Types.h" +#include "Noncopyable.h" + +class Executable { + MAKE_NONCOPYABLE(Executable); + MAKE_NONMOVABLE(Executable); + +public: + Executable(void* code, std::size_t code_size); + ~Executable(); + + template + Return run() const + { + using ExecutableFunction = Return(); + auto executable_function = reinterpret_cast(m_code); + + return executable_function(); + } + +private: + void* m_code; + std::size_t m_code_size; +}; diff --git a/include/Noncopyable.h b/include/Noncopyable.h new file mode 100755 index 0000000..e76191a --- /dev/null +++ b/include/Noncopyable.h @@ -0,0 +1,9 @@ +#define MAKE_NONCOPYABLE(c) \ +private: \ + c(c const&) = delete; \ + c& operator=(c const&) = delete + +#define MAKE_NONMOVABLE(c) \ +private: \ + c(c&&) = delete; \ + c& operator=(c&&) = delete diff --git a/include/Types.h b/include/Types.h new file mode 100755 index 0000000..6fab0da --- /dev/null +++ b/include/Types.h @@ -0,0 +1,23 @@ +using u64 = __UINT64_TYPE__; +using u32 = __UINT32_TYPE__; +using u16 = __UINT16_TYPE__; +using u8 = __UINT8_TYPE__; + +using i64 = __INT64_TYPE__; +using i32 = __INT32_TYPE__; +using i16 = __INT16_TYPE__; +using i8 = __INT8_TYPE__; + +using f32 = float; +static_assert(__FLT_MANT_DIG__ == 24 && __FLT_MAX_EXP__ == 128); + +using f64 = double; +static_assert(__DBL_MANT_DIG__ == 53 && __DBL_MAX_EXP__ == 1024); + +#if __LDBL_MANT_DIG__ == 64 && __LDBL_MAX__EXP__ == 16384 +using f80 = long double; +#elif __LDBL_MANT_DIG__ == 113 && __LDBL_MAX_EXP__ == 16384 +using f128 = long double; +#endif + +using size_t = __SIZE_TYPE__; diff --git a/src/Assembler.cpp b/src/Assembler.cpp new file mode 100755 index 0000000..e0963c6 --- /dev/null +++ b/src/Assembler.cpp @@ -0,0 +1,13 @@ +#include + +#include + +Assembler::Assembler(std::vector& output) + : output(output) +{ +} + +void Assembler::emit8(u8 value) const +{ + output.push_back(value); +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100755 index 0000000..1380ec5 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,12 @@ +set(SOURCES + Executable.cpp + Assembler.cpp) + +add_library(jit STATIC EXCLUDE_FROM_ALL ${SOURCES}) +target_include_directories(jit PUBLIC ${CMAKE_SOURCE_DIR}/include) + +add_executable(executable_bytes main.cpp) +target_link_libraries(executable_bytes jit) +set_target_properties(executable_bytes + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) diff --git a/src/Executable.cpp b/src/Executable.cpp new file mode 100755 index 0000000..651326f --- /dev/null +++ b/src/Executable.cpp @@ -0,0 +1,15 @@ +#include + +#include +#include + +Executable::Executable(void* code, std::size_t code_size) + : m_code(code) + , m_code_size(code_size) +{ +} + +Executable::~Executable() +{ + munmap(m_code, m_code_size); +} diff --git a/src/main.cpp b/src/main.cpp new file mode 100755 index 0000000..4dc18fb --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,30 @@ +#include +#include + +#include + +#include +#include + +int main() +{ + std::vector output; + auto const assembler = Assembler(output); + assembler.emit8(0x55); // push ebp + assembler.emit8(0x89); assembler.emit8(0xE5); // mov ebp, esp + assembler.emit8(0xB8); assembler.emit8(0x2A); assembler.emit8(0x00); assembler.emit8(0x00); assembler.emit8(0x00); // mov eax, 42 + assembler.emit8(0x5D); // pop ebp + assembler.emit8(0xC3); // ret + + auto* executable_memory = mmap(nullptr, output.size(), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); + if (executable_memory == MAP_FAILED) { + return 1; + } + std::memcpy(executable_memory, output.data(), output.size()); + mprotect(executable_memory, output.size(), PROT_READ | PROT_EXEC); + + auto const executable = Executable(executable_memory, output.size()); + auto const result = executable.run(); + + std::println("{}", result); +}