]> git.saurik.com Git - apple/objc4.git/blob - markgc.cpp
objc4-779.1.tar.gz
[apple/objc4.git] / markgc.cpp
1 /*
2 * Copyright (c) 2007-2009 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_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. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <string.h>
27 #include <stdio.h>
28 #include <stdbool.h>
29 #include <fcntl.h>
30 #include <limits.h>
31 #include <sys/mman.h>
32 #include <sys/stat.h>
33 #include <sys/errno.h>
34 #include <os/overflow.h>
35 #include <mach-o/fat.h>
36 #include <mach-o/arch.h>
37 #include <mach-o/loader.h>
38
39 // Some OS X SDKs don't define these.
40 #ifndef CPU_TYPE_ARM
41 #define CPU_TYPE_ARM ((cpu_type_t) 12)
42 #endif
43 #ifndef CPU_ARCH_ABI64
44 #define CPU_ARCH_ABI64 0x01000000 /* 64 bit ABI */
45 #endif
46 #ifndef CPU_TYPE_ARM64
47 #define CPU_TYPE_ARM64 (CPU_TYPE_ARM | CPU_ARCH_ABI64)
48 #endif
49
50 // File abstraction taken from ld64/FileAbstraction.hpp
51 // and ld64/MachOFileAbstraction.hpp.
52
53 #ifdef __OPTIMIZE__
54 #define INLINE __attribute__((always_inline))
55 #else
56 #define INLINE
57 #endif
58
59 //
60 // This abstraction layer is for use with file formats that have 64-bit/32-bit and Big-Endian/Little-Endian variants
61 //
62 // For example: to make a utility that handles 32-bit little enidan files use: Pointer32<LittleEndian>
63 //
64 //
65 // get16() read a 16-bit number from an E endian struct
66 // set16() write a 16-bit number to an E endian struct
67 // get32() read a 32-bit number from an E endian struct
68 // set32() write a 32-bit number to an E endian struct
69 // get64() read a 64-bit number from an E endian struct
70 // set64() write a 64-bit number to an E endian struct
71 //
72 // getBits() read a bit field from an E endian struct (bitCount=number of bits in field, firstBit=bit index of field)
73 // setBits() write a bit field to an E endian struct (bitCount=number of bits in field, firstBit=bit index of field)
74 //
75 // getBitsRaw() read a bit field from a struct with native endianness
76 // setBitsRaw() write a bit field from a struct with native endianness
77 //
78
79 class BigEndian
80 {
81 public:
82 static uint16_t get16(const uint16_t& from) INLINE { return OSReadBigInt16(&from, 0); }
83 static void set16(uint16_t& into, uint16_t value) INLINE { OSWriteBigInt16(&into, 0, value); }
84
85 static uint32_t get32(const uint32_t& from) INLINE { return OSReadBigInt32(&from, 0); }
86 static void set32(uint32_t& into, uint32_t value) INLINE { OSWriteBigInt32(&into, 0, value); }
87
88 static uint64_t get64(const uint64_t& from) INLINE { return OSReadBigInt64(&from, 0); }
89 static void set64(uint64_t& into, uint64_t value) INLINE { OSWriteBigInt64(&into, 0, value); }
90
91 static uint32_t getBits(const uint32_t& from,
92 uint8_t firstBit, uint8_t bitCount) INLINE { return getBitsRaw(get32(from), firstBit, bitCount); }
93 static void setBits(uint32_t& into, uint32_t value,
94 uint8_t firstBit, uint8_t bitCount) INLINE { uint32_t temp = get32(into); setBitsRaw(temp, value, firstBit, bitCount); set32(into, temp); }
95
96 static uint32_t getBitsRaw(const uint32_t& from,
97 uint8_t firstBit, uint8_t bitCount) INLINE { return ((from >> (32-firstBit-bitCount)) & ((1<<bitCount)-1)); }
98 static void setBitsRaw(uint32_t& into, uint32_t value,
99 uint8_t firstBit, uint8_t bitCount) INLINE { uint32_t temp = into;
100 const uint32_t mask = ((1<<bitCount)-1);
101 temp &= ~(mask << (32-firstBit-bitCount));
102 temp |= ((value & mask) << (32-firstBit-bitCount));
103 into = temp; }
104 enum { little_endian = 0 };
105 };
106
107
108 class LittleEndian
109 {
110 public:
111 static uint16_t get16(const uint16_t& from) INLINE { return OSReadLittleInt16(&from, 0); }
112 static void set16(uint16_t& into, uint16_t value) INLINE { OSWriteLittleInt16(&into, 0, value); }
113
114 static uint32_t get32(const uint32_t& from) INLINE { return OSReadLittleInt32(&from, 0); }
115 static void set32(uint32_t& into, uint32_t value) INLINE { OSWriteLittleInt32(&into, 0, value); }
116
117 static uint64_t get64(const uint64_t& from) INLINE { return OSReadLittleInt64(&from, 0); }
118 static void set64(uint64_t& into, uint64_t value) INLINE { OSWriteLittleInt64(&into, 0, value); }
119
120 static uint32_t getBits(const uint32_t& from,
121 uint8_t firstBit, uint8_t bitCount) INLINE { return getBitsRaw(get32(from), firstBit, bitCount); }
122 static void setBits(uint32_t& into, uint32_t value,
123 uint8_t firstBit, uint8_t bitCount) INLINE { uint32_t temp = get32(into); setBitsRaw(temp, value, firstBit, bitCount); set32(into, temp); }
124
125 static uint32_t getBitsRaw(const uint32_t& from,
126 uint8_t firstBit, uint8_t bitCount) INLINE { return ((from >> firstBit) & ((1<<bitCount)-1)); }
127 static void setBitsRaw(uint32_t& into, uint32_t value,
128 uint8_t firstBit, uint8_t bitCount) INLINE { uint32_t temp = into;
129 const uint32_t mask = ((1<<bitCount)-1);
130 temp &= ~(mask << firstBit);
131 temp |= ((value & mask) << firstBit);
132 into = temp; }
133 enum { little_endian = 1 };
134 };
135
136 #if __BIG_ENDIAN__
137 typedef BigEndian CurrentEndian;
138 typedef LittleEndian OtherEndian;
139 #elif __LITTLE_ENDIAN__
140 typedef LittleEndian CurrentEndian;
141 typedef BigEndian OtherEndian;
142 #else
143 #error unknown endianness
144 #endif
145
146
147 template <typename _E>
148 class Pointer32
149 {
150 public:
151 typedef uint32_t uint_t;
152 typedef int32_t sint_t;
153 typedef _E E;
154
155 static uint64_t getP(const uint_t& from) INLINE { return _E::get32(from); }
156 static void setP(uint_t& into, uint64_t value) INLINE { _E::set32(into, value); }
157 };
158
159
160 template <typename _E>
161 class Pointer64
162 {
163 public:
164 typedef uint64_t uint_t;
165 typedef int64_t sint_t;
166 typedef _E E;
167
168 static uint64_t getP(const uint_t& from) INLINE { return _E::get64(from); }
169 static void setP(uint_t& into, uint64_t value) INLINE { _E::set64(into, value); }
170 };
171
172
173 //
174 // mach-o file header
175 //
176 template <typename P> struct macho_header_content {};
177 template <> struct macho_header_content<Pointer32<BigEndian> > { mach_header fields; };
178 template <> struct macho_header_content<Pointer64<BigEndian> > { mach_header_64 fields; };
179 template <> struct macho_header_content<Pointer32<LittleEndian> > { mach_header fields; };
180 template <> struct macho_header_content<Pointer64<LittleEndian> > { mach_header_64 fields; };
181
182 template <typename P>
183 class macho_header {
184 public:
185 uint32_t magic() const INLINE { return E::get32(header.fields.magic); }
186 void set_magic(uint32_t value) INLINE { E::set32(header.fields.magic, value); }
187
188 uint32_t cputype() const INLINE { return E::get32(header.fields.cputype); }
189 void set_cputype(uint32_t value) INLINE { E::set32((uint32_t&)header.fields.cputype, value); }
190
191 uint32_t cpusubtype() const INLINE { return E::get32(header.fields.cpusubtype); }
192 void set_cpusubtype(uint32_t value) INLINE { E::set32((uint32_t&)header.fields.cpusubtype, value); }
193
194 uint32_t filetype() const INLINE { return E::get32(header.fields.filetype); }
195 void set_filetype(uint32_t value) INLINE { E::set32(header.fields.filetype, value); }
196
197 uint32_t ncmds() const INLINE { return E::get32(header.fields.ncmds); }
198 void set_ncmds(uint32_t value) INLINE { E::set32(header.fields.ncmds, value); }
199
200 uint32_t sizeofcmds() const INLINE { return E::get32(header.fields.sizeofcmds); }
201 void set_sizeofcmds(uint32_t value) INLINE { E::set32(header.fields.sizeofcmds, value); }
202
203 uint32_t flags() const INLINE { return E::get32(header.fields.flags); }
204 void set_flags(uint32_t value) INLINE { E::set32(header.fields.flags, value); }
205
206 uint32_t reserved() const INLINE { return E::get32(header.fields.reserved); }
207 void set_reserved(uint32_t value) INLINE { E::set32(header.fields.reserved, value); }
208
209 typedef typename P::E E;
210 private:
211 macho_header_content<P> header;
212 };
213
214
215 //
216 // mach-o load command
217 //
218 template <typename P>
219 class macho_load_command {
220 public:
221 uint32_t cmd() const INLINE { return E::get32(command.cmd); }
222 void set_cmd(uint32_t value) INLINE { E::set32(command.cmd, value); }
223
224 uint32_t cmdsize() const INLINE { return E::get32(command.cmdsize); }
225 void set_cmdsize(uint32_t value) INLINE { E::set32(command.cmdsize, value); }
226
227 typedef typename P::E E;
228 private:
229 load_command command;
230 };
231
232
233
234
235 //
236 // mach-o segment load command
237 //
238 template <typename P> struct macho_segment_content {};
239 template <> struct macho_segment_content<Pointer32<BigEndian> > { segment_command fields; enum { CMD = LC_SEGMENT }; };
240 template <> struct macho_segment_content<Pointer64<BigEndian> > { segment_command_64 fields; enum { CMD = LC_SEGMENT_64 }; };
241 template <> struct macho_segment_content<Pointer32<LittleEndian> > { segment_command fields; enum { CMD = LC_SEGMENT }; };
242 template <> struct macho_segment_content<Pointer64<LittleEndian> > { segment_command_64 fields; enum { CMD = LC_SEGMENT_64 }; };
243
244 template <typename P>
245 class macho_segment_command {
246 public:
247 uint32_t cmd() const INLINE { return E::get32(segment.fields.cmd); }
248 void set_cmd(uint32_t value) INLINE { E::set32(segment.fields.cmd, value); }
249
250 uint32_t cmdsize() const INLINE { return E::get32(segment.fields.cmdsize); }
251 void set_cmdsize(uint32_t value) INLINE { E::set32(segment.fields.cmdsize, value); }
252
253 const char* segname() const INLINE { return segment.fields.segname; }
254 void set_segname(const char* value) INLINE { strncpy(segment.fields.segname, value, 16); }
255
256 uint64_t vmaddr() const INLINE { return P::getP(segment.fields.vmaddr); }
257 void set_vmaddr(uint64_t value) INLINE { P::setP(segment.fields.vmaddr, value); }
258
259 uint64_t vmsize() const INLINE { return P::getP(segment.fields.vmsize); }
260 void set_vmsize(uint64_t value) INLINE { P::setP(segment.fields.vmsize, value); }
261
262 uint64_t fileoff() const INLINE { return P::getP(segment.fields.fileoff); }
263 void set_fileoff(uint64_t value) INLINE { P::setP(segment.fields.fileoff, value); }
264
265 uint64_t filesize() const INLINE { return P::getP(segment.fields.filesize); }
266 void set_filesize(uint64_t value) INLINE { P::setP(segment.fields.filesize, value); }
267
268 uint32_t maxprot() const INLINE { return E::get32(segment.fields.maxprot); }
269 void set_maxprot(uint32_t value) INLINE { E::set32((uint32_t&)segment.fields.maxprot, value); }
270
271 uint32_t initprot() const INLINE { return E::get32(segment.fields.initprot); }
272 void set_initprot(uint32_t value) INLINE { E::set32((uint32_t&)segment.fields.initprot, value); }
273
274 uint32_t nsects() const INLINE { return E::get32(segment.fields.nsects); }
275 void set_nsects(uint32_t value) INLINE { E::set32(segment.fields.nsects, value); }
276
277 uint32_t flags() const INLINE { return E::get32(segment.fields.flags); }
278 void set_flags(uint32_t value) INLINE { E::set32(segment.fields.flags, value); }
279
280 enum {
281 CMD = macho_segment_content<P>::CMD
282 };
283
284 typedef typename P::E E;
285 private:
286 macho_segment_content<P> segment;
287 };
288
289
290 //
291 // mach-o section
292 //
293 template <typename P> struct macho_section_content {};
294 template <> struct macho_section_content<Pointer32<BigEndian> > { section fields; };
295 template <> struct macho_section_content<Pointer64<BigEndian> > { section_64 fields; };
296 template <> struct macho_section_content<Pointer32<LittleEndian> > { section fields; };
297 template <> struct macho_section_content<Pointer64<LittleEndian> > { section_64 fields; };
298
299 template <typename P>
300 class macho_section {
301 public:
302 const char* sectname() const INLINE { return section.fields.sectname; }
303 void set_sectname(const char* value) INLINE { strncpy(section.fields.sectname, value, 16); }
304
305 const char* segname() const INLINE { return section.fields.segname; }
306 void set_segname(const char* value) INLINE { strncpy(section.fields.segname, value, 16); }
307
308 uint64_t addr() const INLINE { return P::getP(section.fields.addr); }
309 void set_addr(uint64_t value) INLINE { P::setP(section.fields.addr, value); }
310
311 uint64_t size() const INLINE { return P::getP(section.fields.size); }
312 void set_size(uint64_t value) INLINE { P::setP(section.fields.size, value); }
313
314 uint32_t offset() const INLINE { return E::get32(section.fields.offset); }
315 void set_offset(uint32_t value) INLINE { E::set32(section.fields.offset, value); }
316
317 uint32_t align() const INLINE { return E::get32(section.fields.align); }
318 void set_align(uint32_t value) INLINE { E::set32(section.fields.align, value); }
319
320 uint32_t reloff() const INLINE { return E::get32(section.fields.reloff); }
321 void set_reloff(uint32_t value) INLINE { E::set32(section.fields.reloff, value); }
322
323 uint32_t nreloc() const INLINE { return E::get32(section.fields.nreloc); }
324 void set_nreloc(uint32_t value) INLINE { E::set32(section.fields.nreloc, value); }
325
326 uint32_t flags() const INLINE { return E::get32(section.fields.flags); }
327 void set_flags(uint32_t value) INLINE { E::set32(section.fields.flags, value); }
328
329 uint32_t reserved1() const INLINE { return E::get32(section.fields.reserved1); }
330 void set_reserved1(uint32_t value) INLINE { E::set32(section.fields.reserved1, value); }
331
332 uint32_t reserved2() const INLINE { return E::get32(section.fields.reserved2); }
333 void set_reserved2(uint32_t value) INLINE { E::set32(section.fields.reserved2, value); }
334
335 typedef typename P::E E;
336 private:
337 macho_section_content<P> section;
338 };
339
340
341
342
343 static bool debug = true;
344
345 bool processFile(const char *filename);
346
347 int main(int argc, const char *argv[]) {
348 for (int i = 1; i < argc; ++i) {
349 if (!processFile(argv[i])) return 1;
350 }
351 return 0;
352 }
353
354 struct imageinfo {
355 uint32_t version;
356 uint32_t flags;
357 };
358
359
360 // Segment and section names are 16 bytes and may be un-terminated.
361 bool segnameEquals(const char *lhs, const char *rhs)
362 {
363 return 0 == strncmp(lhs, rhs, 16);
364 }
365
366 bool segnameStartsWith(const char *segname, const char *prefix)
367 {
368 return 0 == strncmp(segname, prefix, strlen(prefix));
369 }
370
371 bool sectnameEquals(const char *lhs, const char *rhs)
372 {
373 return segnameEquals(lhs, rhs);
374 }
375
376
377 template <typename P>
378 void dosect(uint8_t *start, macho_section<P> *sect)
379 {
380 if (debug) printf("section %.16s from segment %.16s\n",
381 sect->sectname(), sect->segname());
382
383 // Strip S_MOD_INIT/TERM_FUNC_POINTERS. We don't want dyld to call
384 // our init funcs because it is too late, and we don't want anyone to
385 // call our term funcs ever.
386 if (segnameStartsWith(sect->segname(), "__DATA") &&
387 sectnameEquals(sect->sectname(), "__mod_init_func"))
388 {
389 // section type 0 is S_REGULAR
390 sect->set_flags(sect->flags() & ~SECTION_TYPE);
391 sect->set_sectname("__objc_init_func");
392 if (debug) printf("disabled __mod_init_func section\n");
393 }
394 if (segnameStartsWith(sect->segname(), "__DATA") &&
395 sectnameEquals(sect->sectname(), "__mod_term_func"))
396 {
397 // section type 0 is S_REGULAR
398 sect->set_flags(sect->flags() & ~SECTION_TYPE);
399 sect->set_sectname("__objc_term_func");
400 if (debug) printf("disabled __mod_term_func section\n");
401 }
402 }
403
404 template <typename P>
405 void doseg(uint8_t *start, macho_segment_command<P> *seg)
406 {
407 if (debug) printf("segment name: %.16s, nsects %u\n",
408 seg->segname(), seg->nsects());
409 macho_section<P> *sect = (macho_section<P> *)(seg + 1);
410 for (uint32_t i = 0; i < seg->nsects(); ++i) {
411 dosect(start, &sect[i]);
412 }
413 }
414
415
416 template<typename P>
417 bool parse_macho(uint8_t *buffer)
418 {
419 macho_header<P>* mh = (macho_header<P>*)buffer;
420 uint8_t *cmds = (uint8_t *)(mh + 1);
421 for (uint32_t c = 0; c < mh->ncmds(); c++) {
422 macho_load_command<P>* cmd = (macho_load_command<P>*)cmds;
423 cmds += cmd->cmdsize();
424 if (cmd->cmd() == LC_SEGMENT || cmd->cmd() == LC_SEGMENT_64) {
425 doseg(buffer, (macho_segment_command<P>*)cmd);
426 }
427 }
428
429 return true;
430 }
431
432
433 bool parse_macho(uint8_t *buffer)
434 {
435 uint32_t magic = *(uint32_t *)buffer;
436
437 switch (magic) {
438 case MH_MAGIC_64:
439 return parse_macho<Pointer64<CurrentEndian>>(buffer);
440 case MH_MAGIC:
441 return parse_macho<Pointer32<CurrentEndian>>(buffer);
442 case MH_CIGAM_64:
443 return parse_macho<Pointer64<OtherEndian>>(buffer);
444 case MH_CIGAM:
445 return parse_macho<Pointer32<OtherEndian>>(buffer);
446 default:
447 printf("file is not mach-o (magic %x)\n", magic);
448 return false;
449 }
450 }
451
452
453 bool parse_fat(uint8_t *buffer, size_t size)
454 {
455 uint32_t magic;
456
457 if (size < sizeof(magic)) {
458 printf("file is too small\n");
459 return false;
460 }
461
462 magic = *(uint32_t *)buffer;
463 if (magic != FAT_MAGIC && magic != FAT_CIGAM) {
464 /* Not a fat file */
465 return parse_macho(buffer);
466 } else {
467 struct fat_header *fh;
468 uint32_t fat_magic, fat_nfat_arch;
469 struct fat_arch *archs;
470
471 if (size < sizeof(struct fat_header)) {
472 printf("file is too small\n");
473 return false;
474 }
475
476 fh = (struct fat_header *)buffer;
477 fat_magic = OSSwapBigToHostInt32(fh->magic);
478 fat_nfat_arch = OSSwapBigToHostInt32(fh->nfat_arch);
479
480 size_t fat_arch_size;
481 // fat_nfat_arch * sizeof(struct fat_arch) + sizeof(struct fat_header)
482 if (os_mul_and_add_overflow(fat_nfat_arch, sizeof(struct fat_arch),
483 sizeof(struct fat_header), &fat_arch_size))
484 {
485 printf("too many fat archs\n");
486 return false;
487 }
488 if (size < fat_arch_size) {
489 printf("file is too small\n");
490 return false;
491 }
492
493 archs = (struct fat_arch *)(buffer + sizeof(struct fat_header));
494
495 /* Special case hidden CPU_TYPE_ARM64 */
496 size_t fat_arch_plus_one_size;
497 if (os_add_overflow(fat_arch_size, sizeof(struct fat_arch),
498 &fat_arch_plus_one_size))
499 {
500 printf("too many fat archs\n");
501 return false;
502 }
503 if (size >= fat_arch_plus_one_size) {
504 if (fat_nfat_arch > 0
505 && OSSwapBigToHostInt32(archs[fat_nfat_arch].cputype) == CPU_TYPE_ARM64) {
506 fat_nfat_arch++;
507 }
508 }
509 /* End special case hidden CPU_TYPE_ARM64 */
510
511 if (debug) printf("%d fat architectures\n",
512 fat_nfat_arch);
513
514 for (uint32_t i = 0; i < fat_nfat_arch; i++) {
515 uint32_t arch_cputype = OSSwapBigToHostInt32(archs[i].cputype);
516 uint32_t arch_cpusubtype = OSSwapBigToHostInt32(archs[i].cpusubtype);
517 uint32_t arch_offset = OSSwapBigToHostInt32(archs[i].offset);
518 uint32_t arch_size = OSSwapBigToHostInt32(archs[i].size);
519
520 if (debug) printf("cputype %d cpusubtype %d\n",
521 arch_cputype, arch_cpusubtype);
522
523 /* Check that slice data is after all fat headers and archs */
524 if (arch_offset < fat_arch_size) {
525 printf("file is badly formed\n");
526 return false;
527 }
528
529 /* Check that the slice ends before the file does */
530 if (arch_offset > size) {
531 printf("file is badly formed\n");
532 return false;
533 }
534
535 if (arch_size > size) {
536 printf("file is badly formed\n");
537 return false;
538 }
539
540 if (arch_offset > (size - arch_size)) {
541 printf("file is badly formed\n");
542 return false;
543 }
544
545 bool ok = parse_macho(buffer + arch_offset);
546 if (!ok) return false;
547 }
548 return true;
549 }
550 }
551
552 bool processFile(const char *filename)
553 {
554 if (debug) printf("file %s\n", filename);
555 int fd = open(filename, O_RDWR);
556 if (fd < 0) {
557 printf("open %s: %s\n", filename, strerror(errno));
558 return false;
559 }
560
561 struct stat st;
562 if (fstat(fd, &st) < 0) {
563 printf("fstat %s: %s\n", filename, strerror(errno));
564 return false;
565 }
566
567 void *buffer = mmap(NULL, (size_t)st.st_size, PROT_READ|PROT_WRITE,
568 MAP_FILE|MAP_SHARED, fd, 0);
569 if (buffer == MAP_FAILED) {
570 printf("mmap %s: %s\n", filename, strerror(errno));
571 return false;
572 }
573
574 bool result = parse_fat((uint8_t *)buffer, (size_t)st.st_size);
575 munmap(buffer, (size_t)st.st_size);
576 close(fd);
577 return result;
578 }