Relocate program to 0x80000000
This commit is contained in:
parent
d84c5022bd
commit
89d9ca3814
5 changed files with 118 additions and 54 deletions
|
@ -1,6 +1,6 @@
|
|||
.globl _boot
|
||||
_boot:
|
||||
li x2, 0x80000
|
||||
li x2, 0x80080000
|
||||
call main
|
||||
sbreak
|
||||
j .
|
||||
|
|
|
@ -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)
|
||||
|
|
64
src/elf.cpp
64
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<uint8_t> 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<uint8_t> 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<uint8_t> load_elf(const std::string& filename, size_t memory_size) {
|
|||
throw std::runtime_error("No loadable sections found");
|
||||
}
|
||||
|
||||
std::vector<uint8_t> loadedData(memoryEnd, 0);
|
||||
std::vector<uint8_t> 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<uint8_t> 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<char*>(&loadedData[shdr.sh_addr]),
|
||||
file.read(reinterpret_cast<char*>(&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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
95
src/vm.cpp
95
src/vm.cpp
|
@ -35,7 +35,7 @@ bool UART::is_transmitter_ready() {
|
|||
}
|
||||
|
||||
VM::VM(const std::vector<uint8_t>& 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<uint8_t> 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<uint8_t>(size, 0);
|
||||
}
|
||||
return std::vector<uint8_t>(memory_.begin() + start,
|
||||
memory_.begin() + start + size);
|
||||
} else {
|
||||
return std::vector<uint8_t>(size, 0);
|
||||
}
|
||||
return std::vector<uint8_t>(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");
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Reference in a new issue