├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── images └── screenshot.png └── src ├── Constants.h ├── Main.c ├── Types.h ├── UefiIOWrapper.c ├── UefiIOWrapper.h └── jit ├── ChunkEmitters.c ├── ChunkEmitters.h ├── InstructionEmitters.c ├── InstructionEmitters.h ├── JIT.c ├── JIT.h ├── JITConstants.h ├── Opcodes.h ├── SpecialByteEmitters.c └── SpecialByteEmitters.h /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | build/ 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Mateusz Maćkowski 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC := cc 2 | LD := ld 3 | RM := rm -f 4 | 5 | SRC_DIR := src 6 | BUILD_DIR := build 7 | OUT_DIR := bin 8 | 9 | ARCH := x86_64 10 | 11 | NAME := uefijitfuck 12 | SOURCES := $(wildcard $(SRC_DIR)/**/*.c) $(wildcard $(SRC_DIR)/*.c) 13 | OBJS := $(patsubst $(SRC_DIR)/%.c, $(BUILD_DIR)/%.o, $(SOURCES)) 14 | TARGET_LIB := $(BUILD_DIR)/$(NAME).so 15 | TARGET := $(OUT_DIR)/$(NAME).efi 16 | 17 | EFIINC := /usr/include/efi 18 | EFIINCS := -I$(EFIINC) -I$(EFIINC)/$(ARCH) -I$(EFIINC)/protocol 19 | LIB := /usr/lib 20 | EFILIB := /usr/lib 21 | EFI_CRT_OBJS := $(EFILIB)/crt0-efi-$(ARCH).o 22 | EFI_LDS := $(EFILIB)/elf_$(ARCH)_efi.lds 23 | 24 | INCLUDE := -I$(SRC_DIR) 25 | CFLAGS := $(INCLUDE) $(EFIINCS) -fno-stack-protector -fpic \ 26 | -fshort-wchar -mno-red-zone -Wall -DEFI_FUNCTION_WRAPPER 27 | 28 | LDFLAGS := -nostdlib -znocombreloc -T $(EFI_LDS) -shared \ 29 | -Bsymbolic -L $(EFILIB) -L $(LIB) $(EFI_CRT_OBJS) 30 | 31 | 32 | all: $(TARGET) 33 | 34 | $(TARGET): $(TARGET_LIB) 35 | @mkdir -p "$(@D)" 36 | objcopy -j .text -j .sdata -j .data -j .dynamic \ 37 | -j .dynsym -j .rel -j .rela -j .reloc \ 38 | --target=efi-app-$(ARCH) $^ $@ 39 | 40 | $(TARGET_LIB): $(OBJS) 41 | $(LD) $(LDFLAGS) $(OBJS) -o $@ -lefi -lgnuefi 42 | 43 | $(BUILD_DIR)/%.o: $(SRC_DIR)/%.c 44 | @mkdir -p "$(@D)" 45 | $(CC) $(CFLAGS) -c $< -o $@ 46 | 47 | clean: 48 | $(RM) -r $(BUILD_DIR)/ $(OUT_DIR)/ 49 | 50 | 51 | .PHONY: clean all 52 | .SECONDARY: $(OBJS) 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | uefi-jitfuck 2 | ============ 3 | 4 | A JIT (Just-In-Time) compiler for Brainfuck programming language running solely 5 | on x86_64 UEFI (Universal Extensible Firmware Interface). 6 | 7 | ![Screenshot of uefi-jitfuck inside UEFI Shell](images/screenshot.png) 8 | 9 | [The Lost Kingdom](http://jonripley.com/i-fiction/games/LostKingdomBF.html) by 10 | Jon Ripley - over 2-megabyte adventure text game running inside UEFI Shell 11 | with *uefi-jitfuck*. 12 | 13 | ## Building 14 | 15 | ### Requirements 16 | 17 | * C Compiler, e.g. GCC as well as linker (ld) 18 | * GNU Make 19 | * [GNU-EFI](https://sourceforge.net/projects/gnu-efi/) 20 | 21 | ### Compiling 22 | 23 | Just execute: 24 | 25 | ``` 26 | $ make 27 | ``` 28 | 29 | in the project root directory. This will create `bin/` directory with 30 | `uefijitfuck.efi` executable inside - this is the binary you want to run. 31 | 32 | ## Usage 33 | 34 | First, build the project or grab the executable from the 35 | [Releases](https://github.com/m4tx/uefi-jitfuck/releases) page and put it on 36 | your ESP (EFI System Partition) along with the programs you want to run. Then, 37 | there are several options: 38 | 39 | * Use your motherboard's built-in boot entry editor 40 | * Add a boot entry using `efibootmgr` 41 | * Use a UEFI Shell 42 | 43 | If you don't want to run this on your UEFI directly, you can also use OVMF 44 | (UEFI firmware for QEMU/KVM). 45 | 46 | The application requires one argument: the name of the file containing 47 | the Brainfuck program you want to run. 48 | -------------------------------------------------------------------------------- /images/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m4tx/uefi-jitfuck/9ef88968eb5b80a3e09dcfa275865f2519b99076/images/screenshot.png -------------------------------------------------------------------------------- /src/Constants.h: -------------------------------------------------------------------------------- 1 | #ifndef UEFI_JITFUCK_CONSTANTS_H 2 | #define UEFI_JITFUCK_CONSTANTS_H 3 | 4 | #include "Types.h" 5 | 6 | #define READ_BUFFER_SIZE 4096 7 | #define MAX_FILE_SIZE 4096000 8 | #define MAX_PROGRAM_SIZE 4096000 9 | #define MEMORY_CELLS 30000 10 | 11 | // Characters used in the language source code 12 | #define INC_PTR_CHR '>' 13 | #define DEC_PTR_CHR '<' 14 | #define INC_VAL_CHR '+' 15 | #define DEC_VAL_CHR '-' 16 | #define OUTPUT_VAL_CHR '.' 17 | #define INPUT_VAL_CHR ',' 18 | #define BLOCK_START_CHR '[' 19 | #define BLOCK_END_CHR ']' 20 | 21 | #endif //UEFI_JITFUCK_CONSTANTS_H 22 | -------------------------------------------------------------------------------- /src/Main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "Constants.h" 7 | #include "UefiIOWrapper.h" 8 | #include "jit/JIT.h" 9 | 10 | static void runProgram(CHAR8* source, UINTN size); 11 | 12 | EFI_STATUS 13 | EFIAPI 14 | efi_main(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) { 15 | InitializeLib(ImageHandle, SystemTable); 16 | initUefiIOWrapper(SystemTable); 17 | 18 | UINTN argc; 19 | CHAR16 **argv; 20 | argc = GetShellArgcArgv(ImageHandle, &argv); 21 | if (argc < 2) { 22 | Print(L"Usage: %s program.bf\n", argv[0]); 23 | return EFI_INVALID_PARAMETER; 24 | } 25 | 26 | EFI_STATUS status; 27 | 28 | EFI_GUID sfspGuid = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID; 29 | EFI_HANDLE *handles = NULL; 30 | UINTN handleCount = 0; 31 | status = uefi_call_wrapper( 32 | SystemTable->BootServices->LocateHandleBuffer, 5, 33 | ByProtocol, &sfspGuid, NULL, &handleCount, &handles); 34 | if (EFI_ERROR(status)) { 35 | Print(L"Could not acquire File System protocol\n"); 36 | return status; 37 | } 38 | 39 | bool success = false; 40 | for (UINTN i = 0; i < (int) handleCount; ++i) { 41 | // Get FS 42 | EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *fs = NULL; 43 | status = uefi_call_wrapper( 44 | SystemTable->BootServices->HandleProtocol, 3, 45 | handles[i], &sfspGuid, (void **) &fs); 46 | if (EFI_ERROR(status)) { 47 | continue; 48 | } 49 | 50 | // Open volume root 51 | EFI_FILE_PROTOCOL *root = NULL; 52 | status = uefi_call_wrapper( 53 | fs->OpenVolume, 2, 54 | fs, &root); 55 | if (EFI_ERROR(status)) { 56 | continue; 57 | } 58 | 59 | // Try to open the file 60 | EFI_FILE_PROTOCOL *file = NULL; 61 | status = uefi_call_wrapper( 62 | root->Open, 5, 63 | root, &file, argv[1], EFI_FILE_MODE_READ, 0); 64 | if (EFI_ERROR(status)) { 65 | continue; 66 | } 67 | 68 | // Read the file 69 | UINTN bufferSize = MAX_FILE_SIZE; 70 | CHAR8 *buffer = (CHAR8*) AllocatePool(bufferSize); 71 | status = uefi_call_wrapper( 72 | file->Read, 3, 73 | file, &bufferSize, buffer); 74 | if (EFI_ERROR(status)) { 75 | continue; 76 | } 77 | 78 | // Close the volume 79 | status = uefi_call_wrapper(root->Close, 1, root); 80 | if (EFI_ERROR(status)) { 81 | Print(L"Could not close the volume\n"); 82 | return status; 83 | } 84 | 85 | // Close the file 86 | status = uefi_call_wrapper(file->Close, 1, file); 87 | if (EFI_ERROR(status)) { 88 | Print(L"Could not close the file\n"); 89 | return status; 90 | } 91 | 92 | runProgram(buffer, bufferSize); 93 | FreePool(buffer); 94 | success = true; 95 | 96 | break; 97 | } 98 | 99 | if (!success) { 100 | Print(L"Could not load specified file"); 101 | return EFI_LOAD_ERROR; 102 | } 103 | 104 | return EFI_SUCCESS; 105 | } 106 | 107 | static void runProgram(CHAR8* source, UINTN size) { 108 | CHAR8 *prog = (CHAR8*) AllocatePool(MAX_PROGRAM_SIZE); 109 | CHAR8 *memory = (CHAR8*) AllocatePool(MEMORY_CELLS); 110 | ZeroMem(memory, MEMORY_CELLS); 111 | jitCompile(source, size, prog, memory)(); 112 | FreePool(prog); 113 | FreePool(memory); 114 | } 115 | -------------------------------------------------------------------------------- /src/Types.h: -------------------------------------------------------------------------------- 1 | #ifndef UEFI_JITFUCK_TYPES_H 2 | #define UEFI_JITFUCK_TYPES_H 3 | 4 | typedef unsigned char Byte; 5 | 6 | enum Register { 7 | RAX = 0, 8 | RBX = 3, 9 | RCX = 1, 10 | RDX = 2, 11 | RSI = 6, 12 | RDI = 7, 13 | RBP = 5, 14 | RSP = 4 15 | }; 16 | 17 | #endif //UEFI_JITFUCK_TYPES_H 18 | -------------------------------------------------------------------------------- /src/UefiIOWrapper.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "Constants.h" 5 | #include "UefiIOWrapper.h" 6 | 7 | static EFI_SYSTEM_TABLE *GlobalSystemTable = NULL; 8 | 9 | char readBuffer[READ_BUFFER_SIZE]; 10 | unsigned readBufferPos = 0; 11 | unsigned readBufferReturnPos = 0; 12 | 13 | void initUefiIOWrapper(EFI_SYSTEM_TABLE *SystemTable) { 14 | GlobalSystemTable = SystemTable; 15 | } 16 | 17 | void printChar(char ch) { 18 | assert(GlobalSystemTable != NULL); 19 | 20 | Print(L"%c", ch); 21 | } 22 | 23 | // Wrapper over ConIn->ReadKeyStroke that mimics the behavior of stdio's 24 | // getchar() function 25 | char readChar() { 26 | assert(GlobalSystemTable != NULL); 27 | 28 | // If a whole line was already returned, read a new one 29 | if (readBufferPos == readBufferReturnPos) { 30 | readBufferPos = 0; 31 | readBufferReturnPos = 0; 32 | 33 | EFI_INPUT_KEY key; 34 | 35 | char ch; 36 | do { 37 | EFI_STATUS status = uefi_call_wrapper( 38 | GlobalSystemTable->ConIn->ReadKeyStroke, 2, 39 | GlobalSystemTable->ConIn, &key); 40 | while (status == EFI_NOT_READY) { 41 | WaitForSingleEvent(GlobalSystemTable->ConIn->WaitForKey, 0); 42 | status = uefi_call_wrapper( 43 | GlobalSystemTable->ConIn->ReadKeyStroke, 2, 44 | GlobalSystemTable->ConIn, &key); 45 | } 46 | 47 | ch = (char) key.UnicodeChar; 48 | if (ch == '\b') { 49 | // Backspace 50 | if (readBufferPos > 0) { 51 | --readBufferPos; 52 | // Move the cursor left 53 | Print(L"%c", ch); 54 | } 55 | } else { 56 | readBuffer[readBufferPos++] = ch; 57 | // Print the character 58 | Print(L"%c", ch); 59 | } 60 | } while (ch != '\r'); 61 | 62 | // Overwrite '\r' with '\n' 63 | readBuffer[readBufferPos - 1] = '\n'; 64 | // Go to a new line if enter was pressed 65 | Print(L"\n"); 66 | } 67 | 68 | return readBuffer[readBufferReturnPos++]; 69 | } 70 | -------------------------------------------------------------------------------- /src/UefiIOWrapper.h: -------------------------------------------------------------------------------- 1 | #ifndef UEFI_JITFUCK_UEFIIOWRAPPER_H 2 | #define UEFI_JITFUCK_UEFIIOWRAPPER_H 3 | 4 | #include 5 | #include 6 | 7 | void initUefiIOWrapper(EFI_SYSTEM_TABLE *SystemTable); 8 | 9 | void printChar(char ch); 10 | 11 | char readChar(); 12 | 13 | 14 | #endif //UEFI_JITFUCK_UEFIIOWRAPPER_H 15 | -------------------------------------------------------------------------------- /src/jit/ChunkEmitters.c: -------------------------------------------------------------------------------- 1 | #include "ChunkEmitters.h" 2 | 3 | void emitByte(Byte **buf, Byte x) { 4 | *((*buf)++) = x; 5 | } 6 | 7 | void emitShort(Byte **buf, short x) { 8 | *((short *) *buf) = x; 9 | *buf += 2; 10 | } 11 | 12 | void emitInt(Byte **buf, int x) { 13 | *((int *) *buf) = x; 14 | *buf += 4; 15 | } 16 | 17 | void emitLong(Byte **buf, long x) { 18 | *((long *) *buf) = x; 19 | *buf += 8; 20 | } 21 | -------------------------------------------------------------------------------- /src/jit/ChunkEmitters.h: -------------------------------------------------------------------------------- 1 | #ifndef UEFI_JITFUCK_CHUNKEMITTERS_H 2 | #define UEFI_JITFUCK_CHUNKEMITTERS_H 3 | 4 | #include 5 | 6 | void emitByte(Byte **buf, Byte x); 7 | 8 | void emitShort(Byte **buf, short x); 9 | 10 | void emitInt(Byte **buf, int x); 11 | 12 | void emitLong(Byte **buf, long x); 13 | 14 | #endif //UEFI_JITFUCK_CHUNKEMITTERS_H 15 | -------------------------------------------------------------------------------- /src/jit/InstructionEmitters.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "Opcodes.h" 5 | #include "Constants.h" 6 | #include "JITConstants.h" 7 | #include "InstructionEmitters.h" 8 | #include "SpecialByteEmitters.h" 9 | #include "ChunkEmitters.h" 10 | 11 | void emitIncReg64(Byte **buf, enum Register reg) { 12 | emitREX(buf, 1, 0, 0, 0); 13 | emitByte(buf, INC64_OPCODE); 14 | emitModRM(buf, 0b11, 0, reg); 15 | } 16 | 17 | void emitDecReg64(Byte **buf, enum Register reg) { 18 | emitREX(buf, 1, 0, 0, 0); 19 | emitByte(buf, DEC64_OPCODE); 20 | emitModRM(buf, 0b11, 1, reg); 21 | } 22 | 23 | void emitIncBytePtr(Byte **buf, enum Register reg) { 24 | // Doesn't support special REX encoding for RSP and RBP 25 | assert(reg != RSP && reg != RBP); 26 | 27 | emitByte(buf, INC8_OPCODE); 28 | emitModRM(buf, 0, 0, reg); 29 | } 30 | 31 | void emitDecBytePtr(Byte **buf, enum Register reg) { 32 | // Doesn't support special REX encoding for RSP and RBP 33 | assert(reg != RSP && reg != RBP); 34 | 35 | emitByte(buf, DEC8_OPCODE); 36 | emitModRM(buf, 0, 1, reg); 37 | } 38 | 39 | void emitMovRegImm64(Byte **buf, enum Register reg, long imm) { 40 | emitREX(buf, 1, 0, 0, 0); 41 | emitByte(buf, (Byte) (MOV_REGIMM64_OPCODE + reg)); 42 | emitLong(buf, imm); 43 | } 44 | 45 | void emitCmpRegImm8(Byte **buf, enum Register reg, Byte imm) { 46 | Byte *oldBuf = *buf; 47 | 48 | emitByte(buf, CMP_REGIMM8_OPCODE); 49 | emitModRM(buf, 0, 7, reg); 50 | emitByte(buf, imm); 51 | 52 | assert(*buf - oldBuf == CMP_REG_IMM8_LEN); 53 | } 54 | 55 | void emitJmpRel32Off(Byte **buf, int off) { 56 | Byte *oldBuf = *buf; 57 | 58 | emitByte(buf, JMP_REL32_OPCODE); 59 | emitInt(buf, off); 60 | 61 | assert(*buf - oldBuf == JMP_REL32OFF_LEN); 62 | } 63 | 64 | void emitJeRel32Off(Byte **buf, int off) { 65 | Byte *oldBuf = *buf; 66 | 67 | emitShort(buf, JE_REL32_OPCODE); 68 | emitInt(buf, off); 69 | 70 | assert(*buf - oldBuf == JE_REL32OFF_LEN); 71 | } 72 | 73 | static inline bool isByte(int value) { 74 | return value >= -128 && value <= 127; 75 | } 76 | 77 | void emitAddRegImm(Byte **buf, enum Register reg, int imm) { 78 | emitREX(buf, 1, 0, 0, 0); 79 | if (isByte(imm)) { 80 | emitByte(buf, ADD_REG64IMM8_OPCODE); 81 | emitModRM(buf, 0b11, 0, reg); 82 | emitByte(buf, (Byte) imm); 83 | } else { 84 | emitByte(buf, ADD_REG64IMM32_OPCODE); 85 | emitModRM(buf, 0b11, 0, reg); 86 | emitInt(buf, imm); 87 | } 88 | } 89 | 90 | void emitAddBytePtrImm(Byte **buf, enum Register reg, Byte imm) { 91 | // Doesn't support special REX encoding for RSP and RBP 92 | assert(reg != RSP && reg != RBP); 93 | 94 | emitByte(buf, ADD_MEMIMM8_OPCODE); 95 | emitModRM(buf, 0, 0, reg); 96 | emitByte(buf, (Byte) imm); 97 | } 98 | 99 | void emitSubRegImm(Byte **buf, enum Register reg, int imm) { 100 | emitREX(buf, 1, 0, 0, 0); 101 | if (isByte(imm)) { 102 | emitByte(buf, SUB_REG64IMM8_OPCODE); 103 | emitModRM(buf, 0b11, 5, reg); 104 | emitByte(buf, (Byte) imm); 105 | } else { 106 | emitByte(buf, SUB_REG64IMM32_OPCODE); 107 | emitModRM(buf, 0b11, 5, reg); 108 | emitInt(buf, imm); 109 | } 110 | } 111 | 112 | void emitSubBytePtrImm(Byte **buf, enum Register reg, Byte imm) { 113 | // Doesn't support special REX encoding for RSP and RBP 114 | assert(reg != RSP && reg != RBP); 115 | 116 | emitByte(buf, SUB_MEMIMM8_OPCODE); 117 | emitModRM(buf, 0, 5, reg); 118 | emitByte(buf, (Byte) imm); 119 | } 120 | 121 | void emitMovRegRegPtr(Byte **buf, enum Register reg1, enum Register reg2) { 122 | // Doesn't support special REX encoding for RSP and RBP 123 | assert(reg1 != RSP && reg1 != RBP && reg2 != RSP && reg2 != RBP); 124 | 125 | emitREX(buf, 1, 0, 0, 0); 126 | emitByte(buf, MOV_REGMEM64_OPCODE); 127 | emitModRM(buf, 0, reg1, reg2); 128 | } 129 | 130 | void emitMovRegPtrReg(Byte **buf, enum Register reg1, enum Register reg2) { 131 | // Doesn't support special REX encoding for RSP and RBP 132 | assert(reg1 != RSP && reg1 != RBP && reg2 != RSP && reg2 != RBP); 133 | 134 | emitREX(buf, 1, 0, 0, 0); 135 | emitByte(buf, MOV_MEMREG64_OPCODE); 136 | emitModRM(buf, 0, reg2, reg1); 137 | } 138 | 139 | void emitCallReg(Byte **buf, enum Register reg) { 140 | emitByte(buf, CALL_REG_OPCODE); 141 | emitModRM(buf, 0b11, 2, reg); 142 | } 143 | 144 | void emitPushReg(Byte **buf, enum Register reg) { 145 | emitByte(buf, (Byte) (PUSH_REG_OPCODE + reg)); 146 | } 147 | 148 | void emitPopReg(Byte **buf, enum Register reg) { 149 | emitByte(buf, (Byte) (POP_REG_OPCODE + reg)); 150 | } 151 | 152 | void emitRet(Byte **buf) { 153 | emitByte(buf, RET_OPCODE); 154 | } 155 | -------------------------------------------------------------------------------- /src/jit/InstructionEmitters.h: -------------------------------------------------------------------------------- 1 | #ifndef UEFI_JITFUCK_INSTRUCTIONEMITTERS_H 2 | #define UEFI_JITFUCK_INSTRUCTIONEMITTERS_H 3 | 4 | #include "Types.h" 5 | 6 | /// inc reg 7 | void emitIncReg64(Byte **buf, enum Register reg); 8 | 9 | /// dec reg 10 | void emitDecReg64(Byte **buf, enum Register reg); 11 | 12 | /// inc BYTE [reg] 13 | void emitIncBytePtr(Byte **buf, enum Register reg); 14 | 15 | /// dec BYTE [reg] 16 | void emitDecBytePtr(Byte **buf, enum Register reg); 17 | 18 | /// mov reg, imm 19 | void emitMovRegImm64(Byte **buf, enum Register reg, long imm); 20 | 21 | /// cmp reg, imm 22 | void emitCmpRegImm8(Byte **buf, enum Register reg, Byte imm); 23 | 24 | /// jmp +off 25 | void emitJmpRel32Off(Byte **buf, int off); 26 | 27 | /// je +off 28 | void emitJeRel32Off(Byte **buf, int off); 29 | 30 | /// add reg, imm 31 | void emitAddRegImm(Byte **buf, enum Register reg, int imm); 32 | 33 | /// add BYTE [reg], imm 34 | void emitAddBytePtrImm(Byte **buf, enum Register reg, Byte imm); 35 | 36 | /// sub reg, imm 37 | void emitSubRegImm(Byte **buf, enum Register reg, int imm); 38 | 39 | /// sub BYTE [reg], imm 40 | void emitSubBytePtrImm(Byte **buf, enum Register reg, Byte imm); 41 | 42 | /// mov reg1, [reg2] 43 | void emitMovRegRegPtr(Byte **buf, enum Register reg1, enum Register reg2); 44 | 45 | /// mov [reg1], reg2 46 | void emitMovRegPtrReg(Byte **buf, enum Register reg1, enum Register reg2); 47 | 48 | /// call reg 49 | void emitCallReg(Byte **buf, enum Register reg); 50 | 51 | /// push reg 52 | void emitPushReg(Byte **buf, enum Register reg); 53 | 54 | /// pop reg 55 | void emitPopReg(Byte **buf, enum Register reg); 56 | 57 | /// ret 58 | void emitRet(Byte **buf); 59 | 60 | #endif //UEFI_JITFUCK_INSTRUCTIONEMITTERS_H 61 | -------------------------------------------------------------------------------- /src/jit/JIT.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "JIT.h" 4 | #include "Types.h" 5 | #include "JITConstants.h" 6 | #include "Constants.h" 7 | #include "UefiIOWrapper.h" 8 | #include "InstructionEmitters.h" 9 | 10 | static inline void handleIncPtrChr(Byte **buf, size_t count) { 11 | if (count == 1) { 12 | emitIncReg64(buf, POINTER_REGISTER); 13 | } else { 14 | emitAddRegImm(buf, POINTER_REGISTER, (int) count); 15 | } 16 | } 17 | 18 | static inline void handleDecPtrChr(Byte **buf, size_t count) { 19 | if (count == 1) { 20 | emitDecReg64(buf, POINTER_REGISTER); 21 | } else { 22 | emitSubRegImm(buf, POINTER_REGISTER, (int) count); 23 | } 24 | } 25 | 26 | static inline void handleIncValChr(Byte **buf, size_t count) { 27 | if (count == 1) { 28 | emitIncBytePtr(buf, POINTER_REGISTER); 29 | } else { 30 | emitAddBytePtrImm(buf, POINTER_REGISTER, (Byte) (count % 256)); 31 | } 32 | } 33 | 34 | static inline void handleDecValChr(Byte **buf, size_t count) { 35 | if (count == 1) { 36 | emitDecBytePtr(buf, POINTER_REGISTER); 37 | } else { 38 | emitSubBytePtrImm(buf, POINTER_REGISTER, (Byte) (count % 256)); 39 | } 40 | } 41 | 42 | static inline void emitGuardedCallRax(Byte **buf) { 43 | emitPushReg(buf, POINTER_REGISTER); 44 | emitCallReg(buf, RAX); 45 | emitPopReg(buf, POINTER_REGISTER); 46 | } 47 | 48 | static inline void handleOutputValChr(Byte **buf) { 49 | emitMovRegRegPtr(buf, RDI, POINTER_REGISTER); 50 | emitMovRegImm64(buf, RAX, (long) &printChar); 51 | emitGuardedCallRax(buf); 52 | } 53 | 54 | static inline void handleInputValChr(Byte **buf) { 55 | emitMovRegImm64(buf, RAX, (long) &readChar); 56 | emitGuardedCallRax(buf); 57 | emitMovRegPtrReg(buf, POINTER_REGISTER, RAX); 58 | } 59 | 60 | static inline void 61 | handleBlockStartChr(Byte **buf, Byte *blockStack[1000000], int *blockStackTop) { 62 | Byte *oldBuf = *buf; 63 | 64 | blockStack[(*blockStackTop)++] = *buf; 65 | 66 | emitCmpRegImm8(buf, POINTER_REGISTER, 0); 67 | // Will be rewritten at the end of the block 68 | emitJeRel32Off(buf, 0); 69 | 70 | assert(*buf - oldBuf == BLOCK_START_LEN); 71 | } 72 | 73 | static inline void 74 | handleBlockEndChr(Byte **buf, Byte *blockStack[1000000], int *blockStackTop) { 75 | Byte *blockStartPos = blockStack[--(*blockStackTop)]; 76 | int offset = (int) (*buf - blockStartPos + JMP_REL32OFF_LEN); 77 | emitJmpRel32Off(buf, -offset); 78 | 79 | // Update block start jump offset 80 | blockStartPos += BLOCK_START_LEN - JE_REL32OFF_LEN; 81 | emitJeRel32Off(&blockStartPos, offset - BLOCK_START_LEN); 82 | } 83 | 84 | CompiledFunction 85 | jitCompile(const unsigned char *source, size_t length, void *codeBuffer, 86 | void *programMemory) { 87 | Byte *buf = codeBuffer; 88 | 89 | Byte *blockStack[1000000]; 90 | int blockStackTop = 0; 91 | 92 | emitMovRegImm64(&buf, POINTER_REGISTER, (long) programMemory); 93 | 94 | for (size_t i = 0; i < length; ++i) { 95 | switch (source[i]) { 96 | case INC_PTR_CHR: 97 | case DEC_PTR_CHR: 98 | case INC_VAL_CHR: 99 | case DEC_VAL_CHR: { 100 | char op = source[i]; 101 | size_t j = i + 1; 102 | while (j < length && source[j] == op) { 103 | ++j; 104 | } 105 | 106 | size_t count = j - i; 107 | if (op == INC_PTR_CHR) { 108 | handleIncPtrChr(&buf, count); 109 | } else if (op == DEC_PTR_CHR) { 110 | handleDecPtrChr(&buf, count); 111 | } else if (op == INC_VAL_CHR) { 112 | handleIncValChr(&buf, count); 113 | } else if (op == DEC_VAL_CHR) { 114 | handleDecValChr(&buf, count); 115 | } 116 | 117 | i = j - 1; 118 | break; 119 | } 120 | case OUTPUT_VAL_CHR: 121 | handleOutputValChr(&buf); 122 | break; 123 | case INPUT_VAL_CHR: 124 | handleInputValChr(&buf); 125 | break; 126 | case BLOCK_START_CHR: 127 | handleBlockStartChr(&buf, blockStack, &blockStackTop); 128 | break; 129 | case BLOCK_END_CHR: 130 | handleBlockEndChr(&buf, blockStack, &blockStackTop); 131 | break; 132 | default: 133 | // A comment 134 | break; 135 | } 136 | } 137 | 138 | emitRet(&buf); 139 | 140 | return codeBuffer; 141 | } 142 | -------------------------------------------------------------------------------- /src/jit/JIT.h: -------------------------------------------------------------------------------- 1 | #ifndef UEFI_JITFUCK_JIT_H 2 | #define UEFI_JITFUCK_JIT_H 3 | 4 | #include 5 | 6 | typedef void(*CompiledFunction)(); 7 | 8 | CompiledFunction 9 | jitCompile(const unsigned char *source, size_t length, void *codeBuffer, 10 | void *programMemory); 11 | 12 | #endif //UEFI_JITFUCK_JIT_H 13 | -------------------------------------------------------------------------------- /src/jit/JITConstants.h: -------------------------------------------------------------------------------- 1 | #ifndef UEFI_JITFUCK_JITCONSTANTS_H 2 | #define UEFI_JITFUCK_JITCONSTANTS_H 3 | 4 | // Some instruction lengths 5 | #define CMP_REG_IMM8_LEN 3 6 | #define JMP_REL32OFF_LEN 5 7 | #define JE_REL32OFF_LEN 6 8 | #define BLOCK_START_LEN (CMP_REG_IMM8_LEN + JE_REL32OFF_LEN) 9 | 10 | // The register where the current cell pointer is held 11 | #define POINTER_REGISTER RSI 12 | 13 | #endif //UEFI_JITFUCK_JITCONSTANTS_H 14 | -------------------------------------------------------------------------------- /src/jit/Opcodes.h: -------------------------------------------------------------------------------- 1 | #ifndef UEFI_JITFUCK_OPCODES_H 2 | #define UEFI_JITFUCK_OPCODES_H 3 | 4 | #define REX_PREFIX 0b0100 5 | #define INC8_OPCODE 0xFE 6 | #define INC64_OPCODE 0xFF 7 | #define DEC8_OPCODE 0xFE 8 | #define DEC64_OPCODE 0xFF 9 | #define MOV_REGIMM64_OPCODE 0xB8 10 | #define CMP_REGIMM8_OPCODE 0x80 11 | #define JMP_REL32_OPCODE 0xE9 12 | #define JE_REL32_OPCODE ((short) 0x840F) 13 | #define ADD_REG64IMM8_OPCODE 0x83 14 | #define ADD_REG64IMM32_OPCODE 0x81 15 | #define ADD_MEMIMM8_OPCODE 0x80 16 | #define SUB_REG64IMM8_OPCODE 0x83 17 | #define SUB_REG64IMM32_OPCODE 0x81 18 | #define SUB_MEMIMM8_OPCODE 0x80 19 | #define MOV_MEMREG64_OPCODE 0x89 20 | #define MOV_REGMEM64_OPCODE 0x8B 21 | #define CALL_REG_OPCODE 0xFF 22 | #define PUSH_REG_OPCODE 0x50 23 | #define POP_REG_OPCODE 0x58 24 | #define RET_OPCODE 0xC3 25 | 26 | #endif //UEFI_JITFUCK_OPCODES_H 27 | -------------------------------------------------------------------------------- /src/jit/SpecialByteEmitters.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "Opcodes.h" 4 | #include "SpecialByteEmitters.h" 5 | #include "ChunkEmitters.h" 6 | 7 | void emitREX(Byte **buf, Byte w, Byte r, Byte x, Byte b) { 8 | assert((w & ~1u) == 0); 9 | assert((r & ~1u) == 0); 10 | assert((x & ~1u) == 0); 11 | assert((b & ~1u) == 0); 12 | 13 | Byte toEmit = (Byte) 14 | ((REX_PREFIX << 4u) | (w << 3u) | (r << 2u) | (x << 1u) | b); 15 | emitByte(buf, toEmit); 16 | } 17 | 18 | void emitModRM(Byte **buf, Byte mod, Byte reg, Byte rm) { 19 | assert((mod & ~0b11u) == 0); 20 | assert((reg & ~0b111u) == 0); 21 | assert((rm & ~0b111u) == 0); 22 | 23 | emitByte(buf, (mod << 6u) | (reg << 3u) | rm); 24 | } 25 | -------------------------------------------------------------------------------- /src/jit/SpecialByteEmitters.h: -------------------------------------------------------------------------------- 1 | #ifndef UEFI_JITFUCK_SPECIALBYTEEMITTERS_H 2 | #define UEFI_JITFUCK_SPECIALBYTEEMITTERS_H 3 | 4 | #include "Types.h" 5 | 6 | void emitREX(Byte **buf, Byte w, Byte r, Byte x, Byte b); 7 | 8 | void emitModRM(Byte **buf, Byte mod, Byte reg, Byte rm); 9 | 10 | #endif //UEFI_JITFUCK_SPECIALBYTEEMITTERS_H 11 | --------------------------------------------------------------------------------