Remove trailing whitespaces.
[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     initial_state->regs.esp = (uintptr_t)address + 0x200000 - (MAX_PARAMETERS * sizeof(uintptr_t)) - sizeof(process_params_t);
53
54     *(process_params_t*)initial_state->regs.esp = *parameters;
55     push_to_stack(&initial_state->regs.esp, initial_state->regs.esp);
56
57     address = (void*)0x100000;
58
59     switch (header.midmag & 0xFFFF)
60     {
61     case OMAGIC:
62     {
63         data_address = (void*)((uintptr_t)address + header.text_size + sizeof(aout_header_t));
64         bss_address = (void*)((uintptr_t)address + header.text_size + header.data_size + sizeof(aout_header_t));
65
66         ret = syscall(SYSCALL_ALLOC_MEMORY,
67                       INVALID_HANDLE,
68                       &address,
69                       sizeof(aout_header_t) + header.text_size + header.data_size + header.bss_size,
70                       MEMORY_BLOCK_ACCESSIBLE | MEMORY_BLOCK_WRITABLE | MEMORY_BLOCK_EXECUTABLE | MEMORY_BLOCK_USERMODE | MEMORY_BLOCK_EVICTABLE);
71         if (ret != ERR_SUCCESS) return ret;
72
73         ret = syscall(SYSCALL_READ_FILE,
74                       file,
75                       address,
76                       0ULL,
77                       sizeof(aout_header_t) + header.text_size + header.data_size,
78                       &bytes_read);
79         if (ret != ERR_SUCCESS) syscall(SYSCALL_FREE_MEMORY, INVALID_HANDLE, address);
80
81         initial_state->regs.eip = (uintptr_t)address + header.entry_point + sizeof(aout_header_t);
82         break;
83     }
84
85     case NMAGIC:
86         return ERR_NOSYSCALL; // TODO
87
88     case ZMAGIC:
89     {
90         handle_t section;
91         data_address = (void*)((uintptr_t)address + PAGE_ALIGN_UP(header.text_size));
92         bss_address = (void*)((uintptr_t)address + PAGE_ALIGN_UP(header.text_size) + PAGE_ALIGN_UP(header.data_size));
93
94         ret = syscall(SYSCALL_CREATE_MEMORY_SECTION, filename, file, (size_t)filesize, MEMORY_SECTION_WRITABLE, &section);
95         if (ret != ERR_SUCCESS) return ret;
96
97         ret = syscall(SYSCALL_MAP_MEMORY_SECTION,
98                       INVALID_HANDLE,
99                       section,
100                       &address,
101                       0ULL,
102                       PAGE_ALIGN_UP(header.text_size + sizeof(aout_header_t)),
103                       MEMORY_BLOCK_ACCESSIBLE
104                       | MEMORY_BLOCK_WRITABLE
105                       | MEMORY_BLOCK_USERMODE
106                       | MEMORY_BLOCK_EXECUTABLE
107                       | MEMORY_BLOCK_EVICTABLE);
108         if (ret != ERR_SUCCESS)
109         {
110             syscall(SYSCALL_CLOSE_OBJECT, section);
111             return ret;
112         }
113
114         if (header.data_size > 0)
115         {
116             ret = syscall(SYSCALL_MAP_MEMORY_SECTION,
117                           INVALID_HANDLE,
118                           section,
119                           &data_address,
120                           (qword_t)(sizeof(aout_header_t) + header.text_size),
121                           PAGE_ALIGN_UP(header.data_size),
122                           MEMORY_BLOCK_ACCESSIBLE | MEMORY_BLOCK_WRITABLE | MEMORY_BLOCK_USERMODE | MEMORY_BLOCK_EVICTABLE);
123             if (ret != ERR_SUCCESS)
124             {
125                 syscall(SYSCALL_FREE_MEMORY, address);
126                 syscall(SYSCALL_CLOSE_OBJECT, section);
127                 return ret;
128             }
129         }
130
131         if (header.bss_size > 0)
132         {
133             ret = syscall(SYSCALL_ALLOC_MEMORY,
134                           INVALID_HANDLE,
135                           &bss_address,
136                           header.bss_size,
137                           MEMORY_BLOCK_ACCESSIBLE | MEMORY_BLOCK_WRITABLE | MEMORY_BLOCK_USERMODE | MEMORY_BLOCK_EVICTABLE);
138             if (ret != ERR_SUCCESS)
139             {
140                 syscall(SYSCALL_FREE_MEMORY, address);
141                 if (header.data_size > 0) syscall(SYSCALL_FREE_MEMORY, data_address);
142                 syscall(SYSCALL_CLOSE_OBJECT, section);
143                 return ret;
144             }
145         }
146
147         syscall(SYSCALL_CLOSE_OBJECT, section);
148         initial_state->regs.eip = (uintptr_t)address + header.entry_point + sizeof(aout_header_t);
149         break;
150     }
151
152     default:
153         return ERR_INVALID;
154     }
155
156     uintptr_t num_text_relocs = header.text_reloc_size / sizeof(aout_reloc_t);
157     uintptr_t num_data_relocs = header.data_reloc_size / sizeof(aout_reloc_t);
158     uintptr_t num_symbols = header.sym_size / sizeof(aout_symbol_t);
159     aout_reloc_t *text_relocs = NULL;
160     aout_reloc_t *data_relocs = NULL;
161     aout_symbol_t *symbols = NULL;
162     uintptr_t string_table_size = 0;
163     char *string_table = NULL;
164
165     if (header.text_reloc_size)
166     {
167         text_relocs = (aout_reloc_t*)malloc(header.text_reloc_size);
168         if (text_relocs == NULL) return ERR_NOMEMORY;
169
170         ret = syscall(SYSCALL_READ_FILE,
171                       file,
172                       text_relocs,
173                       (qword_t)(sizeof(aout_header_t) + header.text_size + header.data_size),
174                       header.text_reloc_size,
175                       &bytes_read);
176         if (ret != ERR_SUCCESS) goto cleanup;
177     }
178
179     if (header.data_reloc_size)
180     {
181         data_relocs = (aout_reloc_t*)malloc(header.data_reloc_size);
182         if (data_relocs == NULL) return ERR_NOMEMORY;
183
184         ret = syscall(SYSCALL_READ_FILE,
185                       file,
186                       data_relocs,
187                       (qword_t)(sizeof(aout_header_t)
188                                 + header.text_size
189                                 + header.data_size
190                                 + header.text_reloc_size),
191                       header.data_reloc_size,
192                       &bytes_read);
193         if (ret != ERR_SUCCESS) goto cleanup;
194     }
195
196     if (header.sym_size)
197     {
198         symbols = (aout_symbol_t*)malloc(header.sym_size);
199         if (symbols == NULL) return ERR_NOMEMORY;
200
201         ret = syscall(SYSCALL_READ_FILE,
202                       file,
203                       symbols,
204                       (qword_t)(sizeof(aout_header_t)
205                                 + header.text_size
206                                 + header.data_size
207                                 + header.text_reloc_size
208                                 + header.data_reloc_size),
209                       header.sym_size,
210                       &bytes_read);
211         if (ret != ERR_SUCCESS) goto cleanup;
212     }
213
214     ret = syscall(SYSCALL_READ_FILE,
215                   file,
216                   &string_table_size,
217                   (qword_t)(sizeof(aout_header_t)
218                             + header.text_size
219                             + header.data_size
220                             + header.sym_size
221                             + header.text_reloc_size
222                             + header.data_reloc_size),
223                   sizeof(string_table_size),
224                   &bytes_read);
225     if (ret != ERR_SUCCESS) goto cleanup;
226
227     string_table = malloc(string_table_size);
228     if (string_table == NULL)
229     {
230         ret = ERR_NOMEMORY;
231         goto cleanup;
232     }
233
234     ret = syscall(SYSCALL_READ_FILE,
235                   file,
236                   string_table,
237                   (qword_t)(sizeof(aout_header_t)
238                             + header.text_size
239                             + header.data_size
240                             + header.sym_size
241                             + header.text_reloc_size
242                             + header.data_reloc_size),
243                   string_table_size,
244                   &bytes_read);
245     if (ret != ERR_SUCCESS) goto cleanup;
246
247     for (i = 0; i < num_symbols; i++)
248     {
249         symbols[i].name = (char*)((uintptr_t)string_table + symbols[i].name_offset);
250
251         switch (AOUT_TYPE_FIELD(symbols[i].type))
252         {
253         case AOUT_SYM_UNDEFINED:
254             // TODO: Resolve symbol
255             break;
256
257         case AOUT_SYM_TEXT:
258             symbols[i].value += (uintptr_t)address + sizeof(aout_header_t);
259             break;
260
261         case AOUT_SYM_DATA:
262             symbols[i].value += (uintptr_t)data_address;
263             break;
264
265         case AOUT_SYM_BSS:
266             symbols[i].value += (uintptr_t)bss_address;
267             break;
268         }
269     }
270
271     for (i = 0; i < num_text_relocs + num_data_relocs; i++)
272     {
273         aout_reloc_t *reloc = (i < num_text_relocs) ? &text_relocs[i] : &data_relocs[i];
274         uintptr_t base_address = (i < num_text_relocs) ? ((uintptr_t)address + sizeof(aout_header_t)) : (uintptr_t)data_address;
275         dword_t offset;
276
277         if (!reloc->external)
278         {
279             offset = (uintptr_t)address + sizeof(aout_header_t);
280         }
281         else
282         {
283             if (reloc->symbol_num >= num_symbols)
284             {
285                 ret = ERR_INVALID;
286                 goto cleanup;
287             }
288
289             offset = symbols[reloc->symbol_num].value;
290         }
291
292         switch (reloc->length)
293         {
294         case 0:
295             *(byte_t*)(base_address + reloc->address) += offset;
296             break;
297         case 1:
298             *(word_t*)(base_address + reloc->address) += offset;
299             break;
300         case 2:
301             *(dword_t*)(base_address + reloc->address) += offset;
302             break;
303         }
304     }
305
306     if ((header.midmag & 0xFFFF) == ZMAGIC)
307     {
308         syscall(SYSCALL_SET_MEMORY_FLAGS,
309                 INVALID_HANDLE,
310                 address,
311                 MEMORY_BLOCK_ACCESSIBLE | MEMORY_BLOCK_USERMODE | MEMORY_BLOCK_EXECUTABLE | MEMORY_BLOCK_EVICTABLE);
312     }
313
314 cleanup:
315     if (symbols) free(symbols);
316     if (string_table) free(string_table);
317     return ret;
318 }