2 * Copyright (c) 2007-2009 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
32 #include <mach-o/fat.h>
33 #include <mach-o/arch.h>
34 #include <mach-o/loader.h>
36 // from "objc-private.h"
37 // masks for objc_image_info.flags
38 #define OBJC_IMAGE_IS_REPLACEMENT (1<<0)
39 #define OBJC_IMAGE_SUPPORTS_GC (1<<1)
40 #define OBJC_IMAGE_REQUIRES_GC (1<<2)
41 #define OBJC_IMAGE_OPTIMIZED_BY_DYLD (1<<3)
42 #define OBJC_IMAGE_SUPPORTS_COMPACTION (1<<4)
58 void dumpinfo(char *filename
);
65 int main(int argc
, char *argv
[]) {
66 //NSAutoreleasePool *pool = [NSAutoreleasePool new];
68 //dumpinfo("/System/Library/Frameworks/AppKit.framework/AppKit");
70 printf("Usage: markgc [-v] [-r] [--] library_or_executable_image [image2 ...]\n");
71 printf(" changes Garbage Collection readiness of named images, ignoring those without ObjC segments\n");
72 printf(" -p - patch RR binary to (apparently) support GC (default)\n");
73 printf(" -u - unpatch GC binary to RR only\n");
74 printf("\nAuthor: blaine@apple.com\n");
77 for (i
= 1; i
< argc
; ++i
) {
78 if (!strcmp(argv
[i
], "-v")) {
82 if (!strcmp(argv
[i
], "-d")) {
86 if (!strcmp(argv
[i
], "-q")) {
90 if (!strcmp(argv
[i
], "-p")) {
94 if (!strcmp(argv
[i
], "-u")) {
109 void patchFile(uint32_t value
, size_t offset
) {
110 int fd
= open(FileName
, 1);
111 off_t lresult
= lseek(fd
, offset
, SEEK_SET
);
113 printf("couldn't seek to 0x%lx position on fd %d\n", offset
, fd
);
117 size_t wresult
= write(fd
, &value
, 4);
120 printf("didn't write new value\n");
123 printf("patched %s at offset 0x%lx\n", FileName
, offset
);
128 uint32_t iiflags(struct imageInfo
*ii
, size_t size
, bool needsFlip
) {
130 ii
->flags
= OSSwapInt32(ii
->flags
);
132 if (debug
) printf("flags->%x, nitems %lu\n", ii
->flags
, size
/sizeof(struct imageInfo
));
133 uint32_t support_mask
= (OBJC_IMAGE_SUPPORTS_GC
| OBJC_IMAGE_SUPPORTS_COMPACTION
);
134 uint32_t flags
= ii
->flags
;
135 if (patch
&& (flags
& support_mask
) != support_mask
) {
136 //printf("will patch %s at offset %p\n", FileName, (char*)(&ii->flags) - FileBase);
137 uint32_t newvalue
= flags
| support_mask
;
138 if (needsFlip
) newvalue
= OSSwapInt32(newvalue
);
139 patchFile(newvalue
, (char*)(&ii
->flags
) - FileBase
);
141 if (unpatch
&& (flags
& support_mask
) == support_mask
) {
142 uint32_t newvalue
= flags
& ~support_mask
;
143 if (needsFlip
) newvalue
= OSSwapInt32(newvalue
);
144 patchFile(newvalue
, (char*)(&ii
->flags
) - FileBase
);
146 for(unsigned niis
= 1; niis
< size
/sizeof(struct imageInfo
); ++niis
) {
147 if (needsFlip
) ii
[niis
].flags
= OSSwapInt32(ii
[niis
].flags
);
148 if (ii
[niis
].flags
!= flags
) {
150 printf("XXX ii[%d].flags %x != ii[0].flags %x\n", niis
, ii
[niis
].flags
, flags
);
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");
165 void doimageinfo(struct imageInfo *ii, uint32_t size, bool needsFlip) {
166 uint32_t flags = iiflags(ii, size, needsFlip);
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;
179 sect
->offset
= OSSwapInt32(sect
->offset
);
180 sect
->size
= OSSwapInt32(sect
->size
);
182 // these guys aren't inline - they point elsewhere
183 gcip
->flags
= iiflags(start
+ sect
->offset
, sect
->size
, needsFlip
);
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;
193 sect
->offset
= OSSwapInt32(sect
->offset
);
194 sect
->size
= OSSwapInt64(sect
->size
);
196 // these guys aren't inline - they point elsewhere
197 gcip
->flags
= iiflags(start
+ sect
->offset
, (size_t)sect
->size
, needsFlip
);
200 void doseg32(void *start
, struct segment_command
*seg
, bool needsFlip
, struct gcinfo
*gcip
) {
203 seg
->fileoff
= OSSwapInt32(seg
->fileoff
);
204 seg
->nsects
= OSSwapInt32(seg
->nsects
);
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;
210 struct section
*sect
= (struct section
*)(seg
+ 1);
211 for (uint32_t nsects
= 0; nsects
< seg
->nsects
; ++nsects
) {
212 // sections directly follow
214 dosect32(start
, sect
+ nsects
, needsFlip
, gcip
);
217 void doseg64(void *start
, struct segment_command_64
*seg
, bool needsFlip
, struct gcinfo
*gcip
) {
218 if (debug
) printf("segment name: %s\n", seg
->segname
);
219 if (seg
->segname
[0] && strcmp("__OBJC", seg
->segname
) && strcmp("__DATA", seg
->segname
)) return;
220 gcip
->hasObjC
= true;
223 seg
->fileoff
= OSSwapInt64(seg
->fileoff
);
224 seg
->nsects
= OSSwapInt32(seg
->nsects
);
226 struct section_64
*sect
= (struct section_64
*)(seg
+ 1);
227 for (uint32_t nsects
= 0; nsects
< seg
->nsects
; ++nsects
) {
228 // sections directly follow
230 dosect64(start
, sect
+ nsects
, needsFlip
, gcip
);
236 * A variable length string in a load command is represented by an lc_str
237 * union. The strings are stored just after the load command structure and
238 * the offset is from the start of the load command structure. The size
239 * of the string is reflected in the cmdsize field of the load command.
240 * Once again any padded bytes to bring the cmdsize field to a multiple
241 * of 4 bytes must be zero.
244 uint32_t offset
; /* offset to the string */
246 char *ptr
; /* pointer to the string */
251 union lc_str name
; /* library's path name */
252 uint32_t timestamp
; /* library's build time stamp */
253 uint32_t current_version
; /* library's current version number */
254 uint32_t compatibility_version
; /* library's compatibility vers number*/
257 * A dynamically linked shared
library (filetype
== MH_DYLIB in the mach header
)
258 * contains a
dylib_command (cmd
== LC_ID_DYLIB
) to identify the library
.
259 * An object that uses a dynamically linked shared library also contains a
260 * dylib_command (cmd
== LC_LOAD_DYLIB
, LC_LOAD_WEAK_DYLIB
, or
261 * LC_REEXPORT_DYLIB
) for each library it uses
.
263 struct dylib_command
{
264 uint32_t cmd
; /* LC_ID_DYLIB, LC_LOAD_{,WEAK_}DYLIB,
266 uint32_t cmdsize
; /* includes pathname string */
267 struct dylib dylib
; /* the library identification */
271 void dodylib(void *start
, struct dylib_command
*dylibCmd
, bool needsFlip
) {
272 if (!verbose
) return;
275 size_t count
= dylibCmd
->cmdsize
- sizeof(struct dylib_command
);
276 //printf("offset is %d, count is %d\n", dylibCmd->dylib.name.offset, count);
277 if (dylibCmd
->dylib
.name
.offset
> count
) return;
278 //printf("-->%.*s<---", count, ((void *)dylibCmd)+dylibCmd->dylib.name.offset);
279 if (verbose
) printf("load %s\n", ((char *)dylibCmd
)+dylibCmd
->dylib
.name
.offset
);
282 struct load_command
*doloadcommand(void *start
, struct load_command
*lc
, bool needsFlip
, bool is32
, struct gcinfo
*gcip
) {
284 lc
->cmd
= OSSwapInt32(lc
->cmd
);
285 lc
->cmdsize
= OSSwapInt32(lc
->cmdsize
);
290 if (debug
) printf("...segment64\n");
291 if (is32
) printf("XXX we have a 64-bit segment in a 32-bit mach-o\n");
292 doseg64(start
, (struct segment_command_64
*)lc
, needsFlip
, gcip
);
295 if (debug
) printf("...segment32\n");
296 doseg32(start
, (struct segment_command
*)lc
, needsFlip
, gcip
);
298 case LC_SYMTAB
: if (debug
) printf("...dynamic symtab\n"); break;
299 case LC_DYSYMTAB
: if (debug
) printf("...symtab\n"); break;
301 dodylib(start
, (struct dylib_command
*)lc
, needsFlip
);
303 case LC_SUB_UMBRELLA
: if (debug
) printf("...load subumbrella\n"); break;
304 default: if (debug
) printf("cmd is %x\n", lc
->cmd
); break;
307 return (struct load_command
*)((void *)lc
+ lc
->cmdsize
);
310 void doofile(void *start
, size_t size
, struct gcinfo
*gcip
) {
311 struct mach_header
*mh
= (struct mach_header
*)start
;
312 bool isFlipped
= false;
313 if (mh
->magic
== MH_CIGAM
|| mh
->magic
== MH_CIGAM_64
) {
314 if (debug
) printf("(flipping)\n");
315 mh
->magic
= OSSwapInt32(mh
->magic
);
316 mh
->cputype
= OSSwapInt32(mh
->cputype
);
317 mh
->cpusubtype
= OSSwapInt32(mh
->cpusubtype
);
318 mh
->filetype
= OSSwapInt32(mh
->filetype
);
319 mh
->ncmds
= OSSwapInt32(mh
->ncmds
);
320 mh
->sizeofcmds
= OSSwapInt32(mh
->sizeofcmds
);
321 mh
->flags
= OSSwapInt32(mh
->flags
);
324 if (rrOnly
&& mh
->filetype
!= MH_DYLIB
) return; // ignore executables
325 NXArchInfo
*info
= (NXArchInfo
*)NXGetArchInfoFromCpuType(mh
->cputype
, mh
->cpusubtype
);
326 //printf("%s:", info->description);
327 gcip
->arch
= (char *)info
->description
;
328 //if (debug) printf("...description is %s\n", info->description);
329 bool is32
= !(mh
->cputype
& CPU_ARCH_ABI64
);
330 if (debug
) printf("is 32? %d\n", is32
);
331 if (debug
) printf("filetype -> %d\n", mh
->filetype
);
332 if (debug
) printf("ncmds -> %d\n", mh
->ncmds
);
333 struct load_command
*lc
= (is32
? (struct load_command
*)(mh
+ 1) : (struct load_command
*)((struct mach_header_64
*)start
+ 1));
335 for (ncmds
= 0; ncmds
< mh
->ncmds
; ++ncmds
) {
336 lc
= doloadcommand(start
, lc
, isFlipped
, is32
, gcip
);
342 bzero((void *)GCInfo
, sizeof(GCInfo
));
345 void printGCInfo(char *filename
) {
346 if (!GCInfo
[0].hasObjC
) return; // don't bother
347 // verify that flags are all the same
348 uint32_t flags
= GCInfo
[0].flags
;
350 for (int i
= 1; i
< 4 && GCInfo
[i
].arch
; ++i
) {
351 if (flags
!= GCInfo
[i
].flags
) {
356 if (allSame
&& (flags
& 0x2))
358 printf("*** not all GC in %s:\n", filename
);
360 if (allSame
&& !verbose
) {
361 printf("%s:", filename
);
366 printf("%s:\n", filename
);
367 for (int i
= 0; i
< 4 && GCInfo
[i
].arch
; ++i
) {
368 printf("%s:", GCInfo
[i
].arch
);
369 printflags(GCInfo
[i
].flags
);
376 void dofat(void *start
) {
377 struct fat_header
*fh
= start
;
378 bool needsFlip
= false;
379 if (fh
->magic
== FAT_CIGAM
) {
380 fh
->nfat_arch
= OSSwapInt32(fh
->nfat_arch
);
383 if (debug
) printf("%d architectures\n", fh
->nfat_arch
);
385 struct fat_arch
*arch_ptr
= (struct fat_arch
*)(fh
+ 1);
386 for (narchs
= 0; narchs
< fh
->nfat_arch
; ++narchs
) {
387 if (debug
) printf("doing arch %d\n", narchs
);
389 arch_ptr
->offset
= OSSwapInt32(arch_ptr
->offset
);
390 arch_ptr
->size
= OSSwapInt32(arch_ptr
->size
);
392 doofile(start
+arch_ptr
->offset
, arch_ptr
->size
, &GCInfo
[narchs
]);
397 bool openFile(const char *filename
) {
401 int fd
= open(filename
, 0);
403 printf("couldn't open %s for reading\n", filename
);
406 int osresult
= fstat(fd
, &statb
);
408 printf("couldn't get size of %s\n", filename
);
412 if ((sizeof(size_t) == 4) && ((size_t)statb
.st_size
> SIZE_T_MAX
)) {
413 printf("couldn't malloc %llu bytes\n", statb
.st_size
);
417 FileSize
= (size_t)statb
.st_size
;
418 FileBase
= malloc(FileSize
);
420 printf("couldn't malloc %lu bytes\n", FileSize
);
424 ssize_t readsize
= read(fd
, FileBase
, FileSize
);
425 if ((readsize
== -1) || ((size_t)readsize
!= FileSize
)) {
426 printf("read %ld bytes, wanted %ld\n", (size_t)readsize
, FileSize
);
438 void dumpinfo(char *filename
) {
440 if (!openFile(filename
)) exit(1);
441 struct fat_header
*fh
= (struct fat_header
*)FileBase
;
442 if (fh
->magic
== FAT_MAGIC
|| fh
->magic
== FAT_CIGAM
) {
443 dofat((void *)FileBase
);
444 //printGCInfo(filename);
446 else if (fh
->magic
== MH_MAGIC
|| fh
->magic
== MH_CIGAM
|| fh
->magic
== MH_MAGIC_64
|| fh
->magic
== MH_CIGAM_64
) {
447 doofile((void *)FileBase
, FileSize
, &GCInfo
[0]);
448 //printGCInfo(filename);
451 printf("don't understand %s!\n", filename
);