edit readme
[lunaix-os.git] / lunaix-os / kernel / loader / exec.c
1 #include <arch/abi.h>
2 #include <lunaix/elf.h>
3 #include <lunaix/exec.h>
4 #include <lunaix/fs.h>
5 #include <lunaix/mm/mmap.h>
6 #include <lunaix/mm/valloc.h>
7 #include <lunaix/mm/vmm.h>
8 #include <lunaix/process.h>
9 #include <lunaix/spike.h>
10 #include <lunaix/status.h>
11 #include <lunaix/syscall.h>
12 #include <lunaix/syscall_utils.h>
13
14 #include <klibc/string.h>
15
16 void
17 exec_container(struct exec_container* param, struct proc_info* proc, ptr_t vms)
18 {
19     *param = (struct exec_container){ .proc = proc,
20                                       .vms_mnt = vms,
21                                       .executable = { .container = param } };
22 }
23
24 size_t
25 exec_str_size(const char** str_arr, size_t* length)
26 {
27     if (!str_arr) {
28         *length = 0;
29         return 0;
30     }
31
32     const char* chr = *str_arr;
33     size_t sz = 0, len = 0;
34
35     while (chr) {
36         sz += strlen(chr);
37         len++;
38
39         chr = *(str_arr++);
40     }
41
42     *length = len;
43     return sz + sizeof(char*);
44 }
45
46 // externed from mm/dmm.c
47 extern int
48 create_heap(struct proc_mm* pvms, ptr_t addr);
49
50 int
51 exec_load(struct exec_container* container,
52           struct v_file* executable,
53           const char** argv,
54           const char** envp)
55 {
56     int errno = 0;
57     char* ldpath = NULL;
58
59     size_t argv_len, envp_len;
60     size_t sz_argv = exec_str_size(argv, &argv_len);
61     size_t sz_envp = exec_str_size(envp, &envp_len);
62     size_t var_sz = ROUNDUP(sz_envp, PG_SIZE);
63
64     char* argv_extra[2] = { executable->dnode->name.value, 0 };
65
66     if (var_sz / PG_SIZE > MAX_VAR_PAGES) {
67         errno = E2BIG;
68         goto done;
69     }
70
71     struct elf32 elf;
72
73     if ((errno = elf32_openat(&elf, executable))) {
74         goto done;
75     }
76
77     if (!elf32_check_exec(&elf)) {
78         errno = ENOEXEC;
79         goto done;
80     }
81
82     ldpath = valloc(512);
83     errno = elf32_find_loader(&elf, ldpath, 512);
84
85     if (errno < 0) {
86         vfree(ldpath);
87         goto done;
88     }
89
90     if (errno != NO_LOADER) {
91         // TODO load loader
92         argv_extra[1] = ldpath;
93
94         // close old elf
95         if ((errno = elf32_close(&elf))) {
96             goto done;
97         }
98
99         // open the loader instead
100         if ((errno = elf32_open(&elf, ldpath))) {
101             goto done;
102         }
103
104         // Is this the valid loader?
105         if (!elf32_static_linked(&elf) || !elf32_check_exec(&elf)) {
106             errno = ELIBBAD;
107             goto done_close_elf32;
108         }
109
110         // TODO: relocate loader
111     }
112
113     if ((errno = elf32_load(&container->executable, &elf))) {
114         goto done_close_elf32;
115     }
116
117     struct proc_mm* pvms = &container->proc->mm;
118
119     // A dedicated place for process variables (e.g. envp)
120     struct mmap_param map_vars = { .pvms = pvms,
121                                    .vms_mnt = container->vms_mnt,
122                                    .flags = MAP_ANON | MAP_PRIVATE | MAP_FIXED,
123                                    .type = REGION_TYPE_VARS,
124                                    .proct = PROT_READ,
125                                    .mlen = MAX_VAR_PAGES * PG_SIZE };
126
127     void* mapped;
128
129     if (pvms->heap) {
130         mem_unmap_region(container->vms_mnt, pvms->heap);
131         pvms->heap = NULL;
132     }
133
134     if (!argv_extra[1]) {
135         // If loading a statically linked file, then heap remapping we can do,
136         // otherwise delayed.
137         create_heap(container->vms_mnt, PG_ALIGN(container->executable.end));
138     }
139
140     if ((errno = mem_map(&mapped, NULL, UMMAP_END, NULL, &map_vars))) {
141         goto done_close_elf32;
142     }
143
144     if (container->vms_mnt == VMS_SELF) {
145         // we are loading executable into current addr space
146
147         // make some handy infos available to user space
148         if (envp)
149             memcpy(mapped, (void*)envp, sz_envp);
150
151         void* ustack = (void*)USTACK_TOP;
152
153         if (argv) {
154             ustack = (void*)((ptr_t)ustack - sz_argv);
155             memcpy(ustack, (void*)argv, sz_argv);
156         }
157
158         for (size_t i = 0; i < 2 && argv_extra[i]; i++, argv_len++) {
159             char* extra_arg = argv_extra[i];
160             size_t str_len = strlen(extra_arg);
161
162             ustack = (void*)((ptr_t)ustack - str_len);
163             memcpy(ustack, (void*)extra_arg, str_len);
164         }
165
166         // four args (arg{c|v}, env{c|p}) for main
167         struct uexec_param* exec_param = &((struct uexec_param*)ustack)[-1];
168
169         container->stack_top = (ptr_t)exec_param;
170
171         *exec_param = (struct uexec_param){
172             .argc = argv_len, .argv = ustack, .envc = envp_len, .envp = mapped
173         };
174
175     } else {
176         /*
177             TODO need to find a way to inject argv and envp remotely
178                  this is for the support of kernel level implementation of
179                  posix_spawn
180
181             IDEA
182                 1. Allocate a orphaned physical page (i.e., do not belong to any
183                 VMA)
184                 2. Mounted to a temporary mount point in current VMA, (i.e.,
185                 PG_MOUNT_*)
186                 3. Do setup there.
187                 4. Unmount then mounted to the foreign VMA as the first stack
188                 page.
189         */
190         fail("not implemented");
191     }
192
193 done_close_elf32:
194     elf32_close(&elf);
195 done:
196     vfree_safe(ldpath);
197     return errno;
198 }
199
200 int
201 exec_load_byname(struct exec_container* container,
202                  const char* filename,
203                  const char** argv,
204                  const char** envp)
205 {
206     int errno = 0;
207     struct v_dnode* dnode;
208     struct v_file* file;
209
210     if ((errno = vfs_walk_proc(filename, &dnode, NULL, 0))) {
211         goto done;
212     }
213
214     if ((errno = vfs_open(dnode, &file))) {
215         goto done;
216     }
217
218     errno = exec_load(container, file, argv, envp);
219
220 done:
221     return errno;
222 }
223
224 int
225 exec_kexecve(const char* filename, const char* argv[], const char* envp)
226 {
227     int errno = 0;
228     struct exec_container container;
229     exec_container(&container, __current, VMS_SELF);
230
231     errno = exec_load_byname(&container, filename, argv, envp);
232
233     if (errno) {
234         return errno;
235     }
236
237     j_usr(container.stack_top, container.entry);
238 }
239
240 __DEFINE_LXSYSCALL3(int,
241                     execve,
242                     const char*,
243                     filename,
244                     const char*,
245                     argv[],
246                     const char*,
247                     envp[])
248 {
249     int errno = 0;
250     struct exec_container container;
251     exec_container(&container, __current, VMS_SELF);
252
253     if (!(errno = exec_load_byname(&container, filename, argv, envp))) {
254         goto done;
255     }
256
257     // we will jump to new entry point (_u_start) upon syscall's
258     // return so execve 'will not return' from the perspective of it's invoker
259     volatile struct exec_param* execp = __current->intr_ctx.execp;
260     execp->esp = container.stack_top;
261     execp->eip = container.entry;
262
263 done:
264     // set return value
265     store_retval(DO_STATUS(errno));
266
267     // Always yield the process that want execve!
268     schedule();
269
270     // this will never get executed!
271     return -1;
272 }