]>
Commit | Line | Data |
---|---|---|
1c79356b | 1 | /* |
8ad349bb | 2 | * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved. |
1c79356b | 3 | * |
8ad349bb | 4 | * @APPLE_LICENSE_OSREFERENCE_HEADER_START@ |
1c79356b | 5 | * |
8ad349bb A |
6 | * This file contains Original Code and/or Modifications of Original Code |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. The rights granted to you under the | |
10 | * License may not be used to create, or enable the creation or | |
11 | * redistribution of, unlawful or unlicensed copies of an Apple operating | |
12 | * system, or to circumvent, violate, or enable the circumvention or | |
13 | * violation of, any terms of an Apple operating system software license | |
14 | * agreement. | |
15 | * | |
16 | * Please obtain a copy of the License at | |
17 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
18 | * file. | |
19 | * | |
20 | * The Original Code and all software distributed under the License are | |
21 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
22 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
23 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
24 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
25 | * Please see the License for the specific language governing rights and | |
26 | * limitations under the License. | |
27 | * | |
28 | * @APPLE_LICENSE_OSREFERENCE_HEADER_END@ | |
1c79356b A |
29 | */ |
30 | /* | |
31 | * Copyright (C) 1988, 1989, NeXT, Inc. | |
32 | * | |
33 | * File: kern/mach_loader.c | |
34 | * Author: Avadis Tevanian, Jr. | |
35 | * | |
36 | * Mach object file loader (kernel version, for now). | |
37 | * | |
38 | * 21-Jul-88 Avadis Tevanian, Jr. (avie) at NeXT | |
39 | * Started. | |
40 | */ | |
91447636 | 41 | |
1c79356b | 42 | #include <sys/param.h> |
91447636 | 43 | #include <sys/vnode_internal.h> |
1c79356b A |
44 | #include <sys/uio.h> |
45 | #include <sys/namei.h> | |
91447636 A |
46 | #include <sys/proc_internal.h> |
47 | #include <sys/kauth.h> | |
1c79356b A |
48 | #include <sys/stat.h> |
49 | #include <sys/malloc.h> | |
91447636 | 50 | #include <sys/mount_internal.h> |
1c79356b | 51 | #include <sys/fcntl.h> |
91447636 A |
52 | #include <sys/ubc_internal.h> |
53 | #include <sys/imgact.h> | |
1c79356b | 54 | |
1c79356b | 55 | #include <mach/mach_types.h> |
91447636 A |
56 | #include <mach/vm_map.h> /* vm_allocate() */ |
57 | #include <mach/mach_vm.h> /* mach_vm_allocate() */ | |
58 | #include <mach/vm_statistics.h> | |
59 | #include <mach/shared_memory_server.h> | |
60 | #include <mach/task.h> | |
61 | #include <mach/thread_act.h> | |
62 | ||
63 | #include <machine/vmparam.h> | |
1c79356b | 64 | |
91447636 A |
65 | #include <kern/kern_types.h> |
66 | #include <kern/cpu_number.h> | |
1c79356b | 67 | #include <kern/mach_loader.h> |
91447636 | 68 | #include <kern/kalloc.h> |
55e303ae | 69 | #include <kern/task.h> |
91447636 | 70 | #include <kern/thread.h> |
1c79356b A |
71 | |
72 | #include <mach-o/fat.h> | |
73 | #include <mach-o/loader.h> | |
74 | ||
91447636 | 75 | #include <vm/pmap.h> |
1c79356b A |
76 | #include <vm/vm_map.h> |
77 | #include <vm/vm_kern.h> | |
78 | #include <vm/vm_pager.h> | |
79 | #include <vm/vnode_pager.h> | |
9bccf70c | 80 | #include <vm/vm_shared_memory_server.h> |
91447636 | 81 | #include <vm/vm_protos.h> |
9bccf70c | 82 | |
91447636 A |
83 | /* |
84 | * XXX vm/pmap.h should not treat these prototypes as MACH_KERNEL_PRIVATE | |
85 | * when KERNEL is defined. | |
86 | */ | |
8ad349bb | 87 | extern pmap_t pmap_create(vm_map_size_t size); |
91447636 A |
88 | extern void pmap_switch(pmap_t); |
89 | extern void pmap_map_sharedpage(task_t task, pmap_t pmap); | |
90 | ||
91 | /* | |
92 | * XXX kern/thread.h should not treat these prototypes as MACH_KERNEL_PRIVATE | |
93 | * when KERNEL is defined. | |
94 | */ | |
95 | extern kern_return_t thread_setstatus(thread_t thread, int flavor, | |
96 | thread_state_t tstate, | |
97 | mach_msg_type_number_t count); | |
98 | ||
99 | extern kern_return_t thread_state_initialize(thread_t thread); | |
100 | ||
101 | ||
102 | /* XXX should have prototypes in a shared header file */ | |
8ad349bb | 103 | extern int grade_binary(cpu_type_t exectype, cpu_subtype_t execsubtype); |
91447636 A |
104 | extern int get_map_nentries(vm_map_t); |
105 | extern kern_return_t thread_userstack(thread_t, int, thread_state_t, | |
106 | unsigned int, mach_vm_offset_t *, int *); | |
107 | extern kern_return_t thread_entrypoint(thread_t, int, thread_state_t, | |
108 | unsigned int, mach_vm_offset_t *); | |
109 | ||
110 | ||
111 | /* An empty load_result_t */ | |
112 | static load_result_t load_result_null = { | |
113 | MACH_VM_MIN_ADDRESS, | |
114 | MACH_VM_MIN_ADDRESS, | |
115 | MACH_VM_MIN_ADDRESS, | |
116 | 0, | |
117 | 0, | |
118 | 0, | |
119 | 0 | |
120 | }; | |
9bccf70c | 121 | |
1c79356b A |
122 | /* |
123 | * Prototypes of static functions. | |
124 | */ | |
91447636 | 125 | static load_return_t |
1c79356b A |
126 | parse_machfile( |
127 | struct vnode *vp, | |
91447636 A |
128 | vm_map_t map, |
129 | thread_t thr_act, | |
1c79356b | 130 | struct mach_header *header, |
91447636 A |
131 | off_t file_offset, |
132 | off_t macho_size, | |
133 | boolean_t shared_regions, | |
134 | boolean_t clean_regions, | |
135 | int depth, | |
136 | load_result_t *result | |
137 | ); | |
138 | ||
139 | static load_return_t | |
1c79356b A |
140 | load_segment( |
141 | struct segment_command *scp, | |
0b4e3aa0 | 142 | void * pager, |
91447636 A |
143 | off_t pager_offset, |
144 | off_t macho_size, | |
145 | off_t end_of_file, | |
0b4e3aa0 A |
146 | vm_map_t map, |
147 | load_result_t *result | |
91447636 A |
148 | ); |
149 | ||
150 | static load_return_t | |
151 | load_segment_64( | |
152 | struct segment_command_64 *scp64, | |
153 | void *pager, | |
154 | off_t pager_offset, | |
155 | off_t macho_size, | |
156 | off_t end_of_file, | |
157 | vm_map_t map, | |
158 | load_result_t *result | |
159 | ); | |
160 | ||
161 | static load_return_t | |
1c79356b A |
162 | load_unixthread( |
163 | struct thread_command *tcp, | |
91447636 | 164 | thread_t thr_act, |
0b4e3aa0 | 165 | load_result_t *result |
91447636 A |
166 | ); |
167 | ||
168 | static load_return_t | |
1c79356b A |
169 | load_thread( |
170 | struct thread_command *tcp, | |
91447636 | 171 | thread_t thr_act, |
0b4e3aa0 | 172 | load_result_t *result |
91447636 A |
173 | ); |
174 | ||
175 | static load_return_t | |
1c79356b | 176 | load_threadstate( |
0b4e3aa0 | 177 | thread_t thread, |
1c79356b A |
178 | unsigned long *ts, |
179 | unsigned long total_size | |
91447636 A |
180 | ); |
181 | ||
182 | static load_return_t | |
1c79356b | 183 | load_threadstack( |
0b4e3aa0 | 184 | thread_t thread, |
1c79356b A |
185 | unsigned long *ts, |
186 | unsigned long total_size, | |
91447636 | 187 | mach_vm_offset_t *user_stack, |
0b4e3aa0 | 188 | int *customstack |
91447636 A |
189 | ); |
190 | ||
191 | static load_return_t | |
1c79356b | 192 | load_threadentry( |
0b4e3aa0 | 193 | thread_t thread, |
1c79356b A |
194 | unsigned long *ts, |
195 | unsigned long total_size, | |
91447636 A |
196 | mach_vm_offset_t *entry_point |
197 | ); | |
198 | ||
199 | static load_return_t | |
1c79356b A |
200 | load_dylinker( |
201 | struct dylinker_command *lcp, | |
91447636 | 202 | integer_t archbits, |
0b4e3aa0 | 203 | vm_map_t map, |
91447636 | 204 | thread_t thr_act, |
0b4e3aa0 | 205 | int depth, |
55e303ae | 206 | load_result_t *result, |
8ad349bb | 207 | boolean_t clean_regions |
91447636 A |
208 | ); |
209 | ||
210 | static load_return_t | |
1c79356b | 211 | get_macho_vnode( |
0b4e3aa0 | 212 | char *path, |
91447636 | 213 | integer_t archbits, |
1c79356b | 214 | struct mach_header *mach_header, |
91447636 A |
215 | off_t *file_offset, |
216 | off_t *macho_size, | |
1c79356b A |
217 | struct vnode **vpp |
218 | ); | |
219 | ||
220 | load_return_t | |
221 | load_machfile( | |
91447636 | 222 | struct image_params *imgp, |
1c79356b | 223 | struct mach_header *header, |
91447636 | 224 | thread_t thr_act, |
55e303ae | 225 | vm_map_t new_map, |
91447636 A |
226 | boolean_t clean_regions, |
227 | load_result_t *result | |
1c79356b A |
228 | ) |
229 | { | |
91447636 A |
230 | struct vnode *vp = imgp->ip_vp; |
231 | off_t file_offset = imgp->ip_arch_offset; | |
232 | off_t macho_size = imgp->ip_arch_size; | |
233 | ||
234 | pmap_t pmap = 0; /* protected by create_map */ | |
1c79356b A |
235 | vm_map_t map; |
236 | vm_map_t old_map; | |
237 | load_result_t myresult; | |
1c79356b | 238 | load_return_t lret; |
0b4e3aa0 A |
239 | boolean_t create_map = TRUE; |
240 | ||
241 | if (new_map != VM_MAP_NULL) { | |
242 | create_map = FALSE; | |
243 | } | |
1c79356b | 244 | |
0b4e3aa0 A |
245 | if (create_map) { |
246 | old_map = current_map(); | |
8ad349bb | 247 | #ifdef i386 |
0b4e3aa0 A |
248 | pmap = get_task_pmap(current_task()); |
249 | pmap_reference(pmap); | |
8ad349bb A |
250 | #else |
251 | pmap = pmap_create((vm_map_size_t) 0); | |
252 | #endif | |
0b4e3aa0 | 253 | map = vm_map_create(pmap, |
8ad349bb A |
254 | get_map_min(old_map), |
255 | get_map_max(old_map), | |
256 | TRUE); /**** FIXME ****/ | |
0b4e3aa0 A |
257 | } else |
258 | map = new_map; | |
8ad349bb | 259 | |
1c79356b A |
260 | if (!result) |
261 | result = &myresult; | |
262 | ||
91447636 | 263 | *result = load_result_null; |
1c79356b | 264 | |
0b4e3aa0 | 265 | lret = parse_machfile(vp, map, thr_act, header, file_offset, macho_size, |
91447636 A |
266 | ((imgp->ip_flags & IMGPF_IS_64BIT) == 0), /* shared regions? */ |
267 | clean_regions, 0, result); | |
1c79356b A |
268 | |
269 | if (lret != LOAD_SUCCESS) { | |
55e303ae | 270 | if (create_map) { |
0b4e3aa0 | 271 | vm_map_deallocate(map); /* will lose pmap reference too */ |
55e303ae | 272 | } |
1c79356b A |
273 | return(lret); |
274 | } | |
55e303ae | 275 | |
1c79356b A |
276 | /* |
277 | * Commit to new map. First make sure that the current | |
278 | * users of the task get done with it, and that we clean | |
279 | * up the old contents of IPC and memory. The task is | |
280 | * guaranteed to be single threaded upon return (us). | |
281 | * | |
55e303ae A |
282 | * Swap the new map for the old, which consumes our new map |
283 | * reference but each leaves us responsible for the old_map reference. | |
1c79356b A |
284 | * That lets us get off the pmap associated with it, and |
285 | * then we can release it. | |
286 | */ | |
0b4e3aa0 A |
287 | if (create_map) { |
288 | task_halt(current_task()); | |
1c79356b | 289 | |
0b4e3aa0 | 290 | old_map = swap_task_map(current_task(), map); |
8ad349bb | 291 | #ifndef i386 |
0b4e3aa0 | 292 | pmap_switch(pmap); /* Make sure we are using the new pmap */ |
8ad349bb | 293 | #endif |
0b4e3aa0 A |
294 | vm_map_deallocate(old_map); |
295 | } | |
1c79356b A |
296 | return(LOAD_SUCCESS); |
297 | } | |
298 | ||
299 | int dylink_test = 1; | |
1c79356b | 300 | |
91447636 A |
301 | /* |
302 | * The file size of a mach-o file is limited to 32 bits; this is because | |
303 | * this is the limit on the kalloc() of enough bytes for a mach_header and | |
304 | * the contents of its sizeofcmds, which is currently constrained to 32 | |
305 | * bits in the file format itself. We read into the kernel buffer the | |
306 | * commands section, and then parse it in order to parse the mach-o file | |
307 | * format load_command segment(s). We are only interested in a subset of | |
308 | * the total set of possible commands. | |
309 | */ | |
1c79356b A |
310 | static |
311 | load_return_t | |
312 | parse_machfile( | |
91447636 | 313 | struct vnode *vp, |
1c79356b | 314 | vm_map_t map, |
91447636 | 315 | thread_t thr_act, |
1c79356b | 316 | struct mach_header *header, |
91447636 A |
317 | off_t file_offset, |
318 | off_t macho_size, | |
319 | boolean_t shared_regions, | |
320 | boolean_t clean_regions, | |
1c79356b | 321 | int depth, |
91447636 | 322 | load_result_t *result |
1c79356b A |
323 | ) |
324 | { | |
a3d08fcd | 325 | uint32_t ncmds; |
91447636 | 326 | struct load_command *lcp; |
1c79356b | 327 | struct dylinker_command *dlp = 0; |
91447636 | 328 | integer_t dlarchbits = 0; |
1c79356b | 329 | void * pager; |
55e303ae | 330 | load_return_t ret = LOAD_SUCCESS; |
91447636 A |
331 | caddr_t addr; |
332 | void * kl_addr; | |
1c79356b | 333 | vm_size_t size,kl_size; |
a3d08fcd A |
334 | size_t offset; |
335 | size_t oldoffset; /* for overflow check */ | |
1c79356b A |
336 | int pass; |
337 | struct proc *p = current_proc(); /* XXXX */ | |
338 | int error; | |
339 | int resid=0; | |
0b4e3aa0 | 340 | task_t task; |
91447636 A |
341 | size_t mach_header_sz = sizeof(struct mach_header); |
342 | boolean_t abi64; | |
343 | ||
344 | if (header->magic == MH_MAGIC_64 || | |
345 | header->magic == MH_CIGAM_64) { | |
346 | mach_header_sz = sizeof(struct mach_header_64); | |
347 | } | |
1c79356b A |
348 | |
349 | /* | |
350 | * Break infinite recursion | |
351 | */ | |
352 | if (depth > 6) | |
353 | return(LOAD_FAILURE); | |
0b4e3aa0 A |
354 | |
355 | task = (task_t)get_threadtask(thr_act); | |
356 | ||
1c79356b A |
357 | depth++; |
358 | ||
359 | /* | |
360 | * Check to see if right machine type. | |
361 | */ | |
91447636 A |
362 | if (((cpu_type_t)(header->cputype & ~CPU_ARCH_MASK) != cpu_type()) || |
363 | !grade_binary(header->cputype, header->cpusubtype)) | |
1c79356b A |
364 | return(LOAD_BADARCH); |
365 | ||
91447636 A |
366 | abi64 = ((header->cputype & CPU_ARCH_ABI64) == CPU_ARCH_ABI64); |
367 | ||
1c79356b A |
368 | switch (header->filetype) { |
369 | ||
370 | case MH_OBJECT: | |
371 | case MH_EXECUTE: | |
372 | case MH_PRELOAD: | |
373 | if (depth != 1) | |
374 | return (LOAD_FAILURE); | |
375 | break; | |
376 | ||
377 | case MH_FVMLIB: | |
378 | case MH_DYLIB: | |
379 | if (depth == 1) | |
380 | return (LOAD_FAILURE); | |
381 | break; | |
382 | ||
383 | case MH_DYLINKER: | |
384 | if (depth != 2) | |
385 | return (LOAD_FAILURE); | |
386 | break; | |
387 | ||
388 | default: | |
389 | return (LOAD_FAILURE); | |
390 | } | |
391 | ||
392 | /* | |
393 | * Get the pager for the file. | |
394 | */ | |
395 | UBCINFOCHECK("parse_machfile", vp); | |
396 | pager = (void *) ubc_getpager(vp); | |
397 | ||
398 | /* | |
399 | * Map portion that must be accessible directly into | |
400 | * kernel's map. | |
401 | */ | |
91447636 | 402 | if ((mach_header_sz + header->sizeofcmds) > macho_size) |
1c79356b A |
403 | return(LOAD_BADMACHO); |
404 | ||
405 | /* | |
406 | * Round size of Mach-O commands up to page boundry. | |
407 | */ | |
91447636 | 408 | size = round_page(mach_header_sz + header->sizeofcmds); |
1c79356b A |
409 | if (size <= 0) |
410 | return(LOAD_BADMACHO); | |
411 | ||
412 | /* | |
413 | * Map the load commands into kernel memory. | |
414 | */ | |
415 | addr = 0; | |
1c79356b A |
416 | kl_size = size; |
417 | kl_addr = kalloc(size); | |
91447636 | 418 | addr = (caddr_t)kl_addr; |
0b4e3aa0 | 419 | if (addr == NULL) |
1c79356b | 420 | return(LOAD_NOSPACE); |
0b4e3aa0 | 421 | |
91447636 A |
422 | error = vn_rdwr(UIO_READ, vp, addr, size, file_offset, |
423 | UIO_SYSSPACE32, 0, kauth_cred_get(), &resid, p); | |
424 | if (error) { | |
0b4e3aa0 A |
425 | if (kl_addr ) |
426 | kfree(kl_addr, kl_size); | |
55e303ae | 427 | return(LOAD_IOERROR); |
1c79356b | 428 | } |
91447636 | 429 | /* (void)ubc_map(vp, PROT_EXEC); */ /* NOT HERE */ |
1c79356b | 430 | |
1c79356b A |
431 | /* |
432 | * Scan through the commands, processing each one as necessary. | |
433 | */ | |
434 | for (pass = 1; pass <= 2; pass++) { | |
a3d08fcd A |
435 | /* |
436 | * Loop through each of the load_commands indicated by the | |
437 | * Mach-O header; if an absurd value is provided, we just | |
438 | * run off the end of the reserved section by incrementing | |
439 | * the offset too far, so we are implicitly fail-safe. | |
440 | */ | |
91447636 | 441 | offset = mach_header_sz; |
1c79356b A |
442 | ncmds = header->ncmds; |
443 | while (ncmds--) { | |
444 | /* | |
445 | * Get a pointer to the command. | |
446 | */ | |
447 | lcp = (struct load_command *)(addr + offset); | |
a3d08fcd | 448 | oldoffset = offset; |
1c79356b A |
449 | offset += lcp->cmdsize; |
450 | ||
451 | /* | |
a3d08fcd A |
452 | * Perform prevalidation of the struct load_command |
453 | * before we attempt to use its contents. Invalid | |
454 | * values are ones which result in an overflow, or | |
455 | * which can not possibly be valid commands, or which | |
456 | * straddle or exist past the reserved section at the | |
457 | * start of the image. | |
1c79356b | 458 | */ |
a3d08fcd A |
459 | if (oldoffset > offset || |
460 | lcp->cmdsize < sizeof(struct load_command) || | |
91447636 A |
461 | offset > header->sizeofcmds + mach_header_sz) { |
462 | ret = LOAD_BADMACHO; | |
a3d08fcd | 463 | break; |
1c79356b A |
464 | } |
465 | ||
466 | /* | |
a3d08fcd A |
467 | * Act on struct load_command's for which kernel |
468 | * intervention is required. | |
1c79356b A |
469 | */ |
470 | switch(lcp->cmd) { | |
91447636 A |
471 | case LC_SEGMENT_64: |
472 | if (pass != 1) | |
473 | break; | |
474 | ret = load_segment_64( | |
475 | (struct segment_command_64 *)lcp, | |
476 | pager, | |
477 | file_offset, | |
478 | macho_size, | |
479 | ubc_getsize(vp), | |
480 | map, | |
481 | result); | |
482 | break; | |
1c79356b A |
483 | case LC_SEGMENT: |
484 | if (pass != 1) | |
485 | break; | |
486 | ret = load_segment( | |
487 | (struct segment_command *) lcp, | |
91447636 A |
488 | pager, |
489 | file_offset, | |
1c79356b | 490 | macho_size, |
91447636 | 491 | ubc_getsize(vp), |
1c79356b A |
492 | map, |
493 | result); | |
494 | break; | |
495 | case LC_THREAD: | |
496 | if (pass != 2) | |
497 | break; | |
91447636 A |
498 | ret = load_thread((struct thread_command *)lcp, |
499 | thr_act, | |
1c79356b A |
500 | result); |
501 | break; | |
502 | case LC_UNIXTHREAD: | |
503 | if (pass != 2) | |
504 | break; | |
505 | ret = load_unixthread( | |
91447636 A |
506 | (struct thread_command *) lcp, |
507 | thr_act, | |
1c79356b A |
508 | result); |
509 | break; | |
1c79356b A |
510 | case LC_LOAD_DYLINKER: |
511 | if (pass != 2) | |
512 | break; | |
91447636 | 513 | if ((depth == 1) && (dlp == 0)) { |
1c79356b | 514 | dlp = (struct dylinker_command *)lcp; |
91447636 A |
515 | dlarchbits = (header->cputype & CPU_ARCH_MASK); |
516 | } else { | |
1c79356b | 517 | ret = LOAD_FAILURE; |
91447636 | 518 | } |
1c79356b A |
519 | break; |
520 | default: | |
a3d08fcd A |
521 | /* Other commands are ignored by the kernel */ |
522 | ret = LOAD_SUCCESS; | |
91447636 | 523 | break; |
1c79356b A |
524 | } |
525 | if (ret != LOAD_SUCCESS) | |
526 | break; | |
527 | } | |
528 | if (ret != LOAD_SUCCESS) | |
529 | break; | |
530 | } | |
91447636 A |
531 | if (ret == LOAD_SUCCESS) { |
532 | ||
533 | if (shared_regions) { | |
534 | vm_offset_t vmaddr; | |
1c79356b A |
535 | shared_region_mapping_t shared_region; |
536 | struct shared_region_task_mappings map_info; | |
537 | shared_region_mapping_t next; | |
538 | ||
539 | RedoLookup: | |
0b4e3aa0 | 540 | vm_get_shared_region(task, &shared_region); |
1c79356b A |
541 | map_info.self = (vm_offset_t)shared_region; |
542 | shared_region_mapping_info(shared_region, | |
543 | &(map_info.text_region), | |
544 | &(map_info.text_size), | |
545 | &(map_info.data_region), | |
546 | &(map_info.data_size), | |
547 | &(map_info.region_mappings), | |
548 | &(map_info.client_base), | |
549 | &(map_info.alternate_base), | |
550 | &(map_info.alternate_next), | |
55e303ae A |
551 | &(map_info.fs_base), |
552 | &(map_info.system), | |
1c79356b A |
553 | &(map_info.flags), &next); |
554 | ||
55e303ae A |
555 | if((map_info.flags & SHARED_REGION_FULL) || |
556 | (map_info.flags & SHARED_REGION_STALE)) { | |
557 | shared_region_mapping_t system_region; | |
558 | system_region = lookup_default_shared_region( | |
559 | map_info.fs_base, map_info.system); | |
560 | if((map_info.self != (vm_offset_t)system_region) && | |
561 | (map_info.flags & SHARED_REGION_SYSTEM)) { | |
562 | if(system_region == NULL) { | |
563 | shared_file_boot_time_init( | |
564 | map_info.fs_base, map_info.system); | |
565 | } else { | |
566 | vm_set_shared_region(task, system_region); | |
567 | } | |
568 | shared_region_mapping_dealloc( | |
1c79356b | 569 | (shared_region_mapping_t)map_info.self); |
55e303ae A |
570 | goto RedoLookup; |
571 | } else if (map_info.flags & SHARED_REGION_SYSTEM) { | |
572 | shared_region_mapping_dealloc(system_region); | |
573 | shared_file_boot_time_init( | |
574 | map_info.fs_base, map_info.system); | |
575 | shared_region_mapping_dealloc( | |
576 | (shared_region_mapping_t)map_info.self); | |
577 | } else { | |
578 | shared_region_mapping_dealloc(system_region); | |
579 | } | |
1c79356b A |
580 | } |
581 | ||
1c79356b | 582 | if (dylink_test) { |
9bccf70c | 583 | p->p_flag |= P_NOSHLIB; /* no shlibs in use */ |
91447636 | 584 | vmaddr = map_info.client_base; |
55e303ae | 585 | if(clean_regions) { |
91447636 A |
586 | vm_map(map, &vmaddr, map_info.text_size, |
587 | 0, SHARED_LIB_ALIAS|VM_FLAGS_FIXED, | |
55e303ae A |
588 | map_info.text_region, 0, FALSE, |
589 | VM_PROT_READ, VM_PROT_READ, VM_INHERIT_SHARE); | |
590 | } else { | |
91447636 | 591 | vm_map(map, &vmaddr, map_info.text_size, 0, |
1c79356b | 592 | (VM_MEMORY_SHARED_PMAP << 24) |
91447636 | 593 | | SHARED_LIB_ALIAS | VM_FLAGS_FIXED, |
1c79356b A |
594 | map_info.text_region, 0, FALSE, |
595 | VM_PROT_READ, VM_PROT_READ, VM_INHERIT_SHARE); | |
55e303ae | 596 | } |
91447636 A |
597 | vmaddr = map_info.client_base + map_info.text_size; |
598 | vm_map(map, &vmaddr, map_info.data_size, | |
599 | 0, SHARED_LIB_ALIAS | VM_FLAGS_FIXED, | |
1c79356b A |
600 | map_info.data_region, 0, TRUE, |
601 | VM_PROT_READ, VM_PROT_READ, VM_INHERIT_SHARE); | |
55e303ae A |
602 | |
603 | while (next) { | |
604 | /* this should be fleshed out for the general case */ | |
605 | /* but this is not necessary for now. Indeed we */ | |
606 | /* are handling the com page inside of the */ | |
607 | /* shared_region mapping create calls for now for */ | |
608 | /* simplicities sake. If more general support is */ | |
609 | /* needed the code to manipulate the shared range */ | |
610 | /* chain can be pulled out and moved to the callers*/ | |
611 | shared_region_mapping_info(next, | |
612 | &(map_info.text_region), | |
613 | &(map_info.text_size), | |
614 | &(map_info.data_region), | |
615 | &(map_info.data_size), | |
616 | &(map_info.region_mappings), | |
617 | &(map_info.client_base), | |
618 | &(map_info.alternate_base), | |
619 | &(map_info.alternate_next), | |
620 | &(map_info.fs_base), | |
621 | &(map_info.system), | |
622 | &(map_info.flags), &next); | |
623 | ||
91447636 A |
624 | vmaddr = map_info.client_base; |
625 | vm_map(map, &vmaddr, map_info.text_size, | |
626 | 0, SHARED_LIB_ALIAS | VM_FLAGS_FIXED, | |
55e303ae A |
627 | map_info.text_region, 0, FALSE, |
628 | VM_PROT_READ, VM_PROT_READ, VM_INHERIT_SHARE); | |
629 | } | |
1c79356b | 630 | } |
91447636 A |
631 | } |
632 | if (dlp != 0) | |
8ad349bb | 633 | ret = load_dylinker(dlp, dlarchbits, map, thr_act, depth, result, clean_regions); |
91447636 A |
634 | |
635 | if(depth == 1) { | |
636 | if (result->thread_count == 0) | |
637 | ret = LOAD_FAILURE; | |
8ad349bb | 638 | #ifdef __ppc__ |
91447636 A |
639 | else if ( abi64 ) { |
640 | /* Map in 64-bit commpage */ | |
641 | /* LP64todo - make this clean */ | |
642 | pmap_map_sharedpage(current_task(), get_map_pmap(map)); | |
643 | vm_map_commpage64(map); | |
644 | } | |
8ad349bb | 645 | #endif |
91447636 | 646 | } |
1c79356b A |
647 | } |
648 | ||
0b4e3aa0 A |
649 | if (kl_addr ) |
650 | kfree(kl_addr, kl_size); | |
651 | ||
1c79356b | 652 | if (ret == LOAD_SUCCESS) |
91447636 | 653 | (void)ubc_map(vp, PROT_EXEC); |
1c79356b A |
654 | |
655 | return(ret); | |
656 | } | |
657 | ||
658 | static | |
659 | load_return_t | |
660 | load_segment( | |
661 | struct segment_command *scp, | |
662 | void * pager, | |
91447636 A |
663 | off_t pager_offset, |
664 | off_t macho_size, | |
665 | __unused off_t end_of_file, | |
1c79356b A |
666 | vm_map_t map, |
667 | load_result_t *result | |
668 | ) | |
669 | { | |
670 | kern_return_t ret; | |
671 | vm_offset_t map_addr, map_offset; | |
672 | vm_size_t map_size, seg_size, delta_size; | |
1c79356b A |
673 | vm_prot_t initprot; |
674 | vm_prot_t maxprot; | |
1c79356b A |
675 | |
676 | /* | |
677 | * Make sure what we get from the file is really ours (as specified | |
678 | * by macho_size). | |
679 | */ | |
680 | if (scp->fileoff + scp->filesize > macho_size) | |
681 | return (LOAD_BADMACHO); | |
682 | ||
91447636 | 683 | seg_size = round_page(scp->vmsize); |
1c79356b A |
684 | if (seg_size == 0) |
685 | return(KERN_SUCCESS); | |
686 | ||
687 | /* | |
688 | * Round sizes to page size. | |
689 | */ | |
91447636 A |
690 | map_size = round_page(scp->filesize); |
691 | map_addr = trunc_page(scp->vmaddr); | |
1c79356b A |
692 | |
693 | map_offset = pager_offset + scp->fileoff; | |
694 | ||
695 | if (map_size > 0) { | |
696 | initprot = (scp->initprot) & VM_PROT_ALL; | |
697 | maxprot = (scp->maxprot) & VM_PROT_ALL; | |
698 | /* | |
699 | * Map a copy of the file into the address space. | |
700 | */ | |
701 | ret = vm_map(map, | |
91447636 A |
702 | &map_addr, map_size, (vm_offset_t)0, |
703 | VM_FLAGS_FIXED, pager, map_offset, TRUE, | |
1c79356b A |
704 | initprot, maxprot, |
705 | VM_INHERIT_DEFAULT); | |
706 | if (ret != KERN_SUCCESS) | |
707 | return(LOAD_NOSPACE); | |
708 | ||
1c79356b A |
709 | /* |
710 | * If the file didn't end on a page boundary, | |
711 | * we need to zero the leftover. | |
712 | */ | |
713 | delta_size = map_size - scp->filesize; | |
714 | #if FIXME | |
715 | if (delta_size > 0) { | |
716 | vm_offset_t tmp; | |
717 | ||
91447636 | 718 | ret = vm_allocate(kernel_map, &tmp, delta_size, VM_FLAGS_ANYWHERE); |
1c79356b A |
719 | if (ret != KERN_SUCCESS) |
720 | return(LOAD_RESOURCE); | |
721 | ||
722 | if (copyout(tmp, map_addr + scp->filesize, | |
723 | delta_size)) { | |
724 | (void) vm_deallocate( | |
725 | kernel_map, tmp, delta_size); | |
726 | return(LOAD_FAILURE); | |
727 | } | |
728 | ||
729 | (void) vm_deallocate(kernel_map, tmp, delta_size); | |
730 | } | |
731 | #endif /* FIXME */ | |
732 | } | |
733 | ||
734 | /* | |
735 | * If the virtual size of the segment is greater | |
736 | * than the size from the file, we need to allocate | |
737 | * zero fill memory for the rest. | |
738 | */ | |
739 | delta_size = seg_size - map_size; | |
740 | if (delta_size > 0) { | |
741 | vm_offset_t tmp = map_addr + map_size; | |
742 | ||
8ad349bb | 743 | ret = vm_allocate(map, &tmp, delta_size, VM_FLAGS_FIXED); |
1c79356b A |
744 | if (ret != KERN_SUCCESS) |
745 | return(LOAD_NOSPACE); | |
746 | } | |
747 | ||
8ad349bb A |
748 | /* |
749 | * Set protection values. (Note: ignore errors!) | |
750 | */ | |
c0fea474 | 751 | |
8ad349bb A |
752 | if (scp->maxprot != VM_PROT_DEFAULT) { |
753 | (void) vm_protect(map, | |
754 | map_addr, seg_size, | |
755 | TRUE, scp->maxprot); | |
c0fea474 | 756 | } |
8ad349bb A |
757 | if (scp->initprot != VM_PROT_DEFAULT) { |
758 | (void) vm_protect(map, | |
759 | map_addr, seg_size, | |
760 | FALSE, scp->initprot); | |
761 | } | |
762 | if ( (scp->fileoff == 0) && (scp->filesize != 0) ) | |
763 | result->mach_header = map_addr; | |
764 | return(LOAD_SUCCESS); | |
1c79356b A |
765 | } |
766 | ||
767 | static | |
768 | load_return_t | |
91447636 A |
769 | load_segment_64( |
770 | struct segment_command_64 *scp64, | |
771 | void * pager, | |
772 | off_t pager_offset, | |
773 | off_t macho_size, | |
774 | __unused off_t end_of_file, | |
775 | vm_map_t map, | |
1c79356b A |
776 | load_result_t *result |
777 | ) | |
778 | { | |
91447636 A |
779 | kern_return_t ret; |
780 | mach_vm_offset_t map_addr, map_offset; | |
781 | mach_vm_size_t map_size, seg_size, delta_size; | |
782 | vm_prot_t initprot; | |
783 | vm_prot_t maxprot; | |
1c79356b | 784 | |
91447636 A |
785 | /* |
786 | * Make sure what we get from the file is really ours (as specified | |
787 | * by macho_size). | |
788 | */ | |
789 | if (scp64->fileoff + scp64->filesize > (uint64_t)macho_size) | |
790 | return (LOAD_BADMACHO); | |
791 | ||
792 | seg_size = round_page_64(scp64->vmsize); | |
793 | if (seg_size == 0) | |
794 | return(KERN_SUCCESS); | |
795 | ||
796 | /* | |
797 | * Round sizes to page size. | |
798 | */ | |
799 | map_size = round_page_64(scp64->filesize); /* limited to 32 bits */ | |
800 | map_addr = round_page_64(scp64->vmaddr); | |
801 | ||
802 | map_offset = pager_offset + scp64->fileoff; /* limited to 32 bits */ | |
803 | ||
804 | if (map_size > 0) { | |
805 | initprot = (scp64->initprot) & VM_PROT_ALL; | |
806 | maxprot = (scp64->maxprot) & VM_PROT_ALL; | |
807 | /* | |
808 | * Map a copy of the file into the address space. | |
809 | */ | |
810 | ret = mach_vm_map(map, | |
811 | &map_addr, map_size, (mach_vm_offset_t)0, | |
812 | VM_FLAGS_FIXED, pager, map_offset, TRUE, | |
813 | initprot, maxprot, | |
814 | VM_INHERIT_DEFAULT); | |
815 | if (ret != KERN_SUCCESS) | |
816 | return(LOAD_NOSPACE); | |
817 | ||
818 | /* | |
819 | * If the file didn't end on a page boundary, | |
820 | * we need to zero the leftover. | |
821 | */ | |
822 | delta_size = map_size - scp64->filesize; | |
823 | #if FIXME | |
824 | if (delta_size > 0) { | |
825 | mach_vm_offset_t tmp; | |
826 | ||
827 | ret = vm_allocate(kernel_map, &tmp, delta_size, VM_FLAGS_ANYWHERE); | |
828 | if (ret != KERN_SUCCESS) | |
829 | return(LOAD_RESOURCE); | |
830 | ||
831 | if (copyout(tmp, map_addr + scp64->filesize, | |
832 | delta_size)) { | |
833 | (void) vm_deallocate( | |
834 | kernel_map, tmp, delta_size); | |
1c79356b | 835 | return (LOAD_FAILURE); |
91447636 | 836 | } |
1c79356b | 837 | |
91447636 A |
838 | (void) vm_deallocate(kernel_map, tmp, delta_size); |
839 | } | |
840 | #endif /* FIXME */ | |
841 | } | |
1c79356b | 842 | |
91447636 A |
843 | /* |
844 | * If the virtual size of the segment is greater | |
845 | * than the size from the file, we need to allocate | |
846 | * zero fill memory for the rest. | |
847 | */ | |
848 | delta_size = seg_size - map_size; | |
849 | if (delta_size > 0) { | |
850 | mach_vm_offset_t tmp = map_addr + map_size; | |
1c79356b | 851 | |
8ad349bb | 852 | ret = mach_vm_allocate(map, &tmp, delta_size, VM_FLAGS_FIXED); |
91447636 A |
853 | if (ret != KERN_SUCCESS) |
854 | return(LOAD_NOSPACE); | |
855 | } | |
1c79356b | 856 | |
8ad349bb A |
857 | /* |
858 | * Set protection values. (Note: ignore errors!) | |
859 | */ | |
c0fea474 | 860 | |
8ad349bb A |
861 | if (scp64->maxprot != VM_PROT_DEFAULT) { |
862 | (void) mach_vm_protect(map, | |
863 | map_addr, seg_size, | |
864 | TRUE, scp64->maxprot); | |
c0fea474 | 865 | } |
8ad349bb A |
866 | if (scp64->initprot != VM_PROT_DEFAULT) { |
867 | (void) mach_vm_protect(map, | |
868 | map_addr, seg_size, | |
869 | FALSE, scp64->initprot); | |
870 | } | |
871 | if ( (scp64->fileoff == 0) && (scp64->filesize != 0) ) | |
872 | result->mach_header = map_addr; | |
873 | return(LOAD_SUCCESS); | |
1c79356b A |
874 | } |
875 | ||
876 | static | |
877 | load_return_t | |
878 | load_thread( | |
879 | struct thread_command *tcp, | |
91447636 | 880 | thread_t thread, |
1c79356b A |
881 | load_result_t *result |
882 | ) | |
883 | { | |
1c79356b A |
884 | kern_return_t kret; |
885 | load_return_t lret; | |
0b4e3aa0 A |
886 | task_t task; |
887 | int customstack=0; | |
1c79356b | 888 | |
55e303ae | 889 | task = get_threadtask(thread); |
0b4e3aa0 A |
890 | |
891 | /* if count is 0; same as thr_act */ | |
892 | if (result->thread_count != 0) { | |
893 | kret = thread_create(task, &thread); | |
1c79356b A |
894 | if (kret != KERN_SUCCESS) |
895 | return(LOAD_RESOURCE); | |
91447636 | 896 | thread_deallocate(thread); |
1c79356b A |
897 | } |
898 | ||
899 | lret = load_threadstate(thread, | |
900 | (unsigned long *)(((vm_offset_t)tcp) + | |
901 | sizeof(struct thread_command)), | |
902 | tcp->cmdsize - sizeof(struct thread_command)); | |
903 | if (lret != LOAD_SUCCESS) | |
904 | return (lret); | |
905 | ||
906 | if (result->thread_count == 0) { | |
0b4e3aa0 | 907 | lret = load_threadstack(thread, |
1c79356b A |
908 | (unsigned long *)(((vm_offset_t)tcp) + |
909 | sizeof(struct thread_command)), | |
910 | tcp->cmdsize - sizeof(struct thread_command), | |
0b4e3aa0 A |
911 | &result->user_stack, |
912 | &customstack); | |
913 | if (customstack) | |
914 | result->customstack = 1; | |
915 | else | |
916 | result->customstack = 0; | |
917 | ||
1c79356b A |
918 | if (lret != LOAD_SUCCESS) |
919 | return(lret); | |
920 | ||
0b4e3aa0 | 921 | lret = load_threadentry(thread, |
1c79356b A |
922 | (unsigned long *)(((vm_offset_t)tcp) + |
923 | sizeof(struct thread_command)), | |
924 | tcp->cmdsize - sizeof(struct thread_command), | |
925 | &result->entry_point); | |
926 | if (lret != LOAD_SUCCESS) | |
927 | return(lret); | |
928 | } | |
929 | /* | |
930 | * Resume thread now, note that this means that the thread | |
931 | * commands should appear after all the load commands to | |
932 | * be sure they don't reference anything not yet mapped. | |
933 | */ | |
934 | else | |
935 | thread_resume(thread); | |
936 | ||
937 | result->thread_count++; | |
938 | ||
939 | return(LOAD_SUCCESS); | |
940 | } | |
941 | ||
91447636 A |
942 | static |
943 | load_return_t | |
944 | load_unixthread( | |
945 | struct thread_command *tcp, | |
946 | thread_t thread, | |
947 | load_result_t *result | |
948 | ) | |
949 | { | |
950 | load_return_t ret; | |
951 | int customstack =0; | |
952 | ||
953 | if (result->thread_count != 0) | |
954 | return (LOAD_FAILURE); | |
955 | ||
956 | ret = load_threadstack(thread, | |
957 | (unsigned long *)(((vm_offset_t)tcp) + | |
958 | sizeof(struct thread_command)), | |
959 | tcp->cmdsize - sizeof(struct thread_command), | |
960 | &result->user_stack, | |
961 | &customstack); | |
962 | if (ret != LOAD_SUCCESS) | |
963 | return(ret); | |
964 | ||
965 | if (customstack) | |
966 | result->customstack = 1; | |
967 | else | |
968 | result->customstack = 0; | |
969 | ret = load_threadentry(thread, | |
970 | (unsigned long *)(((vm_offset_t)tcp) + | |
971 | sizeof(struct thread_command)), | |
972 | tcp->cmdsize - sizeof(struct thread_command), | |
973 | &result->entry_point); | |
974 | if (ret != LOAD_SUCCESS) | |
975 | return(ret); | |
976 | ||
977 | ret = load_threadstate(thread, | |
978 | (unsigned long *)(((vm_offset_t)tcp) + | |
979 | sizeof(struct thread_command)), | |
980 | tcp->cmdsize - sizeof(struct thread_command)); | |
981 | if (ret != LOAD_SUCCESS) | |
982 | return (ret); | |
983 | ||
984 | result->unixproc = TRUE; | |
985 | result->thread_count++; | |
986 | ||
987 | return(LOAD_SUCCESS); | |
988 | } | |
989 | ||
1c79356b A |
990 | static |
991 | load_return_t | |
992 | load_threadstate( | |
993 | thread_t thread, | |
994 | unsigned long *ts, | |
995 | unsigned long total_size | |
996 | ) | |
997 | { | |
998 | kern_return_t ret; | |
999 | unsigned long size; | |
1000 | int flavor; | |
91447636 | 1001 | unsigned long thread_size; |
1c79356b | 1002 | |
91447636 A |
1003 | ret = thread_state_initialize( thread ); |
1004 | if (ret != KERN_SUCCESS) | |
1005 | return(LOAD_FAILURE); | |
1006 | ||
1c79356b | 1007 | /* |
91447636 A |
1008 | * Set the new thread state; iterate through the state flavors in |
1009 | * the mach-o file. | |
1c79356b | 1010 | */ |
1c79356b A |
1011 | while (total_size > 0) { |
1012 | flavor = *ts++; | |
1013 | size = *ts++; | |
91447636 A |
1014 | thread_size = (size+2)*sizeof(unsigned long); |
1015 | if (thread_size > total_size) | |
1c79356b | 1016 | return(LOAD_BADMACHO); |
91447636 A |
1017 | total_size -= thread_size; |
1018 | /* | |
1019 | * Third argument is a kernel space pointer; it gets cast | |
1020 | * to the appropriate type in machine_thread_set_state() | |
1021 | * based on the value of flavor. | |
1022 | */ | |
1023 | ret = thread_setstatus(thread, flavor, (thread_state_t)ts, size); | |
1c79356b A |
1024 | if (ret != KERN_SUCCESS) |
1025 | return(LOAD_FAILURE); | |
1026 | ts += size; /* ts is a (unsigned long *) */ | |
1027 | } | |
1028 | return(LOAD_SUCCESS); | |
1029 | } | |
1030 | ||
1031 | static | |
1032 | load_return_t | |
1033 | load_threadstack( | |
1034 | thread_t thread, | |
1035 | unsigned long *ts, | |
1036 | unsigned long total_size, | |
91447636 | 1037 | user_addr_t *user_stack, |
0b4e3aa0 | 1038 | int *customstack |
1c79356b A |
1039 | ) |
1040 | { | |
1041 | kern_return_t ret; | |
1042 | unsigned long size; | |
1043 | int flavor; | |
91447636 | 1044 | unsigned long stack_size; |
1c79356b | 1045 | |
1c79356b A |
1046 | while (total_size > 0) { |
1047 | flavor = *ts++; | |
1048 | size = *ts++; | |
91447636 A |
1049 | stack_size = (size+2)*sizeof(unsigned long); |
1050 | if (stack_size > total_size) | |
1c79356b | 1051 | return(LOAD_BADMACHO); |
91447636 A |
1052 | total_size -= stack_size; |
1053 | ||
1054 | /* | |
1055 | * Third argument is a kernel space pointer; it gets cast | |
1056 | * to the appropriate type in thread_userstack() based on | |
1057 | * the value of flavor. | |
1058 | */ | |
1059 | ret = thread_userstack(thread, flavor, (thread_state_t)ts, size, user_stack, customstack); | |
1c79356b A |
1060 | if (ret != KERN_SUCCESS) |
1061 | return(LOAD_FAILURE); | |
1062 | ts += size; /* ts is a (unsigned long *) */ | |
1063 | } | |
1064 | return(LOAD_SUCCESS); | |
1065 | } | |
1066 | ||
1067 | static | |
1068 | load_return_t | |
1069 | load_threadentry( | |
1070 | thread_t thread, | |
1071 | unsigned long *ts, | |
1072 | unsigned long total_size, | |
91447636 | 1073 | mach_vm_offset_t *entry_point |
1c79356b A |
1074 | ) |
1075 | { | |
1076 | kern_return_t ret; | |
1077 | unsigned long size; | |
1078 | int flavor; | |
91447636 | 1079 | unsigned long entry_size; |
1c79356b A |
1080 | |
1081 | /* | |
1082 | * Set the thread state. | |
1083 | */ | |
91447636 | 1084 | *entry_point = MACH_VM_MIN_ADDRESS; |
1c79356b A |
1085 | while (total_size > 0) { |
1086 | flavor = *ts++; | |
1087 | size = *ts++; | |
91447636 A |
1088 | entry_size = (size+2)*sizeof(unsigned long); |
1089 | if (entry_size > total_size) | |
1c79356b | 1090 | return(LOAD_BADMACHO); |
91447636 A |
1091 | total_size -= entry_size; |
1092 | /* | |
1093 | * Third argument is a kernel space pointer; it gets cast | |
1094 | * to the appropriate type in thread_entrypoint() based on | |
1095 | * the value of flavor. | |
1096 | */ | |
1097 | ret = thread_entrypoint(thread, flavor, (thread_state_t)ts, size, entry_point); | |
1c79356b A |
1098 | if (ret != KERN_SUCCESS) |
1099 | return(LOAD_FAILURE); | |
1100 | ts += size; /* ts is a (unsigned long *) */ | |
1101 | } | |
1102 | return(LOAD_SUCCESS); | |
1103 | } | |
1104 | ||
1c79356b A |
1105 | |
1106 | static | |
1107 | load_return_t | |
1108 | load_dylinker( | |
1109 | struct dylinker_command *lcp, | |
91447636 | 1110 | integer_t archbits, |
1c79356b | 1111 | vm_map_t map, |
91447636 | 1112 | thread_t thr_act, |
1c79356b | 1113 | int depth, |
55e303ae | 1114 | load_result_t *result, |
8ad349bb | 1115 | boolean_t clean_regions |
1c79356b A |
1116 | ) |
1117 | { | |
1118 | char *name; | |
1119 | char *p; | |
1120 | struct vnode *vp; | |
1121 | struct mach_header header; | |
91447636 A |
1122 | off_t file_offset; |
1123 | off_t macho_size; | |
1c79356b A |
1124 | vm_map_t copy_map; |
1125 | load_result_t myresult; | |
1126 | kern_return_t ret; | |
1127 | vm_map_copy_t tmp; | |
91447636 A |
1128 | mach_vm_offset_t dyl_start, map_addr; |
1129 | mach_vm_size_t dyl_length; | |
1c79356b A |
1130 | |
1131 | name = (char *)lcp + lcp->name.offset; | |
1132 | /* | |
1133 | * Check for a proper null terminated string. | |
1134 | */ | |
1135 | p = name; | |
1136 | do { | |
1137 | if (p >= (char *)lcp + lcp->cmdsize) | |
1138 | return(LOAD_BADMACHO); | |
1139 | } while (*p++); | |
1140 | ||
91447636 | 1141 | ret = get_macho_vnode(name, archbits, &header, &file_offset, &macho_size, &vp); |
1c79356b A |
1142 | if (ret) |
1143 | return (ret); | |
1144 | ||
1c79356b A |
1145 | /* |
1146 | * Load the Mach-O. | |
91447636 | 1147 | * Use a temporary map to do the work. |
1c79356b | 1148 | */ |
8ad349bb | 1149 | copy_map = vm_map_create(pmap_create(vm_map_round_page(macho_size)), |
91447636 A |
1150 | get_map_min(map), get_map_max(map), TRUE); |
1151 | if (VM_MAP_NULL == copy_map) { | |
1152 | ret = LOAD_RESOURCE; | |
1153 | goto out; | |
1154 | } | |
1155 | ||
1156 | myresult = load_result_null; | |
1c79356b | 1157 | |
0b4e3aa0 | 1158 | ret = parse_machfile(vp, copy_map, thr_act, &header, |
1c79356b | 1159 | file_offset, macho_size, |
91447636 | 1160 | FALSE, clean_regions, depth, &myresult); |
1c79356b A |
1161 | |
1162 | if (ret) | |
1163 | goto out; | |
1164 | ||
1165 | if (get_map_nentries(copy_map) > 0) { | |
1166 | ||
91447636 A |
1167 | dyl_start = mach_get_vm_start(copy_map); |
1168 | dyl_length = mach_get_vm_end(copy_map) - dyl_start; | |
1c79356b A |
1169 | |
1170 | map_addr = dyl_start; | |
91447636 | 1171 | ret = mach_vm_allocate(map, &map_addr, dyl_length, VM_FLAGS_FIXED); |
1c79356b | 1172 | if (ret != KERN_SUCCESS) { |
91447636 | 1173 | ret = mach_vm_allocate(map, &map_addr, dyl_length, VM_FLAGS_ANYWHERE); |
1c79356b A |
1174 | } |
1175 | ||
1176 | if (ret != KERN_SUCCESS) { | |
1177 | ret = LOAD_NOSPACE; | |
1178 | goto out; | |
1179 | ||
1180 | } | |
91447636 A |
1181 | ret = vm_map_copyin(copy_map, |
1182 | (vm_map_address_t)dyl_start, | |
1183 | (vm_map_size_t)dyl_length, | |
1184 | TRUE, &tmp); | |
1c79356b A |
1185 | if (ret != KERN_SUCCESS) { |
1186 | (void) vm_map_remove(map, | |
91447636 A |
1187 | vm_map_trunc_page(map_addr), |
1188 | vm_map_round_page(map_addr + dyl_length), | |
1189 | VM_MAP_NO_FLAGS); | |
1c79356b A |
1190 | goto out; |
1191 | } | |
1192 | ||
91447636 A |
1193 | ret = vm_map_copy_overwrite(map, |
1194 | (vm_map_address_t)map_addr, | |
1195 | tmp, FALSE); | |
1c79356b | 1196 | if (ret != KERN_SUCCESS) { |
91447636 A |
1197 | vm_map_copy_discard(tmp); |
1198 | (void) vm_map_remove(map, | |
1199 | vm_map_trunc_page(map_addr), | |
1200 | vm_map_round_page(map_addr + dyl_length), | |
1201 | VM_MAP_NO_FLAGS); | |
1202 | goto out; | |
1203 | } | |
1c79356b A |
1204 | |
1205 | if (map_addr != dyl_start) | |
1206 | myresult.entry_point += (map_addr - dyl_start); | |
1207 | } else | |
1208 | ret = LOAD_FAILURE; | |
1209 | ||
1210 | if (ret == LOAD_SUCCESS) { | |
1211 | result->dynlinker = TRUE; | |
1212 | result->entry_point = myresult.entry_point; | |
91447636 | 1213 | (void)ubc_map(vp, PROT_EXEC); |
1c79356b A |
1214 | } |
1215 | out: | |
1216 | vm_map_deallocate(copy_map); | |
1217 | ||
91447636 | 1218 | vnode_put(vp); |
1c79356b A |
1219 | return (ret); |
1220 | ||
1221 | } | |
1222 | ||
91447636 A |
1223 | /* |
1224 | * This routine exists to support the load_dylinker(). | |
1225 | * | |
1226 | * This routine has its own, separate, understanding of the FAT file format, | |
1227 | * which is terrifically unfortunate. | |
1228 | */ | |
1c79356b A |
1229 | static |
1230 | load_return_t | |
1231 | get_macho_vnode( | |
1232 | char *path, | |
91447636 | 1233 | integer_t archbits, |
1c79356b | 1234 | struct mach_header *mach_header, |
91447636 A |
1235 | off_t *file_offset, |
1236 | off_t *macho_size, | |
1c79356b A |
1237 | struct vnode **vpp |
1238 | ) | |
1239 | { | |
1240 | struct vnode *vp; | |
91447636 | 1241 | struct vfs_context context; |
1c79356b A |
1242 | struct nameidata nid, *ndp; |
1243 | struct proc *p = current_proc(); /* XXXX */ | |
1244 | boolean_t is_fat; | |
1245 | struct fat_arch fat_arch; | |
55e303ae | 1246 | int error = LOAD_SUCCESS; |
1c79356b A |
1247 | int resid; |
1248 | union { | |
1249 | struct mach_header mach_header; | |
1250 | struct fat_header fat_header; | |
1251 | char pad[512]; | |
1252 | } header; | |
0b4e3aa0 | 1253 | off_t fsize = (off_t)0; |
91447636 | 1254 | struct ucred *cred = kauth_cred_get(); |
55e303ae | 1255 | int err2; |
1c79356b | 1256 | |
91447636 A |
1257 | context.vc_proc = p; |
1258 | context.vc_ucred = cred; | |
1259 | ||
1c79356b | 1260 | ndp = &nid; |
1c79356b A |
1261 | |
1262 | /* init the namei data to point the file user's program name */ | |
91447636 | 1263 | NDINIT(ndp, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE32, CAST_USER_ADDR_T(path), &context); |
1c79356b | 1264 | |
91447636 | 1265 | if ((error = namei(ndp)) != 0) { |
55e303ae A |
1266 | if (error == ENOENT) |
1267 | error = LOAD_ENOENT; | |
1268 | else | |
1269 | error = LOAD_FAILURE; | |
1c79356b | 1270 | return(error); |
55e303ae | 1271 | } |
91447636 | 1272 | nameidone(ndp); |
1c79356b A |
1273 | vp = ndp->ni_vp; |
1274 | ||
1275 | /* check for regular file */ | |
1276 | if (vp->v_type != VREG) { | |
55e303ae | 1277 | error = LOAD_PROTECT; |
1c79356b A |
1278 | goto bad1; |
1279 | } | |
1280 | ||
91447636 A |
1281 | /* get size */ |
1282 | if ((error = vnode_size(vp, &fsize, &context)) != 0) { | |
55e303ae | 1283 | error = LOAD_FAILURE; |
1c79356b | 1284 | goto bad1; |
55e303ae | 1285 | } |
1c79356b A |
1286 | |
1287 | /* Check mount point */ | |
1288 | if (vp->v_mount->mnt_flag & MNT_NOEXEC) { | |
55e303ae | 1289 | error = LOAD_PROTECT; |
1c79356b A |
1290 | goto bad1; |
1291 | } | |
1292 | ||
91447636 A |
1293 | /* check access */ |
1294 | if ((error = vnode_authorize(vp, NULL, KAUTH_VNODE_EXECUTE, &context)) != 0) { | |
55e303ae | 1295 | error = LOAD_PROTECT; |
1c79356b | 1296 | goto bad1; |
55e303ae | 1297 | } |
0b4e3aa0 | 1298 | |
1c79356b | 1299 | /* try to open it */ |
91447636 | 1300 | if ((error = VNOP_OPEN(vp, FREAD, &context)) != 0) { |
55e303ae | 1301 | error = LOAD_PROTECT; |
1c79356b | 1302 | goto bad1; |
0b4e3aa0 A |
1303 | } |
1304 | ||
91447636 A |
1305 | if ((error = vn_rdwr(UIO_READ, vp, (caddr_t)&header, sizeof(header), 0, |
1306 | UIO_SYSSPACE32, IO_NODELOCKED, cred, &resid, p)) != 0) { | |
55e303ae | 1307 | error = LOAD_IOERROR; |
1c79356b | 1308 | goto bad2; |
55e303ae | 1309 | } |
1c79356b | 1310 | |
91447636 A |
1311 | if (header.mach_header.magic == MH_MAGIC || |
1312 | header.mach_header.magic == MH_MAGIC_64) | |
1c79356b A |
1313 | is_fat = FALSE; |
1314 | else if (header.fat_header.magic == FAT_MAGIC || | |
1315 | header.fat_header.magic == FAT_CIGAM) | |
1316 | is_fat = TRUE; | |
1317 | else { | |
1318 | error = LOAD_BADMACHO; | |
1319 | goto bad2; | |
1320 | } | |
1321 | ||
1322 | if (is_fat) { | |
0b4e3aa0 | 1323 | /* Look up our architecture in the fat file. */ |
91447636 | 1324 | error = fatfile_getarch_with_bits(vp, archbits, (vm_offset_t)(&header.fat_header), &fat_arch); |
0b4e3aa0 | 1325 | if (error != LOAD_SUCCESS) |
1c79356b | 1326 | goto bad2; |
0b4e3aa0 A |
1327 | |
1328 | /* Read the Mach-O header out of it */ | |
55e303ae | 1329 | error = vn_rdwr(UIO_READ, vp, (caddr_t)&header.mach_header, |
1c79356b | 1330 | sizeof(header.mach_header), fat_arch.offset, |
91447636 | 1331 | UIO_SYSSPACE32, IO_NODELOCKED, cred, &resid, p); |
1c79356b | 1332 | if (error) { |
55e303ae | 1333 | error = LOAD_IOERROR; |
1c79356b A |
1334 | goto bad2; |
1335 | } | |
1336 | ||
0b4e3aa0 | 1337 | /* Is this really a Mach-O? */ |
91447636 A |
1338 | if (header.mach_header.magic != MH_MAGIC && |
1339 | header.mach_header.magic != MH_MAGIC_64) { | |
1c79356b A |
1340 | error = LOAD_BADMACHO; |
1341 | goto bad2; | |
1342 | } | |
0b4e3aa0 | 1343 | |
1c79356b | 1344 | *file_offset = fat_arch.offset; |
0b4e3aa0 | 1345 | *macho_size = fsize = fat_arch.size; |
1c79356b | 1346 | } else { |
91447636 A |
1347 | /* |
1348 | * Force get_macho_vnode() to fail if the architecture bits | |
1349 | * do not match the expected architecture bits. This in | |
1350 | * turn causes load_dylinker() to fail for the same reason, | |
1351 | * so it ensures the dynamic linker and the binary are in | |
1352 | * lock-step. This is potentially bad, if we ever add to | |
1353 | * the CPU_ARCH_* bits any bits that are desirable but not | |
1354 | * required, since the dynamic linker might work, but we will | |
1355 | * refuse to load it because of this check. | |
1356 | */ | |
1357 | if ((cpu_type_t)(header.mach_header.cputype & CPU_ARCH_MASK) != archbits) | |
1358 | return(LOAD_BADARCH); | |
0b4e3aa0 | 1359 | |
1c79356b | 1360 | *file_offset = 0; |
91447636 | 1361 | *macho_size = fsize; |
1c79356b A |
1362 | } |
1363 | ||
0b4e3aa0 A |
1364 | *mach_header = header.mach_header; |
1365 | *vpp = vp; | |
91447636 A |
1366 | |
1367 | ubc_setsize(vp, fsize); | |
0b4e3aa0 | 1368 | |
0b4e3aa0 A |
1369 | return (error); |
1370 | ||
1c79356b | 1371 | bad2: |
91447636 A |
1372 | err2 = VNOP_CLOSE(vp, FREAD, &context); |
1373 | vnode_put(vp); | |
1c79356b | 1374 | return (error); |
0b4e3aa0 | 1375 | |
1c79356b | 1376 | bad1: |
91447636 | 1377 | vnode_put(vp); |
1c79356b A |
1378 | return(error); |
1379 | } |