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@
33 #include <sys/errno.h>
34 #include <os/overflow.h>
35 #include <mach-o/fat.h>
36 #include <mach-o/arch.h>
37 #include <mach-o/loader.h>
39 // Some OS X SDKs don't define these.
41 #define CPU_TYPE_ARM ((cpu_type_t) 12)
43 #ifndef CPU_ARCH_ABI64
44 #define CPU_ARCH_ABI64 0x01000000 /* 64 bit ABI */
46 #ifndef CPU_TYPE_ARM64
47 #define CPU_TYPE_ARM64 (CPU_TYPE_ARM | CPU_ARCH_ABI64)
50 // File abstraction taken from ld64/FileAbstraction.hpp
51 // and ld64/MachOFileAbstraction.hpp.
54 #define INLINE __attribute__((always_inline))
60 // This abstraction layer is for use with file formats that have 64-bit/32-bit and Big-Endian/Little-Endian variants
62 // For example: to make a utility that handles 32-bit little enidan files use: Pointer32<LittleEndian>
65 // get16() read a 16-bit number from an E endian struct
66 // set16() write a 16-bit number to an E endian struct
67 // get32() read a 32-bit number from an E endian struct
68 // set32() write a 32-bit number to an E endian struct
69 // get64() read a 64-bit number from an E endian struct
70 // set64() write a 64-bit number to an E endian struct
72 // getBits() read a bit field from an E endian struct (bitCount=number of bits in field, firstBit=bit index of field)
73 // setBits() write a bit field to an E endian struct (bitCount=number of bits in field, firstBit=bit index of field)
75 // getBitsRaw() read a bit field from a struct with native endianness
76 // setBitsRaw() write a bit field from a struct with native endianness
82 static uint16_t get16(const uint16_t& from
) INLINE
{ return OSReadBigInt16(&from
, 0); }
83 static void set16(uint16_t& into
, uint16_t value
) INLINE
{ OSWriteBigInt16(&into
, 0, value
); }
85 static uint32_t get32(const uint32_t& from
) INLINE
{ return OSReadBigInt32(&from
, 0); }
86 static void set32(uint32_t& into
, uint32_t value
) INLINE
{ OSWriteBigInt32(&into
, 0, value
); }
88 static uint64_t get64(const uint64_t& from
) INLINE
{ return OSReadBigInt64(&from
, 0); }
89 static void set64(uint64_t& into
, uint64_t value
) INLINE
{ OSWriteBigInt64(&into
, 0, value
); }
91 static uint32_t getBits(const uint32_t& from
,
92 uint8_t firstBit
, uint8_t bitCount
) INLINE
{ return getBitsRaw(get32(from
), firstBit
, bitCount
); }
93 static void setBits(uint32_t& into
, uint32_t value
,
94 uint8_t firstBit
, uint8_t bitCount
) INLINE
{ uint32_t temp
= get32(into
); setBitsRaw(temp
, value
, firstBit
, bitCount
); set32(into
, temp
); }
96 static uint32_t getBitsRaw(const uint32_t& from
,
97 uint8_t firstBit
, uint8_t bitCount
) INLINE
{ return ((from
>> (32-firstBit
-bitCount
)) & ((1<<bitCount
)-1)); }
98 static void setBitsRaw(uint32_t& into
, uint32_t value
,
99 uint8_t firstBit
, uint8_t bitCount
) INLINE
{ uint32_t temp
= into
;
100 const uint32_t mask
= ((1<<bitCount
)-1);
101 temp
&= ~(mask
<< (32-firstBit
-bitCount
));
102 temp
|= ((value
& mask
) << (32-firstBit
-bitCount
));
104 enum { little_endian
= 0 };
111 static uint16_t get16(const uint16_t& from
) INLINE
{ return OSReadLittleInt16(&from
, 0); }
112 static void set16(uint16_t& into
, uint16_t value
) INLINE
{ OSWriteLittleInt16(&into
, 0, value
); }
114 static uint32_t get32(const uint32_t& from
) INLINE
{ return OSReadLittleInt32(&from
, 0); }
115 static void set32(uint32_t& into
, uint32_t value
) INLINE
{ OSWriteLittleInt32(&into
, 0, value
); }
117 static uint64_t get64(const uint64_t& from
) INLINE
{ return OSReadLittleInt64(&from
, 0); }
118 static void set64(uint64_t& into
, uint64_t value
) INLINE
{ OSWriteLittleInt64(&into
, 0, value
); }
120 static uint32_t getBits(const uint32_t& from
,
121 uint8_t firstBit
, uint8_t bitCount
) INLINE
{ return getBitsRaw(get32(from
), firstBit
, bitCount
); }
122 static void setBits(uint32_t& into
, uint32_t value
,
123 uint8_t firstBit
, uint8_t bitCount
) INLINE
{ uint32_t temp
= get32(into
); setBitsRaw(temp
, value
, firstBit
, bitCount
); set32(into
, temp
); }
125 static uint32_t getBitsRaw(const uint32_t& from
,
126 uint8_t firstBit
, uint8_t bitCount
) INLINE
{ return ((from
>> firstBit
) & ((1<<bitCount
)-1)); }
127 static void setBitsRaw(uint32_t& into
, uint32_t value
,
128 uint8_t firstBit
, uint8_t bitCount
) INLINE
{ uint32_t temp
= into
;
129 const uint32_t mask
= ((1<<bitCount
)-1);
130 temp
&= ~(mask
<< firstBit
);
131 temp
|= ((value
& mask
) << firstBit
);
133 enum { little_endian
= 1 };
137 typedef BigEndian CurrentEndian
;
138 typedef LittleEndian OtherEndian
;
139 #elif __LITTLE_ENDIAN__
140 typedef LittleEndian CurrentEndian
;
141 typedef BigEndian OtherEndian
;
143 #error unknown endianness
147 template <typename _E
>
151 typedef uint32_t uint_t
;
152 typedef int32_t sint_t
;
155 static uint64_t getP(const uint_t
& from
) INLINE
{ return _E::get32(from
); }
156 static void setP(uint_t
& into
, uint64_t value
) INLINE
{ _E::set32(into
, value
); }
160 template <typename _E
>
164 typedef uint64_t uint_t
;
165 typedef int64_t sint_t
;
168 static uint64_t getP(const uint_t
& from
) INLINE
{ return _E::get64(from
); }
169 static void setP(uint_t
& into
, uint64_t value
) INLINE
{ _E::set64(into
, value
); }
174 // mach-o file header
176 template <typename P
> struct macho_header_content
{};
177 template <> struct macho_header_content
<Pointer32
<BigEndian
> > { mach_header fields
; };
178 template <> struct macho_header_content
<Pointer64
<BigEndian
> > { mach_header_64 fields
; };
179 template <> struct macho_header_content
<Pointer32
<LittleEndian
> > { mach_header fields
; };
180 template <> struct macho_header_content
<Pointer64
<LittleEndian
> > { mach_header_64 fields
; };
182 template <typename P
>
185 uint32_t magic() const INLINE
{ return E::get32(header
.fields
.magic
); }
186 void set_magic(uint32_t value
) INLINE
{ E::set32(header
.fields
.magic
, value
); }
188 uint32_t cputype() const INLINE
{ return E::get32(header
.fields
.cputype
); }
189 void set_cputype(uint32_t value
) INLINE
{ E::set32((uint32_t&)header
.fields
.cputype
, value
); }
191 uint32_t cpusubtype() const INLINE
{ return E::get32(header
.fields
.cpusubtype
); }
192 void set_cpusubtype(uint32_t value
) INLINE
{ E::set32((uint32_t&)header
.fields
.cpusubtype
, value
); }
194 uint32_t filetype() const INLINE
{ return E::get32(header
.fields
.filetype
); }
195 void set_filetype(uint32_t value
) INLINE
{ E::set32(header
.fields
.filetype
, value
); }
197 uint32_t ncmds() const INLINE
{ return E::get32(header
.fields
.ncmds
); }
198 void set_ncmds(uint32_t value
) INLINE
{ E::set32(header
.fields
.ncmds
, value
); }
200 uint32_t sizeofcmds() const INLINE
{ return E::get32(header
.fields
.sizeofcmds
); }
201 void set_sizeofcmds(uint32_t value
) INLINE
{ E::set32(header
.fields
.sizeofcmds
, value
); }
203 uint32_t flags() const INLINE
{ return E::get32(header
.fields
.flags
); }
204 void set_flags(uint32_t value
) INLINE
{ E::set32(header
.fields
.flags
, value
); }
206 uint32_t reserved() const INLINE
{ return E::get32(header
.fields
.reserved
); }
207 void set_reserved(uint32_t value
) INLINE
{ E::set32(header
.fields
.reserved
, value
); }
209 typedef typename
P::E E
;
211 macho_header_content
<P
> header
;
216 // mach-o load command
218 template <typename P
>
219 class macho_load_command
{
221 uint32_t cmd() const INLINE
{ return E::get32(command
.cmd
); }
222 void set_cmd(uint32_t value
) INLINE
{ E::set32(command
.cmd
, value
); }
224 uint32_t cmdsize() const INLINE
{ return E::get32(command
.cmdsize
); }
225 void set_cmdsize(uint32_t value
) INLINE
{ E::set32(command
.cmdsize
, value
); }
227 typedef typename
P::E E
;
229 load_command command
;
236 // mach-o segment load command
238 template <typename P
> struct macho_segment_content
{};
239 template <> struct macho_segment_content
<Pointer32
<BigEndian
> > { segment_command fields
; enum { CMD
= LC_SEGMENT
}; };
240 template <> struct macho_segment_content
<Pointer64
<BigEndian
> > { segment_command_64 fields
; enum { CMD
= LC_SEGMENT_64
}; };
241 template <> struct macho_segment_content
<Pointer32
<LittleEndian
> > { segment_command fields
; enum { CMD
= LC_SEGMENT
}; };
242 template <> struct macho_segment_content
<Pointer64
<LittleEndian
> > { segment_command_64 fields
; enum { CMD
= LC_SEGMENT_64
}; };
244 template <typename P
>
245 class macho_segment_command
{
247 uint32_t cmd() const INLINE
{ return E::get32(segment
.fields
.cmd
); }
248 void set_cmd(uint32_t value
) INLINE
{ E::set32(segment
.fields
.cmd
, value
); }
250 uint32_t cmdsize() const INLINE
{ return E::get32(segment
.fields
.cmdsize
); }
251 void set_cmdsize(uint32_t value
) INLINE
{ E::set32(segment
.fields
.cmdsize
, value
); }
253 const char* segname() const INLINE
{ return segment
.fields
.segname
; }
254 void set_segname(const char* value
) INLINE
{ strncpy(segment
.fields
.segname
, value
, 16); }
256 uint64_t vmaddr() const INLINE
{ return P::getP(segment
.fields
.vmaddr
); }
257 void set_vmaddr(uint64_t value
) INLINE
{ P::setP(segment
.fields
.vmaddr
, value
); }
259 uint64_t vmsize() const INLINE
{ return P::getP(segment
.fields
.vmsize
); }
260 void set_vmsize(uint64_t value
) INLINE
{ P::setP(segment
.fields
.vmsize
, value
); }
262 uint64_t fileoff() const INLINE
{ return P::getP(segment
.fields
.fileoff
); }
263 void set_fileoff(uint64_t value
) INLINE
{ P::setP(segment
.fields
.fileoff
, value
); }
265 uint64_t filesize() const INLINE
{ return P::getP(segment
.fields
.filesize
); }
266 void set_filesize(uint64_t value
) INLINE
{ P::setP(segment
.fields
.filesize
, value
); }
268 uint32_t maxprot() const INLINE
{ return E::get32(segment
.fields
.maxprot
); }
269 void set_maxprot(uint32_t value
) INLINE
{ E::set32((uint32_t&)segment
.fields
.maxprot
, value
); }
271 uint32_t initprot() const INLINE
{ return E::get32(segment
.fields
.initprot
); }
272 void set_initprot(uint32_t value
) INLINE
{ E::set32((uint32_t&)segment
.fields
.initprot
, value
); }
274 uint32_t nsects() const INLINE
{ return E::get32(segment
.fields
.nsects
); }
275 void set_nsects(uint32_t value
) INLINE
{ E::set32(segment
.fields
.nsects
, value
); }
277 uint32_t flags() const INLINE
{ return E::get32(segment
.fields
.flags
); }
278 void set_flags(uint32_t value
) INLINE
{ E::set32(segment
.fields
.flags
, value
); }
281 CMD
= macho_segment_content
<P
>::CMD
284 typedef typename
P::E E
;
286 macho_segment_content
<P
> segment
;
293 template <typename P
> struct macho_section_content
{};
294 template <> struct macho_section_content
<Pointer32
<BigEndian
> > { section fields
; };
295 template <> struct macho_section_content
<Pointer64
<BigEndian
> > { section_64 fields
; };
296 template <> struct macho_section_content
<Pointer32
<LittleEndian
> > { section fields
; };
297 template <> struct macho_section_content
<Pointer64
<LittleEndian
> > { section_64 fields
; };
299 template <typename P
>
300 class macho_section
{
302 const char* sectname() const INLINE
{ return section
.fields
.sectname
; }
303 void set_sectname(const char* value
) INLINE
{ strncpy(section
.fields
.sectname
, value
, 16); }
305 const char* segname() const INLINE
{ return section
.fields
.segname
; }
306 void set_segname(const char* value
) INLINE
{ strncpy(section
.fields
.segname
, value
, 16); }
308 uint64_t addr() const INLINE
{ return P::getP(section
.fields
.addr
); }
309 void set_addr(uint64_t value
) INLINE
{ P::setP(section
.fields
.addr
, value
); }
311 uint64_t size() const INLINE
{ return P::getP(section
.fields
.size
); }
312 void set_size(uint64_t value
) INLINE
{ P::setP(section
.fields
.size
, value
); }
314 uint32_t offset() const INLINE
{ return E::get32(section
.fields
.offset
); }
315 void set_offset(uint32_t value
) INLINE
{ E::set32(section
.fields
.offset
, value
); }
317 uint32_t align() const INLINE
{ return E::get32(section
.fields
.align
); }
318 void set_align(uint32_t value
) INLINE
{ E::set32(section
.fields
.align
, value
); }
320 uint32_t reloff() const INLINE
{ return E::get32(section
.fields
.reloff
); }
321 void set_reloff(uint32_t value
) INLINE
{ E::set32(section
.fields
.reloff
, value
); }
323 uint32_t nreloc() const INLINE
{ return E::get32(section
.fields
.nreloc
); }
324 void set_nreloc(uint32_t value
) INLINE
{ E::set32(section
.fields
.nreloc
, value
); }
326 uint32_t flags() const INLINE
{ return E::get32(section
.fields
.flags
); }
327 void set_flags(uint32_t value
) INLINE
{ E::set32(section
.fields
.flags
, value
); }
329 uint32_t reserved1() const INLINE
{ return E::get32(section
.fields
.reserved1
); }
330 void set_reserved1(uint32_t value
) INLINE
{ E::set32(section
.fields
.reserved1
, value
); }
332 uint32_t reserved2() const INLINE
{ return E::get32(section
.fields
.reserved2
); }
333 void set_reserved2(uint32_t value
) INLINE
{ E::set32(section
.fields
.reserved2
, value
); }
335 typedef typename
P::E E
;
337 macho_section_content
<P
> section
;
343 static bool debug
= true;
345 bool processFile(const char *filename
);
347 int main(int argc
, const char *argv
[]) {
348 for (int i
= 1; i
< argc
; ++i
) {
349 if (!processFile(argv
[i
])) return 1;
360 // Segment and section names are 16 bytes and may be un-terminated.
361 bool segnameEquals(const char *lhs
, const char *rhs
)
363 return 0 == strncmp(lhs
, rhs
, 16);
366 bool segnameStartsWith(const char *segname
, const char *prefix
)
368 return 0 == strncmp(segname
, prefix
, strlen(prefix
));
371 bool sectnameEquals(const char *lhs
, const char *rhs
)
373 return segnameEquals(lhs
, rhs
);
377 template <typename P
>
378 void dosect(uint8_t *start
, macho_section
<P
> *sect
)
380 if (debug
) printf("section %.16s from segment %.16s\n",
381 sect
->sectname(), sect
->segname());
383 // Strip S_MOD_INIT/TERM_FUNC_POINTERS. We don't want dyld to call
384 // our init funcs because it is too late, and we don't want anyone to
385 // call our term funcs ever.
386 if (segnameStartsWith(sect
->segname(), "__DATA") &&
387 sectnameEquals(sect
->sectname(), "__mod_init_func"))
389 // section type 0 is S_REGULAR
390 sect
->set_flags(sect
->flags() & ~SECTION_TYPE
);
391 sect
->set_sectname("__objc_init_func");
392 if (debug
) printf("disabled __mod_init_func section\n");
394 if (segnameStartsWith(sect
->segname(), "__DATA") &&
395 sectnameEquals(sect
->sectname(), "__mod_term_func"))
397 // section type 0 is S_REGULAR
398 sect
->set_flags(sect
->flags() & ~SECTION_TYPE
);
399 sect
->set_sectname("__objc_term_func");
400 if (debug
) printf("disabled __mod_term_func section\n");
404 template <typename P
>
405 void doseg(uint8_t *start
, macho_segment_command
<P
> *seg
)
407 if (debug
) printf("segment name: %.16s, nsects %u\n",
408 seg
->segname(), seg
->nsects());
409 macho_section
<P
> *sect
= (macho_section
<P
> *)(seg
+ 1);
410 for (uint32_t i
= 0; i
< seg
->nsects(); ++i
) {
411 dosect(start
, §
[i
]);
417 bool parse_macho(uint8_t *buffer
)
419 macho_header
<P
>* mh
= (macho_header
<P
>*)buffer
;
420 uint8_t *cmds
= (uint8_t *)(mh
+ 1);
421 for (uint32_t c
= 0; c
< mh
->ncmds(); c
++) {
422 macho_load_command
<P
>* cmd
= (macho_load_command
<P
>*)cmds
;
423 cmds
+= cmd
->cmdsize();
424 if (cmd
->cmd() == LC_SEGMENT
|| cmd
->cmd() == LC_SEGMENT_64
) {
425 doseg(buffer
, (macho_segment_command
<P
>*)cmd
);
433 bool parse_macho(uint8_t *buffer
)
435 uint32_t magic
= *(uint32_t *)buffer
;
439 return parse_macho
<Pointer64
<CurrentEndian
>>(buffer
);
441 return parse_macho
<Pointer32
<CurrentEndian
>>(buffer
);
443 return parse_macho
<Pointer64
<OtherEndian
>>(buffer
);
445 return parse_macho
<Pointer32
<OtherEndian
>>(buffer
);
447 printf("file is not mach-o (magic %x)\n", magic
);
453 bool parse_fat(uint8_t *buffer
, size_t size
)
457 if (size
< sizeof(magic
)) {
458 printf("file is too small\n");
462 magic
= *(uint32_t *)buffer
;
463 if (magic
!= FAT_MAGIC
&& magic
!= FAT_CIGAM
) {
465 return parse_macho(buffer
);
467 struct fat_header
*fh
;
468 uint32_t fat_magic
, fat_nfat_arch
;
469 struct fat_arch
*archs
;
471 if (size
< sizeof(struct fat_header
)) {
472 printf("file is too small\n");
476 fh
= (struct fat_header
*)buffer
;
477 fat_magic
= OSSwapBigToHostInt32(fh
->magic
);
478 fat_nfat_arch
= OSSwapBigToHostInt32(fh
->nfat_arch
);
480 size_t fat_arch_size
;
481 // fat_nfat_arch * sizeof(struct fat_arch) + sizeof(struct fat_header)
482 if (os_mul_and_add_overflow(fat_nfat_arch
, sizeof(struct fat_arch
),
483 sizeof(struct fat_header
), &fat_arch_size
))
485 printf("too many fat archs\n");
488 if (size
< fat_arch_size
) {
489 printf("file is too small\n");
493 archs
= (struct fat_arch
*)(buffer
+ sizeof(struct fat_header
));
495 /* Special case hidden CPU_TYPE_ARM64 */
496 size_t fat_arch_plus_one_size
;
497 if (os_add_overflow(fat_arch_size
, sizeof(struct fat_arch
),
498 &fat_arch_plus_one_size
))
500 printf("too many fat archs\n");
503 if (size
>= fat_arch_plus_one_size
) {
504 if (fat_nfat_arch
> 0
505 && OSSwapBigToHostInt32(archs
[fat_nfat_arch
].cputype
) == CPU_TYPE_ARM64
) {
509 /* End special case hidden CPU_TYPE_ARM64 */
511 if (debug
) printf("%d fat architectures\n",
514 for (uint32_t i
= 0; i
< fat_nfat_arch
; i
++) {
515 uint32_t arch_cputype
= OSSwapBigToHostInt32(archs
[i
].cputype
);
516 uint32_t arch_cpusubtype
= OSSwapBigToHostInt32(archs
[i
].cpusubtype
);
517 uint32_t arch_offset
= OSSwapBigToHostInt32(archs
[i
].offset
);
518 uint32_t arch_size
= OSSwapBigToHostInt32(archs
[i
].size
);
520 if (debug
) printf("cputype %d cpusubtype %d\n",
521 arch_cputype
, arch_cpusubtype
);
523 /* Check that slice data is after all fat headers and archs */
524 if (arch_offset
< fat_arch_size
) {
525 printf("file is badly formed\n");
529 /* Check that the slice ends before the file does */
530 if (arch_offset
> size
) {
531 printf("file is badly formed\n");
535 if (arch_size
> size
) {
536 printf("file is badly formed\n");
540 if (arch_offset
> (size
- arch_size
)) {
541 printf("file is badly formed\n");
545 bool ok
= parse_macho(buffer
+ arch_offset
);
546 if (!ok
) return false;
552 bool processFile(const char *filename
)
554 if (debug
) printf("file %s\n", filename
);
555 int fd
= open(filename
, O_RDWR
);
557 printf("open %s: %s\n", filename
, strerror(errno
));
562 if (fstat(fd
, &st
) < 0) {
563 printf("fstat %s: %s\n", filename
, strerror(errno
));
567 void *buffer
= mmap(NULL
, (size_t)st
.st_size
, PROT_READ
|PROT_WRITE
,
568 MAP_FILE
|MAP_SHARED
, fd
, 0);
569 if (buffer
== MAP_FAILED
) {
570 printf("mmap %s: %s\n", filename
, strerror(errno
));
574 bool result
= parse_fat((uint8_t *)buffer
, (size_t)st
.st_size
);
575 munmap(buffer
, (size_t)st
.st_size
);