e5e64fe30d22e0174c918bbb3b5f6399451d02dd
[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 #include <lunaix/syscall_utils.h>
11
12 #include <klibc/string.h>
13
14 size_t
15 exec_str_size(const char** str_arr, size_t* length)
16 {
17     if (!str_arr) {
18         *length = 0;
19         return 0;
20     }
21
22     const char* chr = *str_arr;
23     size_t sz = 0, len = 0;
24
25     while (chr) {
26         sz += strlen(chr);
27         len++;
28
29         chr = *(str_arr + sz);
30     }
31
32     *length = len;
33     return sz + 1;
34 }
35
36 void
37 __heap_copied(struct mm_region* region)
38 {
39     mm_index((void**)&region->proc_vms->heap, region);
40 }
41
42 int
43 __exec_remap_heap(struct ld_param* param, struct proc_mm* pvms)
44 {
45     if (pvms->heap) {
46         mem_unmap_region(param->vms_mnt, pvms->heap);
47     }
48
49     struct mmap_param map_param = { .pvms = pvms,
50                                     .vms_mnt = param->vms_mnt,
51                                     .flags = MAP_ANON | MAP_PRIVATE,
52                                     .type = REGION_TYPE_HEAP,
53                                     .proct = PROT_READ | PROT_WRITE,
54                                     .mlen = PG_SIZE };
55     int status = 0;
56     struct mm_region* heap;
57     if ((status = mem_map(NULL, &heap, param->info.end, NULL, &map_param))) {
58         param->status |= LD_STAT_FKUP;
59         return status;
60     }
61
62     heap->region_copied = __heap_copied;
63     mm_index((void**)&pvms->heap, heap);
64
65     return status;
66 }
67
68 int
69 exec_load(struct ld_param* param,
70           struct v_file* executable,
71           const char** argv,
72           const char** envp)
73 {
74     int errno = 0;
75
76     size_t argv_len, envp_len;
77     size_t sz_argv = exec_str_size(argv, &argv_len);
78     size_t sz_envp = exec_str_size(envp, &envp_len);
79     size_t total_sz = ROUNDUP(sz_argv + sz_envp, PG_SIZE);
80
81     if (total_sz / PG_SIZE > MAX_VAR_PAGES) {
82         errno = E2BIG;
83         goto done;
84     }
85
86     if ((errno = elf_load(param, executable))) {
87         goto done;
88     }
89
90     struct proc_mm* pvms = &param->proc->mm;
91     struct mmap_param map_vars = { .pvms = pvms,
92                                    .vms_mnt = param->vms_mnt,
93                                    .flags = MAP_ANON | MAP_PRIVATE | MAP_FIXED,
94                                    .type = REGION_TYPE_VARS,
95                                    .proct = PROT_READ,
96                                    .mlen = MAX_VAR_PAGES * PG_SIZE };
97
98     void* mapped;
99
100     if ((errno = __exec_remap_heap(param, pvms))) {
101         goto done;
102     }
103
104     if ((errno = mem_map(&mapped, NULL, UMMAP_END, NULL, &map_vars))) {
105         goto done;
106     }
107
108     if (param->vms_mnt == VMS_SELF) {
109         // we are loading executable into current addr space
110
111         // make some handy infos available to user space
112         ptr_t arg_start = mapped + sizeof(struct usr_exec_param);
113         if (argv)
114             memcpy(arg_start, (void*)argv, sz_argv);
115         if (envp)
116             memcpy(arg_start + sz_argv, (void*)envp, sz_envp);
117
118         ptr_t* ustack = (ptr_t*)USTACK_TOP;
119         struct usr_exec_param* exec_param = mapped;
120
121         ustack[-1] = (ptr_t)mapped;
122         param->info.stack_top = &ustack[-1];
123
124         *exec_param = (struct usr_exec_param){ .argc = argv_len,
125                                                .argv = arg_start,
126                                                .envc = envp_len,
127                                                .envp = arg_start + sz_argv,
128                                                .info = param->info };
129     } else {
130         // TODO need to find a way to inject argv and envp remotely
131         //      this is for the support of kernel level implementation of
132         //      posix_spawn
133         fail("not implemented");
134     }
135
136     param->info.entry = param->info.ehdr_out.e_entry;
137 done:
138     return errno;
139 }
140
141 int
142 exec_load_byname(struct ld_param* param,
143                  const char* filename,
144                  const char** argv,
145                  const char** envp)
146 {
147     int errno = 0;
148     struct v_dnode* dnode;
149     struct v_file* file;
150
151     if ((errno = vfs_walk_proc(filename, &dnode, NULL, 0))) {
152         goto done;
153     }
154
155     if ((errno = vfs_open(dnode, &file))) {
156         goto done;
157     }
158
159     if ((errno = exec_load(param, file, argv, envp))) {
160         vfs_pclose(file, __current->pid);
161     }
162
163 done:
164     return errno;
165 }
166
167 __DEFINE_LXSYSCALL3(int,
168                     execve,
169                     const char*,
170                     filename,
171                     const char*,
172                     argv[],
173                     const char*,
174                     envp[])
175 {
176     int errno = 0;
177     struct ld_param ldparam;
178     ld_create_param(&ldparam, __current, VMS_SELF);
179
180     if ((errno = exec_load_byname(&ldparam, filename, argv, envp))) {
181         if ((ldparam.status & LD_STAT_FKUP)) {
182             // we fucked up our address space.
183             terminate_proc(11451);
184             schedule();
185             fail("should not reach");
186         }
187         goto done;
188     }
189
190     volatile struct exec_param* execp = __current->intr_ctx.execp;
191     execp->esp = ldparam.info.stack_top;
192     execp->eip = ldparam.info.entry;
193
194     // we will jump to new entry point (_u_start) upon syscall's
195     // return so execve 'will not return' from the perspective of it's invoker
196
197 done:
198     return DO_STATUS(errno);
199 }