-
Notifications
You must be signed in to change notification settings - Fork 0
/
loader.c
174 lines (141 loc) · 3.71 KB
/
loader.c
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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
/*
* Loader Implementation
*
* 2018, Operating Systems
*/
// This line is here because it didn't work on my local system
// I hope it doesn't break the checker
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "exec_parser.h"
#include <signal.h>
#include <sys/mman.h>
#include <unistd.h>
static so_exec_t *exec;
static struct sigaction default_handler;
static int page_size = 0;
static int fd;
static void handler(int signum, siginfo_t *info, void *context) {
if (signum != SIGSEGV)
return;
uintptr_t cur_addr = (uintptr_t)info->si_addr;
int page_no;
int flags = MAP_PRIVATE | MAP_FIXED;
int outside_mapped = 0;
// search for the segment
struct so_seg *found = NULL;
struct so_seg *temp = NULL;
for (int i = 0; i < exec->segments_no; ++i) {
temp = &exec->segments[i];
if (cur_addr >= temp->vaddr && cur_addr < temp->vaddr + temp->mem_size) {
// segment found
found = temp;
break;
}
}
char *mapped = (char *)found->data;
page_no = (cur_addr - found->vaddr) / page_size;
/*
call default page fault handler if page already mapped
or segment is not found
*/
if (!found || mapped[page_no] == 1) {
default_handler.sa_sigaction(signum, info, context);
return;
}
if (found->file_size < found->mem_size) {
if (found->file_size < page_no * page_size) {
// undefined
flags |= MAP_ANONYMOUS;
} else if ((page_no + 1) * page_size > found->file_size) {
// outside of file boundary
outside_mapped = (page_no + 1) * page_size - found->file_size;
}
}
char *p = mmap((void *)found->vaddr + page_no * page_size, page_size,
found->perm, flags, fd, found->offset + page_no * page_size);
if (p == MAP_FAILED)
exit(-ENOMEM);
if (outside_mapped != 0) {
// zero outside mapped bytes
uintptr_t temp =
found->vaddr + page_no * page_size + (page_size - outside_mapped);
memset((char *)temp, 0, outside_mapped);
}
// page is mapped
mapped[page_no] = 1;
}
int so_init_loader(void) {
// init page size
if (!page_size) {
page_size = getpagesize();
}
/* TODO: initialize on-demand loader */
// init handler
struct sigaction action;
action.sa_sigaction = handler;
sigemptyset(&action.sa_mask);
sigaddset(&action.sa_mask, SIGSEGV);
action.sa_flags = SA_SIGINFO;
int ret = sigaction(SIGSEGV, &action, &default_handler);
if (ret == -1) {
exit(-1);
}
return -1;
}
int so_execute(char *path, char *argv[]) {
fd = open(path, O_RDONLY);
if (fd == -1) {
exit(-ENOENT);
}
exec = so_parse_exec(path);
if (!exec) {
return -1;
}
int no_pages = 0;
struct so_seg *cur_segm = NULL;
for (int i = 0; i < exec->segments_no; ++i) {
cur_segm = &exec->segments[i];
no_pages = cur_segm->mem_size / page_size;
if (cur_segm->mem_size % page_size != 0) {
no_pages++;
}
char *ret = calloc(no_pages, 1);
if (!ret) {
return -1;
}
cur_segm->data = ret;
}
so_start_exec(exec, argv);
/*
free used memory
unmap every mapped page from every segment
*/
cur_segm = NULL;
no_pages = 0;
char *mapped = NULL;
int ret;
for (int i = 0; i < exec->segments_no; ++i) {
cur_segm = &exec->segments[i];
mapped = (char *)cur_segm->data;
no_pages = cur_segm->mem_size / page_size;
if (cur_segm->mem_size % page_size != 0) {
no_pages++;
}
for (int j = 0; j < no_pages; ++j) {
if (mapped[j] == 1) {
uintptr_t addr = cur_segm->vaddr + j * page_size;
// unmap page
ret = munmap((char *)addr, page_size);
if (ret == -1)
exit(-ENOMEM);
}
}
free(cur_segm->data);
}
return -1;
}