criscv/src/elf_program_loader.c
2024-10-19 11:50:02 +02:00

137 lines
No EOL
4 KiB
C

#include <gelf.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include "cpu.h"
static inline int process_elf(Elf *elf, CPU *cpu) {
AddressSpace *addressSpace = cpu->addressSpace;
if (elf_kind(elf) != ELF_K_ELF) {
fprintf(stderr, "File is not a valid ELF file\n");
return 1;
}
GElf_Ehdr ehdr;
if (gelf_getehdr(elf, &ehdr) == NULL) {
fprintf(stderr, "gelf_getehdr() failed: %s\n", elf_errmsg(-1));
return 1;
}
if (ehdr.e_type != ET_EXEC) {
printf("ELF is not an executable, proceeding anyway.\n");
}
if (ehdr.e_machine != EM_RISCV) {
fprintf(stderr, "ELF is not for RISC-V. Machine: 0x%X\n", ehdr.e_machine);
return 1;
}
Elf_Scn *scn = NULL;
GElf_Shdr shdr;
int warnedArch = 0;
size_t shdrstrndx; // Section HeaDeR STRing table iNDeX
if (elf_getshdrstrndx(elf, &shdrstrndx) != 0) {
fprintf(stderr, "elf_getshdrstrndx error: %s\n", elf_errmsg(-1));
return 1;
}
while ((scn = elf_nextscn(elf, scn)) != NULL) {
if (gelf_getshdr(scn, &shdr) != &shdr) {
fprintf(stderr, "getshdr() failed: %s\n", elf_errmsg(-1));
continue;
}
char *sectionName = elf_strptr(elf, shdrstrndx, shdr.sh_name);
if (shdr.sh_type == SHT_RISCV_ATTRIBUTES) {
Elf_Data *data = elf_getdata(scn, NULL);
if (data == NULL || data->d_size == 0) {
fprintf(stderr, "elf_getdata() failed: %s\n", elf_errmsg(-1));
continue;
}
// TODO this is highly fragile
// https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc
char *content = (char *)data->d_buf;
content += 19; // first 17 chars are subsection headers, and I have to +2 to make strcmp match
if (strcmp(content, "rv32i2p1") == 0) {
warnedArch = 1;
}
break;
} else if (strcmp(sectionName, ".text") == 0) {
Elf_Data *data = elf_getdata(scn, NULL);
if (data == NULL || data->d_size == 0) {
fprintf(stderr, "elf_getdata() failed: %s\n", elf_errmsg(-1));
continue;
}
char *content = (char *)data->d_buf;
uint32_t contentSize = data->d_size;
if (contentSize > addressSpace->romSize) {
fprintf(stderr, "Code has %d bytes, and doesn't fit in ROM of capacity %d bytes\n", contentSize, addressSpace->romSize);
return 1;
}
memcpy(addressSpace->rom, content, contentSize);
printf("Loaded %d bytes or %d instructions from an ELF file\n", contentSize, contentSize / 4);
} else {
printf("Unrecognized section: %s\n", sectionName);
}
}
if (warnedArch == 0) {
printf("I couldn't verify whether the ELF was compiled for the correct instruction set!\n");
}
cpu->programCounter = ehdr.e_entry;
return 0;
}
int load_elf_to_cpu_and_rom(const char filename[], CPU *cpu) {
if (elf_version(EV_CURRENT) == EV_NONE) {
fprintf(stderr, "Invalid ELF version: %s\n", elf_errmsg(-1)); // TODO can perror be used?
return 1;
}
int code = 0;
int fd = open(filename, O_RDONLY, 0);
if (fd < 0) {
perror("Failed opening file");
return 1;
}
Elf *elf = elf_begin(fd, ELF_C_READ, NULL);
if (elf == NULL) {
fprintf(stderr, "Error reading ELF file: %s\n", elf_errmsg(-1));
if (close(fd) < 0) {
perror("Also failed closing file");
}
return 1;
}
if ((code = process_elf(elf, cpu)) != 0) {
fprintf(stderr, "Failed processing ELF\n");
}
if (elf_end(elf) < 0) {
fprintf(stderr, "Failed closing ELF: %s\n", elf_errmsg(-1)); // TODO can perror be used?
}
if (close(fd) < 0) {
perror("Failed closing file"); // TODO perhaps make one close for all the errors above
}
return code;
}