]>
Commit | Line | Data |
---|---|---|
b0d623f7 A |
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_sect.h" | |
43 | #include "kxld_seg.h" | |
6d2010ae | 44 | #include "kxld_symtab.h" |
b0d623f7 A |
45 | #include "kxld_util.h" |
46 | ||
47 | #define MAX_SEGS 20 | |
48 | ||
49 | #define TEXT_SEG_PROT (VM_PROT_READ | VM_PROT_EXECUTE) | |
50 | #define DATA_SEG_PROT (VM_PROT_READ | VM_PROT_WRITE) | |
51 | ||
52 | #if KXLD_USER_OR_OBJECT | |
53 | static kern_return_t reorder_sections(KXLDSeg *seg, KXLDArray *section_order); | |
54 | static void reorder_section(KXLDArray *sects, u_int *sect_reorder_index, | |
55 | KXLDSect **reorder_buffer, u_int reorder_buffer_index); | |
56 | #endif /* KXLD_USER_OR_OBJECT */ | |
57 | ||
58 | #if 0 | |
59 | static KXLDSeg * get_segment_by_name(KXLDArray *segarray, const char *name); | |
60 | #endif | |
61 | ||
62 | #if KXLD_USER_OR_ILP32 | |
63 | static kern_return_t seg_export_macho_header_32(const KXLDSeg *seg, u_char *buf, | |
64 | u_long *header_offset, u_long header_size, u_long data_offset); | |
65 | #endif | |
66 | #if KXLD_USER_OR_LP64 | |
67 | static kern_return_t seg_export_macho_header_64(const KXLDSeg *seg, u_char *buf, | |
68 | u_long *header_offset, u_long header_size, u_long data_offset); | |
69 | #endif | |
70 | ||
71 | static KXLDSect * get_sect_by_index(const KXLDSeg *seg, u_int idx); | |
72 | ||
73 | #if KXLD_USER_OR_ILP32 | |
74 | /******************************************************************************* | |
75 | *******************************************************************************/ | |
76 | kern_return_t | |
77 | kxld_seg_init_from_macho_32(KXLDSeg *seg, struct segment_command *src) | |
78 | { | |
79 | kern_return_t rval = KERN_FAILURE; | |
80 | check(seg); | |
81 | check(src); | |
82 | ||
83 | strlcpy(seg->segname, src->segname, sizeof(seg->segname)); | |
84 | seg->base_addr = src->vmaddr; | |
85 | seg->link_addr = src->vmaddr; | |
86 | seg->vmsize = src->vmsize; | |
87 | seg->fileoff = src->fileoff; | |
88 | seg->maxprot = src->maxprot; | |
89 | seg->initprot = src->initprot; | |
90 | seg->flags = src->flags; | |
91 | ||
92 | rval = kxld_array_init(&seg->sects, sizeof(KXLDSect *), src->nsects); | |
93 | require_noerr(rval, finish); | |
94 | ||
95 | rval = KERN_SUCCESS; | |
96 | ||
97 | finish: | |
98 | return rval; | |
99 | } | |
100 | #endif /* KXLD_USER_OR_ILP32 */ | |
101 | ||
102 | #if KXLD_USER_OR_LP64 | |
103 | /******************************************************************************* | |
104 | *******************************************************************************/ | |
105 | kern_return_t | |
106 | kxld_seg_init_from_macho_64(KXLDSeg *seg, struct segment_command_64 *src) | |
107 | { | |
108 | kern_return_t rval = KERN_FAILURE; | |
109 | check(seg); | |
110 | check(src); | |
111 | ||
112 | strlcpy(seg->segname, src->segname, sizeof(seg->segname)); | |
113 | seg->base_addr = src->vmaddr; | |
114 | seg->link_addr = src->vmaddr; | |
115 | seg->vmsize = src->vmsize; | |
116 | seg->fileoff = src->fileoff; | |
117 | seg->maxprot = src->maxprot; | |
118 | seg->initprot = src->initprot; | |
119 | seg->flags = src->flags; | |
120 | ||
121 | rval = kxld_array_init(&seg->sects, sizeof(KXLDSect *), src->nsects); | |
122 | require_noerr(rval, finish); | |
123 | ||
124 | rval = KERN_SUCCESS; | |
125 | ||
126 | finish: | |
127 | return rval; | |
128 | } | |
129 | #endif /* KXLD_USER_OR_LP64 */ | |
130 | ||
131 | #if KXLD_USER_OR_OBJECT | |
132 | /******************************************************************************* | |
133 | *******************************************************************************/ | |
134 | kern_return_t | |
135 | kxld_seg_create_seg_from_sections(KXLDArray *segarray, KXLDArray *sectarray) | |
136 | { | |
137 | kern_return_t rval = KERN_FAILURE; | |
138 | KXLDSeg *seg = NULL; | |
139 | KXLDSect *sect = NULL; | |
140 | KXLDSect **sectp = NULL; | |
141 | u_int i = 0; | |
142 | ||
143 | /* Initialize the segment array to one segment */ | |
144 | ||
145 | rval = kxld_array_init(segarray, sizeof(KXLDSeg), 1); | |
146 | require_noerr(rval, finish); | |
147 | ||
148 | /* Initialize the segment */ | |
149 | ||
150 | seg = kxld_array_get_item(segarray, 0); | |
151 | seg->initprot = VM_PROT_ALL; | |
152 | seg->maxprot = VM_PROT_ALL; | |
153 | seg->link_addr = 0; | |
154 | ||
155 | /* Add the sections to the segment */ | |
156 | ||
157 | rval = kxld_array_init(&seg->sects, sizeof(KXLDSect *), sectarray->nitems); | |
158 | require_noerr(rval, finish); | |
159 | ||
160 | for (i = 0; i < sectarray->nitems; ++i) { | |
161 | sect = kxld_array_get_item(sectarray, i); | |
162 | sectp = kxld_array_get_item(&seg->sects, i); | |
163 | ||
164 | *sectp = sect; | |
165 | } | |
166 | ||
167 | rval = KERN_SUCCESS; | |
168 | finish: | |
169 | return rval; | |
170 | } | |
171 | ||
172 | /******************************************************************************* | |
173 | *******************************************************************************/ | |
174 | kern_return_t | |
175 | kxld_seg_finalize_object_segment(KXLDArray *segarray, KXLDArray *section_order, | |
176 | u_long hdrsize) | |
177 | { | |
178 | kern_return_t rval = KERN_FAILURE; | |
179 | KXLDSeg *seg = NULL; | |
180 | KXLDSect *sect = NULL; | |
181 | u_long sect_offset = 0; | |
182 | u_int i = 0; | |
183 | ||
184 | check(segarray); | |
185 | check(section_order); | |
186 | require_action(segarray->nitems == 1, finish, rval=KERN_FAILURE); | |
187 | ||
188 | seg = kxld_array_get_item(segarray, 0); | |
189 | ||
190 | /* Reorder the sections */ | |
191 | ||
192 | rval = reorder_sections(seg, section_order); | |
193 | require_noerr(rval, finish); | |
194 | ||
195 | /* Set the initial link address at the end of the header pages */ | |
196 | ||
197 | seg->link_addr = round_page(hdrsize); | |
198 | ||
199 | /* Fix up all of the section addresses */ | |
200 | ||
201 | sect_offset = (u_long) seg->link_addr; | |
202 | for (i = 0; i < seg->sects.nitems; ++i) { | |
203 | sect = *(KXLDSect **)kxld_array_get_item(&seg->sects, i); | |
204 | ||
205 | sect->link_addr = kxld_sect_align_address(sect, sect_offset); | |
206 | sect_offset = (u_long) (sect->link_addr + sect->size); | |
207 | } | |
208 | ||
209 | /* Finish initializing the segment */ | |
210 | ||
211 | seg->vmsize = round_page(sect_offset) - seg->link_addr; | |
212 | ||
213 | rval = KERN_SUCCESS; | |
214 | finish: | |
215 | return rval; | |
216 | } | |
217 | ||
218 | /******************************************************************************* | |
219 | * The legacy section ordering used by kld was based of the order of sections | |
220 | * in the kernel file. To achieve the same layout, we save the kernel's | |
221 | * section ordering as an array of section names when the kernel file itself | |
222 | * is linked. Then, when kexts are linked with the KXLD_LEGACY_LAYOUT flag, | |
223 | * we refer to the kernel's section layout to order the kext's sections. | |
224 | * | |
225 | * The algorithm below is as follows. We iterate through all of the kernel's | |
226 | * sections grouped by segment name, so that we are processing all of the __TEXT | |
227 | * sections, then all of the __DATA sections, etc. We then iterate through the | |
228 | * kext's sections with a similar grouping, looking for sections that match | |
229 | * the current kernel's section. In this way, we order all of the matching | |
230 | * kext sections in the order in which they appear in the kernel, and then place | |
231 | * all remaining kext sections at the end of the current segment grouping in | |
232 | * the order in which they originally appeared. Sections that only appear in | |
233 | * the kernel are not created. segments that only appear in the kext are | |
234 | * left in their original ordering. | |
235 | * | |
236 | * An example: | |
237 | * | |
238 | * Kernel sections: | |
239 | * __TEXT,__text | |
240 | * __TEXT,__initcode | |
241 | * __TEXT,__const | |
242 | * __DATA,__data | |
243 | * | |
244 | * Kext sections: | |
245 | * __TEXT,__const | |
246 | * __TEXT,__literal4 | |
247 | * __TEXT,__text | |
248 | * __DATA,__const | |
249 | * __DATA,__data | |
250 | * | |
251 | * Reordered kext sections: | |
252 | * __TEXT,__text | |
253 | * __TEXT,__const | |
254 | * __TEXT,__literal4 | |
255 | * __DATA,__data | |
256 | * __DATA,__const | |
257 | * | |
258 | * In the implementation below, we use a reorder buffer to hold pointers to the | |
259 | * sections of the current working segment. We scan this buffer looking for | |
260 | * matching sections, placing them in the segment's section index as we find them. | |
261 | * If this function must exit early, the segment's section index is left in an | |
262 | * unusable state. | |
263 | *******************************************************************************/ | |
264 | static kern_return_t | |
265 | reorder_sections(KXLDSeg *seg, KXLDArray *section_order) | |
266 | { | |
267 | kern_return_t rval = KERN_FAILURE; | |
268 | KXLDSect *sect = NULL; | |
269 | KXLDSect **reorder_buffer = NULL; | |
270 | KXLDSectionName *section_name = NULL; | |
271 | const char *segname = NULL; | |
272 | u_int sect_index = 0, legacy_index = 0, sect_reorder_index = 0; | |
273 | u_int i = 0, j = 0; | |
274 | u_int sect_start = 0, sect_end = 0, legacy_start = 0, legacy_end = 0; | |
275 | u_int nsects = 0; | |
276 | ||
277 | check(seg); | |
278 | check(section_order); | |
279 | ||
280 | /* Allocate the reorder buffer with enough space to hold all of the | |
281 | * sections. | |
282 | */ | |
283 | ||
284 | reorder_buffer = kxld_alloc( | |
285 | seg->sects.nitems * sizeof(*reorder_buffer)); | |
286 | require_action(reorder_buffer, finish, rval=KERN_RESOURCE_SHORTAGE); | |
287 | ||
288 | while (legacy_index < section_order->nitems) { | |
289 | ||
290 | /* Find the next group of sections with a common segment in the | |
291 | * section_order array. | |
292 | */ | |
293 | ||
294 | legacy_start = legacy_index++; | |
295 | legacy_end = legacy_index; | |
296 | ||
297 | section_name = kxld_array_get_item(section_order, legacy_start); | |
298 | segname = section_name->segname; | |
299 | while (legacy_index < section_order->nitems) { | |
300 | section_name = kxld_array_get_item(section_order, legacy_index); | |
301 | if (!streq_safe(segname, section_name->segname, | |
302 | sizeof(section_name->segname))) | |
303 | { | |
304 | break; | |
305 | } | |
306 | ||
307 | ++legacy_index; | |
308 | ++legacy_end; | |
309 | } | |
310 | ||
311 | /* Find a group of sections in the kext that match the current | |
312 | * section_order segment. | |
313 | */ | |
314 | ||
315 | sect_start = sect_index; | |
316 | sect_end = sect_index; | |
317 | ||
318 | while (sect_index < seg->sects.nitems) { | |
319 | sect = *(KXLDSect **) kxld_array_get_item(&seg->sects, sect_index); | |
320 | if (!streq_safe(segname, sect->segname, sizeof(sect->segname))) { | |
321 | break; | |
322 | } | |
323 | ||
324 | ++sect_index; | |
325 | ++sect_end; | |
326 | } | |
327 | nsects = sect_end - sect_start; | |
328 | ||
329 | if (!nsects) continue; | |
330 | ||
331 | /* Populate the reorder buffer with the current group of kext sections */ | |
332 | ||
333 | for (i = sect_start; i < sect_end; ++i) { | |
334 | reorder_buffer[i - sect_start] = | |
335 | *(KXLDSect **) kxld_array_get_item(&seg->sects, i); | |
336 | } | |
337 | ||
338 | /* For each section_order section, scan the reorder buffer for a matching | |
339 | * kext section. If one is found, copy it into the next slot in the | |
340 | * segment's section index. | |
341 | */ | |
342 | ||
343 | sect_reorder_index = sect_start; | |
344 | for (i = legacy_start; i < legacy_end; ++i) { | |
345 | section_name = kxld_array_get_item(section_order, i); | |
346 | sect = NULL; | |
347 | ||
348 | for (j = 0; j < nsects; ++j) { | |
349 | sect = reorder_buffer[j]; | |
350 | if (!sect) continue; | |
351 | ||
352 | if (streq_safe(section_name->sectname, sect->sectname, | |
353 | sizeof(section_name->sectname))) | |
354 | { | |
355 | break; | |
356 | } | |
357 | ||
358 | sect = NULL; | |
359 | } | |
360 | ||
361 | if (sect) { | |
362 | (void) reorder_section(&seg->sects, §_reorder_index, | |
363 | reorder_buffer, j); | |
364 | } | |
365 | } | |
366 | ||
367 | /* If any sections remain in the reorder buffer, they are not specified | |
368 | * in the section_order array, so append them to the section index in | |
369 | * in the order they are found. | |
370 | */ | |
371 | ||
372 | for (i = 0; i < nsects; ++i) { | |
373 | if (!reorder_buffer[i]) continue; | |
374 | reorder_section(&seg->sects, §_reorder_index, reorder_buffer, i); | |
375 | } | |
376 | } | |
377 | ||
378 | rval = KERN_SUCCESS; | |
379 | ||
380 | finish: | |
381 | ||
382 | if (reorder_buffer) { | |
383 | kxld_free(reorder_buffer, seg->sects.nitems * sizeof(*reorder_buffer)); | |
384 | reorder_buffer = NULL; | |
385 | } | |
386 | ||
387 | return rval; | |
388 | } | |
389 | ||
390 | /******************************************************************************* | |
391 | *******************************************************************************/ | |
392 | static void | |
393 | reorder_section(KXLDArray *sects, u_int *sect_reorder_index, | |
394 | KXLDSect **reorder_buffer, u_int reorder_buffer_index) | |
395 | { | |
396 | KXLDSect **tmp = NULL; | |
397 | ||
398 | tmp = kxld_array_get_item(sects, *sect_reorder_index); | |
399 | ||
400 | *tmp = reorder_buffer[reorder_buffer_index]; | |
401 | reorder_buffer[reorder_buffer_index]->sectnum = *sect_reorder_index; | |
402 | reorder_buffer[reorder_buffer_index] = NULL; | |
403 | ||
404 | ++(*sect_reorder_index); | |
405 | } | |
6d2010ae A |
406 | |
407 | /******************************************************************************* | |
408 | *******************************************************************************/ | |
409 | kern_return_t | |
410 | kxld_seg_init_linkedit(KXLDArray *segs) | |
411 | { | |
412 | kern_return_t rval = KERN_FAILURE; | |
413 | KXLDSeg *seg = NULL; | |
414 | KXLDSeg *le = NULL; | |
415 | ||
416 | rval = kxld_array_resize(segs, 2); | |
417 | require_noerr(rval, finish); | |
418 | ||
419 | seg = kxld_array_get_item(segs, 0); | |
420 | le = kxld_array_get_item(segs, 1); | |
421 | ||
422 | strlcpy(le->segname, SEG_LINKEDIT, sizeof(le->segname)); | |
423 | le->link_addr = round_page(seg->link_addr + seg->vmsize); | |
424 | le->maxprot = VM_PROT_ALL; | |
425 | le->initprot = VM_PROT_DEFAULT; | |
426 | ||
427 | rval = KERN_SUCCESS; | |
428 | ||
429 | finish: | |
430 | return rval; | |
431 | } | |
b0d623f7 A |
432 | #endif /* KXLD_USER_OR_OBJECT */ |
433 | ||
434 | /******************************************************************************* | |
435 | *******************************************************************************/ | |
436 | void | |
437 | kxld_seg_clear(KXLDSeg *seg) | |
438 | { | |
439 | check(seg); | |
440 | ||
441 | bzero(seg->segname, sizeof(seg->segname)); | |
442 | seg->base_addr = 0; | |
443 | seg->link_addr = 0; | |
444 | seg->vmsize = 0; | |
445 | seg->flags = 0; | |
446 | seg->maxprot = 0; | |
447 | seg->initprot = 0; | |
448 | ||
449 | /* Don't clear the individual sections here because kxld_kext.c will take | |
450 | * care of that. | |
451 | */ | |
452 | kxld_array_clear(&seg->sects); | |
453 | } | |
454 | ||
455 | /******************************************************************************* | |
456 | *******************************************************************************/ | |
457 | void | |
458 | kxld_seg_deinit(KXLDSeg *seg) | |
459 | { | |
460 | check(seg); | |
461 | ||
462 | kxld_array_deinit(&seg->sects); | |
463 | bzero(seg, sizeof(*seg)); | |
464 | } | |
465 | ||
466 | /******************************************************************************* | |
467 | *******************************************************************************/ | |
468 | kxld_size_t | |
469 | kxld_seg_get_vmsize(const KXLDSeg *seg) | |
470 | { | |
471 | check(seg); | |
472 | ||
473 | return seg->vmsize; | |
474 | } | |
475 | ||
476 | /******************************************************************************* | |
477 | *******************************************************************************/ | |
478 | u_long | |
479 | kxld_seg_get_macho_header_size(const KXLDSeg *seg, boolean_t is_32_bit) | |
480 | { | |
481 | u_long size = 0; | |
482 | ||
483 | check(seg); | |
484 | ||
485 | if (is_32_bit) { | |
486 | size += sizeof(struct segment_command); | |
487 | } else { | |
488 | size += sizeof(struct segment_command_64); | |
489 | } | |
490 | size += seg->sects.nitems * kxld_sect_get_macho_header_size(is_32_bit); | |
491 | ||
492 | return size; | |
493 | } | |
494 | ||
495 | /******************************************************************************* | |
496 | *******************************************************************************/ | |
497 | u_long | |
498 | kxld_seg_get_macho_data_size(const KXLDSeg *seg) | |
499 | { | |
500 | u_long size = 0; | |
501 | u_int i = 0; | |
502 | KXLDSect *sect = NULL; | |
503 | ||
504 | check(seg); | |
505 | ||
506 | for (i = 0; i < seg->sects.nitems; ++i) { | |
507 | sect = get_sect_by_index(seg, i); | |
508 | size = (u_long) kxld_sect_align_address(sect, size); | |
509 | size += kxld_sect_get_macho_data_size(sect); | |
510 | } | |
511 | ||
512 | return round_page(size); | |
513 | } | |
514 | ||
515 | /******************************************************************************* | |
516 | *******************************************************************************/ | |
517 | static KXLDSect * | |
518 | get_sect_by_index(const KXLDSeg *seg, u_int idx) | |
519 | { | |
520 | check(seg); | |
521 | ||
522 | return *(KXLDSect **) kxld_array_get_item(&seg->sects, idx); | |
523 | } | |
524 | ||
525 | /******************************************************************************* | |
526 | *******************************************************************************/ | |
527 | kern_return_t | |
528 | kxld_seg_export_macho_to_file_buffer(const KXLDSeg *seg, u_char *buf, | |
529 | u_long *header_offset, u_long header_size, | |
530 | u_long *data_offset, u_long data_size, | |
531 | boolean_t is_32_bit) | |
532 | { | |
533 | kern_return_t rval = KERN_FAILURE; | |
534 | KXLDSect *sect = NULL; | |
535 | u_long base_data_offset = *data_offset; | |
536 | u_int i = 0; | |
537 | struct segment_command *hdr32 = | |
538 | (struct segment_command *) (buf + *header_offset); | |
539 | struct segment_command_64 *hdr64 = | |
540 | (struct segment_command_64 *) (buf + *header_offset); | |
541 | ||
542 | check(seg); | |
543 | check(buf); | |
544 | check(header_offset); | |
545 | check(data_offset); | |
546 | ||
547 | /* Write out the header */ | |
548 | ||
549 | KXLD_3264_FUNC(is_32_bit, rval, | |
550 | seg_export_macho_header_32, seg_export_macho_header_64, | |
551 | seg, buf, header_offset, header_size, *data_offset); | |
552 | require_noerr(rval, finish); | |
553 | ||
554 | /* Write out each section */ | |
555 | ||
556 | for (i = 0; i < seg->sects.nitems; ++i) { | |
557 | sect = get_sect_by_index(seg, i); | |
558 | ||
559 | rval = kxld_sect_export_macho_to_file_buffer(sect, buf, header_offset, | |
560 | header_size, data_offset, data_size, is_32_bit); | |
561 | require_noerr(rval, finish); | |
562 | } | |
563 | ||
564 | /* Update the filesize */ | |
565 | ||
566 | if (is_32_bit) { | |
567 | hdr32->filesize = (uint32_t) (*data_offset - base_data_offset); | |
568 | } else { | |
569 | hdr64->filesize = (uint64_t) (*data_offset - base_data_offset); | |
570 | } | |
571 | ||
572 | *data_offset = round_page(*data_offset); | |
573 | ||
574 | rval = KERN_SUCCESS; | |
575 | ||
576 | finish: | |
577 | return rval; | |
578 | ||
579 | } | |
580 | ||
581 | /******************************************************************************* | |
582 | *******************************************************************************/ | |
583 | kern_return_t | |
584 | kxld_seg_export_macho_to_vm(const KXLDSeg *seg, u_char *buf, | |
585 | u_long *header_offset, u_long header_size, | |
586 | u_long data_size, kxld_addr_t file_link_addr, | |
587 | boolean_t is_32_bit) | |
588 | { | |
589 | kern_return_t rval = KERN_FAILURE; | |
590 | KXLDSect *sect = NULL; | |
591 | u_long data_offset = (u_long) (seg->link_addr - file_link_addr); | |
592 | u_int i = 0; | |
593 | ||
594 | check(seg); | |
595 | check(buf); | |
596 | check(header_offset); | |
597 | ||
598 | /* Write out the header */ | |
599 | ||
600 | KXLD_3264_FUNC(is_32_bit, rval, | |
601 | seg_export_macho_header_32, seg_export_macho_header_64, | |
602 | seg, buf, header_offset, header_size, data_offset); | |
603 | require_noerr(rval, finish); | |
604 | ||
605 | /* Write out each section */ | |
606 | ||
607 | for (i = 0; i < seg->sects.nitems; ++i) { | |
608 | sect = get_sect_by_index(seg, i); | |
609 | ||
610 | rval = kxld_sect_export_macho_to_vm(sect, buf, header_offset, | |
611 | header_size, file_link_addr, data_size, is_32_bit); | |
612 | require_noerr(rval, finish); | |
613 | } | |
614 | ||
615 | rval = KERN_SUCCESS; | |
616 | ||
617 | finish: | |
618 | return rval; | |
619 | } | |
620 | ||
621 | #if KXLD_USER_OR_ILP32 | |
622 | /******************************************************************************* | |
623 | *******************************************************************************/ | |
624 | static kern_return_t | |
625 | seg_export_macho_header_32(const KXLDSeg *seg, u_char *buf, | |
626 | u_long *header_offset, u_long header_size, u_long data_offset) | |
627 | { | |
628 | kern_return_t rval = KERN_FAILURE; | |
629 | struct segment_command *seghdr = NULL; | |
630 | ||
631 | check(seg); | |
632 | check(buf); | |
633 | check(header_offset); | |
634 | ||
635 | require_action(sizeof(*seghdr) <= header_size - *header_offset, finish, | |
636 | rval=KERN_FAILURE); | |
637 | seghdr = (struct segment_command *) (buf + *header_offset); | |
638 | *header_offset += sizeof(*seghdr); | |
639 | ||
640 | seghdr->cmd = LC_SEGMENT; | |
641 | seghdr->cmdsize = (uint32_t) sizeof(*seghdr); | |
642 | seghdr->cmdsize += | |
643 | (uint32_t) (seg->sects.nitems * kxld_sect_get_macho_header_size(TRUE)); | |
644 | strlcpy(seghdr->segname, seg->segname, sizeof(seghdr->segname)); | |
645 | seghdr->vmaddr = (uint32_t) seg->link_addr; | |
646 | seghdr->vmsize = (uint32_t) seg->vmsize; | |
647 | seghdr->fileoff = (uint32_t) data_offset; | |
648 | seghdr->filesize = (uint32_t) seg->vmsize; | |
649 | seghdr->maxprot = seg->maxprot; | |
650 | seghdr->initprot = seg->initprot; | |
651 | seghdr->nsects = seg->sects.nitems; | |
652 | seghdr->flags = 0; | |
653 | ||
654 | rval = KERN_SUCCESS; | |
655 | ||
656 | finish: | |
657 | return rval; | |
658 | } | |
659 | #endif /* KXLD_USER_OR_ILP32 */ | |
660 | ||
661 | #if KXLD_USER_OR_LP64 | |
662 | /******************************************************************************* | |
663 | *******************************************************************************/ | |
664 | static kern_return_t | |
665 | seg_export_macho_header_64(const KXLDSeg *seg, u_char *buf, | |
666 | u_long *header_offset, u_long header_size, u_long data_offset) | |
667 | { | |
668 | kern_return_t rval = KERN_FAILURE; | |
669 | struct segment_command_64 *seghdr = NULL; | |
670 | ||
671 | check(seg); | |
672 | check(buf); | |
673 | check(header_offset); | |
674 | ||
675 | require_action(sizeof(*seghdr) <= header_size - *header_offset, finish, | |
676 | rval=KERN_FAILURE); | |
677 | seghdr = (struct segment_command_64 *) (buf + *header_offset); | |
678 | *header_offset += sizeof(*seghdr); | |
679 | ||
680 | seghdr->cmd = LC_SEGMENT_64; | |
681 | seghdr->cmdsize = (uint32_t) sizeof(*seghdr); | |
682 | seghdr->cmdsize += | |
683 | (uint32_t) (seg->sects.nitems * kxld_sect_get_macho_header_size(FALSE)); | |
684 | strlcpy(seghdr->segname, seg->segname, sizeof(seghdr->segname)); | |
685 | seghdr->vmaddr = (uint64_t) seg->link_addr; | |
686 | seghdr->vmsize = (uint64_t) seg->vmsize; | |
687 | seghdr->fileoff = (uint64_t) data_offset; | |
688 | seghdr->filesize = (uint64_t) seg->vmsize; | |
689 | seghdr->maxprot = seg->maxprot; | |
690 | seghdr->initprot = seg->initprot; | |
691 | seghdr->nsects = seg->sects.nitems; | |
692 | seghdr->flags = 0; | |
693 | ||
694 | rval = KERN_SUCCESS; | |
695 | ||
696 | finish: | |
697 | return rval; | |
698 | } | |
699 | #endif /* KXLD_USER_OR_LP64 */ | |
700 | ||
701 | /******************************************************************************* | |
702 | *******************************************************************************/ | |
703 | kern_return_t | |
704 | kxld_seg_add_section(KXLDSeg *seg, KXLDSect *sect) | |
705 | { | |
706 | kern_return_t rval = KERN_FAILURE; | |
707 | KXLDSect **sectp = NULL; | |
708 | u_int i; | |
709 | ||
710 | check(seg); | |
711 | check(sect); | |
712 | require_action(streq_safe(seg->segname, sect->segname, sizeof(seg->segname)), | |
713 | finish, rval=KERN_FAILURE); | |
714 | ||
715 | /* Add the section into the section index */ | |
716 | ||
717 | for (i = 0; i < seg->sects.nitems; ++i) { | |
718 | sectp = kxld_array_get_item(&seg->sects, i); | |
719 | if (NULL == *sectp) { | |
720 | *sectp = sect; | |
721 | break; | |
722 | } | |
723 | } | |
724 | require_action(i < seg->sects.nitems, finish, rval=KERN_FAILURE); | |
725 | ||
726 | rval = KERN_SUCCESS; | |
727 | ||
728 | finish: | |
729 | ||
730 | return rval; | |
731 | } | |
732 | ||
733 | /******************************************************************************* | |
734 | *******************************************************************************/ | |
735 | kern_return_t | |
736 | kxld_seg_finish_init(KXLDSeg *seg) | |
737 | { | |
738 | kern_return_t rval = KERN_FAILURE; | |
739 | u_int i = 0; | |
740 | KXLDSect *sect = NULL; | |
741 | kxld_addr_t maxaddr = 0; | |
742 | kxld_size_t maxsize = 0; | |
743 | ||
744 | if (seg->sects.nitems) { | |
745 | for (i = 0; i < seg->sects.nitems; ++i) { | |
746 | sect = get_sect_by_index(seg, i); | |
747 | require_action(sect, finish, rval=KERN_FAILURE); | |
748 | if (sect->base_addr > maxaddr) { | |
749 | maxaddr = sect->base_addr; | |
750 | maxsize = sect->size; | |
751 | } | |
752 | } | |
753 | ||
754 | /* XXX Cross architecture linking will fail if the page size ever differs | |
755 | * from 4096. (As of this writing, we're fine on ppc, i386, x86_64, and | |
756 | * arm.) | |
757 | */ | |
758 | seg->vmsize = round_page(maxaddr + maxsize - seg->base_addr); | |
759 | } | |
760 | ||
761 | rval = KERN_SUCCESS; | |
762 | ||
763 | finish: | |
764 | return rval; | |
765 | } | |
766 | ||
767 | /******************************************************************************* | |
768 | *******************************************************************************/ | |
769 | void | |
770 | kxld_seg_set_vm_protections(KXLDSeg *seg, boolean_t strict_protections) | |
771 | { | |
6d2010ae A |
772 | /* This is unnecessary except to make the clang analyzer happy. When |
773 | * the analyzer no longer ignores nonnull attributes for if statements, | |
774 | * we can remove this line. | |
775 | */ | |
776 | if (!seg) return; | |
777 | ||
b0d623f7 | 778 | if (strict_protections) { |
6d2010ae | 779 | if (streq_safe(seg->segname, SEG_TEXT, const_strlen(SEG_TEXT))) { |
b0d623f7 A |
780 | seg->initprot = TEXT_SEG_PROT; |
781 | seg->maxprot = VM_PROT_ALL; | |
782 | } else { | |
783 | seg->initprot = DATA_SEG_PROT; | |
784 | seg->maxprot = DATA_SEG_PROT; | |
785 | } | |
786 | } else { | |
787 | seg->initprot = VM_PROT_ALL; | |
788 | seg->maxprot = VM_PROT_ALL; | |
789 | } | |
790 | } | |
791 | ||
792 | /******************************************************************************* | |
793 | *******************************************************************************/ | |
794 | void | |
795 | kxld_seg_relocate(KXLDSeg *seg, kxld_addr_t link_addr) | |
796 | { | |
797 | KXLDSect *sect = NULL; | |
798 | u_int i = 0; | |
799 | ||
800 | seg->link_addr += link_addr; | |
801 | for (i = 0; i < seg->sects.nitems; ++i) { | |
802 | sect = get_sect_by_index(seg, i); | |
803 | kxld_sect_relocate(sect, link_addr); | |
804 | } | |
805 | } | |
806 | ||
6d2010ae A |
807 | /******************************************************************************* |
808 | *******************************************************************************/ | |
809 | void | |
810 | kxld_seg_populate_linkedit(KXLDSeg *seg, | |
811 | const KXLDSymtab *symtab, boolean_t is_32_bit) | |
812 | { | |
813 | seg->vmsize = round_page(kxld_symtab_get_macho_data_size(symtab, is_32_bit)); | |
814 | } | |
815 |