diff --git a/Makefile b/Makefile index a6dae17..510bba1 100644 --- a/Makefile +++ b/Makefile @@ -49,7 +49,7 @@ build/%.riscv.o: src/emulated/%.c $(GENERATED) | build $(RISCV_CC) $(RISCV_ARCH_FLAGS) -I $(dir $(GENERATED)) -I src/emulated -c $< -o $@ $(EMU_OBJ): $(EMU_OBJ_FILES) | build - $(RISCV_CC) $(RISCV_ARCH_FLAGS) -nostdlib -Wl,-e,entry -Wl,-Ttext=0 $(EMU_OBJ_FILES) -o $@ + $(RISCV_CC) $(RISCV_ARCH_FLAGS) -nostdlib -Wl,-e,entry -Wl,-Ttext=0 -Wl,--emit-relocs $(EMU_OBJ_FILES) -o $@ .FORCE: diff --git a/src/config.h b/src/config.h index 8a8ac5e..b3a70a5 100644 --- a/src/config.h +++ b/src/config.h @@ -1,5 +1,10 @@ #pragma once -#define EMU_MEM_SIZE 4096u +#define EMU_MEM_SIZE 16384u #define EMU_PROGRAM_PATH "build/emulated.riscv.elf" #define EMU_ENTRY_SYMBOL "entry" + +#define STACK_SIZE 1024 +#define RISCV_MEM_OFFSET 0x80000000 +#define SIZE_USERDATA 512 +#define SECTION_USERDATA (EMU_MEM_SIZE - STACK_SIZE - SIZE_USERDATA) diff --git a/src/elf.c b/src/elf.c index 41c1fce..dca16fe 100644 --- a/src/elf.c +++ b/src/elf.c @@ -67,6 +67,13 @@ uint32_t read_u32_le(const uint8_t* data) { | ((uint32_t)data[3] << 24); } +static void write_u32_le(uint8_t* data, uint32_t value) { + data[0] = (uint8_t)(value & 0xffu); + data[1] = (uint8_t)((value >> 8) & 0xffu); + data[2] = (uint8_t)((value >> 16) & 0xffu); + data[3] = (uint8_t)((value >> 24) & 0xffu); +} + uint32_t align_up(uint32_t value, uint32_t align) { if (align == 0u) { return value; @@ -74,6 +81,192 @@ uint32_t align_up(uint32_t value, uint32_t align) { return (value + align - 1u) & ~(align - 1u); } +static int apply_relocations(const uint8_t* file_buf, size_t bytes, uint8_t* memory, + uint32_t mem_size, uint32_t e_shoff, uint16_t e_shentsize, uint16_t e_shnum, + const uint32_t* section_runtime, const uint32_t* section_addr, + const uint32_t* section_size, const uint8_t* section_loaded) { + + for (uint16_t i = 0; i < e_shnum; ++i) { + const uint8_t* sh = file_buf + e_shoff + (uint32_t)i * e_shentsize; + uint32_t sh_type = read_u32_le(sh + 4); + uint32_t sh_offset = read_u32_le(sh + 16); + uint32_t sh_size = read_u32_le(sh + 20); + uint32_t sh_link = read_u32_le(sh + 24); + uint32_t sh_info = read_u32_le(sh + 28); + uint32_t sh_entsize = read_u32_le(sh + 36); + + if (sh_type != SHT_RELA && sh_type != SHT_REL) { + continue; + } + + if (sh_link >= e_shnum || sh_info >= e_shnum) { + return LOAD_ERR_RELOC_INVALID; + } + + if (sh_offset >= bytes || sh_size > (uint32_t)bytes - sh_offset) { + return LOAD_ERR_RELOC_INVALID; + } + + if (!section_loaded[sh_info]) { + continue; + } + + const uint8_t* symtab_sh = file_buf + e_shoff + sh_link * (uint32_t)e_shentsize; + uint32_t symtab_offset = read_u32_le(symtab_sh + 16); + uint32_t symtab_size = read_u32_le(symtab_sh + 20); + uint32_t symtab_entsize = read_u32_le(symtab_sh + 36); + + if (symtab_offset >= bytes || symtab_size > (uint32_t)bytes - symtab_offset) { + return LOAD_ERR_STRTAB_INVALID; + } + + if (symtab_entsize == 0u) { + symtab_entsize = 16u; + } + + if (symtab_entsize < 16u) { + return LOAD_ERR_STRTAB_INVALID; + } + + const uint8_t* symtab = file_buf + symtab_offset; + uint32_t symtab_count = symtab_size / symtab_entsize; + + uint32_t entry_size = sh_entsize; + if (entry_size == 0u) { + entry_size = (sh_type == SHT_RELA) ? 12u : 8u; + } + + if (entry_size < ((sh_type == SHT_RELA) ? 12u : 8u)) { + return LOAD_ERR_RELOC_INVALID; + } + + uint32_t count = sh_size / entry_size; + const uint8_t* reltab = file_buf + sh_offset; + + for (uint32_t r = 0u; r < count; ++r) { + const uint8_t* rel = reltab + r * entry_size; + uint32_t r_offset = read_u32_le(rel + 0); + uint32_t r_info = read_u32_le(rel + 4); + int32_t r_addend = 0; + + if (sh_type == SHT_RELA) { + r_addend = (int32_t)read_u32_le(rel + 8); + } + + uint32_t sym_index = r_info >> 8; + uint32_t r_type = r_info & 0xffu; + + if (sym_index >= symtab_count) { + return LOAD_ERR_RELOC_INVALID; + } + + const uint8_t* sym = symtab + sym_index * symtab_entsize; + uint32_t st_value = read_u32_le(sym + 4); + uint16_t st_shndx = read_u16_le(sym + 14); + + uint32_t sym_runtime = 0u; + if (st_shndx == 0u) { + sym_runtime = st_value; + } else { + if (st_shndx >= e_shnum || !section_loaded[st_shndx]) { + return LOAD_ERR_SYMBOL_INVALID_SECTION; + } + + uint32_t base_runtime = section_runtime[st_shndx]; + uint32_t base_elf = section_addr[st_shndx]; + if (st_value < base_elf) { + return LOAD_ERR_SYMBOL_INVALID_SECTION; + } + sym_runtime = base_runtime + (st_value - base_elf); + } + + uint32_t reloc_addr = 0u; + if (section_addr[sh_info] != 0u) { + reloc_addr = r_offset; + } else { + if (r_offset >= section_size[sh_info]) { + return LOAD_ERR_RELOC_INVALID; + } + reloc_addr = section_runtime[sh_info] + r_offset; + } + + if (reloc_addr >= mem_size || mem_size - reloc_addr < 4u) { + return LOAD_ERR_RELOC_INVALID; + } + + uint32_t inst = read_u32_le(memory + reloc_addr); + uint32_t sym_addr = sym_runtime + RISCV_MEM_OFFSET; + int32_t value = (int32_t)sym_addr + r_addend; + + switch (r_type) { + case R_RISCV_NONE: + case R_RISCV_RELAX: + break; + case R_RISCV_HI20: { + uint32_t hi20 = (uint32_t)((value + 0x800) >> 12); + inst = (inst & 0x00000fffu) | (hi20 << 12); + write_u32_le(memory + reloc_addr, inst); + break; + } + case R_RISCV_LO12_I: { + uint32_t imm12 = (uint32_t)(value & 0xfffu); + inst = (inst & 0x000fffffu) | (imm12 << 20); + write_u32_le(memory + reloc_addr, inst); + break; + } + case R_RISCV_LO12_S: { + uint32_t imm12 = (uint32_t)(value & 0xfffu); + inst &= 0x01fff07fu; + inst |= (imm12 & 0x1fu) << 7; + inst |= ((imm12 >> 5) & 0x7fu) << 25; + write_u32_le(memory + reloc_addr, inst); + break; + } + case R_RISCV_BRANCH: { + uint32_t reloc_addr_cpu = reloc_addr + RISCV_MEM_OFFSET; + int32_t rel = (int32_t)(value - (int32_t)reloc_addr_cpu); + uint32_t uimm = (uint32_t)rel; + uint32_t imm12 = (uimm >> 12) & 0x1u; + uint32_t imm10_5 = (uimm >> 5) & 0x3fu; + uint32_t imm4_1 = (uimm >> 1) & 0xfu; + uint32_t imm11 = (uimm >> 11) & 0x1u; + inst &= 0x01fff07fu; + inst |= (imm11 << 7); + inst |= (imm4_1 << 8); + inst |= (imm10_5 << 25); + inst |= (imm12 << 31); + write_u32_le(memory + reloc_addr, inst); + break; + } + case R_RISCV_JAL: { + uint32_t reloc_addr_cpu = reloc_addr + RISCV_MEM_OFFSET; + int32_t rel = (int32_t)(value - (int32_t)reloc_addr_cpu); + uint32_t uimm = (uint32_t)rel; + uint32_t imm20 = (uimm >> 20) & 0x1u; + uint32_t imm10_1 = (uimm >> 1) & 0x3ffu; + uint32_t imm11 = (uimm >> 11) & 0x1u; + uint32_t imm19_12 = (uimm >> 12) & 0xffu; + inst = (inst & 0x00000fffu) + | (imm20 << 31) + | (imm19_12 << 12) + | (imm11 << 20) + | (imm10_1 << 21); + write_u32_le(memory + reloc_addr, inst); + break; + } + case R_RISCV_32: { + write_u32_le(memory + reloc_addr, (uint32_t)value); + break; + } + default: + return LOAD_ERR_RELOC_UNSUPPORTED; + } + } + } + + return LOAD_OK; +} + int load_elf_object_sections(const char* path, uint8_t* memory, uint32_t mem_size) { uint8_t* file_buf = NULL; size_t bytes = 0u; @@ -117,6 +310,19 @@ int load_elf_object_sections(const char* path, uint8_t* memory, uint32_t mem_siz return LOAD_ERR_SHT_OUT_OF_BOUNDS; } + uint32_t* section_runtime = (uint32_t*)calloc(e_shnum, sizeof(uint32_t)); + uint32_t* section_addr = (uint32_t*)calloc(e_shnum, sizeof(uint32_t)); + uint32_t* section_size = (uint32_t*)calloc(e_shnum, sizeof(uint32_t)); + uint8_t* section_loaded = (uint8_t*)calloc(e_shnum, sizeof(uint8_t)); + if (!section_runtime || !section_addr || !section_size || !section_loaded) { + free(section_runtime); + free(section_addr); + free(section_size); + free(section_loaded); + free(file_buf); + return LOAD_ERR_OOM; + } + uint32_t cursor = 0u; for (uint16_t i = 0; i < e_shnum; ++i) { @@ -129,6 +335,9 @@ int load_elf_object_sections(const char* path, uint8_t* memory, uint32_t mem_siz uint32_t sh_size = read_u32_le(sh + 20); uint32_t sh_addralign = read_u32_le(sh + 32); + section_addr[i] = sh_addr; + section_size[i] = sh_size; + if ((sh_flags & SHF_ALLOC) == 0u || sh_size == 0u) { continue; } @@ -137,6 +346,10 @@ int load_elf_object_sections(const char* path, uint8_t* memory, uint32_t mem_siz if (dst >= mem_size || sh_size > mem_size - dst) { fprintf(stderr, "Section %u does not fit into emulator memory\n", (unsigned)i); + free(section_runtime); + free(section_addr); + free(section_size); + free(section_loaded); free(file_buf); return LOAD_ERR_SECTION_TOO_LARGE; } @@ -146,19 +359,34 @@ int load_elf_object_sections(const char* path, uint8_t* memory, uint32_t mem_siz } else { if (sh_offset >= bytes || sh_size > (uint32_t)bytes - sh_offset) { fprintf(stderr, "Section %u exceeds object file bounds\n", (unsigned)i); + free(section_runtime); + free(section_addr); + free(section_size); + free(section_loaded); free(file_buf); return LOAD_ERR_SECTION_FILE_BOUNDS; } memcpy(memory + dst, file_buf + sh_offset, sh_size); } + section_runtime[i] = dst; + section_loaded[i] = 1u; + if (sh_addr == 0u) { cursor = dst + sh_size; } } + int reloc_status = apply_relocations(file_buf, bytes, memory, mem_size, + e_shoff, e_shentsize, e_shnum, section_runtime, section_addr, + section_size, section_loaded); + + free(section_runtime); + free(section_addr); + free(section_size); + free(section_loaded); free(file_buf); - return LOAD_OK; + return reloc_status; } int resolve_elf_symbol_memory_offset(const char* path, const char* symbol_name, diff --git a/src/elf.h b/src/elf.h index b5e4336..3f48b08 100644 --- a/src/elf.h +++ b/src/elf.h @@ -8,10 +8,21 @@ #define ELFCLASS32 1 #define ELFDATA2LSB 1 -#define SHT_NOBITS 8u #define SHT_SYMTAB 2u +#define SHT_RELA 4u +#define SHT_NOBITS 8u +#define SHT_REL 9u #define SHF_ALLOC 0x2u +#define R_RISCV_32 1u +#define R_RISCV_NONE 0u +#define R_RISCV_BRANCH 16u +#define R_RISCV_JAL 17u +#define R_RISCV_HI20 26u +#define R_RISCV_LO12_I 27u +#define R_RISCV_LO12_S 28u +#define R_RISCV_RELAX 51u + #include enum { @@ -29,7 +40,9 @@ enum { LOAD_ERR_STRTAB_INVALID = -11, LOAD_ERR_SYMBOL_NOT_FOUND = -12, LOAD_ERR_SYMBOL_INVALID_SECTION = -13, - LOAD_ERR_SYMBOL_OUT_OF_RANGE = -14 + LOAD_ERR_SYMBOL_OUT_OF_RANGE = -14, + LOAD_ERR_RELOC_INVALID = -15, + LOAD_ERR_RELOC_UNSUPPORTED = -16 }; uint16_t read_u16_le(const uint8_t* data); diff --git a/src/emulated/function.c b/src/emulated/function.c deleted file mode 100644 index f7ab9a6..0000000 --- a/src/emulated/function.c +++ /dev/null @@ -1,13 +0,0 @@ - -#include "function.h" - -#include "custom_instructions.h" -#include - - - -int32_t operation(int32_t a) { - return trunc_xor(a, 3); -} - - diff --git a/src/emulated/function.h b/src/emulated/function.h deleted file mode 100644 index b167114..0000000 --- a/src/emulated/function.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -#include - -int32_t operation(int32_t a); - diff --git a/src/emulated/program.c b/src/emulated/program.c index decd3eb..4184d2f 100644 --- a/src/emulated/program.c +++ b/src/emulated/program.c @@ -1,10 +1,14 @@ #include -#include "function.h" +#include "../config.h" #include "custom_instructions.h" +#include "generated.h" -uint8_t entry(uint8_t a) { - return operation(a); +uint32_t entry(void* userdata) { + uint8_t len = (uint8_t)*((uint8_t*)userdata); + char* userflag = (char*)(userdata + 1); + + return check(userflag, len); } diff --git a/src/main.c b/src/main.c index 6a8b397..caeae27 100644 --- a/src/main.c +++ b/src/main.c @@ -15,14 +15,28 @@ int main(){ + const int maxninput = 255; + printf("Please enter the flag: "); + fflush(stdout); + char userinput[maxninput]; + fgets(userinput, sizeof(userinput), stdin); + // Replace newline with null terminator + userinput[strcspn(userinput, "\r\n")] = 0; + uint8_t memory[EMU_MEM_SIZE]; //4kb of internal memory tinyriscv_core cpu; //create the emulation core memset(&cpu, 0, sizeof(cpu)); cpu.mem = memory; //use the uint8_t array as the memory cpu.mem_size = EMU_MEM_SIZE; memset(memory, 0, sizeof(memory)); + uint8_t real_len = strnlen(userinput, maxninput); - int load_status = load_elf_object_sections(EMU_PROGRAM_PATH, memory, cpu.mem_size); + // Load user data, first, the string length + memory[SECTION_USERDATA] = real_len; + memcpy(memory + SECTION_USERDATA + 1, userinput, real_len); + + // Load elf, keep 512 bytes as safe buffer + int load_status = load_elf_object_sections(EMU_PROGRAM_PATH, memory, cpu.mem_size - SIZE_USERDATA); if (load_status != LOAD_OK) { fprintf(stderr, "ELF load failed with code %d for '%s'\n", load_status, EMU_PROGRAM_PATH); fflush(stderr); @@ -43,9 +57,7 @@ int main(){ tinyriscv_init(&cpu); //initialise core for execution cpu.pc = tinyriscv_MEM_OFFSET + entry_offset; - // Call operation(a, b) using RV32 calling convention: - // a0=x10, return value in a0, return address in ra=x1. - cpu.x[10] = TEST_A; + cpu.x[10] = tinyriscv_MEM_OFFSET + SECTION_USERDATA; cpu.x[1] = tinyriscv_MEM_OFFSET + cpu.mem_size; uint32_t steps = 0u; @@ -59,15 +71,12 @@ int main(){ return 2; } - uint32_t actual = (uint32_t)cpu.x[10]; - uint32_t expected = ((TEST_A << 3) ^ 0xFF) >> 3; - printf("operation(%u) -> %u (expected %u)\n", - (unsigned)TEST_A, - (unsigned)actual, (unsigned)expected); + uint32_t result = (uint32_t)cpu.x[10]; - if (actual != expected) { - fprintf(stderr, "Verification failed\n"); - return 3; + if(result == 1) { + printf("Congrats! That's the flag!"); + } else { + printf("Nope, sorry, that's incorrect!"); } return 0;