]>
Commit | Line | Data |
---|---|---|
6d2010ae A |
1 | /* |
2 | * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
0a7de745 | 5 | * |
6d2010ae 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. Please obtain a copy of the License at | |
10 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
11 | * file. | |
0a7de745 | 12 | * |
6d2010ae A |
13 | * The Original Code and all software distributed under the License are |
14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
18 | * Please see the License for the specific language governing rights and | |
19 | * limitations under the License. | |
0a7de745 | 20 | * |
6d2010ae A |
21 | * @APPLE_LICENSE_HEADER_END@ |
22 | */ | |
23 | #include <libc.h> | |
24 | #include <errno.h> | |
25 | #include <ctype.h> | |
26 | ||
39236c6e A |
27 | #include <mach/mach_init.h> |
28 | ||
6d2010ae A |
29 | #include <sys/stat.h> |
30 | #include <sys/file.h> | |
31 | #include <sys/mman.h> | |
32 | ||
33 | #include <mach-o/arch.h> | |
34 | #include <mach-o/fat.h> | |
35 | #include <mach-o/loader.h> | |
36 | #include <mach-o/nlist.h> | |
37 | #include <mach-o/swap.h> | |
38 | ||
39 | #include <uuid/uuid.h> | |
39236c6e | 40 | #include <stdbool.h> |
6d2010ae A |
41 | |
42 | #pragma mark Typedefs, Enums, Constants | |
43 | /********************************************************************* | |
44 | * Typedefs, Enums, Constants | |
45 | *********************************************************************/ | |
46 | typedef enum { | |
0a7de745 A |
47 | kErrorNone = 0, |
48 | kError, | |
49 | kErrorFileAccess, | |
50 | kErrorDiskFull, | |
51 | kErrorDuplicate | |
6d2010ae A |
52 | } ToolError; |
53 | ||
54 | #pragma mark Function Protos | |
55 | /********************************************************************* | |
56 | * Function Protos | |
57 | *********************************************************************/ | |
58 | __private_extern__ ToolError | |
59 | readFile(const char *path, vm_offset_t * objAddr, vm_size_t * objSize); | |
60 | ||
61 | __private_extern__ ToolError | |
62 | writeFile(int fd, const void * data, size_t length); | |
63 | ||
39236c6e A |
64 | __private_extern__ ToolError |
65 | seekFile(int fd, off_t offset); | |
66 | ||
0a7de745 A |
67 | extern char* __cxa_demangle(const char* mangled_name, |
68 | char* buf, | |
69 | size_t* n, | |
70 | int* status); | |
6d2010ae A |
71 | |
72 | #pragma mark Functions | |
73 | /********************************************************************* | |
74 | *********************************************************************/ | |
75 | __private_extern__ ToolError | |
76 | writeFile(int fd, const void * data, size_t length) | |
77 | { | |
0a7de745 | 78 | ToolError err; |
6d2010ae | 79 | |
0a7de745 A |
80 | if (length != (size_t)write(fd, data, length)) { |
81 | err = kErrorDiskFull; | |
82 | } else { | |
83 | err = kErrorNone; | |
84 | } | |
6d2010ae | 85 | |
0a7de745 A |
86 | if (kErrorNone != err) { |
87 | perror("couldn't write output"); | |
88 | } | |
6d2010ae | 89 | |
0a7de745 | 90 | return err; |
6d2010ae A |
91 | } |
92 | ||
0a7de745 A |
93 | /********************************************************************* |
94 | *********************************************************************/ | |
39236c6e A |
95 | __private_extern__ ToolError |
96 | seekFile(int fd, off_t offset) | |
97 | { | |
0a7de745 | 98 | ToolError err; |
39236c6e | 99 | |
0a7de745 A |
100 | if (offset != lseek(fd, offset, SEEK_SET)) { |
101 | err = kErrorDiskFull; | |
102 | } else { | |
103 | err = kErrorNone; | |
104 | } | |
39236c6e | 105 | |
0a7de745 A |
106 | if (kErrorNone != err) { |
107 | perror("couldn't write output"); | |
108 | } | |
39236c6e | 109 | |
0a7de745 | 110 | return err; |
39236c6e A |
111 | } |
112 | ||
6d2010ae A |
113 | /********************************************************************* |
114 | *********************************************************************/ | |
115 | __private_extern__ ToolError | |
116 | readFile(const char *path, vm_offset_t * objAddr, vm_size_t * objSize) | |
117 | { | |
0a7de745 A |
118 | ToolError err = kErrorFileAccess; |
119 | int fd; | |
120 | struct stat stat_buf; | |
121 | ||
122 | *objAddr = 0; | |
123 | *objSize = 0; | |
124 | ||
125 | do{ | |
126 | if ((fd = open(path, O_RDONLY)) == -1) { | |
127 | continue; | |
128 | } | |
129 | ||
130 | if (fstat(fd, &stat_buf) == -1) { | |
131 | continue; | |
132 | } | |
133 | ||
134 | if (0 == (stat_buf.st_mode & S_IFREG)) { | |
135 | continue; | |
136 | } | |
6d2010ae | 137 | |
0a7de745 A |
138 | /* Don't try to map an empty file, it fails now due to conformance |
139 | * stuff (PR 4611502). | |
140 | */ | |
141 | if (0 == stat_buf.st_size) { | |
142 | err = kErrorNone; | |
143 | continue; | |
144 | } | |
145 | ||
146 | *objSize = stat_buf.st_size; | |
6d2010ae | 147 | |
0a7de745 A |
148 | *objAddr = (vm_offset_t)mmap(NULL /* address */, *objSize, |
149 | PROT_READ | PROT_WRITE, MAP_FILE | MAP_PRIVATE /* flags */, | |
150 | fd, 0 /* offset */); | |
6d2010ae | 151 | |
0a7de745 A |
152 | if ((void *)*objAddr == MAP_FAILED) { |
153 | *objAddr = 0; | |
154 | *objSize = 0; | |
155 | continue; | |
156 | } | |
6d2010ae | 157 | |
0a7de745 A |
158 | err = kErrorNone; |
159 | } while (false); | |
160 | ||
161 | if (-1 != fd) { | |
162 | close(fd); | |
163 | } | |
164 | if (kErrorNone != err) { | |
165 | fprintf(stderr, "couldn't read %s: %s\n", path, strerror(errno)); | |
166 | } | |
167 | ||
168 | return err; | |
6d2010ae A |
169 | } |
170 | ||
171 | ||
172 | enum { kExported = 0x00000001, kObsolete = 0x00000002 }; | |
173 | ||
174 | struct symbol { | |
0a7de745 A |
175 | char * name; |
176 | unsigned int name_len; | |
177 | char * indirect; | |
178 | unsigned int indirect_len; | |
179 | unsigned int flags; | |
180 | struct symbol * list; | |
181 | unsigned int list_count; | |
6d2010ae A |
182 | }; |
183 | ||
0a7de745 A |
184 | static bool |
185 | issymchar( char c ) | |
6d2010ae | 186 | { |
0a7de745 | 187 | return (c > ' ') && (c <= '~') && (c != ':') && (c != '#'); |
6d2010ae A |
188 | } |
189 | ||
0a7de745 A |
190 | static bool |
191 | iswhitespace( char c ) | |
6d2010ae | 192 | { |
0a7de745 | 193 | return (c == ' ') || (c == '\t'); |
6d2010ae A |
194 | } |
195 | ||
196 | /* | |
197 | * Function for qsort for comparing symbol list names. | |
198 | */ | |
199 | static int | |
200 | qsort_cmp(const void * _left, const void * _right) | |
201 | { | |
0a7de745 A |
202 | struct symbol * left = (struct symbol *) _left; |
203 | struct symbol * right = (struct symbol *) _right; | |
6d2010ae | 204 | |
0a7de745 | 205 | return strcmp(left->name, right->name); |
6d2010ae A |
206 | } |
207 | ||
208 | /* | |
209 | * Function for bsearch for finding a symbol name. | |
210 | */ | |
211 | ||
212 | static int | |
213 | bsearch_cmp( const void * _key, const void * _cmp) | |
214 | { | |
0a7de745 A |
215 | char * key = (char *)_key; |
216 | struct symbol * cmp = (struct symbol *) _cmp; | |
6d2010ae | 217 | |
0a7de745 | 218 | return strcmp(key, cmp->name); |
6d2010ae A |
219 | } |
220 | ||
0a7de745 A |
221 | struct bsearch_key { |
222 | char * name; | |
223 | unsigned int name_len; | |
6d2010ae A |
224 | }; |
225 | ||
226 | static int | |
227 | bsearch_cmp_prefix( const void * _key, const void * _cmp) | |
228 | { | |
0a7de745 A |
229 | struct bsearch_key * key = (struct bsearch_key *)_key; |
230 | struct symbol * cmp = (struct symbol *) _cmp; | |
6d2010ae | 231 | |
0a7de745 | 232 | return strncmp(key->name, cmp->name, key->name_len); |
6d2010ae A |
233 | } |
234 | ||
235 | static uint32_t | |
236 | count_symbols(char * file, vm_size_t file_size) | |
237 | { | |
0a7de745 A |
238 | uint32_t nsyms = 0; |
239 | char * scan; | |
240 | char * eol; | |
241 | char * next; | |
242 | ||
243 | for (scan = file; true; scan = next) { | |
244 | eol = memchr(scan, '\n', file_size - (scan - file)); | |
245 | if (eol == NULL) { | |
246 | break; | |
247 | } | |
248 | next = eol + 1; | |
249 | ||
250 | /* Skip empty lines. | |
251 | */ | |
252 | if (eol == scan) { | |
253 | continue; | |
254 | } | |
255 | ||
256 | /* Skip comment lines. | |
257 | */ | |
258 | if (scan[0] == '#') { | |
259 | continue; | |
260 | } | |
261 | ||
262 | /* Scan past any non-symbol characters at the beginning of the line. */ | |
263 | while ((scan < eol) && !issymchar(*scan)) { | |
264 | scan++; | |
265 | } | |
266 | ||
267 | /* No symbol on line? Move along. | |
268 | */ | |
269 | if (scan == eol) { | |
270 | continue; | |
271 | } | |
272 | ||
273 | /* Skip symbols starting with '.'. | |
274 | */ | |
275 | if (scan[0] == '.') { | |
276 | continue; | |
277 | } | |
278 | nsyms++; | |
279 | } | |
280 | ||
281 | return nsyms; | |
6d2010ae A |
282 | } |
283 | ||
284 | static uint32_t | |
285 | store_symbols(char * file, vm_size_t file_size, struct symbol * symbols, uint32_t idx, uint32_t max_symbols) | |
286 | { | |
0a7de745 A |
287 | char * scan; |
288 | char * line; | |
289 | char * eol; | |
290 | char * next; | |
291 | ||
292 | uint32_t strtabsize; | |
293 | ||
294 | strtabsize = 0; | |
295 | ||
296 | for (scan = file, line = file; true; scan = next, line = next) { | |
297 | char * name = NULL; | |
298 | char * name_term = NULL; | |
299 | unsigned int name_len = 0; | |
300 | char * indirect = NULL; | |
301 | char * indirect_term = NULL; | |
302 | unsigned int indirect_len = 0; | |
303 | char * option = NULL; | |
304 | char * option_term = NULL; | |
305 | unsigned int option_len = 0; | |
306 | char optionstr[256]; | |
307 | boolean_t obsolete = 0; | |
308 | ||
309 | eol = memchr(scan, '\n', file_size - (scan - file)); | |
310 | if (eol == NULL) { | |
311 | break; | |
312 | } | |
313 | next = eol + 1; | |
314 | ||
315 | /* Skip empty lines. | |
316 | */ | |
317 | if (eol == scan) { | |
318 | continue; | |
319 | } | |
320 | ||
321 | *eol = '\0'; | |
322 | ||
323 | /* Skip comment lines. | |
324 | */ | |
325 | if (scan[0] == '#') { | |
326 | continue; | |
327 | } | |
328 | ||
329 | /* Scan past any non-symbol characters at the beginning of the line. */ | |
330 | while ((scan < eol) && !issymchar(*scan)) { | |
331 | scan++; | |
332 | } | |
333 | ||
334 | /* No symbol on line? Move along. | |
335 | */ | |
336 | if (scan == eol) { | |
337 | continue; | |
338 | } | |
339 | ||
340 | /* Skip symbols starting with '.'. | |
341 | */ | |
342 | if (scan[0] == '.') { | |
343 | continue; | |
344 | } | |
345 | ||
346 | name = scan; | |
347 | ||
348 | /* Find the end of the symbol. | |
349 | */ | |
350 | while ((*scan != '\0') && issymchar(*scan)) { | |
351 | scan++; | |
352 | } | |
353 | ||
354 | /* Note char past end of symbol. | |
355 | */ | |
356 | name_term = scan; | |
357 | ||
358 | /* Stored length must include the terminating nul char. | |
359 | */ | |
360 | name_len = name_term - name + 1; | |
361 | ||
362 | /* Now look for an indirect. | |
363 | */ | |
364 | if (*scan != '\0') { | |
365 | while ((*scan != '\0') && iswhitespace(*scan)) { | |
366 | scan++; | |
367 | } | |
368 | if (*scan == ':') { | |
369 | scan++; | |
370 | while ((*scan != '\0') && iswhitespace(*scan)) { | |
371 | scan++; | |
372 | } | |
373 | if (issymchar(*scan)) { | |
374 | indirect = scan; | |
375 | ||
376 | /* Find the end of the symbol. | |
377 | */ | |
378 | while ((*scan != '\0') && issymchar(*scan)) { | |
379 | scan++; | |
380 | } | |
381 | ||
382 | /* Note char past end of symbol. | |
383 | */ | |
384 | indirect_term = scan; | |
385 | ||
386 | /* Stored length must include the terminating nul char. | |
387 | */ | |
388 | indirect_len = indirect_term - indirect + 1; | |
389 | } else if (*scan == '\0') { | |
390 | fprintf(stderr, "bad format in symbol line: %s\n", line); | |
391 | exit(1); | |
392 | } | |
393 | } else if (*scan != '\0' && *scan != '-') { | |
394 | fprintf(stderr, "bad format in symbol line: %s\n", line); | |
395 | exit(1); | |
396 | } | |
397 | } | |
398 | ||
399 | /* Look for options. | |
400 | */ | |
401 | if (*scan != '\0') { | |
402 | while ((*scan != '\0') && iswhitespace(*scan)) { | |
403 | scan++; | |
404 | } | |
405 | ||
406 | if (*scan == '-') { | |
407 | scan++; | |
408 | ||
409 | if (isalpha(*scan)) { | |
410 | option = scan; | |
411 | ||
412 | /* Find the end of the option. | |
413 | */ | |
414 | while ((*scan != '\0') && isalpha(*scan)) { | |
415 | scan++; | |
416 | } | |
417 | ||
418 | /* Note char past end of option. | |
419 | */ | |
420 | option_term = scan; | |
421 | option_len = option_term - option; | |
422 | ||
423 | if (option_len >= sizeof(optionstr)) { | |
424 | fprintf(stderr, "option too long in symbol line: %s\n", line); | |
425 | exit(1); | |
426 | } | |
427 | memcpy(optionstr, option, option_len); | |
428 | optionstr[option_len] = '\0'; | |
429 | ||
430 | /* Find the option. | |
431 | */ | |
432 | if (!strncmp(optionstr, "obsolete", option_len)) { | |
433 | obsolete = TRUE; | |
434 | } | |
435 | } else if (*scan == '\0') { | |
436 | fprintf(stderr, "bad format in symbol line: %s\n", line); | |
437 | exit(1); | |
438 | } | |
439 | } | |
440 | } | |
441 | ||
442 | if (idx >= max_symbols) { | |
443 | fprintf(stderr, "symbol[%d/%d] overflow: %s\n", idx, max_symbols, line); | |
444 | exit(1); | |
445 | } | |
446 | ||
447 | *name_term = '\0'; | |
448 | if (indirect_term) { | |
449 | *indirect_term = '\0'; | |
450 | } | |
451 | ||
452 | symbols[idx].name = name; | |
453 | symbols[idx].name_len = name_len; | |
454 | symbols[idx].indirect = indirect; | |
455 | symbols[idx].indirect_len = indirect_len; | |
456 | symbols[idx].flags = (obsolete) ? kObsolete : 0; | |
457 | ||
458 | strtabsize += symbols[idx].name_len + symbols[idx].indirect_len; | |
459 | idx++; | |
460 | } | |
461 | ||
462 | return strtabsize; | |
6d2010ae A |
463 | } |
464 | ||
3e170ce0 A |
465 | static const NXArchInfo * |
466 | lookup_arch(const char *archstring) | |
467 | { | |
468 | /* | |
469 | * As new architectures are supported by xnu, add a mapping function | |
470 | * without relying on host libraries. | |
471 | */ | |
472 | static const NXArchInfo archlist[] = { | |
473 | { "x86_64", 0x01000007 /* CPU_TYPE_X86_64 */, 3 /* CPU_SUBTYPE_X86_64_ALL */, NX_LittleEndian, NULL }, | |
474 | { "x86_64h", 0x01000007 /* CPU_TYPE_X86_64 */, 8 /* CPU_SUBTYPE_X86_64_H */, NX_LittleEndian, NULL }, | |
d9a64523 A |
475 | { "armv7", 12 /* CPU_TYPE_ARM */, 9 /* CPU_SUBTYPE_ARM_V7 */, NX_LittleEndian, NULL }, |
476 | { "armv7s", 12 /* CPU_TYPE_ARM */, 11 /* CPU_SUBTYPE_ARM_V7S */, NX_LittleEndian, NULL }, | |
477 | { "armv7k", 12 /* CPU_TYPE_ARM */, 12 /* CPU_SUBTYPE_ARM_V7K */, NX_LittleEndian, NULL }, | |
478 | { "arm64", 0x0100000c /* CPU_TYPE_ARM64 */, 0 /* CPU_SUBTYPE_ARM64_ALL */, NX_LittleEndian, NULL }, | |
2a1bd2d3 | 479 | { "arm64e", 0x0100000c /* CPU_TYPE_ARM64 */, 2 /* CPU_SUBTYPE_ARM64_E */, NX_LittleEndian, NULL }, |
3e170ce0 A |
480 | }; |
481 | unsigned long i; | |
482 | ||
0a7de745 | 483 | for (i = 0; i < sizeof(archlist) / sizeof(archlist[0]); i++) { |
3e170ce0 A |
484 | if (0 == strcmp(archstring, archlist[i].name)) { |
485 | return &archlist[i]; | |
486 | } | |
487 | } | |
488 | ||
489 | return NULL; | |
490 | } | |
491 | ||
6d2010ae A |
492 | /********************************************************************* |
493 | *********************************************************************/ | |
0a7de745 A |
494 | int |
495 | main(int argc, char * argv[]) | |
6d2010ae | 496 | { |
0a7de745 A |
497 | ToolError err; |
498 | int i, fd; | |
499 | const char * output_name = NULL; | |
500 | uint32_t zero = 0, num_files = 0; | |
501 | uint32_t filenum; | |
502 | uint32_t strx, strtabsize, strtabpad; | |
503 | struct symbol * import_symbols; | |
504 | struct symbol * export_symbols; | |
505 | uint32_t num_import_syms, num_export_syms; | |
506 | uint32_t result_count, num_removed_syms; | |
507 | uint32_t import_idx, export_idx; | |
508 | const NXArchInfo * host_arch; | |
509 | const NXArchInfo * target_arch; | |
510 | boolean_t require_imports = true; | |
511 | boolean_t diff = false; | |
512 | ||
513 | ||
514 | struct file { | |
515 | vm_offset_t mapped; | |
516 | vm_size_t mapped_size; | |
517 | uint32_t nsyms; | |
518 | boolean_t import; | |
519 | const char * path; | |
520 | }; | |
521 | struct file files[64]; | |
522 | ||
523 | host_arch = NXGetLocalArchInfo(); | |
524 | target_arch = host_arch; | |
525 | ||
526 | for (i = 1; i < argc; i += 2) { | |
527 | boolean_t import; | |
528 | ||
529 | if (!strcmp("-sect", argv[i])) { | |
530 | require_imports = false; | |
531 | i--; | |
532 | continue; | |
533 | } | |
534 | if (!strcmp("-diff", argv[i])) { | |
535 | require_imports = false; | |
536 | diff = true; | |
537 | i--; | |
538 | continue; | |
539 | } | |
540 | ||
541 | if (i == (argc - 1)) { | |
542 | fprintf(stderr, "bad arguments: %s\n", argv[i]); | |
543 | exit(1); | |
544 | } | |
545 | ||
546 | if (!strcmp("-arch", argv[i])) { | |
547 | target_arch = lookup_arch(argv[i + 1]); | |
548 | if (!target_arch) { | |
549 | fprintf(stderr, "unknown architecture name: %s\n", argv[i + 1]); | |
550 | exit(1); | |
551 | } | |
552 | continue; | |
553 | } | |
554 | if (!strcmp("-output", argv[i])) { | |
555 | output_name = argv[i + 1]; | |
556 | continue; | |
557 | } | |
558 | ||
559 | if (!strcmp("-import", argv[i])) { | |
560 | import = true; | |
561 | } else if (!strcmp("-export", argv[i])) { | |
562 | import = false; | |
563 | } else { | |
564 | fprintf(stderr, "unknown option: %s\n", argv[i]); | |
565 | exit(1); | |
566 | } | |
567 | ||
568 | err = readFile(argv[i + 1], &files[num_files].mapped, &files[num_files].mapped_size); | |
569 | if (kErrorNone != err) { | |
570 | exit(1); | |
571 | } | |
572 | ||
573 | if (files[num_files].mapped && files[num_files].mapped_size) { | |
574 | files[num_files].import = import; | |
575 | files[num_files].path = argv[i + 1]; | |
576 | num_files++; | |
577 | } | |
6d2010ae A |
578 | } |
579 | ||
0a7de745 A |
580 | if (!output_name) { |
581 | fprintf(stderr, "no output file\n"); | |
6d2010ae | 582 | exit(1); |
6d2010ae A |
583 | } |
584 | ||
0a7de745 A |
585 | num_import_syms = 0; |
586 | num_export_syms = 0; | |
587 | for (filenum = 0; filenum < num_files; filenum++) { | |
588 | files[filenum].nsyms = count_symbols((char *) files[filenum].mapped, files[filenum].mapped_size); | |
589 | if (files[filenum].import) { | |
590 | num_import_syms += files[filenum].nsyms; | |
591 | } else { | |
592 | num_export_syms += files[filenum].nsyms; | |
593 | } | |
594 | } | |
6d2010ae | 595 | |
0a7de745 A |
596 | import_symbols = calloc(num_import_syms, sizeof(struct symbol)); |
597 | export_symbols = calloc(num_export_syms, sizeof(struct symbol)); | |
598 | ||
599 | import_idx = 0; | |
600 | export_idx = 0; | |
601 | ||
602 | for (filenum = 0; filenum < num_files; filenum++) { | |
603 | if (files[filenum].import) { | |
604 | store_symbols((char *) files[filenum].mapped, files[filenum].mapped_size, | |
605 | import_symbols, import_idx, num_import_syms); | |
606 | import_idx += files[filenum].nsyms; | |
607 | } else { | |
608 | store_symbols((char *) files[filenum].mapped, files[filenum].mapped_size, | |
609 | export_symbols, export_idx, num_export_syms); | |
610 | export_idx += files[filenum].nsyms; | |
611 | } | |
612 | if (false && !files[filenum].nsyms) { | |
613 | fprintf(stderr, "warning: file %s contains no names\n", files[filenum].path); | |
614 | } | |
6d2010ae | 615 | } |
0a7de745 A |
616 | |
617 | ||
618 | qsort(import_symbols, num_import_syms, sizeof(struct symbol), &qsort_cmp); | |
619 | qsort(export_symbols, num_export_syms, sizeof(struct symbol), &qsort_cmp); | |
620 | ||
621 | result_count = 0; | |
622 | num_removed_syms = 0; | |
623 | strtabsize = 4; | |
624 | if (num_import_syms) { | |
625 | for (export_idx = 0; export_idx < num_export_syms; export_idx++) { | |
626 | struct symbol * result; | |
627 | char * name; | |
628 | size_t len; | |
629 | boolean_t wild; | |
630 | ||
631 | name = export_symbols[export_idx].indirect; | |
632 | len = export_symbols[export_idx].indirect_len; | |
633 | if (!name) { | |
634 | name = export_symbols[export_idx].name; | |
635 | len = export_symbols[export_idx].name_len; | |
636 | } | |
637 | wild = ((len > 2) && ('*' == name[len -= 2])); | |
638 | if (wild) { | |
639 | struct bsearch_key key; | |
640 | key.name = name; | |
641 | key.name_len = len; | |
642 | result = bsearch(&key, import_symbols, | |
643 | num_import_syms, sizeof(struct symbol), &bsearch_cmp_prefix); | |
644 | ||
645 | if (result) { | |
646 | struct symbol * first; | |
647 | struct symbol * last; | |
648 | ||
649 | strtabsize += (result->name_len + result->indirect_len); | |
650 | ||
651 | first = result; | |
652 | while (--first >= &import_symbols[0]) { | |
653 | if (bsearch_cmp_prefix(&key, first)) { | |
654 | break; | |
655 | } | |
656 | strtabsize += (first->name_len + first->indirect_len); | |
657 | } | |
658 | first++; | |
659 | ||
660 | last = result; | |
661 | while (++last < (&import_symbols[0] + num_import_syms)) { | |
662 | if (bsearch_cmp_prefix(&key, last)) { | |
663 | break; | |
664 | } | |
665 | strtabsize += (last->name_len + last->indirect_len); | |
666 | } | |
667 | result_count += last - first; | |
668 | result = first; | |
669 | export_symbols[export_idx].list = first; | |
670 | export_symbols[export_idx].list_count = last - first; | |
671 | export_symbols[export_idx].flags |= kExported; | |
672 | } | |
673 | } else { | |
674 | result = bsearch(name, import_symbols, | |
675 | num_import_syms, sizeof(struct symbol), &bsearch_cmp); | |
676 | } | |
677 | ||
678 | if (!result && require_imports) { | |
679 | int status; | |
680 | char * demangled_result = | |
681 | __cxa_demangle(export_symbols[export_idx].name + 1, NULL, NULL, &status); | |
682 | fprintf(stderr, "exported name not in import list: %s\n", | |
683 | demangled_result ? demangled_result : export_symbols[export_idx].name); | |
684 | // fprintf(stderr, " : %s\n", export_symbols[export_idx].name); | |
685 | if (demangled_result) { | |
686 | free(demangled_result); | |
687 | } | |
688 | num_removed_syms++; | |
689 | } | |
690 | if (diff) { | |
691 | if (!result) { | |
692 | result = &export_symbols[export_idx]; | |
693 | } else { | |
694 | result = NULL; | |
695 | } | |
696 | } | |
697 | if (result && !wild) { | |
698 | export_symbols[export_idx].flags |= kExported; | |
699 | strtabsize += (export_symbols[export_idx].name_len + export_symbols[export_idx].indirect_len); | |
700 | result_count++; | |
701 | export_symbols[export_idx].list = &export_symbols[export_idx]; | |
702 | export_symbols[export_idx].list_count = 1; | |
703 | } | |
704 | } | |
6d2010ae | 705 | } |
0a7de745 A |
706 | strtabpad = (strtabsize + 3) & ~3; |
707 | ||
708 | if (require_imports && num_removed_syms) { | |
709 | err = kError; | |
710 | goto finish; | |
6d2010ae | 711 | } |
0a7de745 A |
712 | |
713 | fd = open(output_name, O_WRONLY | O_CREAT | O_TRUNC, 0755); | |
714 | if (-1 == fd) { | |
715 | perror("couldn't write output"); | |
716 | err = kErrorFileAccess; | |
717 | goto finish; | |
6d2010ae | 718 | } |
6d2010ae | 719 | |
0a7de745 A |
720 | struct symtab_command symcmd; |
721 | struct uuid_command uuidcmd; | |
722 | off_t symsoffset; | |
723 | ||
724 | symcmd.cmd = LC_SYMTAB; | |
725 | symcmd.cmdsize = sizeof(symcmd); | |
726 | symcmd.nsyms = result_count; | |
727 | symcmd.strsize = strtabpad; | |
728 | ||
729 | uuidcmd.cmd = LC_UUID; | |
730 | uuidcmd.cmdsize = sizeof(uuidcmd); | |
731 | uuid_generate(uuidcmd.uuid); | |
732 | ||
733 | if (CPU_ARCH_ABI64 & target_arch->cputype) { | |
734 | struct mach_header_64 hdr; | |
735 | struct segment_command_64 segcmd; | |
736 | ||
737 | hdr.magic = MH_MAGIC_64; | |
738 | hdr.cputype = target_arch->cputype; | |
739 | hdr.cpusubtype = target_arch->cpusubtype; | |
740 | hdr.filetype = MH_KEXT_BUNDLE; | |
741 | hdr.ncmds = 3; | |
742 | hdr.sizeofcmds = sizeof(segcmd) + sizeof(symcmd) + sizeof(uuidcmd); | |
743 | hdr.flags = MH_INCRLINK; | |
744 | symsoffset = mach_vm_round_page(hdr.sizeofcmds); | |
745 | ||
746 | segcmd.cmd = LC_SEGMENT_64; | |
747 | segcmd.cmdsize = sizeof(segcmd); | |
748 | strncpy(segcmd.segname, SEG_LINKEDIT, sizeof(segcmd.segname)); | |
749 | segcmd.vmaddr = 0; | |
750 | segcmd.vmsize = result_count * sizeof(struct nlist_64) + strtabpad; | |
751 | segcmd.fileoff = symsoffset; | |
752 | segcmd.filesize = segcmd.vmsize; | |
753 | segcmd.maxprot = PROT_READ; | |
754 | segcmd.initprot = PROT_READ; | |
755 | segcmd.nsects = 0; | |
756 | segcmd.flags = SG_NORELOC; | |
757 | ||
758 | symcmd.symoff = symsoffset; | |
759 | symcmd.stroff = result_count * sizeof(struct nlist_64) | |
760 | + symcmd.symoff; | |
761 | ||
762 | if (target_arch->byteorder != host_arch->byteorder) { | |
763 | swap_mach_header_64(&hdr, target_arch->byteorder); | |
764 | swap_segment_command_64(&segcmd, target_arch->byteorder); | |
765 | } | |
766 | err = writeFile(fd, &hdr, sizeof(hdr)); | |
767 | if (kErrorNone != err) { | |
768 | goto finish; | |
769 | } | |
770 | err = writeFile(fd, &segcmd, sizeof(segcmd)); | |
771 | } else { | |
772 | struct mach_header hdr; | |
773 | struct segment_command segcmd; | |
774 | ||
775 | hdr.magic = MH_MAGIC; | |
776 | hdr.cputype = target_arch->cputype; | |
777 | hdr.cpusubtype = target_arch->cpusubtype; | |
778 | hdr.filetype = MH_KEXT_BUNDLE; | |
779 | hdr.ncmds = 3; | |
780 | hdr.sizeofcmds = sizeof(segcmd) + sizeof(symcmd) + sizeof(uuidcmd); | |
781 | hdr.flags = MH_INCRLINK; | |
782 | symsoffset = mach_vm_round_page(hdr.sizeofcmds); | |
783 | ||
784 | segcmd.cmd = LC_SEGMENT; | |
785 | segcmd.cmdsize = sizeof(segcmd); | |
786 | strncpy(segcmd.segname, SEG_LINKEDIT, sizeof(segcmd.segname)); | |
787 | segcmd.vmaddr = 0; | |
788 | segcmd.vmsize = result_count * sizeof(struct nlist) + strtabpad; | |
789 | segcmd.fileoff = symsoffset; | |
790 | segcmd.filesize = segcmd.vmsize; | |
791 | segcmd.maxprot = PROT_READ; | |
792 | segcmd.initprot = PROT_READ; | |
793 | segcmd.nsects = 0; | |
794 | segcmd.flags = SG_NORELOC; | |
795 | ||
796 | symcmd.symoff = symsoffset; | |
797 | symcmd.stroff = result_count * sizeof(struct nlist) | |
798 | + symcmd.symoff; | |
799 | ||
800 | if (target_arch->byteorder != host_arch->byteorder) { | |
801 | swap_mach_header(&hdr, target_arch->byteorder); | |
802 | swap_segment_command(&segcmd, target_arch->byteorder); | |
803 | } | |
804 | err = writeFile(fd, &hdr, sizeof(hdr)); | |
805 | if (kErrorNone != err) { | |
806 | goto finish; | |
807 | } | |
808 | err = writeFile(fd, &segcmd, sizeof(segcmd)); | |
809 | } | |
6d2010ae | 810 | |
0a7de745 A |
811 | if (kErrorNone != err) { |
812 | goto finish; | |
6d2010ae | 813 | } |
0a7de745 A |
814 | |
815 | if (target_arch->byteorder != host_arch->byteorder) { | |
816 | swap_symtab_command(&symcmd, target_arch->byteorder); | |
817 | swap_uuid_command(&uuidcmd, target_arch->byteorder); | |
39236c6e | 818 | } |
0a7de745 A |
819 | err = writeFile(fd, &symcmd, sizeof(symcmd)); |
820 | if (kErrorNone != err) { | |
821 | goto finish; | |
39236c6e | 822 | } |
0a7de745 A |
823 | err = writeFile(fd, &uuidcmd, sizeof(uuidcmd)); |
824 | if (kErrorNone != err) { | |
825 | goto finish; | |
6d2010ae A |
826 | } |
827 | ||
0a7de745 A |
828 | err = seekFile(fd, symsoffset); |
829 | if (kErrorNone != err) { | |
830 | goto finish; | |
6d2010ae A |
831 | } |
832 | ||
0a7de745 A |
833 | strx = 4; |
834 | for (export_idx = 0; export_idx < num_export_syms; export_idx++) { | |
835 | if (!export_symbols[export_idx].name) { | |
836 | continue; | |
837 | } | |
838 | if (!(kExported & export_symbols[export_idx].flags)) { | |
839 | continue; | |
840 | } | |
841 | ||
842 | if (export_idx | |
843 | && export_symbols[export_idx - 1].name | |
844 | && !strcmp(export_symbols[export_idx - 1].name, export_symbols[export_idx].name)) { | |
845 | fprintf(stderr, "duplicate export: %s\n", export_symbols[export_idx - 1].name); | |
846 | err = kErrorDuplicate; | |
847 | goto finish; | |
848 | } | |
849 | ||
850 | for (import_idx = 0; import_idx < export_symbols[export_idx].list_count; import_idx++) { | |
851 | if (export_symbols[export_idx].list != &export_symbols[export_idx]) { | |
852 | printf("wild: %s, %s\n", export_symbols[export_idx].name, | |
853 | export_symbols[export_idx].list[import_idx].name); | |
854 | } | |
855 | if (CPU_ARCH_ABI64 & target_arch->cputype) { | |
856 | struct nlist_64 nl; | |
857 | ||
858 | nl.n_sect = 0; | |
859 | nl.n_desc = 0; | |
860 | nl.n_un.n_strx = strx; | |
861 | strx += export_symbols[export_idx].list[import_idx].name_len; | |
862 | ||
863 | if (export_symbols[export_idx].flags & kObsolete) { | |
864 | nl.n_desc |= N_DESC_DISCARDED; | |
865 | } | |
866 | ||
867 | if (export_symbols[export_idx].list[import_idx].indirect) { | |
868 | nl.n_type = N_INDR | N_EXT; | |
869 | nl.n_value = strx; | |
870 | strx += export_symbols[export_idx].list[import_idx].indirect_len; | |
871 | } else { | |
872 | nl.n_type = N_UNDF | N_EXT; | |
873 | nl.n_value = 0; | |
874 | } | |
875 | ||
876 | if (target_arch->byteorder != host_arch->byteorder) { | |
877 | swap_nlist_64(&nl, 1, target_arch->byteorder); | |
878 | } | |
879 | ||
880 | err = writeFile(fd, &nl, sizeof(nl)); | |
881 | } else { | |
882 | struct nlist nl; | |
883 | ||
884 | nl.n_sect = 0; | |
885 | nl.n_desc = 0; | |
886 | nl.n_un.n_strx = strx; | |
887 | strx += export_symbols[export_idx].list[import_idx].name_len; | |
888 | ||
889 | if (export_symbols[export_idx].flags & kObsolete) { | |
890 | nl.n_desc |= N_DESC_DISCARDED; | |
891 | } | |
892 | ||
893 | if (export_symbols[export_idx].list[import_idx].indirect) { | |
894 | nl.n_type = N_INDR | N_EXT; | |
895 | nl.n_value = strx; | |
896 | strx += export_symbols[export_idx].list[import_idx].indirect_len; | |
897 | } else { | |
898 | nl.n_type = N_UNDF | N_EXT; | |
899 | nl.n_value = 0; | |
900 | } | |
901 | ||
902 | if (target_arch->byteorder != host_arch->byteorder) { | |
903 | swap_nlist(&nl, 1, target_arch->byteorder); | |
904 | } | |
905 | ||
906 | err = writeFile(fd, &nl, sizeof(nl)); | |
907 | } | |
908 | } | |
909 | ||
910 | if (kErrorNone != err) { | |
911 | goto finish; | |
912 | } | |
913 | } | |
914 | ||
915 | strx = sizeof(uint32_t); | |
916 | err = writeFile(fd, &zero, strx); | |
917 | if (kErrorNone != err) { | |
918 | goto finish; | |
919 | } | |
920 | ||
921 | for (export_idx = 0; export_idx < num_export_syms; export_idx++) { | |
922 | if (!export_symbols[export_idx].name) { | |
923 | continue; | |
924 | } | |
925 | ||
926 | for (import_idx = 0; import_idx < export_symbols[export_idx].list_count; import_idx++) { | |
927 | err = writeFile(fd, export_symbols[export_idx].list[import_idx].name, | |
928 | export_symbols[export_idx].list[import_idx].name_len); | |
929 | if (kErrorNone != err) { | |
930 | goto finish; | |
931 | } | |
932 | if (export_symbols[export_idx].list[import_idx].indirect) { | |
933 | err = writeFile(fd, export_symbols[export_idx].list[import_idx].indirect, | |
934 | export_symbols[export_idx].list[import_idx].indirect_len); | |
935 | if (kErrorNone != err) { | |
936 | goto finish; | |
937 | } | |
938 | } | |
939 | } | |
940 | } | |
941 | ||
942 | err = writeFile(fd, &zero, strtabpad - strtabsize); | |
943 | if (kErrorNone != err) { | |
6d2010ae | 944 | goto finish; |
6d2010ae | 945 | } |
6d2010ae | 946 | |
0a7de745 | 947 | close(fd); |
6d2010ae A |
948 | |
949 | ||
950 | finish: | |
0a7de745 A |
951 | for (filenum = 0; filenum < num_files; filenum++) { |
952 | // unmap file | |
953 | if (files[filenum].mapped_size) { | |
954 | munmap((caddr_t)files[filenum].mapped, files[filenum].mapped_size); | |
955 | files[filenum].mapped = 0; | |
956 | files[filenum].mapped_size = 0; | |
957 | } | |
958 | } | |
6d2010ae | 959 | |
0a7de745 A |
960 | if (kErrorNone != err) { |
961 | if (output_name && strncmp(output_name, "/dev/", 5)) { | |
962 | unlink(output_name); | |
963 | } | |
964 | exit(1); | |
965 | } else { | |
966 | exit(0); | |
967 | } | |
968 | return 0; | |
969 | } |