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