188 lines
4.9 KiB
C
188 lines
4.9 KiB
C
//---
|
|
// fxBoot:loader:entry - ELF Loader entry
|
|
//---
|
|
#include "fxBoot/hypervisor.h"
|
|
#include "fxBoot/fs/smemfs.h"
|
|
#include "fxBoot/elf.h"
|
|
#include "fxBoot/terminal.h"
|
|
|
|
#include <gint/std/stdlib.h>
|
|
#include <gint/std/string.h>
|
|
|
|
#include "./src/hypervisor/internal/elf.h"
|
|
|
|
//---
|
|
// Internal helpers
|
|
//---
|
|
/* relocinfo: Internal structure used to stoe all ELF information */
|
|
struct relocinfo {
|
|
Elf32_Ehdr *ehdr;
|
|
Elf32_Shdr ehdr_dynamic;
|
|
Elf32_Dyn *dynamic;
|
|
Elf32_Sym *dynsym;
|
|
char *dynstr;
|
|
char *shstrtab;
|
|
struct smemfs_inode *inode;
|
|
};
|
|
|
|
/* reloc_dump_section(): Dump one section */
|
|
static void *reloc_dump_section(struct smemfs_inode *inode, Elf32_Shdr *shdr)
|
|
{
|
|
void *section;
|
|
|
|
section = malloc(shdr->sh_size);
|
|
if (section == NULL)
|
|
return (NULL);
|
|
if (smemfs_pread(inode, section, shdr->sh_size, shdr->sh_offset)
|
|
!= (ssize_t)shdr->sh_size) {
|
|
free(section);
|
|
return (NULL);
|
|
}
|
|
return (section);
|
|
}
|
|
|
|
/* reloc_get_dynamic_info(): Dump all dynamic information */
|
|
static int reloc_get_dynamic_info(struct relocinfo *elf)
|
|
{
|
|
Elf32_Shdr shdr;
|
|
off_t offset;
|
|
void **tmp;
|
|
|
|
/* get ELF section header string table */
|
|
offset = elf->ehdr->e_shoff;
|
|
offset += elf->ehdr->e_shstrndx * elf->ehdr->e_shentsize;
|
|
if (smemfs_pread(elf->inode, &shdr, elf->ehdr->e_shentsize, offset)
|
|
!= elf->ehdr->e_shentsize) {
|
|
return (-1);
|
|
}
|
|
elf->shstrtab = reloc_dump_section(elf->inode, &shdr);
|
|
if (elf->shstrtab == NULL)
|
|
return (-2);
|
|
|
|
/* get ELF information */
|
|
elf->dynstr = NULL;
|
|
elf->dynamic = NULL;
|
|
for (int i = 1 ; i < elf->ehdr->e_shnum ; ++i) {
|
|
offset = elf->ehdr->e_shoff + (i * elf->ehdr->e_shentsize);
|
|
if (smemfs_pread(elf->inode, &shdr,
|
|
elf->ehdr->e_shentsize, offset)
|
|
!= elf->ehdr->e_shentsize) {
|
|
continue;
|
|
}
|
|
tmp = NULL;
|
|
if (strcmp(".dynamic", &elf->shstrtab[shdr.sh_name]) == 0) {
|
|
memcpy(&elf->ehdr_dynamic, &shdr, sizeof(Elf32_Shdr));
|
|
tmp = (void*)&elf->dynamic;
|
|
}
|
|
if (strcmp(".dynstr", &elf->shstrtab[shdr.sh_name]) == 0)
|
|
tmp = (void*)&elf->dynstr;
|
|
if (strcmp(".dynsym", &elf->shstrtab[shdr.sh_name]) == 0)
|
|
tmp = (void*)&elf->dynsym;
|
|
if (tmp == NULL)
|
|
continue;
|
|
*tmp = reloc_dump_section(elf->inode, &shdr);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/* reloc_get_dynsym(): Try to find the dynamic symbol address */
|
|
static void *reloc_get_dynsym(struct relocinfo *info, int symid)
|
|
{
|
|
char *name;
|
|
|
|
if (info->dynsym == NULL || info->dynstr == NULL)
|
|
return (NULL);
|
|
//FIXME: check symid validity !
|
|
name = &info->dynstr[info->dynsym[symid].st_name];
|
|
for (int i = 0; gint_reloc[i].name != NULL; ++i) {
|
|
if (strcmp(name, gint_reloc[i].name) != 0)
|
|
continue;
|
|
terminal_write(" * reloc '%s'\n", name);
|
|
return ((void*)gint_reloc[i].addr);
|
|
}
|
|
terminal_write("unknown '%s' symbols\n", name);
|
|
return (NULL);
|
|
}
|
|
|
|
/* reloc_section(): Relocalise section's symbols */
|
|
static int reloc_rela(struct hworld *world,
|
|
struct relocinfo *info, Elf32_Shdr *shdr)
|
|
{
|
|
Elf32_Rela rela;
|
|
uint32_t *prog;
|
|
off_t offset;
|
|
void *dyn;
|
|
|
|
if (shdr->sh_size == 0 || shdr->sh_entsize == 0) {
|
|
terminal_write("empty section\n");
|
|
return (0);
|
|
}
|
|
prog = (void*)world->memory.program.start;
|
|
for (uint32_t i = 0 ; i < shdr->sh_size / shdr->sh_entsize ; ++i) {
|
|
offset = shdr->sh_offset + (i * shdr->sh_entsize);
|
|
if (smemfs_pread(info->inode, &rela, shdr->sh_entsize, offset)
|
|
!= (ssize_t)shdr->sh_entsize) {
|
|
return (hel_reloc_size_error);
|
|
}
|
|
if (ELF32_R_TYPE(rela.r_info) == R_SH_RELATIVE) {
|
|
offset = rela.r_offset;
|
|
offset -= world->memory.program.elf.vmin;
|
|
prog[offset >> 2] -= world->memory.program.elf.vmin;
|
|
prog[offset >> 2] +=
|
|
(uintptr_t)world->memory.program.start;
|
|
continue;
|
|
}
|
|
if (ELF32_R_TYPE(rela.r_info) == R_SH_JMP_SLOT) {
|
|
dyn = reloc_get_dynsym(info, ELF32_R_SYM(rela.r_info));
|
|
if (dyn == NULL)
|
|
return (-1);
|
|
offset = rela.r_offset;
|
|
offset -= world->memory.program.elf.vmin;
|
|
prog[offset >> 2] = (uintptr_t)dyn;
|
|
world->private.env = HYPERVISOR_ENV_GINT;
|
|
continue;
|
|
}
|
|
terminal_write("unknown type %x\n", rela.r_info);
|
|
return (-1);
|
|
}
|
|
return (hel_reloc_success);
|
|
}
|
|
|
|
//---
|
|
// Public helpers
|
|
//---
|
|
/* loader_reloc_sym(): Relocalise all symbols */
|
|
int hypervisor_elf_loader_reloc_sym(struct hworld *world,
|
|
struct smemfs_inode *inode, Elf32_Ehdr *ehdr)
|
|
{
|
|
struct relocinfo info;
|
|
Elf32_Shdr shdr;
|
|
off_t offset;
|
|
|
|
|
|
/* get dynamic information */
|
|
info.inode = inode;
|
|
info.ehdr = ehdr;
|
|
if (reloc_get_dynamic_info(&info) != 0)
|
|
return (-1);
|
|
|
|
/* find all reloc section */
|
|
//TODO : support SH_REL
|
|
//TODO : check if the GOT, PLT sections are relocalized !
|
|
for (int i = 1 ; i < ehdr->e_shnum ; ++i) {
|
|
offset = ehdr->e_shoff + (i * ehdr->e_shentsize);
|
|
if (smemfs_pread(inode, &shdr, ehdr->e_shentsize, offset)
|
|
!= ehdr->e_shentsize) {
|
|
return (hel_reloc_size_error);
|
|
}
|
|
if (shdr.sh_type == SHT_RELA
|
|
&& reloc_rela(world, &info, &shdr) != hel_reloc_success)
|
|
return (hel_reloc_error);
|
|
if (shdr.sh_type == SHT_REL) {
|
|
terminal_write("SHT_REL not yet supported\n");
|
|
return (hel_reloc_error);
|
|
}
|
|
}
|
|
return (hel_reloc_success);
|
|
}
|