vxBoot/src/loader/elf/rela.c

166 lines
3.7 KiB
C

#include "vxBoot/loader.h"
#include "vxBoot/fs/smemfs.h"
#include "vxBoot/terminal.h"
#include <string.h>
#include <stdlib.h>
struct {
char const * restrict const name;
uint16_t id;
} const table[] = {
{"R_SH_NONE", 0},
{"R_SH_DIR32", 1},
{"R_SH_REL32", 2},
{"R_SH_DIR8WPN", 3},
{"R_SH_IND12W", 4},
{"R_SH_DIR8WPL", 5},
{"R_SH_DIR8WPZ", 6},
{"R_SH_DIR8BP", 7},
{"R_SH_DIR8W", 8},
{"R_SH_DIR8L", 9},
{"R_SH_SWITCH16", 25},
{"R_SH_SWITCH32", 26},
{"R_SH_USES", 27},
{"R_SH_COUNT", 28},
{"R_SH_ALIGN", 29},
{"R_SH_CODE", 30},
{"R_SH_DATA", 31},
{"R_SH_LABEL", 32},
{"R_SH_SWITCH8", 33},
{"R_SH_GNU_VTINHERIT", 4},
{"R_SH_GNU_VTENTRY", 5},
{"R_SH_TLS_GD_32", 144},
{"R_SH_TLS_LD_32", 145},
{"R_SH_TLS_LDO_32", 146},
{"R_SH_TLS_IE_32", 147},
{"R_SH_TLS_LE_32", 148},
{"R_SH_TLS_DTPMOD32", 49},
{"R_SH_TLS_DTPOFF32", 50},
{"R_SH_TLS_TPOFF32", 51},
{"R_SH_GOT32", 160},
{"R_SH_PLT32", 161},
{"R_SH_COPY", 162},
{"R_SH_GLOB_DAT", 163},
{"R_SH_JMP_SLOT", 164},
{"R_SH_RELATIVE", 165},
{"R_SH_GOTOFF", 166},
{"R_SH_GOTPC", 167},
{"R_SH_NUM", 256},
{NULL, 0}
};
/* loader_reloc_symbols() : relocalize symbols */
static int loader_reloc_section(
Elf32_Rela *rela,
int nb_rela,
char const * const name,
struct kernel * const kernel
) {
uintptr_t voff;
uintptr_t val;
uintptr_t loc;
int type;
/* logs */
terminal_log(
LOG_INFO,
" section '%s' with %d entries\n",
name, nb_rela
);
/* precalculate relocalisation operation */
voff = (uintptr_t)kernel->hardware.ram.physical.kernel_addr;
voff |= (uintptr_t)0x80000000;
/* try to relocalise symbols */
for (int i = 0; i < nb_rela; ++i) {
type = -1;
for (int j = 0; table[j].name != NULL; ++j) {
if (table[j].id != ELF32_R_TYPE(rela[i].r_info))
continue;
if (ELF32_R_TYPE(rela[i].r_info) == R_SH_NUM)
return (0);
type = j;
break;
}
if (type < 0) {
terminal_write("unable to relocalize symbols %d\n", i);
return (-1);
}
loc = rela[i].r_offset;
loc += (uintptr_t)kernel->hardware.ram.physical.kernel_addr;
loc |= (uintptr_t)0xa0000000;
/* IMPORTANT NOTE:
We cannot use the real symbols value for patching the binary
because some symbols are completely broken and refer to
something else. Specially when a global use another global
(for example: const char * const text = "vive les g@m3rz").
But, after some RE, each time a global symbol is used, its
virtual address is set. So, for now, we just need to add the
relocation offset to the content of the location. */
//val = kernel->elf.sym.tab[ELF32_R_SYM(rela[i].r_info)].st_value;
val = *(uintptr_t*)loc;
switch (table[type].id) {
case R_SH_GOT32:
case R_SH_PLT32:
case R_SH_GOTPC:
case R_SH_GOTOFF:
break;
case R_SH_DIR32:
//terminal_log(
// LOG_DEBUG,
// " %08x %-8s %08x\n",
// rela[i].r_offset,
// table[type].name,
// val
//);
*(uintptr_t *)loc = voff + val;
break;
default:
terminal_log(
LOG_ALERT,
" [%d] type %s not supported yet o(x_x)o\n",
i, table[type].name
);
return (-1);
}
}
return (0);
}
/* loader_rela_patch() : Try to patch the GOT/PLT section */
int loader_rela_patch(struct kernel * const kernel)
{
Elf32_Rela *rela;
int error;
rela = NULL;
for (int i = 0; i < kernel->elf.hdr.e_shnum; ++i) {
if (kernel->elf.shdr[i].sh_type != SHT_RELA)
continue;
rela = realloc(rela, kernel->elf.shdr[i].sh_size);
smemfs_pread(
kernel->inode,
rela,
kernel->elf.shdr[i].sh_size,
kernel->elf.shdr[i].sh_offset
);
error = loader_reloc_section(
rela,
kernel->elf.shdr[i].sh_size / sizeof(Elf32_Rela),
&kernel->elf.shstrtab[kernel->elf.shdr[i].sh_name],
kernel
);
if (error != 0)
return (error);
}
return (0);
}