feat: basic flag challenge working + fix elf loading
This commit is contained in:
@@ -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)
|
||||
|
||||
230
src/elf.c
230
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,
|
||||
|
||||
17
src/elf.h
17
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 <stdint.h>
|
||||
|
||||
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);
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
|
||||
#include "function.h"
|
||||
|
||||
#include "custom_instructions.h"
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
|
||||
int32_t operation(int32_t a) {
|
||||
return trunc_xor(a, 3);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
int32_t operation(int32_t a);
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
|
||||
|
||||
33
src/main.c
33
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;
|
||||
|
||||
Reference in New Issue
Block a user