aboutsummaryrefslogtreecommitdiff
path: root/common/elf.c
blob: da4750256beb669d294d50e4935353204b414311 (plain) (blame)
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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
/* SPDX-License-Identifier: copyleft-next-0.3.1 */
/* Copyright 2021 - 2022, Kim Kuparinen < kimi.h.kuparinen@gmail.com > */

/**
 * @file elf.c
 * Handle elf executables, set up requested memory mappings etc.
 */

#include <kmi/elf.h>
#include <kmi/vmem.h>
#include <kmi/bits.h>
#include <kmi/string.h>
#include <kmi/assert.h>

/**
 * Convert ELF flags to page flags.
 *
 * @param elf_flags ELF flags to convert.
 * @return Corresponding page flags.
 */
static uint8_t __elf_to_uvflags(uint8_t elf_flags)
{
	uint8_t uvflags = VM_V | VM_U;
	if (elf_flags & PF_X)
		uvflags |= VM_X;

	if (elf_flags & PF_W)
		uvflags |= VM_W;

	if (elf_flags & PF_R)
		uvflags |= VM_R;

	return uvflags;
}

/**
 * Map ELF executable.
 *
 * @param t Thread space to work in.
 * @param bin Address of binary to map.
 * @param ei_c ELF identity class.
 * @param phstart Program header start.
 * @param phnum Number of program header entries.
 * @param phsize Size of page header entry.
 */
static void __map_exec(struct tcb *t, vm_t bin, uint8_t ei_c, vm_t phstart,
                       size_t phnum, size_t phsize)
{
	hard_assert(t && is_proc(t), RETURN_VOID);

	/** \todo take alignment into consideration? */
	/** \todo take overlapping memory regions into account, probably mostly
	 * by keeping track of previously allocated area and seeing if the
	 * segment fits into it */
	/** \todo check if p_memsz is larger than p_filesz, the segment should be
	 * filled with zeroes. */
	/** \todo in general, make this a lot more clean. */
	/* useful bit of info: all segments are sorted in ascending order of p_vaddr */
	vm_t runner = phstart;
	vmflags_t default_flags = VM_V | VM_R | VM_W | VM_X | VM_U;
	for (size_t i = 0; i < phnum; ++i, runner += phsize) {
		if (program_header_prop(ei_c, runner, p_type) != PT_LOAD)
			continue;

		vm_t va = program_header_prop(ei_c, runner, p_vaddr);
		size_t vsz = program_header_prop(ei_c, runner, p_memsz);

		vm_t start = alloc_fixed_region(&t->sp_r, va, vsz, &vsz,
		                                default_flags);
		if (!start)
			return; /* out of memory or something */

		uint8_t elf_flags = program_header_prop(ei_c, runner, p_flags);
		uint8_t uvflags = __elf_to_uvflags(elf_flags);

		map_allocd_region(t->proc.vmem, start, vsz, default_flags, 0);
		memset((void *)start, 0, vsz);

		vm_t vo = bin + program_header_prop(ei_c, runner, p_offset);
		vm_t vfz = program_header_prop(ei_c, runner, p_filesz);
		memcpy((void *)va, (void *)vo, vfz);

		/* skip while testing
		 * \todo: also fix, this modifies only the first region. Create new
		 * function?
		 *
		   pm_t paddr = 0;
		   stat_vpage(t->b_r, va, &paddr, 0, 0);
		   mod_vpage(t->b_r, va, paddr, uvflags);
		 */
	}
}

/**
 * Map ELF dynamic object.
 *
 * @param t Thread space to work in.
 * @param bin Address of binary to map.
 * @param ei_c ELF identity class.
 * @param phstart Program header start.
 * @param phnum Number of program header entries.
 * @param phsize Size of page header entry.
 * @return Base of dynamic mapping.
 */
static vm_t __map_dyn(struct tcb *t, vm_t bin, uint8_t ei_c, vm_t phstart,
                      size_t phnum, size_t phsize)
{
	/** \todo this path should only be taken when no PT_INTERP is defined, as
	 * making sure ld is loaded should be done in userspace. Maybe a bit
	 * hacky, I know.*/
}

/**
 * Map binary and optional interpreter.
 *
 * \todo Implement interpeter handling.
 *
 * @param t Thread space to work in.
 * @param ei_c ELF identity class. (Of binary, should do one for interp?)
 * @param elf ELF binary.
 * @param interp ELF interpeter.
 * @return Entry address.
 */
static vm_t __prepare_proc(struct tcb *t, uint8_t ei_c, vm_t elf, vm_t interp)
{
	short e_type = elf_header_prop(ei_c, elf, e_type);
	if (e_type != ET_DYN && e_type != ET_EXEC)
		return 0;

	vm_t phstart = ptradd(elf, elf_header_prop(ei_c, elf, e_phoff));
	size_t phnum = elf_header_prop(ei_c, elf, e_phnum);
	size_t phsize = elf_header_prop(ei_c, elf, e_phentsize);

	vm_t entry = elf_header_prop(ei_c, elf, e_entry);
	if (e_type == ET_EXEC) {
		__map_exec(t, elf, ei_c, phstart, phnum, phsize);
		return entry;
	} else {
		vm_t o = __map_dyn(t, elf, ei_c, phstart, phnum, phsize);
		return o + entry;
	}
}

/* sets up all memory regions etc, returns the entry address */
vm_t load_elf(struct tcb *t, vm_t elf, vm_t interp)
{
	struct elf_ident *i = (struct elf_ident *)elf;
	if (i->ei_magic != cpu_to_be32(EI_MAGIC))
		return 0;

	if (i->ei_class != ELFCLASS32 && i->ei_class != ELFCLASS64)
		return 0;

	/* more sanity checks? */
	return __prepare_proc(t, i->ei_class, elf, interp);
}