Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 452 lines (417 sloc) 15.186 kb
9c1d230 committing experimental branch content
Laurent Sansonetti authored
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 != 6) 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 == 18 || mh->cputype == 7);
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 (needsFlip) {
390 arch_ptr->offset = OSSwapInt32(arch_ptr->offset);
391 arch_ptr->size = OSSwapInt32(arch_ptr->size);
392 }
393 doofile(start+arch_ptr->offset, arch_ptr->size, &GCInfo[narchs]);
394 arch_ptr++;
395 }
396 }
397
398 bool openFile(const char *filename) {
399 FileName = filename;
400 // get size
401 struct stat statb;
402 int fd = open(filename, 0);
403 if (fd < 0) {
404 printf("couldn't open %s for reading\n", filename);
405 return false;
406 }
407 int osresult = fstat(fd, &statb);
408 if (osresult != 0) {
409 printf("couldn't get size of %s\n", filename);
410 close(fd);
411 return false;
412 }
413 FileSize = statb.st_size;
414 FileBase = malloc(FileSize);
415 if (!FileBase) {
416 printf("couldn't malloc %d bytes\n", FileSize);
417 close(fd);
418 return false;
419 }
420 off_t readsize = read(fd, FileBase, FileSize);
421 if (readsize != FileSize) {
422 printf("read %d bytes, wanted %d\n", readsize, FileSize);
423 close(fd);
424 return false;
425 }
426 close(fd);
427 return true;
428 }
429
430 void closeFile() {
431 free(FileBase);
432 }
433
434 void dumpinfo(char *filename) {
435 initGCInfo();
436 openFile(filename);
437 struct fat_header *fh = (struct fat_header *)FileBase;
438 if (fh->magic == FAT_MAGIC || fh->magic == FAT_CIGAM) {
439 dofat((void *)FileBase);
440 //printGCInfo(filename);
441 }
442 else if (fh->magic == MH_MAGIC || fh->magic == MH_CIGAM || fh->magic == MH_MAGIC_64 || fh->magic == MH_CIGAM_64) {
443 doofile((void *)FileBase, FileSize, &GCInfo[0]);
444 //printGCInfo(filename);
445 }
446 else if (!quiet) {
447 printf("don't understand %s!\n", filename);
448 }
449 closeFile();
450 }
451
Something went wrong with that request. Please try again.