]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_OSREFERENCE_HEADER_START@ | |
5 | * | |
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@ | |
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 | */ | |
41 | ||
42 | #include <sys/param.h> | |
43 | #include <sys/vnode_internal.h> | |
44 | #include <sys/uio.h> | |
45 | #include <sys/namei.h> | |
46 | #include <sys/proc_internal.h> | |
47 | #include <sys/kauth.h> | |
48 | #include <sys/stat.h> | |
49 | #include <sys/malloc.h> | |
50 | #include <sys/mount_internal.h> | |
51 | #include <sys/fcntl.h> | |
52 | #include <sys/ubc_internal.h> | |
53 | #include <sys/imgact.h> | |
54 | ||
55 | #include <mach/mach_types.h> | |
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> | |
64 | ||
65 | #include <kern/kern_types.h> | |
66 | #include <kern/cpu_number.h> | |
67 | #include <kern/mach_loader.h> | |
68 | #include <kern/kalloc.h> | |
69 | #include <kern/task.h> | |
70 | #include <kern/thread.h> | |
71 | ||
72 | #include <mach-o/fat.h> | |
73 | #include <mach-o/loader.h> | |
74 | ||
75 | #include <vm/pmap.h> | |
76 | #include <vm/vm_map.h> | |
77 | #include <vm/vm_kern.h> | |
78 | #include <vm/vm_pager.h> | |
79 | #include <vm/vnode_pager.h> | |
80 | #include <vm/vm_shared_memory_server.h> | |
81 | #include <vm/vm_protos.h> | |
82 | ||
83 | /* | |
84 | * XXX vm/pmap.h should not treat these prototypes as MACH_KERNEL_PRIVATE | |
85 | * when KERNEL is defined. | |
86 | */ | |
87 | extern pmap_t pmap_create(vm_map_size_t size); | |
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 */ | |
103 | extern int grade_binary(cpu_type_t exectype, cpu_subtype_t execsubtype); | |
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 | }; | |
121 | ||
122 | /* | |
123 | * Prototypes of static functions. | |
124 | */ | |
125 | static load_return_t | |
126 | parse_machfile( | |
127 | struct vnode *vp, | |
128 | vm_map_t map, | |
129 | thread_t thr_act, | |
130 | struct mach_header *header, | |
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 | |
140 | load_segment( | |
141 | struct segment_command *scp, | |
142 | void * pager, | |
143 | off_t pager_offset, | |
144 | off_t macho_size, | |
145 | off_t end_of_file, | |
146 | vm_map_t map, | |
147 | load_result_t *result | |
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 | |
162 | load_unixthread( | |
163 | struct thread_command *tcp, | |
164 | thread_t thr_act, | |
165 | load_result_t *result | |
166 | ); | |
167 | ||
168 | static load_return_t | |
169 | load_thread( | |
170 | struct thread_command *tcp, | |
171 | thread_t thr_act, | |
172 | load_result_t *result | |
173 | ); | |
174 | ||
175 | static load_return_t | |
176 | load_threadstate( | |
177 | thread_t thread, | |
178 | unsigned long *ts, | |
179 | unsigned long total_size | |
180 | ); | |
181 | ||
182 | static load_return_t | |
183 | load_threadstack( | |
184 | thread_t thread, | |
185 | unsigned long *ts, | |
186 | unsigned long total_size, | |
187 | mach_vm_offset_t *user_stack, | |
188 | int *customstack | |
189 | ); | |
190 | ||
191 | static load_return_t | |
192 | load_threadentry( | |
193 | thread_t thread, | |
194 | unsigned long *ts, | |
195 | unsigned long total_size, | |
196 | mach_vm_offset_t *entry_point | |
197 | ); | |
198 | ||
199 | static load_return_t | |
200 | load_dylinker( | |
201 | struct dylinker_command *lcp, | |
202 | integer_t archbits, | |
203 | vm_map_t map, | |
204 | thread_t thr_act, | |
205 | int depth, | |
206 | load_result_t *result, | |
207 | boolean_t clean_regions | |
208 | ); | |
209 | ||
210 | static load_return_t | |
211 | get_macho_vnode( | |
212 | char *path, | |
213 | integer_t archbits, | |
214 | struct mach_header *mach_header, | |
215 | off_t *file_offset, | |
216 | off_t *macho_size, | |
217 | struct vnode **vpp | |
218 | ); | |
219 | ||
220 | load_return_t | |
221 | load_machfile( | |
222 | struct image_params *imgp, | |
223 | struct mach_header *header, | |
224 | thread_t thr_act, | |
225 | vm_map_t new_map, | |
226 | boolean_t clean_regions, | |
227 | load_result_t *result | |
228 | ) | |
229 | { | |
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 */ | |
235 | vm_map_t map; | |
236 | vm_map_t old_map; | |
237 | load_result_t myresult; | |
238 | load_return_t lret; | |
239 | boolean_t create_map = TRUE; | |
240 | ||
241 | if (new_map != VM_MAP_NULL) { | |
242 | create_map = FALSE; | |
243 | } | |
244 | ||
245 | if (create_map) { | |
246 | old_map = current_map(); | |
247 | #ifdef i386 | |
248 | pmap = get_task_pmap(current_task()); | |
249 | pmap_reference(pmap); | |
250 | #else | |
251 | pmap = pmap_create((vm_map_size_t) 0); | |
252 | #endif | |
253 | map = vm_map_create(pmap, | |
254 | get_map_min(old_map), | |
255 | get_map_max(old_map), | |
256 | TRUE); /**** FIXME ****/ | |
257 | } else | |
258 | map = new_map; | |
259 | ||
260 | if (!result) | |
261 | result = &myresult; | |
262 | ||
263 | *result = load_result_null; | |
264 | ||
265 | lret = parse_machfile(vp, map, thr_act, header, file_offset, macho_size, | |
266 | ((imgp->ip_flags & IMGPF_IS_64BIT) == 0), /* shared regions? */ | |
267 | clean_regions, 0, result); | |
268 | ||
269 | if (lret != LOAD_SUCCESS) { | |
270 | if (create_map) { | |
271 | vm_map_deallocate(map); /* will lose pmap reference too */ | |
272 | } | |
273 | return(lret); | |
274 | } | |
275 | ||
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 | * | |
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. | |
284 | * That lets us get off the pmap associated with it, and | |
285 | * then we can release it. | |
286 | */ | |
287 | if (create_map) { | |
288 | task_halt(current_task()); | |
289 | ||
290 | old_map = swap_task_map(current_task(), map); | |
291 | #ifndef i386 | |
292 | pmap_switch(pmap); /* Make sure we are using the new pmap */ | |
293 | #endif | |
294 | vm_map_deallocate(old_map); | |
295 | } | |
296 | return(LOAD_SUCCESS); | |
297 | } | |
298 | ||
299 | int dylink_test = 1; | |
300 | ||
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 | */ | |
310 | static | |
311 | load_return_t | |
312 | parse_machfile( | |
313 | struct vnode *vp, | |
314 | vm_map_t map, | |
315 | thread_t thr_act, | |
316 | struct mach_header *header, | |
317 | off_t file_offset, | |
318 | off_t macho_size, | |
319 | boolean_t shared_regions, | |
320 | boolean_t clean_regions, | |
321 | int depth, | |
322 | load_result_t *result | |
323 | ) | |
324 | { | |
325 | uint32_t ncmds; | |
326 | struct load_command *lcp; | |
327 | struct dylinker_command *dlp = 0; | |
328 | integer_t dlarchbits = 0; | |
329 | void * pager; | |
330 | load_return_t ret = LOAD_SUCCESS; | |
331 | caddr_t addr; | |
332 | void * kl_addr; | |
333 | vm_size_t size,kl_size; | |
334 | size_t offset; | |
335 | size_t oldoffset; /* for overflow check */ | |
336 | int pass; | |
337 | struct proc *p = current_proc(); /* XXXX */ | |
338 | int error; | |
339 | int resid=0; | |
340 | task_t task; | |
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 | } | |
348 | ||
349 | /* | |
350 | * Break infinite recursion | |
351 | */ | |
352 | if (depth > 6) | |
353 | return(LOAD_FAILURE); | |
354 | ||
355 | task = (task_t)get_threadtask(thr_act); | |
356 | ||
357 | depth++; | |
358 | ||
359 | /* | |
360 | * Check to see if right machine type. | |
361 | */ | |
362 | if (((cpu_type_t)(header->cputype & ~CPU_ARCH_MASK) != cpu_type()) || | |
363 | !grade_binary(header->cputype, header->cpusubtype)) | |
364 | return(LOAD_BADARCH); | |
365 | ||
366 | abi64 = ((header->cputype & CPU_ARCH_ABI64) == CPU_ARCH_ABI64); | |
367 | ||
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 | */ | |
402 | if ((mach_header_sz + header->sizeofcmds) > macho_size) | |
403 | return(LOAD_BADMACHO); | |
404 | ||
405 | /* | |
406 | * Round size of Mach-O commands up to page boundry. | |
407 | */ | |
408 | size = round_page(mach_header_sz + header->sizeofcmds); | |
409 | if (size <= 0) | |
410 | return(LOAD_BADMACHO); | |
411 | ||
412 | /* | |
413 | * Map the load commands into kernel memory. | |
414 | */ | |
415 | addr = 0; | |
416 | kl_size = size; | |
417 | kl_addr = kalloc(size); | |
418 | addr = (caddr_t)kl_addr; | |
419 | if (addr == NULL) | |
420 | return(LOAD_NOSPACE); | |
421 | ||
422 | error = vn_rdwr(UIO_READ, vp, addr, size, file_offset, | |
423 | UIO_SYSSPACE32, 0, kauth_cred_get(), &resid, p); | |
424 | if (error) { | |
425 | if (kl_addr ) | |
426 | kfree(kl_addr, kl_size); | |
427 | return(LOAD_IOERROR); | |
428 | } | |
429 | /* (void)ubc_map(vp, PROT_EXEC); */ /* NOT HERE */ | |
430 | ||
431 | /* | |
432 | * Scan through the commands, processing each one as necessary. | |
433 | */ | |
434 | for (pass = 1; pass <= 2; pass++) { | |
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 | */ | |
441 | offset = mach_header_sz; | |
442 | ncmds = header->ncmds; | |
443 | while (ncmds--) { | |
444 | /* | |
445 | * Get a pointer to the command. | |
446 | */ | |
447 | lcp = (struct load_command *)(addr + offset); | |
448 | oldoffset = offset; | |
449 | offset += lcp->cmdsize; | |
450 | ||
451 | /* | |
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. | |
458 | */ | |
459 | if (oldoffset > offset || | |
460 | lcp->cmdsize < sizeof(struct load_command) || | |
461 | offset > header->sizeofcmds + mach_header_sz) { | |
462 | ret = LOAD_BADMACHO; | |
463 | break; | |
464 | } | |
465 | ||
466 | /* | |
467 | * Act on struct load_command's for which kernel | |
468 | * intervention is required. | |
469 | */ | |
470 | switch(lcp->cmd) { | |
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; | |
483 | case LC_SEGMENT: | |
484 | if (pass != 1) | |
485 | break; | |
486 | ret = load_segment( | |
487 | (struct segment_command *) lcp, | |
488 | pager, | |
489 | file_offset, | |
490 | macho_size, | |
491 | ubc_getsize(vp), | |
492 | map, | |
493 | result); | |
494 | break; | |
495 | case LC_THREAD: | |
496 | if (pass != 2) | |
497 | break; | |
498 | ret = load_thread((struct thread_command *)lcp, | |
499 | thr_act, | |
500 | result); | |
501 | break; | |
502 | case LC_UNIXTHREAD: | |
503 | if (pass != 2) | |
504 | break; | |
505 | ret = load_unixthread( | |
506 | (struct thread_command *) lcp, | |
507 | thr_act, | |
508 | result); | |
509 | break; | |
510 | case LC_LOAD_DYLINKER: | |
511 | if (pass != 2) | |
512 | break; | |
513 | if ((depth == 1) && (dlp == 0)) { | |
514 | dlp = (struct dylinker_command *)lcp; | |
515 | dlarchbits = (header->cputype & CPU_ARCH_MASK); | |
516 | } else { | |
517 | ret = LOAD_FAILURE; | |
518 | } | |
519 | break; | |
520 | default: | |
521 | /* Other commands are ignored by the kernel */ | |
522 | ret = LOAD_SUCCESS; | |
523 | break; | |
524 | } | |
525 | if (ret != LOAD_SUCCESS) | |
526 | break; | |
527 | } | |
528 | if (ret != LOAD_SUCCESS) | |
529 | break; | |
530 | } | |
531 | if (ret == LOAD_SUCCESS) { | |
532 | ||
533 | if (shared_regions) { | |
534 | vm_offset_t vmaddr; | |
535 | shared_region_mapping_t shared_region; | |
536 | struct shared_region_task_mappings map_info; | |
537 | shared_region_mapping_t next; | |
538 | ||
539 | RedoLookup: | |
540 | vm_get_shared_region(task, &shared_region); | |
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), | |
551 | &(map_info.fs_base), | |
552 | &(map_info.system), | |
553 | &(map_info.flags), &next); | |
554 | ||
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( | |
569 | (shared_region_mapping_t)map_info.self); | |
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 | } | |
580 | } | |
581 | ||
582 | if (dylink_test) { | |
583 | p->p_flag |= P_NOSHLIB; /* no shlibs in use */ | |
584 | vmaddr = map_info.client_base; | |
585 | if(clean_regions) { | |
586 | vm_map(map, &vmaddr, map_info.text_size, | |
587 | 0, SHARED_LIB_ALIAS|VM_FLAGS_FIXED, | |
588 | map_info.text_region, 0, FALSE, | |
589 | VM_PROT_READ, VM_PROT_READ, VM_INHERIT_SHARE); | |
590 | } else { | |
591 | vm_map(map, &vmaddr, map_info.text_size, 0, | |
592 | (VM_MEMORY_SHARED_PMAP << 24) | |
593 | | SHARED_LIB_ALIAS | VM_FLAGS_FIXED, | |
594 | map_info.text_region, 0, FALSE, | |
595 | VM_PROT_READ, VM_PROT_READ, VM_INHERIT_SHARE); | |
596 | } | |
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, | |
600 | map_info.data_region, 0, TRUE, | |
601 | VM_PROT_READ, VM_PROT_READ, VM_INHERIT_SHARE); | |
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 | ||
624 | vmaddr = map_info.client_base; | |
625 | vm_map(map, &vmaddr, map_info.text_size, | |
626 | 0, SHARED_LIB_ALIAS | VM_FLAGS_FIXED, | |
627 | map_info.text_region, 0, FALSE, | |
628 | VM_PROT_READ, VM_PROT_READ, VM_INHERIT_SHARE); | |
629 | } | |
630 | } | |
631 | } | |
632 | if (dlp != 0) | |
633 | ret = load_dylinker(dlp, dlarchbits, map, thr_act, depth, result, clean_regions); | |
634 | ||
635 | if(depth == 1) { | |
636 | if (result->thread_count == 0) | |
637 | ret = LOAD_FAILURE; | |
638 | #ifdef __ppc__ | |
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 | } | |
645 | #endif | |
646 | } | |
647 | } | |
648 | ||
649 | if (kl_addr ) | |
650 | kfree(kl_addr, kl_size); | |
651 | ||
652 | if (ret == LOAD_SUCCESS) | |
653 | (void)ubc_map(vp, PROT_EXEC); | |
654 | ||
655 | return(ret); | |
656 | } | |
657 | ||
658 | static | |
659 | load_return_t | |
660 | load_segment( | |
661 | struct segment_command *scp, | |
662 | void * pager, | |
663 | off_t pager_offset, | |
664 | off_t macho_size, | |
665 | __unused off_t end_of_file, | |
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; | |
673 | vm_prot_t initprot; | |
674 | vm_prot_t maxprot; | |
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 | ||
683 | seg_size = round_page(scp->vmsize); | |
684 | if (seg_size == 0) | |
685 | return(KERN_SUCCESS); | |
686 | ||
687 | /* | |
688 | * Round sizes to page size. | |
689 | */ | |
690 | map_size = round_page(scp->filesize); | |
691 | map_addr = trunc_page(scp->vmaddr); | |
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, | |
702 | &map_addr, map_size, (vm_offset_t)0, | |
703 | VM_FLAGS_FIXED, pager, map_offset, TRUE, | |
704 | initprot, maxprot, | |
705 | VM_INHERIT_DEFAULT); | |
706 | if (ret != KERN_SUCCESS) | |
707 | return(LOAD_NOSPACE); | |
708 | ||
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 | ||
718 | ret = vm_allocate(kernel_map, &tmp, delta_size, VM_FLAGS_ANYWHERE); | |
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 | ||
743 | ret = vm_allocate(map, &tmp, delta_size, VM_FLAGS_FIXED); | |
744 | if (ret != KERN_SUCCESS) | |
745 | return(LOAD_NOSPACE); | |
746 | } | |
747 | ||
748 | /* | |
749 | * Set protection values. (Note: ignore errors!) | |
750 | */ | |
751 | ||
752 | if (scp->maxprot != VM_PROT_DEFAULT) { | |
753 | (void) vm_protect(map, | |
754 | map_addr, seg_size, | |
755 | TRUE, scp->maxprot); | |
756 | } | |
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); | |
765 | } | |
766 | ||
767 | static | |
768 | load_return_t | |
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, | |
776 | load_result_t *result | |
777 | ) | |
778 | { | |
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; | |
784 | ||
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); | |
835 | return (LOAD_FAILURE); | |
836 | } | |
837 | ||
838 | (void) vm_deallocate(kernel_map, tmp, delta_size); | |
839 | } | |
840 | #endif /* FIXME */ | |
841 | } | |
842 | ||
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; | |
851 | ||
852 | ret = mach_vm_allocate(map, &tmp, delta_size, VM_FLAGS_FIXED); | |
853 | if (ret != KERN_SUCCESS) | |
854 | return(LOAD_NOSPACE); | |
855 | } | |
856 | ||
857 | /* | |
858 | * Set protection values. (Note: ignore errors!) | |
859 | */ | |
860 | ||
861 | if (scp64->maxprot != VM_PROT_DEFAULT) { | |
862 | (void) mach_vm_protect(map, | |
863 | map_addr, seg_size, | |
864 | TRUE, scp64->maxprot); | |
865 | } | |
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); | |
874 | } | |
875 | ||
876 | static | |
877 | load_return_t | |
878 | load_thread( | |
879 | struct thread_command *tcp, | |
880 | thread_t thread, | |
881 | load_result_t *result | |
882 | ) | |
883 | { | |
884 | kern_return_t kret; | |
885 | load_return_t lret; | |
886 | task_t task; | |
887 | int customstack=0; | |
888 | ||
889 | task = get_threadtask(thread); | |
890 | ||
891 | /* if count is 0; same as thr_act */ | |
892 | if (result->thread_count != 0) { | |
893 | kret = thread_create(task, &thread); | |
894 | if (kret != KERN_SUCCESS) | |
895 | return(LOAD_RESOURCE); | |
896 | thread_deallocate(thread); | |
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) { | |
907 | lret = load_threadstack(thread, | |
908 | (unsigned long *)(((vm_offset_t)tcp) + | |
909 | sizeof(struct thread_command)), | |
910 | tcp->cmdsize - sizeof(struct thread_command), | |
911 | &result->user_stack, | |
912 | &customstack); | |
913 | if (customstack) | |
914 | result->customstack = 1; | |
915 | else | |
916 | result->customstack = 0; | |
917 | ||
918 | if (lret != LOAD_SUCCESS) | |
919 | return(lret); | |
920 | ||
921 | lret = load_threadentry(thread, | |
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 | ||
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 | ||
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; | |
1001 | unsigned long thread_size; | |
1002 | ||
1003 | ret = thread_state_initialize( thread ); | |
1004 | if (ret != KERN_SUCCESS) | |
1005 | return(LOAD_FAILURE); | |
1006 | ||
1007 | /* | |
1008 | * Set the new thread state; iterate through the state flavors in | |
1009 | * the mach-o file. | |
1010 | */ | |
1011 | while (total_size > 0) { | |
1012 | flavor = *ts++; | |
1013 | size = *ts++; | |
1014 | thread_size = (size+2)*sizeof(unsigned long); | |
1015 | if (thread_size > total_size) | |
1016 | return(LOAD_BADMACHO); | |
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); | |
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, | |
1037 | user_addr_t *user_stack, | |
1038 | int *customstack | |
1039 | ) | |
1040 | { | |
1041 | kern_return_t ret; | |
1042 | unsigned long size; | |
1043 | int flavor; | |
1044 | unsigned long stack_size; | |
1045 | ||
1046 | while (total_size > 0) { | |
1047 | flavor = *ts++; | |
1048 | size = *ts++; | |
1049 | stack_size = (size+2)*sizeof(unsigned long); | |
1050 | if (stack_size > total_size) | |
1051 | return(LOAD_BADMACHO); | |
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); | |
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, | |
1073 | mach_vm_offset_t *entry_point | |
1074 | ) | |
1075 | { | |
1076 | kern_return_t ret; | |
1077 | unsigned long size; | |
1078 | int flavor; | |
1079 | unsigned long entry_size; | |
1080 | ||
1081 | /* | |
1082 | * Set the thread state. | |
1083 | */ | |
1084 | *entry_point = MACH_VM_MIN_ADDRESS; | |
1085 | while (total_size > 0) { | |
1086 | flavor = *ts++; | |
1087 | size = *ts++; | |
1088 | entry_size = (size+2)*sizeof(unsigned long); | |
1089 | if (entry_size > total_size) | |
1090 | return(LOAD_BADMACHO); | |
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); | |
1098 | if (ret != KERN_SUCCESS) | |
1099 | return(LOAD_FAILURE); | |
1100 | ts += size; /* ts is a (unsigned long *) */ | |
1101 | } | |
1102 | return(LOAD_SUCCESS); | |
1103 | } | |
1104 | ||
1105 | ||
1106 | static | |
1107 | load_return_t | |
1108 | load_dylinker( | |
1109 | struct dylinker_command *lcp, | |
1110 | integer_t archbits, | |
1111 | vm_map_t map, | |
1112 | thread_t thr_act, | |
1113 | int depth, | |
1114 | load_result_t *result, | |
1115 | boolean_t clean_regions | |
1116 | ) | |
1117 | { | |
1118 | char *name; | |
1119 | char *p; | |
1120 | struct vnode *vp; | |
1121 | struct mach_header header; | |
1122 | off_t file_offset; | |
1123 | off_t macho_size; | |
1124 | vm_map_t copy_map; | |
1125 | load_result_t myresult; | |
1126 | kern_return_t ret; | |
1127 | vm_map_copy_t tmp; | |
1128 | mach_vm_offset_t dyl_start, map_addr; | |
1129 | mach_vm_size_t dyl_length; | |
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 | ||
1141 | ret = get_macho_vnode(name, archbits, &header, &file_offset, &macho_size, &vp); | |
1142 | if (ret) | |
1143 | return (ret); | |
1144 | ||
1145 | /* | |
1146 | * Load the Mach-O. | |
1147 | * Use a temporary map to do the work. | |
1148 | */ | |
1149 | copy_map = vm_map_create(pmap_create(vm_map_round_page(macho_size)), | |
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; | |
1157 | ||
1158 | ret = parse_machfile(vp, copy_map, thr_act, &header, | |
1159 | file_offset, macho_size, | |
1160 | FALSE, clean_regions, depth, &myresult); | |
1161 | ||
1162 | if (ret) | |
1163 | goto out; | |
1164 | ||
1165 | if (get_map_nentries(copy_map) > 0) { | |
1166 | ||
1167 | dyl_start = mach_get_vm_start(copy_map); | |
1168 | dyl_length = mach_get_vm_end(copy_map) - dyl_start; | |
1169 | ||
1170 | map_addr = dyl_start; | |
1171 | ret = mach_vm_allocate(map, &map_addr, dyl_length, VM_FLAGS_FIXED); | |
1172 | if (ret != KERN_SUCCESS) { | |
1173 | ret = mach_vm_allocate(map, &map_addr, dyl_length, VM_FLAGS_ANYWHERE); | |
1174 | } | |
1175 | ||
1176 | if (ret != KERN_SUCCESS) { | |
1177 | ret = LOAD_NOSPACE; | |
1178 | goto out; | |
1179 | ||
1180 | } | |
1181 | ret = vm_map_copyin(copy_map, | |
1182 | (vm_map_address_t)dyl_start, | |
1183 | (vm_map_size_t)dyl_length, | |
1184 | TRUE, &tmp); | |
1185 | if (ret != KERN_SUCCESS) { | |
1186 | (void) vm_map_remove(map, | |
1187 | vm_map_trunc_page(map_addr), | |
1188 | vm_map_round_page(map_addr + dyl_length), | |
1189 | VM_MAP_NO_FLAGS); | |
1190 | goto out; | |
1191 | } | |
1192 | ||
1193 | ret = vm_map_copy_overwrite(map, | |
1194 | (vm_map_address_t)map_addr, | |
1195 | tmp, FALSE); | |
1196 | if (ret != KERN_SUCCESS) { | |
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 | } | |
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; | |
1213 | (void)ubc_map(vp, PROT_EXEC); | |
1214 | } | |
1215 | out: | |
1216 | vm_map_deallocate(copy_map); | |
1217 | ||
1218 | vnode_put(vp); | |
1219 | return (ret); | |
1220 | ||
1221 | } | |
1222 | ||
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 | */ | |
1229 | static | |
1230 | load_return_t | |
1231 | get_macho_vnode( | |
1232 | char *path, | |
1233 | integer_t archbits, | |
1234 | struct mach_header *mach_header, | |
1235 | off_t *file_offset, | |
1236 | off_t *macho_size, | |
1237 | struct vnode **vpp | |
1238 | ) | |
1239 | { | |
1240 | struct vnode *vp; | |
1241 | struct vfs_context context; | |
1242 | struct nameidata nid, *ndp; | |
1243 | struct proc *p = current_proc(); /* XXXX */ | |
1244 | boolean_t is_fat; | |
1245 | struct fat_arch fat_arch; | |
1246 | int error = LOAD_SUCCESS; | |
1247 | int resid; | |
1248 | union { | |
1249 | struct mach_header mach_header; | |
1250 | struct fat_header fat_header; | |
1251 | char pad[512]; | |
1252 | } header; | |
1253 | off_t fsize = (off_t)0; | |
1254 | struct ucred *cred = kauth_cred_get(); | |
1255 | int err2; | |
1256 | ||
1257 | context.vc_proc = p; | |
1258 | context.vc_ucred = cred; | |
1259 | ||
1260 | ndp = &nid; | |
1261 | ||
1262 | /* init the namei data to point the file user's program name */ | |
1263 | NDINIT(ndp, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE32, CAST_USER_ADDR_T(path), &context); | |
1264 | ||
1265 | if ((error = namei(ndp)) != 0) { | |
1266 | if (error == ENOENT) | |
1267 | error = LOAD_ENOENT; | |
1268 | else | |
1269 | error = LOAD_FAILURE; | |
1270 | return(error); | |
1271 | } | |
1272 | nameidone(ndp); | |
1273 | vp = ndp->ni_vp; | |
1274 | ||
1275 | /* check for regular file */ | |
1276 | if (vp->v_type != VREG) { | |
1277 | error = LOAD_PROTECT; | |
1278 | goto bad1; | |
1279 | } | |
1280 | ||
1281 | /* get size */ | |
1282 | if ((error = vnode_size(vp, &fsize, &context)) != 0) { | |
1283 | error = LOAD_FAILURE; | |
1284 | goto bad1; | |
1285 | } | |
1286 | ||
1287 | /* Check mount point */ | |
1288 | if (vp->v_mount->mnt_flag & MNT_NOEXEC) { | |
1289 | error = LOAD_PROTECT; | |
1290 | goto bad1; | |
1291 | } | |
1292 | ||
1293 | /* check access */ | |
1294 | if ((error = vnode_authorize(vp, NULL, KAUTH_VNODE_EXECUTE, &context)) != 0) { | |
1295 | error = LOAD_PROTECT; | |
1296 | goto bad1; | |
1297 | } | |
1298 | ||
1299 | /* try to open it */ | |
1300 | if ((error = VNOP_OPEN(vp, FREAD, &context)) != 0) { | |
1301 | error = LOAD_PROTECT; | |
1302 | goto bad1; | |
1303 | } | |
1304 | ||
1305 | if ((error = vn_rdwr(UIO_READ, vp, (caddr_t)&header, sizeof(header), 0, | |
1306 | UIO_SYSSPACE32, IO_NODELOCKED, cred, &resid, p)) != 0) { | |
1307 | error = LOAD_IOERROR; | |
1308 | goto bad2; | |
1309 | } | |
1310 | ||
1311 | if (header.mach_header.magic == MH_MAGIC || | |
1312 | header.mach_header.magic == MH_MAGIC_64) | |
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) { | |
1323 | /* Look up our architecture in the fat file. */ | |
1324 | error = fatfile_getarch_with_bits(vp, archbits, (vm_offset_t)(&header.fat_header), &fat_arch); | |
1325 | if (error != LOAD_SUCCESS) | |
1326 | goto bad2; | |
1327 | ||
1328 | /* Read the Mach-O header out of it */ | |
1329 | error = vn_rdwr(UIO_READ, vp, (caddr_t)&header.mach_header, | |
1330 | sizeof(header.mach_header), fat_arch.offset, | |
1331 | UIO_SYSSPACE32, IO_NODELOCKED, cred, &resid, p); | |
1332 | if (error) { | |
1333 | error = LOAD_IOERROR; | |
1334 | goto bad2; | |
1335 | } | |
1336 | ||
1337 | /* Is this really a Mach-O? */ | |
1338 | if (header.mach_header.magic != MH_MAGIC && | |
1339 | header.mach_header.magic != MH_MAGIC_64) { | |
1340 | error = LOAD_BADMACHO; | |
1341 | goto bad2; | |
1342 | } | |
1343 | ||
1344 | *file_offset = fat_arch.offset; | |
1345 | *macho_size = fsize = fat_arch.size; | |
1346 | } else { | |
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); | |
1359 | ||
1360 | *file_offset = 0; | |
1361 | *macho_size = fsize; | |
1362 | } | |
1363 | ||
1364 | *mach_header = header.mach_header; | |
1365 | *vpp = vp; | |
1366 | ||
1367 | ubc_setsize(vp, fsize); | |
1368 | ||
1369 | return (error); | |
1370 | ||
1371 | bad2: | |
1372 | err2 = VNOP_CLOSE(vp, FREAD, &context); | |
1373 | vnode_put(vp); | |
1374 | return (error); | |
1375 | ||
1376 | bad1: | |
1377 | vnode_put(vp); | |
1378 | return(error); | |
1379 | } |