use mmap for memory, implement gettimeofday, fix setup argc/argv/envp on the stack

This commit is contained in:
2026-03-08 22:46:58 +01:00
parent bc29d79586
commit 39df51900d

View File

@@ -13,6 +13,8 @@
#include <iostream> #include <iostream>
#include <libelf.h> #include <libelf.h>
#include <print> #include <print>
#include <sys/mman.h>
#include <sys/time.h>
#include <vector> #include <vector>
using i8 = int8_t; using i8 = int8_t;
@@ -29,7 +31,8 @@ static_assert(sizeof(size_t) == sizeof(u64), "u64 must be 64-bit");
static_assert(std::endian::native == std::endian::little, static_assert(std::endian::native == std::endian::little,
"Big endianness not supported"); "Big endianness not supported");
static constexpr u64 MEMORY_SIZE = 20 * 1024 * 1024; // should be enough static constexpr u64 MEMORY_SIZE =
2ULL * 1024 * 1024 * 1024; // should be enough
static constexpr std::array<const char *, 32> REGS = { static constexpr std::array<const char *, 32> REGS = {
"zero", "ra", "sp", "gp", "tp", "t0", "t1", "t2", "fp", "s1", "a0", "zero", "ra", "sp", "gp", "tp", "t0", "t1", "t2", "fp", "s1", "a0",
@@ -165,11 +168,23 @@ static constexpr std::array<OpDef, 63> OP_TABLE = {{
class RISCV64 { class RISCV64 {
public: public:
RISCV64(const std::vector<char> &exe_bytes) { RISCV64(const std::vector<char> &exe_bytes) {
m_memory = (u8 *)mmap(nullptr, MEMORY_SIZE, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (m_memory == MAP_FAILED) {
std::println(stderr, "Failed to mmap memory");
exit(1);
}
Elf *elf = Elf *elf =
elf_memory(const_cast<char *>(exe_bytes.data()), exe_bytes.size()); elf_memory(const_cast<char *>(exe_bytes.data()), exe_bytes.size());
GElf_Ehdr ehdr; GElf_Ehdr ehdr;
gelf_getehdr(elf, &ehdr); gelf_getehdr(elf, &ehdr);
if (ehdr.e_machine != EM_RISCV) {
std::println(stderr, "ehdr.e_machine != EM_RISCV");
exit(1);
}
m_code_section = get_code_section(elf, ehdr); m_code_section = get_code_section(elf, ehdr);
m_pc = m_code_section.offset; m_pc = m_code_section.offset;
@@ -178,7 +193,7 @@ public:
gelf_getphdr(elf, i, &phdr); gelf_getphdr(elf, i, &phdr);
if (phdr.p_type == PT_LOAD) { if (phdr.p_type == PT_LOAD) {
std::copy_n(exe_bytes.data() + phdr.p_offset, phdr.p_filesz, std::copy_n(exe_bytes.data() + phdr.p_offset, phdr.p_filesz,
m_memory.data() + phdr.p_vaddr); m_memory + phdr.p_vaddr);
} }
} }
elf_end(elf); elf_end(elf);
@@ -187,12 +202,13 @@ public:
m_decoded.resize(num_ins); m_decoded.resize(num_ins);
for (u64 i = 0; i < num_ins; i++) { for (u64 i = 0; i < num_ins; i++) {
u32 raw; u32 raw;
std::memcpy(&raw, m_memory.data() + m_code_section.offset + i * 4, std::memcpy(&raw, m_memory + m_code_section.offset + i * 4, sizeof(raw));
sizeof(raw));
m_decoded[i] = decode_raw(raw); m_decoded[i] = decode_raw(raw);
} }
} }
~RISCV64() { munmap(m_memory, MEMORY_SIZE); }
void disassemble_all() { void disassemble_all() {
for (m_pc = m_code_section.offset; for (m_pc = m_code_section.offset;
m_pc < m_code_section.offset + m_code_section.size; m_pc += 4) { m_pc < m_code_section.offset + m_code_section.size; m_pc += 4) {
@@ -250,10 +266,36 @@ public:
std::println(); std::println();
} }
void push_u64(u64 x) {
m_regs[2] -= 8;
*(u64 *)(m_memory + m_regs[2]) = x;
}
void execute() { void execute() {
m_pc = m_code_section.entrypoint; m_pc = m_code_section.entrypoint;
m_regs[2] = MEMORY_SIZE - 1024; // set the stack pointer // set up the stack
i64 &sp = m_regs[2];
sp = MEMORY_SIZE - 1024;
// push "program"
const char *prog = "program";
u64 len = strlen(prog) + 1;
sp -= len;
std::memcpy(m_memory + sp, prog, len);
u64 prog_ptr = sp;
sp &= ~15;
// auxv = {0}
push_u64(0);
push_u64(0);
// envp = {0}
push_u64(0);
// argv = {prog_ptr, 0}
push_u64(0);
push_u64(prog_ptr);
// argc = 1
push_u64(1);
while (m_pc < MEMORY_SIZE) { while (m_pc < MEMORY_SIZE) {
m_regs[0] = 0; // clear the zero register m_regs[0] = 0; // clear the zero register
@@ -339,10 +381,14 @@ public:
} }
}; break; }; break;
case Op::DIVW: { case Op::DIVW: {
if (m_regs[i.rs2] == 0) { i32 a = m_regs[i.rs1];
i32 b = m_regs[i.rs2];
if (b == 0) {
m_regs[i.rd] = (u64)(i64)(-1); m_regs[i.rd] = (u64)(i64)(-1);
} else if (a == INT32_MIN && b == -1) {
m_regs[i.rd] = (i64)INT32_MIN;
} else { } else {
m_regs[i.rd] = (i64)(m_regs[i.rs1] / m_regs[i.rs2]); m_regs[i.rd] = (i64)(a / b);
} }
}; break; }; break;
case Op::ECALL: { case Op::ECALL: {
@@ -388,21 +434,22 @@ public:
return; return;
}; break; }; break;
case 169: { // gettimeofday case 169: { // gettimeofday
// TODO: actually return the time
i64 tv_addr = m_regs[10]; i64 tv_addr = m_regs[10];
i64 tz_addr = m_regs[11]; i64 tz_addr = m_regs[11];
u8 tv_buf[16] = { struct timeval tv;
0xD2, 0x02, 0x96, 0x49, struct timezone tz;
0x00, 0x00, 0x00, 0x00, // tv_sec = 1234567890
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, // tv_usec = 0
};
memcpy(&m_memory[tv_addr], tv_buf, 16);
memset(&m_memory[tz_addr], 0, 8); i32 ret = gettimeofday(&tv, (tz_addr != 0) ? &tz : nullptr);
if (ret == 0) {
m_regs[10] = 0; memcpy(&m_memory[tv_addr], &tv, sizeof(tv));
if (tz_addr != 0) {
memcpy(&m_memory[tz_addr], &tz, sizeof(tz));
}
m_regs[10] = 0;
} else {
m_regs[10] = -errno;
}
}; break; }; break;
default: default:
std::println(stderr, "Unimplemented syscall: {}", m_regs[17]); std::println(stderr, "Unimplemented syscall: {}", m_regs[17]);
@@ -600,7 +647,7 @@ public:
} }
private: private:
std::vector<u8> m_memory = std::vector<u8>(MEMORY_SIZE, 0); u8 *m_memory;
std::vector<Ins> m_decoded; std::vector<Ins> m_decoded;
u64 m_pc; u64 m_pc;
std::array<i64, 32> m_regs{}; std::array<i64, 32> m_regs{};
@@ -753,7 +800,7 @@ private:
i.imm = ((i32)raw) >> 20; i.imm = ((i32)raw) >> 20;
if (funct3 == 0b000) { if (funct3 == 0b000) {
if (i.imm == 0) { if (i.imm == 0b000000000000) {
i.op = Op::ECALL; i.op = Op::ECALL;
} else { } else {
std::println(stderr, "I-type 4: funct3=000 unrecognized imm: {:b}", std::println(stderr, "I-type 4: funct3=000 unrecognized imm: {:b}",
@@ -863,9 +910,9 @@ private:
exit(1); exit(1);
} }
} else if (funct3 == 0b111) { } else if (funct3 == 0b111) {
if (funct7 == 0b000) { if (funct7 == 0b0000000) {
i.op = Op::AND; i.op = Op::AND;
} else if (funct7 == 0b001) { } else if (funct7 == 0b0000001) {
i.op = Op::REMU; i.op = Op::REMU;
} else { } else {
std::println(stderr, std::println(stderr,