1 | /* |
2 | * Copyright (c) 2008 Apple Inc. All rights reserved. |
3 | * |
4 | * @APPLE_OSREFERENCE_LICENSE_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 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. |
14 | * |
15 | * Please obtain a copy of the License at |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. |
17 | * |
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. |
25 | * |
26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
27 | */ |
28 | #include <string.h> |
29 | #include <mach/vm_prot.h> |
30 | #include <mach-o/loader.h> |
31 | #include <sys/types.h> |
32 | |
33 | #if KERNEL |
34 | #include <mach/vm_param.h> |
35 | #else |
36 | #include <mach/mach_init.h> |
37 | #endif /* KERNEL */ |
38 | |
39 | #define DEBUG_ASSERT_COMPONENT_NAME_STRING "kxld" |
40 | #include <AssertMacros.h> |
41 | |
42 | #include "kxld_reloc.h" |
43 | #include "kxld_sect.h" |
44 | #include "kxld_seg.h" |
45 | #include "kxld_symtab.h" |
46 | #include "kxld_util.h" |
47 | |
48 | #define MAX_SEGS 20 |
49 | |
50 | #define TEXT_SEG_PROT (VM_PROT_READ | VM_PROT_EXECUTE) |
51 | #define DATA_SEG_PROT (VM_PROT_READ | VM_PROT_WRITE) |
52 | |
53 | extern boolean_t isSplitKext; |
54 | extern boolean_t isOldInterface; |
55 | |
56 | #if KXLD_USER_OR_OBJECT |
57 | static kern_return_t reorder_sections(KXLDSeg *seg, KXLDArray *section_order); |
58 | static void reorder_section(KXLDArray *sects, u_int *sect_reorder_index, |
59 | KXLDSect **reorder_buffer, u_int reorder_buffer_index); |
60 | #endif /* KXLD_USER_OR_OBJECT */ |
61 | |
62 | #if 0 |
63 | static KXLDSeg * get_segment_by_name(KXLDArray *segarray, const char *name); |
64 | #endif |
65 | |
66 | #if KXLD_USER_OR_ILP32 |
67 | static kern_return_t seg_export_macho_header_32(const KXLDSeg *seg, u_char *buf, |
68 | u_long *header_offset, u_long header_size, u_long data_offset); |
69 | #endif |
70 | #if KXLD_USER_OR_LP64 |
71 | static kern_return_t seg_export_macho_header_64(const KXLDSeg *seg, u_char *buf, |
72 | u_long *, u_long , u_long data_offset); |
73 | #endif |
74 | |
75 | static KXLDSect * get_sect_by_index(const KXLDSeg *seg, u_int idx); |
76 | |
77 | #if KXLD_USER_OR_ILP32 |
78 | /******************************************************************************* |
79 | *******************************************************************************/ |
80 | kern_return_t |
81 | kxld_seg_init_from_macho_32(KXLDSeg *seg, struct segment_command *src) |
82 | { |
83 | kern_return_t rval = KERN_FAILURE; |
84 | check(seg); |
85 | check(src); |
86 | |
87 | strlcpy(seg->segname, src->segname, sizeof(seg->segname)); |
88 | seg->base_addr = src->vmaddr; |
89 | seg->link_addr = src->vmaddr; |
90 | seg->vmsize = src->vmsize; |
91 | seg->fileoff = src->fileoff; |
92 | seg->maxprot = src->maxprot; |
93 | seg->initprot = src->initprot; |
94 | seg->flags = src->flags; |
95 | |
96 | rval = kxld_array_init(&seg->sects, sizeof(KXLDSect *), src->nsects); |
97 | require_noerr(rval, finish); |
98 | |
99 | rval = KERN_SUCCESS; |
100 | |
101 | finish: |
102 | return rval; |
103 | } |
104 | #endif /* KXLD_USER_OR_ILP32 */ |
105 | |
106 | #if KXLD_USER_OR_LP64 |
107 | /******************************************************************************* |
108 | *******************************************************************************/ |
109 | kern_return_t |
110 | kxld_seg_init_from_macho_64(KXLDSeg *seg, struct segment_command_64 *src) |
111 | { |
112 | kern_return_t rval = KERN_FAILURE; |
113 | check(seg); |
114 | check(src); |
115 | |
116 | strlcpy(seg->segname, src->segname, sizeof(seg->segname)); |
117 | seg->base_addr = src->vmaddr; |
118 | seg->link_addr = src->vmaddr; |
119 | seg->vmsize = src->vmsize; |
120 | |
121 | seg->fileoff = src->fileoff; |
122 | seg->maxprot = src->maxprot; |
123 | seg->initprot = src->initprot; |
124 | seg->flags = src->flags; |
125 | |
126 | rval = kxld_array_init(&seg->sects, sizeof(KXLDSect *), src->nsects); |
127 | require_noerr(rval, finish); |
128 | |
129 | rval = KERN_SUCCESS; |
130 | |
131 | finish: |
132 | return rval; |
133 | } |
134 | #endif /* KXLD_USER_OR_LP64 */ |
135 | |
136 | #if KXLD_USER_OR_OBJECT |
137 | /******************************************************************************* |
138 | *******************************************************************************/ |
139 | kern_return_t |
140 | kxld_seg_create_seg_from_sections(KXLDArray *segarray, KXLDArray *sectarray) |
141 | { |
142 | kern_return_t rval = KERN_FAILURE; |
143 | KXLDSeg *seg = NULL; |
144 | KXLDSect *sect = NULL; |
145 | KXLDSect **sectp = NULL; |
146 | u_int i = 0; |
147 | |
148 | /* Initialize the segment array to one segment */ |
149 | |
150 | rval = kxld_array_init(segarray, sizeof(KXLDSeg), 1); |
151 | require_noerr(rval, finish); |
152 | |
153 | /* Initialize the segment */ |
154 | |
155 | seg = kxld_array_get_item(segarray, 0); |
156 | seg->initprot = VM_PROT_ALL; |
157 | seg->maxprot = VM_PROT_ALL; |
158 | seg->link_addr = 0; |
159 | |
160 | /* Add the sections to the segment */ |
161 | |
162 | rval = kxld_array_init(&seg->sects, sizeof(KXLDSect *), sectarray->nitems); |
163 | require_noerr(rval, finish); |
164 | |
165 | for (i = 0; i < sectarray->nitems; ++i) { |
166 | sect = kxld_array_get_item(sectarray, i); |
167 | sectp = kxld_array_get_item(&seg->sects, i); |
168 | |
169 | *sectp = sect; |
170 | } |
171 | |
172 | rval = KERN_SUCCESS; |
173 | finish: |
174 | return rval; |
175 | } |
176 | |
177 | /******************************************************************************* |
178 | *******************************************************************************/ |
179 | kern_return_t |
180 | kxld_seg_finalize_object_segment(KXLDArray *segarray, KXLDArray *section_order, |
181 | u_long hdrsize) |
182 | { |
183 | kern_return_t rval = KERN_FAILURE; |
184 | KXLDSeg *seg = NULL; |
185 | KXLDSect *sect = NULL; |
186 | u_long sect_offset = 0; |
187 | u_int i = 0; |
188 | |
189 | check(segarray); |
190 | check(section_order); |
191 | require_action(segarray->nitems == 1, finish, rval=KERN_FAILURE); |
192 | |
193 | seg = kxld_array_get_item(segarray, 0); |
194 | |
195 | /* Reorder the sections */ |
196 | |
197 | rval = reorder_sections(seg, section_order); |
198 | require_noerr(rval, finish); |
199 | |
200 | /* Set the initial link address at the end of the header pages */ |
201 | |
202 | seg->link_addr = kxld_round_page_cross_safe(hdrsize); |
203 | |
204 | /* Fix up all of the section addresses */ |
205 | |
206 | sect_offset = (u_long) seg->link_addr; |
207 | for (i = 0; i < seg->sects.nitems; ++i) { |
208 | sect = *(KXLDSect **)kxld_array_get_item(&seg->sects, i); |
209 | |
210 | sect->link_addr = kxld_sect_align_address(sect, sect_offset); |
211 | sect_offset = (u_long) (sect->link_addr + sect->size); |
212 | } |
213 | |
214 | /* Finish initializing the segment */ |
215 | |
216 | seg->vmsize = kxld_round_page_cross_safe(sect_offset) - seg->link_addr; |
217 | |
218 | rval = KERN_SUCCESS; |
219 | finish: |
220 | return rval; |
221 | } |
222 | |
223 | /******************************************************************************* |
224 | * The legacy section ordering used by kld was based of the order of sections |
225 | * in the kernel file. To achieve the same layout, we save the kernel's |
226 | * section ordering as an array of section names when the kernel file itself |
227 | * is linked. Then, when kexts are linked with the KXLD_LEGACY_LAYOUT flag, |
228 | * we refer to the kernel's section layout to order the kext's sections. |
229 | * |
230 | * The algorithm below is as follows. We iterate through all of the kernel's |
231 | * sections grouped by segment name, so that we are processing all of the __TEXT |
232 | * sections, then all of the __DATA sections, etc. We then iterate through the |
233 | * kext's sections with a similar grouping, looking for sections that match |
234 | * the current kernel's section. In this way, we order all of the matching |
235 | * kext sections in the order in which they appear in the kernel, and then place |
236 | * all remaining kext sections at the end of the current segment grouping in |
237 | * the order in which they originally appeared. Sections that only appear in |
238 | * the kernel are not created. segments that only appear in the kext are |
239 | * left in their original ordering. |
240 | * |
241 | * An example: |
242 | * |
243 | * Kernel sections: |
244 | * __TEXT,__text |
245 | * __TEXT,__const |
246 | * __DATA,__data |
247 | * |
248 | * Kext sections: |
249 | * __TEXT,__const |
250 | * __TEXT,__literal4 |
251 | * __TEXT,__text |
252 | * __DATA,__const |
253 | * __DATA,__data |
254 | * |
255 | * Reordered kext sections: |
256 | * __TEXT,__text |
257 | * __TEXT,__const |
258 | * __TEXT,__literal4 |
259 | * __DATA,__data |
260 | * __DATA,__const |
261 | * |
262 | * In the implementation below, we use a reorder buffer to hold pointers to the |
263 | * sections of the current working segment. We scan this buffer looking for |
264 | * matching sections, placing them in the segment's section index as we find them. |
265 | * If this function must exit early, the segment's section index is left in an |
266 | * unusable state. |
267 | *******************************************************************************/ |
268 | static kern_return_t |
269 | reorder_sections(KXLDSeg *seg, KXLDArray *section_order) |
270 | { |
271 | kern_return_t rval = KERN_FAILURE; |
272 | KXLDSect *sect = NULL; |
273 | KXLDSect **reorder_buffer = NULL; |
274 | KXLDSectionName *section_name = NULL; |
275 | const char *segname = NULL; |
276 | u_int sect_index = 0, legacy_index = 0, sect_reorder_index = 0; |
277 | u_int i = 0, j = 0; |
278 | u_int sect_start = 0, sect_end = 0, legacy_start = 0, legacy_end = 0; |
279 | u_int nsects = 0; |
280 | |
281 | check(seg); |
282 | check(section_order); |
283 | |
284 | /* Allocate the reorder buffer with enough space to hold all of the |
285 | * sections. |
286 | */ |
287 | |
288 | reorder_buffer = kxld_alloc( |
289 | seg->sects.nitems * sizeof(*reorder_buffer)); |
290 | require_action(reorder_buffer, finish, rval=KERN_RESOURCE_SHORTAGE); |
291 | |
292 | while (legacy_index < section_order->nitems) { |
293 | |
294 | /* Find the next group of sections with a common segment in the |
295 | * section_order array. |
296 | */ |
297 | |
298 | legacy_start = legacy_index++; |
299 | legacy_end = legacy_index; |
300 | |
301 | section_name = kxld_array_get_item(section_order, legacy_start); |
302 | segname = section_name->segname; |
303 | while (legacy_index < section_order->nitems) { |
304 | section_name = kxld_array_get_item(section_order, legacy_index); |
305 | if (!streq_safe(segname, section_name->segname, |
306 | sizeof(section_name->segname))) |
307 | { |
308 | break; |
309 | } |
310 | |
311 | ++legacy_index; |
312 | ++legacy_end; |
313 | } |
314 | |
315 | /* Find a group of sections in the kext that match the current |
316 | * section_order segment. |
317 | */ |
318 | |
319 | sect_start = sect_index; |
320 | sect_end = sect_index; |
321 | |
322 | while (sect_index < seg->sects.nitems) { |
323 | sect = *(KXLDSect **) kxld_array_get_item(&seg->sects, sect_index); |
324 | if (!streq_safe(segname, sect->segname, sizeof(sect->segname))) { |
325 | break; |
326 | } |
327 | |
328 | ++sect_index; |
329 | ++sect_end; |
330 | } |
331 | nsects = sect_end - sect_start; |
332 | |
333 | if (!nsects) continue; |
334 | |
335 | /* Populate the reorder buffer with the current group of kext sections */ |
336 | |
337 | for (i = sect_start; i < sect_end; ++i) { |
338 | reorder_buffer[i - sect_start] = |
339 | *(KXLDSect **) kxld_array_get_item(&seg->sects, i); |
340 | } |
341 | |
342 | /* For each section_order section, scan the reorder buffer for a matching |
343 | * kext section. If one is found, copy it into the next slot in the |
344 | * segment's section index. |
345 | */ |
346 | |
347 | sect_reorder_index = sect_start; |
348 | for (i = legacy_start; i < legacy_end; ++i) { |
349 | section_name = kxld_array_get_item(section_order, i); |
350 | sect = NULL; |
351 | |
352 | for (j = 0; j < nsects; ++j) { |
353 | sect = reorder_buffer[j]; |
354 | if (!sect) continue; |
355 | |
356 | if (streq_safe(section_name->sectname, sect->sectname, |
357 | sizeof(section_name->sectname))) |
358 | { |
359 | break; |
360 | } |
361 | |
362 | sect = NULL; |
363 | } |
364 | |
365 | if (sect) { |
366 | (void) reorder_section(&seg->sects, §_reorder_index, |
367 | reorder_buffer, j); |
368 | } |
369 | } |
370 | |
371 | /* If any sections remain in the reorder buffer, they are not specified |
372 | * in the section_order array, so append them to the section index in |
373 | * in the order they are found. |
374 | */ |
375 | |
376 | for (i = 0; i < nsects; ++i) { |
377 | if (!reorder_buffer[i]) continue; |
378 | reorder_section(&seg->sects, §_reorder_index, reorder_buffer, i); |
379 | } |
380 | } |
381 | |
382 | rval = KERN_SUCCESS; |
383 | |
384 | finish: |
385 | |
386 | if (reorder_buffer) { |
387 | kxld_free(reorder_buffer, seg->sects.nitems * sizeof(*reorder_buffer)); |
388 | reorder_buffer = NULL; |
389 | } |
390 | |
391 | return rval; |
392 | } |
393 | |
394 | /******************************************************************************* |
395 | *******************************************************************************/ |
396 | static void |
397 | reorder_section(KXLDArray *sects, u_int *sect_reorder_index, |
398 | KXLDSect **reorder_buffer, u_int reorder_buffer_index) |
399 | { |
400 | KXLDSect **tmp = NULL; |
401 | |
402 | tmp = kxld_array_get_item(sects, *sect_reorder_index); |
403 | |
404 | *tmp = reorder_buffer[reorder_buffer_index]; |
405 | reorder_buffer[reorder_buffer_index]->sectnum = *sect_reorder_index; |
406 | reorder_buffer[reorder_buffer_index] = NULL; |
407 | |
408 | ++(*sect_reorder_index); |
409 | } |
410 | |
411 | /******************************************************************************* |
412 | *******************************************************************************/ |
413 | kern_return_t |
414 | kxld_seg_init_linkedit(KXLDArray *segs) |
415 | { |
416 | kern_return_t rval = KERN_FAILURE; |
417 | KXLDSeg *seg = NULL; |
418 | KXLDSeg *le = NULL; |
419 | |
420 | rval = kxld_array_resize(segs, 2); |
421 | require_noerr(rval, finish); |
422 | |
423 | seg = kxld_array_get_item(segs, 0); |
424 | le = kxld_array_get_item(segs, 1); |
425 | |
426 | strlcpy(le->segname, SEG_LINKEDIT, sizeof(le->segname)); |
427 | le->link_addr = kxld_round_page_cross_safe(seg->link_addr + seg->vmsize); |
428 | le->maxprot = VM_PROT_ALL; |
429 | le->initprot = VM_PROT_DEFAULT; |
430 | |
431 | rval = KERN_SUCCESS; |
432 | |
433 | finish: |
434 | return rval; |
435 | } |
436 | #endif /* KXLD_USER_OR_OBJECT */ |
437 | |
438 | /******************************************************************************* |
439 | *******************************************************************************/ |
440 | void |
441 | kxld_seg_clear(KXLDSeg *seg) |
442 | { |
443 | check(seg); |
444 | |
445 | bzero(seg->segname, sizeof(seg->segname)); |
446 | seg->base_addr = 0; |
447 | seg->link_addr = 0; |
448 | seg->vmsize = 0; |
449 | seg->flags = 0; |
450 | seg->maxprot = 0; |
451 | seg->initprot = 0; |
452 | |
453 | /* Don't clear the individual sections here because kxld_kext.c will take |
454 | * care of that. |
455 | */ |
456 | kxld_array_clear(&seg->sects); |
457 | } |
458 | |
459 | /******************************************************************************* |
460 | *******************************************************************************/ |
461 | void |
462 | kxld_seg_deinit(KXLDSeg *seg) |
463 | { |
464 | check(seg); |
465 | |
466 | kxld_array_deinit(&seg->sects); |
467 | bzero(seg, sizeof(*seg)); |
468 | } |
469 | |
470 | /******************************************************************************* |
471 | *******************************************************************************/ |
472 | kxld_size_t |
473 | kxld_seg_get_vmsize(const KXLDSeg *seg) |
474 | { |
475 | check(seg); |
476 | |
477 | return seg->vmsize; |
478 | } |
479 | |
480 | /******************************************************************************* |
481 | *******************************************************************************/ |
482 | u_long |
483 | (const KXLDSeg *seg, boolean_t is_32_bit) |
484 | { |
485 | u_long size = 0; |
486 | |
487 | check(seg); |
488 | |
489 | if (is_32_bit) { |
490 | size += sizeof(struct segment_command); |
491 | } else { |
492 | size += sizeof(struct segment_command_64); |
493 | } |
494 | size += seg->sects.nitems * kxld_sect_get_macho_header_size(is_32_bit); |
495 | |
496 | return size; |
497 | } |
498 | |
499 | /******************************************************************************* |
500 | *******************************************************************************/ |
501 | /* This is no longer used, but may be useful some day... */ |
502 | #if 0 |
503 | u_long |
504 | kxld_seg_get_macho_data_size(const KXLDSeg *seg) |
505 | { |
506 | u_long size = 0; |
507 | u_int i = 0; |
508 | KXLDSect *sect = NULL; |
509 | |
510 | check(seg); |
511 | |
512 | for (i = 0; i < seg->sects.nitems; ++i) { |
513 | sect = get_sect_by_index(seg, i); |
514 | size = (u_long) kxld_sect_align_address(sect, size); |
515 | size += kxld_sect_get_macho_data_size(sect); |
516 | } |
517 | |
518 | return kxld_round_page_cross_safe(size); |
519 | } |
520 | #endif |
521 | |
522 | /******************************************************************************* |
523 | *******************************************************************************/ |
524 | static KXLDSect * |
525 | get_sect_by_index(const KXLDSeg *seg, u_int idx) |
526 | { |
527 | check(seg); |
528 | |
529 | return *(KXLDSect **) kxld_array_get_item(&seg->sects, idx); |
530 | } |
531 | |
532 | /******************************************************************************* |
533 | *******************************************************************************/ |
534 | kern_return_t |
535 | kxld_seg_export_macho_to_file_buffer(const KXLDSeg *seg, u_char *buf, |
536 | u_long *, u_long , |
537 | u_long *data_offset, u_long data_size, |
538 | boolean_t is_32_bit) |
539 | { |
540 | kern_return_t rval = KERN_FAILURE; |
541 | KXLDSect *sect = NULL; |
542 | u_long base_data_offset = *data_offset; |
543 | u_int i = 0; |
544 | struct segment_command *hdr32 = |
545 | (struct segment_command *) ((void *) (buf + *header_offset)); |
546 | struct segment_command_64 *hdr64 = |
547 | (struct segment_command_64 *) ((void *) (buf + *header_offset)); |
548 | |
549 | check(seg); |
550 | check(buf); |
551 | check(header_offset); |
552 | check(data_offset); |
553 | |
554 | /* Write out the header */ |
555 | |
556 | KXLD_3264_FUNC(is_32_bit, rval, |
557 | seg_export_macho_header_32, seg_export_macho_header_64, |
558 | seg, buf, header_offset, header_size, *data_offset); |
559 | require_noerr(rval, finish); |
560 | |
561 | /* Write out each section */ |
562 | |
563 | for (i = 0; i < seg->sects.nitems; ++i) { |
564 | sect = get_sect_by_index(seg, i); |
565 | |
566 | rval = kxld_sect_export_macho_to_file_buffer(sect, buf, header_offset, |
567 | header_size, data_offset, data_size, is_32_bit); |
568 | require_noerr(rval, finish); |
569 | } |
570 | |
571 | /* Update the filesize */ |
572 | |
573 | if (is_32_bit) { |
574 | hdr32->filesize = (uint32_t) (*data_offset - base_data_offset); |
575 | } else { |
576 | hdr64->filesize = (uint64_t) (*data_offset - base_data_offset); |
577 | } |
578 | |
579 | *data_offset = (u_long)kxld_round_page_cross_safe(*data_offset); |
580 | |
581 | rval = KERN_SUCCESS; |
582 | |
583 | finish: |
584 | return rval; |
585 | |
586 | } |
587 | |
588 | |
589 | /******************************************************************************* |
590 | *******************************************************************************/ |
591 | kern_return_t |
592 | kxld_seg_export_macho_to_vm(const KXLDSeg *seg, |
593 | u_char *buf, |
594 | u_long *, |
595 | u_long , |
596 | u_long data_size, |
597 | kxld_addr_t file_link_addr, |
598 | boolean_t is_32_bit) |
599 | { |
600 | kern_return_t rval = KERN_FAILURE; |
601 | KXLDSect * sect = NULL; |
602 | |
603 | // data_offset is used to set fileoff field in segment header |
604 | u_long data_offset; |
605 | u_int i = 0; |
606 | |
607 | check(seg); |
608 | check(buf); |
609 | check(header_offset); |
610 | |
611 | data_offset = (u_long) (seg->link_addr - file_link_addr); |
612 | |
613 | /* Write out the header */ |
614 | |
615 | KXLD_3264_FUNC(is_32_bit, rval, |
616 | seg_export_macho_header_32, seg_export_macho_header_64, |
617 | seg, |
618 | buf, |
619 | header_offset, header_size, data_offset); |
620 | require_noerr(rval, finish); |
621 | |
622 | /* Write out each section */ |
623 | |
624 | for (i = 0; i < seg->sects.nitems; ++i) { |
625 | sect = get_sect_by_index(seg, i); |
626 | |
627 | rval = kxld_sect_export_macho_to_vm(sect, buf, header_offset, |
628 | header_size, file_link_addr, data_size, is_32_bit); |
629 | require_noerr(rval, finish); |
630 | } |
631 | |
632 | rval = KERN_SUCCESS; |
633 | |
634 | finish: |
635 | return rval; |
636 | } |
637 | |
638 | #if KXLD_USER_OR_ILP32 |
639 | /******************************************************************************* |
640 | *******************************************************************************/ |
641 | static kern_return_t |
642 | seg_export_macho_header_32(const KXLDSeg *seg, u_char *buf, |
643 | u_long *header_offset, u_long header_size, u_long data_offset) |
644 | { |
645 | kern_return_t rval = KERN_FAILURE; |
646 | struct segment_command *seghdr = NULL; |
647 | |
648 | check(seg); |
649 | check(buf); |
650 | check(header_offset); |
651 | |
652 | require_action(sizeof(*seghdr) <= header_size - *header_offset, finish, |
653 | rval=KERN_FAILURE); |
654 | seghdr = (struct segment_command *) ((void *) (buf + *header_offset)); |
655 | *header_offset += sizeof(*seghdr); |
656 | |
657 | seghdr->cmd = LC_SEGMENT; |
658 | seghdr->cmdsize = (uint32_t) sizeof(*seghdr); |
659 | seghdr->cmdsize += |
660 | (uint32_t) (seg->sects.nitems * kxld_sect_get_macho_header_size(TRUE)); |
661 | strlcpy(seghdr->segname, seg->segname, sizeof(seghdr->segname)); |
662 | seghdr->vmaddr = (uint32_t) seg->link_addr; |
663 | seghdr->vmsize = (uint32_t) seg->vmsize; |
664 | seghdr->fileoff = (uint32_t) data_offset; |
665 | seghdr->filesize = (uint32_t) seg->vmsize; |
666 | seghdr->maxprot = seg->maxprot; |
667 | seghdr->initprot = seg->initprot; |
668 | seghdr->nsects = seg->sects.nitems; |
669 | seghdr->flags = 0; |
670 | |
671 | #if SPLIT_KEXTS_DEBUG |
672 | { |
673 | kxld_log(kKxldLogLinking, kKxldLogErr, |
674 | "segname %s seghdr %p vmaddr %p vmsize 0x%02X %u fileoff 0x%02X %u <%s>" , |
675 | seg->segname[0] ? seg->segname : "none" , |
676 | (void *) seghdr, |
677 | (void *) ((uint64_t)seghdr->vmaddr), |
678 | seghdr->vmsize, |
679 | seghdr->vmsize, |
680 | seghdr->fileoff, |
681 | seghdr->fileoff, |
682 | __func__); |
683 | } |
684 | #endif |
685 | |
686 | rval = KERN_SUCCESS; |
687 | |
688 | finish: |
689 | return rval; |
690 | } |
691 | #endif /* KXLD_USER_OR_ILP32 */ |
692 | |
693 | #if KXLD_USER_OR_LP64 |
694 | /******************************************************************************* |
695 | *******************************************************************************/ |
696 | static kern_return_t |
697 | (const KXLDSeg *seg, u_char *buf, |
698 | u_long *, u_long , u_long data_offset) |
699 | { |
700 | kern_return_t rval = KERN_FAILURE; |
701 | struct segment_command_64 *seghdr = NULL; |
702 | |
703 | check(seg); |
704 | check(buf); |
705 | check(header_offset); |
706 | |
707 | require_action(sizeof(*seghdr) <= header_size - *header_offset, finish, |
708 | rval=KERN_FAILURE); |
709 | |
710 | #if SPLIT_KEXTS_DEBUG |
711 | { |
712 | struct mach_header_64 *mach; |
713 | |
714 | mach = (struct mach_header_64 *) ((void *) buf); |
715 | |
716 | if (mach->magic != MH_MAGIC_64) { |
717 | kxld_log(kKxldLogLinking, kKxldLogErr, |
718 | "bad macho header at %p <%s>" , |
719 | (void *) mach, __func__); |
720 | goto finish; |
721 | } |
722 | } |
723 | #endif |
724 | |
725 | seghdr = (struct segment_command_64 *) ((void *) (buf + *header_offset)); |
726 | *header_offset += sizeof(*seghdr); |
727 | |
728 | seghdr->cmd = LC_SEGMENT_64; |
729 | seghdr->cmdsize = (uint32_t) sizeof(*seghdr); |
730 | seghdr->cmdsize += |
731 | (uint32_t) (seg->sects.nitems * kxld_sect_get_macho_header_size(FALSE)); |
732 | strlcpy(seghdr->segname, seg->segname, sizeof(seghdr->segname)); |
733 | seghdr->vmaddr = (uint64_t) seg->link_addr; |
734 | seghdr->vmsize = (uint64_t) seg->vmsize; |
735 | seghdr->fileoff = (uint64_t) data_offset; |
736 | seghdr->filesize = (uint64_t) seg->vmsize; |
737 | seghdr->maxprot = seg->maxprot; |
738 | seghdr->initprot = seg->initprot; |
739 | seghdr->nsects = seg->sects.nitems; |
740 | seghdr->flags = 0; |
741 | |
742 | #if SPLIT_KEXTS_DEBUG |
743 | { |
744 | kxld_log(kKxldLogLinking, kKxldLogErr, |
745 | "%p >>> Start of %s seghdr (size %lu) <%s>" , |
746 | (void *) seghdr, |
747 | seg->segname[0] ? seg->segname : "none" , |
748 | sizeof(*seghdr), |
749 | __func__); |
750 | kxld_log(kKxldLogLinking, kKxldLogErr, |
751 | "%p <<< End of %s seghdr <%s>" , |
752 | (void *) ((u_char *)seghdr + sizeof(*seghdr)), |
753 | seg->segname[0] ? seg->segname : "none" , |
754 | __func__); |
755 | |
756 | kxld_log(kKxldLogLinking, kKxldLogErr, |
757 | "%s seghdr, cmdsize %d vmaddr %p vmsize %p %llu fileoff %p %llu <%s>" , |
758 | seg->segname[0] ? seg->segname : "none" , |
759 | seghdr->cmdsize, |
760 | (void *) seghdr->vmaddr, |
761 | (void *) seghdr->vmsize, |
762 | seghdr->vmsize, |
763 | (void *) seghdr->fileoff, |
764 | seghdr->fileoff, |
765 | __func__); |
766 | } |
767 | #endif |
768 | |
769 | rval = KERN_SUCCESS; |
770 | |
771 | finish: |
772 | return rval; |
773 | } |
774 | #endif /* KXLD_USER_OR_LP64 */ |
775 | |
776 | /******************************************************************************* |
777 | *******************************************************************************/ |
778 | kern_return_t |
779 | kxld_seg_add_section(KXLDSeg *seg, KXLDSect *sect) |
780 | { |
781 | kern_return_t rval = KERN_FAILURE; |
782 | KXLDSect **sectp = NULL; |
783 | u_int i; |
784 | |
785 | check(seg); |
786 | check(sect); |
787 | require_action(streq_safe(seg->segname, sect->segname, sizeof(seg->segname)), |
788 | finish, rval=KERN_FAILURE); |
789 | |
790 | /* Add the section into the section index */ |
791 | |
792 | for (i = 0; i < seg->sects.nitems; ++i) { |
793 | sectp = kxld_array_get_item(&seg->sects, i); |
794 | if (NULL == *sectp) { |
795 | *sectp = sect; |
796 | break; |
797 | } |
798 | } |
799 | require_action(i < seg->sects.nitems, finish, rval=KERN_FAILURE); |
800 | |
801 | rval = KERN_SUCCESS; |
802 | |
803 | finish: |
804 | |
805 | return rval; |
806 | } |
807 | |
808 | /******************************************************************************* |
809 | *******************************************************************************/ |
810 | kern_return_t |
811 | kxld_seg_finish_init(KXLDSeg *seg) |
812 | { |
813 | kern_return_t rval = KERN_FAILURE; |
814 | u_int i = 0; |
815 | KXLDSect *sect = NULL; |
816 | kxld_addr_t maxaddr = 0; |
817 | kxld_size_t maxsize = 0; |
818 | |
819 | /* If we already have a size for this segment (e.g. from the mach-o load |
820 | * command) then don't recalculate the segment size. This is safer since |
821 | * when we recalculate we are making assumptions about page alignment and |
822 | * padding that the kext mach-o file was built with. Better to trust the |
823 | * macho-o info, if we have it. If we don't (i.e. vmsize == 0) then add up |
824 | * the section sizes and take a best guess at page padding. |
825 | */ |
826 | if ((seg->vmsize == 0) && (seg->sects.nitems)) { |
827 | for (i = 0; i < seg->sects.nitems; ++i) { |
828 | sect = get_sect_by_index(seg, i); |
829 | require_action(sect, finish, rval=KERN_FAILURE); |
830 | if (sect->base_addr > maxaddr) { |
831 | maxaddr = sect->base_addr; |
832 | maxsize = sect->size; |
833 | } |
834 | } |
835 | seg->vmsize = kxld_round_page_cross_safe(maxaddr + |
836 | maxsize - seg->base_addr); |
837 | |
838 | } |
839 | |
840 | rval = KERN_SUCCESS; |
841 | |
842 | finish: |
843 | return rval; |
844 | } |
845 | |
846 | /******************************************************************************* |
847 | *******************************************************************************/ |
848 | void |
849 | kxld_seg_set_vm_protections(KXLDSeg *seg, boolean_t strict_protections) |
850 | { |
851 | if (strict_protections) { |
852 | if (!strncmp(seg->segname, SEG_TEXT, sizeof(seg->segname))) { |
853 | seg->initprot = TEXT_SEG_PROT; |
854 | seg->maxprot = TEXT_SEG_PROT; |
855 | } else { |
856 | seg->initprot = DATA_SEG_PROT; |
857 | seg->maxprot = DATA_SEG_PROT; |
858 | } |
859 | } else { |
860 | seg->initprot = VM_PROT_ALL; |
861 | seg->maxprot = VM_PROT_ALL; |
862 | } |
863 | } |
864 | |
865 | /******************************************************************************* |
866 | *******************************************************************************/ |
867 | void |
868 | kxld_seg_relocate(KXLDSeg *seg, kxld_addr_t link_addr) |
869 | { |
870 | KXLDSect *sect = NULL; |
871 | u_int i = 0; |
872 | splitKextLinkInfo * link_info = (splitKextLinkInfo *) link_addr; |
873 | kxld_addr_t my_link_addr; |
874 | |
875 | if (isOldInterface) { |
876 | seg->link_addr += link_addr; |
877 | } |
878 | else { |
879 | if (isSplitKext) { |
880 | // we have a split kext |
881 | if (kxld_seg_is_text_seg(seg)) { |
882 | // assumes this is the beginning of the kext |
883 | my_link_addr = link_info->vmaddr_TEXT; |
884 | seg->link_addr = my_link_addr; |
885 | } |
886 | else if (kxld_seg_is_text_exec_seg(seg)) { |
887 | my_link_addr = link_info->vmaddr_TEXT_EXEC; |
888 | seg->link_addr = my_link_addr; |
889 | // vmaddr_TEXT_EXEC is the actual vmaddr for this segment so we need |
890 | // to adjust for kxld_sect_relocate assuming the link addr is |
891 | // the address of the kext (macho header in __TEXT) |
892 | my_link_addr -= seg->base_addr; |
893 | } |
894 | else if (kxld_seg_is_data_seg(seg)) { |
895 | my_link_addr = link_info->vmaddr_DATA; |
896 | seg->link_addr = my_link_addr; |
897 | // vmaddr_DATA is the actual vmaddr for this segment so we need |
898 | // to adjust for kxld_sect_relocate assuming the link addr is |
899 | // the address of the kext (macho header in __TEXT) |
900 | my_link_addr -= seg->base_addr; |
901 | } |
902 | else if (kxld_seg_is_data_const_seg(seg)) { |
903 | my_link_addr = link_info->vmaddr_DATA_CONST; |
904 | seg->link_addr = my_link_addr; |
905 | // vmaddr_DATA_CONST is the actual vmaddr for this segment so we need |
906 | // to adjust for kxld_sect_relocate assuming the link addr is |
907 | // the address of the kext (macho header in __TEXT) |
908 | my_link_addr -= seg->base_addr; |
909 | } |
910 | else if (kxld_seg_is_llvm_cov_seg(seg)) { |
911 | my_link_addr = link_info->vmaddr_LLVM_COV; |
912 | seg->link_addr = my_link_addr; |
913 | // vmaddr_LLVM_COV is the actual vmaddr for this segment so we need |
914 | // to adjust for kxld_sect_relocate assuming the link addr is |
915 | // the address of the kext (macho header in __TEXT) |
916 | my_link_addr -= seg->base_addr; |
917 | } |
918 | else if (kxld_seg_is_linkedit_seg(seg)) { |
919 | my_link_addr = link_info->vmaddr_LINKEDIT; |
920 | seg->link_addr = my_link_addr; |
921 | // vmaddr_DATA is the actual vmaddr for this segment so we need |
922 | // to adjust for kxld_sect_relocate assuming the link addr is |
923 | // the address of the kext (macho header in __TEXT) |
924 | my_link_addr -= seg->base_addr; |
925 | } |
926 | else { |
927 | kxld_log(kKxldLogLinking, kKxldLogErr, |
928 | " not expecting this segment %s!!! <%s>" , |
929 | seg->segname[0] ? seg->segname : "none" , |
930 | __func__); |
931 | my_link_addr = link_info->vmaddr_TEXT; |
932 | seg->link_addr += my_link_addr; |
933 | } |
934 | } |
935 | else { |
936 | my_link_addr = link_info->vmaddr_TEXT; |
937 | seg->link_addr += my_link_addr; |
938 | } |
939 | } |
940 | |
941 | #if SPLIT_KEXTS_DEBUG |
942 | { |
943 | kxld_log(kKxldLogLinking, kKxldLogErr, |
944 | "%p >>> Start of %s segment (vmsize %llu) <%s>)" , |
945 | (void *) seg->link_addr, |
946 | seg->segname[0] ? seg->segname : "none" , |
947 | seg->vmsize, |
948 | __func__); |
949 | kxld_log(kKxldLogLinking, kKxldLogErr, |
950 | "%p <<< End of %s segment <%s>" , |
951 | (void *) (seg->link_addr + seg->vmsize), |
952 | seg->segname[0] ? seg->segname : "none" , |
953 | __func__); |
954 | } |
955 | #endif |
956 | |
957 | for (i = 0; i < seg->sects.nitems; ++i) { |
958 | sect = get_sect_by_index(seg, i); |
959 | if (isOldInterface) { |
960 | kxld_sect_relocate(sect, link_addr); |
961 | } |
962 | else { |
963 | kxld_sect_relocate(sect, my_link_addr); |
964 | } |
965 | } |
966 | } |
967 | |
968 | /******************************************************************************* |
969 | *******************************************************************************/ |
970 | void |
971 | kxld_seg_populate_linkedit(KXLDSeg *seg, const KXLDSymtab *symtab, boolean_t is_32_bit |
972 | #if KXLD_PIC_KEXTS |
973 | , const KXLDArray *locrelocs |
974 | , const KXLDArray *extrelocs |
975 | , boolean_t target_supports_slideable_kexts |
976 | #endif /* KXLD_PIC_KEXTS */ |
977 | , uint32_t splitinfolc_size |
978 | ) |
979 | { |
980 | u_long size = 0; |
981 | |
982 | size += kxld_symtab_get_macho_data_size(symtab, is_32_bit); |
983 | |
984 | #if KXLD_PIC_KEXTS |
985 | if (target_supports_slideable_kexts) { |
986 | size += kxld_reloc_get_macho_data_size(locrelocs, extrelocs); |
987 | } |
988 | #endif /* KXLD_PIC_KEXTS */ |
989 | |
990 | // 0 unless this is a split kext |
991 | size += splitinfolc_size; |
992 | |
993 | seg->vmsize = kxld_round_page_cross_safe(size); |
994 | } |
995 | |
996 | /******************************************************************************* |
997 | *******************************************************************************/ |
998 | boolean_t |
999 | kxld_seg_is_split_seg(const KXLDSeg *seg) |
1000 | { |
1001 | boolean_t result = FALSE; |
1002 | |
1003 | check(seg); |
1004 | if (isSplitKext) { |
1005 | if (kxld_seg_is_data_seg(seg) || kxld_seg_is_linkedit_seg(seg) || |
1006 | kxld_seg_is_text_exec_seg(seg) || kxld_seg_is_data_const_seg(seg) || |
1007 | kxld_seg_is_llvm_cov_seg(seg)) { |
1008 | result = TRUE; |
1009 | } |
1010 | } |
1011 | |
1012 | return result; |
1013 | } |
1014 | |
1015 | boolean_t |
1016 | kxld_seg_is_text_seg(const KXLDSeg *seg) |
1017 | { |
1018 | boolean_t result = FALSE; |
1019 | |
1020 | check(seg); |
1021 | result = !strncmp(seg->segname, SEG_TEXT, sizeof(seg->segname)); |
1022 | |
1023 | return result; |
1024 | } |
1025 | |
1026 | boolean_t |
1027 | kxld_seg_is_text_exec_seg(const KXLDSeg *seg) |
1028 | { |
1029 | boolean_t result = FALSE; |
1030 | |
1031 | check(seg); |
1032 | result = !strncmp(seg->segname, "__TEXT_EXEC" , sizeof(seg->segname)); |
1033 | |
1034 | return result; |
1035 | } |
1036 | |
1037 | boolean_t |
1038 | kxld_seg_is_data_seg(const KXLDSeg *seg) |
1039 | { |
1040 | boolean_t result = FALSE; |
1041 | |
1042 | check(seg); |
1043 | result = !strncmp(seg->segname, SEG_DATA, sizeof(seg->segname)); |
1044 | |
1045 | return result; |
1046 | } |
1047 | |
1048 | boolean_t |
1049 | kxld_seg_is_data_const_seg(const KXLDSeg *seg) |
1050 | { |
1051 | boolean_t result = FALSE; |
1052 | |
1053 | check(seg); |
1054 | result = !strncmp(seg->segname, "__DATA_CONST" , sizeof(seg->segname)); |
1055 | |
1056 | return result; |
1057 | } |
1058 | |
1059 | boolean_t |
1060 | kxld_seg_is_linkedit_seg(const KXLDSeg *seg) |
1061 | { |
1062 | boolean_t result = FALSE; |
1063 | |
1064 | check(seg); |
1065 | result = !strncmp(seg->segname, SEG_LINKEDIT, sizeof(seg->segname)); |
1066 | |
1067 | return result; |
1068 | } |
1069 | |
1070 | boolean_t |
1071 | kxld_seg_is_llvm_cov_seg(const KXLDSeg *seg) |
1072 | { |
1073 | boolean_t result = FALSE; |
1074 | |
1075 | check(seg); |
1076 | result = !strncmp(seg->segname, "__LLVM_COV" , sizeof(seg->segname)); |
1077 | |
1078 | return result; |
1079 | } |
1080 | |