summaryrefslogtreecommitdiff
path: root/boot/elf.c
blob: b8328b00d6189e37179ed42fefa36791e1ec17a6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#include <elf.h>
#include <stddef.h>

#include "bootloader.h"

static int cmp(const char *a, const char *b, size_t len)
{
	while (len--) if (*a++ != *b++) return 1;
	return 0;
}

static void load_segment(char *dst, const char *src, size_t outlen, size_t inlen)
{
	outlen -= inlen;
	while (inlen--) *dst++ = *src++;
	while (outlen--) *dst++ = 0;
}

static const char e_ident[] = {
	0x7f, 'E', 'L', 'F',  /* magic number  */
	ELFCLASS32,           /* 32-bit        */
	ELFDATA2MSB,          /* big endian    */
	1,                    /* ELF version 1 */
};

_Noreturn void bootloader(struct systemid *boot)
{
	if (!boot->load_addr || boot->load_addr & 3) fail();
	if (boot->stack_master & 3) fail();

	char *base  = (void*) boot->load_addr;
	long *stack = (long*)(boot->stack_master ? boot->stack_master : 0x06002000);

	Elf32_Ehdr *ehdr = (void*)base;
	if (cmp(ehdr->e_ident, e_ident, sizeof e_ident)
		|| ehdr->e_type    != ET_EXEC
		|| ehdr->e_machine != EM_SH
		|| ehdr->e_version != EV_CURRENT
		|| !ehdr->e_entry)
		fail();

	void  *entry     = (void*)ehdr->e_entry;
	char  *phdrs     = base + ehdr->e_phoff;
	size_t phentsize = ehdr->e_phentsize;
	size_t phnum     = ehdr->e_phnum;

	/* find load address for program headers, immediately before first segment */
	char *phdr_out = NULL, *p;
	size_t n;
	for (p = phdrs, n = phnum; n--; p += phentsize) {
		Elf32_Phdr *ph = (void*)p;
		if (ph->p_type == PT_LOAD) {
			phdr_out = (char*)ph->p_vaddr - phentsize * phnum;
			break;
		}
	}
	if (!phdr_out) fail();

	/* fixup PT_PHDR program header (if present) to point to where we're loading it */
	for (p = phdrs, n = phnum; n--; p += phentsize) {
		Elf32_Phdr *ph = (void*)p;
		if (ph->p_type == PT_PHDR) {
			ph->p_vaddr = (Elf32_Word)phdr_out;
			break;
		}
	}

	/* load program headers */
	load_segment(phdr_out, phdrs, phentsize * phnum, phentsize * phnum);

	/* load all the PT_LOAD segments */
	for (p = phdrs, n = phnum; n--; p += phentsize) {
		Elf32_Phdr *ph = (void*)p;
		if (ph->p_type != PT_LOAD) continue;
		load_segment((void*)ph->p_vaddr, base + ph->p_offset, ph->p_memsz, ph->p_filesz);
	}

#define PUSH(v) do { *--stack = (long)(v); } while(0)
#define PUSH_AUX(a,v) do { PUSH(v); PUSH(a); } while(0)

	/* push aux vector onto target process stack */
	long *emptystr = stack - 1;
	PUSH_AUX(AT_NULL,  0);
	PUSH_AUX(AT_PHDR,  phdr_out);
	PUSH_AUX(AT_PHENT, phentsize);
	PUSH_AUX(AT_PHNUM, phnum);
	PUSH_AUX(AT_ENTRY, entry);

	/* push environment and arguments */
	PUSH(0);         /* envp[0] */
	PUSH(0);         /* argv[1] */
	PUSH(emptystr);  /* argv[0] */
	PUSH(1);         /* argc    */

	jump(entry, stack);
	fail();
}