331cacca5283df528a73b79b2e1d0dca65275061
[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         fail("not implemented");
132     }
133
134     param->info.entry = param->info.ehdr_out.e_entry;
135 done:
136     return errno;
137 }
138
139 int
140 exec_load_byname(struct ld_param* param,
141                  const char* filename,
142                  const char** argv,
143                  const char** envp)
144 {
145     int errno = 0;
146     struct v_dnode* dnode;
147     struct v_file* file;
148
149     if ((errno = vfs_walk_proc(filename, &dnode, NULL, 0))) {
150         goto done;
151     }
152
153     if ((errno = vfs_open(dnode, &file))) {
154         goto done;
155     }
156
157     if ((errno = exec_load(param, file, argv, envp))) {
158         vfs_pclose(file, __current->pid);
159     }
160
161 done:
162     return errno;
163 }
164
165 __DEFINE_LXSYSCALL3(int,
166                     execve,
167                     const char*,
168                     filename,
169                     const char*,
170                     argv[],
171                     const char*,
172                     envp[])
173 {
174     int errno = 0;
175     struct ld_param ldparam;
176     ld_create_param(&ldparam, __current, VMS_SELF);
177
178     if ((errno = exec_load_byname(&ldparam, filename, argv, envp))) {
179         if ((ldparam.status & LD_STAT_FKUP)) {
180             // we fucked up our address space.
181             terminate_proc(11451);
182             schedule();
183             fail("should not reach");
184         }
185         goto done;
186     }
187
188     volatile struct exec_param* execp = __current->intr_ctx.execp;
189     execp->esp = ldparam.info.stack_top;
190     execp->eip = ldparam.info.entry;
191
192     // we will jump to new entry point (_u_start) upon syscall's
193     // return so execve 'will not return' from the perspective of it's invoker
194
195 done:
196     return DO_STATUS(errno);
197 }