]> git.saurik.com Git - apple/objc4.git/blob - markgc.c
objc4-437.tar.gz
[apple/objc4.git] / markgc.c
1 /*
2 * Copyright (c) 2007 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 #import <stdio.h>
28 #include <fcntl.h>
29 #import <sys/stat.h>
30 #import <mach-o/fat.h>
31 #import <mach-o/arch.h>
32 #import <mach-o/loader.h>
33
34 typedef char bool;
35 #define true 1
36 #define false 0
37
38 bool debug;
39 bool verbose;
40 bool quiet;
41 bool rrOnly;
42 bool patch = true;
43
44 struct gcinfo {
45 bool hasObjC;
46 bool hasInfo;
47 uint32_t flags;
48 char *arch;
49 } GCInfo[4];
50
51 void dumpinfo(char *filename);
52
53 int Errors = 0;
54 char *FileBase;
55 size_t FileSize;
56 const char *FileName;
57
58 int main(int argc, char *argv[]) {
59 //NSAutoreleasePool *pool = [NSAutoreleasePool new];
60 int i;
61 //dumpinfo("/System/Library/Frameworks/AppKit.framework/AppKit");
62 if (argc == 1) {
63 printf("Usage: gcinfo [-v] [-r] [--] library_or_executable_image [image2 ...]\n");
64 printf(" prints Garbage Collection readiness of named images, ignoring those without ObjC segments\n");
65 printf(" 'GC' - compiled with write-barriers, presumably otherwise aware\n");
66 printf(" 'RR' - retain/release (presumed) aware for non-GC\n");
67 printf(" 'GC-only' - compiled with write-barriers and marked at compile time as not being retain/release savvy\n");
68 printf(" -v - provide archtectural details\n");
69 printf(" -r - only show libraries that are non-GC, e.g. RR only\n");
70 printf(" -- - read files & directories from stdin, e.g. find /Plug-ins | gcinfo --\n");
71 printf("\nAuthor: blaine@apple.com\n");
72 exit(0);
73 }
74 for (i = 1; i < argc; ++i) {
75 if (!strcmp(argv[i], "-v")) {
76 verbose = true;
77 continue;
78 }
79 if (!strcmp(argv[i], "-d")) {
80 debug = true;
81 continue;
82 }
83 if (!strcmp(argv[i], "-q")) {
84 quiet = true;
85 continue;
86 }
87 if (!strcmp(argv[i], "-r")) {
88 quiet = true;
89 rrOnly = true;
90 continue;
91 }
92 if (!strcmp(argv[i], "-p")) {
93 patch = true;
94 continue;
95 }
96 if (!strcmp(argv[i], "--")) {
97 char buf[1024];
98 while (fgets(buf, 1024, stdin)) {
99 int len = strlen(buf);
100 buf[len-1] = 0;
101 dumpinfo(buf);
102 }
103 continue;
104 }
105 dumpinfo(argv[i]);
106 }
107 return Errors;
108 }
109
110 struct imageInfo {
111 uint32_t version;
112 uint32_t flags;
113 };
114
115 void patchFile(uint32_t value, size_t offset) {
116 int fd = open(FileName, 1);
117 off_t lresult = lseek(fd, offset, SEEK_SET);
118 if (lresult == -1) {
119 printf("couldn't seek to %x position on fd %d\n", offset, fd);
120 ++Errors;
121 return;
122 }
123 int wresult = write(fd, &value, 4);
124 if (wresult != 4) {
125 ++Errors;
126 printf("didn't write new value\n");
127 }
128 else {
129 printf("patched %s at offset %p\n", FileName, offset);
130 }
131 close(fd);
132 }
133
134 uint32_t iiflags(struct imageInfo *ii, uint32_t size, bool needsFlip) {
135 if (needsFlip) {
136 ii->flags = OSSwapInt32(ii->flags);
137 }
138 if (debug) printf("flags->%x, nitems %d\n", ii->flags, size/sizeof(struct imageInfo));
139 uint32_t flags = ii->flags;
140 if (patch && (flags&0x2)==0) {
141 //printf("will patch %s at offset %p\n", FileName, (char*)(&ii->flags) - FileBase);
142 uint32_t newvalue = flags | 0x2;
143 if (needsFlip) newvalue = OSSwapInt32(newvalue);
144 patchFile(newvalue, (char*)(&ii->flags) - FileBase);
145 }
146 for(int niis = 1; niis < size/sizeof(struct imageInfo); ++niis) {
147 if (needsFlip) ii[niis].flags = OSSwapInt32(ii[niis].flags);
148 if (ii[niis].flags != flags) {
149 // uh, oh.
150 printf("XXX ii[%d].flags %x != ii[0].flags %x\n", niis, ii[niis].flags, flags);
151 ++Errors;
152 }
153 }
154 return flags;
155 }
156
157 void printflags(uint32_t flags) {
158 if (flags & 0x1) printf(" F&C");
159 if (flags & 0x2) printf(" GC");
160 if (flags & 0x4) printf(" GC-only");
161 else printf(" RR");
162 }
163
164 /*
165 void doimageinfo(struct imageInfo *ii, uint32_t size, bool needsFlip) {
166 uint32_t flags = iiflags(ii, size, needsFlip);
167 printflags(flags);
168 }
169 */
170
171
172 void dosect32(void *start, struct section *sect, bool needsFlip, struct gcinfo *gcip) {
173 if (debug) printf("section %s from segment %s\n", sect->sectname, sect->segname);
174 if (strcmp(sect->segname, "__OBJC")) return;
175 gcip->hasObjC = true;
176 if (strcmp(sect->sectname, "__image_info")) return;
177 gcip->hasInfo = true;
178 if (needsFlip) {
179 sect->offset = OSSwapInt32(sect->offset);
180 sect->size = OSSwapInt32(sect->size);
181 }
182 // these guys aren't inline - they point elsewhere
183 gcip->flags = iiflags(start + sect->offset, sect->size, needsFlip);
184 }
185
186 void dosect64(void *start, struct section_64 *sect, bool needsFlip, struct gcinfo *gcip) {
187 if (debug) printf("section %s from segment %s\n", sect->sectname, sect->segname);
188 if (strcmp(sect->segname, "__OBJC") && strcmp(sect->segname, "__DATA")) return;
189 if (strcmp(sect->sectname, "__image_info") && strncmp(sect->sectname, "__objc_imageinfo", 16)) return;
190 gcip->hasObjC = true;
191 gcip->hasInfo = true;
192 if (needsFlip) {
193 sect->offset = OSSwapInt32(sect->offset);
194 sect->size = OSSwapInt64(sect->size);
195 }
196 // these guys aren't inline - they point elsewhere
197 gcip->flags = iiflags(start + sect->offset, sect->size, needsFlip);
198 }
199
200 void doseg32(void *start, struct segment_command *seg, bool needsFlip, struct gcinfo *gcip) {
201 // lets do sections
202 if (needsFlip) {
203 seg->fileoff = OSSwapInt32(seg->fileoff);
204 seg->nsects = OSSwapInt32(seg->nsects);
205 }
206 if (debug) printf("segment name: %s, nsects %d\n", seg->segname, seg->nsects);
207 if (seg->segname[0]) {
208 if (strcmp("__OBJC", seg->segname)) return;
209 }
210 int nsects;
211 struct section *sect = (struct section *)(seg + 1);
212 for (int nsects = 0; nsects < seg->nsects; ++nsects) {
213 // sections directly follow
214
215 dosect32(start, sect + nsects, needsFlip, gcip);
216 }
217 }
218 void doseg64(void *start, struct segment_command_64 *seg, bool needsFlip, struct gcinfo *gcip) {
219 if (debug) printf("segment name: %s\n", seg->segname);
220 if (seg->segname[0] && strcmp("__OBJC", seg->segname) && strcmp("__DATA", seg->segname)) return;
221 gcip->hasObjC = true;
222 // lets do sections
223 if (needsFlip) {
224 seg->fileoff = OSSwapInt64(seg->fileoff);
225 seg->nsects = OSSwapInt32(seg->nsects);
226 }
227 int nsects;
228 struct section_64 *sect = (struct section_64 *)(seg + 1);
229 for (int nsects = 0; nsects < seg->nsects; ++nsects) {
230 // sections directly follow
231
232 dosect64(start, sect + nsects, needsFlip, gcip);
233 }
234 }
235
236 #if 0
237 /*
238 * A variable length string in a load command is represented by an lc_str
239 * union. The strings are stored just after the load command structure and
240 * the offset is from the start of the load command structure. The size
241 * of the string is reflected in the cmdsize field of the load command.
242 * Once again any padded bytes to bring the cmdsize field to a multiple
243 * of 4 bytes must be zero.
244 */
245 union lc_str {
246 uint32_t offset; /* offset to the string */
247 #ifndef __LP64__
248 char *ptr; /* pointer to the string */
249 #endif
250 };
251
252 struct dylib {
253 union lc_str name; /* library's path name */
254 uint32_t timestamp; /* library's build time stamp */
255 uint32_t current_version; /* library's current version number */
256 uint32_t compatibility_version; /* library's compatibility vers number*/
257 };
258
259 * A dynamically linked shared library (filetype == MH_DYLIB in the mach header)
260 * contains a dylib_command (cmd == LC_ID_DYLIB) to identify the library.
261 * An object that uses a dynamically linked shared library also contains a
262 * dylib_command (cmd == LC_LOAD_DYLIB, LC_LOAD_WEAK_DYLIB, or
263 * LC_REEXPORT_DYLIB) for each library it uses.
264
265 struct dylib_command {
266 uint32_t cmd; /* LC_ID_DYLIB, LC_LOAD_{,WEAK_}DYLIB,
267 LC_REEXPORT_DYLIB */
268 uint32_t cmdsize; /* includes pathname string */
269 struct dylib dylib; /* the library identification */
270 };
271 #endif
272
273 void dodylib(void *start, struct dylib_command *dylibCmd, bool needsFlip) {
274 if (!verbose) return;
275 if (needsFlip) {
276 }
277 int count = dylibCmd->cmdsize - sizeof(struct dylib_command);
278 //printf("offset is %d, count is %d\n", dylibCmd->dylib.name.offset, count);
279 if (dylibCmd->dylib.name.offset > count) return;
280 //printf("-->%.*s<---", count, ((void *)dylibCmd)+dylibCmd->dylib.name.offset);
281 if (verbose) printf("load %s\n", ((void *)dylibCmd)+dylibCmd->dylib.name.offset);
282 }
283
284 struct load_command *doloadcommand(void *start, struct load_command *lc, bool needsFlip, bool is32, struct gcinfo *gcip) {
285 if (needsFlip) {
286 lc->cmd = OSSwapInt32(lc->cmd);
287 lc->cmdsize = OSSwapInt32(lc->cmdsize);
288 }
289
290 switch(lc->cmd) {
291 case LC_SEGMENT_64:
292 if (debug) printf("...segment64\n");
293 if (is32) printf("XXX we have a 64-bit segment in a 32-bit mach-o\n");
294 doseg64(start, (struct segment_command_64 *)lc, needsFlip, gcip);
295 break;
296 case LC_SEGMENT:
297 if (debug) printf("...segment32\n");
298 doseg32(start, (struct segment_command *)lc, needsFlip, gcip);
299 break;
300 case LC_SYMTAB: if (debug) printf("...dynamic symtab\n"); break;
301 case LC_DYSYMTAB: if (debug) printf("...symtab\n"); break;
302 case LC_LOAD_DYLIB:
303 dodylib(start, (struct dylib_command *)lc, needsFlip);
304 break;
305 case LC_SUB_UMBRELLA: if (debug) printf("...load subumbrella\n"); break;
306 default: if (debug) printf("cmd is %x\n", lc->cmd); break;
307 }
308
309 return (struct load_command *)((void *)lc + lc->cmdsize);
310 }
311
312 void doofile(void *start, uint32_t size, struct gcinfo *gcip) {
313 struct mach_header *mh = (struct mach_header *)start;
314 bool isFlipped = false;
315 if (mh->magic == MH_CIGAM || mh->magic == MH_CIGAM_64) {
316 if (debug) printf("(flipping)\n");
317 mh->magic = OSSwapInt32(mh->magic);
318 mh->cputype = OSSwapInt32(mh->cputype);
319 mh->cpusubtype = OSSwapInt32(mh->cpusubtype);
320 mh->filetype = OSSwapInt32(mh->filetype);
321 mh->ncmds = OSSwapInt32(mh->ncmds);
322 mh->sizeofcmds = OSSwapInt32(mh->sizeofcmds);
323 mh->flags = OSSwapInt32(mh->flags);
324 isFlipped = true;
325 }
326 if (rrOnly && mh->filetype != MH_DYLIB) return; // ignore executables
327 NXArchInfo *info = (NXArchInfo *)NXGetArchInfoFromCpuType(mh->cputype, mh->cpusubtype);
328 //printf("%s:", info->description);
329 gcip->arch = (char *)info->description;
330 //if (debug) printf("...description is %s\n", info->description);
331 bool is32 = !(mh->cputype & CPU_ARCH_ABI64);
332 if (debug) printf("is 32? %d\n", is32);
333 if (debug) printf("filetype -> %d\n", mh->filetype);
334 if (debug) printf("ncmds -> %d\n", mh->ncmds);
335 struct load_command *lc = (is32 ? (struct load_command *)(mh + 1) : (struct load_command *)((struct mach_header_64 *)start + 1));
336 int ncmds;
337 for (ncmds = 0; ncmds < mh->ncmds; ++ncmds) {
338 lc = doloadcommand(start, lc, isFlipped, is32, gcip);
339 }
340 //printf("\n");
341 }
342
343 void initGCInfo() {
344 bzero((void *)GCInfo, sizeof(GCInfo));
345 }
346
347 void printGCInfo(char *filename) {
348 if (!GCInfo[0].hasObjC) return; // don't bother
349 // verify that flags are all the same
350 uint32_t flags = GCInfo[0].flags;
351 bool allSame = true;
352 for (int i = 1; i < 4 && GCInfo[i].arch; ++i) {
353 if (flags != GCInfo[i].flags) {
354 allSame = false;
355 }
356 }
357 if (rrOnly) {
358 if (allSame && (flags & 0x2))
359 return;
360 printf("*** not all GC in %s:\n", filename);
361 }
362 if (allSame && !verbose) {
363 printf("%s:", filename);
364 printflags(flags);
365 printf("\n");
366 }
367 else {
368 printf("%s:\n", filename);
369 for (int i = 0; i < 4 && GCInfo[i].arch; ++i) {
370 printf("%s:", GCInfo[i].arch);
371 printflags(GCInfo[i].flags);
372 printf("\n");
373 }
374 printf("\n");
375 }
376 }
377
378 void dofat(void *start) {
379 struct fat_header *fh = start;
380 bool needsFlip = false;
381 if (fh->magic == FAT_CIGAM) {
382 fh->nfat_arch = OSSwapInt32(fh->nfat_arch);
383 needsFlip = true;
384 }
385 if (debug) printf("%d architectures\n", fh->nfat_arch);
386 int narchs;
387 struct fat_arch *arch_ptr = (struct fat_arch *)(fh + 1);
388 for (narchs = 0; narchs < fh->nfat_arch; ++narchs) {
389 if (debug) printf("doing arch %d\n", narchs);
390 if (needsFlip) {
391 arch_ptr->offset = OSSwapInt32(arch_ptr->offset);
392 arch_ptr->size = OSSwapInt32(arch_ptr->size);
393 }
394 doofile(start+arch_ptr->offset, arch_ptr->size, &GCInfo[narchs]);
395 arch_ptr++;
396 }
397 }
398
399 bool openFile(const char *filename) {
400 FileName = filename;
401 // get size
402 struct stat statb;
403 int fd = open(filename, 0);
404 if (fd < 0) {
405 printf("couldn't open %s for reading\n", filename);
406 return false;
407 }
408 int osresult = fstat(fd, &statb);
409 if (osresult != 0) {
410 printf("couldn't get size of %s\n", filename);
411 close(fd);
412 return false;
413 }
414 FileSize = statb.st_size;
415 FileBase = malloc(FileSize);
416 if (!FileBase) {
417 printf("couldn't malloc %d bytes\n", FileSize);
418 close(fd);
419 return false;
420 }
421 off_t readsize = read(fd, FileBase, FileSize);
422 if (readsize != FileSize) {
423 printf("read %d bytes, wanted %d\n", readsize, FileSize);
424 close(fd);
425 return false;
426 }
427 close(fd);
428 return true;
429 }
430
431 void closeFile() {
432 free(FileBase);
433 }
434
435 void dumpinfo(char *filename) {
436 initGCInfo();
437 if (!openFile(filename)) exit(1);
438 struct fat_header *fh = (struct fat_header *)FileBase;
439 if (fh->magic == FAT_MAGIC || fh->magic == FAT_CIGAM) {
440 dofat((void *)FileBase);
441 //printGCInfo(filename);
442 }
443 else if (fh->magic == MH_MAGIC || fh->magic == MH_CIGAM || fh->magic == MH_MAGIC_64 || fh->magic == MH_CIGAM_64) {
444 doofile((void *)FileBase, FileSize, &GCInfo[0]);
445 //printGCInfo(filename);
446 }
447 else if (!quiet) {
448 printf("don't understand %s!\n", filename);
449 }
450 closeFile();
451 }
452