Relocate program to 0x80000000

This commit is contained in:
Konstantin Nazarov 2024-12-15 16:44:39 +00:00
parent d84c5022bd
commit 89d9ca3814
Signed by: knazarov
GPG key ID: 4CFE0A42FA409C22
5 changed files with 118 additions and 54 deletions

View file

@ -1,6 +1,6 @@
.globl _boot .globl _boot
_boot: _boot:
li x2, 0x80000 li x2, 0x80080000
call main call main
sbreak sbreak
j . j .

View file

@ -1,25 +1,25 @@
ENTRY(_start) ENTRY(_start)
SECTIONS { SECTIONS {
. = 0x0000; /* Start address of the program */ . = 0x80000000; /* Start address of the program */
.text : { .text : {
*(.text) /* Place all .text sections (code) here */ *(.text) /* Place all .text sections (code) here */
} }
. = 0x10000; . = ALIGN(0x1000);
.data : { .data : {
*(.data) /* Place all .data sections (initialized data) here */ *(.data) /* Place all .data sections (initialized data) here */
} }
. = 0x20000; . = ALIGN(0x1000);
.bss : { .bss : {
*(.bss) /* Place all .bss sections (uninitialized data) here */ *(.bss) /* Place all .bss sections (uninitialized data) here */
} }
. = 0x30000; . = ALIGN(0x1000);
.stack : { .stack : {
*(.stack) *(.stack)

View file

@ -15,6 +15,18 @@ constexpr uint16_t ELF_MACHINE_RISCV = 243;
constexpr uint8_t ELF_CLASS_32 = 1; constexpr uint8_t ELF_CLASS_32 = 1;
constexpr uint8_t ELF_LITTLE_ENDIAN = 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 { struct Elf32Header {
uint8_t e_ident[16]; // Magic number and other info uint8_t e_ident[16]; // Magic number and other info
uint16_t e_type; // Object file type uint16_t e_type; // Object file type
@ -45,6 +57,31 @@ struct Elf32Section {
uint32_t sh_entsize; // Entry size if section holds table 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::vector<uint8_t> load_elf(const std::string& filename, size_t memory_size) {
std::ifstream file(filename, std::ios::binary); std::ifstream file(filename, std::ios::binary);
if (!file.is_open()) { 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 // Determine memory range for allocation
uint32_t memoryEnd = 0; uint32_t memoryStart = 0x80000000;
uint32_t memoryEnd = 0x80000000;
for (const Elf32Section& shdr : sectionHeaders) { for (const Elf32Section& shdr : sectionHeaders) {
const char* sectionName = &sectionStrTable[shdr.sh_name]; if (should_load_section(shdr)) {
if (std::strcmp(sectionName, ".text") == 0 || if (shdr.sh_addr < memoryStart) {
std::strcmp(sectionName, ".sdata") == 0 || throw std::runtime_error("Section address out of range");
std::strcmp(sectionName, ".rodata") == 0) { }
memoryEnd = std::max(memoryEnd, shdr.sh_addr + shdr.sh_size); 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"); 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 // Load .text and .sdata sections
for (const Elf32Section& shdr : sectionHeaders) { for (const Elf32Section& shdr : sectionHeaders) {
const char* sectionName = &sectionStrTable[shdr.sh_name]; if (should_load_section(shdr)) {
if (std::strcmp(sectionName, ".text") == 0 || if (shdr.sh_type == SHT_NOBITS) {
std::strcmp(sectionName, ".sdata") == 0 || throw std::runtime_error("Trying to load section without data");
std::strcmp(sectionName, ".rodata") == 0) { }
std::vector<uint8_t> sectionData(shdr.sh_size);
file.seekg(shdr.sh_offset); 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); shdr.sh_size);
if (!file) { if (!file) {
throw std::runtime_error("Failed to read section data"); throw std::runtime_error("Failed to read section data");
} }
loadedData.insert(loadedData.end(), sectionData.begin(),
sectionData.end());
} }
} }

View file

@ -35,7 +35,7 @@ bool UART::is_transmitter_ready() {
} }
VM::VM(const std::vector<uint8_t>& memory, const std::string& file_path) 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) { void VM::setreg(int regnum, uint32_t value) {
if (regnum == 0) { 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) { std::vector<uint8_t> VM::read_memory(size_t start, size_t size) {
if (start >= PROGRAM_ADDR) {
if (start + size > memory_.size()) { if (start + size > memory_.size()) {
return std::vector<uint8_t>(size, 0); return std::vector<uint8_t>(size, 0);
} }
return std::vector<uint8_t>(memory_.begin() + start, return std::vector<uint8_t>(memory_.begin() + start,
memory_.begin() + start + size); memory_.begin() + start + size);
} else {
return std::vector<uint8_t>(size, 0);
}
} }
uint32_t VM::read_memory_word(size_t pos) { uint32_t VM::read_memory_word(size_t pos) {
if (pos >= PROGRAM_ADDR) {
pos -= PROGRAM_ADDR;
if (pos + 3 >= memory_.size()) { if (pos + 3 >= memory_.size()) {
throw std::runtime_error("Memory access out of bounds"); throw std::runtime_error("Memory access out of bounds");
} }
return *(uint32_t*)&memory_[pos]; return *(uint32_t*)&memory_[pos];
} else {
throw std::runtime_error("Memory access out of bounds");
}
} }
uint16_t VM::read_memory_half_word(size_t pos) { uint16_t VM::read_memory_half_word(size_t pos) {
if (pos >= PROGRAM_ADDR) {
pos -= PROGRAM_ADDR;
if (pos + 1 >= memory_.size()) { if (pos + 1 >= memory_.size()) {
throw std::runtime_error("Memory access out of bounds"); throw std::runtime_error("Memory access out of bounds");
} }
return *(uint16_t*)&memory_[pos]; return *(uint16_t*)&memory_[pos];
} else {
throw std::runtime_error("Memory access out of bounds");
}
} }
uint8_t VM::read_memory_byte(size_t pos) { uint8_t VM::read_memory_byte(size_t pos) {
if (pos >= PROGRAM_ADDR) {
pos -= PROGRAM_ADDR;
if (pos >= memory_.size()) { if (pos >= memory_.size()) {
throw std::runtime_error("Memory access out of bounds"); throw std::runtime_error("Memory access out of bounds");
} }
return memory_[pos]; return memory_[pos];
} else {
throw std::runtime_error("Memory access out of bounds");
}
} }
void VM::write_memory_word(size_t pos, uint32_t value) { void VM::write_memory_word(size_t pos, uint32_t value) {
if (pos >= PROGRAM_ADDR) {
pos -= PROGRAM_ADDR;
if (pos + 1 >= memory_.size()) { if (pos + 1 >= memory_.size()) {
throw std::runtime_error("Memory access out of bounds"); throw std::runtime_error("Memory access out of bounds");
} }
*(uint32_t*)&memory_[pos] = value; *(uint32_t*)&memory_[pos] = value;
} else {
throw std::runtime_error("Memory access out of bounds");
}
} }
void VM::write_memory_half_word(size_t pos, uint16_t value) { void VM::write_memory_half_word(size_t pos, uint16_t value) {
if (pos >= PROGRAM_ADDR) {
pos -= PROGRAM_ADDR;
if (pos + 3 >= memory_.size()) { if (pos + 3 >= memory_.size()) {
throw std::runtime_error("Memory access out of bounds"); throw std::runtime_error("Memory access out of bounds");
} }
*(uint16_t*)&memory_[pos] = value; *(uint16_t*)&memory_[pos] = value;
} else {
throw std::runtime_error("Memory access out of bounds");
}
} }
void VM::write_memory_byte(size_t pos, uint8_t value) { void VM::write_memory_byte(size_t pos, uint8_t 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 (is_mmap(pos, 1)) {
if (pos >= UART_ADDR && pos < UART_ADDR + 8) { if (pos >= UART_ADDR && pos < UART_ADDR + 8) {
uart.write_register(pos - UART_ADDR, value); 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) { 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; } const std::string& VM::get_file_path() { return file_path; }
void VM::step() { 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 << "pc: " << std::hex << pc << std::dec << "\n";
// std::cout << "instr: " << std::hex << instr << "\n"; // std::cout << "instr: " << std::hex << instr << "\n";
pc += 4; pc += 4;
@ -338,16 +371,10 @@ void VM::step() {
write_memory_byte(addr, registers[rs2]); write_memory_byte(addr, registers[rs2]);
} else if (funct3 == 0x1) { // SH } else if (funct3 == 0x1) { // SH
uint32_t addr = registers[rs1] + imm; uint32_t addr = registers[rs1] + imm;
if (addr + 2 > memory_.size()) { write_memory_half_word(addr, registers[rs2]);
throw std::runtime_error("Memory access out of bounds");
}
std::memcpy(&memory_[addr], &registers[rs2], sizeof(uint16_t));
} else if (funct3 == 0x2) { // SW } else if (funct3 == 0x2) { // SW
uint32_t addr = registers[rs1] + imm; uint32_t addr = registers[rs1] + imm;
if (addr + 4 > memory_.size()) { write_memory_word(addr, registers[rs2]);
throw std::runtime_error("Memory access out of bounds");
}
std::memcpy(&memory_[addr], &registers[rs2], sizeof(uint32_t));
} else { } else {
throw std::runtime_error("Unknown store instruction"); throw std::runtime_error("Unknown store instruction");
} }

View file

@ -8,7 +8,8 @@
class EbreakException : std::exception {}; class EbreakException : std::exception {};
const int NUM_REGISTERS = 32; // Standard RISC-V has 32 registers 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 { class UART {
public: public: