mirror of https://github.com/flysand7/ciabatta.git
207 lines
7.3 KiB
C
207 lines
7.3 KiB
C
|
|
// Stage 2 of the dynamic loader
|
|
// the purpose of this module is to relocate itself
|
|
// and jump to the main body of the loader.
|
|
|
|
// Note that loader-entry.asm doesn't "jump" into
|
|
// the entry point of this module, but rather
|
|
// "falls-through". Which means the sections
|
|
// of this module and the module above must be
|
|
// contiguous, which we do by doing linker magic
|
|
|
|
// This also means that this module CANNOT have
|
|
// any externally-defined functions (static functions
|
|
// are fine with gcc and clang (I think))
|
|
|
|
#include <cia/def.h>
|
|
#include <stdarg.h>
|
|
#include <bin/elf.h>
|
|
#include <sys/syscall.h>
|
|
#include <sys/mman.h>
|
|
#include <sched.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
|
|
#include "loader.h"
|
|
|
|
extern void ld_stage2_entry(Loader_Info *ld_info);
|
|
|
|
void ld_stage1_entry(u64 *sp, Elf64_Dyn *dynv) {
|
|
_dbg_print_string("Entered dynamic loader\n");
|
|
// Find argc, argv in stack
|
|
int argc = *sp;
|
|
char **argv = (void *)(sp+1);
|
|
_dbg_printf("ARGV:\n");
|
|
for(int i = 0; i < argc; ++i) {
|
|
_dbg_printf("[%d]: %s\n", (i64)i, argv[i]);
|
|
}
|
|
// Skip over environment
|
|
char **envp = argv+argc+1;
|
|
int envc = 0;
|
|
for(int i = 0; envp[i]; i += 1) {
|
|
envc += 1;
|
|
}
|
|
// Load aux vector, which is responsible
|
|
// for holding information for the loader such as
|
|
// the location of phdrs or its load address
|
|
u64 aux[AUX_CNT];
|
|
Elf64_Aux *auxv = (Elf64_Aux *)(envp + envc + 1);
|
|
for(int i = 0; i < AUX_CNT; ++i) {
|
|
aux[i] = 0;
|
|
}
|
|
for(int i = 0; auxv[i].a_type != AT_NULL; ++i) {
|
|
int aux_type = auxv[i].a_type;
|
|
if(aux_type < AUX_CNT) {
|
|
aux[aux_type] = auxv[i].a_val;
|
|
}
|
|
}
|
|
// Read the _DYNAMIC vector
|
|
u64 dyn[DYN_CNT];
|
|
for(int i = 0; i < DYN_CNT; ++i) {
|
|
dyn[i] = 0;
|
|
}
|
|
_dbg_printf("Dynamic section contents:\n");
|
|
for(int i = 0; dynv[i].d_tag != DT_NULL; ++i) {
|
|
u64 dyn_tag = dynv[i].d_tag;
|
|
_dbg_printf(" %x: %x\n", dyn_tag, dynv[i].d_un.d_val);
|
|
if(dyn_tag < DYN_CNT) {
|
|
dyn[dyn_tag] = dynv[i].d_un.d_val;
|
|
}
|
|
}
|
|
// Find the dynamic section
|
|
if(dynv == NULL) {
|
|
_dbg_printf("_DYNAMIC section wasn't found in dynamic loader\n");
|
|
}
|
|
// If the dynamic linker is not invoked as a command
|
|
// It's load address can't be found in the aux vector
|
|
// Therefore we compute the base as the difference
|
|
// between &_DYNAMIC and it's p_vaddr
|
|
u8 *base = (void *)aux[AT_BASE];
|
|
if(base == NULL) {
|
|
_dbg_printf("trying to find base\n");
|
|
u8 *phdrs = (void *)aux[AT_PHDR];
|
|
u64 ph_ent = aux[AT_PHENT];
|
|
u64 ph_num = aux[AT_PHNUM];
|
|
for(int i = 1; i < ph_num; ++i) {
|
|
u64 ph_off = i * ph_ent;
|
|
Elf64_Phdr *ph = (void *)(phdrs + ph_off);
|
|
if(ph->p_type == PT_DYNAMIC) {
|
|
_dbg_printf("_DYNAMIC vaddr: %x\n", ph->p_vaddr);
|
|
base = (void *)(dynv - ph->p_vaddr);
|
|
}
|
|
}
|
|
}
|
|
_dbg_printf("ld-cia.so base: %x\n", base);
|
|
_dbg_printf("ld-cia.so _dynamic: %x\n", dynv);
|
|
Elf64_Sym *symtab = symtab = (void *)(base + dyn[DT_SYMTAB]);
|
|
if(symtab == NULL) {
|
|
_dbg_printf("ERROR: No .dynsym found\n");
|
|
}
|
|
// Find the string table
|
|
char *strtab = (void *)(base + dyn[DT_STRTAB]);
|
|
if(dyn[DT_STRTAB] == 0) {
|
|
strtab = NULL;
|
|
}
|
|
// Use memory fences, to MAKE SURE the compiler won't reorder code and
|
|
// accidentally use relocations when they are not ready. The code before
|
|
// this point is carefully written to avoid generating relocations.
|
|
_mfence();
|
|
// Apply relocations
|
|
if(dyn[DT_REL] != 0) {
|
|
_dbg_printf("REL Relocations found. This part isn't implemented\n");
|
|
u8 *rel_ents = (void *)(base + dyn[DT_REL]);
|
|
u64 rel_ent = dyn[DT_RELENT];
|
|
u64 rel_size = dyn[DT_RELSZ];
|
|
u64 rel_offs = 0;
|
|
while(rel_offs < rel_size) {
|
|
Elf64_Rel *rel = (void *)(rel_ents + rel_offs);
|
|
u64 offs = rel->r_offset;
|
|
u32 sym = ELF64_R_SYM(rel->r_info);
|
|
u32 type = ELF64_R_TYPE(rel->r_info);
|
|
_dbg_printf(" %d @ %d (%d)\n", sym, offs, type);
|
|
// TODO: if needed
|
|
rel_offs += rel_ent;
|
|
}
|
|
}
|
|
if(dyn[DT_RELA] != 0) {
|
|
_dbg_printf("RELA:\n");
|
|
u8 *rela_ents = (void *)(base + dyn[DT_RELA]);
|
|
u64 rela_ent = dyn[DT_RELAENT];
|
|
u64 rela_size = dyn[DT_RELASZ];
|
|
u64 rela_offs = 0;
|
|
while(rela_offs < rela_size) {
|
|
Elf64_Rela *rela = (void *)(rela_ents + rela_offs);
|
|
u64 reloc_offs = rela->r_offset;
|
|
u64 addend = rela->r_addend;
|
|
Elf64_Sym *sym = &symtab[ELF64_R_SYM(rela->r_info)];
|
|
u32 type = ELF64_R_TYPE(rela->r_info);
|
|
void *sym_addr = (void *)(base + sym->st_value);
|
|
void **reloc_addr = (void *)(base + reloc_offs);
|
|
{
|
|
u32 sym_name_offset = sym->st_name;
|
|
if(sym_name_offset == 0) {
|
|
_dbg_printf(" %x+%d, @%x (%d)", sym_addr, addend, reloc_offs, type);
|
|
}
|
|
else {
|
|
char *sym_name = &strtab[sym_name_offset];
|
|
_dbg_printf(" %s+%d, @%x (%d)", sym_name, addend, reloc_offs, type);
|
|
}
|
|
}
|
|
if(type == R_X86_64_GLOB_DAT) {
|
|
*reloc_addr = sym_addr;
|
|
}
|
|
else if(type == R_X86_64_RELATIVE) {
|
|
*reloc_addr = (void *)(base + addend);
|
|
}
|
|
else {
|
|
printf("ERROR: unhandled relocation type: %d\n", type);
|
|
sys_exit(1);
|
|
}
|
|
_dbg_printf(" -> %x\n", *reloc_addr);
|
|
rela_offs += rela_ent;
|
|
}
|
|
}
|
|
if(dyn[DT_PLTGOT] != 0) {
|
|
_dbg_printf("PLT relocations found\n");
|
|
if(dyn[DT_PLTREL] == DT_REL) {
|
|
_dbg_printf(" PLT relocations use relocations of type REL\n");
|
|
printf("ERROR: .plt relocations of type REL not implemented\n");
|
|
sys_exit(1);
|
|
}
|
|
else if(dyn[DT_PLTREL] == DT_RELA) {
|
|
_dbg_printf(" PLT relocations use relocations of type RELA\n");
|
|
}
|
|
void *plt = (void *)(base + dyn[DT_PLTGOT]);
|
|
void *rela_plt = (void *)(base + dyn[DT_JMPREL]);
|
|
u64 rela_ent = sizeof(Elf64_Rela);
|
|
u64 rela_size = dyn[DT_PLTRELSZ];
|
|
u64 rela_offs = 0;
|
|
while(rela_offs < rela_size) {
|
|
Elf64_Rela *rela = (void *)(rela_plt + rela_offs);
|
|
u64 reloc_offs = rela->r_offset;
|
|
u64 addend = rela->r_addend;
|
|
u32 sym_idx = ELF64_R_SYM(rela->r_info);
|
|
u32 type = ELF64_R_TYPE(rela->r_info);
|
|
_dbg_printf(" %x+%d, @%x (%d)\n", sym_idx, addend, reloc_offs, type);
|
|
if(type == R_X86_64_JUMP_SLOT) {
|
|
Elf64_Sym *sym = &symtab[sym_idx];
|
|
void *sym_addr = (void *)(base + sym->st_value);
|
|
void **reloc_addr = (void *)(base + reloc_offs);
|
|
_dbg_printf(" -> resolving with %x\n", sym_addr);
|
|
*reloc_addr = sym_addr;
|
|
}
|
|
rela_offs += rela_ent;
|
|
}
|
|
}
|
|
_mfence();
|
|
Loader_Info ld_info;
|
|
ld_info.sp = sp;
|
|
ld_info.ldso_base = base;
|
|
ld_info.dyn = dyn;
|
|
ld_info.aux = aux;
|
|
_dbg_printf("Self-relocation finished. Entering the loader\n");
|
|
ld_stage2_entry(&ld_info);
|
|
sys_exit(0);
|
|
}
|
|
|