Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
script3r
GitHub Repository: script3r/os161
Path: blob/master/kern/syscall/loadelf.c
2093 views
1
/*
2
* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009
3
* The President and Fellows of Harvard College.
4
*
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions
7
* are met:
8
* 1. Redistributions of source code must retain the above copyright
9
* notice, this list of conditions and the following disclaimer.
10
* 2. Redistributions in binary form must reproduce the above copyright
11
* notice, this list of conditions and the following disclaimer in the
12
* documentation and/or other materials provided with the distribution.
13
* 3. Neither the name of the University nor the names of its contributors
14
* may be used to endorse or promote products derived from this software
15
* without specific prior written permission.
16
*
17
* THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
18
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
* ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
21
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27
* SUCH DAMAGE.
28
*/
29
30
31
/*
32
* Code to load an ELF-format executable into the current address space.
33
*
34
* It makes the following address space calls:
35
* - first, as_define_region once for each segment of the program;
36
* - then, as_prepare_load;
37
* - then it loads each chunk of the program;
38
* - finally, as_complete_load.
39
*
40
* This gives the VM code enough flexibility to deal with even grossly
41
* mis-linked executables if that proves desirable. Under normal
42
* circumstances, as_prepare_load and as_complete_load probably don't
43
* need to do anything.
44
*
45
* If you wanted to support memory-mapped executables you would need
46
* to rearrange this to map each segment.
47
*
48
* To support dynamically linked executables with shared libraries
49
* you'd need to change this to load the "ELF interpreter" (dynamic
50
* linker). And you'd have to write a dynamic linker...
51
*/
52
53
#include <types.h>
54
#include <kern/errno.h>
55
#include <lib.h>
56
#include <uio.h>
57
#include <thread.h>
58
#include <current.h>
59
#include <addrspace.h>
60
#include <vnode.h>
61
#include <elf.h>
62
63
/*
64
* Load a segment at virtual address VADDR. The segment in memory
65
* extends from VADDR up to (but not including) VADDR+MEMSIZE. The
66
* segment on disk is located at file offset OFFSET and has length
67
* FILESIZE.
68
*
69
* FILESIZE may be less than MEMSIZE; if so the remaining portion of
70
* the in-memory segment should be zero-filled.
71
*
72
* Note that uiomove will catch it if someone tries to load an
73
* executable whose load address is in kernel space. If you should
74
* change this code to not use uiomove, be sure to check for this case
75
* explicitly.
76
*/
77
static
78
int
79
load_segment(struct vnode *v, off_t offset, vaddr_t vaddr,
80
size_t memsize, size_t filesize,
81
int is_executable)
82
{
83
struct iovec iov;
84
struct uio u;
85
int result;
86
87
if (filesize > memsize) {
88
kprintf("ELF: warning: segment filesize > segment memsize\n");
89
filesize = memsize;
90
}
91
92
DEBUG(DB_EXEC, "ELF: Loading %lu bytes to 0x%lx\n",
93
(unsigned long) filesize, (unsigned long) vaddr);
94
95
iov.iov_ubase = (userptr_t)vaddr;
96
iov.iov_len = memsize; // length of the memory space
97
u.uio_iov = &iov;
98
u.uio_iovcnt = 1;
99
u.uio_resid = filesize; // amount to read from the file
100
u.uio_offset = offset;
101
u.uio_segflg = is_executable ? UIO_USERISPACE : UIO_USERSPACE;
102
u.uio_rw = UIO_READ;
103
u.uio_space = curthread->t_addrspace;
104
105
result = VOP_READ(v, &u);
106
if (result) {
107
return result;
108
}
109
110
if (u.uio_resid != 0) {
111
/* short read; problem with executable? */
112
kprintf("ELF: short read on segment - file truncated?\n");
113
return ENOEXEC;
114
}
115
116
/*
117
* If memsize > filesize, the remaining space should be
118
* zero-filled. There is no need to do this explicitly,
119
* because the VM system should provide pages that do not
120
* contain other processes' data, i.e., are already zeroed.
121
*
122
* During development of your VM system, it may have bugs that
123
* cause it to (maybe only sometimes) not provide zero-filled
124
* pages, which can cause user programs to fail in strange
125
* ways. Explicitly zeroing program BSS may help identify such
126
* bugs, so the following disabled code is provided as a
127
* diagnostic tool. Note that it must be disabled again before
128
* you submit your code for grading.
129
*/
130
#if 0
131
{
132
size_t fillamt;
133
134
fillamt = memsize - filesize;
135
if (fillamt > 0) {
136
DEBUG(DB_EXEC, "ELF: Zero-filling %lu more bytes\n",
137
(unsigned long) fillamt);
138
u.uio_resid += fillamt;
139
result = uiomovezeros(fillamt, &u);
140
}
141
}
142
#endif
143
144
return result;
145
}
146
147
/*
148
* Load an ELF executable user program into the current address space.
149
*
150
* Returns the entry point (initial PC) for the program in ENTRYPOINT.
151
*/
152
int
153
load_elf(struct vnode *v, vaddr_t *entrypoint)
154
{
155
Elf_Ehdr eh; /* Executable header */
156
Elf_Phdr ph; /* "Program header" = segment header */
157
int result, i;
158
struct iovec iov;
159
struct uio ku;
160
161
/*
162
* Read the executable header from offset 0 in the file.
163
*/
164
165
uio_kinit(&iov, &ku, &eh, sizeof(eh), 0, UIO_READ);
166
result = VOP_READ(v, &ku);
167
if (result) {
168
return result;
169
}
170
171
if (ku.uio_resid != 0) {
172
/* short read; problem with executable? */
173
kprintf("ELF: short read on header - file truncated?\n");
174
return ENOEXEC;
175
}
176
177
/*
178
* Check to make sure it's a 32-bit ELF-version-1 executable
179
* for our processor type. If it's not, we can't run it.
180
*
181
* Ignore EI_OSABI and EI_ABIVERSION - properly, we should
182
* define our own, but that would require tinkering with the
183
* linker to have it emit our magic numbers instead of the
184
* default ones. (If the linker even supports these fields,
185
* which were not in the original elf spec.)
186
*/
187
188
if (eh.e_ident[EI_MAG0] != ELFMAG0 ||
189
eh.e_ident[EI_MAG1] != ELFMAG1 ||
190
eh.e_ident[EI_MAG2] != ELFMAG2 ||
191
eh.e_ident[EI_MAG3] != ELFMAG3 ||
192
eh.e_ident[EI_CLASS] != ELFCLASS32 ||
193
eh.e_ident[EI_DATA] != ELFDATA2MSB ||
194
eh.e_ident[EI_VERSION] != EV_CURRENT ||
195
eh.e_version != EV_CURRENT ||
196
eh.e_type!=ET_EXEC ||
197
eh.e_machine!=EM_MACHINE) {
198
return ENOEXEC;
199
}
200
201
/*
202
* Go through the list of segments and set up the address space.
203
*
204
* Ordinarily there will be one code segment, one read-only
205
* data segment, and one data/bss segment, but there might
206
* conceivably be more. You don't need to support such files
207
* if it's unduly awkward to do so.
208
*
209
* Note that the expression eh.e_phoff + i*eh.e_phentsize is
210
* mandated by the ELF standard - we use sizeof(ph) to load,
211
* because that's the structure we know, but the file on disk
212
* might have a larger structure, so we must use e_phentsize
213
* to find where the phdr starts.
214
*/
215
216
for (i=0; i<eh.e_phnum; i++) {
217
off_t offset = eh.e_phoff + i*eh.e_phentsize;
218
uio_kinit(&iov, &ku, &ph, sizeof(ph), offset, UIO_READ);
219
220
result = VOP_READ(v, &ku);
221
if (result) {
222
return result;
223
}
224
225
if (ku.uio_resid != 0) {
226
/* short read; problem with executable? */
227
kprintf("ELF: short read on phdr - file truncated?\n");
228
return ENOEXEC;
229
}
230
231
switch (ph.p_type) {
232
case PT_NULL: /* skip */ continue;
233
case PT_PHDR: /* skip */ continue;
234
case PT_MIPS_REGINFO: /* skip */ continue;
235
case PT_LOAD: break;
236
default:
237
kprintf("loadelf: unknown segment type %d\n",
238
ph.p_type);
239
return ENOEXEC;
240
}
241
242
result = as_define_region(curthread->t_addrspace,
243
ph.p_vaddr, ph.p_memsz,
244
ph.p_flags & PF_R,
245
ph.p_flags & PF_W,
246
ph.p_flags & PF_X);
247
if (result) {
248
return result;
249
}
250
}
251
252
result = as_prepare_load(curthread->t_addrspace);
253
if (result) {
254
return result;
255
}
256
257
/*
258
* Now actually load each segment.
259
*/
260
261
for (i=0; i<eh.e_phnum; i++) {
262
off_t offset = eh.e_phoff + i*eh.e_phentsize;
263
uio_kinit(&iov, &ku, &ph, sizeof(ph), offset, UIO_READ);
264
265
result = VOP_READ(v, &ku);
266
if (result) {
267
return result;
268
}
269
270
if (ku.uio_resid != 0) {
271
/* short read; problem with executable? */
272
kprintf("ELF: short read on phdr - file truncated?\n");
273
return ENOEXEC;
274
}
275
276
switch (ph.p_type) {
277
case PT_NULL: /* skip */ continue;
278
case PT_PHDR: /* skip */ continue;
279
case PT_MIPS_REGINFO: /* skip */ continue;
280
case PT_LOAD: break;
281
default:
282
kprintf("loadelf: unknown segment type %d\n",
283
ph.p_type);
284
return ENOEXEC;
285
}
286
287
result = load_segment(v, ph.p_offset, ph.p_vaddr,
288
ph.p_memsz, ph.p_filesz,
289
ph.p_flags & PF_X);
290
if (result) {
291
return result;
292
}
293
}
294
295
result = as_complete_load(curthread->t_addrspace);
296
if (result) {
297
return result;
298
}
299
300
*entrypoint = eh.e_entry;
301
302
return 0;
303
}
304
305