Move the stack initialization code to process.c
[monolithium.git] / kernel / src / exec / aout.c
1 /*
2  * aout.c
3  *
4  * Copyright (C) 2016 Aleksandar Andrejevic <theflash@sdf.lonestar.org>
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Affero General Public License as
8  * published by the Free Software Foundation, either version 3 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Affero General Public License for more details.
15  *
16  * You should have received a copy of the GNU Affero General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <exec/aout.h>
21 #include <memory.h>
22 #include <heap.h>
23 #include <syscalls.h>
24
25 dword_t load_aout(handle_t file, process_params_t *parameters, thread_state_t *initial_state)
26 {
27     aout_header_t header;
28     size_t bytes_read;
29     char filename[MAX_PATH];
30     qword_t filesize;
31     uintptr_t i;
32
33     dword_t ret = syscall(SYSCALL_QUERY_FILE, file, FILE_INFO_NAME, filename, sizeof(filename));
34     if (ret != ERR_SUCCESS) return ret;
35
36     ret = syscall(SYSCALL_QUERY_FILE, file, FILE_INFO_SIZE, &filesize, sizeof(filesize));
37     if (ret != ERR_SUCCESS) return ret;
38
39     ret = syscall(SYSCALL_READ_FILE, file, &header, 0ULL, sizeof(aout_header_t), &bytes_read);
40     if (ret != ERR_SUCCESS) return ret;
41
42     void *address = (void*)0x7FE00000;
43     void *data_address;
44     void *bss_address;
45
46     ret = syscall(SYSCALL_ALLOC_MEMORY,
47                   INVALID_HANDLE,
48                   &address,
49                   0x200000,
50                   MEMORY_BLOCK_ACCESSIBLE | MEMORY_BLOCK_WRITABLE | MEMORY_BLOCK_USERMODE | MEMORY_BLOCK_EVICTABLE);
51     if (ret != ERR_SUCCESS) return ret;
52
53     initial_state->regs.esp = (uintptr_t)address + 0x200000;
54     init_user_stack((uintptr_t*)&initial_state->regs.esp, parameters);
55
56     address = (void*)0x100000;
57
58     switch (header.midmag & 0xFFFF)
59     {
60     case OMAGIC:
61     {
62         data_address = (void*)((uintptr_t)address + header.text_size + sizeof(aout_header_t));
63         bss_address = (void*)((uintptr_t)address + header.text_size + header.data_size + sizeof(aout_header_t));
64
65         ret = syscall(SYSCALL_ALLOC_MEMORY,
66                       INVALID_HANDLE,
67                       &address,
68                       sizeof(aout_header_t) + header.text_size + header.data_size + header.bss_size,
69                       MEMORY_BLOCK_ACCESSIBLE | MEMORY_BLOCK_WRITABLE | MEMORY_BLOCK_EXECUTABLE | MEMORY_BLOCK_USERMODE | MEMORY_BLOCK_EVICTABLE);
70         if (ret != ERR_SUCCESS) return ret;
71
72         ret = syscall(SYSCALL_READ_FILE,
73                       file,
74                       address,
75                       0ULL,
76                       sizeof(aout_header_t) + header.text_size + header.data_size,
77                       &bytes_read);
78         if (ret != ERR_SUCCESS) syscall(SYSCALL_FREE_MEMORY, INVALID_HANDLE, address);
79
80         initial_state->regs.eip = (uintptr_t)address + header.entry_point + sizeof(aout_header_t);
81         break;
82     }
83
84     case NMAGIC:
85         return ERR_NOSYSCALL; // TODO
86
87     case ZMAGIC:
88     {
89         handle_t section;
90         data_address = (void*)((uintptr_t)address + PAGE_ALIGN_UP(header.text_size));
91         bss_address = (void*)((uintptr_t)address + PAGE_ALIGN_UP(header.text_size) + PAGE_ALIGN_UP(header.data_size));
92
93         ret = syscall(SYSCALL_CREATE_MEMORY_SECTION, filename, file, (size_t)filesize, MEMORY_SECTION_WRITABLE, &section);
94         if (ret != ERR_SUCCESS) return ret;
95
96         ret = syscall(SYSCALL_MAP_MEMORY_SECTION,
97                       INVALID_HANDLE,
98                       section,
99                       &address,
100                       0ULL,
101                       PAGE_ALIGN_UP(header.text_size + sizeof(aout_header_t)),
102                       MEMORY_BLOCK_ACCESSIBLE
103                       | MEMORY_BLOCK_WRITABLE
104                       | MEMORY_BLOCK_USERMODE
105                       | MEMORY_BLOCK_EXECUTABLE
106                       | MEMORY_BLOCK_EVICTABLE);
107         if (ret != ERR_SUCCESS)
108         {
109             syscall(SYSCALL_CLOSE_OBJECT, section);
110             return ret;
111         }
112
113         if (header.data_size > 0)
114         {
115             ret = syscall(SYSCALL_MAP_MEMORY_SECTION,
116                           INVALID_HANDLE,
117                           section,
118                           &data_address,
119                           (qword_t)(sizeof(aout_header_t) + header.text_size),
120                           PAGE_ALIGN_UP(header.data_size),
121                           MEMORY_BLOCK_ACCESSIBLE | MEMORY_BLOCK_WRITABLE | MEMORY_BLOCK_USERMODE | MEMORY_BLOCK_EVICTABLE);
122             if (ret != ERR_SUCCESS)
123             {
124                 syscall(SYSCALL_FREE_MEMORY, address);
125                 syscall(SYSCALL_CLOSE_OBJECT, section);
126                 return ret;
127             }
128         }
129
130         if (header.bss_size > 0)
131         {
132             ret = syscall(SYSCALL_ALLOC_MEMORY,
133                           INVALID_HANDLE,
134                           &bss_address,
135                           header.bss_size,
136                           MEMORY_BLOCK_ACCESSIBLE | MEMORY_BLOCK_WRITABLE | MEMORY_BLOCK_USERMODE | MEMORY_BLOCK_EVICTABLE);
137             if (ret != ERR_SUCCESS)
138             {
139                 syscall(SYSCALL_FREE_MEMORY, address);
140                 if (header.data_size > 0) syscall(SYSCALL_FREE_MEMORY, data_address);
141                 syscall(SYSCALL_CLOSE_OBJECT, section);
142                 return ret;
143             }
144         }
145
146         syscall(SYSCALL_CLOSE_OBJECT, section);
147         initial_state->regs.eip = (uintptr_t)address + header.entry_point + sizeof(aout_header_t);
148         break;
149     }
150
151     default:
152         return ERR_INVALID;
153     }
154
155     uintptr_t num_text_relocs = header.text_reloc_size / sizeof(aout_reloc_t);
156     uintptr_t num_data_relocs = header.data_reloc_size / sizeof(aout_reloc_t);
157     uintptr_t num_symbols = header.sym_size / sizeof(aout_symbol_t);
158     aout_reloc_t *text_relocs = NULL;
159     aout_reloc_t *data_relocs = NULL;
160     aout_symbol_t *symbols = NULL;
161     uintptr_t string_table_size = 0;
162     char *string_table = NULL;
163
164     if (header.text_reloc_size)
165     {
166         text_relocs = (aout_reloc_t*)malloc(header.text_reloc_size);
167         if (text_relocs == NULL) return ERR_NOMEMORY;
168
169         ret = syscall(SYSCALL_READ_FILE,
170                       file,
171                       text_relocs,
172                       (qword_t)(sizeof(aout_header_t) + header.text_size + header.data_size),
173                       header.text_reloc_size,
174                       &bytes_read);
175         if (ret != ERR_SUCCESS) goto cleanup;
176     }
177
178     if (header.data_reloc_size)
179     {
180         data_relocs = (aout_reloc_t*)malloc(header.data_reloc_size);
181         if (data_relocs == NULL) return ERR_NOMEMORY;
182
183         ret = syscall(SYSCALL_READ_FILE,
184                       file,
185                       data_relocs,
186                       (qword_t)(sizeof(aout_header_t)
187                                 + header.text_size
188                                 + header.data_size
189                                 + header.text_reloc_size),
190                       header.data_reloc_size,
191                       &bytes_read);
192         if (ret != ERR_SUCCESS) goto cleanup;
193     }
194
195     if (header.sym_size)
196     {
197         symbols = (aout_symbol_t*)malloc(header.sym_size);
198         if (symbols == NULL) return ERR_NOMEMORY;
199
200         ret = syscall(SYSCALL_READ_FILE,
201                       file,
202                       symbols,
203                       (qword_t)(sizeof(aout_header_t)
204                                 + header.text_size
205                                 + header.data_size
206                                 + header.text_reloc_size
207                                 + header.data_reloc_size),
208                       header.sym_size,
209                       &bytes_read);
210         if (ret != ERR_SUCCESS) goto cleanup;
211     }
212
213     ret = syscall(SYSCALL_READ_FILE,
214                   file,
215                   &string_table_size,
216                   (qword_t)(sizeof(aout_header_t)
217                             + header.text_size
218                             + header.data_size
219                             + header.sym_size
220                             + header.text_reloc_size
221                             + header.data_reloc_size),
222                   sizeof(string_table_size),
223                   &bytes_read);
224     if (ret != ERR_SUCCESS) goto cleanup;
225
226     string_table = malloc(string_table_size);
227     if (string_table == NULL)
228     {
229         ret = ERR_NOMEMORY;
230         goto cleanup;
231     }
232
233     ret = syscall(SYSCALL_READ_FILE,
234                   file,
235                   string_table,
236                   (qword_t)(sizeof(aout_header_t)
237                             + header.text_size
238                             + header.data_size
239                             + header.sym_size
240                             + header.text_reloc_size
241                             + header.data_reloc_size),
242                   string_table_size,
243                   &bytes_read);
244     if (ret != ERR_SUCCESS) goto cleanup;
245
246     for (i = 0; i < num_symbols; i++)
247     {
248         symbols[i].name = (char*)((uintptr_t)string_table + symbols[i].name_offset);
249
250         switch (AOUT_TYPE_FIELD(symbols[i].type))
251         {
252         case AOUT_SYM_UNDEFINED:
253             // TODO: Resolve symbol
254             break;
255
256         case AOUT_SYM_TEXT:
257             symbols[i].value += (uintptr_t)address + sizeof(aout_header_t);
258             break;
259
260         case AOUT_SYM_DATA:
261             symbols[i].value += (uintptr_t)data_address;
262             break;
263
264         case AOUT_SYM_BSS:
265             symbols[i].value += (uintptr_t)bss_address;
266             break;
267         }
268     }
269
270     for (i = 0; i < num_text_relocs + num_data_relocs; i++)
271     {
272         aout_reloc_t *reloc = (i < num_text_relocs) ? &text_relocs[i] : &data_relocs[i];
273         uintptr_t base_address = (i < num_text_relocs) ? ((uintptr_t)address + sizeof(aout_header_t)) : (uintptr_t)data_address;
274         dword_t offset;
275
276         if (!reloc->external)
277         {
278             offset = (uintptr_t)address + sizeof(aout_header_t);
279         }
280         else
281         {
282             if (reloc->symbol_num >= num_symbols)
283             {
284                 ret = ERR_INVALID;
285                 goto cleanup;
286             }
287
288             offset = symbols[reloc->symbol_num].value;
289         }
290
291         switch (reloc->length)
292         {
293         case 0:
294             *(byte_t*)(base_address + reloc->address) += offset;
295             break;
296         case 1:
297             *(word_t*)(base_address + reloc->address) += offset;
298             break;
299         case 2:
300             *(dword_t*)(base_address + reloc->address) += offset;
301             break;
302         }
303     }
304
305     if ((header.midmag & 0xFFFF) == ZMAGIC)
306     {
307         syscall(SYSCALL_SET_MEMORY_FLAGS,
308                 INVALID_HANDLE,
309                 address,
310                 MEMORY_BLOCK_ACCESSIBLE | MEMORY_BLOCK_USERMODE | MEMORY_BLOCK_EXECUTABLE | MEMORY_BLOCK_EVICTABLE);
311     }
312
313 cleanup:
314     if (symbols) free(symbols);
315     if (string_table) free(string_table);
316     return ret;
317 }