diff --git a/example/boot.s b/example/boot.s index edaff0d..35e973f 100644 --- a/example/boot.s +++ b/example/boot.s @@ -1,6 +1,6 @@ .globl _boot _boot: - li x2, 0x80000 + li x2, 0x80080000 call main sbreak j . diff --git a/example/linker.ld b/example/linker.ld index 3cf558e..8f7e17e 100644 --- a/example/linker.ld +++ b/example/linker.ld @@ -1,25 +1,25 @@ ENTRY(_start) SECTIONS { - . = 0x0000; /* Start address of the program */ + . = 0x80000000; /* Start address of the program */ .text : { *(.text) /* Place all .text sections (code) here */ } - . = 0x10000; + . = ALIGN(0x1000); .data : { *(.data) /* Place all .data sections (initialized data) here */ } - . = 0x20000; + . = ALIGN(0x1000); .bss : { *(.bss) /* Place all .bss sections (uninitialized data) here */ } - . = 0x30000; + . = ALIGN(0x1000); .stack : { *(.stack) diff --git a/src/elf.cpp b/src/elf.cpp index 3062f41..96823fa 100644 --- a/src/elf.cpp +++ b/src/elf.cpp @@ -15,6 +15,18 @@ constexpr uint16_t ELF_MACHINE_RISCV = 243; constexpr uint8_t ELF_CLASS_32 = 1; constexpr uint8_t ELF_LITTLE_ENDIAN = 1; +constexpr uint8_t SHT_PROGBITS = 1; +constexpr uint8_t SHT_SYMTAB = 2; +constexpr uint8_t SHT_STRTAB = 3; +constexpr uint8_t SHT_RELA = 4; +constexpr uint8_t SHT_DYNAMIC = 6; +constexpr uint8_t SHT_NOTE = 7; +constexpr uint8_t SHT_NOBITS = 8; +constexpr uint8_t SHT_INIT_ARRAY = 14; +constexpr uint8_t SHT_FINI_ARRAY = 15; + +constexpr uint8_t SHF_ALLOC = 2; + struct Elf32Header { uint8_t e_ident[16]; // Magic number and other info uint16_t e_type; // Object file type @@ -45,6 +57,31 @@ struct Elf32Section { uint32_t sh_entsize; // Entry size if section holds table }; +bool should_load_section(const Elf32Section& section_header) { + // Check if the section has the SHF_ALLOC flag, which indicates + // the section should be loaded into memory + if (section_header.sh_flags & SHF_ALLOC) { + // Further filter out sections that shouldn't be loaded + switch (section_header.sh_type) { + case SHT_PROGBITS: // Typically contains code or data + case SHT_INIT_ARRAY: // Initialization function pointers + case SHT_FINI_ARRAY: // Termination function pointers + case SHT_NOBITS: // .bss section (uninitialized data) + return true; + + // Exclude debug and other non-loadable sections + case SHT_SYMTAB: // Symbol table + case SHT_STRTAB: // String table + case SHT_RELA: // Relocation entries + case SHT_DYNAMIC: // Dynamic linking information + case SHT_NOTE: // Note section + default: + return false; + } + } + return false; +} + std::vector load_elf(const std::string& filename, size_t memory_size) { std::ifstream file(filename, std::ios::binary); if (!file.is_open()) { @@ -91,12 +128,13 @@ std::vector load_elf(const std::string& filename, size_t memory_size) { } // Determine memory range for allocation - uint32_t memoryEnd = 0; + uint32_t memoryStart = 0x80000000; + uint32_t memoryEnd = 0x80000000; for (const Elf32Section& shdr : sectionHeaders) { - const char* sectionName = §ionStrTable[shdr.sh_name]; - if (std::strcmp(sectionName, ".text") == 0 || - std::strcmp(sectionName, ".sdata") == 0 || - std::strcmp(sectionName, ".rodata") == 0) { + if (should_load_section(shdr)) { + if (shdr.sh_addr < memoryStart) { + throw std::runtime_error("Section address out of range"); + } memoryEnd = std::max(memoryEnd, shdr.sh_addr + shdr.sh_size); } } @@ -105,23 +143,21 @@ std::vector load_elf(const std::string& filename, size_t memory_size) { throw std::runtime_error("No loadable sections found"); } - std::vector loadedData(memoryEnd, 0); + std::vector loadedData(memoryEnd-memoryStart, 0); // Load .text and .sdata sections for (const Elf32Section& shdr : sectionHeaders) { - const char* sectionName = §ionStrTable[shdr.sh_name]; - if (std::strcmp(sectionName, ".text") == 0 || - std::strcmp(sectionName, ".sdata") == 0 || - std::strcmp(sectionName, ".rodata") == 0) { - std::vector sectionData(shdr.sh_size); + if (should_load_section(shdr)) { + if (shdr.sh_type == SHT_NOBITS) { + throw std::runtime_error("Trying to load section without data"); + } + file.seekg(shdr.sh_offset); - file.read(reinterpret_cast(&loadedData[shdr.sh_addr]), + file.read(reinterpret_cast(&loadedData[shdr.sh_addr-memoryStart]), shdr.sh_size); if (!file) { throw std::runtime_error("Failed to read section data"); } - loadedData.insert(loadedData.end(), sectionData.begin(), - sectionData.end()); } } diff --git a/src/vm.cpp b/src/vm.cpp index 313b7bc..238be6c 100644 --- a/src/vm.cpp +++ b/src/vm.cpp @@ -35,7 +35,7 @@ bool UART::is_transmitter_ready() { } VM::VM(const std::vector& memory, const std::string& file_path) - : memory_(memory), file_path(file_path) {} + : memory_(memory), pc(PROGRAM_ADDR), file_path(file_path) {} void VM::setreg(int regnum, uint32_t value) { if (regnum == 0) { @@ -46,62 +46,94 @@ void VM::setreg(int regnum, uint32_t value) { } std::vector VM::read_memory(size_t start, size_t size) { - if (start + size > memory_.size()) { + if (start >= PROGRAM_ADDR) { + if (start + size > memory_.size()) { + return std::vector(size, 0); + } + return std::vector(memory_.begin() + start, + memory_.begin() + start + size); + } else { return std::vector(size, 0); } - return std::vector(memory_.begin() + start, - memory_.begin() + start + size); } uint32_t VM::read_memory_word(size_t pos) { - if (pos + 3 >= memory_.size()) { + if (pos >= PROGRAM_ADDR) { + pos -= PROGRAM_ADDR; + if (pos + 3 >= memory_.size()) { + throw std::runtime_error("Memory access out of bounds"); + } + return *(uint32_t*)&memory_[pos]; + } else { throw std::runtime_error("Memory access out of bounds"); } - return *(uint32_t*)&memory_[pos]; } uint16_t VM::read_memory_half_word(size_t pos) { - if (pos + 1 >= memory_.size()) { + if (pos >= PROGRAM_ADDR) { + pos -= PROGRAM_ADDR; + if (pos + 1 >= memory_.size()) { + throw std::runtime_error("Memory access out of bounds"); + } + + return *(uint16_t*)&memory_[pos]; + } else { throw std::runtime_error("Memory access out of bounds"); } - - return *(uint16_t*)&memory_[pos]; } uint8_t VM::read_memory_byte(size_t pos) { - if (pos >= memory_.size()) { + if (pos >= PROGRAM_ADDR) { + pos -= PROGRAM_ADDR; + if (pos >= memory_.size()) { + throw std::runtime_error("Memory access out of bounds"); + } + + return memory_[pos]; + } else { throw std::runtime_error("Memory access out of bounds"); } - - return memory_[pos]; } void VM::write_memory_word(size_t pos, uint32_t value) { - if (pos + 1 >= memory_.size()) { + if (pos >= PROGRAM_ADDR) { + pos -= PROGRAM_ADDR; + if (pos + 1 >= memory_.size()) { + throw std::runtime_error("Memory access out of bounds"); + } + *(uint32_t*)&memory_[pos] = value; + } else { throw std::runtime_error("Memory access out of bounds"); } - *(uint32_t*)&memory_[pos] = value; } void VM::write_memory_half_word(size_t pos, uint16_t value) { - if (pos + 3 >= memory_.size()) { + if (pos >= PROGRAM_ADDR) { + pos -= PROGRAM_ADDR; + if (pos + 3 >= memory_.size()) { + throw std::runtime_error("Memory access out of bounds"); + } + *(uint16_t*)&memory_[pos] = value; + } else { throw std::runtime_error("Memory access out of bounds"); } - *(uint16_t*)&memory_[pos] = value; } void VM::write_memory_byte(size_t pos, uint8_t value) { - if (is_mmap(pos, 1)) { - if (pos >= UART_ADDR && pos < UART_ADDR + 8) { - uart.write_register(pos - UART_ADDR, value); + if (pos >= PROGRAM_ADDR) { + pos -= PROGRAM_ADDR; + if (pos >= memory_.size()) { + throw std::runtime_error("Memory access out of bounds"); + } + memory_[pos] = value; + } else { + if (is_mmap(pos, 1)) { + if (pos >= UART_ADDR && pos < UART_ADDR + 8) { + uart.write_register(pos - UART_ADDR, value); + } + return; } - return; } - - if (pos >= memory_.size()) { - throw std::runtime_error("Memory access out of bounds"); - } - memory_[pos] = value; } bool VM::is_mmap(size_t pos, size_t size) { @@ -124,7 +156,8 @@ uint32_t VM::read_register(size_t regnum) { const std::string& VM::get_file_path() { return file_path; } void VM::step() { - uint32_t instr = *(uint32_t*)&memory_[pc]; + if (pc < PROGRAM_ADDR) throw std::runtime_error("PC out of range"); + uint32_t instr = *(uint32_t*)&memory_[pc - PROGRAM_ADDR]; // std::cout << "pc: " << std::hex << pc << std::dec << "\n"; // std::cout << "instr: " << std::hex << instr << "\n"; pc += 4; @@ -338,16 +371,10 @@ void VM::step() { write_memory_byte(addr, registers[rs2]); } else if (funct3 == 0x1) { // SH uint32_t addr = registers[rs1] + imm; - if (addr + 2 > memory_.size()) { - throw std::runtime_error("Memory access out of bounds"); - } - std::memcpy(&memory_[addr], ®isters[rs2], sizeof(uint16_t)); + write_memory_half_word(addr, registers[rs2]); } else if (funct3 == 0x2) { // SW uint32_t addr = registers[rs1] + imm; - if (addr + 4 > memory_.size()) { - throw std::runtime_error("Memory access out of bounds"); - } - std::memcpy(&memory_[addr], ®isters[rs2], sizeof(uint32_t)); + write_memory_word(addr, registers[rs2]); } else { throw std::runtime_error("Unknown store instruction"); } diff --git a/src/vm.hpp b/src/vm.hpp index 75c62cd..afc81b7 100644 --- a/src/vm.hpp +++ b/src/vm.hpp @@ -8,7 +8,8 @@ class EbreakException : std::exception {}; const int NUM_REGISTERS = 32; // Standard RISC-V has 32 registers -const int UART_ADDR = 0x10000000; +const uint32_t UART_ADDR = 0x10000000; +const uint32_t PROGRAM_ADDR = 0x80000000; class UART { public: