]> git.saurik.com Git - apple/ld64.git/blame - src/other/objcimageinfo.cpp
ld64-302.3.tar.gz
[apple/ld64.git] / src / other / objcimageinfo.cpp
CommitLineData
0a8dc3df
A
1/*
2 * Copyright (c) 2007-2009 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// objcimageinfo.cpp
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.
28
29#include <stdlib.h>
30#include <unistd.h>
31#include <string.h>
32#include <stdio.h>
33#include <stdbool.h>
34#include <fcntl.h>
35#include <limits.h>
36#include <sys/mman.h>
37#include <sys/stat.h>
38#include <sys/errno.h>
39#include <mach-o/fat.h>
40#include <mach-o/arch.h>
41#include <mach-o/loader.h>
42
43#include "MachOFileAbstraction.hpp"
44
45#if __BIG_ENDIAN__
46 typedef BigEndian CurrentEndian;
47 typedef LittleEndian OtherEndian;
48#elif __LITTLE_ENDIAN__
49 typedef LittleEndian CurrentEndian;
50 typedef BigEndian OtherEndian;
51#else
52# error unknown endianness
53#endif
54
55static const bool debug = false;
56
57static bool processFile(const char *filename, uint32_t set, uint32_t clear);
58
59// fixme use objc/objc-abi.h instead
60struct objc_image_info {
61 uint32_t version;
62 uint32_t flags;
63
64 enum : uint32_t {
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
72
73 SwiftVersionMask = 0xff << 8 // Swift ABI version
74 };
75};
76
77// objc_image_info flags and their names
78static const struct {
79 const char *name;
80 uint32_t value;
81} Flags[] = {
82 { "supports-gc", objc_image_info::SupportsGC },
83 { "requires-gc", objc_image_info::RequiresGC },
84 { "has-category-class-properties", objc_image_info::HasCategoryClassProperties },
85 { 0, 0 }
86};
87
88static void usage(const char *self)
89{
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);
95 }
96 printf("\n");
97}
98
99static uint32_t flagForName(const char *name)
100{
101 for (int i = 0; Flags[i].name != 0; i++) {
102 if (0 == strcmp(Flags[i].name, name)) {
103 return Flags[i].value;
104 }
105 }
106 return 0;
107}
108
109static const char *nameForFlag(uint32_t flag)
110{
111 for (int i = 0; Flags[i].name != 0; i++) {
112 if (Flags[i].value == flag) {
113 return Flags[i].name;
114 }
115 }
116 return 0;
117}
118
119static void printFlags(uint32_t flags)
120{
121 printf("0x%x", flags);
122
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;
127 if (flags & flag) {
128 const char *name = nameForFlag(flag);
129 if (name) printf(" %s", name);
130 else printf(" unknown-%u", flag);
131 }
132 }
133
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;
138 if (swift > 0) {
139 printf(" swift-version=%u", swift);
140 }
141}
142
143static bool isFlagArgument(const char *arg)
144{
145 return (arg && (arg[0] == '+' || arg[0] == '-'));
146}
147
148int main(int argc, const char *argv[]) {
149 uint32_t set = 0;
150 uint32_t clear = 0;
151
152 // Find flag arguments (which are +FLAG or -FLAG).
153 int i;
154 for (i = 1; i < argc && isFlagArgument(argv[i]); i++) {
155 const char *arg = argv[i];
156 uint32_t flag = flagForName(arg+1);
157 if (flag) {
158 if (arg[0] == '+') {
159 set |= flag;
160 } else {
161 clear |= flag;
162 }
163 } else {
164 printf("error: unrecognized ObjC flag '%s'\n", arg);
165 usage(argv[0]);
166 return 1;
167 }
168 }
169
170 // Complain if +FLAG and -FLAG are both set for some flag.
171 uint32_t overlap = set & clear;
172 if (overlap) {
173 printf("error: conflicting changes specified: ");
174 printFlags(overlap);
175 printf("\n");
176 usage(argv[0]);
177 return 1;
178 }
179
180 // Complain if there are no filenames.
181 if (i == argc) {
182 printf("error: no files specified\n");
183 usage(argv[0]);
184 return 1;
185 }
186
187 // Process files.
188 for (; i < argc; i++) {
189 if (!processFile(argv[i], set, clear)) return 1;
190 }
191 return 0;
192}
193
194
195// Segment and section names are 16 bytes and may be un-terminated.
196static bool segnameEquals(const char *lhs, const char *rhs)
197{
198 return 0 == strncmp(lhs, rhs, 16);
199}
200
201static bool segnameStartsWith(const char *segname, const char *prefix)
202{
203 return 0 == strncmp(segname, prefix, strlen(prefix));
204}
205
206static bool sectnameEquals(const char *lhs, const char *rhs)
207{
208 return segnameEquals(lhs, rhs);
209}
210
211
212template <typename P>
213static void dosect(const char *filename, uint8_t *start, macho_section<P> *sect,
214 uint32_t set, uint32_t clear)
215{
216 if (debug) printf("section %.16s from segment %.16s\n",
217 sect->sectname(), sect->segname());
218
219 if ((segnameStartsWith(sect->segname(), "__DATA") &&
220 sectnameEquals(sect->sectname(), "__objc_imageinfo")) ||
221 (segnameEquals(sect->segname(), "__OBJC") &&
222 sectnameEquals(sect->sectname(), "__image_info")))
223 {
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",
230 oldFlags, newFlags);
231 }
232
233 printf("%s: ", filename);
234 printFlags(newFlags);
235 printf("\n");
236 }
237}
238
239template <typename P>
240static void doseg(const char *filename,
241 uint8_t *start, macho_segment_command<P> *seg,
242 uint32_t set, uint32_t clear)
243{
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, &sect[i], set, clear);
249 }
250}
251
252
253template<typename P>
254static bool parse_macho(const char *filename, uint8_t *buffer,
255 uint32_t set, uint32_t clear)
256{
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);
264 }
265 }
266
267 return true;
268}
269
270
271static bool parse_macho(const char *filename, uint8_t *buffer,
272 uint32_t set, uint32_t clear)
273{
274 uint32_t magic = *(uint32_t *)buffer;
275
276 switch (magic) {
277 case MH_MAGIC_64:
278 return parse_macho<Pointer64<CurrentEndian>>
279 (filename, buffer, set, clear);
280 case MH_MAGIC:
281 return parse_macho<Pointer32<CurrentEndian>>
282 (filename, buffer, set, clear);
283 case MH_CIGAM_64:
284 return parse_macho<Pointer64<OtherEndian>>
285 (filename, buffer, set, clear);
286 case MH_CIGAM:
287 return parse_macho<Pointer32<OtherEndian>>
288 (filename, buffer, set, clear);
289 default:
290 printf("error: file '%s' is not mach-o (magic %x)\n", filename, magic);
291 return false;
292 }
293}
294
295
296static bool parse_fat(const char *filename, uint8_t *buffer, size_t size,
297 uint32_t set, uint32_t clear)
298{
299 uint32_t magic;
300
301 if (size < sizeof(magic)) {
302 printf("error: file '%s' is too small\n", filename);
303 return false;
304 }
305
306 magic = *(uint32_t *)buffer;
307 if (magic != FAT_MAGIC && magic != FAT_CIGAM) {
308 /* Not a fat file */
309 return parse_macho(filename, buffer, set, clear);
310 } else {
311 struct fat_header *fh;
312 uint32_t fat_magic, fat_nfat_arch;
313 struct fat_arch *archs;
314
315 if (size < sizeof(struct fat_header)) {
316 printf("error: file '%s' is too small\n", filename);
317 return false;
318 }
319
320 fh = (struct fat_header *)buffer;
321 fat_magic = OSSwapBigToHostInt32(fh->magic);
322 fat_nfat_arch = OSSwapBigToHostInt32(fh->nfat_arch);
323
324 if (size < (sizeof(struct fat_header) + fat_nfat_arch * sizeof(struct fat_arch))) {
325 printf("error: file '%s' is too small\n", filename);
326 return false;
327 }
328
329 archs = (struct fat_arch *)(buffer + sizeof(struct fat_header));
330
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) {
335 fat_nfat_arch++;
336 }
337 }
338 /* End special case hidden CPU_TYPE_ARM64 */
339
340 if (debug) printf("%d fat architectures\n", fat_nfat_arch);
341
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);
347
348 if (debug) printf("cputype %d cpusubtype %d\n",
349 arch_cputype, arch_cpusubtype);
350
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");
354 return false;
355 }
356
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);
360 return false;
361 }
362
363 if (arch_size > size) {
364 printf("error: file '%s' is badly formed\n", filename);
365 return false;
366 }
367
368 if (arch_offset > (size - arch_size)) {
369 printf("error: file '%s' is badly formed\n", filename);
370 return false;
371 }
372
373 bool ok = parse_macho(filename, buffer + arch_offset, set, clear);
374 if (!ok) return false;
375 }
376 return true;
377 }
378}
379
380static bool processFile(const char *filename, uint32_t set, uint32_t clear)
381{
382 if (debug) printf("file %s\n", filename);
383 int openPerm = O_RDONLY;
384 int mmapPerm = PROT_READ;
385 if (set || clear) {
386 openPerm = O_RDWR;
387 mmapPerm = PROT_READ | PROT_WRITE;
388 }
389
390 int fd = open(filename, openPerm);
391 if (fd < 0) {
392 printf("error: open %s: %s\n", filename, strerror(errno));
393 return false;
394 }
395
396 struct stat st;
397 if (fstat(fd, &st) < 0) {
398 printf("error: fstat %s: %s\n", filename, strerror(errno));
399 return false;
400 }
401
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));
406 return false;
407 }
408
409 bool result =
410 parse_fat(filename, (uint8_t *)buffer, (size_t)st.st_size, set, clear);
411 munmap(buffer, (size_t)st.st_size);
412 close(fd);
413 return result;
414}