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