]>
Commit | Line | Data |
---|---|---|
b0d623f7 | 1 | /* |
39037602 | 2 | * Copyright (c) 2007-2016 Apple Inc. All rights reserved. |
b0d623f7 A |
3 | * |
4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ | |
39037602 | 5 | * |
b0d623f7 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 License | |
10 | * may not be used to create, or enable the creation or redistribution of, | |
11 | * unlawful or unlicensed copies of an Apple operating system, or to | |
12 | * circumvent, violate, or enable the circumvention or violation of, any | |
13 | * terms of an Apple operating system software license agreement. | |
39037602 | 14 | * |
b0d623f7 A |
15 | * Please obtain a copy of the License at |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. | |
39037602 | 17 | * |
b0d623f7 A |
18 | * The Original Code and all software distributed under the License are |
19 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
22 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
23 | * Please see the License for the specific language governing rights and | |
24 | * limitations under the License. | |
39037602 | 25 | * |
b0d623f7 A |
26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
27 | */ | |
28 | #include <stdarg.h> | |
29 | #include <string.h> | |
30 | #include <mach-o/loader.h> | |
31 | #include <mach-o/nlist.h> | |
32 | #include <mach-o/reloc.h> | |
33 | #if KERNEL | |
34 | #include <kern/kalloc.h> | |
35 | #include <libkern/libkern.h> | |
36 | #include <mach/vm_param.h> | |
37 | #include <vm/vm_kern.h> | |
38 | #else | |
39 | #include <stdio.h> | |
40 | #include <stdlib.h> | |
41 | #include <mach/mach_init.h> | |
42 | #include <mach-o/swap.h> | |
43 | #endif | |
44 | ||
45 | #define DEBUG_ASSERT_COMPONENT_NAME_STRING "kxld" | |
46 | #include <AssertMacros.h> | |
47 | ||
48 | #include "kxld_util.h" | |
49 | ||
50 | #if !KERNEL | |
0a7de745 | 51 | static void unswap_macho_32(u_char *file, enum NXByteOrder host_order, |
b0d623f7 | 52 | enum NXByteOrder target_order); |
0a7de745 | 53 | static void unswap_macho_64(u_char *file, enum NXByteOrder host_order, |
b0d623f7 A |
54 | enum NXByteOrder target_order); |
55 | #endif /* !KERNEL */ | |
56 | ||
57 | #if DEBUG | |
58 | static unsigned long num_allocations = 0; | |
59 | static unsigned long num_frees = 0; | |
60 | static unsigned long bytes_allocated = 0; | |
61 | static unsigned long bytes_freed = 0; | |
62 | #endif | |
63 | ||
64 | static KXLDLoggingCallback s_logging_callback = NULL; | |
4d15aeb1 | 65 | static char s_callback_name[64] = "internal"; |
b0d623f7 A |
66 | static void *s_callback_data = NULL; |
67 | ||
3e170ce0 A |
68 | #if !KERNEL |
69 | static boolean_t s_cross_link_enabled = FALSE; | |
f427ee49 A |
70 | /* Can't use PAGE_SIZE here because it is not a compile-time constant. |
71 | * However from inspection below, s_cross_link_page_size is not used | |
72 | * unless s_cross_link_enabled is TRUE, and s_cross_link_enabled is | |
73 | * only set to TRUE when a client specifies the value. So the | |
74 | * default should never be used in practice, | |
75 | */ | |
76 | static kxld_size_t s_cross_link_page_size; | |
3e170ce0 A |
77 | #endif |
78 | ||
79 | ||
b0d623f7 A |
80 | /******************************************************************************* |
81 | *******************************************************************************/ | |
0a7de745 | 82 | void |
b0d623f7 A |
83 | kxld_set_logging_callback(KXLDLoggingCallback logging_callback) |
84 | { | |
0a7de745 | 85 | s_logging_callback = logging_callback; |
b0d623f7 A |
86 | } |
87 | ||
88 | /******************************************************************************* | |
89 | *******************************************************************************/ | |
0a7de745 | 90 | void |
b0d623f7 A |
91 | kxld_set_logging_callback_data(const char *name, void *user_data) |
92 | { | |
0a7de745 A |
93 | if (name) { |
94 | (void)strlcpy(s_callback_name, name, sizeof(s_callback_name)); | |
95 | /* disallow format strings in the kxld logging callback name */ | |
96 | for (size_t i = 0; i < sizeof(s_callback_name); i++) { | |
97 | if (s_callback_name[i] == '%') { | |
98 | s_callback_name[i] = '.'; | |
99 | } | |
100 | } | |
101 | } else { | |
102 | (void)strlcpy(s_callback_name, "internal", sizeof(s_callback_name)); | |
103 | } | |
104 | ||
105 | s_callback_data = user_data; | |
b0d623f7 A |
106 | } |
107 | ||
108 | /******************************************************************************* | |
109 | *******************************************************************************/ | |
0a7de745 A |
110 | void |
111 | kxld_log(KXLDLogSubsystem subsystem, KXLDLogLevel level, | |
b0d623f7 A |
112 | const char *in_format, ...) |
113 | { | |
0a7de745 A |
114 | char stack_buffer[256]; |
115 | char *alloc_buffer = NULL; | |
116 | char *format = stack_buffer; | |
117 | u_int length = 0; | |
118 | va_list ap; | |
119 | ||
120 | if (s_logging_callback) { | |
121 | length = snprintf(stack_buffer, sizeof(stack_buffer), "kxld[%s]: %s", | |
122 | s_callback_name, in_format); | |
123 | ||
124 | if (length >= sizeof(stack_buffer)) { | |
125 | length += 1; | |
126 | alloc_buffer = kxld_alloc(length); | |
127 | if (!alloc_buffer) { | |
128 | return; | |
129 | } | |
130 | ||
131 | snprintf(alloc_buffer, length, "kxld[%s]: %s", | |
132 | s_callback_name, in_format); | |
133 | format = alloc_buffer; | |
134 | } | |
135 | ||
136 | va_start(ap, in_format); | |
137 | s_logging_callback(subsystem, level, format, ap, s_callback_data); | |
138 | va_end(ap); | |
139 | ||
140 | if (alloc_buffer) { | |
141 | kxld_free(alloc_buffer, length); | |
142 | } | |
143 | } | |
b0d623f7 A |
144 | } |
145 | ||
146 | /* We'll use kalloc for any page-based allocations under this threshold, and | |
147 | * kmem_alloc otherwise. | |
148 | */ | |
149 | #define KALLOC_MAX 16 * 1024 | |
150 | ||
151 | /******************************************************************************* | |
152 | *******************************************************************************/ | |
cb323159 A |
153 | void * |
154 | kxld_calloc(size_t size) | |
155 | { | |
156 | void * ptr = NULL; | |
157 | ||
158 | #if KERNEL | |
159 | ptr = kalloc(size); | |
160 | if (ptr) { | |
161 | bzero(ptr, size); | |
162 | } | |
163 | #else | |
164 | ptr = calloc(1, size); | |
165 | #endif | |
166 | ||
167 | #if DEBUG | |
168 | if (ptr) { | |
169 | ++num_allocations; | |
170 | bytes_allocated += size; | |
171 | } | |
172 | #endif | |
173 | ||
174 | return ptr; | |
175 | } | |
176 | ||
0a7de745 | 177 | void * |
b0d623f7 A |
178 | kxld_alloc(size_t size) |
179 | { | |
0a7de745 A |
180 | void * ptr = NULL; |
181 | ||
b0d623f7 | 182 | #if KERNEL |
0a7de745 | 183 | ptr = kalloc(size); |
b0d623f7 | 184 | #else |
0a7de745 | 185 | ptr = malloc(size); |
b0d623f7 A |
186 | #endif |
187 | ||
188 | #if DEBUG | |
0a7de745 A |
189 | if (ptr) { |
190 | ++num_allocations; | |
191 | bytes_allocated += size; | |
192 | } | |
b0d623f7 A |
193 | #endif |
194 | ||
0a7de745 | 195 | return ptr; |
b0d623f7 A |
196 | } |
197 | ||
198 | /******************************************************************************* | |
199 | *******************************************************************************/ | |
200 | void * | |
201 | kxld_page_alloc_untracked(size_t size) | |
202 | { | |
0a7de745 | 203 | void * ptr = NULL; |
b0d623f7 | 204 | #if KERNEL |
0a7de745 A |
205 | kern_return_t rval = 0; |
206 | vm_offset_t addr = 0; | |
b0d623f7 A |
207 | #endif /* KERNEL */ |
208 | ||
0a7de745 | 209 | size = round_page(size); |
b0d623f7 A |
210 | |
211 | #if KERNEL | |
0a7de745 A |
212 | if (size < KALLOC_MAX) { |
213 | ptr = kalloc(size); | |
214 | } else { | |
215 | rval = kmem_alloc(kernel_map, &addr, size, VM_KERN_MEMORY_OSKEXT); | |
216 | if (!rval) { | |
217 | ptr = (void *) addr; | |
218 | } | |
219 | } | |
cb323159 A |
220 | if (ptr) { |
221 | bzero(ptr, size); | |
222 | } | |
b0d623f7 | 223 | #else /* !KERNEL */ |
cb323159 | 224 | ptr = calloc(1, size); |
b0d623f7 A |
225 | #endif /* KERNEL */ |
226 | ||
0a7de745 | 227 | return ptr; |
b0d623f7 A |
228 | } |
229 | ||
230 | /******************************************************************************* | |
231 | *******************************************************************************/ | |
232 | void * | |
233 | kxld_page_alloc(size_t size) | |
234 | { | |
0a7de745 | 235 | void * ptr = NULL; |
b0d623f7 | 236 | |
0a7de745 | 237 | ptr = kxld_page_alloc_untracked(size); |
b0d623f7 | 238 | #if DEBUG |
0a7de745 A |
239 | if (ptr) { |
240 | ++num_allocations; | |
241 | bytes_allocated += round_page(size); | |
242 | } | |
b0d623f7 A |
243 | #endif /* DEBUG */ |
244 | ||
0a7de745 | 245 | return ptr; |
b0d623f7 A |
246 | } |
247 | ||
248 | /******************************************************************************* | |
249 | *******************************************************************************/ | |
250 | void * | |
251 | kxld_alloc_pageable(size_t size) | |
252 | { | |
0a7de745 | 253 | size = round_page(size); |
b0d623f7 A |
254 | |
255 | #if KERNEL | |
0a7de745 A |
256 | kern_return_t rval = 0; |
257 | vm_offset_t ptr = 0; | |
b0d623f7 | 258 | |
0a7de745 A |
259 | rval = kmem_alloc_pageable(kernel_map, &ptr, size, VM_KERN_MEMORY_OSKEXT); |
260 | if (rval) { | |
261 | ptr = 0; | |
262 | } | |
b0d623f7 | 263 | |
0a7de745 | 264 | return (void *) ptr; |
b0d623f7 | 265 | #else |
0a7de745 | 266 | return kxld_page_alloc_untracked(size); |
b0d623f7 A |
267 | #endif |
268 | } | |
269 | ||
270 | /******************************************************************************* | |
271 | *******************************************************************************/ | |
272 | void | |
273 | kxld_free(void *ptr, size_t size __unused) | |
274 | { | |
275 | #if DEBUG | |
0a7de745 A |
276 | ++num_frees; |
277 | bytes_freed += size; | |
b0d623f7 A |
278 | #endif |
279 | ||
280 | #if KERNEL | |
0a7de745 | 281 | kfree(ptr, size); |
b0d623f7 | 282 | #else |
0a7de745 | 283 | free(ptr); |
b0d623f7 A |
284 | #endif |
285 | } | |
286 | ||
287 | /******************************************************************************* | |
288 | *******************************************************************************/ | |
289 | void | |
290 | kxld_page_free_untracked(void *ptr, size_t size __unused) | |
291 | { | |
292 | #if KERNEL | |
0a7de745 | 293 | size = round_page(size); |
b0d623f7 | 294 | |
0a7de745 A |
295 | if (size < KALLOC_MAX) { |
296 | kfree(ptr, size); | |
297 | } else { | |
298 | kmem_free(kernel_map, (vm_offset_t) ptr, size); | |
299 | } | |
b0d623f7 | 300 | #else /* !KERNEL */ |
0a7de745 | 301 | free(ptr); |
b0d623f7 A |
302 | #endif /* KERNEL */ |
303 | } | |
0a7de745 | 304 | |
b0d623f7 A |
305 | |
306 | /******************************************************************************* | |
307 | *******************************************************************************/ | |
308 | void | |
309 | kxld_page_free(void *ptr, size_t size) | |
310 | { | |
311 | #if DEBUG | |
0a7de745 A |
312 | ++num_frees; |
313 | bytes_freed += round_page(size); | |
b0d623f7 | 314 | #endif /* DEBUG */ |
0a7de745 | 315 | kxld_page_free_untracked(ptr, size); |
b0d623f7 A |
316 | } |
317 | ||
318 | /******************************************************************************* | |
319 | *******************************************************************************/ | |
320 | kern_return_t | |
321 | validate_and_swap_macho_32(u_char *file, u_long size | |
322 | #if !KERNEL | |
323 | , enum NXByteOrder host_order | |
324 | #endif /* !KERNEL */ | |
325 | ) | |
326 | { | |
0a7de745 A |
327 | kern_return_t rval = KERN_FAILURE; |
328 | struct mach_header *mach_hdr = (struct mach_header *) ((void *) file); | |
329 | struct load_command *load_hdr = NULL; | |
330 | struct segment_command *seg_hdr = NULL; | |
331 | struct section *sects = NULL; | |
332 | struct relocation_info *relocs = NULL; | |
333 | struct symtab_command *symtab_hdr = NULL; | |
334 | struct nlist *symtab = NULL; | |
335 | u_long offset = 0; | |
336 | u_int cmd = 0; | |
337 | u_int cmdsize = 0; | |
338 | u_int i = 0; | |
339 | u_int j = 0; | |
b0d623f7 | 340 | #if !KERNEL |
0a7de745 | 341 | boolean_t swap = FALSE; |
b0d623f7 A |
342 | #endif /* !KERNEL */ |
343 | ||
0a7de745 A |
344 | check(file); |
345 | check(size); | |
b0d623f7 | 346 | |
0a7de745 A |
347 | /* Verify that the file is big enough for the mach header */ |
348 | require_action(size >= sizeof(*mach_hdr), finish, | |
349 | rval = KERN_FAILURE; | |
350 | kxld_log(kKxldLogLinking, kKxldLogErr, kKxldLogTruncatedMachO)); | |
351 | offset = sizeof(*mach_hdr); | |
b0d623f7 A |
352 | |
353 | #if !KERNEL | |
0a7de745 A |
354 | /* Swap the mach header if necessary */ |
355 | if (mach_hdr->magic == MH_CIGAM) { | |
356 | swap = TRUE; | |
357 | (void) swap_mach_header(mach_hdr, host_order); | |
358 | } | |
b0d623f7 A |
359 | #endif /* !KERNEL */ |
360 | ||
0a7de745 A |
361 | /* Validate the mach_header's magic number */ |
362 | require_action(mach_hdr->magic == MH_MAGIC, finish, | |
363 | rval = KERN_FAILURE; | |
364 | kxld_log(kKxldLogLinking, kKxldLogErr, kKxldLogMalformedMachO | |
365 | "Invalid magic number: 0x%x.", mach_hdr->magic)); | |
b0d623f7 | 366 | |
0a7de745 A |
367 | /* If in the running kernel, and asked to validate the kernel |
368 | * (which is the only file of type MH_EXECUTE we should ever see), | |
369 | * then just assume it's ok or we wouldn't be running to begin with. | |
370 | */ | |
6d2010ae | 371 | #if KERNEL |
0a7de745 A |
372 | if (mach_hdr->filetype == MH_EXECUTE) { |
373 | rval = KERN_SUCCESS; | |
374 | goto finish; | |
375 | } | |
6d2010ae A |
376 | #endif /* KERNEL */ |
377 | ||
0a7de745 A |
378 | /* Validate and potentially swap the load commands */ |
379 | for (i = 0; i < mach_hdr->ncmds; ++i, offset += cmdsize) { | |
380 | /* Get the load command and size */ | |
381 | load_hdr = (struct load_command *) ((void *) (file + offset)); | |
382 | cmd = load_hdr->cmd; | |
383 | cmdsize = load_hdr->cmdsize; | |
b0d623f7 A |
384 | |
385 | #if !KERNEL | |
0a7de745 A |
386 | if (swap) { |
387 | cmd = OSSwapInt32(load_hdr->cmd); | |
388 | cmdsize = OSSwapInt32(load_hdr->cmdsize); | |
389 | } | |
b0d623f7 A |
390 | #endif /* !KERNEL */ |
391 | ||
0a7de745 A |
392 | /* Verify that the file is big enough to contain the load command */ |
393 | require_action(size >= offset + cmdsize, finish, | |
394 | rval = KERN_FAILURE; | |
395 | kxld_log(kKxldLogLinking, kKxldLogErr, kKxldLogTruncatedMachO)); | |
b0d623f7 | 396 | |
0a7de745 A |
397 | switch (cmd) { |
398 | case LC_SEGMENT: | |
399 | /* Get and swap the segment header */ | |
400 | seg_hdr = (struct segment_command *) load_hdr; | |
b0d623f7 | 401 | #if !KERNEL |
0a7de745 A |
402 | if (swap) { |
403 | swap_segment_command(seg_hdr, host_order); | |
404 | } | |
b0d623f7 A |
405 | #endif /* !KERNEL */ |
406 | ||
0a7de745 A |
407 | /* Get and swap the section headers */ |
408 | sects = (struct section *) &seg_hdr[1]; | |
b0d623f7 | 409 | #if !KERNEL |
0a7de745 A |
410 | if (swap) { |
411 | swap_section(sects, seg_hdr->nsects, host_order); | |
412 | } | |
b0d623f7 A |
413 | #endif /* !KERNEL */ |
414 | ||
0a7de745 A |
415 | /* Ignore segments with no vm size */ |
416 | if (!seg_hdr->vmsize) { | |
417 | continue; | |
418 | } | |
419 | ||
420 | /* Verify that the file is big enough for the segment data. */ | |
421 | require_action(size >= seg_hdr->fileoff + seg_hdr->filesize, finish, | |
422 | rval = KERN_FAILURE; | |
423 | kxld_log(kKxldLogLinking, kKxldLogErr, kKxldLogTruncatedMachO)); | |
424 | ||
425 | for (j = 0; j < seg_hdr->nsects; ++j) { | |
426 | /* Verify that, if the section is not to be zero filled on | |
427 | * demand, that file is big enough for the section's data. | |
428 | */ | |
429 | require_action((sects[j].flags & S_ZEROFILL) || | |
430 | (size >= sects[j].offset + sects[j].size), finish, | |
431 | rval = KERN_FAILURE; | |
432 | kxld_log(kKxldLogLinking, kKxldLogErr, kKxldLogTruncatedMachO)); | |
433 | ||
434 | /* Verify that the file is big enough for the section's | |
435 | * relocation entries. | |
436 | */ | |
437 | require_action(size >= | |
438 | sects[j].reloff + sects[j].nreloc * sizeof(*relocs), finish, | |
439 | rval = KERN_FAILURE; | |
440 | kxld_log(kKxldLogLinking, kKxldLogErr, kKxldLogTruncatedMachO)); | |
441 | ||
442 | /* Swap the relocation entries */ | |
443 | relocs = (struct relocation_info *) ((void *) (file + sects[j].reloff)); | |
b0d623f7 | 444 | #if !KERNEL |
0a7de745 A |
445 | if (swap) { |
446 | swap_relocation_info(relocs, sects[j].nreloc, | |
447 | host_order); | |
448 | } | |
b0d623f7 | 449 | #endif /* !KERNEL */ |
0a7de745 | 450 | } |
b0d623f7 | 451 | |
0a7de745 A |
452 | break; |
453 | case LC_SYMTAB: | |
454 | /* Get and swap the symtab header */ | |
455 | symtab_hdr = (struct symtab_command *) load_hdr; | |
b0d623f7 | 456 | #if !KERNEL |
0a7de745 A |
457 | if (swap) { |
458 | swap_symtab_command(symtab_hdr, host_order); | |
459 | } | |
b0d623f7 A |
460 | #endif /* !KERNEL */ |
461 | ||
0a7de745 A |
462 | /* Verify that the file is big enough for the symbol table */ |
463 | require_action(size >= | |
464 | symtab_hdr->symoff + symtab_hdr->nsyms * sizeof(*symtab), finish, | |
465 | rval = KERN_FAILURE; | |
466 | kxld_log(kKxldLogLinking, kKxldLogErr, kKxldLogTruncatedMachO)); | |
b0d623f7 | 467 | |
0a7de745 A |
468 | /* Verify that the file is big enough for the string table */ |
469 | require_action(size >= symtab_hdr->stroff + symtab_hdr->strsize, finish, | |
470 | rval = KERN_FAILURE; | |
471 | kxld_log(kKxldLogLinking, kKxldLogErr, kKxldLogTruncatedMachO)); | |
b0d623f7 A |
472 | |
473 | #if !KERNEL | |
0a7de745 A |
474 | /* Swap the symbol table entries */ |
475 | symtab = (struct nlist *) ((void *) (file + symtab_hdr->symoff)); | |
476 | if (swap) { | |
477 | swap_nlist(symtab, symtab_hdr->nsyms, host_order); | |
478 | } | |
b0d623f7 A |
479 | #endif /* !KERNEL */ |
480 | ||
0a7de745 A |
481 | break; |
482 | default: | |
b0d623f7 | 483 | #if !KERNEL |
0a7de745 A |
484 | /* Swap the load command */ |
485 | if (swap) { | |
486 | swap_load_command(load_hdr, host_order); | |
487 | } | |
b0d623f7 | 488 | #endif /* !KERNEL */ |
0a7de745 A |
489 | break; |
490 | } | |
491 | } | |
b0d623f7 | 492 | |
0a7de745 | 493 | rval = KERN_SUCCESS; |
b0d623f7 A |
494 | |
495 | finish: | |
0a7de745 | 496 | return rval; |
b0d623f7 A |
497 | } |
498 | ||
499 | /******************************************************************************* | |
500 | *******************************************************************************/ | |
501 | kern_return_t | |
502 | validate_and_swap_macho_64(u_char *file, u_long size | |
503 | #if !KERNEL | |
504 | , enum NXByteOrder host_order | |
505 | #endif /* !KERNEL */ | |
506 | ) | |
507 | { | |
0a7de745 A |
508 | kern_return_t rval = KERN_FAILURE; |
509 | struct mach_header_64 *mach_hdr = (struct mach_header_64 *) ((void *) file); | |
510 | struct load_command *load_hdr = NULL; | |
511 | struct segment_command_64 *seg_hdr = NULL; | |
512 | struct section_64 *sects = NULL; | |
513 | struct relocation_info *relocs = NULL; | |
514 | struct symtab_command *symtab_hdr = NULL; | |
515 | struct nlist_64 *symtab = NULL; | |
516 | u_long offset = 0; | |
517 | u_int cmd = 0; | |
518 | u_int cmdsize = 0; | |
519 | u_int i = 0; | |
520 | u_int j = 0; | |
b0d623f7 | 521 | #if !KERNEL |
0a7de745 | 522 | boolean_t swap = FALSE; |
b0d623f7 A |
523 | #endif /* !KERNEL */ |
524 | ||
0a7de745 A |
525 | check(file); |
526 | check(size); | |
b0d623f7 | 527 | |
0a7de745 A |
528 | /* Verify that the file is big enough for the mach header */ |
529 | require_action(size >= sizeof(*mach_hdr), finish, | |
530 | rval = KERN_FAILURE; | |
531 | kxld_log(kKxldLogLinking, kKxldLogErr, kKxldLogTruncatedMachO)); | |
532 | offset = sizeof(*mach_hdr); | |
b0d623f7 A |
533 | |
534 | #if !KERNEL | |
0a7de745 A |
535 | /* Swap the mach header if necessary */ |
536 | if (mach_hdr->magic == MH_CIGAM_64) { | |
537 | swap = TRUE; | |
538 | (void) swap_mach_header_64(mach_hdr, host_order); | |
539 | } | |
b0d623f7 A |
540 | #endif /* !KERNEL */ |
541 | ||
0a7de745 A |
542 | /* Validate the mach_header's magic number */ |
543 | require_action(mach_hdr->magic == MH_MAGIC_64, finish, | |
544 | rval = KERN_FAILURE; | |
545 | kxld_log(kKxldLogLinking, kKxldLogErr, kKxldLogMalformedMachO | |
546 | "Invalid magic number: 0x%x.", mach_hdr->magic)); | |
b0d623f7 | 547 | |
0a7de745 A |
548 | /* If in the running kernel, and asked to validate the kernel |
549 | * (which is the only file of type MH_EXECUTE we should ever see), | |
550 | * then just assume it's ok or we wouldn't be running to begin with. | |
551 | */ | |
6d2010ae | 552 | #if KERNEL |
0a7de745 A |
553 | if (mach_hdr->filetype == MH_EXECUTE) { |
554 | rval = KERN_SUCCESS; | |
555 | goto finish; | |
556 | } | |
6d2010ae A |
557 | #endif /* KERNEL */ |
558 | ||
0a7de745 A |
559 | /* Validate and potentially swap the load commands */ |
560 | for (i = 0; i < mach_hdr->ncmds; ++i, offset += cmdsize) { | |
561 | /* Get the load command and size */ | |
562 | load_hdr = (struct load_command *) ((void *) (file + offset)); | |
563 | cmd = load_hdr->cmd; | |
564 | cmdsize = load_hdr->cmdsize; | |
b0d623f7 A |
565 | |
566 | #if !KERNEL | |
0a7de745 A |
567 | if (swap) { |
568 | cmd = OSSwapInt32(load_hdr->cmd); | |
569 | cmdsize = OSSwapInt32(load_hdr->cmdsize); | |
570 | } | |
b0d623f7 A |
571 | #endif /* !KERNEL */ |
572 | ||
0a7de745 A |
573 | /* Verify that the file is big enough to contain the load command */ |
574 | require_action(size >= offset + cmdsize, finish, | |
575 | rval = KERN_FAILURE; | |
576 | kxld_log(kKxldLogLinking, kKxldLogErr, kKxldLogTruncatedMachO)); | |
577 | switch (cmd) { | |
578 | case LC_SEGMENT_64: | |
579 | /* Get and swap the segment header */ | |
580 | seg_hdr = (struct segment_command_64 *) ((void *) load_hdr); | |
b0d623f7 | 581 | #if !KERNEL |
0a7de745 A |
582 | if (swap) { |
583 | swap_segment_command_64(seg_hdr, host_order); | |
584 | } | |
b0d623f7 A |
585 | #endif /* !KERNEL */ |
586 | ||
0a7de745 A |
587 | /* Get and swap the section headers */ |
588 | sects = (struct section_64 *) &seg_hdr[1]; | |
b0d623f7 | 589 | #if !KERNEL |
0a7de745 A |
590 | if (swap) { |
591 | swap_section_64(sects, seg_hdr->nsects, host_order); | |
592 | } | |
b0d623f7 A |
593 | #endif /* !KERNEL */ |
594 | ||
0a7de745 A |
595 | /* If the segment has no vm footprint, skip it */ |
596 | if (!seg_hdr->vmsize) { | |
597 | continue; | |
598 | } | |
599 | ||
600 | /* Verify that the file is big enough for the segment data. */ | |
601 | require_action(size >= seg_hdr->fileoff + seg_hdr->filesize, finish, | |
602 | rval = KERN_FAILURE; | |
603 | kxld_log(kKxldLogLinking, kKxldLogErr, kKxldLogTruncatedMachO)); | |
604 | ||
605 | for (j = 0; j < seg_hdr->nsects; ++j) { | |
606 | /* Verify that, if the section is not to be zero filled on | |
607 | * demand, that file is big enough for the section's data. | |
608 | */ | |
609 | require_action((sects[j].flags & S_ZEROFILL) || | |
610 | (size >= sects[j].offset + sects[j].size), finish, | |
611 | rval = KERN_FAILURE; | |
612 | kxld_log(kKxldLogLinking, kKxldLogErr, kKxldLogTruncatedMachO)); | |
613 | ||
614 | /* Verify that the file is big enough for the section's | |
615 | * relocation entries. | |
616 | */ | |
617 | require_action(size >= | |
618 | sects[j].reloff + sects[j].nreloc * sizeof(*relocs), finish, | |
619 | rval = KERN_FAILURE; | |
620 | kxld_log(kKxldLogLinking, kKxldLogErr, kKxldLogTruncatedMachO)); | |
621 | ||
622 | /* Swap the relocation entries */ | |
623 | relocs = (struct relocation_info *) ((void *) (file + sects[j].reloff)); | |
b0d623f7 | 624 | #if !KERNEL |
0a7de745 A |
625 | if (swap) { |
626 | swap_relocation_info(relocs, sects[j].nreloc, | |
627 | host_order); | |
628 | } | |
b0d623f7 | 629 | #endif /* !KERNEL */ |
0a7de745 | 630 | } |
b0d623f7 | 631 | |
0a7de745 A |
632 | break; |
633 | case LC_SYMTAB: | |
634 | /* Get and swap the symtab header */ | |
635 | symtab_hdr = (struct symtab_command *) load_hdr; | |
b0d623f7 | 636 | #if !KERNEL |
0a7de745 A |
637 | if (swap) { |
638 | swap_symtab_command(symtab_hdr, host_order); | |
639 | } | |
b0d623f7 A |
640 | #endif /* !KERNEL */ |
641 | ||
0a7de745 A |
642 | /* Verify that the file is big enough for the symbol table */ |
643 | require_action(size >= | |
644 | symtab_hdr->symoff + symtab_hdr->nsyms * sizeof(*symtab), finish, | |
645 | rval = KERN_FAILURE; | |
646 | kxld_log(kKxldLogLinking, kKxldLogErr, kKxldLogTruncatedMachO)); | |
b0d623f7 | 647 | |
0a7de745 A |
648 | /* Verify that the file is big enough for the string table */ |
649 | require_action(size >= symtab_hdr->stroff + symtab_hdr->strsize, finish, | |
650 | rval = KERN_FAILURE; | |
651 | kxld_log(kKxldLogLinking, kKxldLogErr, kKxldLogTruncatedMachO)); | |
b0d623f7 A |
652 | |
653 | #if !KERNEL | |
0a7de745 A |
654 | /* Swap the symbol table entries */ |
655 | symtab = (struct nlist_64 *) ((void *) (file + symtab_hdr->symoff)); | |
656 | if (swap) { | |
657 | swap_nlist_64(symtab, symtab_hdr->nsyms, host_order); | |
658 | } | |
b0d623f7 A |
659 | #endif /* !KERNEL */ |
660 | ||
0a7de745 A |
661 | break; |
662 | default: | |
b0d623f7 | 663 | #if !KERNEL |
0a7de745 A |
664 | /* Swap the load command */ |
665 | if (swap) { | |
666 | swap_load_command(load_hdr, host_order); | |
667 | } | |
b0d623f7 | 668 | #endif /* !KERNEL */ |
0a7de745 A |
669 | break; |
670 | } | |
671 | } | |
b0d623f7 | 672 | |
0a7de745 | 673 | rval = KERN_SUCCESS; |
b0d623f7 A |
674 | |
675 | finish: | |
0a7de745 | 676 | return rval; |
b0d623f7 A |
677 | } |
678 | ||
679 | #if !KERNEL | |
680 | /******************************************************************************* | |
681 | *******************************************************************************/ | |
0a7de745 A |
682 | void |
683 | unswap_macho(u_char *file, enum NXByteOrder host_order, | |
b0d623f7 A |
684 | enum NXByteOrder target_order) |
685 | { | |
0a7de745 | 686 | struct mach_header *hdr = (struct mach_header *) ((void *) file); |
b0d623f7 | 687 | |
0a7de745 A |
688 | if (!hdr) { |
689 | return; | |
690 | } | |
b0d623f7 | 691 | |
0a7de745 A |
692 | if (hdr->magic == MH_MAGIC) { |
693 | unswap_macho_32(file, host_order, target_order); | |
694 | } else if (hdr->magic == MH_MAGIC_64) { | |
695 | unswap_macho_64(file, host_order, target_order); | |
696 | } | |
b0d623f7 A |
697 | } |
698 | ||
699 | /******************************************************************************* | |
700 | *******************************************************************************/ | |
701 | static void | |
0a7de745 | 702 | unswap_macho_32(u_char *file, enum NXByteOrder host_order, |
b0d623f7 A |
703 | enum NXByteOrder target_order) |
704 | { | |
0a7de745 A |
705 | struct mach_header *mach_hdr = (struct mach_header *) ((void *) file); |
706 | struct load_command *load_hdr = NULL; | |
707 | struct segment_command *seg_hdr = NULL; | |
708 | struct section *sects = NULL; | |
709 | struct symtab_command *symtab_hdr = NULL; | |
710 | struct nlist *symtab = NULL; | |
711 | u_long offset = 0; | |
712 | u_int cmd = 0; | |
713 | u_int size = 0; | |
714 | u_int i = 0; | |
715 | ||
716 | check(file); | |
717 | ||
718 | if (target_order == host_order) { | |
719 | return; | |
720 | } | |
721 | ||
722 | offset = sizeof(*mach_hdr); | |
723 | for (i = 0; i < mach_hdr->ncmds; ++i, offset += size) { | |
724 | load_hdr = (struct load_command *) ((void *) (file + offset)); | |
725 | cmd = load_hdr->cmd; | |
726 | size = load_hdr->cmdsize; | |
727 | ||
728 | switch (cmd) { | |
729 | case LC_SEGMENT: | |
730 | seg_hdr = (struct segment_command *) load_hdr; | |
731 | sects = (struct section *) &seg_hdr[1]; | |
732 | ||
733 | /* We don't need to unswap relocations because this function is | |
734 | * called when linking is completed (so there are no relocations). | |
735 | */ | |
736 | ||
737 | swap_section(sects, seg_hdr->nsects, target_order); | |
738 | swap_segment_command(seg_hdr, target_order); | |
739 | break; | |
740 | case LC_SYMTAB: | |
741 | symtab_hdr = (struct symtab_command *) load_hdr; | |
742 | symtab = (struct nlist*) ((void *) (file + symtab_hdr->symoff)); | |
743 | ||
744 | swap_nlist(symtab, symtab_hdr->nsyms, target_order); | |
745 | swap_symtab_command(symtab_hdr, target_order); | |
746 | ||
747 | break; | |
748 | default: | |
749 | swap_load_command(load_hdr, target_order); | |
750 | break; | |
751 | } | |
752 | } | |
753 | ||
754 | (void) swap_mach_header(mach_hdr, target_order); | |
b0d623f7 A |
755 | } |
756 | ||
757 | /******************************************************************************* | |
758 | *******************************************************************************/ | |
759 | static void | |
0a7de745 | 760 | unswap_macho_64(u_char *file, enum NXByteOrder host_order, |
b0d623f7 A |
761 | enum NXByteOrder target_order) |
762 | { | |
0a7de745 A |
763 | struct mach_header_64 *mach_hdr = (struct mach_header_64 *) ((void *) file); |
764 | struct load_command *load_hdr = NULL; | |
765 | struct segment_command_64 *seg_hdr = NULL; | |
766 | struct section_64 *sects = NULL; | |
767 | struct symtab_command *symtab_hdr = NULL; | |
768 | struct nlist_64 *symtab = NULL; | |
769 | u_long offset = 0; | |
770 | u_int cmd = 0; | |
771 | u_int size = 0; | |
772 | u_int i = 0; | |
773 | ||
774 | check(file); | |
775 | ||
776 | if (target_order == host_order) { | |
777 | return; | |
778 | } | |
779 | ||
780 | offset = sizeof(*mach_hdr); | |
781 | for (i = 0; i < mach_hdr->ncmds; ++i, offset += size) { | |
782 | load_hdr = (struct load_command *) ((void *) (file + offset)); | |
783 | cmd = load_hdr->cmd; | |
784 | size = load_hdr->cmdsize; | |
785 | ||
786 | switch (cmd) { | |
787 | case LC_SEGMENT_64: | |
788 | seg_hdr = (struct segment_command_64 *) ((void *) load_hdr); | |
789 | sects = (struct section_64 *) &seg_hdr[1]; | |
790 | ||
791 | /* We don't need to unswap relocations because this function is | |
792 | * called when linking is completed (so there are no relocations). | |
793 | */ | |
794 | ||
795 | swap_section_64(sects, seg_hdr->nsects, target_order); | |
796 | swap_segment_command_64(seg_hdr, target_order); | |
797 | break; | |
798 | case LC_SYMTAB: | |
799 | symtab_hdr = (struct symtab_command *) load_hdr; | |
800 | symtab = (struct nlist_64 *) ((void *) (file + symtab_hdr->symoff)); | |
801 | ||
802 | swap_nlist_64(symtab, symtab_hdr->nsyms, target_order); | |
803 | swap_symtab_command(symtab_hdr, target_order); | |
804 | ||
805 | break; | |
806 | default: | |
807 | swap_load_command(load_hdr, target_order); | |
808 | break; | |
809 | } | |
810 | } | |
811 | ||
812 | (void) swap_mach_header_64(mach_hdr, target_order); | |
b0d623f7 A |
813 | } |
814 | #endif /* !KERNEL */ | |
0a7de745 | 815 | |
b0d623f7 A |
816 | /******************************************************************************* |
817 | *******************************************************************************/ | |
818 | kxld_addr_t | |
819 | kxld_align_address(kxld_addr_t address, u_int align) | |
820 | { | |
0a7de745 A |
821 | kxld_addr_t alignment = (1 << align); |
822 | kxld_addr_t low_bits = 0; | |
b0d623f7 | 823 | |
0a7de745 A |
824 | if (!align) { |
825 | return address; | |
826 | } | |
6d2010ae | 827 | |
0a7de745 A |
828 | low_bits = (address) & (alignment - 1); |
829 | if (low_bits) { | |
830 | address += (alignment - low_bits); | |
831 | } | |
b0d623f7 | 832 | |
0a7de745 | 833 | return address; |
b0d623f7 A |
834 | } |
835 | ||
836 | /******************************************************************************* | |
837 | *******************************************************************************/ | |
838 | boolean_t | |
839 | kxld_is_32_bit(cpu_type_t cputype) | |
840 | { | |
0a7de745 | 841 | return !(cputype & CPU_ARCH_ABI64); |
b0d623f7 A |
842 | } |
843 | ||
b0d623f7 A |
844 | /******************************************************************************* |
845 | *******************************************************************************/ | |
846 | void | |
847 | kxld_print_memory_report(void) | |
848 | { | |
849 | #if DEBUG | |
0a7de745 A |
850 | kxld_log(kKxldLogLinking, kKxldLogExplicit, "kxld memory usage report:\n" |
851 | "\tNumber of allocations: %8lu\n" | |
852 | "\tNumber of frees: %8lu\n" | |
853 | "\tAverage allocation size: %8lu\n" | |
854 | "\tTotal bytes allocated: %8lu\n" | |
855 | "\tTotal bytes freed: %8lu\n" | |
856 | "\tTotal bytes leaked: %8lu", | |
857 | num_allocations, num_frees, bytes_allocated / num_allocations, | |
858 | bytes_allocated, bytes_freed, bytes_allocated - bytes_freed); | |
b0d623f7 A |
859 | #endif |
860 | } | |
861 | ||
3e170ce0 A |
862 | /********************************************************************* |
863 | *********************************************************************/ | |
864 | #if !KERNEL | |
0a7de745 A |
865 | boolean_t |
866 | kxld_set_cross_link_page_size(kxld_size_t target_page_size) | |
3e170ce0 | 867 | { |
0a7de745 A |
868 | // verify radix 2 |
869 | if ((target_page_size != 0) && | |
870 | ((target_page_size & (target_page_size - 1)) == 0)) { | |
871 | s_cross_link_enabled = TRUE; | |
872 | s_cross_link_page_size = target_page_size; | |
873 | ||
874 | return TRUE; | |
875 | } else { | |
876 | return FALSE; | |
877 | } | |
3e170ce0 A |
878 | } |
879 | #endif /* !KERNEL */ | |
880 | ||
881 | /********************************************************************* | |
882 | *********************************************************************/ | |
0a7de745 A |
883 | kxld_size_t |
884 | kxld_get_effective_page_size(void) | |
3e170ce0 A |
885 | { |
886 | #if KERNEL | |
0a7de745 | 887 | return PAGE_SIZE; |
3e170ce0 | 888 | #else |
0a7de745 A |
889 | if (s_cross_link_enabled) { |
890 | return s_cross_link_page_size; | |
891 | } else { | |
892 | return PAGE_SIZE; | |
893 | } | |
3e170ce0 A |
894 | #endif /* KERNEL */ |
895 | } | |
896 | ||
897 | /********************************************************************* | |
898 | *********************************************************************/ | |
0a7de745 A |
899 | kxld_addr_t |
900 | kxld_round_page_cross_safe(kxld_addr_t offset) | |
3e170ce0 A |
901 | { |
902 | #if KERNEL | |
0a7de745 | 903 | return round_page(offset); |
3e170ce0 | 904 | #else |
0a7de745 A |
905 | // assume s_cross_link_page_size is power of 2 |
906 | if (s_cross_link_enabled) { | |
907 | return (offset + (s_cross_link_page_size - 1)) & | |
908 | (~(s_cross_link_page_size - 1)); | |
909 | } else { | |
910 | return round_page(offset); | |
911 | } | |
3e170ce0 A |
912 | #endif /* KERNEL */ |
913 | } | |
39037602 A |
914 | |
915 | #if SPLIT_KEXTS_DEBUG | |
916 | ||
0a7de745 A |
917 | void |
918 | kxld_show_split_info(splitKextLinkInfo *info) | |
39037602 | 919 | { |
0a7de745 A |
920 | kxld_log(kKxldLogLinking, kKxldLogErr, |
921 | "splitKextLinkInfo: \n" | |
922 | "kextExecutable %p to %p kextSize %lu \n" | |
923 | "linkedKext %p to %p linkedKextSize %lu \n" | |
924 | "vmaddr_TEXT %p vmaddr_TEXT_EXEC %p " | |
925 | "vmaddr_DATA %p vmaddr_DATA_CONST %p " | |
926 | "vmaddr_LLVM_COV %p vmaddr_LINKEDIT %p", | |
927 | (void *) info->kextExecutable, | |
928 | (void *) (info->kextExecutable + info->kextSize), | |
929 | info->kextSize, | |
930 | (void*) info->linkedKext, | |
931 | (void*) (info->linkedKext + info->linkedKextSize), | |
932 | info->linkedKextSize, | |
933 | (void *) info->vmaddr_TEXT, | |
934 | (void *) info->vmaddr_TEXT_EXEC, | |
935 | (void *) info->vmaddr_DATA, | |
936 | (void *) info->vmaddr_DATA_CONST, | |
937 | (void *) info->vmaddr_LLVM_COV, | |
938 | (void *) info->vmaddr_LINKEDIT); | |
39037602 A |
939 | } |
940 | ||
0a7de745 A |
941 | boolean_t |
942 | isTargetKextName(const char * the_name) | |
39037602 | 943 | { |
0a7de745 A |
944 | if (the_name && 0 == strcmp(the_name, KXLD_TARGET_KEXT)) { |
945 | return TRUE; | |
946 | } | |
947 | return FALSE; | |
39037602 A |
948 | } |
949 | #endif |