6c3349537a2a93afbde80689da62dc5a0a2717cc
[lunaix-os.git] / lunaix-os / kernel / loader / exec.c
1 #include <lunaix/elf.h>
2 #include <lunaix/fs.h>
3 #include <lunaix/ld.h>
4 #include <lunaix/mm/mmap.h>
5 #include <lunaix/mm/vmm.h>
6 #include <lunaix/process.h>
7 #include <lunaix/spike.h>
8 #include <lunaix/status.h>
9 #include <lunaix/syscall.h>
10
11 #include <klibc/string.h>
12
13 size_t
14 exec_str_size(const char** str_arr, size_t* length)
15 {
16     const char* chr = *str_arr;
17     size_t sz = 0, len = 0;
18
19     while (chr) {
20         sz += strlen(chr);
21         len++;
22
23         chr = *(str_arr + sz);
24     }
25
26     *length = len;
27     return sz + 1;
28 }
29
30 int
31 exec_loadto(struct ld_param* param,
32             struct v_file* executable,
33             const char** argv,
34             const char** envp)
35 {
36     int errno = 0;
37
38     size_t argv_len, envp_len;
39     size_t sz_argv = exec_str_size(argv, &argv_len);
40     size_t sz_envp = exec_str_size(envp, &envp_len);
41     size_t total_sz = ROUNDUP(sz_argv + sz_envp, PG_SIZE);
42
43     if (total_sz / PG_SIZE > MAX_VAR_PAGES) {
44         errno = E2BIG;
45         goto done;
46     }
47
48     if ((errno = elf_load(param, executable))) {
49         goto done;
50     }
51
52     struct mmap_param map_param = { .regions = &param->proc->mm.regions,
53                                     .vms_mnt = param->vms_mnt,
54                                     .flags = MAP_ANON | MAP_PRIVATE | MAP_FIXED,
55                                     .type = REGION_TYPE_VARS,
56                                     .proct = PROT_READ,
57                                     .length = MAX_VAR_PAGES * PG_SIZE };
58
59     void* mapped;
60     isr_param* intr_ctx = &param->proc->intr_ctx;
61
62     // TODO reinitialize heap
63
64     if (param->vms_mnt == VMS_SELF) {
65         // we are loading executable into current addr space
66         if ((errno = mem_map(&mapped, NULL, UMMAP_END, NULL, &map_param))) {
67             goto done;
68         }
69
70         memcpy(mapped, (void*)argv, sz_argv);
71         memcpy(mapped + sz_argv, (void*)envp, sz_envp);
72
73         ptr_t* ustack = (void*)USTACK_TOP;
74         size_t* argc = &((size_t*)&ustack[-1])[-1];
75
76         ustack[-1] = (ptr_t)mapped;
77         *argc = argv_len;
78
79         // TODO handle envp.
80
81         intr_ctx->esp = argc;
82     } else {
83         // TODO need to find a way to inject argv and envp remotely
84         fail("not implemented");
85     }
86
87     intr_ctx->eip = param->ehdr_out.e_entry;
88     // we will jump to new entry point upon syscall's return
89     // so execve will not return from the perspective of it's invoker
90
91 done:
92     return errno;
93 }
94
95 __DEFINE_LXSYSCALL3(int,
96                     execve,
97                     const char*,
98                     filename,
99                     const char*,
100                     argv[],
101                     const char*,
102                     envp[])
103 {
104     int errno = 0;
105     struct v_dnode* dnode;
106     struct v_file* file;
107
108     if ((errno = vfs_walk_proc(filename, &dnode, NULL, 0))) {
109         goto done;
110     }
111
112     if ((errno = vfs_open(dnode, &file))) {
113         goto done;
114     }
115
116     struct ld_param ldparam;
117     ld_create_param(&ldparam, __current, VMS_SELF);
118
119     if ((errno = exec_loadto(&ldparam, file, argv, envp))) {
120         vfs_pclose(file, __current->pid);
121
122         if ((ldparam.status & LD_STAT_FKUP)) {
123             // we fucked up our address space.
124             terminate_proc(11451);
125             schedule();
126             fail("should not reach");
127         }
128     }
129
130 done:
131     return errno;
132 }