Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

452 lines (417 sloc) 15.192 kB
/*
* Copyright (c) 2007-2011, Apple Inc. All Rights Reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#import <stdio.h>
#include <fcntl.h>
#import <sys/stat.h>
#import <mach-o/fat.h>
#import <mach-o/arch.h>
#import <mach-o/loader.h>
typedef char bool;
#define true 1
#define false 0
bool debug;
bool verbose;
bool quiet;
bool rrOnly;
bool patch = true;
struct gcinfo {
bool hasObjC;
bool hasInfo;
uint32_t flags;
char *arch;
} GCInfo[4];
void dumpinfo(char *filename);
int Errors = 0;
char *FileBase;
size_t FileSize;
const char *FileName;
int main(int argc, char *argv[]) {
//NSAutoreleasePool *pool = [NSAutoreleasePool new];
int i;
//dumpinfo("/System/Library/Frameworks/AppKit.framework/AppKit");
if (argc == 1) {
printf("Usage: gcinfo [-v] [-r] [--] library_or_executable_image [image2 ...]\n");
printf(" prints Garbage Collection readiness of named images, ignoring those without ObjC segments\n");
printf(" 'GC' - compiled with write-barriers, presumably otherwise aware\n");
printf(" 'RR' - retain/release (presumed) aware for non-GC\n");
printf(" 'GC-only' - compiled with write-barriers and marked at compile time as not being retain/release savvy\n");
printf(" -v - provide archtectural details\n");
printf(" -r - only show libraries that are non-GC, e.g. RR only\n");
printf(" -- - read files & directories from stdin, e.g. find /Plug-ins | gcinfo --\n");
printf("\nAuthor: blaine@apple.com\n");
exit(0);
}
for (i = 1; i < argc; ++i) {
if (!strcmp(argv[i], "-v")) {
verbose = true;
continue;
}
if (!strcmp(argv[i], "-d")) {
debug = true;
continue;
}
if (!strcmp(argv[i], "-q")) {
quiet = true;
continue;
}
if (!strcmp(argv[i], "-r")) {
quiet = true;
rrOnly = true;
continue;
}
if (!strcmp(argv[i], "-p")) {
patch = true;
continue;
}
if (!strcmp(argv[i], "--")) {
char buf[1024];
while (fgets(buf, 1024, stdin)) {
int len = strlen(buf);
buf[len-1] = 0;
dumpinfo(buf);
}
continue;
}
dumpinfo(argv[i]);
}
return Errors;
}
struct imageInfo {
uint32_t version;
uint32_t flags;
};
void patchFile(uint32_t value, size_t offset) {
int fd = open(FileName, 1);
off_t lresult = lseek(fd, offset, SEEK_SET);
if (lresult == -1) {
printf("couldn't seek to %x position on fd %d\n", offset, fd);
++Errors;
return;
}
int wresult = write(fd, &value, 4);
if (wresult != 4) {
++Errors;
printf("didn't write new value\n");
}
else {
printf("patched %s at offset %p\n", FileName, offset);
}
close(fd);
}
uint32_t iiflags(struct imageInfo *ii, uint32_t size, bool needsFlip) {
if (needsFlip) {
ii->flags = OSSwapInt32(ii->flags);
}
if (debug) printf("flags->%x, nitems %d\n", ii->flags, size/sizeof(struct imageInfo));
uint32_t flags = ii->flags;
if (patch && (flags&0x2)==0) {
//printf("will patch %s at offset %p\n", FileName, (char*)(&ii->flags) - FileBase);
uint32_t newvalue = flags | 0x2;
if (needsFlip) newvalue = OSSwapInt32(newvalue);
patchFile(newvalue, (char*)(&ii->flags) - FileBase);
}
for(int niis = 1; niis < size/sizeof(struct imageInfo); ++niis) {
if (needsFlip) ii[niis].flags = OSSwapInt32(ii[niis].flags);
if (ii[niis].flags != flags) {
// uh, oh.
printf("XXX ii[%d].flags %x != ii[0].flags %x\n", niis, ii[niis].flags, flags);
++Errors;
}
}
return flags;
}
void printflags(uint32_t flags) {
if (flags & 0x1) printf(" F&C");
if (flags & 0x2) printf(" GC");
if (flags & 0x4) printf(" GC-only");
else printf(" RR");
}
/*
void doimageinfo(struct imageInfo *ii, uint32_t size, bool needsFlip) {
uint32_t flags = iiflags(ii, size, needsFlip);
printflags(flags);
}
*/
void dosect32(void *start, struct section *sect, bool needsFlip, struct gcinfo *gcip) {
if (debug) printf("section %s from segment %s\n", sect->sectname, sect->segname);
if (strcmp(sect->segname, "__OBJC")) return;
gcip->hasObjC = true;
if (strcmp(sect->sectname, "__image_info")) return;
gcip->hasInfo = true;
if (needsFlip) {
sect->offset = OSSwapInt32(sect->offset);
sect->size = OSSwapInt32(sect->size);
}
// these guys aren't inline - they point elsewhere
gcip->flags = iiflags(start + sect->offset, sect->size, needsFlip);
}
void dosect64(void *start, struct section_64 *sect, bool needsFlip, struct gcinfo *gcip) {
if (debug) printf("section %s from segment %s\n", sect->sectname, sect->segname);
if (strcmp(sect->segname, "__OBJC") && strcmp(sect->segname, "__DATA")) return;
if (strcmp(sect->sectname, "__image_info") && strncmp(sect->sectname, "__objc_imageinfo", 16)) return;
gcip->hasObjC = true;
gcip->hasInfo = true;
if (needsFlip) {
sect->offset = OSSwapInt32(sect->offset);
sect->size = OSSwapInt64(sect->size);
}
// these guys aren't inline - they point elsewhere
gcip->flags = iiflags(start + sect->offset, sect->size, needsFlip);
}
void doseg32(void *start, struct segment_command *seg, bool needsFlip, struct gcinfo *gcip) {
// lets do sections
if (needsFlip) {
seg->fileoff = OSSwapInt32(seg->fileoff);
seg->nsects = OSSwapInt32(seg->nsects);
}
if (debug) printf("segment name: %s, nsects %d\n", seg->segname, seg->nsects);
if (seg->segname[0]) {
if (strcmp("__OBJC", seg->segname)) return;
}
int nsects;
struct section *sect = (struct section *)(seg + 1);
for (int nsects = 0; nsects < seg->nsects; ++nsects) {
// sections directly follow
dosect32(start, sect + nsects, needsFlip, gcip);
}
}
void doseg64(void *start, struct segment_command_64 *seg, bool needsFlip, struct gcinfo *gcip) {
if (debug) printf("segment name: %s\n", seg->segname);
if (seg->segname[0] && strcmp("__OBJC", seg->segname) && strcmp("__DATA", seg->segname)) return;
gcip->hasObjC = true;
// lets do sections
if (needsFlip) {
seg->fileoff = OSSwapInt64(seg->fileoff);
seg->nsects = OSSwapInt32(seg->nsects);
}
int nsects;
struct section_64 *sect = (struct section_64 *)(seg + 1);
for (int nsects = 0; nsects < seg->nsects; ++nsects) {
// sections directly follow
dosect64(start, sect + nsects, needsFlip, gcip);
}
}
#if 0
/*
* A variable length string in a load command is represented by an lc_str
* union. The strings are stored just after the load command structure and
* the offset is from the start of the load command structure. The size
* of the string is reflected in the cmdsize field of the load command.
* Once again any padded bytes to bring the cmdsize field to a multiple
* of 4 bytes must be zero.
*/
union lc_str {
uint32_t offset; /* offset to the string */
#ifndef __LP64__
char *ptr; /* pointer to the string */
#endif
};
struct dylib {
union lc_str name; /* library's path name */
uint32_t timestamp; /* library's build time stamp */
uint32_t current_version; /* library's current version number */
uint32_t compatibility_version; /* library's compatibility vers number*/
};
* A dynamically linked shared library (filetype == MH_DYLIB in the mach header)
* contains a dylib_command (cmd == LC_ID_DYLIB) to identify the library.
* An object that uses a dynamically linked shared library also contains a
* dylib_command (cmd == LC_LOAD_DYLIB, LC_LOAD_WEAK_DYLIB, or
* LC_REEXPORT_DYLIB) for each library it uses.
struct dylib_command {
uint32_t cmd; /* LC_ID_DYLIB, LC_LOAD_{,WEAK_}DYLIB,
LC_REEXPORT_DYLIB */
uint32_t cmdsize; /* includes pathname string */
struct dylib dylib; /* the library identification */
};
#endif
void dodylib(void *start, struct dylib_command *dylibCmd, bool needsFlip) {
if (!verbose) return;
if (needsFlip) {
}
int count = dylibCmd->cmdsize - sizeof(struct dylib_command);
//printf("offset is %d, count is %d\n", dylibCmd->dylib.name.offset, count);
if (dylibCmd->dylib.name.offset > count) return;
//printf("-->%.*s<---", count, ((void *)dylibCmd)+dylibCmd->dylib.name.offset);
if (verbose) printf("load %s\n", ((void *)dylibCmd)+dylibCmd->dylib.name.offset);
}
struct load_command *doloadcommand(void *start, struct load_command *lc, bool needsFlip, bool is32, struct gcinfo *gcip) {
if (needsFlip) {
lc->cmd = OSSwapInt32(lc->cmd);
lc->cmdsize = OSSwapInt32(lc->cmdsize);
}
switch(lc->cmd) {
case LC_SEGMENT_64:
if (debug) printf("...segment64\n");
if (is32) printf("XXX we have a 64-bit segment in a 32-bit mach-o\n");
doseg64(start, (struct segment_command_64 *)lc, needsFlip, gcip);
break;
case LC_SEGMENT:
if (debug) printf("...segment32\n");
doseg32(start, (struct segment_command *)lc, needsFlip, gcip);
break;
case LC_SYMTAB: if (debug) printf("...dynamic symtab\n"); break;
case LC_DYSYMTAB: if (debug) printf("...symtab\n"); break;
case LC_LOAD_DYLIB:
dodylib(start, (struct dylib_command *)lc, needsFlip);
break;
case LC_SUB_UMBRELLA: if (debug) printf("...load subumbrella\n"); break;
default: if (debug) printf("cmd is %x\n", lc->cmd); break;
}
return (struct load_command *)((void *)lc + lc->cmdsize);
}
void doofile(void *start, uint32_t size, struct gcinfo *gcip) {
struct mach_header *mh = (struct mach_header *)start;
bool isFlipped = false;
if (mh->magic == MH_CIGAM || mh->magic == MH_CIGAM_64) {
if (debug) printf("(flipping)\n");
mh->magic = OSSwapInt32(mh->magic);
mh->cputype = OSSwapInt32(mh->cputype);
mh->cpusubtype = OSSwapInt32(mh->cpusubtype);
mh->filetype = OSSwapInt32(mh->filetype);
mh->ncmds = OSSwapInt32(mh->ncmds);
mh->sizeofcmds = OSSwapInt32(mh->sizeofcmds);
mh->flags = OSSwapInt32(mh->flags);
isFlipped = true;
}
if (rrOnly && mh->filetype != 6) return; // ignore executables
NXArchInfo *info = (NXArchInfo *)NXGetArchInfoFromCpuType(mh->cputype, mh->cpusubtype);
//printf("%s:", info->description);
gcip->arch = (char *)info->description;
//if (debug) printf("...description is %s\n", info->description);
bool is32 = (mh->cputype == 18 || mh->cputype == 7);
if (debug) printf("is 32? %d\n", is32);
if (debug) printf("filetype -> %d\n", mh->filetype);
if (debug) printf("ncmds -> %d\n", mh->ncmds);
struct load_command *lc = (is32 ? (struct load_command *)(mh + 1) : (struct load_command *)((struct mach_header_64 *)start + 1));
int ncmds;
for (ncmds = 0; ncmds < mh->ncmds; ++ncmds) {
lc = doloadcommand(start, lc, isFlipped, is32, gcip);
}
//printf("\n");
}
void initGCInfo() {
bzero((void *)GCInfo, sizeof(GCInfo));
}
void printGCInfo(char *filename) {
if (!GCInfo[0].hasObjC) return; // don't bother
// verify that flags are all the same
uint32_t flags = GCInfo[0].flags;
bool allSame = true;
for (int i = 1; i < 4 && GCInfo[i].arch; ++i) {
if (flags != GCInfo[i].flags) {
allSame = false;
}
}
if (rrOnly) {
if (allSame && (flags & 0x2))
return;
printf("*** not all GC in %s:\n", filename);
}
if (allSame && !verbose) {
printf("%s:", filename);
printflags(flags);
printf("\n");
}
else {
printf("%s:\n", filename);
for (int i = 0; i < 4 && GCInfo[i].arch; ++i) {
printf("%s:", GCInfo[i].arch);
printflags(GCInfo[i].flags);
printf("\n");
}
printf("\n");
}
}
void dofat(void *start) {
struct fat_header *fh = start;
bool needsFlip = false;
if (fh->magic == FAT_CIGAM) {
fh->nfat_arch = OSSwapInt32(fh->nfat_arch);
needsFlip = true;
}
if (debug) printf("%d architectures\n", fh->nfat_arch);
int narchs;
struct fat_arch *arch_ptr = (struct fat_arch *)(fh + 1);
for (narchs = 0; narchs < fh->nfat_arch; ++narchs) {
if (needsFlip) {
arch_ptr->offset = OSSwapInt32(arch_ptr->offset);
arch_ptr->size = OSSwapInt32(arch_ptr->size);
}
doofile(start+arch_ptr->offset, arch_ptr->size, &GCInfo[narchs]);
arch_ptr++;
}
}
bool openFile(const char *filename) {
FileName = filename;
// get size
struct stat statb;
int fd = open(filename, 0);
if (fd < 0) {
printf("couldn't open %s for reading\n", filename);
return false;
}
int osresult = fstat(fd, &statb);
if (osresult != 0) {
printf("couldn't get size of %s\n", filename);
close(fd);
return false;
}
FileSize = statb.st_size;
FileBase = malloc(FileSize);
if (!FileBase) {
printf("couldn't malloc %d bytes\n", FileSize);
close(fd);
return false;
}
off_t readsize = read(fd, FileBase, FileSize);
if (readsize != FileSize) {
printf("read %d bytes, wanted %d\n", readsize, FileSize);
close(fd);
return false;
}
close(fd);
return true;
}
void closeFile() {
free(FileBase);
}
void dumpinfo(char *filename) {
initGCInfo();
openFile(filename);
struct fat_header *fh = (struct fat_header *)FileBase;
if (fh->magic == FAT_MAGIC || fh->magic == FAT_CIGAM) {
dofat((void *)FileBase);
//printGCInfo(filename);
}
else if (fh->magic == MH_MAGIC || fh->magic == MH_CIGAM || fh->magic == MH_MAGIC_64 || fh->magic == MH_CIGAM_64) {
doofile((void *)FileBase, FileSize, &GCInfo[0]);
//printGCInfo(filename);
}
else if (!quiet) {
printf("don't understand %s!\n", filename);
}
closeFile();
}
Jump to Line
Something went wrong with that request. Please try again.