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@
25 // Print or edit ObjC image info bits.
26 // This is used to verify ld's handling of these bits
27 // for values that are not emitted by current compilers.
38 #include <sys/errno.h>
39 #include <mach-o/fat.h>
40 #include <mach-o/arch.h>
41 #include <mach-o/loader.h>
43 #include "MachOFileAbstraction.hpp"
46 typedef BigEndian CurrentEndian
;
47 typedef LittleEndian OtherEndian
;
48 #elif __LITTLE_ENDIAN__
49 typedef LittleEndian CurrentEndian
;
50 typedef BigEndian OtherEndian
;
52 # error unknown endianness
55 static const bool debug
= false;
57 static bool processFile(const char *filename
, uint32_t set
, uint32_t clear
);
59 // fixme use objc/objc-abi.h instead
60 struct objc_image_info
{
65 IsReplacement
= 1<<0, // used for Fix&Continue, now ignored
66 SupportsGC
= 1<<1, // image supports GC
67 RequiresGC
= 1<<2, // image requires GC
68 OptimizedByDyld
= 1<<3, // image is from an optimized shared cache
69 CorrectedSynthesize
= 1<<4, // used for an old workaround, now ignored
70 IsSimulated
= 1<<5, // image compiled for a simulator platform
71 HasCategoryClassProperties
= 1<<6, // class properties in category_t
73 SwiftVersionMask
= 0xff << 8 // Swift ABI version
77 // objc_image_info flags and their names
82 { "supports-gc", objc_image_info::SupportsGC
},
83 { "requires-gc", objc_image_info::RequiresGC
},
84 { "has-category-class-properties", objc_image_info::HasCategoryClassProperties
},
88 static void usage(const char *self
)
90 printf("usage: %s [+FLAG|-FLAG ...] file ...\n", self
);
91 printf("Use +FLAG to set and -FLAG to clear.\n");
92 printf("Known flags: ");
93 for (int i
= 0; Flags
[i
].name
!= 0; i
++) {
94 printf("%s ", Flags
[i
].name
);
99 static uint32_t flagForName(const char *name
)
101 for (int i
= 0; Flags
[i
].name
!= 0; i
++) {
102 if (0 == strcmp(Flags
[i
].name
, name
)) {
103 return Flags
[i
].value
;
109 static const char *nameForFlag(uint32_t flag
)
111 for (int i
= 0; Flags
[i
].name
!= 0; i
++) {
112 if (Flags
[i
].value
== flag
) {
113 return Flags
[i
].name
;
119 static void printFlags(uint32_t flags
)
121 printf("0x%x", flags
);
123 // Print flags and unknown bits
124 for (int i
= 0; i
< 24; i
++) {
125 uint32_t flag
= 1<<i
;
126 if (flag
& objc_image_info::SwiftVersionMask
) continue;
128 const char *name
= nameForFlag(flag
);
129 if (name
) printf(" %s", name
);
130 else printf(" unknown-%u", flag
);
134 // Print Swift version
135 uint32_t mask
= objc_image_info::SwiftVersionMask
;
136 uint32_t shift
= __builtin_ctzl(mask
);
137 uint32_t swift
= (flags
& mask
) >> shift
;
139 printf(" swift-version=%u", swift
);
143 static bool isFlagArgument(const char *arg
)
145 return (arg
&& (arg
[0] == '+' || arg
[0] == '-'));
148 int main(int argc
, const char *argv
[]) {
152 // Find flag arguments (which are +FLAG or -FLAG).
154 for (i
= 1; i
< argc
&& isFlagArgument(argv
[i
]); i
++) {
155 const char *arg
= argv
[i
];
156 uint32_t flag
= flagForName(arg
+1);
164 printf("error: unrecognized ObjC flag '%s'\n", arg
);
170 // Complain if +FLAG and -FLAG are both set for some flag.
171 uint32_t overlap
= set
& clear
;
173 printf("error: conflicting changes specified: ");
180 // Complain if there are no filenames.
182 printf("error: no files specified\n");
188 for (; i
< argc
; i
++) {
189 if (!processFile(argv
[i
], set
, clear
)) return 1;
195 // Segment and section names are 16 bytes and may be un-terminated.
196 static bool segnameEquals(const char *lhs
, const char *rhs
)
198 return 0 == strncmp(lhs
, rhs
, 16);
201 static bool segnameStartsWith(const char *segname
, const char *prefix
)
203 return 0 == strncmp(segname
, prefix
, strlen(prefix
));
206 static bool sectnameEquals(const char *lhs
, const char *rhs
)
208 return segnameEquals(lhs
, rhs
);
212 template <typename P
>
213 static void dosect(const char *filename
, uint8_t *start
, macho_section
<P
> *sect
,
214 uint32_t set
, uint32_t clear
)
216 if (debug
) printf("section %.16s from segment %.16s\n",
217 sect
->sectname(), sect
->segname());
219 if ((segnameStartsWith(sect
->segname(), "__DATA") &&
220 sectnameEquals(sect
->sectname(), "__objc_imageinfo")) ||
221 (segnameEquals(sect
->segname(), "__OBJC") &&
222 sectnameEquals(sect
->sectname(), "__image_info")))
224 objc_image_info
*ii
= (objc_image_info
*)(start
+ sect
->offset());
225 uint32_t oldFlags
= P::E::get32(ii
->flags
);
226 uint32_t newFlags
= (oldFlags
| set
) & ~clear
;
227 if (oldFlags
!= newFlags
) {
228 P::E::set32(ii
->flags
, newFlags
);
229 if (debug
) printf("changed flags from 0x%x to 0x%x\n",
233 printf("%s: ", filename
);
234 printFlags(newFlags
);
239 template <typename P
>
240 static void doseg(const char *filename
,
241 uint8_t *start
, macho_segment_command
<P
> *seg
,
242 uint32_t set
, uint32_t clear
)
244 if (debug
) printf("segment name: %.16s, nsects %u\n",
245 seg
->segname(), seg
->nsects());
246 macho_section
<P
> *sect
= (macho_section
<P
> *)(seg
+ 1);
247 for (uint32_t i
= 0; i
< seg
->nsects(); ++i
) {
248 dosect(filename
, start
, §
[i
], set
, clear
);
254 static bool parse_macho(const char *filename
, uint8_t *buffer
,
255 uint32_t set
, uint32_t clear
)
257 macho_header
<P
>* mh
= (macho_header
<P
>*)buffer
;
258 uint8_t *cmds
= (uint8_t *)(mh
+ 1);
259 for (uint32_t c
= 0; c
< mh
->ncmds(); c
++) {
260 macho_load_command
<P
>* cmd
= (macho_load_command
<P
>*)cmds
;
261 cmds
+= cmd
->cmdsize();
262 if (cmd
->cmd() == LC_SEGMENT
|| cmd
->cmd() == LC_SEGMENT_64
) {
263 doseg(filename
, buffer
, (macho_segment_command
<P
>*)cmd
, set
, clear
);
271 static bool parse_macho(const char *filename
, uint8_t *buffer
,
272 uint32_t set
, uint32_t clear
)
274 uint32_t magic
= *(uint32_t *)buffer
;
278 return parse_macho
<Pointer64
<CurrentEndian
>>
279 (filename
, buffer
, set
, clear
);
281 return parse_macho
<Pointer32
<CurrentEndian
>>
282 (filename
, buffer
, set
, clear
);
284 return parse_macho
<Pointer64
<OtherEndian
>>
285 (filename
, buffer
, set
, clear
);
287 return parse_macho
<Pointer32
<OtherEndian
>>
288 (filename
, buffer
, set
, clear
);
290 printf("error: file '%s' is not mach-o (magic %x)\n", filename
, magic
);
296 static bool parse_fat(const char *filename
, uint8_t *buffer
, size_t size
,
297 uint32_t set
, uint32_t clear
)
301 if (size
< sizeof(magic
)) {
302 printf("error: file '%s' is too small\n", filename
);
306 magic
= *(uint32_t *)buffer
;
307 if (magic
!= FAT_MAGIC
&& magic
!= FAT_CIGAM
) {
309 return parse_macho(filename
, buffer
, set
, clear
);
311 struct fat_header
*fh
;
312 uint32_t fat_magic
, fat_nfat_arch
;
313 struct fat_arch
*archs
;
315 if (size
< sizeof(struct fat_header
)) {
316 printf("error: file '%s' is too small\n", filename
);
320 fh
= (struct fat_header
*)buffer
;
321 fat_magic
= OSSwapBigToHostInt32(fh
->magic
);
322 fat_nfat_arch
= OSSwapBigToHostInt32(fh
->nfat_arch
);
324 if (size
< (sizeof(struct fat_header
) + fat_nfat_arch
* sizeof(struct fat_arch
))) {
325 printf("error: file '%s' is too small\n", filename
);
329 archs
= (struct fat_arch
*)(buffer
+ sizeof(struct fat_header
));
331 /* Special case hidden CPU_TYPE_ARM64 */
332 if (size
>= (sizeof(struct fat_header
) + (fat_nfat_arch
+ 1) * sizeof(struct fat_arch
))) {
333 if (fat_nfat_arch
> 0
334 && OSSwapBigToHostInt32(archs
[fat_nfat_arch
].cputype
) == CPU_TYPE_ARM64
) {
338 /* End special case hidden CPU_TYPE_ARM64 */
340 if (debug
) printf("%d fat architectures\n", fat_nfat_arch
);
342 for (uint32_t i
= 0; i
< fat_nfat_arch
; i
++) {
343 uint32_t arch_cputype
= OSSwapBigToHostInt32(archs
[i
].cputype
);
344 uint32_t arch_cpusubtype
= OSSwapBigToHostInt32(archs
[i
].cpusubtype
);
345 uint32_t arch_offset
= OSSwapBigToHostInt32(archs
[i
].offset
);
346 uint32_t arch_size
= OSSwapBigToHostInt32(archs
[i
].size
);
348 if (debug
) printf("cputype %d cpusubtype %d\n",
349 arch_cputype
, arch_cpusubtype
);
351 /* Check that slice data is after all fat headers and archs */
352 if (arch_offset
< (sizeof(struct fat_header
) + fat_nfat_arch
* sizeof(struct fat_arch
))) {
353 printf("error: file is badly formed\n");
357 /* Check that the slice ends before the file does */
358 if (arch_offset
> size
) {
359 printf("error: file '%s' is badly formed\n", filename
);
363 if (arch_size
> size
) {
364 printf("error: file '%s' is badly formed\n", filename
);
368 if (arch_offset
> (size
- arch_size
)) {
369 printf("error: file '%s' is badly formed\n", filename
);
373 bool ok
= parse_macho(filename
, buffer
+ arch_offset
, set
, clear
);
374 if (!ok
) return false;
380 static bool processFile(const char *filename
, uint32_t set
, uint32_t clear
)
382 if (debug
) printf("file %s\n", filename
);
383 int openPerm
= O_RDONLY
;
384 int mmapPerm
= PROT_READ
;
387 mmapPerm
= PROT_READ
| PROT_WRITE
;
390 int fd
= open(filename
, openPerm
);
392 printf("error: open %s: %s\n", filename
, strerror(errno
));
397 if (fstat(fd
, &st
) < 0) {
398 printf("error: fstat %s: %s\n", filename
, strerror(errno
));
402 void *buffer
= mmap(NULL
, (size_t)st
.st_size
, mmapPerm
,
403 MAP_FILE
|MAP_SHARED
, fd
, 0);
404 if (buffer
== MAP_FAILED
) {
405 printf("error: mmap %s: %s\n", filename
, strerror(errno
));
410 parse_fat(filename
, (uint8_t *)buffer
, (size_t)st
.st_size
, set
, clear
);
411 munmap(buffer
, (size_t)st
.st_size
);